Skip to content

Commit

Permalink
GLTFLoader updates (#245)
Browse files Browse the repository at this point in the history
* comments house cleaning

* add int8array component type

* detect glb by magic header

* house cleaning

* prettier

* comments house cleaning

* prettier

* webp and draco support initial commit

* prettier

* comments house cleaning

* check for extensions

* draco index attribute hard coded as uint32array

* comments house cleaning

* update gltf-transform link

* explicitly set mode cors

* update to basis universal v1.50

* comments house cleaning

* add draco decoder libs

* house cleaning

* update basis universal link

* draco worker initial commit

* prettier

* update attributedata types

* disable console logs

* comments house cleaning

* update gltf loader description

* add gltf draco examples

* bufferviewindex check

* prettier

* test webp texture compression

* rename gltf draco webp example

* explicitly set mode cors for binary fetch

* fix skins primitives map

* prefer record syntax

* record syntax for extensions and extras
  • Loading branch information
pschroen authored Jan 26, 2025
1 parent 36b50ad commit 58dde6a
Show file tree
Hide file tree
Showing 30 changed files with 2,093 additions and 227 deletions.
Binary file added examples/assets/gltf/cottage-basis-draco.glb
Binary file not shown.
Binary file added examples/assets/gltf/hershel-optimized.glb
Binary file not shown.
23 changes: 14 additions & 9 deletions examples/assets/libs/basis/BasisWorker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// https://github.com/mrdoob/three.js/tree/dev/examples/js/libs/basis
// TODO
// [ ] WASM support

// https://github.com/mrdoob/three.js/tree/master/examples/jsm/libs/basis
importScripts('basis_transcoder.js');

let KTX2File, moduleReadyResolve;
let KTX2File;

let moduleReadyResolve;
const moduleReady = new Promise((res) => (moduleReadyResolve = res));

// CONSTS
Expand Down Expand Up @@ -78,12 +83,12 @@ const INTERNAL_FORMAT_MAP = {
};

// Init basis transcoder
const initStartTime = performance.now();
// const initStartTime = performance.now();
BASIS().then((module) => {
({ KTX2File } = module);
module.initializeBasis();
const elapsed = performance.now() - initStartTime;
// console.log('worker init time', elapsed.toFixed(2) + 'ms');
// const elapsed = performance.now() - initStartTime;
// console.log('worker init time', `${elapsed.toFixed(2)}ms`);
moduleReadyResolve();
});

Expand All @@ -94,7 +99,7 @@ addEventListener('message', ({ data }) => {
async function transcode({ id, buffer, supportedFormat }) {
await moduleReady;

const startTime = performance.now();
// const startTime = performance.now();
const ktx2File = new KTX2File(new Uint8Array(buffer));

const width = ktx2File.getWidth();
Expand Down Expand Up @@ -143,7 +148,7 @@ async function transcode({ id, buffer, supportedFormat }) {
if (basisFormat === BASIS_FORMAT.RGB565 || basisFormat === BASIS_FORMAT.RGBA4444) {
// 16 bit formats require Uint16 typed array
const dst16 = new Uint16Array(dstSize / 2);
for (i = 0; i < dstSize / 2; ++i) {
for (let i = 0; i < dstSize / 2; ++i) {
dst16[i] = dst[i * 2] + dst[i * 2 + 1] * 256;
}
dst = dst16;
Expand All @@ -169,8 +174,8 @@ async function transcode({ id, buffer, supportedFormat }) {
ktx2File.close();
ktx2File.delete();

const elapsed = performance.now() - startTime;
// console.log('transcode time', elapsed.toFixed(2) + 'ms');
// const elapsed = performance.now() - startTime;
// console.log('transcode time', `${elapsed.toFixed(2)}ms`);

// Needed for texture properties
image.isCompressedTexture = isCompressed;
Expand Down
18 changes: 8 additions & 10 deletions examples/assets/libs/basis/basis_transcoder.js

Large diffs are not rendered by default.

Binary file modified examples/assets/libs/basis/basis_transcoder.wasm
Binary file not shown.
126 changes: 126 additions & 0 deletions examples/assets/libs/draco/DracoWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// TODO
// [ ] Cleanup decoder
// [ ] Point clouds
// [ ] WASM support

// https://github.com/mrdoob/three.js/tree/master/examples/jsm/libs/draco
importScripts('gltf/draco_decoder.js');

let draco;
let decoder;

let moduleReadyResolve;
const moduleReady = new Promise((res) => (moduleReadyResolve = res));

// Create the Draco decoder
// const initStartTime = performance.now();
DracoDecoderModule().then((module) => {
draco = module;
decoder = new draco.Decoder();
// const elapsed = performance.now() - initStartTime;
// console.log('worker init time', `${elapsed.toFixed(2)}ms`);
moduleReadyResolve();
});

addEventListener('message', ({ data }) => {
decodeGeometry(data);
});

async function decodeGeometry({ id, buffer, config }) {
await moduleReady;

// const startTime = performance.now();
const array = new Int8Array(buffer);
const { attributeIds, attributeTypes } = config;

const dracoGeometry = new draco.Mesh();
const decodingStatus = decoder.DecodeArrayToMesh(array, array.byteLength, dracoGeometry);

if (!decodingStatus.ok() || dracoGeometry.ptr === 0)
postMessage({ id, error: `Decoding failed: ${decodingStatus.error_msg()}` });

const geometry = { index: null, attributes: [] };

// Gather all vertex attributes
for (const attributeName in attributeIds) {
const attributeType = self[attributeTypes[attributeName]];
const attribute = decoder.GetAttributeByUniqueId(dracoGeometry, attributeIds[attributeName]);
const attributeResult = decodeAttribute(dracoGeometry, attributeName, attributeType, attribute);

geometry.attributes.push(attributeResult);
}

// Add index
geometry.index = decodeIndex(dracoGeometry);

draco.destroy(dracoGeometry);
// draco.destroy(decoder);

// const elapsed = performance.now() - startTime;
// console.log('decodeGeometry time', `${elapsed.toFixed(2)}ms`);

// Transfer buffers
const buffers = geometry.attributes.map((attr) => attr.array.buffer);
if (geometry.index) buffers.push(geometry.index.array.buffer);

postMessage(
{
id,
geometry,
},
buffers
);
}

// https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/DRACOLoader.js

function decodeIndex(dracoGeometry) {
const numFaces = dracoGeometry.num_faces();
const numIndices = numFaces * 3;
const byteLength = numIndices * 4;

const ptr = draco._malloc(byteLength);
decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr);
const index = new Uint32Array(draco.HEAPF32.buffer, ptr, numIndices).slice();
draco._free(ptr);

return { array: index, itemSize: 1 };
}

function decodeAttribute(dracoGeometry, attributeName, attributeType, attribute) {
const numComponents = attribute.num_components();
const numPoints = dracoGeometry.num_points();
const numValues = numPoints * numComponents;
const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
const dataType = getDracoDataType(attributeType);

const ptr = draco._malloc(byteLength);
decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, attribute, dataType, byteLength, ptr);
const array = new attributeType(draco.HEAPF32.buffer, ptr, numValues).slice();
draco._free(ptr);

return {
name: attributeName,
array,
itemSize: numComponents,
};
}

function getDracoDataType(attributeType) {
switch (attributeType) {
case Float32Array:
return draco.DT_FLOAT32;
case Int8Array:
return draco.DT_INT8;
case Int16Array:
return draco.DT_INT16;
case Int32Array:
return draco.DT_INT32;
case Uint8Array:
return draco.DT_UINT8;
case Uint16Array:
return draco.DT_UINT16;
case Uint32Array:
return draco.DT_UINT32;
}
}
34 changes: 34 additions & 0 deletions examples/assets/libs/draco/draco_decoder.js

Large diffs are not rendered by default.

Binary file added examples/assets/libs/draco/draco_decoder.wasm
Binary file not shown.
33 changes: 33 additions & 0 deletions examples/assets/libs/draco/draco_encoder.js

Large diffs are not rendered by default.

Loading

0 comments on commit 58dde6a

Please sign in to comment.