Spaces:
Running
Running
import { Quaternion } from '../math/Quaternion.js'; | |
import { Vector3 } from '../math/Vector3.js'; | |
import { Matrix4 } from '../math/Matrix4.js'; | |
import { EventDispatcher } from './EventDispatcher.js'; | |
import { Euler } from '../math/Euler.js'; | |
import { Layers } from './Layers.js'; | |
import { Matrix3 } from '../math/Matrix3.js'; | |
import { _Math } from '../math/Math.js'; | |
import { TrianglesDrawMode } from '../constants.js'; | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author WestLangley / http://github.com/WestLangley | |
* @author elephantatwork / www.elephantatwork.ch | |
*/ | |
var object3DId = 0; | |
function Object3D() { | |
Object.defineProperty( this, 'id', { value: object3DId ++ } ); | |
this.uuid = _Math.generateUUID(); | |
this.name = ''; | |
this.type = 'Object3D'; | |
this.parent = null; | |
this.children = []; | |
this.up = Object3D.DefaultUp.clone(); | |
var position = new Vector3(); | |
var rotation = new Euler(); | |
var quaternion = new Quaternion(); | |
var scale = new Vector3( 1, 1, 1 ); | |
function onRotationChange() { | |
quaternion.setFromEuler( rotation, false ); | |
} | |
function onQuaternionChange() { | |
rotation.setFromQuaternion( quaternion, undefined, false ); | |
} | |
rotation.onChange( onRotationChange ); | |
quaternion.onChange( onQuaternionChange ); | |
Object.defineProperties( this, { | |
position: { | |
configurable: true, | |
enumerable: true, | |
value: position | |
}, | |
rotation: { | |
configurable: true, | |
enumerable: true, | |
value: rotation | |
}, | |
quaternion: { | |
configurable: true, | |
enumerable: true, | |
value: quaternion | |
}, | |
scale: { | |
configurable: true, | |
enumerable: true, | |
value: scale | |
}, | |
modelViewMatrix: { | |
value: new Matrix4() | |
}, | |
normalMatrix: { | |
value: new Matrix3() | |
} | |
} ); | |
this.matrix = new Matrix4(); | |
this.matrixWorld = new Matrix4(); | |
this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; | |
this.matrixWorldNeedsUpdate = false; | |
this.layers = new Layers(); | |
this.visible = true; | |
this.castShadow = false; | |
this.receiveShadow = false; | |
this.frustumCulled = true; | |
this.renderOrder = 0; | |
this.userData = {}; | |
} | |
Object3D.DefaultUp = new Vector3( 0, 1, 0 ); | |
Object3D.DefaultMatrixAutoUpdate = true; | |
Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { | |
constructor: Object3D, | |
isObject3D: true, | |
onBeforeRender: function () {}, | |
onAfterRender: function () {}, | |
applyMatrix: function ( matrix ) { | |
this.matrix.multiplyMatrices( matrix, this.matrix ); | |
this.matrix.decompose( this.position, this.quaternion, this.scale ); | |
}, | |
applyQuaternion: function ( q ) { | |
this.quaternion.premultiply( q ); | |
return this; | |
}, | |
setRotationFromAxisAngle: function ( axis, angle ) { | |
// assumes axis is normalized | |
this.quaternion.setFromAxisAngle( axis, angle ); | |
}, | |
setRotationFromEuler: function ( euler ) { | |
this.quaternion.setFromEuler( euler, true ); | |
}, | |
setRotationFromMatrix: function ( m ) { | |
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
this.quaternion.setFromRotationMatrix( m ); | |
}, | |
setRotationFromQuaternion: function ( q ) { | |
// assumes q is normalized | |
this.quaternion.copy( q ); | |
}, | |
rotateOnAxis: function () { | |
// rotate object on axis in object space | |
// axis is assumed to be normalized | |
var q1 = new Quaternion(); | |
return function rotateOnAxis( axis, angle ) { | |
q1.setFromAxisAngle( axis, angle ); | |
this.quaternion.multiply( q1 ); | |
return this; | |
}; | |
}(), | |
rotateOnWorldAxis: function () { | |
// rotate object on axis in world space | |
// axis is assumed to be normalized | |
// method assumes no rotated parent | |
var q1 = new Quaternion(); | |
return function rotateOnWorldAxis( axis, angle ) { | |
q1.setFromAxisAngle( axis, angle ); | |
this.quaternion.premultiply( q1 ); | |
return this; | |
}; | |
}(), | |
rotateX: function () { | |
var v1 = new Vector3( 1, 0, 0 ); | |
return function rotateX( angle ) { | |
return this.rotateOnAxis( v1, angle ); | |
}; | |
}(), | |
rotateY: function () { | |
var v1 = new Vector3( 0, 1, 0 ); | |
return function rotateY( angle ) { | |
return this.rotateOnAxis( v1, angle ); | |
}; | |
}(), | |
rotateZ: function () { | |
var v1 = new Vector3( 0, 0, 1 ); | |
return function rotateZ( angle ) { | |
return this.rotateOnAxis( v1, angle ); | |
}; | |
}(), | |
translateOnAxis: function () { | |
// translate object by distance along axis in object space | |
// axis is assumed to be normalized | |
var v1 = new Vector3(); | |
return function translateOnAxis( axis, distance ) { | |
v1.copy( axis ).applyQuaternion( this.quaternion ); | |
this.position.add( v1.multiplyScalar( distance ) ); | |
return this; | |
}; | |
}(), | |
translateX: function () { | |
var v1 = new Vector3( 1, 0, 0 ); | |
return function translateX( distance ) { | |
return this.translateOnAxis( v1, distance ); | |
}; | |
}(), | |
translateY: function () { | |
var v1 = new Vector3( 0, 1, 0 ); | |
return function translateY( distance ) { | |
return this.translateOnAxis( v1, distance ); | |
}; | |
}(), | |
translateZ: function () { | |
var v1 = new Vector3( 0, 0, 1 ); | |
return function translateZ( distance ) { | |
return this.translateOnAxis( v1, distance ); | |
}; | |
}(), | |
localToWorld: function ( vector ) { | |
return vector.applyMatrix4( this.matrixWorld ); | |
}, | |
worldToLocal: function () { | |
var m1 = new Matrix4(); | |
return function worldToLocal( vector ) { | |
return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); | |
}; | |
}(), | |
lookAt: function () { | |
// This method does not support objects having non-uniformly-scaled parent(s) | |
var q1 = new Quaternion(); | |
var m1 = new Matrix4(); | |
var target = new Vector3(); | |
var position = new Vector3(); | |
return function lookAt( x, y, z ) { | |
if ( x.isVector3 ) { | |
target.copy( x ); | |
} else { | |
target.set( x, y, z ); | |
} | |
var parent = this.parent; | |
this.updateWorldMatrix( true, false ); | |
position.setFromMatrixPosition( this.matrixWorld ); | |
if ( this.isCamera || this.isLight ) { | |
m1.lookAt( position, target, this.up ); | |
} else { | |
m1.lookAt( target, position, this.up ); | |
} | |
this.quaternion.setFromRotationMatrix( m1 ); | |
if ( parent ) { | |
m1.extractRotation( parent.matrixWorld ); | |
q1.setFromRotationMatrix( m1 ); | |
this.quaternion.premultiply( q1.inverse() ); | |
} | |
}; | |
}(), | |
add: function ( object ) { | |
if ( arguments.length > 1 ) { | |
for ( var i = 0; i < arguments.length; i ++ ) { | |
this.add( arguments[ i ] ); | |
} | |
return this; | |
} | |
if ( object === this ) { | |
console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); | |
return this; | |
} | |
if ( ( object && object.isObject3D ) ) { | |
if ( object.parent !== null ) { | |
object.parent.remove( object ); | |
} | |
object.parent = this; | |
object.dispatchEvent( { type: 'added' } ); | |
this.children.push( object ); | |
} else { | |
console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); | |
} | |
return this; | |
}, | |
remove: function ( object ) { | |
if ( arguments.length > 1 ) { | |
for ( var i = 0; i < arguments.length; i ++ ) { | |
this.remove( arguments[ i ] ); | |
} | |
return this; | |
} | |
var index = this.children.indexOf( object ); | |
if ( index !== - 1 ) { | |
object.parent = null; | |
object.dispatchEvent( { type: 'removed' } ); | |
this.children.splice( index, 1 ); | |
} | |
return this; | |
}, | |
getObjectById: function ( id ) { | |
return this.getObjectByProperty( 'id', id ); | |
}, | |
getObjectByName: function ( name ) { | |
return this.getObjectByProperty( 'name', name ); | |
}, | |
getObjectByProperty: function ( name, value ) { | |
if ( this[ name ] === value ) return this; | |
for ( var i = 0, l = this.children.length; i < l; i ++ ) { | |
var child = this.children[ i ]; | |
var object = child.getObjectByProperty( name, value ); | |
if ( object !== undefined ) { | |
return object; | |
} | |
} | |
return undefined; | |
}, | |
getWorldPosition: function ( target ) { | |
if ( target === undefined ) { | |
console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' ); | |
target = new Vector3(); | |
} | |
this.updateMatrixWorld( true ); | |
return target.setFromMatrixPosition( this.matrixWorld ); | |
}, | |
getWorldQuaternion: function () { | |
var position = new Vector3(); | |
var scale = new Vector3(); | |
return function getWorldQuaternion( target ) { | |
if ( target === undefined ) { | |
console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' ); | |
target = new Quaternion(); | |
} | |
this.updateMatrixWorld( true ); | |
this.matrixWorld.decompose( position, target, scale ); | |
return target; | |
}; | |
}(), | |
getWorldScale: function () { | |
var position = new Vector3(); | |
var quaternion = new Quaternion(); | |
return function getWorldScale( target ) { | |
if ( target === undefined ) { | |
console.warn( 'THREE.Object3D: .getWorldScale() target is now required' ); | |
target = new Vector3(); | |
} | |
this.updateMatrixWorld( true ); | |
this.matrixWorld.decompose( position, quaternion, target ); | |
return target; | |
}; | |
}(), | |
getWorldDirection: function ( target ) { | |
if ( target === undefined ) { | |
console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); | |
target = new Vector3(); | |
} | |
this.updateMatrixWorld( true ); | |
var e = this.matrixWorld.elements; | |
return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); | |
}, | |
raycast: function () {}, | |
traverse: function ( callback ) { | |
callback( this ); | |
var children = this.children; | |
for ( var i = 0, l = children.length; i < l; i ++ ) { | |
children[ i ].traverse( callback ); | |
} | |
}, | |
traverseVisible: function ( callback ) { | |
if ( this.visible === false ) return; | |
callback( this ); | |
var children = this.children; | |
for ( var i = 0, l = children.length; i < l; i ++ ) { | |
children[ i ].traverseVisible( callback ); | |
} | |
}, | |
traverseAncestors: function ( callback ) { | |
var parent = this.parent; | |
if ( parent !== null ) { | |
callback( parent ); | |
parent.traverseAncestors( callback ); | |
} | |
}, | |
updateMatrix: function () { | |
this.matrix.compose( this.position, this.quaternion, this.scale ); | |
this.matrixWorldNeedsUpdate = true; | |
}, | |
updateMatrixWorld: function ( force ) { | |
if ( this.matrixAutoUpdate ) this.updateMatrix(); | |
if ( this.matrixWorldNeedsUpdate || force ) { | |
if ( this.parent === null ) { | |
this.matrixWorld.copy( this.matrix ); | |
} else { | |
this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); | |
} | |
this.matrixWorldNeedsUpdate = false; | |
force = true; | |
} | |
// update children | |
var children = this.children; | |
for ( var i = 0, l = children.length; i < l; i ++ ) { | |
children[ i ].updateMatrixWorld( force ); | |
} | |
}, | |
updateWorldMatrix: function ( updateParents, updateChildren ) { | |
var parent = this.parent; | |
if ( updateParents === true && parent !== null ) { | |
parent.updateWorldMatrix( true, false ); | |
} | |
if ( this.matrixAutoUpdate ) this.updateMatrix(); | |
if ( this.parent === null ) { | |
this.matrixWorld.copy( this.matrix ); | |
} else { | |
this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); | |
} | |
// update children | |
if ( updateChildren === true ) { | |
var children = this.children; | |
for ( var i = 0, l = children.length; i < l; i ++ ) { | |
children[ i ].updateWorldMatrix( false, true ); | |
} | |
} | |
}, | |
toJSON: function ( meta ) { | |
// meta is a string when called from JSON.stringify | |
var isRootObject = ( meta === undefined || typeof meta === 'string' ); | |
var output = {}; | |
// meta is a hash used to collect geometries, materials. | |
// not providing it implies that this is the root object | |
// being serialized. | |
if ( isRootObject ) { | |
// initialize meta obj | |
meta = { | |
geometries: {}, | |
materials: {}, | |
textures: {}, | |
images: {}, | |
shapes: {} | |
}; | |
output.metadata = { | |
version: 4.5, | |
type: 'Object', | |
generator: 'Object3D.toJSON' | |
}; | |
} | |
// standard Object3D serialization | |
var object = {}; | |
object.uuid = this.uuid; | |
object.type = this.type; | |
if ( this.name !== '' ) object.name = this.name; | |
if ( this.castShadow === true ) object.castShadow = true; | |
if ( this.receiveShadow === true ) object.receiveShadow = true; | |
if ( this.visible === false ) object.visible = false; | |
if ( this.frustumCulled === false ) object.frustumCulled = false; | |
if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; | |
if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; | |
object.layers = this.layers.mask; | |
object.matrix = this.matrix.toArray(); | |
if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; | |
// object specific properties | |
if ( this.isMesh && this.drawMode !== TrianglesDrawMode ) object.drawMode = this.drawMode; | |
// | |
function serialize( library, element ) { | |
if ( library[ element.uuid ] === undefined ) { | |
library[ element.uuid ] = element.toJSON( meta ); | |
} | |
return element.uuid; | |
} | |
if ( this.isMesh || this.isLine || this.isPoints ) { | |
object.geometry = serialize( meta.geometries, this.geometry ); | |
var parameters = this.geometry.parameters; | |
if ( parameters !== undefined && parameters.shapes !== undefined ) { | |
var shapes = parameters.shapes; | |
if ( Array.isArray( shapes ) ) { | |
for ( var i = 0, l = shapes.length; i < l; i ++ ) { | |
var shape = shapes[ i ]; | |
serialize( meta.shapes, shape ); | |
} | |
} else { | |
serialize( meta.shapes, shapes ); | |
} | |
} | |
} | |
if ( this.material !== undefined ) { | |
if ( Array.isArray( this.material ) ) { | |
var uuids = []; | |
for ( var i = 0, l = this.material.length; i < l; i ++ ) { | |
uuids.push( serialize( meta.materials, this.material[ i ] ) ); | |
} | |
object.material = uuids; | |
} else { | |
object.material = serialize( meta.materials, this.material ); | |
} | |
} | |
// | |
if ( this.children.length > 0 ) { | |
object.children = []; | |
for ( var i = 0; i < this.children.length; i ++ ) { | |
object.children.push( this.children[ i ].toJSON( meta ).object ); | |
} | |
} | |
if ( isRootObject ) { | |
var geometries = extractFromCache( meta.geometries ); | |
var materials = extractFromCache( meta.materials ); | |
var textures = extractFromCache( meta.textures ); | |
var images = extractFromCache( meta.images ); | |
var shapes = extractFromCache( meta.shapes ); | |
if ( geometries.length > 0 ) output.geometries = geometries; | |
if ( materials.length > 0 ) output.materials = materials; | |
if ( textures.length > 0 ) output.textures = textures; | |
if ( images.length > 0 ) output.images = images; | |
if ( shapes.length > 0 ) output.shapes = shapes; | |
} | |
output.object = object; | |
return output; | |
// extract data from the cache hash | |
// remove metadata on each item | |
// and return as array | |
function extractFromCache( cache ) { | |
var values = []; | |
for ( var key in cache ) { | |
var data = cache[ key ]; | |
delete data.metadata; | |
values.push( data ); | |
} | |
return values; | |
} | |
}, | |
clone: function ( recursive ) { | |
return new this.constructor().copy( this, recursive ); | |
}, | |
copy: function ( source, recursive ) { | |
if ( recursive === undefined ) recursive = true; | |
this.name = source.name; | |
this.up.copy( source.up ); | |
this.position.copy( source.position ); | |
this.quaternion.copy( source.quaternion ); | |
this.scale.copy( source.scale ); | |
this.matrix.copy( source.matrix ); | |
this.matrixWorld.copy( source.matrixWorld ); | |
this.matrixAutoUpdate = source.matrixAutoUpdate; | |
this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; | |
this.layers.mask = source.layers.mask; | |
this.visible = source.visible; | |
this.castShadow = source.castShadow; | |
this.receiveShadow = source.receiveShadow; | |
this.frustumCulled = source.frustumCulled; | |
this.renderOrder = source.renderOrder; | |
this.userData = JSON.parse( JSON.stringify( source.userData ) ); | |
if ( recursive === true ) { | |
for ( var i = 0; i < source.children.length; i ++ ) { | |
var child = source.children[ i ]; | |
this.add( child.clone() ); | |
} | |
} | |
return this; | |
} | |
} ); | |
export { Object3D }; | |