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 * as MathUtils from '../math/MathUtils.js'; | |
| let _object3DId = 0; | |
| const _v1 = /*@__PURE__*/ new Vector3(); | |
| const _q1 = /*@__PURE__*/ new Quaternion(); | |
| const _m1 = /*@__PURE__*/ new Matrix4(); | |
| const _target = /*@__PURE__*/ new Vector3(); | |
| const _position = /*@__PURE__*/ new Vector3(); | |
| const _scale = /*@__PURE__*/ new Vector3(); | |
| const _quaternion = /*@__PURE__*/ new Quaternion(); | |
| const _xAxis = /*@__PURE__*/ new Vector3(1, 0, 0); | |
| const _yAxis = /*@__PURE__*/ new Vector3(0, 1, 0); | |
| const _zAxis = /*@__PURE__*/ new Vector3(0, 0, 1); | |
| const _addedEvent = { type: 'added' }; | |
| const _removedEvent = { type: 'removed' }; | |
| class Object3D extends EventDispatcher { | |
| constructor() { | |
| super(); | |
| Object.defineProperty(this, 'id', { value: _object3DId++ }); | |
| this.uuid = MathUtils.generateUUID(); | |
| this.name = ''; | |
| this.type = 'Object3D'; | |
| this.parent = null; | |
| this.children = []; | |
| this.up = Object3D.DefaultUp.clone(); | |
| const position = new Vector3(); | |
| const rotation = new Euler(); | |
| const quaternion = new Quaternion(); | |
| const 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.animations = []; | |
| this.userData = {}; | |
| } | |
| onBeforeRender(/* renderer, scene, camera, geometry, material, group */) {} | |
| onAfterRender(/* renderer, scene, camera, geometry, material, group */) {} | |
| applyMatrix4(matrix) { | |
| if (this.matrixAutoUpdate) this.updateMatrix(); | |
| this.matrix.premultiply(matrix); | |
| this.matrix.decompose(this.position, this.quaternion, this.scale); | |
| } | |
| applyQuaternion(q) { | |
| this.quaternion.premultiply(q); | |
| return this; | |
| } | |
| setRotationFromAxisAngle(axis, angle) { | |
| // assumes axis is normalized | |
| this.quaternion.setFromAxisAngle(axis, angle); | |
| } | |
| setRotationFromEuler(euler) { | |
| this.quaternion.setFromEuler(euler, true); | |
| } | |
| setRotationFromMatrix(m) { | |
| // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
| this.quaternion.setFromRotationMatrix(m); | |
| } | |
| setRotationFromQuaternion(q) { | |
| // assumes q is normalized | |
| this.quaternion.copy(q); | |
| } | |
| rotateOnAxis(axis, angle) { | |
| // rotate object on axis in object space | |
| // axis is assumed to be normalized | |
| _q1.setFromAxisAngle(axis, angle); | |
| this.quaternion.multiply(_q1); | |
| return this; | |
| } | |
| rotateOnWorldAxis(axis, angle) { | |
| // rotate object on axis in world space | |
| // axis is assumed to be normalized | |
| // method assumes no rotated parent | |
| _q1.setFromAxisAngle(axis, angle); | |
| this.quaternion.premultiply(_q1); | |
| return this; | |
| } | |
| rotateX(angle) { | |
| return this.rotateOnAxis(_xAxis, angle); | |
| } | |
| rotateY(angle) { | |
| return this.rotateOnAxis(_yAxis, angle); | |
| } | |
| rotateZ(angle) { | |
| return this.rotateOnAxis(_zAxis, angle); | |
| } | |
| translateOnAxis(axis, distance) { | |
| // translate object by distance along axis in object space | |
| // axis is assumed to be normalized | |
| _v1.copy(axis).applyQuaternion(this.quaternion); | |
| this.position.add(_v1.multiplyScalar(distance)); | |
| return this; | |
| } | |
| translateX(distance) { | |
| return this.translateOnAxis(_xAxis, distance); | |
| } | |
| translateY(distance) { | |
| return this.translateOnAxis(_yAxis, distance); | |
| } | |
| translateZ(distance) { | |
| return this.translateOnAxis(_zAxis, distance); | |
| } | |
| localToWorld(vector) { | |
| return vector.applyMatrix4(this.matrixWorld); | |
| } | |
| worldToLocal(vector) { | |
| return vector.applyMatrix4(_m1.copy(this.matrixWorld).invert()); | |
| } | |
| lookAt(x, y, z) { | |
| // This method does not support objects having non-uniformly-scaled parent(s) | |
| if (x.isVector3) { | |
| _target.copy(x); | |
| } else { | |
| _target.set(x, y, z); | |
| } | |
| const 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.invert()); | |
| } | |
| } | |
| add(object) { | |
| if (arguments.length > 1) { | |
| for (let 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; | |
| this.children.push(object); | |
| object.dispatchEvent(_addedEvent); | |
| } else { | |
| console.error('THREE.Object3D.add: object not an instance of THREE.Object3D.', object); | |
| } | |
| return this; | |
| } | |
| remove(object) { | |
| if (arguments.length > 1) { | |
| for (let i = 0; i < arguments.length; i++) { | |
| this.remove(arguments[i]); | |
| } | |
| return this; | |
| } | |
| const index = this.children.indexOf(object); | |
| if (index !== -1) { | |
| object.parent = null; | |
| this.children.splice(index, 1); | |
| object.dispatchEvent(_removedEvent); | |
| } | |
| return this; | |
| } | |
| removeFromParent() { | |
| const parent = this.parent; | |
| if (parent !== null) { | |
| parent.remove(this); | |
| } | |
| return this; | |
| } | |
| clear() { | |
| for (let i = 0; i < this.children.length; i++) { | |
| const object = this.children[i]; | |
| object.parent = null; | |
| object.dispatchEvent(_removedEvent); | |
| } | |
| this.children.length = 0; | |
| return this; | |
| } | |
| attach(object) { | |
| // adds object as a child of this, while maintaining the object's world transform | |
| // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) | |
| this.updateWorldMatrix(true, false); | |
| _m1.copy(this.matrixWorld).invert(); | |
| if (object.parent !== null) { | |
| object.parent.updateWorldMatrix(true, false); | |
| _m1.multiply(object.parent.matrixWorld); | |
| } | |
| object.applyMatrix4(_m1); | |
| this.add(object); | |
| object.updateWorldMatrix(false, true); | |
| return this; | |
| } | |
| getObjectById(id) { | |
| return this.getObjectByProperty('id', id); | |
| } | |
| getObjectByName(name) { | |
| return this.getObjectByProperty('name', name); | |
| } | |
| getObjectByProperty(name, value) { | |
| if (this[name] === value) return this; | |
| for (let i = 0, l = this.children.length; i < l; i++) { | |
| const child = this.children[i]; | |
| const object = child.getObjectByProperty(name, value); | |
| if (object !== undefined) { | |
| return object; | |
| } | |
| } | |
| return undefined; | |
| } | |
| getWorldPosition(target) { | |
| this.updateWorldMatrix(true, false); | |
| return target.setFromMatrixPosition(this.matrixWorld); | |
| } | |
| getWorldQuaternion(target) { | |
| this.updateWorldMatrix(true, false); | |
| this.matrixWorld.decompose(_position, target, _scale); | |
| return target; | |
| } | |
| getWorldScale(target) { | |
| this.updateWorldMatrix(true, false); | |
| this.matrixWorld.decompose(_position, _quaternion, target); | |
| return target; | |
| } | |
| getWorldDirection(target) { | |
| this.updateWorldMatrix(true, false); | |
| const e = this.matrixWorld.elements; | |
| return target.set(e[8], e[9], e[10]).normalize(); | |
| } | |
| raycast(/* raycaster, intersects */) {} | |
| traverse(callback) { | |
| callback(this); | |
| const children = this.children; | |
| for (let i = 0, l = children.length; i < l; i++) { | |
| children[i].traverse(callback); | |
| } | |
| } | |
| traverseVisible(callback) { | |
| if (this.visible === false) return; | |
| callback(this); | |
| const children = this.children; | |
| for (let i = 0, l = children.length; i < l; i++) { | |
| children[i].traverseVisible(callback); | |
| } | |
| } | |
| traverseAncestors(callback) { | |
| const parent = this.parent; | |
| if (parent !== null) { | |
| callback(parent); | |
| parent.traverseAncestors(callback); | |
| } | |
| } | |
| updateMatrix() { | |
| this.matrix.compose(this.position, this.quaternion, this.scale); | |
| this.matrixWorldNeedsUpdate = true; | |
| } | |
| updateMatrixWorld(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 | |
| const children = this.children; | |
| for (let i = 0, l = children.length; i < l; i++) { | |
| children[i].updateMatrixWorld(force); | |
| } | |
| } | |
| updateWorldMatrix(updateParents, updateChildren) { | |
| const 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) { | |
| const children = this.children; | |
| for (let i = 0, l = children.length; i < l; i++) { | |
| children[i].updateWorldMatrix(false, true); | |
| } | |
| } | |
| } | |
| toJSON(meta) { | |
| // meta is a string when called from JSON.stringify | |
| const isRootObject = meta === undefined || typeof meta === 'string'; | |
| const 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: {}, | |
| skeletons: {}, | |
| animations: {}, | |
| }; | |
| output.metadata = { | |
| version: 4.5, | |
| type: 'Object', | |
| generator: 'Object3D.toJSON', | |
| }; | |
| } | |
| // standard Object3D serialization | |
| const 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.isInstancedMesh) { | |
| object.type = 'InstancedMesh'; | |
| object.count = this.count; | |
| object.instanceMatrix = this.instanceMatrix.toJSON(); | |
| if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); | |
| } | |
| // | |
| function serialize(library, element) { | |
| if (library[element.uuid] === undefined) { | |
| library[element.uuid] = element.toJSON(meta); | |
| } | |
| return element.uuid; | |
| } | |
| if (this.isScene) { | |
| if (this.background) { | |
| if (this.background.isColor) { | |
| object.background = this.background.toJSON(); | |
| } else if (this.background.isTexture) { | |
| object.background = this.background.toJSON(meta).uuid; | |
| } | |
| } | |
| if (this.environment && this.environment.isTexture) { | |
| object.environment = this.environment.toJSON(meta).uuid; | |
| } | |
| } else if (this.isMesh || this.isLine || this.isPoints) { | |
| object.geometry = serialize(meta.geometries, this.geometry); | |
| const parameters = this.geometry.parameters; | |
| if (parameters !== undefined && parameters.shapes !== undefined) { | |
| const shapes = parameters.shapes; | |
| if (Array.isArray(shapes)) { | |
| for (let i = 0, l = shapes.length; i < l; i++) { | |
| const shape = shapes[i]; | |
| serialize(meta.shapes, shape); | |
| } | |
| } else { | |
| serialize(meta.shapes, shapes); | |
| } | |
| } | |
| } | |
| if (this.isSkinnedMesh) { | |
| object.bindMode = this.bindMode; | |
| object.bindMatrix = this.bindMatrix.toArray(); | |
| if (this.skeleton !== undefined) { | |
| serialize(meta.skeletons, this.skeleton); | |
| object.skeleton = this.skeleton.uuid; | |
| } | |
| } | |
| if (this.material !== undefined) { | |
| if (Array.isArray(this.material)) { | |
| const uuids = []; | |
| for (let 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 (let i = 0; i < this.children.length; i++) { | |
| object.children.push(this.children[i].toJSON(meta).object); | |
| } | |
| } | |
| // | |
| if (this.animations.length > 0) { | |
| object.animations = []; | |
| for (let i = 0; i < this.animations.length; i++) { | |
| const animation = this.animations[i]; | |
| object.animations.push(serialize(meta.animations, animation)); | |
| } | |
| } | |
| if (isRootObject) { | |
| const geometries = extractFromCache(meta.geometries); | |
| const materials = extractFromCache(meta.materials); | |
| const textures = extractFromCache(meta.textures); | |
| const images = extractFromCache(meta.images); | |
| const shapes = extractFromCache(meta.shapes); | |
| const skeletons = extractFromCache(meta.skeletons); | |
| const animations = extractFromCache(meta.animations); | |
| 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; | |
| if (skeletons.length > 0) output.skeletons = skeletons; | |
| if (animations.length > 0) output.animations = animations; | |
| } | |
| output.object = object; | |
| return output; | |
| // extract data from the cache hash | |
| // remove metadata on each item | |
| // and return as array | |
| function extractFromCache(cache) { | |
| const values = []; | |
| for (const key in cache) { | |
| const data = cache[key]; | |
| delete data.metadata; | |
| values.push(data); | |
| } | |
| return values; | |
| } | |
| } | |
| clone(recursive) { | |
| return new this.constructor().copy(this, recursive); | |
| } | |
| copy(source, recursive = true) { | |
| this.name = source.name; | |
| this.up.copy(source.up); | |
| this.position.copy(source.position); | |
| this.rotation.order = source.rotation.order; | |
| 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 (let i = 0; i < source.children.length; i++) { | |
| const child = source.children[i]; | |
| this.add(child.clone()); | |
| } | |
| } | |
| return this; | |
| } | |
| } | |
| Object3D.DefaultUp = new Vector3(0, 1, 0); | |
| Object3D.DefaultMatrixAutoUpdate = true; | |
| Object3D.prototype.isObject3D = true; | |
| export { Object3D }; | |