Threejs 源码解析 (Object3D)

版本95

本来想就这么用算了,可是发现好多都不理解,也就起了解析源码的心思,下苦功夫了,以前老版本的有人写过,不过threejs 更新太快,有些变动,并且他只是简单的注释,并未详细的解释,对于我这种刚开始接触的小白来说,数学就是一大硬伤,本来想做个原生的射线拾取,结果看的我一脸懵逼,跳的函数太多,刚开始容易迷路,所有就打算慢慢啃;方法都尽量解释,以便知道这样的方法为什么得这样的结果;

相关连的部分,我会看完再回来更新;

以前大神写的旧版本:https://blog.csdn.net/omni360

Object3d 是所有模型的基类;

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() {
    /**
    * Object.defineProperty(obj, prop , descriptior) :表示新增或者修改一个已经存在的属性,并返回这个对象;
    *        obj : 需定义属性的对象
    *        prop:  需定义或者修改属性的名字
    *        descriptor : 将被定义或修改的属性的描述符
     */
   Object.defineProperty( this, 'id', { value: object3DId ++ } );

   this.uuid = _Math.generateUUID();//生成id

   this.name = '';
   this.type = 'Object3D';

   this.parent = null;
   this.children = [];

   this.up = Object3D.DefaultUp.clone();//初始化以Y轴正方向的向量

   var position = new Vector3();
   var rotation = new Euler();//初始化欧拉角
   var quaternion = new Quaternion();//初始化成四元矩阵
   var scale = new Vector3( 1, 1, 1 );

    /**
    * 给对象rotation属性绑定setFromEuler()方法
    * 当rotation属性值更改,调用setFromEuler()方法
     */
   function onRotationChange() {

      quaternion.setFromEuler( rotation, false );//欧拉角转化成四元数

   }

    /**
    * 给对象quaternion属性绑定setFromQuaternion()方法
    * 当quaternion属性值更改,setFromQuaternion()方法
     */
   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;//每帧是否重新计算世界矩阵

   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 的up方向,用以camera 的up 默认y轴的正方向
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;

   },
   //通过四元数的方式旋转任意坐标轴(参数axis)旋转角度(参数angle),最后将结果返回到this.quternion属性中
   setRotationFromAxisAngle: function ( axis, angle ) {

      // assumes axis is normalized

      this.quaternion.setFromAxisAngle( axis, angle );

   },
   //通过一次欧拉旋转(参数euler)设置四元数旋转,最后将结果返回到this.quternion属性中
   setRotationFromEuler: function ( euler ) {

      this.quaternion.setFromEuler( euler, true );

   },
   //利用一个参数m(旋转矩阵),达到旋转变换的目的吧,最后将结果返回到this.quternion属性中
   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 );

   },
    /**
     * @function
     * @desc 通过坐标轴旋转
     * @param {THREE.Vector3} axis
     * @param {float} angle 弧度
     * @return {THREE.Object3D}
     */

    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;

      };

   }(),
    /**
    * 绕X轴旋转angle度
     */
   rotateX: function () {

      var v1 = new Vector3( 1, 0, 0 );

      return function rotateX( angle ) {

         return this.rotateOnAxis( v1, angle );

      };

   }(),
    /**
    * 绕Z轴旋转angle度
     */
   rotateY: function () {

      var v1 = new Vector3( 0, 1, 0 );

      return function rotateY( angle ) {

         return this.rotateOnAxis( v1, angle );

      };

   }(),
    /**
    * 绕Z轴旋转angle度
     */
   rotateZ: function () {

      var v1 = new Vector3( 0, 0, 1 );

      return function rotateZ( angle ) {

         return this.rotateOnAxis( v1, angle );

      };

   }(),
    /**
     * @function
     * @desc 对象延任意坐标轴(参数axis)移动指定距离(参数distance)
     * @param {THREE.Vector3} axis 平移轴
     * @param {float} distance 平移距离
     * @return {THREE.Object3D}
     */
   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;

      };

   }(),
    /**
    * 对象延X轴方向移动距离为(distance)
     */
   translateX: function () {

      var v1 = new Vector3( 1, 0, 0 );

      return function translateX( distance ) {

         return this.translateOnAxis( v1, distance );

      };

   }(),
    /**
     * 对象延Y轴方向移动距离为(distance)
     */
   translateY: function () {

      var v1 = new Vector3( 0, 1, 0 );

      return function translateY( distance ) {

         return this.translateOnAxis( v1, distance );

      };

   }(),
    /**
     * 对象延Z轴方向移动距离为(distance)
     */
   translateZ: function () {

      var v1 = new Vector3( 0, 0, 1 );

      return function translateZ( distance ) {

         return this.translateOnAxis( v1, distance );

      };

   }(),
    /**
     * @desc 将参数vector,从对象坐标空间变换成世界坐标空间
     * @param {THREE.Vector3} vector
     * @returns {THREE.Vector3}
     */
   localToWorld: function ( vector ) {

      return vector.applyMatrix4( this.matrixWorld );

   },
    /**
     * @desc 将参数vector,从世界坐标空间变换成对象坐标空间
     * @param {THREE.Vector3} vector
     * @returns {THREE.Vector3}
     */
   worldToLocal: function () {

      var m1 = new Matrix4();

      return function worldToLocal( vector ) {

         return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );

      };

   }(),
    /**
    * 对象看向的位置(vector)
     */
   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 ) {

         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 );

      };

   }(),
    /**
     * @desc 对象(参数object),设置为当前对象的子对象
     * @param {THREE.Object3D} object
     * @returns {THREE.Object3D}
     */
   add: function ( object ) {
      //arguments 是每个函数都有的,它是函数参数数组, arguments[ i ] 得到参数对象,i的最大值是传了多少个参数
      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;

   },
    /**
     * @desc 对象(参数object),从当前对象的子对象列表中删除
     * @param {THREE.Object3D} object
     */
   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;

   },
    /**
     * @desc 通过id获得子对象
     * @param {String} name
     * @param {boolean} recursive 默认为false,表示不才查找子对象的子对象
     * @returns {THREE.Object3D}
     */
   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;

   },
    /**
     * @desc 获得世界坐标系下的平移坐标,也就是根据变换矩阵分解得到平移的坐标
     * @param {THREE.Vector3} optionalTarget
     * @returns {THREE.Vector3}
     */
   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 );

   },
    /**
     * @function
     * @desc 获得世界坐标系下的四元数,通过变换矩阵,分解成位置坐标,四元数,缩放,返回四元数
     * @param {THREE.Quaternion} optionalTarget
     * @return {THREE.Quaternion}
     */
   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;

      };

   }(),
    /**
     * @function
     * @desc 获得世界坐标系下的缩放向量
     * @param {THREE.Vector3} optionalTarget
     * @return {THREE.Vector3}
     */
   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;

      };

   }(),
    /**
     * @function
     * @desc 获得世界坐标系下的旋转角
     * @param {THREE.Vector3} optionalTarget
     * @return {THREE.Vector3}
     */
   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 );

      };

   }(),
    /**
     * @desc 光线跟踪 ,未写代码
     */
   raycast: function () {},
    /**
     * @desc 遍历当前对象以及子对象并且应用callback方法
     * @param {requestCallback} callback
     */
   traverse: function ( callback ) {

      callback( this );

      var children = this.children;

      for ( var i = 0, l = children.length; i < l; i ++ ) {

         children[ i ].traverse( callback );

      }

   },
    /**
     * @desc 遍历当前对象以及子对象,当对象可见时并且应用callback方法
     * @param {requestCallback} 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 );

      }

   },
    /**
     * @desc 根据位置,四元数,缩放比例更新矩阵,并设置世界矩阵需要更新
     */
   updateMatrix: function () {

      this.matrix.compose( this.position, this.quaternion, this.scale );

      this.matrixWorldNeedsUpdate = true;

   },
    /**
     * @desc 根据位置,四元数,缩放比例更新世界矩阵
     * @param {boolean} force 是否强制更新
     */
   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 );

      }

   },
    /**
    * Object3D存为json 格式
     * @param meta
     * @returns {{}}
     */
   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;

      //

      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;

      }

   },
    /**
    * 克隆Object3D
     * @param recursive 为true ,克隆其子对象,否则只克隆当前对象,默认为true
     */
   clone: function ( recursive ) {

      return new this.constructor().copy( this, recursive );

   },
    /**
    * 复制
     * @param source
     * @param recursive
     * @returns {copy}
     */
   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 };
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Three.js是一个用于在Web上创建3D图形的JavaScript库。CSS2DObjectThree.js的一个扩展类,用于在3D场景中呈现具有2D样式的DOM元素。 要使用CSS2DObject,首先需要将Three.js库引入到你的项目中。然后,你可以使用CSS2DRenderer创建一个CSS2DObject,并将其添加到你的场景中。 创建一个CSS2DObject的步骤如下: 1. 创建一个div元素,用于包含你想要呈现的DOM元素。例如,你可以创建一个div元素来包含一个带有文本内容的<span>标签。 2. 使用CSS2DObject构造函数创建一个新的CSS2DObject实例,并将div元素作为参数传递给构造函数。例如,你可以使用以下代码创建一个CSS2DObject实例: ```javascript var divElement = document.createElement('div'); var cssObject = new THREE.CSS2DObject(divElement); ``` 3. 设置CSS2DObject的位置和旋转。你可以使用CSS2DObject的position和rotation属性来设置其在3D场景中的位置和旋转。例如,你可以使用以下代码将CSS2DObject放置在x轴上的位置0,y轴上的位置100,z轴上的位置0处,并将其沿着x轴旋转45度: ```javascript cssObject.position.set(0, 100, 0); cssObject.rotation.x = Math.PI / 4; ``` 4. 将CSS2DObject添加到场景中。使用场景的add方法将CSS2DObject添加到场景中。例如,你可以使用以下代码将CSS2DObject添加到名为scene的场景中: ```javascript scene.add(cssObject); ``` 通过这些步骤,你可以在Three.js3D场景中创建一个具有2D样式的DOM元素。 :https://threejs.org/docs/#examples/en/objects/CSS2DObject
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值