diff --git a/gltf.js b/gltf.js index 3c45dd3..2d75f1c 100644 --- a/gltf.js +++ b/gltf.js @@ -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 }; diff --git a/index2.js b/index2.js index df6f927..7cc0622 100644 --- a/index2.js +++ b/index2.js @@ -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, - usage: GPUBufferUsage.INDEX | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, - }); - device.queue.writeBuffer(this.vertexBuffer, 0, gltf.bin.buffer, gltf.bin.offset, 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(buffer, 0, + gltf.bin[i].buffer, gltf.bin[i].offset, gltf.bin[i].length); + this.buffers.push(buffer); + } ////////////////////////////////////////////////////////////////////// // pipeline @@ -39,30 +45,54 @@ class GltfRenderer { bindGroupLayouts: bindGroupLayouts, }); - this.renderPipeline = device.createRenderPipeline({ - label: "gltf pipeline", - layout: pipelineLayout, - vertex: { - module: shaderModule, - entryPoint: "vertexMain", - buffers: bufferLayouts, - }, - fragment: { - module: shaderModule, - entryPoint: "fragmentMain", - targets: [{ - format: canvasFormat, - }] - }, - primitive: { - topology: 'triangle-list', - }, - depthStencil: { - depthWriteEnabled: true, - depthCompare: 'less', - format: 'depth24plus', - }, - }); + // 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: { + module: shaderModule, + entryPoint: "vertexMain", + buffers: bufferLayouts, + }, + fragment: { + module: shaderModule, + entryPoint: "fragmentMain", + targets: [{ + format: canvasFormat, + }] + }, + primitive: { + topology: 'triangle-list', + }, + depthStencil: { + depthWriteEnabled: true, + depthCompare: 'less', + format: 'depth24plus', + }, + }); + + 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,27 +156,20 @@ 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(); const commandBuffer = encoder.finish(); @@ -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; }