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

Quick links: front; laboratories agenda, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, JOGL template.


USING COMPLEX OBJECTS

edit

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.

JOGL Utils

edit

Installation

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

Usage

edit
Creating a Model3DS instance and loading the model
edit
[...]
	
	private Model3DS model;
	private TextureHandler texture3DS;

[...]
	public void init(GLAutoDrawable canvas)
	{
		[...]
		
		gl.glEnable(GL2.GL_NORMALIZE);
		
		// Create an instance of the model class.
		this.model = new Model3DS();
		// Load the object from the file
		this.model.load("./data/geographos.3ds");

		// Load the texture		
		this.texture3DS = new TextureHandler(gl, glu, "./data/asteroid.bmp", true);

		[...]
	}

[...]
Rendering the model
edit
[...]
	public void display(GLAutoDrawable canvas)
	{
		GL2 gl = canvas.getGL().getGL2();
		
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
		
		gl.glMatrixMode(GLMatrixFunc.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 (+texture coordinates) belonging to the model.
	private void renderObject(GL gl, final Model3DS model) 
	{
		for (int objectI = 0; objectI < this.model.getNumberOfObjects(); objectI++) {
			Obj object = model.getObject(objectI);
			
			// The texture is read with TextureHandler in init(...)
			this.texture3DS.bind();
			this.texture3DS.enable();
			gl.glBegin(GL2.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]];
						
					if (object.hasTexture) {
						Vec3 texture = object.texVerts[face.vertIndex[vertexI]];
						gl.glTexCoord2d(texture.x, texture.y);
					}
					gl.glNormal3f(normal.x, normal.y, normal.z);
					gl.glVertex3f(vertex.x, vertex.y, vertex.z);
				}	
			}
				
			gl.glEnd();
			this.texture3DS.disable();
		}
	}
[...]

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.

Alternatives?

edit

Sure! You could use any of the classes found on the Internet for loading 3ds, obj or other format files.

A simple obj loader can be found at [1]

Links:

3D GRAPHICS COMPLEX ISSUES (PART 1)

edit

Billboards

edit

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

Steps for orienting the billboard towards the camera

edit
  • 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)
	{
		GL2 gl = canvas.getGL().getGL2();
		
		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(GL2.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()
	{
		// The lines between BEGIN and END are for moving the billboard. For fixed objects ignore them and just set bbCx, bbCy, bbCz to some values
		// BEGIN 
		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;
		// END		

		this.camCx = 0;
		this.camCy = 0;
		this.camCz = 0;
		
		// Compute the billboard vertices based on its center (bbCx, bbCy, bbCz)
		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};
		
		// Compute the billboard-camera vector. Its x,y,z components will be used to get the angles of rotation
		double deltaX = this.camCx - this.bbCx;
		double deltaY = this.camCy - this.bbCy;
		double deltaZ = this.camCz - this.bbCz;
		
		// Convert cartesian coordinates to polar coordinates, i.e., get the angles of rotation
		double alpha = Math.atan2(deltaZ, deltaX) - Math.PI / 2.0;
		double beta = Math.atan2(deltaY, Math.sqrt(deltaX * deltaX + deltaZ * deltaZ));
		
		// Perform rotation
		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:

Exercise

edit
  • Given the previous scene from the previous laboratory add some 3DS objects to the scene as follows:
    • add a comet like mesh to the scene and make it rotate around its OY axis and around the Sun
    • add a space ship mesh to the scene and place it around Earth's orbit
  • Add the Sun rays to the sphere representing it. Make it a billboard.

NOTE: search for corresponding textures and 3DS objects on the Internet. (http://www.celestiamotherlode.net/ is a good place to look into)

NOTE: the Sun ray billboard should slice the sun into two equal semi-spheres.