Created
June 1, 2021 02:12
-
-
Save zadvorsky/8c653fd4ffadaa05205e301d8e05b1b4 to your computer and use it in GitHub Desktop.
Three.js deprecated `Geometry` in favor of `BufferGeometry` some time ago, though they still offer the `Geometry` class as an external ES6 module. This is that same class, but modified to use the global THREE namespace, so it can be included in a regular script tag for older projects without a build step. Once included, the script defines `THREE…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const _m1 = new THREE.Matrix4(); | |
const _obj = new THREE.Object3D(); | |
const _offset = new THREE.Vector3(); | |
class Geometry extends THREE.EventDispatcher { | |
constructor() { | |
super(); | |
this.uuid = THREE.MathUtils.generateUUID(); | |
this.name = ''; | |
this.type = 'Geometry'; | |
this.vertices = []; | |
this.colors = []; | |
this.faces = []; | |
this.faceVertexUvs = [[]]; | |
this.morphTargets = []; | |
this.morphNormals = []; | |
this.skinWeights = []; | |
this.skinIndices = []; | |
this.lineDistances = []; | |
this.boundingBox = null; | |
this.boundingSphere = null; | |
// update flags | |
this.elementsNeedUpdate = false; | |
this.verticesNeedUpdate = false; | |
this.uvsNeedUpdate = false; | |
this.normalsNeedUpdate = false; | |
this.colorsNeedUpdate = false; | |
this.lineDistancesNeedUpdate = false; | |
this.groupsNeedUpdate = false; | |
} | |
applyMatrix4( matrix ) { | |
const normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); | |
for ( let i = 0, il = this.vertices.length; i < il; i ++ ) { | |
const vertex = this.vertices[ i ]; | |
vertex.applyMatrix4( matrix ); | |
} | |
for ( let i = 0, il = this.faces.length; i < il; i ++ ) { | |
const face = this.faces[ i ]; | |
face.normal.applyMatrix3( normalMatrix ).normalize(); | |
for ( let j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { | |
face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); | |
} | |
} | |
if ( this.boundingBox !== null ) { | |
this.computeBoundingBox(); | |
} | |
if ( this.boundingSphere !== null ) { | |
this.computeBoundingSphere(); | |
} | |
this.verticesNeedUpdate = true; | |
this.normalsNeedUpdate = true; | |
return this; | |
} | |
rotateX( angle ) { | |
// rotate geometry around world x-axis | |
_m1.makeRotationX( angle ); | |
this.applyMatrix4( _m1 ); | |
return this; | |
} | |
rotateY( angle ) { | |
// rotate geometry around world y-axis | |
_m1.makeRotationY( angle ); | |
this.applyMatrix4( _m1 ); | |
return this; | |
} | |
rotateZ( angle ) { | |
// rotate geometry around world z-axis | |
_m1.makeRotationZ( angle ); | |
this.applyMatrix4( _m1 ); | |
return this; | |
} | |
translate( x, y, z ) { | |
// translate geometry | |
_m1.makeTranslation( x, y, z ); | |
this.applyMatrix4( _m1 ); | |
return this; | |
} | |
scale( x, y, z ) { | |
// scale geometry | |
_m1.makeScale( x, y, z ); | |
this.applyMatrix4( _m1 ); | |
return this; | |
} | |
lookAt( vector ) { | |
_obj.lookAt( vector ); | |
_obj.updateMatrix(); | |
this.applyMatrix4( _obj.matrix ); | |
return this; | |
} | |
fromBufferGeometry( geometry ) { | |
const scope = this; | |
const index = geometry.index !== null ? geometry.index : undefined; | |
const attributes = geometry.attributes; | |
if ( attributes.position === undefined ) { | |
console.error( 'THREE.Geometry.fromBufferGeometry(): Position attribute required for conversion.' ); | |
return this; | |
} | |
const position = attributes.position; | |
const normal = attributes.normal; | |
const color = attributes.color; | |
const uv = attributes.uv; | |
const uv2 = attributes.uv2; | |
if ( uv2 !== undefined ) this.faceVertexUvs[ 1 ] = []; | |
for ( let i = 0; i < position.count; i ++ ) { | |
scope.vertices.push( new THREE.Vector3().fromBufferAttribute( position, i ) ); | |
if ( color !== undefined ) { | |
scope.colors.push( new THREE.Color().fromBufferAttribute( color, i ) ); | |
} | |
} | |
function addFace( a, b, c, materialIndex ) { | |
const vertexColors = ( color === undefined ) ? [] : [ | |
scope.colors[ a ].clone(), | |
scope.colors[ b ].clone(), | |
scope.colors[ c ].clone() | |
]; | |
const vertexNormals = ( normal === undefined ) ? [] : [ | |
new THREE.Vector3().fromBufferAttribute( normal, a ), | |
new THREE.Vector3().fromBufferAttribute( normal, b ), | |
new THREE.Vector3().fromBufferAttribute( normal, c ) | |
]; | |
const face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); | |
scope.faces.push( face ); | |
if ( uv !== undefined ) { | |
scope.faceVertexUvs[ 0 ].push( [ | |
new THREE.Vector2().fromBufferAttribute( uv, a ), | |
new THREE.Vector2().fromBufferAttribute( uv, b ), | |
new THREE.Vector2().fromBufferAttribute( uv, c ) | |
] ); | |
} | |
if ( uv2 !== undefined ) { | |
scope.faceVertexUvs[ 1 ].push( [ | |
new THREE.Vector2().fromBufferAttribute( uv2, a ), | |
new THREE.Vector2().fromBufferAttribute( uv2, b ), | |
new THREE.Vector2().fromBufferAttribute( uv2, c ) | |
] ); | |
} | |
} | |
const groups = geometry.groups; | |
if ( groups.length > 0 ) { | |
for ( let i = 0; i < groups.length; i ++ ) { | |
const group = groups[ i ]; | |
const start = group.start; | |
const count = group.count; | |
for ( let j = start, jl = start + count; j < jl; j += 3 ) { | |
if ( index !== undefined ) { | |
addFace( index.getX( j ), index.getX( j + 1 ), index.getX( j + 2 ), group.materialIndex ); | |
} else { | |
addFace( j, j + 1, j + 2, group.materialIndex ); | |
} | |
} | |
} | |
} else { | |
if ( index !== undefined ) { | |
for ( let i = 0; i < index.count; i += 3 ) { | |
addFace( index.getX( i ), index.getX( i + 1 ), index.getX( i + 2 ) ); | |
} | |
} else { | |
for ( let i = 0; i < position.count; i += 3 ) { | |
addFace( i, i + 1, i + 2 ); | |
} | |
} | |
} | |
this.computeFaceNormals(); | |
if ( geometry.boundingBox !== null ) { | |
this.boundingBox = geometry.boundingBox.clone(); | |
} | |
if ( geometry.boundingSphere !== null ) { | |
this.boundingSphere = geometry.boundingSphere.clone(); | |
} | |
return this; | |
} | |
center() { | |
this.computeBoundingBox(); | |
this.boundingBox.getCenter( _offset ).negate(); | |
this.translate( _offset.x, _offset.y, _offset.z ); | |
return this; | |
} | |
normalize() { | |
this.computeBoundingSphere(); | |
const center = this.boundingSphere.center; | |
const radius = this.boundingSphere.radius; | |
const s = radius === 0 ? 1 : 1.0 / radius; | |
const matrix = new THREE.Matrix4(); | |
matrix.set( | |
s, 0, 0, - s * center.x, | |
0, s, 0, - s * center.y, | |
0, 0, s, - s * center.z, | |
0, 0, 0, 1 | |
); | |
this.applyMatrix4( matrix ); | |
return this; | |
} | |
computeFaceNormals() { | |
const cb = new THREE.Vector3(), ab = new THREE.Vector3(); | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const face = this.faces[ f ]; | |
const vA = this.vertices[ face.a ]; | |
const vB = this.vertices[ face.b ]; | |
const vC = this.vertices[ face.c ]; | |
cb.subVectors( vC, vB ); | |
ab.subVectors( vA, vB ); | |
cb.cross( ab ); | |
cb.normalize(); | |
face.normal.copy( cb ); | |
} | |
} | |
computeVertexNormals( areaWeighted = true ) { | |
const vertices = new Array( this.vertices.length ); | |
for ( let v = 0, vl = this.vertices.length; v < vl; v ++ ) { | |
vertices[ v ] = new THREE.Vector3(); | |
} | |
if ( areaWeighted ) { | |
// vertex normals weighted by triangle areas | |
// http://www.iquilezles.org/www/articles/normals/normals.htm | |
const cb = new THREE.Vector3(), ab = new THREE.Vector3(); | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const face = this.faces[ f ]; | |
const vA = this.vertices[ face.a ]; | |
const vB = this.vertices[ face.b ]; | |
const vC = this.vertices[ face.c ]; | |
cb.subVectors( vC, vB ); | |
ab.subVectors( vA, vB ); | |
cb.cross( ab ); | |
vertices[ face.a ].add( cb ); | |
vertices[ face.b ].add( cb ); | |
vertices[ face.c ].add( cb ); | |
} | |
} else { | |
this.computeFaceNormals(); | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const face = this.faces[ f ]; | |
vertices[ face.a ].add( face.normal ); | |
vertices[ face.b ].add( face.normal ); | |
vertices[ face.c ].add( face.normal ); | |
} | |
} | |
for ( let v = 0, vl = this.vertices.length; v < vl; v ++ ) { | |
vertices[ v ].normalize(); | |
} | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const face = this.faces[ f ]; | |
const vertexNormals = face.vertexNormals; | |
if ( vertexNormals.length === 3 ) { | |
vertexNormals[ 0 ].copy( vertices[ face.a ] ); | |
vertexNormals[ 1 ].copy( vertices[ face.b ] ); | |
vertexNormals[ 2 ].copy( vertices[ face.c ] ); | |
} else { | |
vertexNormals[ 0 ] = vertices[ face.a ].clone(); | |
vertexNormals[ 1 ] = vertices[ face.b ].clone(); | |
vertexNormals[ 2 ] = vertices[ face.c ].clone(); | |
} | |
} | |
if ( this.faces.length > 0 ) { | |
this.normalsNeedUpdate = true; | |
} | |
} | |
computeFlatVertexNormals() { | |
this.computeFaceNormals(); | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const face = this.faces[ f ]; | |
const vertexNormals = face.vertexNormals; | |
if ( vertexNormals.length === 3 ) { | |
vertexNormals[ 0 ].copy( face.normal ); | |
vertexNormals[ 1 ].copy( face.normal ); | |
vertexNormals[ 2 ].copy( face.normal ); | |
} else { | |
vertexNormals[ 0 ] = face.normal.clone(); | |
vertexNormals[ 1 ] = face.normal.clone(); | |
vertexNormals[ 2 ] = face.normal.clone(); | |
} | |
} | |
if ( this.faces.length > 0 ) { | |
this.normalsNeedUpdate = true; | |
} | |
} | |
computeMorphNormals() { | |
// save original normals | |
// - create temp variables on first access | |
// otherwise just copy (for faster repeated calls) | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const face = this.faces[ f ]; | |
if ( ! face.__originalFaceNormal ) { | |
face.__originalFaceNormal = face.normal.clone(); | |
} else { | |
face.__originalFaceNormal.copy( face.normal ); | |
} | |
if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; | |
for ( let i = 0, il = face.vertexNormals.length; i < il; i ++ ) { | |
if ( ! face.__originalVertexNormals[ i ] ) { | |
face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); | |
} else { | |
face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); | |
} | |
} | |
} | |
// use temp geometry to compute face and vertex normals for each morph | |
const tmpGeo = new Geometry(); | |
tmpGeo.faces = this.faces; | |
for ( let i = 0, il = this.morphTargets.length; i < il; i ++ ) { | |
// create on first access | |
if ( ! this.morphNormals[ i ] ) { | |
this.morphNormals[ i ] = {}; | |
this.morphNormals[ i ].faceNormals = []; | |
this.morphNormals[ i ].vertexNormals = []; | |
const dstNormalsFace = this.morphNormals[ i ].faceNormals; | |
const dstNormalsVertex = this.morphNormals[ i ].vertexNormals; | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const faceNormal = new THREE.Vector3(); | |
const vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; | |
dstNormalsFace.push( faceNormal ); | |
dstNormalsVertex.push( vertexNormals ); | |
} | |
} | |
const morphNormals = this.morphNormals[ i ]; | |
// set vertices to morph target | |
tmpGeo.vertices = this.morphTargets[ i ].vertices; | |
// compute morph normals | |
tmpGeo.computeFaceNormals(); | |
tmpGeo.computeVertexNormals(); | |
// store morph normals | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const face = this.faces[ f ]; | |
const faceNormal = morphNormals.faceNormals[ f ]; | |
const vertexNormals = morphNormals.vertexNormals[ f ]; | |
faceNormal.copy( face.normal ); | |
vertexNormals.a.copy( face.vertexNormals[ 0 ] ); | |
vertexNormals.b.copy( face.vertexNormals[ 1 ] ); | |
vertexNormals.c.copy( face.vertexNormals[ 2 ] ); | |
} | |
} | |
// restore original normals | |
for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
const face = this.faces[ f ]; | |
face.normal = face.__originalFaceNormal; | |
face.vertexNormals = face.__originalVertexNormals; | |
} | |
} | |
computeBoundingBox() { | |
if ( this.boundingBox === null ) { | |
this.boundingBox = new THREE.Box3(); | |
} | |
this.boundingBox.setFromPoints( this.vertices ); | |
} | |
computeBoundingSphere() { | |
if ( this.boundingSphere === null ) { | |
this.boundingSphere = new THREE.Sphere(); | |
} | |
this.boundingSphere.setFromPoints( this.vertices ); | |
} | |
merge( geometry, matrix, materialIndexOffset = 0 ) { | |
if ( ! ( geometry && geometry.isGeometry ) ) { | |
console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); | |
return; | |
} | |
let normalMatrix; | |
const vertexOffset = this.vertices.length, | |
vertices1 = this.vertices, | |
vertices2 = geometry.vertices, | |
faces1 = this.faces, | |
faces2 = geometry.faces, | |
colors1 = this.colors, | |
colors2 = geometry.colors; | |
if ( matrix !== undefined ) { | |
normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); | |
} | |
// vertices | |
for ( let i = 0, il = vertices2.length; i < il; i ++ ) { | |
const vertex = vertices2[ i ]; | |
const vertexCopy = vertex.clone(); | |
if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); | |
vertices1.push( vertexCopy ); | |
} | |
// colors | |
for ( let i = 0, il = colors2.length; i < il; i ++ ) { | |
colors1.push( colors2[ i ].clone() ); | |
} | |
// faces | |
for ( let i = 0, il = faces2.length; i < il; i ++ ) { | |
const face = faces2[ i ]; | |
let normal, color; | |
const faceVertexNormals = face.vertexNormals, | |
faceVertexColors = face.vertexColors; | |
const faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); | |
faceCopy.normal.copy( face.normal ); | |
if ( normalMatrix !== undefined ) { | |
faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); | |
} | |
for ( let j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { | |
normal = faceVertexNormals[ j ].clone(); | |
if ( normalMatrix !== undefined ) { | |
normal.applyMatrix3( normalMatrix ).normalize(); | |
} | |
faceCopy.vertexNormals.push( normal ); | |
} | |
faceCopy.color.copy( face.color ); | |
for ( let j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { | |
color = faceVertexColors[ j ]; | |
faceCopy.vertexColors.push( color.clone() ); | |
} | |
faceCopy.materialIndex = face.materialIndex + materialIndexOffset; | |
faces1.push( faceCopy ); | |
} | |
// uvs | |
for ( let i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { | |
const faceVertexUvs2 = geometry.faceVertexUvs[ i ]; | |
if ( this.faceVertexUvs[ i ] === undefined ) this.faceVertexUvs[ i ] = []; | |
for ( let j = 0, jl = faceVertexUvs2.length; j < jl; j ++ ) { | |
const uvs2 = faceVertexUvs2[ j ], uvsCopy = []; | |
for ( let k = 0, kl = uvs2.length; k < kl; k ++ ) { | |
uvsCopy.push( uvs2[ k ].clone() ); | |
} | |
this.faceVertexUvs[ i ].push( uvsCopy ); | |
} | |
} | |
} | |
mergeMesh( mesh ) { | |
if ( ! ( mesh && mesh.isMesh ) ) { | |
console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); | |
return; | |
} | |
if ( mesh.matrixAutoUpdate ) mesh.updateMatrix(); | |
this.merge( mesh.geometry, mesh.matrix ); | |
} | |
/* | |
* Checks for duplicate vertices with hashmap. | |
* Duplicated vertices are removed | |
* and faces' vertices are updated. | |
*/ | |
mergeVertices( precisionPoints = 4 ) { | |
const verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) | |
const unique = [], changes = []; | |
const precision = Math.pow( 10, precisionPoints ); | |
for ( let i = 0, il = this.vertices.length; i < il; i ++ ) { | |
const v = this.vertices[ i ]; | |
const key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); | |
if ( verticesMap[ key ] === undefined ) { | |
verticesMap[ key ] = i; | |
unique.push( this.vertices[ i ] ); | |
changes[ i ] = unique.length - 1; | |
} else { | |
//console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); | |
changes[ i ] = changes[ verticesMap[ key ] ]; | |
} | |
} | |
// if faces are completely degenerate after merging vertices, we | |
// have to remove them from the geometry. | |
const faceIndicesToRemove = []; | |
for ( let i = 0, il = this.faces.length; i < il; i ++ ) { | |
const face = this.faces[ i ]; | |
face.a = changes[ face.a ]; | |
face.b = changes[ face.b ]; | |
face.c = changes[ face.c ]; | |
const indices = [ face.a, face.b, face.c ]; | |
// if any duplicate vertices are found in a Face3 | |
// we have to remove the face as nothing can be saved | |
for ( let n = 0; n < 3; n ++ ) { | |
if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { | |
faceIndicesToRemove.push( i ); | |
break; | |
} | |
} | |
} | |
for ( let i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { | |
const idx = faceIndicesToRemove[ i ]; | |
this.faces.splice( idx, 1 ); | |
for ( let j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { | |
this.faceVertexUvs[ j ].splice( idx, 1 ); | |
} | |
} | |
// Use unique set of vertices | |
const diff = this.vertices.length - unique.length; | |
this.vertices = unique; | |
return diff; | |
} | |
setFromPoints( points ) { | |
this.vertices = []; | |
for ( let i = 0, l = points.length; i < l; i ++ ) { | |
const point = points[ i ]; | |
this.vertices.push( new THREE.Vector3( point.x, point.y, point.z || 0 ) ); | |
} | |
return this; | |
} | |
sortFacesByMaterialIndex() { | |
const faces = this.faces; | |
const length = faces.length; | |
// tag faces | |
for ( let i = 0; i < length; i ++ ) { | |
faces[ i ]._id = i; | |
} | |
// sort faces | |
function materialIndexSort( a, b ) { | |
return a.materialIndex - b.materialIndex; | |
} | |
faces.sort( materialIndexSort ); | |
// sort uvs | |
const uvs1 = this.faceVertexUvs[ 0 ]; | |
const uvs2 = this.faceVertexUvs[ 1 ]; | |
let newUvs1, newUvs2; | |
if ( uvs1 && uvs1.length === length ) newUvs1 = []; | |
if ( uvs2 && uvs2.length === length ) newUvs2 = []; | |
for ( let i = 0; i < length; i ++ ) { | |
const id = faces[ i ]._id; | |
if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); | |
if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); | |
} | |
if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; | |
if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; | |
} | |
toJSON() { | |
const data = { | |
metadata: { | |
version: 4.5, | |
type: 'Geometry', | |
generator: 'Geometry.toJSON' | |
} | |
}; | |
// standard Geometry serialization | |
data.uuid = this.uuid; | |
data.type = this.type; | |
if ( this.name !== '' ) data.name = this.name; | |
if ( this.parameters !== undefined ) { | |
const parameters = this.parameters; | |
for ( const key in parameters ) { | |
if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; | |
} | |
return data; | |
} | |
const vertices = []; | |
for ( let i = 0; i < this.vertices.length; i ++ ) { | |
const vertex = this.vertices[ i ]; | |
vertices.push( vertex.x, vertex.y, vertex.z ); | |
} | |
const faces = []; | |
const normals = []; | |
const normalsHash = {}; | |
const colors = []; | |
const colorsHash = {}; | |
const uvs = []; | |
const uvsHash = {}; | |
for ( let i = 0; i < this.faces.length; i ++ ) { | |
const face = this.faces[ i ]; | |
const hasMaterial = true; | |
const hasFaceUv = false; // deprecated | |
const hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; | |
const hasFaceNormal = face.normal.length() > 0; | |
const hasFaceVertexNormal = face.vertexNormals.length > 0; | |
const hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; | |
const hasFaceVertexColor = face.vertexColors.length > 0; | |
let faceType = 0; | |
faceType = setBit( faceType, 0, 0 ); // isQuad | |
faceType = setBit( faceType, 1, hasMaterial ); | |
faceType = setBit( faceType, 2, hasFaceUv ); | |
faceType = setBit( faceType, 3, hasFaceVertexUv ); | |
faceType = setBit( faceType, 4, hasFaceNormal ); | |
faceType = setBit( faceType, 5, hasFaceVertexNormal ); | |
faceType = setBit( faceType, 6, hasFaceColor ); | |
faceType = setBit( faceType, 7, hasFaceVertexColor ); | |
faces.push( faceType ); | |
faces.push( face.a, face.b, face.c ); | |
faces.push( face.materialIndex ); | |
if ( hasFaceVertexUv ) { | |
const faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; | |
faces.push( | |
getUvIndex( faceVertexUvs[ 0 ] ), | |
getUvIndex( faceVertexUvs[ 1 ] ), | |
getUvIndex( faceVertexUvs[ 2 ] ) | |
); | |
} | |
if ( hasFaceNormal ) { | |
faces.push( getNormalIndex( face.normal ) ); | |
} | |
if ( hasFaceVertexNormal ) { | |
const vertexNormals = face.vertexNormals; | |
faces.push( | |
getNormalIndex( vertexNormals[ 0 ] ), | |
getNormalIndex( vertexNormals[ 1 ] ), | |
getNormalIndex( vertexNormals[ 2 ] ) | |
); | |
} | |
if ( hasFaceColor ) { | |
faces.push( getColorIndex( face.color ) ); | |
} | |
if ( hasFaceVertexColor ) { | |
const vertexColors = face.vertexColors; | |
faces.push( | |
getColorIndex( vertexColors[ 0 ] ), | |
getColorIndex( vertexColors[ 1 ] ), | |
getColorIndex( vertexColors[ 2 ] ) | |
); | |
} | |
} | |
function setBit( value, position, enabled ) { | |
return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); | |
} | |
function getNormalIndex( normal ) { | |
const hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); | |
if ( normalsHash[ hash ] !== undefined ) { | |
return normalsHash[ hash ]; | |
} | |
normalsHash[ hash ] = normals.length / 3; | |
normals.push( normal.x, normal.y, normal.z ); | |
return normalsHash[ hash ]; | |
} | |
function getColorIndex( color ) { | |
const hash = color.r.toString() + color.g.toString() + color.b.toString(); | |
if ( colorsHash[ hash ] !== undefined ) { | |
return colorsHash[ hash ]; | |
} | |
colorsHash[ hash ] = colors.length; | |
colors.push( color.getHex() ); | |
return colorsHash[ hash ]; | |
} | |
function getUvIndex( uv ) { | |
const hash = uv.x.toString() + uv.y.toString(); | |
if ( uvsHash[ hash ] !== undefined ) { | |
return uvsHash[ hash ]; | |
} | |
uvsHash[ hash ] = uvs.length / 2; | |
uvs.push( uv.x, uv.y ); | |
return uvsHash[ hash ]; | |
} | |
data.data = {}; | |
data.data.vertices = vertices; | |
data.data.normals = normals; | |
if ( colors.length > 0 ) data.data.colors = colors; | |
if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility | |
data.data.faces = faces; | |
return data; | |
} | |
clone() { | |
/* | |
// Handle primitives | |
const parameters = this.parameters; | |
if ( parameters !== undefined ) { | |
const values = []; | |
for ( const key in parameters ) { | |
values.push( parameters[ key ] ); | |
} | |
const geometry = Object.create( this.constructor.prototype ); | |
this.constructor.apply( geometry, values ); | |
return geometry; | |
} | |
return new this.constructor().copy( this ); | |
*/ | |
return new Geometry().copy( this ); | |
} | |
copy( source ) { | |
// reset | |
this.vertices = []; | |
this.colors = []; | |
this.faces = []; | |
this.faceVertexUvs = [[]]; | |
this.morphTargets = []; | |
this.morphNormals = []; | |
this.skinWeights = []; | |
this.skinIndices = []; | |
this.lineDistances = []; | |
this.boundingBox = null; | |
this.boundingSphere = null; | |
// name | |
this.name = source.name; | |
// vertices | |
const vertices = source.vertices; | |
for ( let i = 0, il = vertices.length; i < il; i ++ ) { | |
this.vertices.push( vertices[ i ].clone() ); | |
} | |
// colors | |
const colors = source.colors; | |
for ( let i = 0, il = colors.length; i < il; i ++ ) { | |
this.colors.push( colors[ i ].clone() ); | |
} | |
// faces | |
const faces = source.faces; | |
for ( let i = 0, il = faces.length; i < il; i ++ ) { | |
this.faces.push( faces[ i ].clone() ); | |
} | |
// face vertex uvs | |
for ( let i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { | |
const faceVertexUvs = source.faceVertexUvs[ i ]; | |
if ( this.faceVertexUvs[ i ] === undefined ) { | |
this.faceVertexUvs[ i ] = []; | |
} | |
for ( let j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { | |
const uvs = faceVertexUvs[ j ], uvsCopy = []; | |
for ( let k = 0, kl = uvs.length; k < kl; k ++ ) { | |
const uv = uvs[ k ]; | |
uvsCopy.push( uv.clone() ); | |
} | |
this.faceVertexUvs[ i ].push( uvsCopy ); | |
} | |
} | |
// morph targets | |
const morphTargets = source.morphTargets; | |
for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { | |
const morphTarget = {}; | |
morphTarget.name = morphTargets[ i ].name; | |
// vertices | |
if ( morphTargets[ i ].vertices !== undefined ) { | |
morphTarget.vertices = []; | |
for ( let j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { | |
morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); | |
} | |
} | |
// normals | |
if ( morphTargets[ i ].normals !== undefined ) { | |
morphTarget.normals = []; | |
for ( let j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { | |
morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); | |
} | |
} | |
this.morphTargets.push( morphTarget ); | |
} | |
// morph normals | |
const morphNormals = source.morphNormals; | |
for ( let i = 0, il = morphNormals.length; i < il; i ++ ) { | |
const morphNormal = {}; | |
// vertex normals | |
if ( morphNormals[ i ].vertexNormals !== undefined ) { | |
morphNormal.vertexNormals = []; | |
for ( let j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { | |
const srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; | |
const destVertexNormal = {}; | |
destVertexNormal.a = srcVertexNormal.a.clone(); | |
destVertexNormal.b = srcVertexNormal.b.clone(); | |
destVertexNormal.c = srcVertexNormal.c.clone(); | |
morphNormal.vertexNormals.push( destVertexNormal ); | |
} | |
} | |
// face normals | |
if ( morphNormals[ i ].faceNormals !== undefined ) { | |
morphNormal.faceNormals = []; | |
for ( let j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { | |
morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); | |
} | |
} | |
this.morphNormals.push( morphNormal ); | |
} | |
// skin weights | |
const skinWeights = source.skinWeights; | |
for ( let i = 0, il = skinWeights.length; i < il; i ++ ) { | |
this.skinWeights.push( skinWeights[ i ].clone() ); | |
} | |
// skin indices | |
const skinIndices = source.skinIndices; | |
for ( let i = 0, il = skinIndices.length; i < il; i ++ ) { | |
this.skinIndices.push( skinIndices[ i ].clone() ); | |
} | |
// line distances | |
const lineDistances = source.lineDistances; | |
for ( let i = 0, il = lineDistances.length; i < il; i ++ ) { | |
this.lineDistances.push( lineDistances[ i ] ); | |
} | |
// bounding box | |
const boundingBox = source.boundingBox; | |
if ( boundingBox !== null ) { | |
this.boundingBox = boundingBox.clone(); | |
} | |
// bounding sphere | |
const boundingSphere = source.boundingSphere; | |
if ( boundingSphere !== null ) { | |
this.boundingSphere = boundingSphere.clone(); | |
} | |
// update flags | |
this.elementsNeedUpdate = source.elementsNeedUpdate; | |
this.verticesNeedUpdate = source.verticesNeedUpdate; | |
this.uvsNeedUpdate = source.uvsNeedUpdate; | |
this.normalsNeedUpdate = source.normalsNeedUpdate; | |
this.colorsNeedUpdate = source.colorsNeedUpdate; | |
this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; | |
this.groupsNeedUpdate = source.groupsNeedUpdate; | |
return this; | |
} | |
toBufferGeometry() { | |
const geometry = new DirectGeometry().fromGeometry( this ); | |
const buffergeometry = new THREE.BufferGeometry(); | |
const positions = new Float32Array( geometry.vertices.length * 3 ); | |
buffergeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); | |
if ( geometry.normals.length > 0 ) { | |
const normals = new Float32Array( geometry.normals.length * 3 ); | |
buffergeometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); | |
} | |
if ( geometry.colors.length > 0 ) { | |
const colors = new Float32Array( geometry.colors.length * 3 ); | |
buffergeometry.setAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); | |
} | |
if ( geometry.uvs.length > 0 ) { | |
const uvs = new Float32Array( geometry.uvs.length * 2 ); | |
buffergeometry.setAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); | |
} | |
if ( geometry.uvs2.length > 0 ) { | |
const uvs2 = new Float32Array( geometry.uvs2.length * 2 ); | |
buffergeometry.setAttribute( 'uv2', new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); | |
} | |
// groups | |
buffergeometry.groups = geometry.groups; | |
// morphs | |
for ( const name in geometry.morphTargets ) { | |
const array = []; | |
const morphTargets = geometry.morphTargets[ name ]; | |
for ( let i = 0, l = morphTargets.length; i < l; i ++ ) { | |
const morphTarget = morphTargets[ i ]; | |
const attribute = new THREE.Float32BufferAttribute( morphTarget.data.length * 3, 3 ); | |
attribute.name = morphTarget.name; | |
array.push( attribute.copyVector3sArray( morphTarget.data ) ); | |
} | |
buffergeometry.morphAttributes[ name ] = array; | |
} | |
// skinning | |
if ( geometry.skinIndices.length > 0 ) { | |
const skinIndices = new THREE.Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); | |
buffergeometry.setAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); | |
} | |
if ( geometry.skinWeights.length > 0 ) { | |
const skinWeights = new THREE.Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); | |
buffergeometry.setAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); | |
} | |
// | |
if ( geometry.boundingSphere !== null ) { | |
buffergeometry.boundingSphere = geometry.boundingSphere.clone(); | |
} | |
if ( geometry.boundingBox !== null ) { | |
buffergeometry.boundingBox = geometry.boundingBox.clone(); | |
} | |
return buffergeometry; | |
} | |
computeTangents() { | |
console.error( 'THREE.Geometry: .computeTangents() has been removed.' ); | |
} | |
computeLineDistances() { | |
console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' ); | |
} | |
applyMatrix( matrix ) { | |
console.warn( 'THREE.Geometry: .applyMatrix() has been renamed to .applyMatrix4().' ); | |
return this.applyMatrix4( matrix ); | |
} | |
dispose() { | |
this.dispatchEvent( { type: 'dispose' } ); | |
} | |
static createBufferGeometryFromObject( object ) { | |
let buffergeometry = new THREE.BufferGeometry(); | |
const geometry = object.geometry; | |
if ( object.isPoints || object.isLine ) { | |
const positions = new THREE.Float32BufferAttribute( geometry.vertices.length * 3, 3 ); | |
const colors = new THREE.Float32BufferAttribute( geometry.colors.length * 3, 3 ); | |
buffergeometry.setAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); | |
buffergeometry.setAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); | |
if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { | |
const lineDistances = new THREE.Float32BufferAttribute( geometry.lineDistances.length, 1 ); | |
buffergeometry.setAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); | |
} | |
if ( geometry.boundingSphere !== null ) { | |
buffergeometry.boundingSphere = geometry.boundingSphere.clone(); | |
} | |
if ( geometry.boundingBox !== null ) { | |
buffergeometry.boundingBox = geometry.boundingBox.clone(); | |
} | |
} else if ( object.isMesh ) { | |
buffergeometry = geometry.toBufferGeometry(); | |
} | |
return buffergeometry; | |
} | |
} | |
Geometry.prototype.isGeometry = true; | |
class DirectGeometry { | |
constructor() { | |
this.vertices = []; | |
this.normals = []; | |
this.colors = []; | |
this.uvs = []; | |
this.uvs2 = []; | |
this.groups = []; | |
this.morphTargets = {}; | |
this.skinWeights = []; | |
this.skinIndices = []; | |
// this.lineDistances = []; | |
this.boundingBox = null; | |
this.boundingSphere = null; | |
// update flags | |
this.verticesNeedUpdate = false; | |
this.normalsNeedUpdate = false; | |
this.colorsNeedUpdate = false; | |
this.uvsNeedUpdate = false; | |
this.groupsNeedUpdate = false; | |
} | |
computeGroups( geometry ) { | |
const groups = []; | |
let group, i; | |
let materialIndex = undefined; | |
const faces = geometry.faces; | |
for ( i = 0; i < faces.length; i ++ ) { | |
const face = faces[ i ]; | |
// materials | |
if ( face.materialIndex !== materialIndex ) { | |
materialIndex = face.materialIndex; | |
if ( group !== undefined ) { | |
group.count = ( i * 3 ) - group.start; | |
groups.push( group ); | |
} | |
group = { | |
start: i * 3, | |
materialIndex: materialIndex | |
}; | |
} | |
} | |
if ( group !== undefined ) { | |
group.count = ( i * 3 ) - group.start; | |
groups.push( group ); | |
} | |
this.groups = groups; | |
} | |
fromGeometry( geometry ) { | |
const faces = geometry.faces; | |
const vertices = geometry.vertices; | |
const faceVertexUvs = geometry.faceVertexUvs; | |
const hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; | |
const hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; | |
// morphs | |
const morphTargets = geometry.morphTargets; | |
const morphTargetsLength = morphTargets.length; | |
let morphTargetsPosition; | |
if ( morphTargetsLength > 0 ) { | |
morphTargetsPosition = []; | |
for ( let i = 0; i < morphTargetsLength; i ++ ) { | |
morphTargetsPosition[ i ] = { | |
name: morphTargets[ i ].name, | |
data: [] | |
}; | |
} | |
this.morphTargets.position = morphTargetsPosition; | |
} | |
const morphNormals = geometry.morphNormals; | |
const morphNormalsLength = morphNormals.length; | |
let morphTargetsNormal; | |
if ( morphNormalsLength > 0 ) { | |
morphTargetsNormal = []; | |
for ( let i = 0; i < morphNormalsLength; i ++ ) { | |
morphTargetsNormal[ i ] = { | |
name: morphNormals[ i ].name, | |
data: [] | |
}; | |
} | |
this.morphTargets.normal = morphTargetsNormal; | |
} | |
// skins | |
const skinIndices = geometry.skinIndices; | |
const skinWeights = geometry.skinWeights; | |
const hasSkinIndices = skinIndices.length === vertices.length; | |
const hasSkinWeights = skinWeights.length === vertices.length; | |
// | |
if ( vertices.length > 0 && faces.length === 0 ) { | |
console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' ); | |
} | |
for ( let i = 0; i < faces.length; i ++ ) { | |
const face = faces[ i ]; | |
this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); | |
const vertexNormals = face.vertexNormals; | |
if ( vertexNormals.length === 3 ) { | |
this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); | |
} else { | |
const normal = face.normal; | |
this.normals.push( normal, normal, normal ); | |
} | |
const vertexColors = face.vertexColors; | |
if ( vertexColors.length === 3 ) { | |
this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); | |
} else { | |
const color = face.color; | |
this.colors.push( color, color, color ); | |
} | |
if ( hasFaceVertexUv === true ) { | |
const vertexUvs = faceVertexUvs[ 0 ][ i ]; | |
if ( vertexUvs !== undefined ) { | |
this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); | |
} else { | |
console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); | |
this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); | |
} | |
} | |
if ( hasFaceVertexUv2 === true ) { | |
const vertexUvs = faceVertexUvs[ 1 ][ i ]; | |
if ( vertexUvs !== undefined ) { | |
this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); | |
} else { | |
console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); | |
this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); | |
} | |
} | |
// morphs | |
for ( let j = 0; j < morphTargetsLength; j ++ ) { | |
const morphTarget = morphTargets[ j ].vertices; | |
morphTargetsPosition[ j ].data.push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); | |
} | |
for ( let j = 0; j < morphNormalsLength; j ++ ) { | |
const morphNormal = morphNormals[ j ].vertexNormals[ i ]; | |
morphTargetsNormal[ j ].data.push( morphNormal.a, morphNormal.b, morphNormal.c ); | |
} | |
// skins | |
if ( hasSkinIndices ) { | |
this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); | |
} | |
if ( hasSkinWeights ) { | |
this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); | |
} | |
} | |
this.computeGroups( geometry ); | |
this.verticesNeedUpdate = geometry.verticesNeedUpdate; | |
this.normalsNeedUpdate = geometry.normalsNeedUpdate; | |
this.colorsNeedUpdate = geometry.colorsNeedUpdate; | |
this.uvsNeedUpdate = geometry.uvsNeedUpdate; | |
this.groupsNeedUpdate = geometry.groupsNeedUpdate; | |
if ( geometry.boundingSphere !== null ) { | |
this.boundingSphere = geometry.boundingSphere.clone(); | |
} | |
if ( geometry.boundingBox !== null ) { | |
this.boundingBox = geometry.boundingBox.clone(); | |
} | |
return this; | |
} | |
} | |
class Face3 { | |
constructor( a, b, c, normal, color, materialIndex = 0 ) { | |
this.a = a; | |
this.b = b; | |
this.c = c; | |
this.normal = ( normal && normal.isVector3 ) ? normal : new THREE.Vector3(); | |
this.vertexNormals = Array.isArray( normal ) ? normal : []; | |
this.color = ( color && color.isColor ) ? color : new THREE.Color(); | |
this.vertexColors = Array.isArray( color ) ? color : []; | |
this.materialIndex = materialIndex; | |
} | |
clone() { | |
return new this.constructor().copy( this ); | |
} | |
copy( source ) { | |
this.a = source.a; | |
this.b = source.b; | |
this.c = source.c; | |
this.normal.copy( source.normal ); | |
this.color.copy( source.color ); | |
this.materialIndex = source.materialIndex; | |
for ( let i = 0, il = source.vertexNormals.length; i < il; i ++ ) { | |
this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); | |
} | |
for ( let i = 0, il = source.vertexColors.length; i < il; i ++ ) { | |
this.vertexColors[ i ] = source.vertexColors[ i ].clone(); | |
} | |
return this; | |
} | |
} | |
THREE.Geometry = Geometry; | |
THREE.Face3 = Face3; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment