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

107
index2.js
View File

@ -1,23 +1,29 @@
import { getPath } from "./common.js";
import { parseGltfChunks, primitiveVertexBufferLayouts } from "./gltf.js";
import { parseGltfChunks, vertexBufferLayouts, primitiveAccessors } from "./gltf.js";
class GltfRenderer {
constructor(device, canvasFormat, shaderModule, gltf, primitive, bufferLayouts)
constructor(device, canvasFormat, shaderModule, gltf)
{
this.gltf = gltf;
this.primitive = primitive;
//////////////////////////////////////////////////////////////////////
// buffer
//////////////////////////////////////////////////////////////////////
console.assert(gltf.json.buffers[0].byteLength == gltf.bin.length);
this.vertexBuffer = device.createBuffer({
label: "gltf vertex buffer 0",
size: gltf.bin.length,
this.buffers = [];
console.assert(gltf.json.buffers.length === 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,
});
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
@ -39,7 +45,28 @@ class GltfRenderer {
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",
layout: pipelineLayout,
vertex: {
@ -64,6 +91,9 @@ class GltfRenderer {
},
});
this.renderPipelines[key] = renderPipeline;
}
//////////////////////////////////////////////////////////////////////
// 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)
{
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"];
for (let i = 0; i < attributes.length; i++) {
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 primitive = mesh.primitives[j];
this.renderPrimitive(renderPass, accessors, primitive.indices);
}
}
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();
@ -140,20 +184,13 @@ async function loadGltf(device, canvasFormat)
const gltf = parseGltfChunks(buffer);
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 shaderModule = device.createShaderModule({
label: "gltf module",
code: await code,
});
const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf, primitive, bufferLayouts);
const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf);
return renderer;
}