webgpu-app/index2.js

274 lines
8.3 KiB
JavaScript

import { getPath } from "./common.js";
import { parseGltfChunks, vertexBufferLayouts, primitiveAccessors, splitGltf } from "./gltf.js";
function loadGltfNode(mem, handle, gltf, nodeIndex)
{
const node = gltf.json.nodes[nodeIndex];
var translation = [0, 0, 0];
if ("translation" in node) {
translation = node.translation;
}
console.log("init", nodeIndex, translation);
mem.instance.exports.node_init_translation(handle, nodeIndex, translation[0], translation[1], translation[2]);
if ("children" in node) {
for (let childIndex in node.children) {
loadGltfNode(mem, handle, gltf, childIndex);
}
}
}
function loadGltfNodes(mem, gltf)
{
const nodesHandle = mem.instance.exports.node_alloc(gltf.json.nodes.length);
for (let nodeIndex of gltf.json.scenes[0].nodes) {
loadGltfNode(mem, nodesHandle, gltf, nodeIndex);
}
return nodesHandle;
}
function printMatrix(m, o)
{
console.log(m[o+0], m[o+1], m[o+2], m[o+3]);
console.log(m[o+4], m[o+5], m[o+6], m[o+7]);
console.log(m[o+8], m[o+9], m[o+10], m[o+11]);
console.log(m[o+12], m[o+13], m[o+14], m[o+15]);
}
class GltfRenderer {
constructor(device, canvasFormat, shaderModule, gltf)
{
this.gltf = gltf;
//////////////////////////////////////////////////////////////////////
// buffer
//////////////////////////////////////////////////////////////////////
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
//////////////////////////////////////////////////////////////////////
const bindGroupLayouts = [
device.createBindGroupLayout({
label: "gltf bind group layout 0",
entries: [{
binding: 0,
visibility: GPUShaderStage.VERTEX,
buffer: { type: "uniform" }
}, {
binding: 1,
visibility: GPUShaderStage.VERTEX,
buffer: { type: "read-only-storage" }
}]
}),
];
const pipelineLayout = device.createPipelineLayout({
label: "gltf pipeline layout",
bindGroupLayouts: bindGroupLayouts,
});
// 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
//////////////////////////////////////////////////////////////////////
const matrixSize = 4 * 4 * 4;
this.uniformBufferSize = matrixSize * 2;
this.uniformBuffer = device.createBuffer({
label: "gltf uniform 0",
size: this.uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
this.nodeBufferSize = matrixSize * this.gltf.json.nodes.length;
this.nodeBuffer = device.createBuffer({
label: "gltf node buffer",
size: this.nodeBufferSize,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
this.bindGroups = [
device.createBindGroup({
label: "cell bind group 0",
layout: bindGroupLayouts[0],
entries: [{
binding: 0,
resource: { buffer: this.uniformBuffer },
}, {
binding: 1,
resource: { buffer: this.nodeBuffer },
}]
}),
];
}
renderPrimitive(renderPass, accessors, indices, nodeIndex)
{
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]);
//console.log("drawIndexed", nodeIndex);
renderPass.drawIndexed(indicesAccessor.count, 1, 0, 0, nodeIndex);
//renderPass.drawIndexed(indicesAccessor.count);
}
renderNode(renderPass, lastKey, nodeIndex)
{
const node = this.gltf.json.nodes[nodeIndex];
if ("mesh" in node) {
const mesh = this.gltf.json.meshes[node.mesh];
for (let j = 0; j < mesh.primitives.length; j++) {
const [key, accessors] = this.meshes[node.mesh][j];
if (key !== lastKey)
renderPass.setPipeline(this.renderPipelines[key]);
lastKey = key;
const primitive = mesh.primitives[j];
this.renderPrimitive(renderPass, accessors, primitive.indices, nodeIndex);
}
}
if ("children" in node) {
for (let childIndex of node.children) {
lastKey = this.renderNode(renderPass, lastKey, childIndex);
}
}
return lastKey;
}
render(device, colorView, depthTexture)
{
const encoder = device.createCommandEncoder();
const renderPass = encoder.beginRenderPass({
colorAttachments: [{
view: colorView,
loadOp: "clear",
clearValue: { r: 0.2, g: 0.2, b: 0.4, a: 1 },
storeOp: "store",
}],
depthStencilAttachment: {
view: depthTexture.createView(),
depthClearValue: 1.0,
depthLoadOp: 'clear',
depthStoreOp: 'store',
},
});
var lastKey = undefined;
for (let nodeIndex of this.gltf.json.scenes[0].nodes) {
lastKey = this.renderNode(renderPass, lastKey, nodeIndex);
}
renderPass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
}
}
async function loadGltf(device, canvasFormat, memory, mem)
{
const gltf = await splitGltf("gltf/lamp.gltf", "gltf/lamp.bin");
const nodesHandle = loadGltfNodes(mem, gltf);
const nodesM = new Float32Array(memory.buffer, nodesHandle);
for (let i = 0; i < gltf.json.nodes.length; i++) {
console.log("[", i, "]");
printMatrix(nodesM, i * 16);
}
const code = await getPath("gltf.wgsl");
const shaderModule = device.createShaderModule({
label: "gltf module",
code: await code,
});
const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf, nodesHandle);
console.log(renderer.nodeBuffer);
device.queue.writeBuffer(renderer.nodeBuffer, 0,
memory.buffer, nodesHandle, (4 * 4 * 4) * gltf.json.nodes.length);
return renderer;
}
export { loadGltf };