Files
scriptc/tests/fixtures/three/gltf.mjs
Chris Tate a07998f667 Expand static three.js workflows and host integration (#621)
* Expand static three.js workflows and host integration

* Preserve JavaScript class state in static three.js workflows

* Preserve inferred JavaScript behavior across static workflows

* Preserve inferred callables and native compatibility boundaries

* Retain annotated JavaScript factory exports and bootstrap-safe call spreads

* Preserve Buffer and child callback identities in static packages

* Clarify JavaScript symbol spread support
2026-10-02 19:56:34 -05:00

79 lines
3.9 KiB
JavaScript

import { AnimationMixer, Vector3, FileLoader } from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
// Resource transport belongs to the host. This fixture uses embedded buffers.
FileLoader.prototype.load = function () { throw new Error("external resource host required"); };
// A JSON scene needs no resource host.
const loader = new GLTFLoader();
const simple = await loader.parseAsync(JSON.stringify({
asset: { version: "2.0" }, scene: 0,
scenes: [{ nodes: [0] }],
nodes: [{ name: "hello", translation: [1, 2, 3] }],
}), "");
console.log("gltf", simple.scene.children[0].name, simple.scene.children[0].position.y);
// A self-contained GLB exercises binary buffers, typed accessors, PBR data,
// mesh construction and animation without fetch, DOM images or decoders.
const binary = new ArrayBuffer(76);
new Float32Array(binary, 0, 9).set([0, 0, 0, 1, 0, 0, 0, 1, 0]);
new Uint16Array(binary, 36, 3).set([0, 1, 2]);
new Float32Array(binary, 44, 2).set([0, 1]);
new Float32Array(binary, 52, 6).set([0, 0, 0, 2, 4, 6]);
const document = {
asset: { version: "2.0" }, scene: 0,
scenes: [{ name: "model", nodes: [0] }],
nodes: [{ name: "triangle", mesh: 0, translation: [3, 2, 1] }],
meshes: [{ primitives: [{ attributes: { POSITION: 0 }, indices: 1, material: 0 }] }],
materials: [{ name: "surface", pbrMetallicRoughness: {
baseColorFactor: [1, 0.5, 0.25, 1], metallicFactor: 0.75, roughnessFactor: 0.25,
} }],
buffers: [{ byteLength: 76 }],
bufferViews: [
{ buffer: 0, byteOffset: 0, byteLength: 36, target: 34962 },
{ buffer: 0, byteOffset: 36, byteLength: 6, target: 34963 },
{ buffer: 0, byteOffset: 44, byteLength: 8 },
{ buffer: 0, byteOffset: 52, byteLength: 24 },
],
accessors: [
{ bufferView: 0, componentType: 5126, count: 3, type: "VEC3", min: [0, 0, 0], max: [1, 1, 0] },
{ bufferView: 1, componentType: 5123, count: 3, type: "SCALAR" },
{ bufferView: 2, componentType: 5126, count: 2, type: "SCALAR", min: [0], max: [1] },
{ bufferView: 3, componentType: 5126, count: 2, type: "VEC3" },
],
animations: [{ name: "move", samplers: [{ input: 2, output: 3, interpolation: "LINEAR" }],
channels: [{ sampler: 0, target: { node: 0, path: "translation" } }] }],
};
const json = new TextEncoder().encode(JSON.stringify(document));
const jsonLength = Math.ceil(json.length / 4) * 4;
const glb = new ArrayBuffer(12 + 8 + jsonLength + 8 + binary.byteLength);
const header = new DataView(glb);
header.setUint32(0, 0x46546c67, true);
header.setUint32(4, 2, true);
header.setUint32(8, glb.byteLength, true);
header.setUint32(12, jsonLength, true);
header.setUint32(16, 0x4e4f534a, true);
const jsonChunk = new Uint8Array(glb, 20, jsonLength);
jsonChunk.fill(0x20);
jsonChunk.set(json);
header.setUint32(20 + jsonLength, binary.byteLength, true);
header.setUint32(24 + jsonLength, 0x004e4942, true);
new Uint8Array(glb, 28 + jsonLength).set(new Uint8Array(binary));
const model = await loader.parseAsync(glb, "");
const mesh = model.scene.children[0];
console.log("glb", model.scene.name, mesh.name, mesh.type, mesh.geometry.attributes.position.count, mesh.geometry.index.getX(2));
console.log("pbr", mesh.material.name, mesh.material.type, mesh.material.color.g, mesh.material.roughness, mesh.material.metalness);
console.log("accessor", mesh.geometry.attributes.position.getX(1), mesh.geometry.attributes.position.getY(2));
const mixer = new AnimationMixer(model.scene);
const clip = model.animations[0];
const action = mixer.clipAction(clip).play();
mixer.update(0.5);
console.log("animation", model.animations[0].name, model.animations[0].duration, mesh.position.x, mesh.position.y, mesh.position.z);
model.scene.updateMatrixWorld(true);
const vertex = mesh.getVertexPosition(1, new Vector3()).applyMatrix4(mesh.matrixWorld);
console.log("vertex", vertex.x, vertex.y, vertex.z);
mixer.stopAllAction();
mixer.uncacheRoot(model.scene);
mesh.geometry.dispose();
mesh.material.dispose();