webgpu-app/index2.js

199 lines
6.0 KiB
JavaScript

import { getPath } from "./common.js";
import { parseGltfChunks, vertexBufferLayouts, primitiveAccessors } from "./gltf.js";
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" }
}]
}),
];
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
//////////////////////////////////////////////////////////////////////
this.uniformBufferSize = (4 * 4 * 4) * 2;
this.uniformBuffer = device.createBuffer({
label: "gltf uniform 0",
size: this.uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
this.bindGroups = [
device.createBindGroup({
label: "cell bind group 0",
layout: bindGroupLayouts[0],
entries: [{
binding: 0,
resource: { buffer: this.uniformBuffer },
}]
}),
];
}
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();
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 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 primitive = mesh.primitives[j];
this.renderPrimitive(renderPass, accessors, primitive.indices);
}
}
renderPass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
}
}
async function loadGltf(device, canvasFormat)
{
const response = await fetch("monkey.glb");
const buffer = await response.arrayBuffer();
const gltf = parseGltfChunks(buffer);
console.log(gltf);
const code = await getPath("gltf.wgsl");
const shaderModule = device.createShaderModule({
label: "gltf module",
code: await code,
});
const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf);
return renderer;
}
export { loadGltf };