ExtrudeGeometry class
@author zz85 / http://www.lab4games.net/zz85/blog
Creates extruded geometry from a path shape.
parameters = {
size: <float>, // size of the text height: <float>, // thickness to extrude text curveSegments: <int>, // number of points on the curves steps: <int> | <List>, // number of points or list of U positions for z-side extrusions / used for subdividing segements of extrude spline too amount: <int>, // Amount
bevelEnabled: <bool>, // turn on bevel bevelThickness: <float>, // how deep into text bevel goes bevelSize: <float>, // how far from text outline is bevel bevelSegments: <int>, // number of bevel layers
extrudePath: <THREE.CurvePath> // 2d/3d spline path to extrude shape orthogonality to frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals
bendPath: <THREE.CurvePath> // 2d path for bend the shape around x/y plane
material: <int> // material index for front and back faces extrudeMaterial: <int> // material index for extrusion and beveled faces
}
class ExtrudeGeometry extends Geometry { List shapes; var shapebb; ExtrudeGeometry( this.shapes, { amount: 100, bevelThickness: 6.0, bevelSize: null, bevelSegments: 3, bevelEnabled: true, curveSegments: 12, steps: 1, //can assume a number of steps or a List with all U's of steps bendPath, extrudePath, frames, material, extrudeMaterial } ) : super() { if (bevelSize == null) bevelSize = bevelThickness - 2.0; if (shapes == null) { shapes = []; return; } shapebb = shapes.last.getBoundingBox(); addShapeList( shapes, amount, bevelThickness, bevelSize, bevelSegments,bevelEnabled, curveSegments, steps, bendPath, extrudePath, frames, material, extrudeMaterial ); computeCentroids(); computeFaceNormals(); // can't really use automatic vertex normals // as then front and back sides get smoothed too // should do separate smoothing just for sides //this.computeVertexNormals(); //console.log( "took", ( Date.now() - startTime ) ); } addShapeList(shapes, amount, bevelThickness, bevelSize, bevelSegments,bevelEnabled, curveSegments,steps,bendPath,extrudePath,frames,material, extrudeMaterial) { var sl = shapes.length; for ( var s = 0; s < sl; s ++ ) { var shape = shapes[ s ]; addShape( shape, amount, bevelThickness, bevelSize, bevelSegments,bevelEnabled, curveSegments, steps, bendPath, extrudePath, frames, material, extrudeMaterial ); } } // addShape Helpers _scalePt2 ( Vector2 pt, Vector2 vec, num size ) { if ( vec == null ) print( "die" ); return vec.clone().scale( size ).add( pt ); } _getBevelVec2( Vector2 pt_i, Vector2 pt_j, Vector2 pt_k ) { var a = ExtrudeGeometry.__v1, b = ExtrudeGeometry.__v2, v_hat = ExtrudeGeometry.__v3, w_hat = ExtrudeGeometry.__v4, p = ExtrudeGeometry.__v5, q = ExtrudeGeometry.__v6, v, w, v_dot_w_hat, q_sub_p_dot_w_hat, s, intersection; // good reading for line-line intersection // http://sputsoft.com/blog/2010/03/line-line-intersection.html // define a as vector j->i // define b as vectot k->i a.setValues( pt_i.x - pt_j.x, pt_i.y - pt_j.y ); b.setValues( pt_i.x - pt_k.x, pt_i.y - pt_k.y ); // get unit vectors v = a.normalize(); w = b.normalize(); // normals from pt i v_hat.setValues( -v.y, v.x ); w_hat.setValues( w.y, -w.x ); // pts from i p.setFrom( pt_i ).add( v_hat ); q.setFrom( pt_i ).add( w_hat ); if ( p.x == q.x && p.y == q.y) { // TODO add vector_math ".equals(p, q)" //console.log("Warning: lines are straight"); return w_hat.clone(); } // Points from j, k. helps prevents points cross overover most of the time p.setFrom( pt_j ).add( v_hat ); q.setFrom( pt_k ).add( w_hat ); v_dot_w_hat = v.dot( w_hat ); q_sub_p_dot_w_hat = q.sub( p ).dot( w_hat ); // We should not reach these conditions if ( v_dot_w_hat == 0 ) { print( "Either infinite or no solutions!" ); if ( q_sub_p_dot_w_hat == 0 ) { print( "Its finite solutions." ); } else { print( "Too bad, no solutions." ); } } s = q_sub_p_dot_w_hat / v_dot_w_hat; if ( s < 0 ) { // in case of emergecy, revert to algorithm 1. return _getBevelVec1( pt_i, pt_j, pt_k ); } intersection = v.scale( s ).add( p ); return intersection.sub( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly } static var RAD_TO_DEGREES = 180 / Math.PI; // Algorithm 2 _getBevelVec( pt_i, pt_j, pt_k ) => _getBevelVec2( pt_i, pt_j, pt_k ); _getBevelVec1( pt_i, pt_j, pt_k ) { var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x ); var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x ); if ( anglea > angleb ) { angleb += Math.PI * 2; } var anglec = ( anglea + angleb ) / 2; //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES); var x = - Math.cos( anglec ); var y = - Math.sin( anglec ); var vec = new Vector2( x, y ); //.normalize(); return vec; } _v( x, y, z ) { vertices.add( new Vector3( x, y, z ) ); } // TODO - This is a helper function to reverse a list added by nelsonsilva List _reverse(List list) { List reversed = []; var i = list.length; while (i > 0) { reversed.add(list[--i]); } return reversed; } addShape( Shape shape, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, TubeGeometry frames, material, extrudeMaterial, {ExtrudeGeometryWorldUVGenerator UVGenerator }) { var extrudePts, extrudeByPath = false; //shapebb = shape.getBoundingBox(); // set UV generator var uvgen = (UVGenerator!= null) ? UVGenerator : new ExtrudeGeometryWorldUVGenerator(); TubeGeometry splineTube; Vector3 binormal, normal, position2; var nSteps = (steps is List)? steps.length : steps; if ( extrudePath != null ) { if(steps is List){ List divisions = [0]; divisions.addAll(steps); extrudePts = extrudePath.getUPoints(divisions); } else{ extrudePts = extrudePath.getSpacedPoints( steps ); } extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // Reuse TNB from TubeGeomtry for now. // TODO1 - have a .isClosed in spline? splineTube = (frames != null) ? frames : new TubeGeometry.FrenetFrames(extrudePath, steps, false); // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); binormal = new Vector3.zero(); normal = new Vector3.zero(); position2 = new Vector3.zero(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; } // Variables initalization List<Vector2> ahole; var h, hl; // looping of holes var bevelPoints = []; var shapesOffset = this.vertices.length; if ( bendPath != null ) { shape.addWrapPath( bendPath ); } var shapePoints = shape.extractPoints(); List vertices = shapePoints["shape"]; List<List<Vector2>> holes = shapePoints["holes"]; var reverse = !ShapeUtils.isClockWise( vertices ) ; if ( reverse ) { vertices = _reverse(vertices); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = _reverse(ahole); } } reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! } var faces = ShapeUtils.triangulateShape ( vertices, holes ); //var faces = THREE.Shape.Utils.triangulate2( vertices, holes ); // Would it be better to move points after triangulation? // shapePoints = shape.extractAllPointsWithBend( curveSegments, bendPath ); // vertices = shapePoints.shape; // holes = shapePoints.holes; //console.log(faces); //// /// Handle Vertices //// var contour = vertices; // vertices has all points but contour has only points of circumference for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; vertices = new List.from(vertices); vertices.addAll( ahole ); } var b, bs, t, z, vert, vlen = vertices.length, face, flen = faces.length, cont, clen = contour.length; //------ // Find directions for point movement // var contourMovements = new List(contour.length); num i = 0, il = contour.length, j = il - 1, k = i + 1; for ( i = 0; i < il; i ++ ) { if ( j == il ) j = 0; if ( k == il ) k = 0; // (j)---(i)---(k) // console.log('i,j,k', i, j , k) var pt_i = contour[ i ]; var pt_j = contour[ j ]; var pt_k = contour[ k ]; contourMovements[ i ]= _getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); j ++; k ++; } List holesMovements = [], oneHoleMovements, verticesMovements = new List.from(contourMovements); for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; oneHoleMovements = new List(ahole.length); i = 0; il = ahole.length; j = il - 1; k = i + 1; for ( i = 0; i < il; i++) { if ( j == il ) j = 0; if ( k == il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ]= _getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); j++; k++; } holesMovements.add( oneHoleMovements ); verticesMovements.addAll( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { t = b / bevelSegments; z = bevelThickness * ( 1 - t ); //z = bevelThickness * t; bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved //bs = bevelSize * t ; // linear // contract shape for ( i = 0; i < contour.length; i ++ ) { vert = _scalePt2( contour[ i ], contourMovements[ i ], bs ); //vert = scalePt( contour[ i ], contourCentroid, bs, false ); _v( vert.x, vert.y, - z ); } // expand holes for ( h = 0; h < holes.length; h++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0; i < ahole.length; i++ ) { vert = _scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true ); _v( vert.x, vert.y, -z ); } } } bs = bevelSize; // Back facing vertices for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? _scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( !extrudeByPath ) { _v( vert.x, vert.y, 0.0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.setFrom(splineTube.normals[0]).scale(vert.x); binormal.setFrom(splineTube.binormals[0]).scale(vert.y); position2.setFrom(extrudePts[0]).add(normal).add(binormal); _v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices var s; for ( s = 1; s <= nSteps; s ++ ) { for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? _scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( !extrudeByPath ) { _v( vert.x, vert.y, amount / nSteps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.setFrom(splineTube.normals[s]).scale(vert.x); binormal.setFrom(splineTube.binormals[s]).scale(vert.y); position2.setFrom(extrudePts[s]).add(normal).add(binormal); _v(position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( b = bevelSegments - 1; b >= 0; b -- ) { t = b / bevelSegments; z = bevelThickness * ( 1 - t ); //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; // contract shape for ( i = 0; i < contour.length; i ++ ) { vert = _scalePt2( contour[ i ], contourMovements[ i ], bs ); _v( vert.x, vert.y, amount + z ); } // expand holes for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0; i < ahole.length; i++ ) { vert = _scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( !extrudeByPath ) { _v( vert.x, vert.y, amount + z ); } else { _v( vert.x, vert.y + extrudePts[ nSteps - 1 ].y, extrudePts[ nSteps - 1 ].x + z ); } } } } //// /// Handle Faces //// ///// Internal functions f3( a, b, c, isBottom ) { a += shapesOffset; b += shapesOffset; c += shapesOffset; // normal, color, material this.faces.add( new Face3( a, b, c, null, null, material ) ); var uvs = isBottom ? uvgen.generateBottomUV( this, shape, null, a, b, c) : uvgen.generateTopUV( this, shape, null, a, b, c); this.faceVertexUvs[ 0 ].add(uvs); } f4( a, b, c, d, wallContour, stepIndex, stepsLength ) { a += shapesOffset; b += shapesOffset; c += shapesOffset; d += shapesOffset; this.faces.add( new Face4( a, b, c, d, null, null, extrudeMaterial ) ); var uvs = uvgen.generateSideWallUV( this, shape, wallContour, null, a, b, c, d, stepIndex, stepsLength); this.faceVertexUvs[ 0 ].add(uvs); } // Top and bottom faces //buildLidFaces() { if ( bevelEnabled ) { var layer = 0 ; // steps + 1 var offset = vlen * layer; // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); } layer = nSteps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); } } else { // Bottom faces for ( i = 0; i < flen; i++ ) { face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); } // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + vlen * nSteps, face[ 1 ] + vlen * nSteps, face[ 2 ] + vlen * nSteps, false ); } } sidewalls( contour, layeroffset ) { var i, j, k; i = contour.length; while ( --i >= 0 ) { j = i; k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log('b', i,j, i-1, k,vertices.length); var s = 0, sl = nSteps + bevelSegments * 2; for ( s = 0; s < sl; s ++ ) { var slen1 = vlen * s; var slen2 = vlen * ( s + 1 ); var a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d, contour, s, sl ); } } } // Sides faces //buildSideFaces() { // Create faces for the z-sides of the shape var layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } } static Vector2 ___v1 = null; static get __v1 => (___v1 == null)? ___v1 = new Vector2.zero() : ___v1; static Vector2 ___v2 = null; static get __v2 => (___v2 == null)? ___v2 = new Vector2.zero() : ___v2; static Vector2 ___v3 = null; static get __v3 => (___v3 == null)? ___v3 = new Vector2.zero() : ___v3; static Vector2 ___v4 = null; static get __v4 => (___v4 == null)? ___v4 = new Vector2.zero() : ___v4; static Vector2 ___v5 = null; static get __v5 => (___v5 == null)? ___v5 = new Vector2.zero() : ___v5; static Vector2 ___v6 = null; static get __v6 => (___v6 == null)? ___v6 = new Vector2.zero() : ___v6; }
Extends
Geometry > ExtrudeGeometry
Subclasses
Static Properties
var RAD_TO_DEGREES #
static var RAD_TO_DEGREES = 180 / Math.PI
Constructors
new ExtrudeGeometry(List shapes, {amount: 100, bevelThickness: 6.0, bevelSize: null, bevelSegments: 3, bevelEnabled: true, curveSegments: 12, steps: 1, bendPath, extrudePath, frames, material, extrudeMaterial}) #
Creates a new Object instance.
Object instances have no meaningful state, and are only useful through their identity. An Object instance is equal to itself only.
ExtrudeGeometry( this.shapes, { amount: 100, bevelThickness: 6.0, bevelSize: null, bevelSegments: 3, bevelEnabled: true, curveSegments: 12, steps: 1, //can assume a number of steps or a List with all U's of steps bendPath, extrudePath, frames, material, extrudeMaterial } ) : super() { if (bevelSize == null) bevelSize = bevelThickness - 2.0; if (shapes == null) { shapes = []; return; } shapebb = shapes.last.getBoundingBox(); addShapeList( shapes, amount, bevelThickness, bevelSize, bevelSegments,bevelEnabled, curveSegments, steps, bendPath, extrudePath, frames, material, extrudeMaterial ); computeCentroids(); computeFaceNormals(); // can't really use automatic vertex normals // as then front and back sides get smoothed too // should do separate smoothing just for sides //this.computeVertexNormals(); //console.log( "took", ( Date.now() - startTime ) ); }
Properties
bool isDynamic #
bool get isDynamic => _dynamic;
set isDynamic(bool value) => _dynamic = value;
var shapebb #
var shapebb
Operators
Methods
dynamic addShape(Shape shape, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, TubeGeometry frames, material, extrudeMaterial, {ExtrudeGeometryWorldUVGenerator UVGenerator}) #
addShape( Shape shape, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, TubeGeometry frames, material, extrudeMaterial, {ExtrudeGeometryWorldUVGenerator UVGenerator }) { var extrudePts, extrudeByPath = false; //shapebb = shape.getBoundingBox(); // set UV generator var uvgen = (UVGenerator!= null) ? UVGenerator : new ExtrudeGeometryWorldUVGenerator(); TubeGeometry splineTube; Vector3 binormal, normal, position2; var nSteps = (steps is List)? steps.length : steps; if ( extrudePath != null ) { if(steps is List){ List divisions = [0]; divisions.addAll(steps); extrudePts = extrudePath.getUPoints(divisions); } else{ extrudePts = extrudePath.getSpacedPoints( steps ); } extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // Reuse TNB from TubeGeomtry for now. // TODO1 - have a .isClosed in spline? splineTube = (frames != null) ? frames : new TubeGeometry.FrenetFrames(extrudePath, steps, false); // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); binormal = new Vector3.zero(); normal = new Vector3.zero(); position2 = new Vector3.zero(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; } // Variables initalization List<Vector2> ahole; var h, hl; // looping of holes var bevelPoints = []; var shapesOffset = this.vertices.length; if ( bendPath != null ) { shape.addWrapPath( bendPath ); } var shapePoints = shape.extractPoints(); List vertices = shapePoints["shape"]; List<List<Vector2>> holes = shapePoints["holes"]; var reverse = !ShapeUtils.isClockWise( vertices ) ; if ( reverse ) { vertices = _reverse(vertices); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = _reverse(ahole); } } reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! } var faces = ShapeUtils.triangulateShape ( vertices, holes ); //var faces = THREE.Shape.Utils.triangulate2( vertices, holes ); // Would it be better to move points after triangulation? // shapePoints = shape.extractAllPointsWithBend( curveSegments, bendPath ); // vertices = shapePoints.shape; // holes = shapePoints.holes; //console.log(faces); //// /// Handle Vertices //// var contour = vertices; // vertices has all points but contour has only points of circumference for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; vertices = new List.from(vertices); vertices.addAll( ahole ); } var b, bs, t, z, vert, vlen = vertices.length, face, flen = faces.length, cont, clen = contour.length; //------ // Find directions for point movement // var contourMovements = new List(contour.length); num i = 0, il = contour.length, j = il - 1, k = i + 1; for ( i = 0; i < il; i ++ ) { if ( j == il ) j = 0; if ( k == il ) k = 0; // (j)---(i)---(k) // console.log('i,j,k', i, j , k) var pt_i = contour[ i ]; var pt_j = contour[ j ]; var pt_k = contour[ k ]; contourMovements[ i ]= _getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); j ++; k ++; } List holesMovements = [], oneHoleMovements, verticesMovements = new List.from(contourMovements); for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; oneHoleMovements = new List(ahole.length); i = 0; il = ahole.length; j = il - 1; k = i + 1; for ( i = 0; i < il; i++) { if ( j == il ) j = 0; if ( k == il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ]= _getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); j++; k++; } holesMovements.add( oneHoleMovements ); verticesMovements.addAll( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { t = b / bevelSegments; z = bevelThickness * ( 1 - t ); //z = bevelThickness * t; bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved //bs = bevelSize * t ; // linear // contract shape for ( i = 0; i < contour.length; i ++ ) { vert = _scalePt2( contour[ i ], contourMovements[ i ], bs ); //vert = scalePt( contour[ i ], contourCentroid, bs, false ); _v( vert.x, vert.y, - z ); } // expand holes for ( h = 0; h < holes.length; h++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0; i < ahole.length; i++ ) { vert = _scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true ); _v( vert.x, vert.y, -z ); } } } bs = bevelSize; // Back facing vertices for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? _scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( !extrudeByPath ) { _v( vert.x, vert.y, 0.0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.setFrom(splineTube.normals[0]).scale(vert.x); binormal.setFrom(splineTube.binormals[0]).scale(vert.y); position2.setFrom(extrudePts[0]).add(normal).add(binormal); _v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices var s; for ( s = 1; s <= nSteps; s ++ ) { for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? _scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( !extrudeByPath ) { _v( vert.x, vert.y, amount / nSteps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.setFrom(splineTube.normals[s]).scale(vert.x); binormal.setFrom(splineTube.binormals[s]).scale(vert.y); position2.setFrom(extrudePts[s]).add(normal).add(binormal); _v(position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( b = bevelSegments - 1; b >= 0; b -- ) { t = b / bevelSegments; z = bevelThickness * ( 1 - t ); //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; // contract shape for ( i = 0; i < contour.length; i ++ ) { vert = _scalePt2( contour[ i ], contourMovements[ i ], bs ); _v( vert.x, vert.y, amount + z ); } // expand holes for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0; i < ahole.length; i++ ) { vert = _scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( !extrudeByPath ) { _v( vert.x, vert.y, amount + z ); } else { _v( vert.x, vert.y + extrudePts[ nSteps - 1 ].y, extrudePts[ nSteps - 1 ].x + z ); } } } } //// /// Handle Faces //// ///// Internal functions f3( a, b, c, isBottom ) { a += shapesOffset; b += shapesOffset; c += shapesOffset; // normal, color, material this.faces.add( new Face3( a, b, c, null, null, material ) ); var uvs = isBottom ? uvgen.generateBottomUV( this, shape, null, a, b, c) : uvgen.generateTopUV( this, shape, null, a, b, c); this.faceVertexUvs[ 0 ].add(uvs); } f4( a, b, c, d, wallContour, stepIndex, stepsLength ) { a += shapesOffset; b += shapesOffset; c += shapesOffset; d += shapesOffset; this.faces.add( new Face4( a, b, c, d, null, null, extrudeMaterial ) ); var uvs = uvgen.generateSideWallUV( this, shape, wallContour, null, a, b, c, d, stepIndex, stepsLength); this.faceVertexUvs[ 0 ].add(uvs); } // Top and bottom faces //buildLidFaces() { if ( bevelEnabled ) { var layer = 0 ; // steps + 1 var offset = vlen * layer; // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); } layer = nSteps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); } } else { // Bottom faces for ( i = 0; i < flen; i++ ) { face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); } // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + vlen * nSteps, face[ 1 ] + vlen * nSteps, face[ 2 ] + vlen * nSteps, false ); } } sidewalls( contour, layeroffset ) { var i, j, k; i = contour.length; while ( --i >= 0 ) { j = i; k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log('b', i,j, i-1, k,vertices.length); var s = 0, sl = nSteps + bevelSegments * 2; for ( s = 0; s < sl; s ++ ) { var slen1 = vlen * s; var slen2 = vlen * ( s + 1 ); var a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d, contour, s, sl ); } } } // Sides faces //buildSideFaces() { // Create faces for the z-sides of the shape var layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } }
dynamic addShapeList(shapes, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, frames, material, extrudeMaterial) #
addShapeList(shapes, amount, bevelThickness, bevelSize, bevelSegments,bevelEnabled, curveSegments,steps,bendPath,extrudePath,frames,material, extrudeMaterial) { var sl = shapes.length; for ( var s = 0; s < sl; s ++ ) { var shape = shapes[ s ]; addShape( shape, amount, bevelThickness, bevelSize, bevelSegments,bevelEnabled, curveSegments, steps, bendPath, extrudePath, frames, material, extrudeMaterial ); } }
void applyMatrix(Matrix4 matrix) #
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 computeBoundingBox() #
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() #
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() #
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() #
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() #
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() #
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() #
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; }