diff --git a/build.sh b/build.sh index c0855b3..bf66895 100644 --- a/build.sh +++ b/build.sh @@ -14,6 +14,24 @@ clang++ \ -o rotate.wasm \ rotate.c +clang++ \ + -v \ + --target=wasm32 \ + -O3 \ + -flto \ + -nostdlib \ + -Wl,--no-entry \ + -Wl,--export=mem_size \ + -Wl,--export=mem_alloc \ + -Wl,--export=node_alloc \ + -Wl,--export=node_init_translation \ + -Wl,--lto-O3 \ + -Wl,--print-map \ + -Wl,--import-memory \ + -D_XM_NO_INTRINSICS_ \ + -I. \ + -o memory.wasm \ + memory.cpp # clang \ # -O1 \ diff --git a/gltf.js b/gltf.js index 2d75f1c..d852bde 100644 --- a/gltf.js +++ b/gltf.js @@ -7,6 +7,29 @@ function jsonChunk(buffer, offset, length) return object; } +async function splitGltf(jsonPath, binPath) +{ + const jsonResponse = await fetch(jsonPath); + const jsonBuffer = await jsonResponse.arrayBuffer(); + const json = jsonChunk(jsonBuffer, 0, jsonBuffer.byteLength); + + const binResponse = await fetch(binPath); + const binBuffer = await binResponse.arrayBuffer(); + return { + json: json, + bin: [{ buffer: binBuffer, offset: 0, length: binBuffer.byteLength }], + }; +} + +function transformMatrix() +{ +} + +function buildTransfomMatrices(json) +{ + +} + function parseGltfChunks(buffer) { const view = new DataView(buffer); @@ -165,4 +188,4 @@ function vertexBufferLayouts(gltf, accessors) return layouts; } -export { parseGltfChunks, vertexBufferLayouts, primitiveAccessors }; +export { parseGltfChunks, vertexBufferLayouts, primitiveAccessors, splitGltf }; diff --git a/gltf.wgsl b/gltf.wgsl index e68ee35..143a678 100644 --- a/gltf.wgsl +++ b/gltf.wgsl @@ -4,11 +4,20 @@ struct Configuration { }; @group(0) @binding(0) var config: Configuration; +@group(0) @binding(1) var nodeMatrix: array; + +/* +struct Imm { + nodeIndex: i32, +}; +var imm: Imm; +*/ struct VertexInput { @location(0) position: vec3f, @location(1) normal: vec3f, @location(2) texture: vec2f, + @builtin(instance_index) instance: u32, }; struct VertexOutput { @@ -25,7 +34,8 @@ struct FragmentInput { @vertex fn vertexMain(input: VertexInput) -> VertexOutput { - let position = vec4f(input.position, 1); + let position = nodeMatrix[input.instance] * vec4f(input.position, 1); + //let position = vec4f(input.position, 1); let normal = vec4f(input.normal, 0.0f); var output: VertexOutput; output.position = config.matrix * position; diff --git a/gltf/Image_0.png b/gltf/Image_0.png new file mode 100644 index 0000000..013ec22 Binary files /dev/null and b/gltf/Image_0.png differ diff --git a/gltf/Image_1.png b/gltf/Image_1.png new file mode 100644 index 0000000..eb26f29 Binary files /dev/null and b/gltf/Image_1.png differ diff --git a/gltf/Image_2.png b/gltf/Image_2.png new file mode 100644 index 0000000..28ee171 Binary files /dev/null and b/gltf/Image_2.png differ diff --git a/gltf/Image_3.png b/gltf/Image_3.png new file mode 100644 index 0000000..e1878ca Binary files /dev/null and b/gltf/Image_3.png differ diff --git a/gltf/lamp.bin b/gltf/lamp.bin new file mode 100644 index 0000000..21dd25d Binary files /dev/null and b/gltf/lamp.bin differ diff --git a/gltf/lamp.gltf b/gltf/lamp.gltf new file mode 100644 index 0000000..b66247a --- /dev/null +++ b/gltf/lamp.gltf @@ -0,0 +1,395 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v5.2.39", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 3 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"LanternPole_Body", + "translation":[ + -3.8231542110443115, + 13.016030311584473, + 0 + ] + }, + { + "mesh":1, + "name":"LanternPole_Chain", + "translation":[ + -9.582000732421875, + 21.037872314453125, + 0 + ] + }, + { + "mesh":2, + "name":"LanternPole_Lantern", + "translation":[ + -9.58200740814209, + 18.009151458740234, + 0 + ] + }, + { + "children":[ + 0, + 1, + 2 + ], + "name":"Lantern", + "rotation":[ + 0, + 1, + 0, + 0 + ] + } + ], + "materials":[ + { + "emissiveFactor":[ + 1, + 1, + 1 + ], + "emissiveTexture":{ + "index":0 + }, + "name":"LanternPost_Mat", + "normalTexture":{ + "index":1 + }, + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":2 + }, + "metallicRoughnessTexture":{ + "index":3 + } + } + } + ], + "meshes":[ + { + "name":"LanternPole_Body", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2, + "TANGENT":3 + }, + "indices":4, + "material":0 + } + ] + }, + { + "name":"LanternPole_Chain", + "primitives":[ + { + "attributes":{ + "POSITION":5, + "NORMAL":6, + "TEXCOORD_0":7, + "TANGENT":8 + }, + "indices":9, + "material":0 + } + ] + }, + { + "name":"LanternPole_Lantern", + "primitives":[ + { + "attributes":{ + "POSITION":10, + "NORMAL":11, + "TEXCOORD_0":12, + "TANGENT":13 + }, + "indices":14, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + }, + { + "sampler":0, + "source":1 + }, + { + "sampler":0, + "source":2 + }, + { + "sampler":0, + "source":3 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"Image_3", + "uri":"Image_3.png" + }, + { + "mimeType":"image/png", + "name":"Image_2", + "uri":"Image_2.png" + }, + { + "mimeType":"image/png", + "name":"Image_0", + "uri":"Image_0.png" + }, + { + "mimeType":"image/png", + "name":"Image_1", + "uri":"Image_1.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":941, + "max":[ + 7.745599269866943, + 12.832109451293945, + 2.3157098293304443 + ], + "min":[ + -7.745599269866943, + -12.832109451293945, + -2.3157098293304443 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":941, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":941, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5126, + "count":941, + "type":"VEC4" + }, + { + "bufferView":4, + "componentType":5123, + "count":2616, + "type":"SCALAR" + }, + { + "bufferView":5, + "componentType":5126, + "count":756, + "max":[ + 0.12920807301998138, + 0.6523504257202148, + 0.12920807301998138 + ], + "min":[ + -0.12920807301998138, + -0.6523504257202148, + -0.12920807301998138 + ], + "type":"VEC3" + }, + { + "bufferView":6, + "componentType":5126, + "count":756, + "type":"VEC3" + }, + { + "bufferView":7, + "componentType":5126, + "count":756, + "type":"VEC2" + }, + { + "bufferView":8, + "componentType":5126, + "count":756, + "type":"VEC4" + }, + { + "bufferView":9, + "componentType":5123, + "count":3744, + "type":"SCALAR" + }, + { + "bufferView":10, + "componentType":5126, + "count":2543, + "max":[ + 1.034082293510437, + 2.5292809009552, + 1.034084677696228 + ], + "min":[ + -1.034082293510437, + -2.5292809009552, + -1.034084677696228 + ], + "type":"VEC3" + }, + { + "bufferView":11, + "componentType":5126, + "count":2543, + "type":"VEC3" + }, + { + "bufferView":12, + "componentType":5126, + "count":2543, + "type":"VEC2" + }, + { + "bufferView":13, + "componentType":5126, + "count":2543, + "type":"VEC4" + }, + { + "bufferView":14, + "componentType":5123, + "count":9822, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":11292, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":11292, + "byteOffset":11292, + "target":34962 + }, + { + "buffer":0, + "byteLength":7528, + "byteOffset":22584, + "target":34962 + }, + { + "buffer":0, + "byteLength":15056, + "byteOffset":30112, + "target":34962 + }, + { + "buffer":0, + "byteLength":5232, + "byteOffset":45168, + "target":34963 + }, + { + "buffer":0, + "byteLength":9072, + "byteOffset":50400, + "target":34962 + }, + { + "buffer":0, + "byteLength":9072, + "byteOffset":59472, + "target":34962 + }, + { + "buffer":0, + "byteLength":6048, + "byteOffset":68544, + "target":34962 + }, + { + "buffer":0, + "byteLength":12096, + "byteOffset":74592, + "target":34962 + }, + { + "buffer":0, + "byteLength":7488, + "byteOffset":86688, + "target":34963 + }, + { + "buffer":0, + "byteLength":30516, + "byteOffset":94176, + "target":34962 + }, + { + "buffer":0, + "byteLength":30516, + "byteOffset":124692, + "target":34962 + }, + { + "buffer":0, + "byteLength":20344, + "byteOffset":155208, + "target":34962 + }, + { + "buffer":0, + "byteLength":40688, + "byteOffset":175552, + "target":34962 + }, + { + "buffer":0, + "byteLength":19644, + "byteOffset":216240, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":235884, + "uri":"lamp.bin" + } + ] +} diff --git a/index.js b/index.js index 3ead499..304cb5d 100644 --- a/index.js +++ b/index.js @@ -256,6 +256,13 @@ const rotate = await WebAssembly.instantiateStreaming(fetch("rotate.wasm"), { env: { memory: memory } }); +const memory2 = new WebAssembly.Memory({ initial: 2 }); +const mem = await WebAssembly.instantiateStreaming(fetch("memory.wasm"), { + env: { memory: memory2 } +}); +console.log("mem"); +document.mem = mem; + const uniformBuffer = device.createBuffer({ label: "grid uniform", size: uniformArraySize, @@ -432,7 +439,8 @@ function render() } //requestAnimationFrame(render); -const gltfRenderer = await loadGltf(device, canvasFormat); +const gltfRenderer = await loadGltf(device, canvasFormat, memory2, mem); + function render2() { recreateDepth(); diff --git a/index2.js b/index2.js index 7cc0622..5cdfe3c 100644 --- a/index2.js +++ b/index2.js @@ -1,5 +1,40 @@ import { getPath } from "./common.js"; -import { parseGltfChunks, vertexBufferLayouts, primitiveAccessors } from "./gltf.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) @@ -12,9 +47,9 @@ class GltfRenderer { this.buffers = []; - console.assert(gltf.json.buffers.length === gltf.bin.length); + //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); + //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, @@ -36,6 +71,10 @@ class GltfRenderer { binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: "uniform" } + }, { + binding: 1, + visibility: GPUShaderStage.VERTEX, + buffer: { type: "read-only-storage" } }] }), ]; @@ -98,13 +137,21 @@ class GltfRenderer { // bind group ////////////////////////////////////////////////////////////////////// - this.uniformBufferSize = (4 * 4 * 4) * 2; + 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", @@ -112,12 +159,15 @@ class GltfRenderer { entries: [{ binding: 0, resource: { buffer: this.uniformBuffer }, + }, { + binding: 1, + resource: { buffer: this.nodeBuffer }, }] }), ]; } - renderPrimitive(renderPass, accessors, indices) + renderPrimitive(renderPass, accessors, indices, nodeIndex) { for (let i = 0; i < accessors.length; i++) { const accessor = accessors[i]; @@ -135,7 +185,33 @@ class GltfRenderer { indicesBufferView.byteLength); renderPass.setBindGroup(0, this.bindGroups[0]); - renderPass.drawIndexed(indicesAccessor.count); + //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) @@ -157,17 +233,8 @@ class GltfRenderer { }); 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); - } + for (let nodeIndex of this.gltf.json.scenes[0].nodes) { + lastKey = this.renderNode(renderPass, lastKey, nodeIndex); } renderPass.end(); @@ -177,12 +244,17 @@ class GltfRenderer { } } -async function loadGltf(device, canvasFormat) +async function loadGltf(device, canvasFormat, memory, mem) { - const response = await fetch("monkey.glb"); - const buffer = await response.arrayBuffer(); - const gltf = parseGltfChunks(buffer); - console.log(gltf); + 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({ @@ -190,7 +262,10 @@ async function loadGltf(device, canvasFormat) code: await code, }); - const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf); + 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; } diff --git a/memory.cpp b/memory.cpp new file mode 100644 index 0000000..ca0f0d7 --- /dev/null +++ b/memory.cpp @@ -0,0 +1,71 @@ +#include + +#include "directxmath/directxmath.h" + +//size_t __builtin_wasm_memory_grow(int, size_t); +//size_t __builtin_wasm_memory_size(int); + +extern char __heap_base[]; +extern char __heap_end[]; + +static size_t heap_pages = 0; +static size_t alloc_index = 0; + +extern "C" { + size_t mem_size(); + size_t mem_alloc(size_t length); + size_t node_alloc(int count); + + void node_init_translation(size_t handle, int index, float x, float y, float z); +}; + +static inline size_t memory_heap_base() +{ + return (size_t)&__heap_base[0]; +} + +static inline size_t align(size_t n, size_t m) +{ + return (n + (m - 1)) & (~(m - 1)); +} + +static inline void memory_grow(size_t npages) +{ + __builtin_wasm_memory_grow(0, npages); +} + +size_t mem_size() +{ + return __builtin_wasm_memory_size(0); +} + +size_t mem_alloc(size_t length) +{ + size_t base = memory_heap_base(); + + size_t heap_end = (heap_pages << 16); + + size_t address = alloc_index; + + alloc_index += align(length, 16); + + if (heap_end < alloc_index) { + size_t npages = align(alloc_index - heap_end, 65536) >> 16; + memory_grow(npages); + heap_pages += npages; + } + return base + address; +} + +size_t node_alloc(int count) +{ + return mem_alloc((sizeof (XMFLOAT4X4)) * count); +} + +void node_init_translation(size_t handle, int index, float x, float y, float z) +{ + XMFLOAT4X4 * nodes = reinterpret_cast(handle); + //XMMATRIX XMLoadFloat4x4(&nodes[index]); + XMMATRIX mat = XMMatrixTranslation(x, y, z); + XMStoreFloat4x4(&nodes[index], mat); +} diff --git a/rotate.c b/rotate.c index 8d0f604..7926924 100644 --- a/rotate.c +++ b/rotate.c @@ -9,7 +9,7 @@ void rotate(float angle, float aspect, float * buffer) //XMMATRIX mat2 = XMMatrixTranslation(0, 0, 1); //XMStoreFloat4x4((XMFLOAT4X4*)buffer, mat * mat2); - float d = 1.9; + float d = 50.9; XMVECTOR eye = XMVectorSet(d, d / 2, d, 0); XMVECTOR at = XMVectorSet(0, 0, 0, 0); XMVECTOR up = XMVectorSet(0, 1, 0, 0);