Commits

Michael Ludwig  committed 443e279

Add TangentGenerator and improve View/Node handling.

  • Participants
  • Parent commits e41b273

Comments (0)

Files changed (7)

File src/com/ferox/renderer/View.java

  * View represents the camera that a scene is rendered through.  It holds onto the
  * type and specifics of the projection, and its location and orientation.  The
  * visible region of a View is a 3D frustum.  It provides functionality to get the
- * planes of the frustum, in camera and world space, and to test BoundVolumes for
- * intersection with the frustum.
+ * planes of the frustum in world space.
+ * 
+ * The View uses a right-handed coordinate system that looks down its negative,
+ * local z-axis.
  * 
  * @author Michael Ludwig
  *

File src/com/ferox/resource/geometry/TangentGenerator.java

+package com.ferox.resource.geometry;
+
+import org.openmali.vecmath.Vector2f;
+import org.openmali.vecmath.Vector3f;
+
+/** TangentGenerator is a utility to generate arrays of tangent and
+ * bitangent vectors from a set vertices, texture coordinates and indices.
+ * 
+ * For the moment, it only supports triangles and floating-point vertices/texcoords.
+ * 
+ * @author Michael Ludwig
+ *
+ */
+public class TangentGenerator {
+	/** Fill the tangents and bitangents arrays with appropriate values based
+	 * on the given vertices, texture coordinates, and an indices array to access them.
+	 * This index array is also used to access the tangents and bitangents arrays.
+	 * 
+	 * All arguments must be non-null.  tangents and bitangents must have the same
+	 * length as vertices.
+	 * 
+	 * It is assumed that vertices are packed together, every 3 primitives being a vertex.
+	 * Similarly, every 2 primitives in texCoords is a texture coordinate. 
+	 * Every 3 ints in indices represent the 3 coordinates of a triangle. */
+	public static void generate(float[] vertices, float[] texCoords, int[] indices, float[] tangents, float[] bitangents) throws IllegalArgumentException, NullPointerException {
+		if (vertices == null || texCoords == null || indices == null || tangents == null || bitangents == null)
+			throw new NullPointerException("All arguments must be non-null");
+		if (texCoords.length / 2 != vertices.length / 3)
+			throw new IllegalArgumentException("Tex coords don't have same number of elements as vertices");
+		if (tangents.length != vertices.length)
+			throw new IllegalArgumentException("Tangents array doesn't have the same length as vertices");
+		if (bitangents.length != vertices.length)
+			throw new IllegalArgumentException("Bitangents array doesn't have the same length as vertices");
+		
+		Vector3f tangent = new Vector3f();
+        Vector3f bitangent = new Vector3f();
+        
+        // for a triangle
+        Vector3f verts[] = new Vector3f[3];
+        Vector2f tcs[] = new Vector2f[3];
+        
+        int v, i;
+        for (i = 0; i < 3; i++) {
+        	verts[i] = new Vector3f();
+        	tcs[i] = new Vector2f();
+        }
+        
+        int[] index = new int[3];
+        
+        int loop = indices.length / 3;
+        for (int t = 0; t < loop; t++) {
+        	for (v = 0; v < 3; v++) {
+        		i = indices[t * 3 + v];
+        		index[v] = i;
+        		
+        		verts[v].x = vertices[i * 3];
+        		verts[v].y = vertices[i * 3 + 1];
+        		verts[v].z = vertices[i * 3 + 2];
+        		
+        		tcs[v].x = texCoords[i * 2];
+        		tcs[v].y = texCoords[i * 2 + 1];
+        	}
+        	
+        	computeTriangleTangentSpace(tangent, bitangent, verts, tcs);
+        	
+        	for (v = 0; v < 3; v++) {
+        		i = index[v] * 3;
+        		tangents[i] = tangent.x;
+        		tangents[i + 1] = tangent.y;
+        		tangents[i + 2] = tangent.z;
+        		
+        		bitangents[i] = bitangent.x;
+        		bitangents[i + 1] = bitangent.y;
+        		bitangents[i + 2] = bitangent.z;
+        	}
+        }
+	}
+
+	// modifies v and t vectors, but that's okay since they aren't used after this method is called
+    private static void computeTriangleTangentSpace(Vector3f tangent, Vector3f bitangent, Vector3f v[], Vector2f t[]) {
+    	Vector3f edge1 = v[1];
+    	edge1.sub(v[0]);
+    	Vector3f edge2 = v[2];
+    	edge2.sub(v[0]);
+    	
+    	Vector2f edge1uv = t[1];
+    	edge1uv.sub(t[0]);
+        Vector2f edge2uv = t[2];
+        edge2uv.sub(t[0]);
+
+        float cp = edge1uv.y * edge2uv.x - edge1uv.x * edge2uv.y;
+
+        if (cp != 0.0f) {
+            float mul = 1.0f / cp;
+            tangent.scale(-edge2uv.y, edge1);
+            tangent.scaleAdd(edge1uv.y, edge2, tangent);
+            tangent.scale(mul);
+            tangent.normalize();
+            
+            bitangent.scale(-edge2uv.x, edge1);
+            bitangent.scaleAdd(edge1uv.x, edge2, bitangent);
+            bitangent.scale(mul);
+            bitangent.normalize();
+        }
+    }
+}

File src/com/ferox/scene/Node.java

 	 * its y-axis is aligned as closely as possible to up (not always exactly up because it has to
 	 * maintain orthogonality). 
 	 * 
-	 * Specify negateDirection = true if the node "faces" backwards.  This is especially useful
-	 * for view nodes, since a view looks down the negative z-axis. */
-	public void lookAt(Vector3f position, Vector3f up, boolean negateDirection) throws NullPointerException {
+	 * The z-axis of the rotation matrix is assumed to be the facing direction. */
+	public void lookAt(Vector3f position, Vector3f up) throws NullPointerException {
 		if (position == null || up == null)
 			throw new SceneException("Can't call lookAt() with null input vectors: " + position + " " + up);
 		
 		Vector3f upVec = Node.upVec.get();
 		
 		dirVec.sub(position, this.worldTransform.getTranslation()); dirVec.normalize();
-		if (negateDirection)
-			dirVec.scale(-1f);
 		
 		leftVec.cross(up, dirVec); leftVec.normalize();
 		upVec.cross(dirVec, leftVec); upVec.normalize();

File src/com/ferox/scene/SceneElement.java

 import com.ferox.renderer.View;
 
 /** Common interface that all scene elements must implement. Only current implementations are 
- * Node-based classes.
+ * Node-based classes.  To match View, it is assumed that we use a right-handed coordinate system.
  * 
  * @author Michael Ludwig
  *

File src/com/ferox/scene/ViewNode.java

 		if (this.view != null) {
 			this.view.setLocation(this.worldTransform.getTranslation());
 			Matrix3f b = this.worldTransform.getRotation();
-			this.view.getDirection().set(-b.m02, -b.m12, -b.m22);
+			this.view.getDirection().set(b.m02, b.m12, b.m22);
 			this.view.getUp().set(b.m01, b.m11, b.m21);
 		}
 	}

File test/com/ferox/BasicApplication.java

 	private static Vector3f up = new Vector3f(0f, 1f, 0f);
 	@Override
 	protected boolean update() {
-		this.view.lookAt(origin, up, true);
+		this.view.lookAt(origin, up);
 		this.scene.update(true);
 		return false;
 	}

File test/com/ferox/scene/GlslTest.java

 import com.ferox.resource.BufferData;
 import com.ferox.resource.Geometry;
 import com.ferox.resource.geometry.Box;
+import com.ferox.resource.geometry.BufferedGeometryDescriptor;
+import com.ferox.resource.geometry.TangentGenerator;
+import com.ferox.resource.geometry.Teapot;
 import com.ferox.resource.geometry.VertexArray;
 import com.ferox.resource.geometry.VertexArrayGeometry;
-import com.ferox.resource.geometry.BufferedGeometry.PolygonType;
 import com.ferox.resource.glsl.GlslProgram;
 import com.ferox.resource.glsl.GlslUniform;
 import com.ferox.resource.glsl.GlslVertexAttribute;
 		view.getLocalTransform().getTranslation().z = 20f;
 		root.add(view);
 		
-		Geometry cube = this.buildCube(4f);
+		Geometry cube = this.build(new Teapot(3f));
 		renderer.requestUpdate(cube, true);
 		Appearance app = this.createGlslAppearance(renderer);
 		
 				"attribute vec3 tangent;",
 				"attribute vec3 bitangent;",
 
-				"varying vec3 half_vector;",
+				"varying vec3 eye_dir;",
 				"varying vec3 light_dir;",
 
 				"varying vec3 tan;",
 					"nm = gl_Normal;",
 
 					"light_dir = gl_LightSource[0].position.xyz - gl_Position.xyz;",
-					"half_vector = gl_LightSource[0].halfVector.xyz;",
-
+					//"half_vector = gl_LightSource[0].halfVector.xyz;",
+					"eye_dir = -gl_Position.xyz;",
+					
 					"gl_Position = gl_ProjectionMatrix * gl_Position;",
 				"}"
 			};
 			"uniform sampler2D normal;",
 			"uniform sampler2D specular;", 
 
-			"varying vec3 half_vector;",
+			"varying vec3 eye_dir;",
 			"varying vec3 light_dir;",
 
 			"varying vec3 tan;",
 
 				"float specularPower = 0.0;",
 				"if (nxDir > 0.0) {",
-					"lightVector = normalize(half_vector);",
+					"lightVector = normalize(eye_dir + light_dir);",
 					"float nxHalf = max(0.0, dot(norm, lightVector));",
 					"specularPower = min(1.0, pow(nxHalf, gl_FrontMaterial.shininess));",
 				"}",
 
 				"vec4 spec = (gl_LightSource[0].specular * vec4(texture2D(specular, gl_TexCoord[0].st).rgb, 1.0)) * specularPower;",
-				"gl_FragColor = (diffuse * vec4(baseColor.rgb, 1.0));",// + spec;",
+				"gl_FragColor = (diffuse * vec4(baseColor.rgb, 1.0)) + spec;",
 			"}"
 		};
 
 	 * Junky code to build a cube with extra vertex attributes.
 	 */
 	
-	private Geometry buildCube(float side) {
-		float[] v = new float[72];
-		float[] n = new float[72];
-		float[] t = new float[48];
-		float[] tan = new float[72];
-		float[] btan = new float[72];
+	private Geometry build(BufferedGeometryDescriptor b) {
+		BufferData verts = b.getVertices();
+		BufferData tcs = b.getTextureCoordinates();
 		
-		// front
-		v[0] = 1f; v[1] = 1f; v[2] = 1f; n[0] = 0f; n[1] = 0f; n[2] = 1f; t[0] = 1f; t[1] = 1f;
-		v[3] = -1f; v[4] = 1f; v[5] = 1f; n[3] = 0f; n[4] = 0f; n[5] = 1f; t[2] = 0f; t[3] = 1f;
-		v[6] = -1f; v[7] = -1f; v[8] = 1f; n[6] = 0f; n[7] = 0f; n[8] = 1f; t[4] = 0f; t[5] = 0f;
-		v[9] = 1f; v[10] = -1f; v[11] = 1f; n[9] = 0f; n[10] = 0f; n[11] = 1f; t[6] = 1f; t[7] = 0f;
-		//back
-		v[12] = -1f; v[13] = -1f; v[14] = -1f; n[12] = 0f; n[13] = 0f; n[14] = -1f; t[8] = 1f; t[9] = 1f;
-		v[21] = 1f; v[22] = -1f; v[23] = -1f; n[21] = 0f; n[22] = 0f; n[23] = -1f; t[10] = 0f; t[11] = 1f;
-		v[18] = 1f; v[19] = 1f; v[20] = -1f; n[18] = 0f; n[19] = 0f; n[20] = -1f; t[12] = 0f; t[13] = 0f;
-		v[15] = -1f; v[16] = 1f; v[17] = -1f; n[15] = 0f; n[16] = 0f; n[17] = -1f; t[14] = 1f; t[15] = 0f;
-		//right
-		v[24] = 1f; v[25] = 1f; v[26] = -1f; n[24] = 1f; n[25] = 0f; n[26] = 0f; t[16] = 1f; t[17] = 1f;
-		v[27] = 1f; v[28] = 1f; v[29] = 1f; n[27] = 1f; n[28] = 0f; n[29] = 0f; t[18] = 0f; t[19] = 1f;
-		v[30] = 1f; v[31] = -1f; v[32] = 1f; n[30] = 1f; n[31] = 0f; n[32] = 0f; t[20] = 0f; t[21] = 0f;
-		v[33] = 1f; v[34] = -1f; v[35] = -1f; n[33] = 1f; n[34] = 0f; n[35] = 0f; t[22] = 1f; t[23] = 0f;
-		//left
-		v[36] = -1f; v[37] = -1f; v[38] = 1f; n[36] = -1f; n[37] = 0f; n[38] = 0f; t[24] = 1f; t[25] = 1f;
-		v[45] = -1f; v[46] = -1f; v[47] = -1f; n[45] = -1f; n[46] = 0f; n[47] = 0f; t[26] = 0f; t[27] = 1f;
-		v[42] = -1f; v[43] = 1f; v[44] = -1f; n[42] = -1f; n[43] = 0f; n[44] = 0f; t[28] = 0f; t[29] = 0f;
-		v[39] = -1f; v[40] = 1f; v[41] = 1f; n[39] = -1f; n[40] = 0f; n[41] = 0f; t[30] = 1f; t[31] = 0f;
-		//top
-		v[48] = -1f; v[49] = 1f; v[50] = -1f; n[48] = 0f; n[49] = 1f; n[50] = 0f; t[32] = 1f; t[33] = 1f;
-		v[57] = 1f; v[58] = 1f; v[59] = -1f; n[57] = 0f; n[58] = 1f; n[59] = 0f; t[34] = 0f; t[35] = 1f;
-		v[54] = 1f; v[55] = 1f; v[56] = 1f; n[54] = 0f; n[55] = 1f; n[56] = 0f; t[36] = 0f; t[37] = 0f;
-		v[51] = -1f; v[52] = 1f; v[53] = 1f; n[51] = 0f; n[52] = 1f; n[53] = 0f; t[38] = 1f; t[39] = 0f;
-		//bottom
-		v[60] = 1f; v[61] = -1f; v[62] = 1f; n[60] = 0f; n[61] = -1f; n[62] = 0f; t[40] = 1f; t[41] = 1f;
-		v[63] = -1f; v[64] = -1f; v[65] = 1f; n[63] = 0f; n[64] = -1f; n[65] = 0f; t[42] = 0f; t[43] = 1f;
-		v[66] = -1f; v[67] = -1f; v[68] = -1f; n[66] = 0f; n[67] = -1f; n[68] = 0f; t[44] = 0f; t[45] = 0f;
-		v[69] = 1f; v[70] = -1f; v[71] = -1f; n[69] = 0f; n[70] = -1f; n[71] = 0f; t[46] = 1f; t[47] = 0f;
+		float[] tan = new float[verts.getCapacity()];
+		float[] bitan = new float[verts.getCapacity()];
+		TangentGenerator.generate((float[]) verts.getData(), (float[]) tcs.getData(), (int[]) b.getIndices().getData(), tan, bitan);
 		
-		for (int i = 0; i < v.length; i++)
-			v[i] = v[i] * side / 2f;
+		VertexArrayGeometry box = new VertexArrayGeometry(b);
+		box.setVertexAttributes(1, new BufferData(tan, false), new VertexArray(3));
+		box.setVertexAttributes(2, new BufferData(bitan, false), new VertexArray(3));
 		
-		int[] i = new int[24];
-		for (int u = 0; u < 24; u++) {
-			i[u] = u;
-		}
-		
-		computeTangentBiTangent(v, t, i, tan, btan);
-		cleanTangentBiTangent(n, tan, btan);
-		
-		i = new int[48];
-		for (int u = 0; u < 6; u++) {
-			int t1 = u * 4;
-			int t2 = u * 4 + 1;
-			int t3 = u * 4 + 2;
-			int t4 = u * 4 + 3;
-			
-			i[u * 6] = t1;
-			i[u * 6 + 1] = t2;
-			i[u * 6 + 2] = t4;
-			
-			i[u * 6 + 3] = t2;
-			i[u * 6 + 4] = t3;
-			i[u * 6 + 5] = t4;
-		}
-		
-		BufferData vb = new BufferData(v, false);
-		BufferData nb = new BufferData(n, false);
-		BufferData tb =new BufferData(t, false);
-		BufferData tanb = new BufferData(tan, false);
-		BufferData btanb = new BufferData(btan, false);
-		
-		BufferData ib = new BufferData(i, true);
-		
-		VertexArrayGeometry geom = new VertexArrayGeometry(vb, new VertexArray(3), ib, new VertexArray(1), PolygonType.TRIANGLES);
-		geom.setNormals(nb, new VertexArray(3));
-		geom.setTextureCoordinates(0, tb, new VertexArray(2));
-		geom.setVertexAttributes(1, tanb, new VertexArray(3));
-		geom.setVertexAttributes(2, btanb, new VertexArray(3));
-
-		return geom;
-	}
-
-	private static void cleanTangentBiTangent(float[] normals, float[] tan, float[] bitan) {
-		Vector3f tp = new Vector3f();
-		Vector3f bp = new Vector3f();
-		Vector3f normal = new Vector3f();
-		Vector3f tangent = new Vector3f();
-		Vector3f bitangent = new Vector3f();
-		
-		float dot;
-		for (int i = 0; i < normals.length / 3; i++) {
-			normal.set(normals[i*3], normals[i*3+1], normals[i*3+2]);
-			tangent.set(tan[i*3], tan[i*3+1], tan[i*3+2]);
-			bitangent.set(bitan[i*3], bitan[i*3+1], bitan[i*3+2]);
-
-			dot = normal.dot(tangent);
-			tp.set(tangent);
-			tangent.scale(-dot, normal);
-			tp.add(tangent);
-			tp.normalize();
-			
-			tan[i*3] = tp.x;
-			tan[i*3+1] = tp.y;
-			tan[i*3+2] = tp.z;
-			
-			dot = tp.dot(bitangent);
-			tangent.scale(-dot, tp);
-			dot = normal.dot(bitangent);
-			tp.scale(-dot, normal);
-			
-			bp.set(bitangent);
-			bp.add(tp);
-			bp.add(tangent);
-			bp.normalize();
-			
-			bitan[i*3] = bp.x;
-			bitan[i*3+1] = bp.y;
-			bitan[i*3+2] = bp.z;
-		}
-	}
-	
-	private static void computeTangentBiTangent(float[] verts, float[] texcoords, int[] indices, float[] tan, float[] bitan) {
-		for (int i = 0; i < indices.length / 4; i++) {
-			computeForTriangle(indices[i*4], indices[i*4+1], indices[i*4+3], verts, texcoords, tan, bitan);
-			computeForTriangle(indices[i*4+1], indices[i*4], indices[i*4+2], verts, texcoords, tan, bitan);
-			computeForTriangle(indices[i*4+2], indices[i*4+1], indices[i*4+3], verts, texcoords, tan, bitan);
-			computeForTriangle(indices[i*4+3], indices[i*4], indices[i*4+2], verts, texcoords, tan, bitan);
-		}
-	}
-	
-	private static void computeForTriangle(int v0, int v1, int v2, float[] verts, float[] texcoords, float[] tan, float[] bitan) {
-		float s1, t1;
-		float s2, t2;
-		
-		Vector3f q1 = new Vector3f();
-		Vector3f q2 = new Vector3f();
-		
-		q1.x = verts[v1*3] - verts[v0*3];
-		q1.y = verts[v1*3 + 1] - verts[v0*3 + 1];
-		q1.z = verts[v1*3 + 2] - verts[v0*3 + 2];
-		
-		q2.x = verts[v2*3] - verts[v0*3];
-		q2.y = verts[v2*3 + 1] - verts[v0*3 + 1];
-		q2.z = verts[v2*3 + 2] - verts[v0*3 + 2];
-		
-		s1 = texcoords[v1*2] - texcoords[v0*2];
-		t1 = texcoords[v1*2+1] - texcoords[v0*2+1];
-		
-		s2 = texcoords[v2*2] - texcoords[v0*2];
-		t2 = texcoords[v2*2+1] - texcoords[v0*2+1];
-		
-		float scale = 1 / (s1*t2 - s2*t1);
-		
-		
-		// as of yet, unnormalized or guaranteed orthogonal
-		tan[v0*3] = scale * (t2 * q1.x - t1 * q2.x);
-		tan[v0*3+1] = scale * (t2 * q1.y - t1 * q2.y);
-		tan[v0*3+2] = scale * (t2 * q1.z - t1 * q2.z);
-		
-		bitan[v0*3] = scale * (-s2 * q1.x + s1 * q2.x);
-		bitan[v0*3+1] = scale * (-s2 * q1.y + s1 * q2.y);
-		bitan[v0*3+2] = scale * (-s2 * q1.z + s1 * q2.z);
+		return box;
 	}
 }