Computer graphics -- 2008-2009 -- info.uvt.ro/Laboratory 9

From Wikiversity

Jump to: navigation, search

Quick links: front; laboratories agenda, 1, 2, 3, 4, 5, JOGL template.


Contents

[edit] USING COMPLEX OBJECTS

There are cases in which the objects we need to draw are to complex for us to draw them ourselves. In such cases (J)OGL allows us to load object models generated by using various tools such as 3D Studio Max (.3ds files). Other formats include .md2, .md3, .obj, etc.

Importing these custom made objects requires a parser. Usually the developer has to create himself or use an existing API (JOGL Utils). which transforms the file into a stream of OpenGL primitives -- points, lines and simple surfaces.

[edit] JOGL Utils

[edit] Installation

  • Download the .jar file from the project site: JOGL Utils
  • Copy the .jar file inside your project lib folder
  • Add it to the class-path

[edit] Usage

[edit] Creating a Model3DS instance and loading the model
[...]
	public void init(GLAutoDrawable canvas)
	{
		[...]
 
		gl.glEnable(GL.GL_NORMALIZE);
 
		// Create an instance of the model class.
		this.model = new Model3DS();
		// Load the object from the file
		this.model.load("./data/spaceship.3ds");
 
		[...]
	}
 
[...]
 
	private Model3DS model;
[...]
[edit] Rendering the model
[...]
	public void display(GLAutoDrawable canvas)
	{
		GL gl = canvas.getGL();
 
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
 
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
 
		gl.glTranslated(0, 0, -4);
 
		gl.glPushMatrix();
 
			gl.glScaled(1.0 / 300, 1.0 / 300, 1.0 / 300);
 
			gl.glRotated(this.angle * 1.1, 1, 0, 0);
			gl.glRotated(this.angle * 1.2, 0, 1, 0);
			gl.glRotated(this.angle * 1.3, 0, 0, 1);
 
			//This method has to be implemented by the developer when using JOGLUtils.
			this.renderObject(gl, this.model);
 
 
 
		gl.glPopMatrix();
 
		gl.glFlush();
 
		this.angle += 1;
	}
 
	// Using the model data draw the normals (for lighting) and vertices belonging to the model.
	private void renderObject(GL gl, final Model3DS model) 
	{
		for (int objectI = 0; objectI < model.getNumberOfObjects(); objectI++) {
			Obj object = model.getObject(objectI);
 
			gl.glBegin(GL.GL_TRIANGLES);
 
			for (int faceI = 0; faceI < object.numOfFaces; faceI++) {
 
				Face face = object.faces[faceI];
 
				for (int vertexI = 0; vertexI < 3; vertexI++) {
 
					Vec3 vertex = object.verts[face.vertIndex[vertexI]];
					Vec3 normal = object.normals[face.vertIndex[vertexI]];
 
					gl.glNormal3f(normal.x, normal.y, normal.z);
					gl.glVertex3f(vertex.x, vertex.y, vertex.z);
				}	
			}
 
			gl.glEnd();
		}
	}
[...]

NOTE: You can find .3ds files on the internet by a simple search query: http://www.google.com/search?hl=en&q=free+3ds+models or try the examples from the following link: 3DS

NOTE: We could use the MyModel class also found in the same API and its load(GLAutoDrawable, String) and render(GLAutoDrawable) methods for loading and rendering objects with textures on them.

Links:

[edit] 3D GRAPHICS COMPLEX ISSUES (PART 1)

[edit] Billboards

A billboard is a 2D texture which always faces the camera. Its main use is for particles, simulating forests (trees), distant mountains, etc.

[edit] Steps for orienting the billboard towards the camera

  • recompute the vertices based on the distance between the billboard and the camera
  • use the updated vertices to draw the quad which will be used to draw the texture on

The following code shows an example on how we can rotate the coordinates to always face the camera. You will also need the TextureHandler class (and its dependencies) to see it work:

[...]
	public void display(GLAutoDrawable canvas)
	{
		GL gl = canvas.getGL();
 
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
 
		// This method is used for updating the vertices.
		// NOTE that it is called BEFORE rendering the quad.		
		this.updateCoordinates();
 
		gl.glPushMatrix();
 
			gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);
 
			this.bbTexture.bind();
			this.bbTexture.enable();
 
			gl.glBegin(GL.GL_QUADS);
				gl.glTexCoord2d(0, 1);
				gl.glVertex3dv(this.bbV1, 0);
				gl.glTexCoord2d(1, 1);
				gl.glVertex3dv(this.bbV2, 0);
				gl.glTexCoord2d(1, 0);
				gl.glVertex3dv(this.bbV3, 0);
				gl.glTexCoord2d(0, 0);
				gl.glVertex3dv(this.bbV4, 0);
			gl.glEnd();
 
		gl.glPopMatrix();
 
		gl.glFlush();
	}
 
	// This method is used for updating the vertex coordinates.	
	private void updateCoordinates()
	{
		this.bbAngle += this.bbAngleDirection * this.bbAngleSpeed;
 
		if (this.bbAngle > 13)
			this.bbAngleDirection = -1;
		if (this.bbAngle < -13)
			this.bbAngleDirection = 1;
 
		this.bbDistance = 10;
 
		this.bbCx = Math.cos(Math.toRadians(90 + this.bbAngle)) * this.bbDistance;
		this.bbCy = 0;
		this.bbCz = Math.sin(Math.toRadians(90 + this.bbAngle)) * this.bbDistance;
 
		this.camCx = 0;
		this.camCy = 0;
		this.camCz = 0;
 
		this.bbV1 = new double[] {this.bbCx - 1, this.bbCy - 1, this.bbCz};
		this.bbV2 = new double[] {this.bbCx + 1, this.bbCy - 1, this.bbCz};
		this.bbV3 = new double[] {this.bbCx + 1, this.bbCy + 1, this.bbCz};
		this.bbV4 = new double[] {this.bbCx - 1, this.bbCy + 1, this.bbCz};
 
		double deltaX = -1 * (this.bbCx - this.camCx);
		double deltaY = -1 * (this.bbCy - this.camCy);
		double deltaZ = -1 * (this.bbCz - this.camCz);
 
		double alpha = Math.atan2(deltaZ, deltaX) - Math.PI / 2.0;
		double beta = Math.atan2(deltaY, Math.sqrt(deltaX * deltaX + deltaZ * deltaZ));
 
		this.bbV1 = this.rotate(this.bbV1, alpha, beta);
		this.bbV2 = this.rotate(this.bbV2, alpha, beta);
		this.bbV3 = this.rotate(this.bbV3, alpha, beta);
		this.bbV4 = this.rotate(this.bbV4, alpha, beta);
	}
 
	// Method for rotating a point represented by a vertex given its alpha (latitude) and beta (longitude) angles
	private double[] rotate(double[] v, double alpha, double beta)
	{
		double y = v[1]  * Math.cos(beta) + v[2] * Math.sin(beta);
		double z = -v[1] * Math.sin(beta) + v[2] * Math.cos(beta);
		double x = v[0]  * Math.cos(alpha) - z * Math.sin(alpha);
		z = v[0]  * Math.sin(alpha) + z	* Math.cos(alpha);
		return (new double [] {x, y, z});
	}
 
	// The position of the camera. 
	// When using aimCamera and moveCamera you have access to those and you can use them to update the following three:	
	private double camCx;
	private double camCy;
	private double camCz;
 
	// Billboard angle, speed, direction and distance from the viewport.
	private double bbAngle = 0;
	private double bbAngleDirection = 1;
	private double bbAngleSpeed = 0.5;
	private double bbDistance = 25;
 
	private double bbCx;
	private double bbCy;
	private double bbCz;
 
	private double[] bbV1;
	private double[] bbV2;
	private double[] bbV3;
	private double[] bbV4;
[...]

If we want to use the camera and navigate through the billboards we need to do several other steps:

  • Get the code for aimCamera and moveCamera
  • Make bbAngle=0 (in the previous example it was changing constantly inside the updateCoordinates method)
  • Change camCx, camCy and camCz variables to point to the actual camera coordinates

The following example shows all of the previous in detail:


[...]
 
	// This method is used for updating the vertex coordinates.	
	private void updateCoordinates()
	{
 
		// We have commented the following line:
		//this.bbAngle += this.bbAngleDirection * this.bbAngleSpeed;
 
		[...]
 
		// We have changed the camC* variables so that they point to the actual camera coordinates:
		this.camCx = this.cameraCoordsPosx;
		this.camCy = this.cameraCoordsPosy;
		this.camCz = this.cameraCoordsPosz;
 
		[...]
	}
 
	//Add aimCamera and moveCamera here.
 
[...]
}


Links:

[edit] Exercise

  • Given the previous scene from the previous laboratory add a 3DS object to the scene
  • Given the previous scene from the previous laboratory apply textures on each of the spheres as follows: make the central sphere the Sun and the following three Mercury, Venus and Earth. Transform the Sun into a billboard.

NOTE: search for corresponding textures and 3DS objects on the Internet.