版本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 };