CubeGeometry class
@author mr.doob / http://mrdoob.com/ based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
Ported to Dart from JS by: @author rob silverton / http://www.unwrong.com/
class CubeGeometry extends Geometry {
//List materials;
CubeGeomSides _sides;
int segmentsWidth;
int segmentsHeight;
int segmentsDepth;
/**
* [materialOrList] is a [Material] or a [List] of [Material]. */
CubeGeometry( double width, double height, double depth, [this.segmentsWidth = 1,
this.segmentsHeight = 1,
this.segmentsDepth = 1,
materialOrList,
List sides] )
: super() {
double width_half = width / 2,
height_half = height / 2,
depth_half = depth / 2;
int mpx, mpy, mpz, mnx, mny, mnz;
if ( materialOrList != null ) {
if ( materialOrList is List ) {
materials = materialOrList;
} else {
materials = [];
for ( int i = 0; i < 6; i ++ ) {
materials.add( materialOrList );
}
}
mpx = 0; mnx = 1; mpy = 2; mny = 3; mpz = 4; mnz = 5;
} else {
materials = [];
}
//_sides = { "px": true, "nx": true, "py": true, "ny": true, "pz": true, "nz": true };
_sides = new CubeGeomSides();
//TODO: not sure if this is the correct use of "dynamic"
if ( sides != null ) {
for ( var s in sides ) {
if ( (_sides as dynamic)[ s ] != null ) {
(_sides as dynamic)[ s ] = sides[ s ];
}
}
}
if (_sides.px) buildPlane( 'z', 'y', -1.0, -1.0, depth, height, width_half, mpx ); // px
if (_sides.nx) buildPlane( 'z', 'y', 1.0, -1.0, depth, height, - width_half, mnx ); // nx
if (_sides.py) buildPlane( 'x', 'z', 1.0, 1.0, width, depth, height_half, mpy ); // py
if (_sides.ny) buildPlane( 'x', 'z', 1.0, -1.0, width, depth, - height_half, mny ); // ny
if (_sides.pz) buildPlane( 'x', 'y', 1.0, -1.0, width, height, depth_half, mpz ); // pz
if (_sides.nz) buildPlane( 'x', 'y', -1.0, -1.0, width, height, - depth_half, mnz ); // nz
computeCentroids();
mergeVertices();
}
void buildPlane( String u, String v, double udir, double vdir, double width, double height, double depth, int material ) {
String w;
int gridX = ( segmentsWidth != null ) ? segmentsWidth : 1;
int gridY = ( segmentsHeight != null ) ? segmentsHeight : 1;
double width_half = width / 2.0;
double height_half = height / 2.0;
int offset = vertices.length;
if ( ( u == 'x' && v == 'y' ) || ( u == 'y' && v == 'x' ) ) {
w = 'z';
} else if ( ( u == 'x' && v == 'z' ) || ( u == 'z' && v == 'x' ) ) {
w = 'y';
gridY = ( segmentsDepth != null ) ? segmentsDepth : 1;
} else if ( ( u == 'z' && v == 'y' ) || ( u == 'y' && v == 'z' ) ) {
w = 'x';
gridX = ( segmentsDepth != null ) ? segmentsDepth : 1;
}
num gridX1 = gridX + 1,
gridY1 = gridY + 1,
segment_width = width / gridX,
segment_height = height / gridY;
Vector3 normal = new Vector3.zero();
//TODO: find out how to do this sort of casting in Dart...
// normal.dynamic[ w ] = depth > 0 ? 1 : - 1;
if ( w == 'x' ) { normal.x = depth > 0 ? 1.0 : - 1.0;
} else if ( w == 'y' ) { normal.y = depth > 0 ? 1.0 : - 1.0;
} else if ( w == 'z' ) normal.z = depth > 0 ? 1.0 : - 1.0;
for ( int iy = 0; iy < gridY1; iy ++ ) {
for ( int ix = 0; ix < gridX1; ix ++ ) {
Vector3 vector = new Vector3.zero();
//TODO: find out how to do this sort of casting in Dart...
// vector[ u ] = ( ix * segment_width - width_half ) * udir;
// vector[ v ] = ( iy * segment_height - height_half ) * vdir;
// vector[ w ] = depth;
if ( u == 'x' ) { vector.x = ( ix * segment_width - width_half ) * udir;
} else if ( u == 'y' ) { vector.y = ( ix * segment_width - width_half ) * udir;
} else if ( u == 'z' ) vector.z = ( ix * segment_width - width_half ) * udir;
if ( v == 'x' ) { vector.x = ( iy * segment_height - height_half ) * vdir;
} else if ( v == 'y' ) { vector.y = ( iy * segment_height - height_half ) * vdir;
} else if ( v == 'z' ) vector.z = ( iy * segment_height - height_half ) * vdir;
if ( w == 'x' ) { vector.x = depth;
} else if ( w == 'y' ) { vector.y = depth;
} else if ( w == 'z' ) vector.z = depth;
vertices.add( vector );
}
}
for ( int iy = 0; iy < gridY; iy++ ) {
for ( int ix = 0; ix < gridX; ix++ ) {
num a = ix + gridX1 * iy;
num b = ix + gridX1 * ( iy + 1 );
num c = ( ix + 1 ) + gridX1 * ( iy + 1 );
num d = ( ix + 1 ) + gridX1 * iy;
Face4 face = new Face4( a + offset, b + offset, c + offset, d + offset );
face.normal.setFrom(normal);
face.vertexNormals.addAll( [normal.clone(), normal.clone(), normal.clone(), normal.clone()] );
face.materialIndex = material;
faces.add( face );
List faceVertexUV = faceVertexUvs[ 0 ];
List newUVs = new List();
newUVs.addAll([
new UV( ix / gridX, iy / gridY ),
new UV( ix / gridX, ( iy + 1 ) / gridY ),
new UV( ( ix + 1 ) / gridX, ( iy + 1 ) / gridY ),
new UV( ( ix + 1 ) / gridX, iy / gridY )
]);
faceVertexUV.add( newUVs );
}
}
}
}
Extends
Geometry > CubeGeometry
Constructors
new CubeGeometry(double width, double height, double depth, [int segmentsWidth = 1, int segmentsHeight = 1, int segmentsDepth = 1, materialOrList, List sides]) #
materialOrList is a Material or a List of Material.
CubeGeometry( double width, double height, double depth, [this.segmentsWidth = 1,
this.segmentsHeight = 1,
this.segmentsDepth = 1,
materialOrList,
List sides] )
: super() {
double width_half = width / 2,
height_half = height / 2,
depth_half = depth / 2;
int mpx, mpy, mpz, mnx, mny, mnz;
if ( materialOrList != null ) {
if ( materialOrList is List ) {
materials = materialOrList;
} else {
materials = [];
for ( int i = 0; i < 6; i ++ ) {
materials.add( materialOrList );
}
}
mpx = 0; mnx = 1; mpy = 2; mny = 3; mpz = 4; mnz = 5;
} else {
materials = [];
}
//_sides = { "px": true, "nx": true, "py": true, "ny": true, "pz": true, "nz": true };
_sides = new CubeGeomSides();
//TODO: not sure if this is the correct use of "dynamic"
if ( sides != null ) {
for ( var s in sides ) {
if ( (_sides as dynamic)[ s ] != null ) {
(_sides as dynamic)[ s ] = sides[ s ];
}
}
}
if (_sides.px) buildPlane( 'z', 'y', -1.0, -1.0, depth, height, width_half, mpx ); // px
if (_sides.nx) buildPlane( 'z', 'y', 1.0, -1.0, depth, height, - width_half, mnx ); // nx
if (_sides.py) buildPlane( 'x', 'z', 1.0, 1.0, width, depth, height_half, mpy ); // py
if (_sides.ny) buildPlane( 'x', 'z', 1.0, -1.0, width, depth, - height_half, mny ); // ny
if (_sides.pz) buildPlane( 'x', 'y', 1.0, -1.0, width, height, depth_half, mpz ); // pz
if (_sides.nz) buildPlane( 'x', 'y', -1.0, -1.0, width, height, - depth_half, mnz ); // nz
computeCentroids();
mergeVertices();
}
Properties
Operators
Methods
void applyMatrix(Matrix4 matrix) #
inherited from Geometry
void applyMatrix( Matrix4 matrix ) {
Matrix4 matrixRotation = new Matrix4.identity();
extractRotation( matrixRotation, matrix);
vertices.forEach((vertex) => vertex.applyProjection(matrix));
faces.forEach((face) {
face.normal.applyProjection(matrixRotation);
face.vertexNormals.forEach((normal) => normal.applyProjection(matrixRotation));
face.centroid.applyProjection(matrix);
});
}
void buildPlane(String u, String v, double udir, double vdir, double width, double height, double depth, int material) #
void buildPlane( String u, String v, double udir, double vdir, double width, double height, double depth, int material ) {
String w;
int gridX = ( segmentsWidth != null ) ? segmentsWidth : 1;
int gridY = ( segmentsHeight != null ) ? segmentsHeight : 1;
double width_half = width / 2.0;
double height_half = height / 2.0;
int offset = vertices.length;
if ( ( u == 'x' && v == 'y' ) || ( u == 'y' && v == 'x' ) ) {
w = 'z';
} else if ( ( u == 'x' && v == 'z' ) || ( u == 'z' && v == 'x' ) ) {
w = 'y';
gridY = ( segmentsDepth != null ) ? segmentsDepth : 1;
} else if ( ( u == 'z' && v == 'y' ) || ( u == 'y' && v == 'z' ) ) {
w = 'x';
gridX = ( segmentsDepth != null ) ? segmentsDepth : 1;
}
num gridX1 = gridX + 1,
gridY1 = gridY + 1,
segment_width = width / gridX,
segment_height = height / gridY;
Vector3 normal = new Vector3.zero();
//TODO: find out how to do this sort of casting in Dart...
// normal.dynamic[ w ] = depth > 0 ? 1 : - 1;
if ( w == 'x' ) { normal.x = depth > 0 ? 1.0 : - 1.0;
} else if ( w == 'y' ) { normal.y = depth > 0 ? 1.0 : - 1.0;
} else if ( w == 'z' ) normal.z = depth > 0 ? 1.0 : - 1.0;
for ( int iy = 0; iy < gridY1; iy ++ ) {
for ( int ix = 0; ix < gridX1; ix ++ ) {
Vector3 vector = new Vector3.zero();
//TODO: find out how to do this sort of casting in Dart...
// vector[ u ] = ( ix * segment_width - width_half ) * udir;
// vector[ v ] = ( iy * segment_height - height_half ) * vdir;
// vector[ w ] = depth;
if ( u == 'x' ) { vector.x = ( ix * segment_width - width_half ) * udir;
} else if ( u == 'y' ) { vector.y = ( ix * segment_width - width_half ) * udir;
} else if ( u == 'z' ) vector.z = ( ix * segment_width - width_half ) * udir;
if ( v == 'x' ) { vector.x = ( iy * segment_height - height_half ) * vdir;
} else if ( v == 'y' ) { vector.y = ( iy * segment_height - height_half ) * vdir;
} else if ( v == 'z' ) vector.z = ( iy * segment_height - height_half ) * vdir;
if ( w == 'x' ) { vector.x = depth;
} else if ( w == 'y' ) { vector.y = depth;
} else if ( w == 'z' ) vector.z = depth;
vertices.add( vector );
}
}
for ( int iy = 0; iy < gridY; iy++ ) {
for ( int ix = 0; ix < gridX; ix++ ) {
num a = ix + gridX1 * iy;
num b = ix + gridX1 * ( iy + 1 );
num c = ( ix + 1 ) + gridX1 * ( iy + 1 );
num d = ( ix + 1 ) + gridX1 * iy;
Face4 face = new Face4( a + offset, b + offset, c + offset, d + offset );
face.normal.setFrom(normal);
face.vertexNormals.addAll( [normal.clone(), normal.clone(), normal.clone(), normal.clone()] );
face.materialIndex = material;
faces.add( face );
List faceVertexUV = faceVertexUvs[ 0 ];
List newUVs = new List();
newUVs.addAll([
new UV( ix / gridX, iy / gridY ),
new UV( ix / gridX, ( iy + 1 ) / gridY ),
new UV( ( ix + 1 ) / gridX, ( iy + 1 ) / gridY ),
new UV( ( ix + 1 ) / gridX, iy / gridY )
]);
faceVertexUV.add( newUVs );
}
}
}
void computeBoundingBox() #
inherited from Geometry
void computeBoundingBox() {
if ( boundingBox == null ) {
boundingBox = new BoundingBox( min: new Vector3.zero(), max: new Vector3.zero() );
}
if ( vertices.length > 0 ) {
Vector3 position, firstPosition = vertices[ 0 ];
boundingBox.min.setFrom( firstPosition );
boundingBox.max.setFrom( firstPosition );
Vector3 min = boundingBox.min,
max = boundingBox.max;
num vl = vertices.length;
for ( int v = 1; v < vl; v ++ ) {
position = vertices[ v ];
if ( position.x < min.x ) {
min.x = position.x;
} else if ( position.x > max.x ) {
max.x = position.x;
}
if ( position.y < min.y ) {
min.y = position.y;
} else if ( position.y > max.y ) {
max.y = position.y;
}
if ( position.z < min.z ) {
min.z = position.z;
} else if ( position.z > max.z ) {
max.z = position.z;
}
}
}
}
void computeBoundingSphere() #
inherited from Geometry
void computeBoundingSphere() {
num radiusSq;
var maxRadiusSq = vertices.fold(0, (num curMaxRadiusSq, Vector3 vertex) {
radiusSq = vertex.length2;
return ( radiusSq > curMaxRadiusSq ) ? radiusSq : curMaxRadiusSq;
});
boundingSphere = new BoundingSphere(radius: Math.sqrt(maxRadiusSq) );
}
void computeCentroids() #
inherited from Geometry
void computeCentroids() {
faces.forEach((Face face) {
face.centroid.setValues( 0.0, 0.0, 0.0 );
face.indices.forEach((idx) {
face.centroid.add( vertices[ idx ] );
});
face.centroid /= face.size.toDouble();
});
}
void computeFaceNormals() #
inherited from Geometry
void computeFaceNormals() {
faces.forEach((face) {
var vA = vertices[ face.a ],
vB = vertices[ face.b ],
vC = vertices[ face.c ];
Vector3 cb = vC - vB;
Vector3 ab = vA - vB;
cb = cb.cross( ab );
cb.normalize();
face.normal = cb;
});
}
void computeTangents() #
inherited from Geometry
void computeTangents() {
// based on http://www.terathon.com/code/tangent.html
// tangents go to vertices
var f, fl, face;
num i, il, vertexIndex, test, w;
Vector3 vA, vB, vC;
UV uvA, uvB, uvC;
List uv;
num x1, x2, y1, y2, z1, z2, s1, s2, t1, t2, r;
Vector3 sdir = new Vector3.zero(),
tdir = new Vector3.zero(),
tmp = new Vector3.zero(),
tmp2 = new Vector3.zero(),
n = new Vector3.zero(),
t;
List<Vector3> tan1 = vertices.map((_) => new Vector3.zero()).toList(),
tan2 = vertices.map((_) => new Vector3.zero()).toList();
var handleTriangle = ( context, a, b, c, ua, ub, uc ) {
vA = context.vertices[ a ];
vB = context.vertices[ b ];
vC = context.vertices[ c ];
uvA = uv[ ua ];
uvB = uv[ ub ];
uvC = uv[ uc ];
x1 = vB.x - vA.x;
x2 = vC.x - vA.x;
y1 = vB.y - vA.y;
y2 = vC.y - vA.y;
z1 = vB.z - vA.z;
z2 = vC.z - vA.z;
s1 = uvB.u - uvA.u;
s2 = uvC.u - uvA.u;
t1 = uvB.v - uvA.v;
t2 = uvC.v - uvA.v;
r = 1.0 / ( s1 * t2 - s2 * t1 );
sdir.setValues( ( t2 * x1 - t1 * x2 ) * r,
( t2 * y1 - t1 * y2 ) * r,
( t2 * z1 - t1 * z2 ) * r );
tdir.setValues( ( s1 * x2 - s2 * x1 ) * r,
( s1 * y2 - s2 * y1 ) * r,
( s1 * z2 - s2 * z1 ) * r );
tan1[ a ].add( sdir );
tan1[ b ].add( sdir );
tan1[ c ].add( sdir );
tan2[ a ].add( tdir );
tan2[ b ].add( tdir );
tan2[ c ].add( tdir );
};
fl = this.faces.length;
for ( f = 0; f < fl; f ++ ) {
face = this.faces[ f ];
UV uv = faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents
// TODO - Come up with a way to handle an arbitrary number of vertexes
var triangles = [];
if ( face.size == 3 ) {
triangles.add([0, 1, 2]);
} else if ( face.size == 4 ) {
triangles.add([0, 1, 3]);
triangles.add([1, 2, 3]);
}
triangles.forEach((t) {
handleTriangle( this, face.indices[t[0]], face.indices[t[1]], face.indices[t[2]], t[0], t[1], t[2] );
});
}
faces.forEach((face) {
il = face.vertexNormals.length;
for ( i = 0; i < il; i++ ) {
n.setFrom( face.vertexNormals[ i ] );
vertexIndex = face.indices[i];
t = tan1[ vertexIndex ];
// Gram-Schmidt orthogonalize
tmp.setFrom( t );
tmp.sub( n.scale( n.dot( t ) ) ).normalize();
// Calculate handedness
tmp2 = face.vertexNormals[i].cross(t);
test = tmp2.dot( tan2[ vertexIndex ] );
w = (test < 0.0) ? -1.0 : 1.0;
face.vertexTangents[ i ] = new Vector4( tmp.x, tmp.y, tmp.z, w );
}
});
hasTangents = true;
}
void computeVertexNormals() #
inherited from Geometry
void computeVertexNormals() {
List<Vector3> vertices;
// create internal buffers for reuse when calling this method repeatedly
// (otherwise memory allocation / deallocation every frame is big resource hog)
if ( __tmpVertices == null ) {
__tmpVertices = [];
this.vertices.forEach((_) => __tmpVertices.add(new Vector3.zero()));
vertices = __tmpVertices;
faces.forEach((face) {
face.vertexNormals = new List.generate(face.size, (_) => new Vector3.zero(), growable: false);
});
} else {
vertices = __tmpVertices;
var vl = this.vertices.length;
for ( var v = 0; v < vl; v ++ ) {
vertices[ v ].setValues( 0.0, 0.0, 0.0 );
}
}
faces.forEach((Face face) {
face.indices.forEach((idx) {
vertices[ idx ].add( face.normal );
});
});
vertices.forEach((v) => v.normalize());
faces.forEach((Face face) {
var i = 0;
face.indices.forEach((idx) {
face.vertexNormals[ i++ ].setFrom( vertices[ idx ] );
});
});
}
int mergeVertices() #
inherited from Geometry
int mergeVertices() {
Map verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique)
List<Vector3> unique = [];
List<int> changes = [];
String key;
int precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001
num precision = Math.pow( 10, precisionPoints );
int i, il;
var abcd = 'abcd', o, k, j, jl, u;
Vector3 v;
il = this.vertices.length;
for( i = 0; i < il; i++) {
v = this.vertices[i];
key = [ ( v.x * precision ).round().toStringAsFixed(0),
( v.y * precision ).round().toStringAsFixed(0),
( v.z * precision ).round().toStringAsFixed(0) ].join('_' );
if ( verticesMap[ key ] == null ) {
verticesMap[ key ] = i;
unique.add( v );
//TODO: pretty sure this is an acceptable change in syntax here:
//changes[ i ] = unique.length - 1;
changes.add( unique.length - 1);
} else {
//print('Duplicate vertex found. $i could be using ${verticesMap[key]}');
//print('changes len ${changes.length} add at i = $i');
//changes[ i ] = changes[ verticesMap[ key ] ];
changes.add( changes[ verticesMap[ key ] ] );
}
}
// Start to patch face indices
faces.forEach((Face face) {
for (var i = 0; i < face.size; i++) {
face.indices[i] = changes[ face.indices[i] ];
/* TODO
// check dups in (a, b, c, d) and convert to -> face3
var o = [ face.a, face.b, face.c, face.d ];
for ( var k = 3; k > 0; k -- ) {
if ( o.indexOf( face[ abcd[ k ] ] ) != k ) {
// console.log('faces', face.a, face.b, face.c, face.d, 'dup at', k);
o.removeAt( k );
this.faces[ i ] = new THREE.Face3( o[0], o[1], o[2], face.normal, face.color, face.materialIndex );
for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
u = this.faceVertexUvs[ j ][ i ];
if ( u ) u.removeAt( k );
}
this.faces[ i ].vertexColors = face.vertexColors;
break;
}
}*/
}
});
// Use unique set of vertices
var diff = vertices.length - unique.length;
vertices = unique;
return diff;
}