对象id生成器:
var count = 0;
function GeometryIdCount() { return count++; }
对象UUID生成器
var _Math = {
DEG2RAD: Math.PI / 180,
RAD2DEG: 180 / Math.PI,
generateUUID: function () {
// http://www.broofa.com/Tools/Math.uuid.htm
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
var uuid = new Array( 36 );
var rnd = 0, r;
return function generateUUID() {
for ( var i = 0; i < 36; i ++ ) {
if ( i === 8 || i === 13 || i === 18 || i === 23 ) {
uuid[ i ] = '-';
} else if ( i === 14 ) {
uuid[ i ] = '4';
} else {
if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
r = rnd & 0xf;
rnd = rnd >> 4;
uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
}
}
return uuid.join( '' );
};
}(),
};
数学表达式:
//输出值为[min,max]
clamp: function ( value, min, max ) {
return Math.max( min, Math.min( max, value ) );
},
// compute euclidian modulo of m % n
// https://en.wikipedia.org/wiki/Modulo_operation
//求余
euclideanModulo: function ( n, m ) {
return ( ( n % m ) + m ) % m;
},
//(a1,b1)和(a2,b2)构成的一条线,如果给出x,算出y
mapLinear: function ( x, a1, a2, b1, b2 ) {
return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
},
//算出插值
lerp: function ( x, y, t ) {
return ( 1 - t ) * x + t * y;
},
//(5,5,10) 给出0
//(10,5,10)给出1
//(7.5,5,10) 给出0.5
smoothstep: function ( x, min, max ) {
if ( x <= min ) return 0;
if ( x >= max ) return 1;
x = ( x - min ) / ( max - min );
return x * x * ( 3 - 2 * x );
},
smootherstep: function ( x, min, max ) {
if ( x <= min ) return 0;
if ( x >= max ) return 1;
x = ( x - min ) / ( max - min );
return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
},
// Math.floor(x) 返回值 小于等于 x,且与 x 最接近的整数
// Math.round(x) 四舍五入
// Math.random() 从[0,1)中返回一个浮点数
//下面这个方法会从[low,high]中返回一个随意整数
randInt: function ( low, high ) {
return low + Math.floor( Math.random() * ( high - low + 1 ) );
},
//下面方法会从[low,high)中返回任意一个浮点数
randFloat: function ( low, high ) {
return low + Math.random() * ( high - low );
},
// Random float from <-range/2, range/2> interval
randFloatSpread: function ( range ) {
return range * ( 0.5 - Math.random() );
},
//角度到弧度
degToRad: function ( degrees ) {
return degrees * _Math.DEG2RAD;
},
//弧度到角度
radToDeg: function ( radians ) {
return radians * _Math.RAD2DEG;
},
//传入2的指数以及其浮点数,如2、2.1、4、4.1、8、8.1、16、16.1 返回的都是true
isPowerOfTwo: function ( value ) {
return ( value & ( value - 1 ) ) === 0 && value !== 0;
},
//返回结果是2的指数方,具体哪一个要看传入的value值离哪一个2的指数方更近
nearestPowerOfTwo: function ( value ) {
return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) );
},
nextPowerOfTwo: function ( value ) {
value --;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value ++;
return value;
}
缓冲几何体构造方法
function BufferGeometry() {
Object.defineProperty( this, 'id', { value: GeometryIdCount() } );
this.uuid = _Math.generateUUID();
this.name = '';
this.type = 'BufferGeometry';
this.index = null;
this.attributes = {};
this.morphAttributes = {};
this.groups = [];
this.boundingBox = null;
this.boundingSphere = null;
this.drawRange = { start: 0, count: Infinity };
}
BufferGeometry.MaxIndex = 65535;
Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
isBufferGeometry: true,
getIndex: function () {
return this.index;
},
setIndex: function ( index ) {
if ( Array.isArray( index ) ) {
this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );
} else {
this.index = index;
}
},
addAttribute: function ( name, attribute ) {
if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) {
console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );
this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );
return;
}
if ( name === 'index' ) {
console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );
this.setIndex( attribute );
return;
}
this.attributes[ name ] = attribute;
return this;
},
getAttribute: function ( name ) {
return this.attributes[ name ];
},
removeAttribute: function ( name ) {
delete this.attributes[ name ];
return this;
},
addGroup: function ( start, count, materialIndex ) {
this.groups.push( {
start: start,
count: count,
materialIndex: materialIndex !== undefined ? materialIndex : 0
} );
},
clearGroups: function () {
this.groups = [];
},
setDrawRange: function ( start, count ) {
this.drawRange.start = start;
this.drawRange.count = count;
},
applyMatrix: function ( matrix ) {
var position = this.attributes.position;
if ( position !== undefined ) {
matrix.applyToBufferAttribute( position );
position.needsUpdate = true;
}
var normal = this.attributes.normal;
if ( normal !== undefined ) {
var normalMatrix = new Matrix3().getNormalMatrix( matrix );
normalMatrix.applyToBufferAttribute( normal );
normal.needsUpdate = true;
}
if ( this.boundingBox !== null ) {
this.computeBoundingBox();
}
if ( this.boundingSphere !== null ) {
this.computeBoundingSphere();
}
return this;
},
rotateX: function () {
// rotate geometry around world x-axis
var m1 = new Matrix4();
return function rotateX( angle ) {
m1.makeRotationX( angle );
this.applyMatrix( m1 );
return this;
};
}(),
rotateY: function () {
// rotate geometry around world y-axis
var m1 = new Matrix4();
return function rotateY( angle ) {
m1.makeRotationY( angle );
this.applyMatrix( m1 );
return this;
};
}(),
rotateZ: function () {
// rotate geometry around world z-axis
var m1 = new Matrix4();
return function rotateZ( angle ) {
m1.makeRotationZ( angle );
this.applyMatrix( m1 );
return this;
};
}(),
translate: function () {
// translate geometry
var m1 = new Matrix4();
return function translate( x, y, z ) {
m1.makeTranslation( x, y, z );
this.applyMatrix( m1 );
return this;
};
}(),
scale: function () {
// scale geometry
var m1 = new Matrix4();
return function scale( x, y, z ) {
m1.makeScale( x, y, z );
this.applyMatrix( m1 );
return this;
};
}(),
lookAt: function () {
var obj = new Object3D();
return function lookAt( vector ) {
obj.lookAt( vector );
obj.updateMatrix();
this.applyMatrix( obj.matrix );
};
}(),
center: function () {
this.computeBoundingBox();
var offset = this.boundingBox.getCenter().negate();
this.translate( offset.x, offset.y, offset.z );
return offset;
},
setFromObject: function ( object ) {
// console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );
var geometry = object.geometry;
if ( object.isPoints || object.isLine ) {
var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 );
var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 );
this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );
this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) );
if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {
var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 );
this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );
}
if ( geometry.boundingSphere !== null ) {
this.boundingSphere = geometry.boundingSphere.clone();
}
if ( geometry.boundingBox !== null ) {
this.boundingBox = geometry.boundingBox.clone();
}
} else if ( object.isMesh ) {
if ( geometry && geometry.isGeometry ) {
this.fromGeometry( geometry );
}
}
return this;
},
updateFromObject: function ( object ) {
var geometry = object.geometry;
if ( object.isMesh ) {
var direct = geometry.__directGeometry;
if ( geometry.elementsNeedUpdate === true ) {
direct = undefined;
geometry.elementsNeedUpdate = false;
}
if ( direct === undefined ) {
return this.fromGeometry( geometry );
}
direct.verticesNeedUpdate = geometry.verticesNeedUpdate;
direct.normalsNeedUpdate = geometry.normalsNeedUpdate;
direct.colorsNeedUpdate = geometry.colorsNeedUpdate;
direct.uvsNeedUpdate = geometry.uvsNeedUpdate;
direct.groupsNeedUpdate = geometry.groupsNeedUpdate;
geometry.verticesNeedUpdate = false;
geometry.normalsNeedUpdate = false;
geometry.colorsNeedUpdate = false;
geometry.uvsNeedUpdate = false;
geometry.groupsNeedUpdate = false;
geometry = direct;
}
var attribute;
if ( geometry.verticesNeedUpdate === true ) {
attribute = this.attributes.position;
if ( attribute !== undefined ) {
attribute.copyVector3sArray( geometry.vertices );
attribute.needsUpdate = true;
}
geometry.verticesNeedUpdate = false;
}
if ( geometry.normalsNeedUpdate === true ) {
attribute = this.attributes.normal;
if ( attribute !== undefined ) {
attribute.copyVector3sArray( geometry.normals );
attribute.needsUpdate = true;
}
geometry.normalsNeedUpdate = false;
}
if ( geometry.colorsNeedUpdate === true ) {
attribute = this.attributes.color;
if ( attribute !== undefined ) {
attribute.copyColorsArray( geometry.colors );
attribute.needsUpdate = true;
}
geometry.colorsNeedUpdate = false;
}
if ( geometry.uvsNeedUpdate ) {
attribute = this.attributes.uv;
if ( attribute !== undefined ) {
attribute.copyVector2sArray( geometry.uvs );
attribute.needsUpdate = true;
}
geometry.uvsNeedUpdate = false;
}
if ( geometry.lineDistancesNeedUpdate ) {
attribute = this.attributes.lineDistance;
if ( attribute !== undefined ) {
attribute.copyArray( geometry.lineDistances );
attribute.needsUpdate = true;
}
geometry.lineDistancesNeedUpdate = false;
}
if ( geometry.groupsNeedUpdate ) {
geometry.computeGroups( object.geometry );
this.groups = geometry.groups;
geometry.groupsNeedUpdate = false;
}
return this;
},
fromGeometry: function ( geometry ) {
geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry );
return this.fromDirectGeometry( geometry.__directGeometry );
},
fromDirectGeometry: function ( geometry ) {
var positions = new Float32Array( geometry.vertices.length * 3 );
this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );
if ( geometry.normals.length > 0 ) {
var normals = new Float32Array( geometry.normals.length * 3 );
this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );
}
if ( geometry.colors.length > 0 ) {
var colors = new Float32Array( geometry.colors.length * 3 );
this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );
}
if ( geometry.uvs.length > 0 ) {
var uvs = new Float32Array( geometry.uvs.length * 2 );
this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );
}
if ( geometry.uvs2.length > 0 ) {
var uvs2 = new Float32Array( geometry.uvs2.length * 2 );
this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );
}
if ( geometry.indices.length > 0 ) {
var TypeArray = arrayMax( geometry.indices ) > 65535 ? Uint32Array : Uint16Array;
var indices = new TypeArray( geometry.indices.length * 3 );
this.setIndex( new BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) );
}
// groups
this.groups = geometry.groups;
// morphs
for ( var name in geometry.morphTargets ) {
var array = [];
var morphTargets = geometry.morphTargets[ name ];
for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {
var morphTarget = morphTargets[ i ];
var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 );
array.push( attribute.copyVector3sArray( morphTarget ) );
}
this.morphAttributes[ name ] = array;
}
// skinning
if ( geometry.skinIndices.length > 0 ) {
var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 );
this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );
}
if ( geometry.skinWeights.length > 0 ) {
var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 );
this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );
}
//
if ( geometry.boundingSphere !== null ) {
this.boundingSphere = geometry.boundingSphere.clone();
}
if ( geometry.boundingBox !== null ) {
this.boundingBox = geometry.boundingBox.clone();
}
return this;
},
computeBoundingBox: function () {
if ( this.boundingBox === null ) {
this.boundingBox = new Box3();
}
var position = this.attributes.position;
if ( position !== undefined ) {
this.boundingBox.setFromBufferAttribute( position );
} else {
this.boundingBox.makeEmpty();
}
if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
}
},
computeBoundingSphere: function () {
var box = new Box3();
var vector = new Vector3();
return function computeBoundingSphere() {
if ( this.boundingSphere === null ) {
this.boundingSphere = new Sphere();
}
var position = this.attributes.position;
if ( position ) {
var center = this.boundingSphere.center;
box.setFromBufferAttribute( position );
box.getCenter( center );
// hoping to find a boundingSphere with a radius smaller than the
// boundingSphere of the boundingBox: sqrt(3) smaller in the best case
var maxRadiusSq = 0;
for ( var i = 0, il = position.count; i < il; i ++ ) {
vector.x = position.getX( i );
vector.y = position.getY( i );
vector.z = position.getZ( i );
maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
}
this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
if ( isNaN( this.boundingSphere.radius ) ) {
console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this );
}
}
};
}(),
computeFaceNormals: function () {
// backwards compatibility
},
computeVertexNormals: function () {
var index = this.index;
var attributes = this.attributes;
var groups = this.groups;
if ( attributes.position ) {
var positions = attributes.position.array;
if ( attributes.normal === undefined ) {
this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) );
} else {
// reset existing normals to zero
var array = attributes.normal.array;
for ( var i = 0, il = array.length; i < il; i ++ ) {
array[ i ] = 0;
}
}
var normals = attributes.normal.array;
var vA, vB, vC;
var pA = new Vector3(), pB = new Vector3(), pC = new Vector3();
var cb = new Vector3(), ab = new Vector3();
// indexed elements
if ( index ) {
var indices = index.array;
if ( groups.length === 0 ) {
this.addGroup( 0, indices.length );
}
for ( var j = 0, jl = groups.length; j < jl; ++ j ) {
var group = groups[ j ];
var start = group.start;
var count = group.count;
for ( var i = start, il = start + count; i < il; i += 3 ) {
vA = indices[ i + 0 ] * 3;
vB = indices[ i + 1 ] * 3;
vC = indices[ i + 2 ] * 3;
pA.fromArray( positions, vA );
pB.fromArray( positions, vB );
pC.fromArray( positions, vC );
cb.subVectors( pC, pB );
ab.subVectors( pA, pB );
cb.cross( ab );
normals[ vA ] += cb.x;
normals[ vA + 1 ] += cb.y;
normals[ vA + 2 ] += cb.z;
normals[ vB ] += cb.x;
normals[ vB + 1 ] += cb.y;
normals[ vB + 2 ] += cb.z;
normals[ vC ] += cb.x;
normals[ vC + 1 ] += cb.y;
normals[ vC + 2 ] += cb.z;
}
}
} else {
// non-indexed elements (unconnected triangle soup)
for ( var i = 0, il = positions.length; i < il; i += 9 ) {
pA.fromArray( positions, i );
pB.fromArray( positions, i + 3 );
pC.fromArray( positions, i + 6 );
cb.subVectors( pC, pB );
ab.subVectors( pA, pB );
cb.cross( ab );
normals[ i ] = cb.x;
normals[ i + 1 ] = cb.y;
normals[ i + 2 ] = cb.z;
normals[ i + 3 ] = cb.x;
normals[ i + 4 ] = cb.y;
normals[ i + 5 ] = cb.z;
normals[ i + 6 ] = cb.x;
normals[ i + 7 ] = cb.y;
normals[ i + 8 ] = cb.z;
}
}
this.normalizeNormals();
attributes.normal.needsUpdate = true;
}
},
merge: function ( geometry, offset ) {
if ( ! ( geometry && geometry.isBufferGeometry ) ) {
console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );
return;
}
if ( offset === undefined ) offset = 0;
var attributes = this.attributes;
for ( var key in attributes ) {
if ( geometry.attributes[ key ] === undefined ) continue;
var attribute1 = attributes[ key ];
var attributeArray1 = attribute1.array;
var attribute2 = geometry.attributes[ key ];
var attributeArray2 = attribute2.array;
var attributeSize = attribute2.itemSize;
for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {
attributeArray1[ j ] = attributeArray2[ i ];
}
}
return this;
},
normalizeNormals: function () {
var vector = new Vector3();
return function normalizeNormals() {
var normals = this.attributes.normal;
for ( var i = 0, il = normals.count; i < il; i ++ ) {
vector.x = normals.getX( i );
vector.y = normals.getY( i );
vector.z = normals.getZ( i );
vector.normalize();
normals.setXYZ( i, vector.x, vector.y, vector.z );
}
};
}(),
toNonIndexed: function () {
if ( this.index === null ) {
console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' );
return this;
}
var geometry2 = new BufferGeometry();
var indices = this.index.array;
var attributes = this.attributes;
for ( var name in attributes ) {
var attribute = attributes[ name ];
var array = attribute.array;
var itemSize = attribute.itemSize;
var array2 = new array.constructor( indices.length * itemSize );
var index = 0, index2 = 0;
for ( var i = 0, l = indices.length; i < l; i ++ ) {
index = indices[ i ] * itemSize;
for ( var j = 0; j < itemSize; j ++ ) {
array2[ index2 ++ ] = array[ index ++ ];
}
}
geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) );
}
return geometry2;
},
toJSON: function () {
var data = {
metadata: {
version: 4.5,
type: 'BufferGeometry',
generator: 'BufferGeometry.toJSON'
}
};
// standard BufferGeometry serialization
data.uuid = this.uuid;
data.type = this.type;
if ( this.name !== '' ) data.name = this.name;
if ( this.parameters !== undefined ) {
var parameters = this.parameters;
for ( var key in parameters ) {
if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
}
return data;
}
data.data = { attributes: {} };
var index = this.index;
if ( index !== null ) {
var array = Array.prototype.slice.call( index.array );
data.data.index = {
type: index.array.constructor.name,
array: array
};
}
var attributes = this.attributes;
for ( var key in attributes ) {
var attribute = attributes[ key ];
var array = Array.prototype.slice.call( attribute.array );
data.data.attributes[ key ] = {
itemSize: attribute.itemSize,
type: attribute.array.constructor.name,
array: array,
normalized: attribute.normalized
};
}
var groups = this.groups;
if ( groups.length > 0 ) {
data.data.groups = JSON.parse( JSON.stringify( groups ) );
}
var boundingSphere = this.boundingSphere;
if ( boundingSphere !== null ) {
data.data.boundingSphere = {
center: boundingSphere.center.toArray(),
radius: boundingSphere.radius
};
}
return data;
},
clone: function () {
/*
// Handle primitives
var parameters = this.parameters;
if ( parameters !== undefined ) {
var values = [];
for ( var key in parameters ) {
values.push( parameters[ key ] );
}
var geometry = Object.create( this.constructor.prototype );
this.constructor.apply( geometry, values );
return geometry;
}
return new this.constructor().copy( this );
*/
return new BufferGeometry().copy( this );
},
copy: function ( source ) {
var name, i, l;
// reset
this.index = null;
this.attributes = {};
this.morphAttributes = {};
this.groups = [];
this.boundingBox = null;
this.boundingSphere = null;
// name
this.name = source.name;
// index
var index = source.index;
if ( index !== null ) {
this.setIndex( index.clone() );
}
// attributes
var attributes = source.attributes;
for ( name in attributes ) {
var attribute = attributes[ name ];
this.addAttribute( name, attribute.clone() );
}
// morph attributes
var morphAttributes = source.morphAttributes;
for ( name in morphAttributes ) {
var array = [];
var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes
for ( i = 0, l = morphAttribute.length; i < l; i ++ ) {
array.push( morphAttribute[ i ].clone() );
}
this.morphAttributes[ name ] = array;
}
// groups
var groups = source.groups;
for ( i = 0, l = groups.length; i < l; i ++ ) {
var group = groups[ i ];
this.addGroup( group.start, group.count, group.materialIndex );
}
// bounding box
var boundingBox = source.boundingBox;
if ( boundingBox !== null ) {
this.boundingBox = boundingBox.clone();
}
// bounding sphere
var boundingSphere = source.boundingSphere;
if ( boundingSphere !== null ) {
this.boundingSphere = boundingSphere.clone();
}
// draw range
this.drawRange.start = source.drawRange.start;
this.drawRange.count = source.drawRange.count;
return this;
},
dispose: function () {
this.dispatchEvent( { type: 'dispose' } );
}
} );
box缓冲集合体构造方法:
// BoxBufferGeometry
function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {
BufferGeometry.call( this );
this.type = 'BoxBufferGeometry';
this.parameters = {
width: width,
height: height,
depth: depth,
widthSegments: widthSegments,
heightSegments: heightSegments,
depthSegments: depthSegments
};
var scope = this;
// segments
widthSegments = Math.floor( widthSegments ) || 1;
heightSegments = Math.floor( heightSegments ) || 1;
depthSegments = Math.floor( depthSegments ) || 1;
// buffers
var indices = [];
var vertices = [];
var normals = [];
var uvs = [];
// helper variables
var numberOfVertices = 0;
var groupStart = 0;
// build each side of the box geometry
buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px
buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx
buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py
buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny
buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz
buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz
// build geometry
this.setIndex( indices );
this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {
var segmentWidth = width / gridX;
var segmentHeight = height / gridY;
var widthHalf = width / 2;
var heightHalf = height / 2;
var depthHalf = depth / 2;
var gridX1 = gridX + 1;
var gridY1 = gridY + 1;
var vertexCounter = 0;
var groupCount = 0;
var ix, iy;
var vector = new Vector3();
// generate vertices, normals and uvs
for ( iy = 0; iy < gridY1; iy ++ ) {
var y = iy * segmentHeight - heightHalf;
for ( ix = 0; ix < gridX1; ix ++ ) {
var x = ix * segmentWidth - widthHalf;
// set values to correct vector component
vector[ u ] = x * udir;
vector[ v ] = y * vdir;
vector[ w ] = depthHalf;
// now apply vector to vertex buffer
vertices.push( vector.x, vector.y, vector.z );
// set values to correct vector component
vector[ u ] = 0;
vector[ v ] = 0;
vector[ w ] = depth > 0 ? 1 : - 1;
// now apply vector to normal buffer
normals.push( vector.x, vector.y, vector.z );
// uvs
uvs.push( ix / gridX );
uvs.push( 1 - ( iy / gridY ) );
// counters
vertexCounter += 1;
}
}
// indices
// 1. you need three indices to draw a single face
// 2. a single segment consists of two faces
// 3. so we need to generate six (2*3) indices per segment
for ( iy = 0; iy < gridY; iy ++ ) {
for ( ix = 0; ix < gridX; ix ++ ) {
var a = numberOfVertices + ix + gridX1 * iy;
var b = numberOfVertices + ix + gridX1 * ( iy + 1 );
var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );
var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;
// faces
indices.push( a, b, d );
indices.push( b, c, d );
// increase counter
groupCount += 6;
}
}
// add a group to the geometry. this will ensure multi material support
scope.addGroup( groupStart, groupCount, materialIndex );
// calculate new start value for groups
groupStart += groupCount;
// update total number of vertices
numberOfVertices += vertexCounter;
}
}
BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
BoxBufferGeometry.prototype.constructor = BoxBufferGeometry;
生成缓存集合体:
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
mesh.name = 'Box ' + ( ++ meshCount );
FR:徐海涛(hunk Xu)
QQ技术交流群:386476712