support for multiple {buffers, meshes, primitives, vertex layouts}

This commit is contained in:
Zack Buhman 2026-07-21 13:12:36 -05:00
parent 2dfd749180
commit f781e5f134
2 changed files with 116 additions and 73 deletions

32
gltf.js
View File

@ -17,7 +17,9 @@ function parseGltfChunks(buffer)
console.assert(magic === 0x46546C67); console.assert(magic === 0x46546C67);
console.assert(version === 2); console.assert(version === 2);
const gltf = {}; const gltf = {
bin: [],
};
var offset = 12; var offset = 12;
while ((length - offset) > 0) { while ((length - offset) > 0) {
@ -34,8 +36,7 @@ function parseGltfChunks(buffer)
gltf.json = object; gltf.json = object;
} }
if (chunkType === 0x004E4942) { if (chunkType === 0x004E4942) {
console.assert(!("bin" in gltf)); gltf.bin.push({buffer: buffer, offset: offset, length: chunkLength});
gltf.bin = {buffer: buffer, offset: offset, length: chunkLength};
} }
offset += chunkLength; offset += chunkLength;
@ -127,24 +128,29 @@ function vertexFormat(componentType, type)
} }
} }
function primitiveVertexBufferLayouts(gltf, primitive) function primitiveAccessors(primitive)
{ {
const accessors = [ return [
gltf.json.accessors[primitive.attributes["POSITION"]], primitive.attributes["POSITION"],
gltf.json.accessors[primitive.attributes["NORMAL"]], primitive.attributes["NORMAL"],
gltf.json.accessors[primitive.attributes["TEXCOORD_0"]], primitive.attributes["TEXCOORD_0"],
primitive.attributes["TANGENT"],
]; ];
}
function vertexBufferLayouts(gltf, accessors)
{
const layouts = []; const layouts = [];
for (let i = 0; i < accessors.length; i++) { for (let i = 0; i < accessors.length; i++) {
const componentType = accessors[i].componentType; const accessor = gltf.json.accessors[accessors[i]];
const type = accessors[i].type; const componentType = accessor.componentType;
const type = accessor.type;
const bufferView = gltf.json.bufferViews[accessors[i].bufferView]; const bufferView = gltf.json.bufferViews[accessor.bufferView];
const arrayStride = "byteStride" in bufferView ? bufferView.byteStride : attributeStride(componentType, type); const arrayStride = "byteStride" in bufferView ? bufferView.byteStride : attributeStride(componentType, type);
const offset = "byteOffset" in accessors[i] ? accessors[i].byteOffset : 0; const offset = "byteOffset" in accessor ? accessor.byteOffset : 0;
const layout = { const layout = {
arrayStride: arrayStride, arrayStride: arrayStride,
attributes: [{ attributes: [{
@ -159,4 +165,4 @@ function primitiveVertexBufferLayouts(gltf, primitive)
return layouts; return layouts;
} }
export { parseGltfChunks, primitiveVertexBufferLayouts }; export { parseGltfChunks, vertexBufferLayouts, primitiveAccessors };

107
index2.js
View File

@ -1,23 +1,29 @@
import { getPath } from "./common.js"; import { getPath } from "./common.js";
import { parseGltfChunks, primitiveVertexBufferLayouts } from "./gltf.js"; import { parseGltfChunks, vertexBufferLayouts, primitiveAccessors } from "./gltf.js";
class GltfRenderer { class GltfRenderer {
constructor(device, canvasFormat, shaderModule, gltf, primitive, bufferLayouts) constructor(device, canvasFormat, shaderModule, gltf)
{ {
this.gltf = gltf; this.gltf = gltf;
this.primitive = primitive;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// buffer // buffer
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
console.assert(gltf.json.buffers[0].byteLength == gltf.bin.length); this.buffers = [];
this.vertexBuffer = device.createBuffer({
label: "gltf vertex buffer 0", console.assert(gltf.json.buffers.length === gltf.bin.length);
size: gltf.bin.length, for (let i = 0; i < gltf.bin.length; i++) {
console.assert(gltf.json.buffers[i].byteLength == gltf.bin[i].length);
const buffer = device.createBuffer({
label: `gltf vertex buffer {i}`,
size: gltf.bin[i].length,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, usage: GPUBufferUsage.INDEX | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
}); });
device.queue.writeBuffer(this.vertexBuffer, 0, gltf.bin.buffer, gltf.bin.offset, gltf.bin.length); device.queue.writeBuffer(buffer, 0,
gltf.bin[i].buffer, gltf.bin[i].offset, gltf.bin[i].length);
this.buffers.push(buffer);
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// pipeline // pipeline
@ -39,7 +45,28 @@ class GltfRenderer {
bindGroupLayouts: bindGroupLayouts, bindGroupLayouts: bindGroupLayouts,
}); });
this.renderPipeline = device.createRenderPipeline({ // by vertex buffer layout
const renderPipelineKeys = {};
this.renderPipelines = {};
this.meshes = [];
for (let i = 0; i < gltf.json.meshes.length; i++) {
const mesh = gltf.json.meshes[i];
const meshPrimitives = [];
for (let j = 0; j < mesh.primitives.length; j++) {
const accessors = primitiveAccessors(mesh.primitives[j]);
const key = accessors.join(";");
meshPrimitives.push([key, accessors]);
renderPipelineKeys[key] = accessors;
}
this.meshes.push(meshPrimitives);
}
for (let [key, accessors] of Object.entries(renderPipelineKeys)) {
const bufferLayouts = vertexBufferLayouts(gltf, accessors);
const renderPipeline = device.createRenderPipeline({
label: "gltf pipeline", label: "gltf pipeline",
layout: pipelineLayout, layout: pipelineLayout,
vertex: { vertex: {
@ -64,6 +91,9 @@ class GltfRenderer {
}, },
}); });
this.renderPipelines[key] = renderPipeline;
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// bind group // bind group
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -87,6 +117,27 @@ class GltfRenderer {
]; ];
} }
renderPrimitive(renderPass, accessors, indices)
{
for (let i = 0; i < accessors.length; i++) {
const accessor = accessors[i];
const bufferViewIndex = this.gltf.json.accessors[accessor].bufferView;
const bufferView = this.gltf.json.bufferViews[bufferViewIndex];
renderPass.setVertexBuffer(i, this.buffers[bufferView.buffer], bufferView.byteOffset, bufferView.byteLength);
}
const indicesAccessor = this.gltf.json.accessors[indices];
const indicesBufferView = this.gltf.json.bufferViews[indicesAccessor.bufferView];
renderPass.setIndexBuffer(this.buffers[indicesBufferView.buffer],
"uint16",
indicesBufferView.byteOffset,
indicesBufferView.byteLength);
renderPass.setBindGroup(0, this.bindGroups[0]);
renderPass.drawIndexed(indicesAccessor.count);
}
render(device, colorView, depthTexture) render(device, colorView, depthTexture)
{ {
const encoder = device.createCommandEncoder(); const encoder = device.createCommandEncoder();
@ -105,26 +156,19 @@ class GltfRenderer {
}, },
}); });
renderPass.setPipeline(this.renderPipeline); var lastKey = undefined;
for (let i = 0; i < this.gltf.json.meshes.length; i++) {
const mesh = this.gltf.json.meshes[i];
for (let j = 0; j < mesh.primitives.length; j++) {
const [key, accessors] = this.meshes[i][j];
if (key !== lastKey)
renderPass.setPipeline(this.renderPipelines[key]);
lastKey = key;
const attributes = ["POSITION", "NORMAL", "TEXCOORD_0"]; const primitive = mesh.primitives[j];
for (let i = 0; i < attributes.length; i++) { this.renderPrimitive(renderPass, accessors, primitive.indices);
const attributeIndex = this.primitive.attributes[attributes[i]]; }
const bufferViewIndex = this.gltf.json.accessors[attributeIndex].bufferView;
const bufferView = this.gltf.json.bufferViews[bufferViewIndex];
renderPass.setVertexBuffer(i, this.vertexBuffer, bufferView.byteOffset, bufferView.byteLength);
} }
const indicesAccessor = this.gltf.json.accessors[this.primitive.indices];
const indicesBufferView = this.gltf.json.bufferViews[indicesAccessor.bufferView];
renderPass.setIndexBuffer(this.vertexBuffer,
"uint16",
indicesBufferView.byteOffset,
indicesBufferView.byteLength);
renderPass.setBindGroup(0, this.bindGroups[0]);
renderPass.drawIndexed(indicesAccessor.count);
renderPass.end(); renderPass.end();
@ -140,20 +184,13 @@ async function loadGltf(device, canvasFormat)
const gltf = parseGltfChunks(buffer); const gltf = parseGltfChunks(buffer);
console.log(gltf); console.log(gltf);
const primitive = gltf.json.meshes[0].primitives[0];
//console.log(primitive.indices);
//console.log(primitive.attributes);
const bufferLayouts = primitiveVertexBufferLayouts(gltf, primitive);
console.log(bufferLayouts);
const code = await getPath("gltf.wgsl"); const code = await getPath("gltf.wgsl");
const shaderModule = device.createShaderModule({ const shaderModule = device.createShaderModule({
label: "gltf module", label: "gltf module",
code: await code, code: await code,
}); });
const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf, primitive, bufferLayouts); const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf);
return renderer; return renderer;
} }