浏览代码

Merge pull request #7182 from simonThiele/unittests

Unit-tests for BufferGeometry
Mr.doob 10 年之前
父节点
当前提交
dca02f6a6f
共有 3 个文件被更改,包括 334 次插入18 次删除
  1. 14 18
      test/unit/cameras/PerspectiveCamera.js
  2. 319 0
      test/unit/core/BufferGeometry.js
  3. 1 0
      test/unit/unittests_three.html

+ 14 - 18
test/unit/cameras/PerspectiveCamera.js

@@ -5,32 +5,28 @@
 module( "PerspectiveCamera" );
 
 test( "updateProjectionMatrix", function() {
-	var near = 1,
-			far = 3,
-			bottom = -1,
-			top = 1,
-			aspect = 16 / 9,
-			left = -top * aspect,
-			right = -bottom * aspect,
-			fov = 90;
 
-	var cam = new THREE.PerspectiveCamera( fov, aspect, near, far );
+	var cam = new THREE.PerspectiveCamera( 75, 16 / 9, 0.1, 300.0 );
 
 	// updateProjectionMatrix is called in contructor
-	var m = cam.projectionMatrix.elements;
+	var m = cam.projectionMatrix;
 
 	// perspective projection is given my the 4x4 Matrix
 	// 2n/r-l		0			l+r/r-l				 0
 	//   0		2n/t-b	t+b/t-b				 0
 	//   0			0		-(f+n/f-n)	-(2fn/f-n)
-	//   0			0				0						 1
-
-	ok( m[0] === ( 2 * near ) / ( right - left ), "m[0,0] === 2n/r-l" );
-	ok( m[5] === ( 2 * near ) / ( top - bottom ), "m[1,1] === 2n/r-l" );
-	ok( m[8] === ( right + left ) / ( right - left ), "m[2,0] === 2n/r-l" );
-	ok( m[9] === ( top + bottom ) / ( top - bottom ), "m[2,1] === 2n/r-l" );
-	ok( m[10] === - ( far + near ) / ( far - near ), "m[2,2] === 2n/r-l" );
-	ok( m[14] === - ( 2 * near * far ) / ( far - near ), "m[3,2] === 2n/r-l" );
+	//   0			0				-1					 0
+
+	// this matrix was calculated by hand via glMatrix.perspective(75, 16 / 9, 0.1, 300.0, pMatrix)
+	// to get a reference matrix from plain WebGL
+	var reference = new THREE.Matrix4().set(
+		0.7330642938613892, 0, 0, 0,
+		0, 1.3032253980636597, 0, 0,
+		0, 0, -1.000666856765747, -0.2000666856765747,
+		0, 0, -1, 0
+	);
+
+	ok( reference.equals(m) );
 });
 
 test( "clone", function() {

+ 319 - 0
test/unit/core/BufferGeometry.js

@@ -0,0 +1,319 @@
+/**
+ * @author simonThiele / https://github.com/simonThiele
+ */
+
+module( "BufferGeometry" );
+
+var DegToRad = Math.PI / 180;
+
+test( "add / delete Attribute", function() {
+	var geometry = new THREE.BufferGeometry();
+	var attributeName = "position";
+
+	ok ( geometry.attributes[attributeName] === undefined , 'no attribute defined' );
+
+	geometry.addAttribute( attributeName, new THREE.BufferAttribute( new Float32Array( [1, 2, 3], 1 ) ) );
+
+	ok ( geometry.attributes[attributeName] !== undefined , 'attribute is defined' );
+
+	geometry.removeAttribute( attributeName );
+
+	ok ( geometry.attributes[attributeName] === undefined , 'no attribute defined' );
+});
+
+test( "applyMatrix", function() {
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( "position", new THREE.BufferAttribute( new Float32Array(6), 3 ) );
+
+	var matrix = new THREE.Matrix4().set(
+		1, 0, 0, 1.5,
+		0, 1, 0, -2,
+		0, 0, 1, 3,
+		0, 0, 0, 1
+	);
+	geometry.applyMatrix(matrix);
+
+	var position = geometry.attributes.position.array;
+	var m = matrix.elements;
+	ok( position[0] === m[12] && position[1] === m[13] && position[2] === m[14], "position was extracted from matrix" );
+	ok( position[3] === m[12] && position[4] === m[13] && position[5] === m[14], "position was extracted from matrix twice" );
+	ok( geometry.attributes.position.version === 1, "version was increased during update" );
+});
+
+test( "rotateX/Y/Z", function() {
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( "position", new THREE.BufferAttribute( new Float32Array([1, 2, 3, 4, 5, 6]), 3 ) );
+
+	var pos = geometry.attributes.position.array;
+
+	geometry.rotateX( 180 * DegToRad );
+
+	// object was rotated around x so all items should be flipped but the x ones
+	ok( pos[0] === 1 && pos[1] === -2 && pos[2] === -3 &&
+			pos[3] === 4 && pos[4] === -5 && pos[5] === -6, "vertices were rotated around x by 180 degrees" );
+
+
+	geometry.rotateY( 180 * DegToRad );
+
+	// vertices were rotated around y so all items should be flipped again but the y ones
+	ok( pos[0] === -1 && pos[1] === -2 && pos[2] === 3 &&
+			pos[3] === -4 && pos[4] === -5 && pos[5] === 6, "vertices were rotated around y by 180 degrees" );
+
+
+	geometry.rotateZ( 180 * DegToRad );
+
+	// vertices were rotated around z so all items should be flipped again but the z ones
+	ok( pos[0] === 1 && pos[1] === 2 && pos[2] === 3 &&
+			pos[3] === 4 && pos[4] === 5 && pos[5] === 6, "vertices were rotated around z by 180 degrees" );
+});
+
+
+test( "translate", function() {
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( "position", new THREE.BufferAttribute( new Float32Array([1, 2, 3, 4, 5, 6]), 3 ) );
+
+	var pos = geometry.attributes.position.array;
+
+	geometry.translate( 10, 20, 30 );
+
+	ok( pos[0] === 11 && pos[1] === 22 && pos[2] === 33 &&
+			pos[3] === 14 && pos[4] === 25 && pos[5] === 36, "vertices were translated" );
+});
+
+test( "scale", function() {
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( "position", new THREE.BufferAttribute( new Float32Array([-1, -1, -1, 2, 2, 2]), 3 ) );
+
+	var pos = geometry.attributes.position.array;
+
+	geometry.scale( 1, 2, 3 );
+
+	ok( pos[0] === -1 && pos[1] === -2 && pos[2] === -3 &&
+			pos[3] === 2 && pos[4] === 4 && pos[5] === 6, "vertices were scaled" );
+});
+
+test( "center", function() {
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( "position", new THREE.BufferAttribute( new Float32Array([
+		-1, -1, -1,
+		1, 1, 1,
+		4, 4, 4
+	]), 3 ) );
+
+	geometry.center();
+
+	var pos = geometry.attributes.position.array;
+	var bb = geometry.boundingBox;
+
+	// the boundingBox should go from (-1, -1, -1) to (4, 4, 4) so it has a size of (5, 5, 5)
+	// after centering it the vertices should be placed between (-2.5, -2.5, -2.5) and (2.5, 2.5, 2.5)
+	ok( pos[0] === -2.5 && pos[1] === -2.5 && pos[2] === -2.5 &&
+			pos[3] === -0.5 && pos[4] === -0.5 && pos[5] === -0.5 &&
+			pos[6] === 2.5 && pos[7] === 2.5 && pos[8] === 2.5, "vertices were replaced by boundingBox dimensions" );
+});
+
+test( "setFromObject", function() {
+	var lineGeo = new THREE.Geometry();
+	lineGeo.vertices.push(
+		new THREE.Vector3( -10, 0, 0 ),
+		new THREE.Vector3( 0, 10, 0 ),
+		new THREE.Vector3( 10, 0, 0 )
+	);
+
+	lineGeo.colors.push(
+		new THREE.Color(1, 0, 0 ),
+		new THREE.Color(0, 1, 0 ),
+		new THREE.Color(0, 0, 1 )
+	);
+
+	var line = new THREE.Line( lineGeo, null );
+	var geometry = new THREE.BufferGeometry().setFromObject( line );
+
+	var pos = geometry.attributes.position.array;
+	var col = geometry.attributes.color.array;
+	var v = lineGeo.vertices;
+	var c = lineGeo.colors;
+
+	ok(
+		 // position exists
+			pos !== undefined &&
+
+			// vertex arrays have the same size
+			v.length * 3 === pos.length &&
+
+			// there are three complete vertices (each vertex contains three values)
+			geometry.attributes.position.count === 3 &&
+
+			// check if both arrays contains the same data
+			pos[0] === v[0].x && pos[1] === v[0].y && pos[2] === v[0].z &&
+			pos[3] === v[1].x && pos[4] === v[1].y && pos[5] === v[1].z &&
+			pos[6] === v[2].x && pos[7] === v[2].y && pos[8] === v[2].z
+			, "positions are equal" );
+
+	ok(
+		 // color exists
+			col !== undefined &&
+
+			// color arrays have the same size
+			c.length * 3 === col.length &&
+
+			// there are three complete colors (each color contains three values)
+			geometry.attributes.color.count === 3 &&
+
+			// check if both arrays contains the same data
+			col[0] === c[0].r && col[1] === c[0].g && col[2] === c[0].b &&
+			col[3] === c[1].r && col[4] === c[1].g && col[5] === c[1].b &&
+			col[6] === c[2].r && col[7] === c[2].g && col[8] === c[2].b
+			, "colors are equal" );
+});
+
+test( "computeBoundingBox", function() {
+	var bb = getBBForVertices( [-1, -2, -3, 13, -2, -3.5, -1, -20, 0, -4, 5, 6] );
+
+	ok( bb.min.x === -4 && bb.min.y === -20 && bb.min.z === -3.5, "min values are set correctly" );
+	ok( bb.max.x === 13 && bb.max.y === 5 && bb.max.z === 6, "max values are set correctly" );
+
+
+	bb = getBBForVertices( [] );
+
+	ok( bb.min.x === 0 && bb.min.y === 0 && bb.min.z === 0, "since there are no values given, the bb has size = 0" );
+	ok( bb.max.x === 0 && bb.max.y === 0 && bb.max.z === 0, "since there are no values given, the bb has size = 0" );
+
+
+	bb = getBBForVertices( [-1, -1, -1] );
+
+	ok( bb.min.x === bb.max.x && bb.min.y === bb.max.y && bb.min.z === bb.max.z, "since there is only one vertex, max and min are equal" );
+	ok( bb.min.x === -1 && bb.min.y === -1 && bb.min.z === -1, "since there is only one vertex, min and max are this vertex" );
+
+	bb = getBBForVertices( [-1] );
+});
+
+test( "computeBoundingSphere", function() {
+	var bs = getBSForVertices( [-10, 0, 0, 10, 0, 0] );
+
+	ok( bs.radius === (10 + 10) / 2, "radius is equal to deltaMinMax / 2" )
+	ok( bs.center.x === 0 && bs.center.y === 0 && bs.center.y === 0, "bounding sphere is at ( 0, 0, 0 )" )
+
+
+	var bs = getBSForVertices( [-5, 11, -3, 5, -11, 3] );
+	var radius = new THREE.Vector3(5, 11, 3).length();
+
+	ok( bs.radius === radius, "radius is equal to directionLength" )
+	ok( bs.center.x === 0 && bs.center.y === 0 && bs.center.y === 0, "bounding sphere is at ( 0, 0, 0 )" )
+});
+
+function getBBForVertices(vertices) {
+	var geometry = new THREE.BufferGeometry();
+
+	geometry.addAttribute( "position", new THREE.BufferAttribute( new Float32Array(vertices), 3 ) );
+	geometry.computeBoundingBox();
+
+	return geometry.boundingBox;
+}
+
+function getBSForVertices(vertices) {
+	var geometry = new THREE.BufferGeometry();
+
+	geometry.addAttribute( "position", new THREE.BufferAttribute( new Float32Array(vertices), 3 ) );
+	geometry.computeBoundingSphere();
+
+	return geometry.boundingSphere;
+}
+
+test( "computeVertexNormals", function() {
+	// get normals for a counter clockwise created triangle
+	var normals = getNormalsForVertices([-1, 0, 0, 1, 0, 0, 0, 1, 0]);
+
+	ok( normals[0] === 0 && normals[1] === 0 && normals[2] === 1,
+		"first normal is pointing to screen since the the triangle was created counter clockwise" );
+
+	ok( normals[3] === 0 && normals[4] === 0 && normals[5] === 1,
+		"second normal is pointing to screen since the the triangle was created counter clockwise" );
+
+	ok( normals[6] === 0 && normals[7] === 0 && normals[8] === 1,
+		"third normal is pointing to screen since the the triangle was created counter clockwise" );
+
+
+	// get normals for a clockwise created triangle
+	var normals = getNormalsForVertices([1, 0, 0, -1, 0, 0, 0, 1, 0]);
+
+	ok( normals[0] === 0 && normals[1] === 0 && normals[2] === -1,
+		"first normal is pointing to screen since the the triangle was created clockwise" );
+
+	ok( normals[3] === 0 && normals[4] === 0 && normals[5] === -1,
+		"second normal is pointing to screen since the the triangle was created clockwise" );
+
+	ok( normals[6] === 0 && normals[7] === 0 && normals[8] === -1,
+		"third normal is pointing to screen since the the triangle was created clockwise" );
+
+
+	var normals = getNormalsForVertices([0, 0, 1, 0, 0, -1, 1, 1, 0]);
+
+	// the triangle is rotated by 45 degrees to the right so the normals of the three vertices
+	// should point to (1, -1, 0).normalized(). The simplest solution is to check against a normalized
+	// vector (1, -1, 0) but you will get calculation errors because of floating calculations so another
+	// valid technique is to create a vector which stands in 90 degrees to the normals and calculate the
+	// dot product which is the cos of the angle between them. This should be < floating calculation error
+	// which can be taken from Number.EPSILON
+	var direction = new THREE.Vector3(1, 1, 0).normalize(); // a vector which should have 90 degrees difference to normals
+	var difference = direction.dot( new THREE.Vector3( normals[0], normals[1], normals[2] ) );
+	ok( difference < Number.EPSILON, "normal is equal to reference vector");
+
+
+	// get normals for a line should be NAN because you need min a triangle to calculate normals
+	var normals = getNormalsForVertices([1, 0, 0, -1, 0, 0]);
+	for (var i = 0; i < normals.length; i++) {
+		ok ( !normals[i], "normals can't be calculated which is good");
+	}
+});
+
+function getNormalsForVertices(vertices) {
+	var geometry = new THREE.BufferGeometry();
+
+	geometry.addAttribute( "position", new THREE.BufferAttribute( new Float32Array(vertices), 3 ) );
+
+	geometry.computeVertexNormals();
+
+	ok( geometry.attributes.normal !== undefined, "normal attribute was created" );
+
+	return geometry.attributes.normal.array;
+}
+
+test( "merge", function() {
+	var geometry1 = new THREE.BufferGeometry();
+	geometry1.addAttribute( "attrName", new THREE.BufferAttribute( new Float32Array([1, 2, 3, 0, 0, 0]), 3 ) );
+
+	var geometry2 = new THREE.BufferGeometry();
+	geometry2.addAttribute( "attrName", new THREE.BufferAttribute( new Float32Array([4, 5, 6]), 3 ) );
+
+	var attr = geometry1.attributes.attrName.array;
+
+	geometry1.merge(geometry2, 1);
+
+	// merged array should be 1, 2, 3, 4, 5, 6
+	for (var i = 0; i < attr.length; i++) {
+	  ok( attr[i] === i + 1, "");
+	}
+
+	geometry1.merge(geometry2);
+	ok( attr[0] === 4 && attr[1] === 5 && attr[2] === 6, "copied the 3 attributes without offset" );
+});
+
+test( "copy", function() {
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( "attrName", new THREE.BufferAttribute( new Float32Array([1, 2, 3, 4, 5, 6]), 3 ) );
+	geometry.addAttribute( "attrName2", new THREE.BufferAttribute( new Float32Array([0, 1, 3, 5, 6]), 1 ) );
+
+	var copy = new THREE.BufferGeometry().copy(geometry);
+
+	ok( copy !== geometry && geometry.id !== copy.id, "new object was created" );
+
+	Object.keys(geometry.attributes).forEach(function(key) {
+		var attribute = geometry.attributes[key];
+		ok( attribute !== undefined, "all attributes where copied");
+
+		for (var i = 0; i < attribute.array.length; i++) {
+			ok( attribute.array[i] === copy.attributes[key].array[i], "values of the attribute are equal" );
+		}
+	});
+});

+ 1 - 0
test/unit/unittests_three.html

@@ -23,6 +23,7 @@
   <script src="cameras/PerspectiveCamera.js"></script>
 
   <script src="core/BufferAttribute.js"></script>
+  <script src="core/BufferGeometry.js"></script>
   <script src="core/Object3D.js"></script>
 
   <script src="math/Constants.js"></script>