/** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ THREE.GeometryUtils = { // Merge two geometries or geometry and geometry from object (using object's transform) merge: function ( geometry1, object2 /* mesh | geometry */ ) { var matrix, matrixRotation, vertexOffset = geometry1.vertices.length, uvPosition = geometry1.faceVertexUvs[ 0 ].length, geometry2 = object2 instanceof THREE.Mesh ? object2.geometry : object2, vertices1 = geometry1.vertices, vertices2 = geometry2.vertices, faces1 = geometry1.faces, faces2 = geometry2.faces, uvs1 = geometry1.faceVertexUvs[ 0 ], uvs2 = geometry2.faceVertexUvs[ 0 ]; if ( object2 instanceof THREE.Mesh ) { object2.matrixAutoUpdate && object2.updateMatrix(); matrix = object2.matrix; matrixRotation = new THREE.Matrix4(); matrixRotation.extractRotation( matrix, object2.scale ); } // vertices for ( var i = 0, il = vertices2.length; i < il; i ++ ) { var vertex = vertices2[ i ]; var vertexCopy = vertex.clone(); if ( matrix ) matrix.multiplyVector3( vertexCopy ); vertices1.push( vertexCopy ); } // faces for ( i = 0, il = faces2.length; i < il; i ++ ) { var face = faces2[ i ], faceCopy, normal, color, faceVertexNormals = face.vertexNormals, faceVertexColors = face.vertexColors; if ( face instanceof THREE.Face3 ) { faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); } else if ( face instanceof THREE.Face4 ) { faceCopy = new THREE.Face4( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset, face.d + vertexOffset ); } faceCopy.normal.copy( face.normal ); if ( matrixRotation ) matrixRotation.multiplyVector3( faceCopy.normal ); for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { normal = faceVertexNormals[ j ].clone(); if ( matrixRotation ) matrixRotation.multiplyVector3( normal ); faceCopy.vertexNormals.push( normal ); } faceCopy.color.copy( face.color ); for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { color = faceVertexColors[ j ]; faceCopy.vertexColors.push( color.clone() ); } faceCopy.materialIndex = face.materialIndex; faceCopy.centroid.copy( face.centroid ); if ( matrix ) matrix.multiplyVector3( faceCopy.centroid ); faces1.push( faceCopy ); } // uvs for ( i = 0, il = uvs2.length; i < il; i ++ ) { var uv = uvs2[ i ], uvCopy = []; for ( var j = 0, jl = uv.length; j < jl; j ++ ) { uvCopy.push( new THREE.UV( uv[ j ].u, uv[ j ].v ) ); } uvs1.push( uvCopy ); } }, removeMaterials: function ( geometry, materialIndexArray ) { var materialIndexMap = {}; for ( var i = 0, il = materialIndexArray.length; i < il; i ++ ) { materialIndexMap[ materialIndexArray[i] ] = true; } var face, newFaces = []; for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) { face = geometry.faces[ i ]; if ( ! ( face.materialIndex in materialIndexMap ) ) newFaces.push( face ); } geometry.faces = newFaces; }, // Get random point in triangle (via barycentric coordinates) // (uniform distribution) // http://www.cgafaq.info/wiki/Random_Point_In_Triangle randomPointInTriangle: function ( vectorA, vectorB, vectorC ) { var a, b, c, point = new THREE.Vector3(), tmp = THREE.GeometryUtils.__v1; a = THREE.GeometryUtils.random(); b = THREE.GeometryUtils.random(); if ( ( a + b ) > 1 ) { a = 1 - a; b = 1 - b; } c = 1 - a - b; point.copy( vectorA ); point.multiplyScalar( a ); tmp.copy( vectorB ); tmp.multiplyScalar( b ); point.addSelf( tmp ); tmp.copy( vectorC ); tmp.multiplyScalar( c ); point.addSelf( tmp ); return point; }, // Get random point in face (triangle / quad) // (uniform distribution) randomPointInFace: function ( face, geometry, useCachedAreas ) { var vA, vB, vC, vD; if ( face instanceof THREE.Face3 ) { vA = geometry.vertices[ face.a ]; vB = geometry.vertices[ face.b ]; vC = geometry.vertices[ face.c ]; return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC ); } else if ( face instanceof THREE.Face4 ) { vA = geometry.vertices[ face.a ]; vB = geometry.vertices[ face.b ]; vC = geometry.vertices[ face.c ]; vD = geometry.vertices[ face.d ]; var area1, area2; if ( useCachedAreas ) { if ( face._area1 && face._area2 ) { area1 = face._area1; area2 = face._area2; } else { area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ); area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); face._area1 = area1; face._area2 = area2; } } else { area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ), area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); } var r = THREE.GeometryUtils.random() * ( area1 + area2 ); if ( r < area1 ) { return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vD ); } else { return THREE.GeometryUtils.randomPointInTriangle( vB, vC, vD ); } } }, // Get uniformly distributed random points in mesh // - create array with cumulative sums of face areas // - pick random number from 0 to total area // - find corresponding place in area array by binary search // - get random point in face randomPointsInGeometry: function ( geometry, n ) { var face, i, faces = geometry.faces, vertices = geometry.vertices, il = faces.length, totalArea = 0, cumulativeAreas = [], vA, vB, vC, vD; // precompute face areas for ( i = 0; i < il; i ++ ) { face = faces[ i ]; if ( face instanceof THREE.Face3 ) { vA = vertices[ face.a ]; vB = vertices[ face.b ]; vC = vertices[ face.c ]; face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC ); } else if ( face instanceof THREE.Face4 ) { vA = vertices[ face.a ]; vB = vertices[ face.b ]; vC = vertices[ face.c ]; vD = vertices[ face.d ]; face._area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ); face._area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); face._area = face._area1 + face._area2; } totalArea += face._area; cumulativeAreas[ i ] = totalArea; } // binary search cumulative areas array function binarySearchIndices( value ) { function binarySearch( start, end ) { // return closest larger index // if exact number is not found if ( end < start ) return start; var mid = start + Math.floor( ( end - start ) / 2 ); if ( cumulativeAreas[ mid ] > value ) { return binarySearch( start, mid - 1 ); } else if ( cumulativeAreas[ mid ] < value ) { return binarySearch( mid + 1, end ); } else { return mid; } } var result = binarySearch( 0, cumulativeAreas.length - 1 ) return result; } // pick random face weighted by face area var r, index, result = []; var stats = {}; for ( i = 0; i < n; i ++ ) { r = THREE.GeometryUtils.random() * totalArea; index = binarySearchIndices( r ); result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true ); if ( ! stats[ index ] ) { stats[ index ] = 1; } else { stats[ index ] += 1; } } return result; }, // Get triangle area (by Heron's formula) // http://en.wikipedia.org/wiki/Heron%27s_formula triangleArea: function ( vectorA, vectorB, vectorC ) { var s, a, b, c, tmp = THREE.GeometryUtils.__v1; tmp.sub( vectorA, vectorB ); a = tmp.length(); tmp.sub( vectorA, vectorC ); b = tmp.length(); tmp.sub( vectorB, vectorC ); c = tmp.length(); s = 0.5 * ( a + b + c ); return Math.sqrt( s * ( s - a ) * ( s - b ) * ( s - c ) ); }, // Center geometry so that 0,0,0 is in center of bounding box center: function ( geometry ) { geometry.computeBoundingBox(); var bb = geometry.boundingBox; var offset = new THREE.Vector3(); offset.add( bb.min, bb.max ); offset.multiplyScalar( -0.5 ); geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); geometry.computeBoundingBox(); return offset; }, // Normalize UVs to be from <0,1> // (for now just the first set of UVs) normalizeUVs: function ( geometry ) { var uvSet = geometry.faceVertexUvs[ 0 ]; for ( var i = 0, il = uvSet.length; i < il; i ++ ) { var uvs = uvSet[ i ]; for ( var j = 0, jl = uvs.length; j < jl; j ++ ) { // texture repeat if( uvs[ j ].u !== 1.0 ) uvs[ j ].u = uvs[ j ].u - Math.floor( uvs[ j ].u ); if( uvs[ j ].v !== 1.0 ) uvs[ j ].v = uvs[ j ].v - Math.floor( uvs[ j ].v ); } } }, triangulateQuads: function ( geometry ) { var i, il, j, jl; var faces = []; var faceUvs = []; var faceVertexUvs = []; for ( i = 0, il = geometry.faceUvs.length; i < il; i ++ ) { faceUvs[ i ] = []; } for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { faceVertexUvs[ i ] = []; } for ( i = 0, il = geometry.faces.length; i < il; i ++ ) { var face = geometry.faces[ i ]; if ( face instanceof THREE.Face4 ) { var a = face.a; var b = face.b; var c = face.c; var d = face.d; var triA = new THREE.Face3(); var triB = new THREE.Face3(); triA.color.copy( face.color ); triB.color.copy( face.color ); triA.materialIndex = face.materialIndex; triB.materialIndex = face.materialIndex; triA.a = a; triA.b = b; triA.c = d; triB.a = b; triB.b = c; triB.c = d; if ( face.vertexColors.length === 4 ) { triA.vertexColors[ 0 ] = face.vertexColors[ 0 ].clone(); triA.vertexColors[ 1 ] = face.vertexColors[ 1 ].clone(); triA.vertexColors[ 2 ] = face.vertexColors[ 3 ].clone(); triB.vertexColors[ 0 ] = face.vertexColors[ 1 ].clone(); triB.vertexColors[ 1 ] = face.vertexColors[ 2 ].clone(); triB.vertexColors[ 2 ] = face.vertexColors[ 3 ].clone(); } faces.push( triA, triB ); for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { if ( geometry.faceVertexUvs[ j ].length ) { var uvs = geometry.faceVertexUvs[ j ][ i ]; var uvA = uvs[ 0 ]; var uvB = uvs[ 1 ]; var uvC = uvs[ 2 ]; var uvD = uvs[ 3 ]; var uvsTriA = [ uvA.clone(), uvB.clone(), uvD.clone() ]; var uvsTriB = [ uvB.clone(), uvC.clone(), uvD.clone() ]; faceVertexUvs[ j ].push( uvsTriA, uvsTriB ); } } for ( j = 0, jl = geometry.faceUvs.length; j < jl; j ++ ) { if ( geometry.faceUvs[ j ].length ) { var faceUv = geometry.faceUvs[ j ][ i ]; faceUvs[ j ].push( faceUv, faceUv ); } } } else { faces.push( face ); for ( j = 0, jl = geometry.faceUvs.length; j < jl; j ++ ) { faceUvs[ j ].push( geometry.faceUvs[ j ][ i ] ); } for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); } } } geometry.faces = faces; geometry.faceUvs = faceUvs; geometry.faceVertexUvs = faceVertexUvs; geometry.computeCentroids(); geometry.computeFaceNormals(); geometry.computeVertexNormals(); if ( geometry.hasTangents ) geometry.computeTangents(); }, // Make all faces use unique vertices // so that each face can be separated from others explode: function( geometry ) { var vertices = []; for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) { var n = vertices.length; var face = geometry.faces[ i ]; if ( face instanceof THREE.Face4 ) { var a = face.a; var b = face.b; var c = face.c; var d = face.d; var va = geometry.vertices[ a ]; var vb = geometry.vertices[ b ]; var vc = geometry.vertices[ c ]; var vd = geometry.vertices[ d ]; vertices.push( va.clone() ); vertices.push( vb.clone() ); vertices.push( vc.clone() ); vertices.push( vd.clone() ); face.a = n; face.b = n + 1; face.c = n + 2; face.d = n + 3; } else { var a = face.a; var b = face.b; var c = face.c; var va = geometry.vertices[ a ]; var vb = geometry.vertices[ b ]; var vc = geometry.vertices[ c ]; vertices.push( va.clone() ); vertices.push( vb.clone() ); vertices.push( vc.clone() ); face.a = n; face.b = n + 1; face.c = n + 2; } } geometry.vertices = vertices; delete geometry.__tmpVertices; }, // Break faces with edges longer than maxEdgeLength // - not recursive tessellate: function ( geometry, maxEdgeLength ) { var i, il, face, a, b, c, d, va, vb, vc, vd, dab, dbc, dac, dcd, dad, m, m1, m2, vm, vm1, vm2, vnm, vnm1, vnm2, vcm, vcm1, vcm2, triA, triB, quadA, quadB, edge; var faces = []; var faceVertexUvs = []; for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { faceVertexUvs[ i ] = []; } for ( i = 0, il = geometry.faces.length; i < il; i ++ ) { face = geometry.faces[ i ]; if ( face instanceof THREE.Face3 ) { a = face.a; b = face.b; c = face.c; va = geometry.vertices[ a ]; vb = geometry.vertices[ b ]; vc = geometry.vertices[ c ]; dab = va.distanceTo( vb ); dbc = vb.distanceTo( vc ); dac = va.distanceTo( vc ); if ( dab > maxEdgeLength || dbc > maxEdgeLength || dac > maxEdgeLength ) { m = geometry.vertices.length; triA = face.clone(); triB = face.clone(); if ( dab >= dbc && dab >= dac ) { vm = va.clone(); vm.lerpSelf( vb, 0.5 ); triA.a = a; triA.b = m; triA.c = c; triB.a = m; triB.b = b; triB.c = c; if ( face.vertexNormals.length === 3 ) { vnm = face.vertexNormals[ 0 ].clone(); vnm.lerpSelf( face.vertexNormals[ 1 ], 0.5 ); triA.vertexNormals[ 1 ].copy( vnm ); triB.vertexNormals[ 0 ].copy( vnm ); } if ( face.vertexColors.length === 3 ) { vcm = face.vertexColors[ 0 ].clone(); vcm.lerpSelf( face.vertexColors[ 1 ], 0.5 ); triA.vertexColors[ 1 ].copy( vcm ); triB.vertexColors[ 0 ].copy( vcm ); } edge = 0; } else if ( dbc >= dab && dbc >= dac ) { vm = vb.clone(); vm.lerpSelf( vc, 0.5 ); triA.a = a; triA.b = b; triA.c = m; triB.a = m; triB.b = c; triB.c = a; if ( face.vertexNormals.length === 3 ) { vnm = face.vertexNormals[ 1 ].clone(); vnm.lerpSelf( face.vertexNormals[ 2 ], 0.5 ); triA.vertexNormals[ 2 ].copy( vnm ); triB.vertexNormals[ 0 ].copy( vnm ); triB.vertexNormals[ 1 ].copy( face.vertexNormals[ 2 ] ); triB.vertexNormals[ 2 ].copy( face.vertexNormals[ 0 ] ); } if ( face.vertexColors.length === 3 ) { vcm = face.vertexColors[ 1 ].clone(); vcm.lerpSelf( face.vertexColors[ 2 ], 0.5 ); triA.vertexColors[ 2 ].copy( vcm ); triB.vertexColors[ 0 ].copy( vcm ); triB.vertexColors[ 1 ].copy( face.vertexColors[ 2 ] ); triB.vertexColors[ 2 ].copy( face.vertexColors[ 0 ] ); } edge = 1; } else { vm = va.clone(); vm.lerpSelf( vc, 0.5 ); triA.a = a; triA.b = b; triA.c = m; triB.a = m; triB.b = b; triB.c = c; if ( face.vertexNormals.length === 3 ) { vnm = face.vertexNormals[ 0 ].clone(); vnm.lerpSelf( face.vertexNormals[ 2 ], 0.5 ); triA.vertexNormals[ 2 ].copy( vnm ); triB.vertexNormals[ 0 ].copy( vnm ); } if ( face.vertexColors.length === 3 ) { vcm = face.vertexColors[ 0 ].clone(); vcm.lerpSelf( face.vertexColors[ 2 ], 0.5 ); triA.vertexColors[ 2 ].copy( vcm ); triB.vertexColors[ 0 ].copy( vcm ); } edge = 2; } faces.push( triA, triB ); geometry.vertices.push( vm ); var j, jl, uvs, uvA, uvB, uvC, uvM, uvsTriA, uvsTriB; for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { if ( geometry.faceVertexUvs[ j ].length ) { uvs = geometry.faceVertexUvs[ j ][ i ]; uvA = uvs[ 0 ]; uvB = uvs[ 1 ]; uvC = uvs[ 2 ]; // AB if ( edge === 0 ) { uvM = uvA.clone(); uvM.lerpSelf( uvB, 0.5 ); uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ]; uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ]; // BC } else if ( edge === 1 ) { uvM = uvB.clone(); uvM.lerpSelf( uvC, 0.5 ); uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ]; uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ]; // AC } else { uvM = uvA.clone(); uvM.lerpSelf( uvC, 0.5 ); uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ]; uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ]; } faceVertexUvs[ j ].push( uvsTriA, uvsTriB ); } } } else { faces.push( face ); for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); } } } else { a = face.a; b = face.b; c = face.c; d = face.d; va = geometry.vertices[ a ]; vb = geometry.vertices[ b ]; vc = geometry.vertices[ c ]; vd = geometry.vertices[ d ]; dab = va.distanceTo( vb ); dbc = vb.distanceTo( vc ); dcd = vc.distanceTo( vd ); dad = va.distanceTo( vd ); if ( dab > maxEdgeLength || dbc > maxEdgeLength || dcd > maxEdgeLength || dad > maxEdgeLength ) { m1 = geometry.vertices.length; m2 = geometry.vertices.length + 1; quadA = face.clone(); quadB = face.clone(); if ( ( dab >= dbc && dab >= dcd && dab >= dad ) || ( dcd >= dbc && dcd >= dab && dcd >= dad ) ) { vm1 = va.clone(); vm1.lerpSelf( vb, 0.5 ); vm2 = vc.clone(); vm2.lerpSelf( vd, 0.5 ); quadA.a = a; quadA.b = m1; quadA.c = m2; quadA.d = d; quadB.a = m1; quadB.b = b; quadB.c = c; quadB.d = m2; if ( face.vertexNormals.length === 4 ) { vnm1 = face.vertexNormals[ 0 ].clone(); vnm1.lerpSelf( face.vertexNormals[ 1 ], 0.5 ); vnm2 = face.vertexNormals[ 2 ].clone(); vnm2.lerpSelf( face.vertexNormals[ 3 ], 0.5 ); quadA.vertexNormals[ 1 ].copy( vnm1 ); quadA.vertexNormals[ 2 ].copy( vnm2 ); quadB.vertexNormals[ 0 ].copy( vnm1 ); quadB.vertexNormals[ 3 ].copy( vnm2 ); } if ( face.vertexColors.length === 4 ) { vcm1 = face.vertexColors[ 0 ].clone(); vcm1.lerpSelf( face.vertexColors[ 1 ], 0.5 ); vcm2 = face.vertexColors[ 2 ].clone(); vcm2.lerpSelf( face.vertexColors[ 3 ], 0.5 ); quadA.vertexColors[ 1 ].copy( vcm1 ); quadA.vertexColors[ 2 ].copy( vcm2 ); quadB.vertexColors[ 0 ].copy( vcm1 ); quadB.vertexColors[ 3 ].copy( vcm2 ); } edge = 0; } else { vm1 = vb.clone(); vm1.lerpSelf( vc, 0.5 ); vm2 = vd.clone(); vm2.lerpSelf( va, 0.5 ); quadA.a = a; quadA.b = b; quadA.c = m1; quadA.d = m2; quadB.a = m2; quadB.b = m1; quadB.c = c; quadB.d = d; if ( face.vertexNormals.length === 4 ) { vnm1 = face.vertexNormals[ 1 ].clone(); vnm1.lerpSelf( face.vertexNormals[ 2 ], 0.5 ); vnm2 = face.vertexNormals[ 3 ].clone(); vnm2.lerpSelf( face.vertexNormals[ 0 ], 0.5 ); quadA.vertexNormals[ 2 ].copy( vnm1 ); quadA.vertexNormals[ 3 ].copy( vnm2 ); quadB.vertexNormals[ 0 ].copy( vnm2 ); quadB.vertexNormals[ 1 ].copy( vnm1 ); } if ( face.vertexColors.length === 4 ) { vcm1 = face.vertexColors[ 1 ].clone(); vcm1.lerpSelf( face.vertexColors[ 2 ], 0.5 ); vcm2 = face.vertexColors[ 3 ].clone(); vcm2.lerpSelf( face.vertexColors[ 0 ], 0.5 ); quadA.vertexColors[ 2 ].copy( vcm1 ); quadA.vertexColors[ 3 ].copy( vcm2 ); quadB.vertexColors[ 0 ].copy( vcm2 ); quadB.vertexColors[ 1 ].copy( vcm1 ); } edge = 1; } faces.push( quadA, quadB ); geometry.vertices.push( vm1, vm2 ); var j, jl, uvs, uvA, uvB, uvC, uvD, uvM1, uvM2, uvsQuadA, uvsQuadB; for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { if ( geometry.faceVertexUvs[ j ].length ) { uvs = geometry.faceVertexUvs[ j ][ i ]; uvA = uvs[ 0 ]; uvB = uvs[ 1 ]; uvC = uvs[ 2 ]; uvD = uvs[ 3 ]; // AB + CD if ( edge === 0 ) { uvM1 = uvA.clone(); uvM1.lerpSelf( uvB, 0.5 ); uvM2 = uvC.clone(); uvM2.lerpSelf( uvD, 0.5 ); uvsQuadA = [ uvA.clone(), uvM1.clone(), uvM2.clone(), uvD.clone() ]; uvsQuadB = [ uvM1.clone(), uvB.clone(), uvC.clone(), uvM2.clone() ]; // BC + AD } else { uvM1 = uvB.clone(); uvM1.lerpSelf( uvC, 0.5 ); uvM2 = uvD.clone(); uvM2.lerpSelf( uvA, 0.5 ); uvsQuadA = [ uvA.clone(), uvB.clone(), uvM1.clone(), uvM2.clone() ]; uvsQuadB = [ uvM2.clone(), uvM1.clone(), uvC.clone(), uvD.clone() ]; } faceVertexUvs[ j ].push( uvsQuadA, uvsQuadB ); } } } else { faces.push( face ); for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); } } } } geometry.faces = faces; geometry.faceVertexUvs = faceVertexUvs; } }; THREE.GeometryUtils.random = THREE.Math.random16; THREE.GeometryUtils.__v1 = new THREE.Vector3();