import { getPath } from "./common.js"; class SnakeRenderer { createBuffers(device) { const boneCount = 100; const boneLength = 0.1; this.boneSystemAddress = this.module.instance.exports.bone_system__create(boneCount, boneLength); const mem = new DataView(this.module.memory.buffer); this.bonesAddress = mem.getUint32(this.boneSystemAddress, true); const configurationOffset = this.module.instance.exports.bone_system__configuration_offset(); this.configurationAddress = this.boneSystemAddress + configurationOffset; this.bonesBufferSize = this.module.instance.exports.bone_system__bones_size(boneCount); this.configurationBufferSize = this.module.instance.exports.bone_system__configuration_size(); this.frames = []; for (let i = 0; i < 2; i++) { const bonesBuffer = device.createBuffer({ label: `bone buffer ${i}`, size: this.bonesBufferSize, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, }); const configurationBuffer = device.createBuffer({ label: `bone configuration buffer ${i}`, size: this.configurationBufferSize, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }); const bindGroup = device.createBindGroup({ label: `bone buffer bind group ${i}`, layout: this.bindGroupLayout.bone, entries: [{ binding: 0, resource: { buffer: bonesBuffer }, }, { binding: 1, resource: { buffer: configurationBuffer }, }] }); this.frames.push({ bonesBuffer: bonesBuffer, configurationBuffer: configurationBuffer, bindGroup: bindGroup, }); } } constructor(device, canvasFormat, viewUniformBuffer, wgsl, module) { const label = "bone"; this.module = module; ////////////////////////////////////////////////////////////////////// // shader module ////////////////////////////////////////////////////////////////////// const shaderModule = device.createShaderModule({ label: `${label} shader`, code: wgsl, }); ////////////////////////////////////////////////////////////////////// // index buffer ////////////////////////////////////////////////////////////////////// const array = new Uint16Array(6); array[0] = 0; array[1] = 1; array[2] = 2; array[3] = 0; array[4] = 3; array[5] = 1; this.buffer = device.createBuffer({ label: `${label} renderer buffer`, size: array.byteLength, usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST, }); device.queue.writeBuffer(this.buffer, 0, array, 0, array.length); ////////////////////////////////////////////////////////////////////// // pipeline ////////////////////////////////////////////////////////////////////// this.bindGroupLayout = { view: device.createBindGroupLayout({ label: `${label} bind group layout view`, entries: [{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: "uniform" } }] }), bone: device.createBindGroupLayout({ label: `${label} bind group layout particle`, entries: [{ // bones binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "read-only-storage" } }, { // configuration binding: 1, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } }] }), }; const pipelineLayout = device.createPipelineLayout({ label: `${label} pipeline layout`, bindGroupLayouts: [ this.bindGroupLayout.view, this.bindGroupLayout.bone ], }); this.renderPipeline = device.createRenderPipeline({ label: `${label} pipeline`, layout: pipelineLayout, vertex: { module: shaderModule, entryPoint: "vertexMain", }, fragment: { module: shaderModule, entryPoint: "fragmentMain", targets: [{ format: canvasFormat, }] }, primitive: { topology: "triangle-list", }, depthStencil: { depthWriteEnabled: true, depthCompare: "less", format: "depth24plus", }, }); ////////////////////////////////////////////////////////////////////// // bones ////////////////////////////////////////////////////////////////////// this.createBuffers(device); ////////////////////////////////////////////////////////////////////// // bind group ////////////////////////////////////////////////////////////////////// this.viewBindGroup = device.createBindGroup({ label: `${label} bind group`, layout: this.bindGroupLayout.view, entries: [{ binding: 0, resource: { buffer: viewUniformBuffer }, }], }); } update(device, frameNumber, mousePosition, configuration) { this.module.instance.exports.bone_system__update_configuration(this.boneSystemAddress, configuration.boneCount, configuration.boneLength, configuration.velocityDamping, configuration.velocityAcceleration); this.module.instance.exports.bone_system__update(this.boneSystemAddress, mousePosition[0], mousePosition[1], mousePosition[2]); device.queue.writeBuffer(this.frames[frameNumber].bonesBuffer, 0, this.module.memory.buffer, this.bonesAddress, this.bonesBufferSize); device.queue.writeBuffer(this.frames[frameNumber].configurationBuffer, 0, this.module.memory.buffer, this.configurationAddress, this.configurationBufferSize); } render(renderPass, frameNumber) { renderPass.setPipeline(this.renderPipeline); renderPass.setIndexBuffer(this.buffer, "uint16"); renderPass.setBindGroup(0, this.viewBindGroup); renderPass.setBindGroup(1, this.frames[frameNumber].bindGroup); renderPass.drawIndexed(6); } } async function loadSnake(device, canvasFormat, viewUniformBuffer, module) { const snakeWgsl = await getPath("snake.wgsl"); const renderer = new SnakeRenderer(device, canvasFormat, viewUniformBuffer, snakeWgsl, module); return renderer; } export { loadSnake };