|
@@ -4,9 +4,10 @@
|
|
|
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
|
|
*/
|
|
|
|
|
|
+import { BaseValues } from '../../mol-gl/renderable/schema';
|
|
|
import { asciiWrite } from '../../mol-io/common/ascii';
|
|
|
import { IsNativeEndianLittle, flipByteOrder } from '../../mol-io/common/binary';
|
|
|
-import { Vec3, Mat3, Mat4 } from '../../mol-math/linear-algebra';
|
|
|
+import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
|
|
import { RuntimeContext } from '../../mol-task';
|
|
|
import { Color } from '../../mol-util/color/color';
|
|
|
import { arrayMinMax, fillSerial } from '../../mol-util/array';
|
|
@@ -15,11 +16,8 @@ import { MeshExporter, AddMeshInput } from './mesh-exporter';
|
|
|
|
|
|
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
|
|
const v3fromArray = Vec3.fromArray;
|
|
|
-const v3transformMat4 = Vec3.transformMat4;
|
|
|
-const v3transformMat3 = Vec3.transformMat3;
|
|
|
const v3normalize = Vec3.normalize;
|
|
|
const v3toArray = Vec3.toArray;
|
|
|
-const mat3directionTransform = Mat3.directionTransform;
|
|
|
|
|
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
|
|
|
|
|
@@ -29,7 +27,8 @@ export type GlbData = {
|
|
|
|
|
|
export class GlbExporter extends MeshExporter<GlbData> {
|
|
|
readonly fileExtension = 'glb';
|
|
|
- private primitives: Record<string, any>[] = [];
|
|
|
+ private nodes: Record<string, any>[] = [];
|
|
|
+ private meshes: Record<string, any>[] = [];
|
|
|
private accessors: Record<string, any>[] = [];
|
|
|
private bufferViews: Record<string, any>[] = [];
|
|
|
private binaryBuffer: ArrayBuffer[] = [];
|
|
@@ -47,20 +46,213 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
|
|
return [ min, max ];
|
|
|
}
|
|
|
|
|
|
- protected async addMeshWithColors(input: AddMeshInput) {
|
|
|
- const { mesh, meshes, values, isGeoTexture, webgl, ctx } = input;
|
|
|
-
|
|
|
- const t = Mat4();
|
|
|
- const n = Mat3();
|
|
|
+ private createGeometryBuffers(vertices: Float32Array, normals: Float32Array, indices: Uint32Array | undefined, vertexCount: number, drawCount: number, isGeoTexture: boolean) {
|
|
|
const tmpV = Vec3();
|
|
|
const stride = isGeoTexture ? 4 : 3;
|
|
|
|
|
|
+ const vertexArray = new Float32Array(vertexCount * 3);
|
|
|
+ const normalArray = new Float32Array(vertexCount * 3);
|
|
|
+ let indexArray: Uint32Array;
|
|
|
+
|
|
|
+ // position
|
|
|
+ for (let i = 0; i < vertexCount; ++i) {
|
|
|
+ v3fromArray(tmpV, vertices, i * stride);
|
|
|
+ v3toArray(tmpV, vertexArray, i * 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ // normal
|
|
|
+ for (let i = 0; i < vertexCount; ++i) {
|
|
|
+ v3fromArray(tmpV, normals, i * stride);
|
|
|
+ v3normalize(tmpV, tmpV);
|
|
|
+ v3toArray(tmpV, normalArray, i * 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ // face
|
|
|
+ if (isGeoTexture) {
|
|
|
+ indexArray = new Uint32Array(drawCount);
|
|
|
+ fillSerial(indexArray);
|
|
|
+ } else {
|
|
|
+ indexArray = indices!.slice(0, drawCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ const [ vertexMin, vertexMax ] = GlbExporter.vecMinMax(vertexArray, 3);
|
|
|
+ const [ normalMin, normalMax ] = GlbExporter.vecMinMax(normalArray, 3);
|
|
|
+ const [ indexMin, indexMax ] = arrayMinMax(indexArray);
|
|
|
+
|
|
|
+ // binary buffer
|
|
|
+ let vertexBuffer = vertexArray.buffer;
|
|
|
+ let normalBuffer = normalArray.buffer;
|
|
|
+ let indexBuffer = indexArray.buffer;
|
|
|
+ if (!IsNativeEndianLittle) {
|
|
|
+ vertexBuffer = flipByteOrder(new Uint8Array(vertexBuffer), 4);
|
|
|
+ normalBuffer = flipByteOrder(new Uint8Array(normalBuffer), 4);
|
|
|
+ indexBuffer = flipByteOrder(new Uint8Array(indexBuffer), 4);
|
|
|
+ }
|
|
|
+ this.binaryBuffer.push(vertexBuffer, normalBuffer, indexBuffer);
|
|
|
+
|
|
|
+ // buffer views
|
|
|
+ const bufferViewOffset = this.bufferViews.length;
|
|
|
+
|
|
|
+ this.bufferViews.push({
|
|
|
+ buffer: 0,
|
|
|
+ byteOffset: this.byteOffset,
|
|
|
+ byteLength: vertexBuffer.byteLength,
|
|
|
+ target: 34962 // ARRAY_BUFFER
|
|
|
+ });
|
|
|
+ this.byteOffset += vertexBuffer.byteLength;
|
|
|
+
|
|
|
+ this.bufferViews.push({
|
|
|
+ buffer: 0,
|
|
|
+ byteOffset: this.byteOffset,
|
|
|
+ byteLength: normalBuffer.byteLength,
|
|
|
+ target: 34962 // ARRAY_BUFFER
|
|
|
+ });
|
|
|
+ this.byteOffset += normalBuffer.byteLength;
|
|
|
+
|
|
|
+ this.bufferViews.push({
|
|
|
+ buffer: 0,
|
|
|
+ byteOffset: this.byteOffset,
|
|
|
+ byteLength: indexBuffer.byteLength,
|
|
|
+ target: 34963 // ELEMENT_ARRAY_BUFFER
|
|
|
+ });
|
|
|
+ this.byteOffset += indexBuffer.byteLength;
|
|
|
+
|
|
|
+ // accessors
|
|
|
+ const accessorOffset = this.accessors.length;
|
|
|
+ this.accessors.push({
|
|
|
+ bufferView: bufferViewOffset,
|
|
|
+ byteOffset: 0,
|
|
|
+ componentType: 5126, // FLOAT
|
|
|
+ count: vertexCount,
|
|
|
+ type: 'VEC3',
|
|
|
+ max: vertexMax,
|
|
|
+ min: vertexMin
|
|
|
+ });
|
|
|
+ this.accessors.push({
|
|
|
+ bufferView: bufferViewOffset + 1,
|
|
|
+ byteOffset: 0,
|
|
|
+ componentType: 5126, // FLOAT
|
|
|
+ count: vertexCount,
|
|
|
+ type: 'VEC3',
|
|
|
+ max: normalMax,
|
|
|
+ min: normalMin
|
|
|
+ });
|
|
|
+ this.accessors.push({
|
|
|
+ bufferView: bufferViewOffset + 2,
|
|
|
+ byteOffset: 0,
|
|
|
+ componentType: 5125, // UNSIGNED_INT
|
|
|
+ count: drawCount,
|
|
|
+ type: 'SCALAR',
|
|
|
+ max: [ indexMax ],
|
|
|
+ min: [ indexMin ]
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ vertexAccessorIndex: accessorOffset,
|
|
|
+ normalAccessorIndex: accessorOffset + 1,
|
|
|
+ indexAccessorIndex: accessorOffset + 2
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ private createColorBuffer(values: BaseValues, groups: Float32Array | Uint8Array, vertexCount: number, instanceIndex: number, isGeoTexture: boolean, interpolatedColors: Uint8Array) {
|
|
|
const groupCount = values.uGroupCount.ref.value;
|
|
|
const colorType = values.dColorType.ref.value;
|
|
|
+ const uColor = values.uColor.ref.value;
|
|
|
const tColor = values.tColor.ref.value.array;
|
|
|
const uAlpha = values.uAlpha.ref.value;
|
|
|
const dTransparency = values.dTransparency.ref.value;
|
|
|
const tTransparency = values.tTransparency.ref.value;
|
|
|
+
|
|
|
+ const colorArray = new Float32Array(vertexCount * 4);
|
|
|
+
|
|
|
+ for (let i = 0; i < vertexCount; ++i) {
|
|
|
+ let color: Color;
|
|
|
+ switch (colorType) {
|
|
|
+ case 'uniform':
|
|
|
+ color = Color.fromNormalizedArray(uColor, 0);
|
|
|
+ break;
|
|
|
+ case 'instance':
|
|
|
+ color = Color.fromArray(tColor, instanceIndex * 3);
|
|
|
+ break;
|
|
|
+ case 'group': {
|
|
|
+ const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
|
|
|
+ color = Color.fromArray(tColor, group * 3);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 'groupInstance': {
|
|
|
+ const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
|
|
|
+ color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 'vertex':
|
|
|
+ color = Color.fromArray(tColor, i * 3);
|
|
|
+ break;
|
|
|
+ case 'vertexInstance':
|
|
|
+ color = Color.fromArray(tColor, (instanceIndex * vertexCount + i) * 3);
|
|
|
+ break;
|
|
|
+ case 'volume':
|
|
|
+ color = Color.fromArray(interpolatedColors!, i * 3);
|
|
|
+ break;
|
|
|
+ case 'volumeInstance':
|
|
|
+ color = Color.fromArray(interpolatedColors!, (instanceIndex * vertexCount + i) * 3);
|
|
|
+ break;
|
|
|
+ default: throw new Error('Unsupported color type.');
|
|
|
+ }
|
|
|
+
|
|
|
+ let alpha = uAlpha;
|
|
|
+ if (dTransparency) {
|
|
|
+ const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
|
|
|
+ const transparency = tTransparency.array[instanceIndex * groupCount + group] / 255;
|
|
|
+ alpha *= 1 - transparency;
|
|
|
+ }
|
|
|
+
|
|
|
+ color = Color.sRGBToLinear(color);
|
|
|
+ Color.toArrayNormalized(color, colorArray, i * 4);
|
|
|
+ colorArray[i * 4 + 3] = alpha;
|
|
|
+ }
|
|
|
+
|
|
|
+ const [ colorMin, colorMax ] = GlbExporter.vecMinMax(colorArray, 4);
|
|
|
+
|
|
|
+ // binary buffer
|
|
|
+ let colorBuffer = colorArray.buffer;
|
|
|
+ if (!IsNativeEndianLittle) {
|
|
|
+ colorBuffer = flipByteOrder(new Uint8Array(colorBuffer), 4);
|
|
|
+ }
|
|
|
+ this.binaryBuffer.push(colorBuffer);
|
|
|
+
|
|
|
+ // buffer view
|
|
|
+ const bufferViewOffset = this.bufferViews.length;
|
|
|
+ this.bufferViews.push({
|
|
|
+ buffer: 0,
|
|
|
+ byteOffset: this.byteOffset,
|
|
|
+ byteLength: colorBuffer.byteLength,
|
|
|
+ target: 34962 // ARRAY_BUFFER
|
|
|
+ });
|
|
|
+ this.byteOffset += colorBuffer.byteLength;
|
|
|
+
|
|
|
+ // accessor
|
|
|
+ const accessorOffset = this.accessors.length;
|
|
|
+ this.accessors.push({
|
|
|
+ bufferView: bufferViewOffset,
|
|
|
+ byteOffset: 0,
|
|
|
+ componentType: 5126, // FLOAT
|
|
|
+ count: vertexCount,
|
|
|
+ type: 'VEC4',
|
|
|
+ max: colorMax,
|
|
|
+ min: colorMin
|
|
|
+ });
|
|
|
+
|
|
|
+ return accessorOffset;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected async addMeshWithColors(input: AddMeshInput) {
|
|
|
+ const { mesh, values, isGeoTexture, webgl, ctx } = input;
|
|
|
+
|
|
|
+ const t = Mat4();
|
|
|
+ const stride = isGeoTexture ? 4 : 3;
|
|
|
+
|
|
|
+ const colorType = values.dColorType.ref.value;
|
|
|
+ const dTransparency = values.dTransparency.ref.value;
|
|
|
const aTransform = values.aTransform.ref.value;
|
|
|
const instanceCount = values.uInstanceCount.ref.value;
|
|
|
|
|
@@ -69,212 +261,59 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
|
|
interpolatedColors = GlbExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
|
|
|
}
|
|
|
|
|
|
+ // instancing
|
|
|
+ const sameGeometryBuffers = mesh !== undefined;
|
|
|
+ const sameColorBuffer = sameGeometryBuffers && colorType !== 'instance' && !colorType.endsWith('Instance') && !dTransparency;
|
|
|
+ let vertexAccessorIndex: number;
|
|
|
+ let normalAccessorIndex: number;
|
|
|
+ let indexAccessorIndex: number;
|
|
|
+ let colorAccessorIndex: number;
|
|
|
+ let meshIndex: number;
|
|
|
+
|
|
|
await ctx.update({ isIndeterminate: false, current: 0, max: instanceCount });
|
|
|
|
|
|
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
|
|
|
if (ctx.shouldUpdate) await ctx.update({ current: instanceIndex + 1 });
|
|
|
|
|
|
- let vertices: Float32Array;
|
|
|
- let normals: Float32Array;
|
|
|
- let indices: Uint32Array | undefined;
|
|
|
- let groups: Float32Array | Uint8Array;
|
|
|
- let vertexCount: number;
|
|
|
- let drawCount: number;
|
|
|
- if (mesh !== undefined) {
|
|
|
- vertices = mesh.vertices;
|
|
|
- normals = mesh.normals;
|
|
|
- indices = mesh.indices;
|
|
|
- groups = mesh.groups;
|
|
|
- vertexCount = mesh.vertexCount;
|
|
|
- drawCount = mesh.drawCount;
|
|
|
- } else {
|
|
|
- const mesh = meshes![instanceIndex];
|
|
|
- vertices = mesh.vertexBuffer.ref.value;
|
|
|
- normals = mesh.normalBuffer.ref.value;
|
|
|
- indices = mesh.indexBuffer.ref.value;
|
|
|
- groups = mesh.groupBuffer.ref.value;
|
|
|
- vertexCount = mesh.vertexCount;
|
|
|
- drawCount = mesh.triangleCount * 3;
|
|
|
- }
|
|
|
-
|
|
|
- Mat4.fromArray(t, aTransform, instanceIndex * 16);
|
|
|
- mat3directionTransform(n, t);
|
|
|
-
|
|
|
- const vertexArray = new Float32Array(vertexCount * 3);
|
|
|
- const normalArray = new Float32Array(vertexCount * 3);
|
|
|
- const colorArray = new Float32Array(vertexCount * 4);
|
|
|
- let indexArray: Uint32Array;
|
|
|
-
|
|
|
- // position
|
|
|
- for (let i = 0; i < vertexCount; ++i) {
|
|
|
- v3transformMat4(tmpV, v3fromArray(tmpV, vertices, i * stride), t);
|
|
|
- v3toArray(tmpV, vertexArray, i * 3);
|
|
|
- }
|
|
|
+ // create a glTF mesh if needed
|
|
|
+ if (instanceIndex === 0 || !sameGeometryBuffers || !sameColorBuffer) {
|
|
|
+ const { vertices, normals, indices, groups, vertexCount, drawCount } = GlbExporter.getInstance(input, instanceIndex);
|
|
|
|
|
|
- // normal
|
|
|
- for (let i = 0; i < vertexCount; ++i) {
|
|
|
- v3fromArray(tmpV, normals, i * stride);
|
|
|
- v3transformMat3(tmpV, v3normalize(tmpV, tmpV), n);
|
|
|
- v3toArray(tmpV, normalArray, i * 3);
|
|
|
- }
|
|
|
-
|
|
|
- // color
|
|
|
- for (let i = 0; i < vertexCount; ++i) {
|
|
|
- let color: Color;
|
|
|
- switch (colorType) {
|
|
|
- case 'uniform':
|
|
|
- color = Color.fromNormalizedArray(values.uColor.ref.value, 0);
|
|
|
- break;
|
|
|
- case 'instance':
|
|
|
- color = Color.fromArray(tColor, instanceIndex * 3);
|
|
|
- break;
|
|
|
- case 'group': {
|
|
|
- const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
|
|
|
- color = Color.fromArray(tColor, group * 3);
|
|
|
- break;
|
|
|
- }
|
|
|
- case 'groupInstance': {
|
|
|
- const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
|
|
|
- color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
|
|
|
- break;
|
|
|
- }
|
|
|
- case 'vertex':
|
|
|
- color = Color.fromArray(tColor, i * 3);
|
|
|
- break;
|
|
|
- case 'vertexInstance':
|
|
|
- color = Color.fromArray(tColor, (instanceIndex * drawCount + i) * 3);
|
|
|
- break;
|
|
|
- case 'volume':
|
|
|
- color = Color.fromArray(interpolatedColors!, i * 3);
|
|
|
- break;
|
|
|
- case 'volumeInstance':
|
|
|
- color = Color.fromArray(interpolatedColors!, (instanceIndex * vertexCount + i) * 3);
|
|
|
- break;
|
|
|
- default: throw new Error('Unsupported color type.');
|
|
|
+ // create geometry buffers if needed
|
|
|
+ if (instanceIndex === 0 || !sameGeometryBuffers) {
|
|
|
+ const accessorIndices = this.createGeometryBuffers(vertices, normals, indices, vertexCount, drawCount, isGeoTexture);
|
|
|
+ vertexAccessorIndex = accessorIndices.vertexAccessorIndex;
|
|
|
+ normalAccessorIndex = accessorIndices.normalAccessorIndex;
|
|
|
+ indexAccessorIndex = accessorIndices.indexAccessorIndex;
|
|
|
}
|
|
|
|
|
|
- let alpha = uAlpha;
|
|
|
- if (dTransparency) {
|
|
|
- const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
|
|
|
- const transparency = tTransparency.array[instanceIndex * groupCount + group] / 255;
|
|
|
- alpha *= 1 - transparency;
|
|
|
+ // create a color buffer if needed
|
|
|
+ if (instanceIndex === 0 || !sameColorBuffer) {
|
|
|
+ colorAccessorIndex = this.createColorBuffer(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors!);
|
|
|
}
|
|
|
|
|
|
- color = Color.sRGBToLinear(color);
|
|
|
- Color.toArrayNormalized(color, colorArray, i * 4);
|
|
|
- colorArray[i * 4 + 3] = alpha;
|
|
|
+ // glTF mesh
|
|
|
+ meshIndex = this.meshes.length;
|
|
|
+ this.meshes.push({
|
|
|
+ primitives: [{
|
|
|
+ attributes: {
|
|
|
+ POSITION: vertexAccessorIndex!,
|
|
|
+ NORMAL: normalAccessorIndex!,
|
|
|
+ COLOR_0: colorAccessorIndex!
|
|
|
+ },
|
|
|
+ indices: indexAccessorIndex!,
|
|
|
+ material: 0
|
|
|
+ }]
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- // face
|
|
|
- if (isGeoTexture) {
|
|
|
- indexArray = new Uint32Array(drawCount);
|
|
|
- fillSerial(indexArray);
|
|
|
- } else {
|
|
|
- indexArray = indices!.slice(0, drawCount);
|
|
|
- }
|
|
|
-
|
|
|
- const [ vertexMin, vertexMax ] = GlbExporter.vecMinMax(vertexArray, 3);
|
|
|
- const [ normalMin, normalMax ] = GlbExporter.vecMinMax(normalArray, 3);
|
|
|
- const [ colorMin, colorMax ] = GlbExporter.vecMinMax(colorArray, 4);
|
|
|
- const [ indexMin, indexMax ] = arrayMinMax(indexArray);
|
|
|
-
|
|
|
- // binary buffer
|
|
|
- let vertexBuffer = vertexArray.buffer;
|
|
|
- let normalBuffer = normalArray.buffer;
|
|
|
- let colorBuffer = colorArray.buffer;
|
|
|
- let indexBuffer = indexArray.buffer;
|
|
|
- if (!IsNativeEndianLittle) {
|
|
|
- vertexBuffer = flipByteOrder(new Uint8Array(vertexBuffer), 4);
|
|
|
- normalBuffer = flipByteOrder(new Uint8Array(normalBuffer), 4);
|
|
|
- colorBuffer = flipByteOrder(new Uint8Array(colorBuffer), 4);
|
|
|
- indexBuffer = flipByteOrder(new Uint8Array(indexBuffer), 4);
|
|
|
- }
|
|
|
- this.binaryBuffer.push(vertexBuffer, normalBuffer, colorBuffer, indexBuffer);
|
|
|
-
|
|
|
- // buffer views
|
|
|
- const bufferViewOffset = this.bufferViews.length;
|
|
|
-
|
|
|
- this.bufferViews.push({
|
|
|
- buffer: 0,
|
|
|
- byteOffset: this.byteOffset,
|
|
|
- byteLength: vertexBuffer.byteLength,
|
|
|
- target: 34962 // ARRAY_BUFFER
|
|
|
- });
|
|
|
- this.byteOffset += vertexBuffer.byteLength;
|
|
|
-
|
|
|
- this.bufferViews.push({
|
|
|
- buffer: 0,
|
|
|
- byteOffset: this.byteOffset,
|
|
|
- byteLength: normalBuffer.byteLength,
|
|
|
- target: 34962 // ARRAY_BUFFER
|
|
|
- });
|
|
|
- this.byteOffset += normalBuffer.byteLength;
|
|
|
-
|
|
|
- this.bufferViews.push({
|
|
|
- buffer: 0,
|
|
|
- byteOffset: this.byteOffset,
|
|
|
- byteLength: colorBuffer.byteLength,
|
|
|
- target: 34962 // ARRAY_BUFFER
|
|
|
- });
|
|
|
- this.byteOffset += colorBuffer.byteLength;
|
|
|
-
|
|
|
- this.bufferViews.push({
|
|
|
- buffer: 0,
|
|
|
- byteOffset: this.byteOffset,
|
|
|
- byteLength: indexBuffer.byteLength,
|
|
|
- target: 34963 // ELEMENT_ARRAY_BUFFER
|
|
|
- });
|
|
|
- this.byteOffset += indexBuffer.byteLength;
|
|
|
-
|
|
|
- // accessors
|
|
|
- const accessorOffset = this.accessors.length;
|
|
|
- this.accessors.push({
|
|
|
- bufferView: bufferViewOffset,
|
|
|
- byteOffset: 0,
|
|
|
- componentType: 5126, // FLOAT
|
|
|
- count: vertexCount,
|
|
|
- type: 'VEC3',
|
|
|
- max: vertexMax,
|
|
|
- min: vertexMin
|
|
|
- });
|
|
|
- this.accessors.push({
|
|
|
- bufferView: bufferViewOffset + 1,
|
|
|
- byteOffset: 0,
|
|
|
- componentType: 5126, // FLOAT
|
|
|
- count: vertexCount,
|
|
|
- type: 'VEC3',
|
|
|
- max: normalMax,
|
|
|
- min: normalMin
|
|
|
- });
|
|
|
- this.accessors.push({
|
|
|
- bufferView: bufferViewOffset + 2,
|
|
|
- byteOffset: 0,
|
|
|
- componentType: 5126, // FLOAT
|
|
|
- count: vertexCount,
|
|
|
- type: 'VEC4',
|
|
|
- max: colorMax,
|
|
|
- min: colorMin
|
|
|
- });
|
|
|
- this.accessors.push({
|
|
|
- bufferView: bufferViewOffset + 3,
|
|
|
- byteOffset: 0,
|
|
|
- componentType: 5125, // UNSIGNED_INT
|
|
|
- count: drawCount,
|
|
|
- type: 'SCALAR',
|
|
|
- max: [ indexMax ],
|
|
|
- min: [ indexMin ]
|
|
|
- });
|
|
|
-
|
|
|
- // primitive
|
|
|
- this.primitives.push({
|
|
|
- attributes: {
|
|
|
- POSITION: accessorOffset,
|
|
|
- NORMAL: accessorOffset + 1,
|
|
|
- COLOR_0: accessorOffset + 2,
|
|
|
- },
|
|
|
- indices: accessorOffset + 3,
|
|
|
- material: 0
|
|
|
- });
|
|
|
+ // node
|
|
|
+ const node: Record<string, any> = {
|
|
|
+ mesh: meshIndex!
|
|
|
+ };
|
|
|
+ Mat4.fromArray(t, aTransform, instanceIndex * 16);
|
|
|
+ if (!Mat4.isIdentity(t)) node.matrix = t.slice();
|
|
|
+ this.nodes.push(node);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -286,14 +325,10 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
|
|
version: '2.0'
|
|
|
},
|
|
|
scenes: [{
|
|
|
- nodes: [ 0 ]
|
|
|
- }],
|
|
|
- nodes: [{
|
|
|
- mesh: 0
|
|
|
- }],
|
|
|
- meshes: [{
|
|
|
- primitives: this.primitives
|
|
|
+ nodes: fillSerial(new Array(this.nodes.length) as number[])
|
|
|
}],
|
|
|
+ nodes: this.nodes,
|
|
|
+ meshes: this.meshes,
|
|
|
buffers: [{
|
|
|
byteLength: binaryBufferLength,
|
|
|
}],
|