Object3d.tstypescript
import { htmlColorToFloat32Array } from "./utils/shared";
import type { TypedArray } from "./utils/shared";
import { Vec3 } from "./math/Vec3";
import type { NumberArray3 } from "./math/Vec3";
import { DEGREES_DOUBLE, MAX, MIN, RADIANS_HALF } from "./math";
import { Mat4 } from "./math/Mat4";
import { Obj } from "./utils/objParser";
import type { IObjGeometry } from "./utils/objParser";
function getColor(color?: number[] | TypedArray | string): Float32Array {
if (color instanceof Array) {
return new Float32Array(color);
} else if (typeof color === "string") {
return htmlColorToFloat32Array(color);
}
return new Float32Array([1, 1, 1, 1]);
}
// function getColor3v(color?: NumberArray3 | TypedArray | string): Float32Array {
// let res = new Float32Array([1.0, 1.0, 1.0]);
// if (color instanceof Array) {
// res[0] = color[0];
// res[1] = color[1];
// res[2] = color[2];
// } else if (typeof color === "string") {
// let c = htmlColorToFloat32Array(color);
// res[0] = c[0];
// res[1] = c[1];
// res[2] = c[2];
// }
// return res;
// }
interface IObject3dParams {
name?: string;
vertices?: number[];
texCoords?: number[];
indices?: number[];
normals?: number[];
center?: boolean;
color?: number[] | TypedArray | string;
scale?: number | Vec3;
metallic?: number;
roughness?: number;
ambientOcclusion?: number;
colorTextureSrc?: string;
normalTextureSrc?: string;
ambientOcclusionTextureSrc?: string;
metallicRoughnessTextureSrc?: string;
colorTextureImage?: HTMLImageElement;
normalTextureImage?: HTMLImageElement;
ambientOcclusionTextureImage?: HTMLImageElement;
metallicRoughnessTextureImage?: HTMLImageElement;
}
type MaterialProperties = Pick<IObject3dParams, "metallic" | "roughness" | "ambientOcclusion">;
class Object3d {
protected _name: string;
protected _vertices: number[];
protected _numVertices: number;
protected _texCoords: number[];
protected _indices: number[];
protected _normals: number[];
public color: Float32Array;
public metallic: number;
public roughness: number;
public ambientOcclusion: number;
public colorTextureSrc: string | null;
public colorTextureImage: HTMLImageElement | null;
public normalTextureSrc: string | null;
public normalTextureImage: HTMLImageElement | null;
public ambientOcclusionTextureSrc: string | null;
public ambientOcclusionTextureImage: HTMLImageElement | null;
public metallicRoughnessTextureSrc: string | null;
public metallicRoughnessTextureImage: HTMLImageElement | null;
public center: Vec3;
constructor(data: IObject3dParams = {}) {
this._name = data.name || "noname";
this._vertices = data.vertices || [];
this._numVertices = this._vertices.length / 3;
this._texCoords = data.texCoords || new Array(2 * this._numVertices);
// if (data.texCoords) {
// this._texCoords = data.texCoords;
// } else {
// this._texCoords = Array.from({length: 2 * this._numVertices}, (_, i) => 0);
// }
this.color = getColor(data.color);
this.metallic = data.metallic ?? 0;
this.roughness = data.roughness ?? 0;
this.ambientOcclusion = data.ambientOcclusion ?? 1;
this.colorTextureSrc = data.colorTextureSrc || null;
this.colorTextureImage = data.colorTextureImage || null;
this.normalTextureSrc = data.normalTextureSrc || null;
this.normalTextureImage = data.normalTextureImage || null;
this.ambientOcclusionTextureSrc = data.ambientOcclusionTextureSrc || null;
this.ambientOcclusionTextureImage = data.ambientOcclusionTextureImage || null;
this.metallicRoughnessTextureSrc = data.metallicRoughnessTextureSrc || null;
this.metallicRoughnessTextureImage = data.metallicRoughnessTextureImage || null;
if (data.scale) {
let s = data.scale;
let scale: Vec3;
if (typeof s === "number") {
scale = new Vec3(s, s, s);
} else {
scale = s;
}
Object3d.scale(this._vertices, scale);
}
if (data.center) {
Object3d.centering(this._vertices);
}
this.center = Object3d.getCenter(this._vertices);
if (data.indices) {
this._indices = data.indices;
this._normals = data.normals || Object3d.getNormals(this._vertices, this._indices);
} else {
this._normals = data.normals || Object3d.getNormals(this._vertices);
this._indices = new Array(this._vertices.length / 3);
for (let i = 0, len = this._indices.length; i < len; i++) {
this._indices[i] = i;
}
}
}
static getCenter(verts: number[]): Vec3 {
let min_x = MAX,
min_y = MAX,
min_z = MAX,
max_x = MIN,
max_y = MIN,
max_z = MIN;
for (let i = 0, len = verts.length; i < len; i += 3) {
let x = verts[i],
y = verts[i + 1],
z = verts[i + 2];
if (x < min_x) min_x = x;
if (y < min_y) min_y = y;
if (z < min_z) min_z = z;
if (x > max_x) max_x = x;
if (y > max_y) max_y = y;
if (z > max_z) max_z = z;
}
return new Vec3(min_x + (max_x - min_x) * 0.5, min_y + (max_y - min_y) * 0.5, min_z + (max_z - min_z) * 0.5);
}
static centering(verts: number[]) {
let c = Object3d.getCenter(verts);
for (let i = 0, len = verts.length; i < len; i += 3) {
verts[i] -= c.x;
verts[i + 1] -= c.y;
verts[i + 2] -= c.z;
}
}
/**
* Sets the material properties for the 3D object.
*
* @param {MaterialProperties} data - An object containing material properties.
* @param {string | NumberArray3} [data.ambient] - Ambient color of the material, as a hex string (e.g., "#ffffff") or an array of three numbers [r, g, b].
* @param {string | NumberArray3} [data.diffuse] - Diffuse color of the material.
* @param {string | NumberArray3} [data.specular] - Specular color of the material.
* @param {number} [data.shininess=100] - Shininess coefficient of the material, controlling specular highlight size.
*/
public setMaterialProperties(data: MaterialProperties) {
this.metallic = data.metallic ?? this.metallic;
this.roughness = data.roughness ?? this.roughness;
this.ambientOcclusion = data.ambientOcclusion ?? this.ambientOcclusion;
return this;
}
public setColor(color: number[] | TypedArray | string): this {
this.color = getColor(color);
return this;
}
public centering(): this {
Object3d.centering(this._vertices);
return this;
}
public applyMat4(m: Mat4): this {
for (let i = 0, len = this._vertices.length; i < len; i += 3) {
let v = new Vec3(this._vertices[i], this._vertices[i + 1], this._vertices[i + 2]),
n = new Vec3(this._normals[i], this._normals[i + 1], this._normals[i + 2]);
v = m.mulVec3(v);
n = m.mulVec3(n);
this._vertices[i] = v.x;
this._vertices[i + 1] = v.y;
this._vertices[i + 2] = v.z;
this._normals[i] = n.x;
this._normals[i + 1] = n.y;
this._normals[i + 2] = n.z;
}
return this;
}
public scale(s: Vec3): this {
Object3d.scale(this._vertices, s);
return this;
}
public translate(v: Vec3): this {
for (let i = 0, len = this._vertices.length; i < len; i += 3) {
this._vertices[i] += v.x;
this._vertices[i + 1] += v.y;
this._vertices[i + 2] += v.z;
}
return this;
}
public get name(): string {
return this._name;
}
public get vertices(): number[] {
return this._vertices;
}
public get normals(): number[] {
return this._normals;
}
public get indices(): number[] {
return this._indices;
}
public get texCoords(): number[] {
return this._texCoords;
}
public get numVertices(): number {
return this._numVertices;
}
static scale(vertices: number[], s: Vec3) {
for (let i = 0; i < vertices.length; i += 3) {
vertices[i] *= s.x;
vertices[i + 1] *= s.y;
vertices[i + 2] *= s.z;
}
}
static centroid(vertices: number[]) {
let minX = 1000.0,
minY = 1000.0,
minZ = 1000.0,
maxX = -1000.0,
maxY = -1000.0,
maxZ = -1000.0;
for (let i = 0; i < vertices.length; i += 3) {
let x = vertices[i],
y = vertices[i + 1],
z = vertices[i + 2];
if (x < minX) minX = x;
if (y < minY) minY = y;
if (z < minZ) minZ = z;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
if (z > maxZ) maxZ = z;
}
return [minX + (maxX - minX) * 0.5, minY + (maxY - minY) * 0.5, minZ + (maxZ - minZ) * 0.5];
}
static translate(vertices: number[], v: NumberArray3) {
for (let i = 0; i < vertices.length; i += 3) {
vertices[i] += v[0];
vertices[i + 1] += v[1];
vertices[i + 2] += v[2];
}
}
static getNormals(vertices: number[], indices?: number[]): number[] {
let res = new Array(vertices.length);
if (indices) {
res.fill(0);
for (let i = 0; i < indices.length; i += 3) {
let t03 = indices[i] * 3,
t13 = indices[i + 1] * 3,
t23 = indices[i + 2] * 3,
v0_x = vertices[t03],
v0_y = vertices[t03 + 1],
v0_z = vertices[t03 + 2],
v1_x = vertices[t13],
v1_y = vertices[t13 + 1],
v1_z = vertices[t13 + 2],
v2_x = vertices[t23],
v2_y = vertices[t23 + 1],
v2_z = vertices[t23 + 2],
vv0_x = v1_x - v0_x,
vv0_y = v1_y - v0_y,
vv0_z = v1_z - v0_z,
vv1_x = v2_x - v0_x,
vv1_y = v2_y - v0_y,
vv1_z = v2_z - v0_z,
n_x = vv0_y * vv1_z - vv0_z * vv1_y,
n_y = vv0_z * vv1_x - vv0_x * vv1_z,
n_z = vv0_x * vv1_y - vv0_y * vv1_x;
res[t03] += n_x;
res[t03 + 1] += n_y;
res[t03 + 2] += n_z;
res[t13] += n_x;
res[t13 + 1] += n_y;
res[t13 + 2] += n_z;
res[t23] += n_x;
res[t23 + 1] += n_y;
res[t23 + 2] += n_z;
}
for (let i = 0; i < res.length; i += 3) {
let n_x = res[i],
n_y = res[i + 1],
n_z = res[i + 2],
l = Math.sqrt(n_x * n_x + n_y * n_y + n_z * n_z);
if (l) {
res[i] = n_x / l;
res[i + 1] = n_y / l;
res[i + 2] = n_z / l;
}
}
return res;
}
for (let i = 0; i < vertices.length; i += 9) {
let t03 = i,
t13 = i + 3,
t23 = i + 6,
v0_x = vertices[t03],
v0_y = vertices[t03 + 1],
v0_z = vertices[t03 + 2],
v1_x = vertices[t13],
v1_y = vertices[t13 + 1],
v1_z = vertices[t13 + 2],
v2_x = vertices[t23],
v2_y = vertices[t23 + 1],
v2_z = vertices[t23 + 2],
vv0_x = v1_x - v0_x,
vv0_y = v1_y - v0_y,
vv0_z = v1_z - v0_z,
vv1_x = v2_x - v0_x,
vv1_y = v2_y - v0_y,
vv1_z = v2_z - v0_z,
n_x = vv0_y * vv1_z - vv0_z * vv1_y,
n_y = vv0_z * vv1_x - vv0_x * vv1_z,
n_z = vv0_x * vv1_y - vv0_y * vv1_x;
let l = Math.sqrt(n_x * n_x + n_y * n_y + n_z * n_z);
n_x /= l;
n_y /= l;
n_z /= l;
res[t03] = n_x;
res[t03 + 1] = n_y;
res[t03 + 2] = n_z;
res[t13] = n_x;
res[t13 + 1] = n_y;
res[t13 + 2] = n_z;
res[t23] = n_x;
res[t23 + 1] = n_y;
res[t23 + 2] = n_z;
}
return res;
}
static createSphere(
lonBands: number = 16,
latBands: number = 16,
radius: number = 1.0,
offsetX: number = 0,
offsetY: number = 0,
offsetZ: number = 0
): Object3d {
let vertices = [],
indices = [],
normals = [];
for (let latNumber = 0; latNumber <= latBands; latNumber++) {
let theta = (latNumber * Math.PI) / latBands;
let sinTheta = Math.sin(theta);
let cosTheta = Math.cos(theta);
for (let longNumber = 0; longNumber <= lonBands; longNumber++) {
let phi = (longNumber * 2 * Math.PI) / lonBands;
let sinPhi = Math.sin(phi);
let cosPhi = Math.cos(phi);
let x = cosPhi * sinTheta + offsetX;
let y = cosTheta + offsetY;
let z = sinPhi * sinTheta + offsetZ;
//var u = 1 - (longNumber / lonBands);
//var v = latNumber / latBands;
normals.push(x);
normals.push(y);
normals.push(z);
//texCoords.push(u);
//texCoords.push(v);
vertices.push(radius * x);
vertices.push(radius * y);
vertices.push(radius * z);
}
}
for (let latNumber = 0; latNumber < latBands; latNumber++) {
for (let longNumber = 0; longNumber < lonBands; longNumber++) {
let first = latNumber * (lonBands + 1) + longNumber;
let second = first + lonBands + 1;
indices.push(first);
indices.push(first + 1);
indices.push(second);
indices.push(second);
indices.push(first + 1);
indices.push(second + 1);
}
}
return new Object3d({
vertices: vertices,
normals: normals,
indices: indices
});
}
static createDisc(
radius: number = 1.0,
height: number = 0.0,
radialSegments: number = 8,
isTop: boolean = true,
startIndex: number = 0,
offsetX: number = 0,
offsetY: number = 0,
offsetZ: number = 0
): Object3d {
let vertices = [],
indices = [],
normals = [];
let thetaStart = 0.0,
thetaLength = Math.PI * 2;
let sign = isTop ? 1.0 : -1.0;
let centerIndexStart = startIndex;
for (let x = 1; x <= radialSegments; x++) {
vertices.push(offsetX, height * sign + offsetY, offsetZ);
normals.push(0, sign, 0);
//texCoords.push(0.5, 0.5);
startIndex++;
}
let centerIndexEnd = startIndex;
for (let x = 0; x <= radialSegments; x++) {
let u = x / radialSegments;
let theta = u * thetaLength + thetaStart;
let cosTheta = Math.cos(theta);
let sinTheta = Math.sin(theta);
vertices.push(radius * sinTheta + offsetX, height * sign + offsetY, radius * cosTheta + offsetZ);
normals.push(0, sign, 0);
//texCoords.push((cosTheta * 0.5) + 0.5, (sinTheta * 0.5 * sign) + 0.5);
startIndex++;
}
for (let x = 0; x < radialSegments; x++) {
let c = centerIndexStart + x,
i = centerIndexEnd + x;
if (isTop) {
indices.push(i, i + 1, c);
} else {
indices.push(i + 1, i, c);
}
}
return new Object3d({
vertices: vertices,
normals: normals,
indices: indices
});
}
/**
* Returns scale parameters for a frustum geoObject created with only Object3d.createFrustum();
* @param length
* @param horizontalAngle
* @param verticalAngle
*/
static getFrustumScaleByCameraAngles(length: number, horizontalAngle: number, verticalAngle: number): Vec3 {
return new Vec3(
2.0 * length * Math.tan(RADIANS_HALF * horizontalAngle),
2.0 * length * Math.tan(RADIANS_HALF * verticalAngle),
length
);
}
/**
* Returns scale parameters for a frustum geoObject created with only Object3d.createFrustum();
* @param length
* @param horizontalAngle
* @param aspectRatio
*/
static getFrustumScaleByCameraAspectRatio(length: number, horizontalAngle: number, aspectRatio: number): Vec3 {
let vAngle = DEGREES_DOUBLE * Math.atan(Math.tan(RADIANS_HALF * horizontalAngle) / aspectRatio);
return Object3d.getFrustumScaleByCameraAngles(length, horizontalAngle, vAngle);
}
static createFrustum(
length: number = 1,
width: number = 1,
height: number = 1,
xOffset: number = 0,
yOffset: number = 0,
zOffset: number = 0
): Object3d {
width *= 0.5;
height *= 0.5;
return new Object3d({
vertices: [
//
//inside
//
//top
xOffset,
yOffset,
zOffset,
-1 * width + xOffset,
height + yOffset,
-1 * length + zOffset,
width + xOffset,
height + yOffset,
-1 * length + zOffset,
//bottop
xOffset,
yOffset,
zOffset,
width + xOffset,
-1 * height + yOffset,
-1 * length + zOffset,
-1 * width + xOffset,
-1 * height + yOffset,
-1 * length + zOffset,
//right
xOffset,
yOffset,
zOffset,
width + xOffset,
height + yOffset,
-1 * length + zOffset,
width + xOffset,
-1 * height + yOffset,
-1 * length + zOffset,
//left
xOffset,
yOffset,
zOffset,
-1 * width + xOffset,
-1 * height + yOffset,
-1 * length + zOffset,
-1 * width + xOffset,
height + yOffset,
-1 * length + zOffset,
//
// outside
//
//top
xOffset,
yOffset,
zOffset,
width + xOffset,
height + yOffset,
-1 * length + zOffset,
-1 * width + xOffset,
height + yOffset,
-1 * length + zOffset,
//bottop
xOffset,
yOffset,
zOffset,
-1 * width + xOffset,
-1 * height + yOffset,
-1 * length + zOffset,
width + xOffset,
-1 * height + yOffset,
-1 * length + zOffset,
//right
xOffset,
yOffset,
zOffset,
width + xOffset,
-1 * height + yOffset,
-1 * length + zOffset,
width + xOffset,
height + yOffset,
-1 * length + zOffset,
//left
xOffset,
yOffset,
zOffset,
-1 * width + xOffset,
height + yOffset,
-1 * length + zOffset,
-1 * width + xOffset,
-1 * height + yOffset,
-1 * length + zOffset
]
});
}
static createCylinder(
radiusTop: number = 1.0,
radiusBottom: number = 1.0,
height: number = 1.0,
radialSegments: number = 32,
heightSegments: number = 1.0,
isTop: boolean = true,
isBottom: boolean = true,
offsetX: number = 0,
offsetY: number = 0,
offsetZ: number = 0
): Object3d {
let vertices: number[] = [],
indices: number[] = [],
normals: number[] = [];
let thetaStart = 0.0,
thetaLength = Math.PI * 2;
let index = 0;
let indexArray = [];
let normal = new Vec3();
let slope = (radiusBottom - radiusTop) / height;
for (let y = 0; y <= heightSegments; y++) {
let indexRow = [];
let v = y / heightSegments;
let radius = v * (radiusBottom - radiusTop) + radiusTop;
for (let x = 0; x <= radialSegments; x++) {
let u = x / radialSegments;
let theta = u * thetaLength + thetaStart;
let sinTheta = Math.sin(theta),
cosTheta = Math.cos(theta);
vertices.push(radius * sinTheta + offsetX, -v * height + height + offsetY, radius * cosTheta + offsetZ);
normal.set(sinTheta, slope, cosTheta).normalize();
normals.push(normal.x, normal.y, normal.z);
//texCoords.push(u, 1 - v);
indexRow.push(index++);
}
indexArray.push(indexRow);
}
for (let x = 0; x < radialSegments; x++) {
for (let y = 0; y < heightSegments; y++) {
let a = indexArray[y][x],
b = indexArray[y + 1][x],
c = indexArray[y + 1][x + 1],
d = indexArray[y][x + 1];
indices.push(a, b, d);
indices.push(b, c, d);
}
}
if (radiusTop !== 0.0 && isTop) {
let cap = Object3d.createDisc(radiusTop, height, radialSegments, true, index, offsetX, offsetY, offsetZ);
vertices.push(...cap.vertices);
normals.push(...cap.normals);
indices.push(...cap.indices);
}
if (radiusBottom !== 0.0 && isBottom) {
let cap = Object3d.createDisc(
radiusBottom,
0,
radialSegments,
false,
index + (isTop ? 1 + 2 * radialSegments : 0),
offsetX,
offsetY,
offsetZ
);
vertices.push(...cap.vertices);
normals.push(...cap.normals);
indices.push(...cap.indices);
}
return new Object3d({
vertices: vertices,
normals: normals,
indices: indices
});
}
static createCube(
length: number = 1,
height: number = 1,
depth: number = 1,
xOffset: number = 0,
yOffset: number = 0,
zOffset: number = 0
): Object3d {
let l = length * 0.5 + xOffset,
h = height * 0.5 + yOffset,
d = depth * 0.5 + zOffset;
return new Object3d({
vertices: [
//bottom
-l,
-h,
d,
l,
-h,
-d,
l,
-h,
d,
-l,
-h,
d,
-l,
-h,
-d,
l,
-h,
-d,
//top
-l,
h,
d,
l,
h,
d,
l,
h,
-d,
-l,
h,
d,
l,
h,
-d,
-l,
h,
-d,
//front
-l,
-h,
d,
l,
-h,
d,
-l,
h,
d,
-l,
h,
d,
l,
-h,
d,
l,
h,
d,
//back
-l,
-h,
-d,
-l,
h,
-d,
l,
-h,
-d,
-l,
h,
-d,
l,
h,
-d,
l,
-h,
-d,
//left
l,
-h,
d,
l,
-h,
-d,
l,
h,
d,
l,
h,
d,
l,
-h,
-d,
l,
h,
-d,
//right
-l,
-h,
d,
-l,
h,
d,
-l,
-h,
-d,
-l,
h,
d,
-l,
h,
-d,
-l,
-h,
-d
]
});
}
static createPlane(
width: number = 1,
height: number = 1,
xOffset: number = 0,
yOffset: number = 0,
zOffset: number = 0
): Object3d {
let sx = width * 0.5,
sy = yOffset,
sz = height * 0.5;
return new Object3d({
vertices: [
//bottom
-sx + xOffset,
sy,
sz + zOffset,
sx + xOffset,
sy,
-sz + zOffset,
sx + xOffset,
sy,
sz + zOffset,
-sx + xOffset,
sy,
sz + zOffset,
-sx + xOffset,
sy,
-sz + zOffset,
sx + xOffset,
sy,
-sz + zOffset,
//top
-sx + xOffset,
sy,
sz + zOffset,
sx + xOffset,
sy,
sz + zOffset,
sx + xOffset,
sy,
-sz + zOffset,
-sx + xOffset,
sy,
sz + zOffset,
sx + xOffset,
sy,
-sz + zOffset,
-sx + xOffset,
sy,
-sz + zOffset
]
});
}
static createArrow(back: number = 0.0, height: number = 2.1, front: number = -15): Object3d {
return new Object3d({
vertices: [
0,
height,
0,
7,
0,
6,
0,
0,
front,
0,
0,
back,
7,
0,
6,
0,
height,
0,
-7,
0,
6,
0,
0,
back,
0,
height,
0,
-7,
0,
6,
0,
height,
0,
0,
0,
front,
-7,
0,
6,
0,
0,
front,
0,
0,
back,
0,
0,
back,
0,
0,
front,
7,
0,
6
]
});
}
static async readFileObj(objFile: File, mtlFile?: File | null, baseUrl?: string): Promise<Object3d[]> {
let obj = new Obj();
const res = await obj.readFile(objFile, mtlFile);
let materials = res.materials;
return res.geometries.map((obj: IObjGeometry) => {
let mat = materials[obj.material] || {};
return new Object3d({
name: obj.object,
vertices: obj.data.vertices,
normals: obj.data.normals,
texCoords: obj.data.texCoords,
color: mat.color || mat.diffuse,
colorTextureSrc: baseUrl ? `${baseUrl}/${mat.colorTexture}` : mat.colorTexture,
normalTextureSrc: baseUrl ? `${baseUrl}/${mat.normalTexture}` : mat.normalTexture,
metallicRoughnessTextureSrc: baseUrl
? `${baseUrl}/${mat.metallicRoughnessTexture}`
: mat.metallicRoughnessTexture
});
});
}
static async loadObj(src: string): Promise<Object3d[]> {
let obj = new Obj();
const res = await obj.load(src);
let materials = res.materials;
return res.geometries.map((obj: IObjGeometry) => {
let mat = materials[obj.material] || {};
return new Object3d({
name: obj.object,
vertices: obj.data.vertices,
normals: obj.data.normals,
texCoords: obj.data.texCoords,
color: mat.color || mat.diffuse,
colorTextureSrc: mat.colorTexture,
normalTextureSrc: mat.normalTexture,
metallicRoughnessTextureSrc: mat.metallicRoughnessTexture
});
});
}
public merge(other: Object3d): Object3d {
const offset = this._vertices.length / 3;
let temp = this._vertices.length;
this._vertices.length = temp + other._vertices.length;
for (let i = 0; i < other._vertices.length; i++) {
this._vertices[temp + i] = other._vertices[i];
}
temp = this._normals.length;
this._normals.length = temp + other._normals.length;
for (let i = 0; i < other._normals.length; i++) {
this._normals[temp + i] = other._normals[i];
}
temp = this._texCoords.length;
this._texCoords.length = temp + other._texCoords.length;
for (let i = 0; i < other._texCoords.length; i++) {
this._texCoords[temp + i] = other._texCoords[i];
}
temp = this._indices.length;
this._indices.length = temp + other._indices.length;
for (let i = 0; i < other._indices.length; i++) {
this._indices[temp + i] = other._indices[i] + offset;
}
this._numVertices = this._vertices.length / 3;
return this;
}
static merge(objects: Object3d[], maxSize?: number): Object3d {
let res = new Object3d();
let size = maxSize ? maxSize : objects.length;
for (let i = 0; i < size; i++) {
res.merge(objects[i]);
}
return res;
}
}
export { Object3d };