three.js 源码注释(五十九)objects/Mesh.js

时间:2022-03-05 05:35:49

商域无疆 (http://blog.csdn.net/omni360/)

本文遵循“署名-非商业用途-保持一致”创作公用协议

转载请保留此句:商域无疆 -  本博客专注于 敏捷开发及移动和物联设备研究:数据可视化、GOLANG、Html5、WEBGL、THREE.JS否则,出自本博客的文章拒绝转载或再转载,谢谢合作。


俺也是刚开始学,好多地儿肯定不对还请见谅.

以下代码是THREE.JS 源码文件中objects/Mesh.js文件的注释.

更多更新在 : https://github.com/omni360/three.js.sourcecode


/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
* @author mikael emtinger / http://gomo.se/
* @author jonobr1 / http://jonobr1.com/
*/

/*
///Mesh对象,最终的网格对象,有高人把图形学建模比作是制作灯笼,先用Geometry创建灯笼的框架,然后将材质material贴在框架上,最后形成的总体灯笼,就是Mesh对象.下面看一下Mesh对象的用法和具体实现.
/// 用法:var geometry = new THREE.Geometry(1,1,1);//创建geometry对象(灯笼的框架),
/////有一下可选对象BoxGeometry,CircleGeometry,CubeGeometry,CylinderGeometry,DodecahedronGeometry,ExtrudeGeometry,IcosahedronGeometry,
/////LatheGeometry,OctahedronGeometry,ParametricGeometry,PlaneGeometry,PolyhedronGeometry,RingGeometry,ShapeGeometry,SphereGeometry,
/////TetrahedronGeometry,TextGeometry,TorusGeometry,TorusKnotGeometry,TubeGeometry
/// var material = new THREE.Material({color: 0xffff00});//创建材质对象(灯笼的表面)
/////有以下可选对象LineBasicMaterial,LineDashedMaterial,Material,MeshBasicMaterial,MeshDepthMaterial,MeshFaceMaterial,
/////MeshLambertMaterial,MeshNormalMaterial,MeshPhongMaterial,PointCloudMaterial,RawShaderMaterial,ShaderMaterial,
/////SpriteCanvasMaterial,SpriteMaterial
/// var mesh = new THREE.Mesh(geometry, material);//创建mesh(灯笼)对象,并将geometry对象(灯笼的框架)和material对象(灯笼的表面)传递给mesh(灯笼)对象
/// scene.add(mesh); //将mesh(灯笼)添加到场景中.
*/
///<summary>Mesh</summary>
///<param name ="geometry" type="THREE.Geometry">Geometry对象(灯笼的框架)</param>
///<param name ="material" type="THREE.Material">Material对象(材质对象)</param>
///<returns type="Mesh">返回Mesh对象</returns>
THREE.Mesh = function ( geometry, material ) {

THREE.Object3D.call( this );//调用Object3D对象的call方法,将原本属于Object3D的方法交给当前对象Mesh来使用.

this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();//将参数geometry赋值给mesh对象的geometry属性
this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } );//将参数material赋值给mesh对象的material属性

/*
///MorphTargets
///原文地址:http://www.tuicool.com/articles/rYzuuu
///先睹为快
///
///MorphTargets允许物体发生变形。如果该物体的geometry有 $n$ 个顶点,那么MorphTargets允许你再指定 $n$ 个, $2n$ 个, $3n$ 个甚至更多个顶点
///(比如,$ p\cdot n$ 个),同时mesh对象提供一个数组morphTargetInfluences(公式中$ f_{j} $表示morphTargetInfluences[j]),具有 $p$ 个元素,
///每个元素取值在0-1之间。渲染这个物体的时候,某个顶点 $V_{i}$ 的位置其实变了,成了:
///
///$$V_{i}=V_{i}+\sum_{j=0}^{p}f_{j}\cdot (V_{j,i}-V_{i})$$
///
///举个简单的例子,一个立方体有8个顶点, MorphTargets又指定了8个顶点,立方体的一个顶点为(1,1,1),而在 MorphTargets中与之对应的顶点为(2,2,2),
///那么当 morphTargetInfluences[0]为0.5的时候,实际渲染的时候该顶点的位置就成了(1.5,1.5,1.5)。这样做的好处是显而易见的,你可以通过简单地调整
/// morphTargetInfluences数组来使物体形变,只要之前你设置好了。
///
///向物体加入morphTargets的方法很简单:
///
///var geometry = new THREE.CubeGeometry(100,100,100);
/// var material = new THREE.MeshLambertMaterial({color:0xffffff, morphTargets:true});
///
/// var vertices = [];
/// for(var i=0; i<geometry.vertices.length; i++)
/// {
/// var f = 2;
/// vertices.push(geometry.vertices[i].clone());
/// vertices[i].x *= f;
/// vertices[i].y *= f;
/// vertices[i].z *= f;
/// }
/// geometry.morphTargets.push({name:'target0', vertices:vertices});
/// 在其他什么地方(比如animate()或render()方法中)改变morphTargetInfluences,实在方便
///
///var s = 0;
///function render()
///{
/// s += 0.03;
/// mesh.morphTargetInfluences[0] = Math.abs(Math.sin(s));
/// ...
///}
///最关键的问题是,我相信,这个功能是通过着色器来完成的。我阅读过一些简单的着色器,因此我发现在着色器中完成这件事实在太合适了。
///如果某个geometry有几千甚至上万个顶点,使用JavaScript逐个计算变形后顶点的位置会造成很大压力,而显卡大规模并行计算的能力很适合处理这个任务
///(毕竟每个顶点是独立地)。
*/

this.updateMorphTargets();//更新目标变形,不影响geometry对象。

};
/*************************************************
****下面是Mesh对象的方法属性定义,继承自Object3D
**************************************************/
THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );

/*
///updateMorphTargets方法将geometry对象的morphTargets属性复制到this.morphTargetInfluences属性,不影响geometry对象本身。
*/
///<summary>updateMorphTargets</summary>
///<returns type="Mesh">返回新的Mesh对象</returns>
THREE.Mesh.prototype.updateMorphTargets = function () {

if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) {//判断geometry对象是否有目标变形数组。

this.morphTargetBase = - 1;//给Mesh对象设置morphTargetBase属性,并初始化为-1.
this.morphTargetForcedOrder = [];//给Mesh对象设置morphTargetForcedOrder属性数组,初始化[].
this.morphTargetInfluences = [];//给Mesh对象设置morphTargetInfluences属性数组,初始化为[].
this.morphTargetDictionary = {};//给Mesh对象设置morphTargetDictionary属性,初始化为}{}.

for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {//遍历geometry对象

this.morphTargetInfluences.push( 0 );
this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;//将geometry.morphTargets属性数组中的值,一一赋值给morphTargetDictionary[]属性数组。

}

}

};
/*
///getMorphTargetIndexByName方法通过参数name获得存储在morphTargetDictionary[]属性数组中的变形目标索引或者说是。
*/
///<summary>getMorphTargetIndexByName</summary>
///<param name ="name" type="String">MorphTarget存储的名字</param>
///<returns type="Mesh">返回参数name所对应的顶点</returns>
THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {

if ( this.morphTargetDictionary[ name ] !== undefined ) {//如果morphTargetDictionary[name]属性对象存在

return this.morphTargetDictionary[ name ];//返回该对象。

}

console.log( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' );//提示用户,该对象不存在。返回值是0.

return 0;

};

/*
///raycast方法用来获得当前对象与射线(参数raycaster)的交点.raycaster.intersectObject会调用这个方法。主要是用来进行碰撞检测,
/// 在选择场景中的对象时经常会用到,判断当前鼠标是否与对象重合用来选择对象.
/// NOTE:raycast方法中参数intersects参数用来存储交点的集合,格式如下
///intersects.push( {
///
///distance: distance,
///point: intersectionPoint,
///indices: [ a, b, c ],
///face: null,
///faceIndex: null,
///object: this
///
///} );
///
*////<summary>raycast</summary>
///<param name ="raycaster" type="THREE.Raycaster">射线对象</param>
///<param name ="intersects" type="ObjectArray">交点的集合</param>
///<returns type="ObjectArray">交点的集合</returns>
THREE.Mesh.prototype.raycast = ( function () {

var inverseMatrix = new THREE.Matrix4();//声明一个4x4矩阵,用来放置逆矩阵
var ray = new THREE.Ray();//声明全局射线对象
var sphere = new THREE.Sphere();//声明全局球体对象

var vA = new THREE.Vector3();//声明3维向量,vA
var vB = new THREE.Vector3();//声明3维向量,vB
var vC = new THREE.Vector3();//声明3维向量,vC

return function ( raycaster, intersects ) {

var geometry = this.geometry;

// Checking boundingSphere distance to ray
// 检查geometry对象的球体边界到射线的距离。

if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();

sphere.copy( geometry.boundingSphere );
sphere.applyMatrix4( this.matrixWorld );

if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {//调用光线跟踪的isIntersectionSphere方法,判断geometry对象的球体边界是否与射线相交。

return;//如果不相交,返回。

}

// Check boundingBox before continuing
//检查geometry对象的立方体边界到射线距离

inverseMatrix.getInverse( this.matrixWorld );
ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );

if ( geometry.boundingBox !== null ) {

if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) {//调用光线跟踪的isIntersectionBox方法,判断geometry对象的立方体边界是否与射线相交。

return;

}

}
//如果geometry对象是BufferGeometry对象
if ( geometry instanceof THREE.BufferGeometry ) {

var material = this.material;

if ( material === undefined ) return;//如果没有材质,返回

var attributes = geometry.attributes;

var a, b, c;
var precision = raycaster.precision;//精度因子

if ( attributes.index !== undefined ) {//如果bufferGeometry对象的attributes.index属性不为undefined

var indices = attributes.index.array;
var positions = attributes.position.array;
var offsets = geometry.offsets;

if ( offsets.length === 0 ) {

offsets = [ { start: 0, count: indices.length, index: 0 } ];

}

for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) {//根据定义bufferGeomentry存放的格式,遍历attributes属性.找到顶点数据存储区域

var start = offsets[ oi ].start;
var count = offsets[ oi ].count;
var index = offsets[ oi ].index;

for ( var i = start, il = start + count; i < il; i += 3 ) {//根据定义bufferGeomentry存放的格式,遍历attributes属性.找到顶点数据

a = index + indices[ i ];
b = index + indices[ i + 1 ];
c = index + indices[ i + 2 ];

vA.set(
positions[ a * 3 ],
positions[ a * 3 + 1 ],
positions[ a * 3 + 2 ]
);
vB.set(
positions[ b * 3 ],
positions[ b * 3 + 1 ],
positions[ b * 3 + 2 ]
);
vC.set(
positions[ c * 3 ],
positions[ c * 3 + 1 ],
positions[ c * 3 + 2 ]
);


if ( material.side === THREE.BackSide ) {//如果材质的side属性为BackSide

var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true );//调用intersectTriangle方法判断是否与参数VC,VB,VA组成的Triangle三角形对象相交,如果相交返回交点.如果不想交返回null,这里最后一个参数true,表示判断背面

} else {

var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );//调用intersectTriangle方法判断是否与参数VA,VB,VC组成的Triangle三角形对象相交,如果相交返回交点.如果不想交返回null,这里最后一个参数,表示判断正面

}

if ( intersectionPoint === null ) continue;//如果没有交点,跳出循环

intersectionPoint.applyMatrix4( this.matrixWorld );//将非null的交点应用世界坐标变换

var distance = raycaster.ray.origin.distanceTo( intersectionPoint );//计算射线原点到交点的距离

if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;//如果距离小于精度因子,或小于射线的近端,或大于射线的远端,跳出循环

intersects.push( {//将相交的对象,顶点索引,距离,交点保存到intersects属性数组中

distance: distance,//距离
point: intersectionPoint,//交点
indices: [ a, b, c ],//顶点在attribute属性中的索引
face: null,//面
faceIndex: null,//面所在属性数组中的索引
object: this //对象

} );

}

}

} else {//如果bufferGeometry对象的attributes.index属性为undefined

var positions = attributes.position.array;

for ( var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9 ) {//找到所有的顶点位置属性

a = i;
b = i + 1;
c = i + 2;

vA.set(
positions[ j ],
positions[ j + 1 ],
positions[ j + 2 ]
);
vB.set(
positions[ j + 3 ],
positions[ j + 4 ],
positions[ j + 5 ]
);
vC.set(
positions[ j + 6 ],
positions[ j + 7 ],
positions[ j + 8 ]
);


if ( material.side === THREE.BackSide ) {//如果材质的side属性为BackSide

var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true );//调用intersectTriangle方法判断是否与参数VC,VB,VA组成的Triangle三角形对象相交,如果相交返回交点.如果不想交返回null,这里最后一个参数true,表示判断背面

} else {

var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );//调用intersectTriangle方法判断是否与参数VA,VB,VC组成的Triangle三角形对象相交,如果相交返回交点.如果不想交返回null,这里最后一个参数,表示判断正面

}

if ( intersectionPoint === null ) continue;//如果没有交点,跳出循环

intersectionPoint.applyMatrix4( this.matrixWorld );//将非null的交点应用世界坐标变换

var distance = raycaster.ray.origin.distanceTo( intersectionPoint );//计算射线原点到交点的距离

if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;//如果距离小于精度因子,或小于射线的近端,或大于射线的远端,跳出循环

intersects.push( {//将相交的对象,顶点索引,距离,交点保存到intersects属性数组中

distance: distance,//距离
point: intersectionPoint,//交点
indices: [ a, b, c ],//顶点在attribute属性中的索引
face: null,//面
faceIndex: null,//面所在属性数组中的索引
object: this //对象

} );

}

}

} else if ( geometry instanceof THREE.Geometry ) {//如果geometry对象是THREE.Geometry类型

var isFaceMaterial = this.material instanceof THREE.MeshFaceMaterial;
var objectMaterials = isFaceMaterial === true ? this.material.materials : null;

var a, b, c, d;
var precision = raycaster.precision;//精度因子

var vertices = geometry.vertices;

for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) {//遍历geometry对象的所有面

var face = geometry.faces[ f ];

var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : this.material;

if ( material === undefined ) continue;//如果没有材质,跳出循环

a = vertices[ face.a ];
b = vertices[ face.b ];
c = vertices[ face.c ];

if ( material.morphTargets === true ) {//如果有变性目标属性

var morphTargets = geometry.morphTargets;
var morphInfluences = this.morphTargetInfluences;

vA.set( 0, 0, 0 );
vB.set( 0, 0, 0 );
vC.set( 0, 0, 0 );

for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {//将所有的顶点按照变形数据做变换

var influence = morphInfluences[ t ];

if ( influence === 0 ) continue;

var targets = morphTargets[ t ].vertices;

vA.x += ( targets[ face.a ].x - a.x ) * influence;
vA.y += ( targets[ face.a ].y - a.y ) * influence;
vA.z += ( targets[ face.a ].z - a.z ) * influence;

vB.x += ( targets[ face.b ].x - b.x ) * influence;
vB.y += ( targets[ face.b ].y - b.y ) * influence;
vB.z += ( targets[ face.b ].z - b.z ) * influence;

vC.x += ( targets[ face.c ].x - c.x ) * influence;
vC.y += ( targets[ face.c ].y - c.y ) * influence;
vC.z += ( targets[ face.c ].z - c.z ) * influence;

}

vA.add( a );
vB.add( b );
vC.add( c );

a = vA;
b = vB;
c = vC;

}

if ( material.side === THREE.BackSide ) {//如果材质的side属性为BackSide

var intersectionPoint = ray.intersectTriangle( c, b, a, true );//调用intersectTriangle方法判断是否与参数VC,VB,VA组成的Triangle三角形对象相交,如果相交返回交点.如果不想交返回null,这里最后一个参数true,表示判断背面

} else {

var intersectionPoint = ray.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide );//调用intersectTriangle方法判断是否与参数VA,VB,VC组成的Triangle三角形对象相交,如果相交返回交点.如果不想交返回null,这里最后一个参数,表示判断正面

}

if ( intersectionPoint === null ) continue;//如果没有交点,跳出循环

intersectionPoint.applyMatrix4( this.matrixWorld );//将非null的交点应用世界坐标变换

var distance = raycaster.ray.origin.distanceTo( intersectionPoint );//计算射线原点到交点的距离

if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;//如果距离小于精度因子,或小于射线的近端,或大于射线的远端,跳出循环

intersects.push( {

distance: distance,//距离
point: intersectionPoint,//交点
face: face,//面
faceIndex: f,//面索引
object: this //对象

} );

}

}

};

}() );

/*clone方法
///clone方法克隆一个Mesh网格对象.
*/
///<summary>clone</summary>
///<param name ="object" type="Object3D">接收克隆的Object3D对象</param>
///<param name ="recursive" type="boolean">是否对子对象一一进行克隆</param>
///<returns type="Ray">返回Mesh网格对象.</returns>
THREE.Mesh.prototype.clone = function ( object, recursive ) {

if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );

THREE.Object3D.prototype.clone.call( this, object, recursive );//继承Object3D的clone方法

return object;//返回Mesh网格对象.

};


商域无疆 (http://blog.csdn.net/omni360/)

本文遵循“署名-非商业用途-保持一致”创作公用协议

转载请保留此句:商域无疆 -  本博客专注于 敏捷开发及移动和物联设备研究:数据可视化、GOLANG、Html5、WEBGL、THREE.JS否则,出自本博客的文章拒绝转载或再转载,谢谢合作。


以下代码是THREE.JS 源码文件中objects/Mesh.js文件的注释.

更多更新在 : https://github.com/omni360/three.js.sourcecode