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'; /** * @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() { // 每次执行该构造函数创建对象的时候,都会给该对象设置一个id属性,并且他一个全局变量加1赋值给id属性 Object.defineProperty(this, 'id', { value: object3DId++ }); // generate :生成 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: { enumerable: true, value: position }, rotation: { enumerable: true, value: rotation }, quaternion: { enumerable: true, value: quaternion }, scale: { 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; // 创建obj3对象的时候,已经默认创建Layers,并且带对象创建的时候默认相当于初始化执行set(0) 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 with rotated and/or translated parent(s) var m1 = new Matrix4(); var vector = new Vector3(); // 参数可以是一个向量,也可以是向量的三个分量 return function lookAt(x, y, z) { // 判断x是不是向量v3,如果是,说明程序员使用的是三维向量 if (x.isVector3) { vector.copy(x); } else { vector.set(x, y, z); } if (this.isCamera) { m1.lookAt(this.position, vector, this.up); } else { m1.lookAt(vector, this.position, this.up); } this.quaternion.setFromRotationMatrix(m1); }; }(), 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 ( object : Object3D, ... ) // 将对象作为对象的子对象移除。可以删除任意数量的对象。 remove: function(object) { // arguments对象表示所有参数组成的数组 if (arguments.length > 1) { // 循环遍历 每一个参数对象执行remove删除方法 for (var i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } // 获得object在children数组中的索引 var index = this.children.indexOf(object); if (index !== -1) { // 设置object的父对象属性parent为null空 object.parent = null; object.dispatchEvent({ type: 'removed' }); // 删除children数组中索引是index的元素 this.children.splice(index, 1); } return this; }, // 通过id获得对象 getObjectById: function(id) { // 浏览器通过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() { var quaternion = new Quaternion(); return function getWorldDirection(target) { if (target === undefined) { console.warn('THREE.Object3D: .getWorldDirection() target is now required'); target = new Vector3(); } this.getWorldQuaternion(quaternion); return target.set(0, 0, 1).applyQuaternion(quaternion); }; }(), 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); } }, 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.matrix = this.matrix.toArray(); // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.geometry !== undefined) { 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 };