xtorcga
Version:
Xtor Compute Geometry Algorithm Libary 计算几何算法库
1,327 lines (766 loc) • 33.4 kB
text/typescript
import { Vec } from "../math/Vec";
import { Mat3 } from "../math/Mat3";
import { Mat4 } from "../math/Mat4";
import { v3, Vec3 } from "../math/Vec3";
import { Box } from "../struct/3d/Box";
import { Sphere } from "../struct/3d/Sphere";
import { BufferAttribute, Float32BufferAttribute, Uint16BufferAttribute, Uint32BufferAttribute } from "./buffer-attribute";
import { isBufferArray, TypedArray } from "./types";
import { Vec2 } from "../math/Vec2";
import { Vec4 } from "../math/Vec4";
import { verctorToNumbers } from "../alg/common";
export interface IGeometry {
position: number[];
normal?: number[];
index?: number[];
uv?: number[];
uv2?: number[];
tangent?: number[];
}
export interface IBufferGeometry {
position: Float32Array | undefined;
index?: Uint32Array | Uint16Array
normal?: Float32Array;
uv?: Float32Array;
uv2?: Float32Array;
tangent?: Float32Array;
}
var _bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id
var _m1 = new Mat4();
var _offset = new Vec3();
var _box = new Box();
var _boxMorphTargets = new Box();
var _vector = new Vec3();
/**
* BufferType 几何体,用于独立计算几何体
*/
export class BufferGeometry {
name: string;
index: BufferAttribute | undefined;
morphAttributes: any;
morphTargetsRelative: boolean;
groups: { start: number; count: number; materialIndex?: number }[];
boundingBox: Box | undefined;
boundingSphere: Sphere | undefined;
drawRange: { start: number; count: number; };
attributes: { [key: string]: BufferAttribute };
parameters: any;
readonly isBufferGeometry: true = true;
uuid: string = "";
type: string = "BufferGeometry";
constructor() {
Object.defineProperty(this, 'id', { value: _bufferGeometryId += 2 });
this.name = '';
this.attributes = {};
this.morphAttributes = {};
this.morphTargetsRelative = false;
this.groups = [];
this.drawRange = { start: 0, count: Infinity };
}
/**
* 转化成BufferArray来计算
* @param geo
*/
setFromGeometry(geo: IGeometry) {
this.setAttribute('position', new Float32BufferAttribute(geo.position, 3))
if (geo.uv)
this.setAttribute('uv', new Float32BufferAttribute(geo.uv, 2));
if (geo.index)
this.setIndex(geo.index)
return this;
}
getIndex() {
return this.index;
}
setIndex(index: BufferAttribute | TypedArray | number[]) {
if (Array.isArray(index)) {
this.index = new (Vec.max(index as any) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1);
} else if (index instanceof BufferAttribute) {
this.index = index;
} else {
this.index = new (Vec.max(index as any) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1)
}
}
getAttribute(name: string) {
return this.attributes[name];
}
setAttribute(name: string, attribute: BufferAttribute) {
this.attributes[name] = attribute;
return this;
}
addAttribute(name: string, attribute: BufferAttribute | TypedArray | Array<number | Vec2 | Vec3 | Vec4>, itemSize: number = 1) {
if (Array.isArray(attribute)) {
if (attribute[0] instanceof Vec2) {
var nums = verctorToNumbers(attribute);
this.setAttribute(name, new Float32BufferAttribute(nums, 2))
} else if (attribute[0] instanceof Vec3) {
var nums = verctorToNumbers(attribute);
this.setAttribute(name, new Float32BufferAttribute(nums, 3))
} else if (attribute[0] instanceof Vec4) {
var nums = verctorToNumbers(attribute);
this.setAttribute(name, new Float32BufferAttribute(nums, 4))
} else if (!isNaN(attribute[0])) {
this.setAttribute(name, new Float32BufferAttribute(attribute, itemSize));
} else {
console.error("类型不存在");
}
}
else if (attribute instanceof BufferAttribute) {
this.attributes[name] = attribute;
} else if (isBufferArray(attribute)) {
this.setAttribute(name, new BufferAttribute(attribute as any, itemSize))
}
return this;
}
deleteAttribute(name: string) {
delete this.attributes[name];
return this;
}
addGroup(start: number, count: number, materialIndex?: number) {
this.groups.push({
start: start,
count: count,
materialIndex: materialIndex !== undefined ? materialIndex : 0
});
}
clearGroups() {
this.groups = [];
}
setDrawRange(start: number, count: number) {
this.drawRange.start = start;
this.drawRange.count = count;
}
applyMat4(matrix: Mat4) {
var position = this.attributes.position;
if (position !== undefined) {
position.applyMat4(matrix);
position.needsUpdate = true;
}
var normal = this.attributes.normal;
if (normal !== undefined) {
var normalMatrix = new Mat3().getNormalMat(matrix);
normal.applyNormalMat(normalMatrix);
normal.needsUpdate = true;
}
var tangent = this.attributes.tangent;
if (tangent !== undefined) {
tangent.transformDirection(matrix);
tangent.needsUpdate = true;
}
if (!this.boundingBox) {
this.computeBoundingBox();
}
if (!this.boundingSphere) {
this.computeBoundingSphere();
}
return this;
}
rotateX(angle: number) {
// rotate geometry around world x-axis
_m1.makeRotationX(angle);
this.applyMat4(_m1);
return this;
}
rotateY(angle: number) {
// rotate geometry around world y-axis
_m1.makeRotationY(angle);
this.applyMat4(_m1);
return this;
}
rotateZ(angle: number) {
// rotate geometry around world z-axis
_m1.makeRotationZ(angle);
this.applyMat4(_m1);
return this;
}
translate(x: number, y: number, z: number) {
// translate geometry
_m1.makeTranslation(x, y, z);
this.applyMat4(_m1);
return this;
}
scale(x: number, y: number, z: number) {
// scale geometry
_m1.makeScale(x, y, z);
this.applyMat4(_m1);
return this;
}
lookAt(vector: Vec3) {
_m1.lookAt(v3(), vector, Vec3.UnitY);
this.applyMat4(_m1);
return this;
}
center() {
this.computeBoundingBox();
this.boundingBox!.getCenter(_offset).negate();
this.translate(_offset.x, _offset.y, _offset.z);
return this;
}
setFromObject(object: any) {
// 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.setAttribute('position', positions.copyVec3sArray(geometry.vertices));
this.setAttribute('color', colors.copyColorsArray(geometry.colors));
if (geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length) {
var lineDistances = new Float32BufferAttribute(geometry.lineDistances.length, 1);
this.setAttribute('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;
}
setFromPoints(points: Vec3[]) {
var position = [];
for (var i = 0, l = points.length; i < l; i++) {
var point = points[i];
position.push(point.x, point.y, point.z || 0);
}
this.setAttribute('position', new Float32BufferAttribute(position, 3));
return this;
}
updateFromObject(object: any) {
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.copyVec3sArray(geometry.vertices);
attribute.needsUpdate = true;
}
geometry.verticesNeedUpdate = false;
}
if (geometry.normalsNeedUpdate === true) {
attribute = this.attributes.normal;
if (attribute !== undefined) {
attribute.copyVec3sArray(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.copyVec2sArray(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(geometry: any) {
// geometry.__directGeometry = new DirectGeometry().fromGeometry(geometry);
// return this.fromDirectGeometry(geometry.__directGeometry);
// }
// fromDirectGeometry(geometry) {
// var positions = new Float32Array(geometry.vertices.length * 3);
// this.setAttribute('position', new BufferAttribute(positions, 3).copyVec3sArray(geometry.vertices));
// if (geometry.normals.length > 0) {
// var normals = new Float32Array(geometry.normals.length * 3);
// this.setAttribute('normal', new BufferAttribute(normals, 3).copyVec3sArray(geometry.normals));
// }
// if (geometry.colors.length > 0) {
// var colors = new Float32Array(geometry.colors.length * 3);
// this.setAttribute('color', new BufferAttribute(colors, 3).copyColorsArray(geometry.colors));
// }
// if (geometry.uvs.length > 0) {
// var uvs = new Float32Array(geometry.uvs.length * 2);
// this.setAttribute('uv', new BufferAttribute(uvs, 2).copyVec2sArray(geometry.uvs));
// }
// if (geometry.uvs2.length > 0) {
// var uvs2 = new Float32Array(geometry.uvs2.length * 2);
// this.setAttribute('uv2', new BufferAttribute(uvs2, 2).copyVec2sArray(geometry.uvs2));
// }
// // 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.data.length * 3, 3);
// attribute.name = morphTarget.name;
// array.push(attribute.copyVec3sArray(morphTarget.data));
// }
// this.morphAttributes[name] = array;
// }
// // skinning
// if (geometry.skinIndices.length > 0) {
// var skinIndices = new Float32BufferAttribute(geometry.skinIndices.length * 4, 4);
// this.setAttribute('skinIndex', skinIndices.copyVec4sArray(geometry.skinIndices));
// }
// if (geometry.skinWeights.length > 0) {
// var skinWeights = new Float32BufferAttribute(geometry.skinWeights.length * 4, 4);
// this.setAttribute('skinWeight', skinWeights.copyVec4sArray(geometry.skinWeights));
// }
// //
// if (geometry.boundingSphere !== null) {
// this.boundingSphere = geometry.boundingSphere.clone();
// }
// if (geometry.boundingBox !== null) {
// this.boundingBox = geometry.boundingBox.clone();
// }
// return this;
// }
computeBoundingBox() {
if (!this.boundingBox) {
this.boundingBox = new Box();
}
var position = this.attributes.position;
var morphAttributesPosition = this.morphAttributes.position;
if (position) {
this.boundingBox!.setFromBufferAttribute(position);
// process morph attributes if present
if (morphAttributesPosition) {
for (var i = 0, il = morphAttributesPosition.length; i < il; i++) {
var morphAttribute = morphAttributesPosition[i];
_box.setFromBufferAttribute(morphAttribute);
if (this.morphTargetsRelative) {
_vector.addVecs(this.boundingBox.min, _box.min);
this.boundingBox.expandByPoint(_vector);
_vector.addVecs(this.boundingBox.max, _box.max);
this.boundingBox.expandByPoint(_vector);
} else {
this.boundingBox.expandByPoint(_box.min);
this.boundingBox.expandByPoint(_box.max);
}
}
}
} 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() {
if (!this.boundingSphere) {
this.boundingSphere = new Sphere();
}
var position = this.attributes.position;
var morphAttributesPosition = this.morphAttributes.position;
if (position) {
// first, find the center of the bounding sphere
var center = this.boundingSphere!.center;
_box.setFromBufferAttribute(position);
// process morph attributes if present
if (morphAttributesPosition) {
for (var i = 0, il: number = morphAttributesPosition.length; i < il; i++) {
var morphAttribute = morphAttributesPosition[i];
_boxMorphTargets.setFromBufferAttribute(morphAttribute);
if (this.morphTargetsRelative) {
_vector.addVecs(_box.min, _boxMorphTargets.min);
_box.expandByPoint(_vector);
_vector.addVecs(_box.max, _boxMorphTargets.max);
_box.expandByPoint(_vector);
} else {
_box.expandByPoint(_boxMorphTargets.min);
_box.expandByPoint(_boxMorphTargets.max);
}
}
}
_box.getCenter(center);
// second, try 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: number = position.count; i < il; i++) {
_vector.fromBufferAttribute(position, i);
maxRadiusSq = Math.max(maxRadiusSq, center!.distanceToSquared(_vector));
}
// process morph attributes if present
if (morphAttributesPosition) {
for (var i = 0, il: number = morphAttributesPosition.length; i < il; i++) {
var morphAttribute = morphAttributesPosition[i];
var morphTargetsRelative = this.morphTargetsRelative;
for (var j = 0, jl = morphAttribute.count; j < jl; j++) {
_vector.fromBufferAttribute(morphAttribute, j);
if (morphTargetsRelative) {
_offset.fromBufferAttribute(position, j);
_vector.add(_offset);
}
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() {
// backwards compatibility
}
computeVertexNormals() {
var index = this.index;
var attributes = this.attributes;
if (attributes.position) {
var positions = attributes.position.array;
if (attributes.normal === undefined) {
this.setAttribute('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 Vec3(), pB = new Vec3(), pC = new Vec3();
var cb = new Vec3(), ab = new Vec3();
// indexed elements
if (index) {
var indices = index.array;
for (var i = 0, il = index.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.subVecs(pC, pB);
ab.subVecs(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.subVecs(pC, pB);
ab.subVecs(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(geometry: BufferGeometry, offset: number) {
if (!(geometry && geometry.isBufferGeometry)) {
console.error('THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry);
return;
}
if (offset === undefined) {
offset = 0;
console.warn(
'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. '
+ 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.'
);
}
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 attributeOffset = attribute2.itemSize * offset;
var length = Math.min(attributeArray2.length, attributeArray1.length - attributeOffset);
for (var i = 0, j = attributeOffset; i < length; i++, j++) {
attributeArray1[j] = attributeArray2[i];
}
}
return this;
}
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);
}
}
toFlat() {
var indices = this.index!.array;
var attributes = this.attributes;
var geometry2 = new BufferGeometry();
function convertBufferAttribute(attribute: BufferAttribute, indices: ArrayLike<number>) {
var array = attribute.array;
var itemSize = attribute.itemSize;
var array2 = new (array as any).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++];
}
}
return new BufferAttribute(array2, itemSize);
}
for (var name in attributes) {
var attribute = attributes[name];
var newAttribute = convertBufferAttribute(attribute, indices);
geometry2.setAttribute(name, newAttribute);
}
const indices2 = indices.map((v: number, i: number) => i);
geometry2.setIndex(indices2);
return geometry2;
}
toNonIndexed() {
function convertBufferAttribute(attribute: BufferAttribute, indices: ArrayLike<number>) {
var array = attribute.array;
var itemSize = attribute.itemSize;
var array2 = new (array as any).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++];
}
}
return new BufferAttribute(array2, itemSize);
}
//
if (this.index === undefined) {
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;
// attributes
for (var name in attributes) {
var attribute = attributes[name];
var newAttribute = convertBufferAttribute(attribute, indices);
geometry2.setAttribute(name, newAttribute);
}
// morph attributes
var morphAttributes = this.morphAttributes;
for (name in morphAttributes) {
var morphArray = [];
var morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes
for (var i = 0, il = morphAttribute.length; i < il; i++) {
var attribute: BufferAttribute = morphAttribute[i];
var newAttribute = convertBufferAttribute(attribute, indices);
morphArray.push(newAttribute);
}
geometry2.morphAttributes[name] = morphArray;
}
geometry2.morphTargetsRelative = this.morphTargetsRelative;
// groups
var groups = this.groups;
for (var i = 0, l = groups.length; i < l; i++) {
var group = groups[i];
geometry2.addGroup(group.start, group.count, group.materialIndex);
}
return geometry2;
}
toJSON() {
var data: any = {
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 (Object.keys(this.userData).length > 0) data.userData = this.userData;
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) {
data.data.index = {
type: index.array.constructor.name,
array: Array.prototype.slice.call(index.array)
};
}
var attributes = this.attributes;
for (var key in attributes) {
var attribute = attributes[key];
var attributeData: any = attribute.toJSON();
if (attribute.name !== '') attributeData.name = attribute.name;
data.data.attributes[key] = attributeData;
}
var morphAttributes: any = {};
var hasMorphAttributes = false;
for (var key in this.morphAttributes) {
var attributeArray = this.morphAttributes[key];
var array = [];
for (var i = 0, il = attributeArray.length; i < il; i++) {
var attribute: BufferAttribute = attributeArray[i];
var attributeData: any = attribute.toJSON();
if (attribute.name !== '') attributeData.name = attribute.name;
array.push(attributeData);
}
if (array.length > 0) {
morphAttributes[key] = array;
hasMorphAttributes = true;
}
}
if (hasMorphAttributes) {
data.data.morphAttributes = morphAttributes;
data.data.morphTargetsRelative = this.morphTargetsRelative;
}
var groups = this.groups;
if (groups.length > 0) {
data.data.groups = JSON.parse(JSON.stringify(groups));
}
var boundingSphere = this.boundingSphere;
if (boundingSphere) {
data.data.boundingSphere = {
center: boundingSphere.center.toArray(),
radius: boundingSphere.radius
};
}
return data;
}
userData(userData: any) {
throw new Error("Method not implemented.");
}
clone() {
/*
// 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(source: BufferGeometry) {
var name, i, l;
// reset
this.attributes = {};
this.morphAttributes = {};
this.groups = [];
// name
this.name = source.name;
// index
var index = source.index;
if (index) {
this.setIndex(index.clone());
}
// attributes
var attributes = source.attributes;
for (name in attributes) {
var attribute = attributes[name];
this.setAttribute(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;
}
this.morphTargetsRelative = source.morphTargetsRelative;
// 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) {
this.boundingBox = boundingBox.clone();
}
// bounding sphere
var boundingSphere = source.boundingSphere;
if (boundingSphere) {
this.boundingSphere = boundingSphere.clone();
}
// draw range
this.drawRange.start = source.drawRange.start;
this.drawRange.count = source.drawRange.count;
// user data
this.userData = source.userData;
return this;
}
}