diff --git a/src/sample/skinnedMesh/glbUtils.ts b/src/sample/skinnedMesh/glbUtils.ts index 870f587a..ca697f83 100644 --- a/src/sample/skinnedMesh/glbUtils.ts +++ b/src/sample/skinnedMesh/glbUtils.ts @@ -4,6 +4,7 @@ import { Mat4, Vec3, mat4 } from 'wgpu-matrix'; //NOTE: GLTF code is not generally extensible +// Determines the topology of our pipeline enum GLTFRenderMode { POINTS = 0, LINE = 1, @@ -11,11 +12,10 @@ enum GLTFRenderMode { LINE_STRIP = 3, TRIANGLES = 4, TRIANGLE_STRIP = 5, - // Note= fans are not supported in WebGPU, use should be - // an error or converted into a list/strip TRIANGLE_FAN = 6, } +// Determines how to interpret each element of the structure that is accessed from our accessor enum GLTFDataComponentType { BYTE = 5120, UNSIGNED_BYTE = 5121, @@ -27,6 +27,7 @@ enum GLTFDataComponentType { DOUBLE = 5130, } +// Determines how to interpret the structure of the values accessed by an accessor enum GLTFDataStructureType { SCALAR = 0, VEC2 = 1, @@ -550,11 +551,13 @@ export class GLTFNode { } else { mat4.copy(this.localMatrix, this.worldMatrix); } - const worldMatrix = this.worldMatrix; + const worldMatrix = this.worldMatrix as Float32Array; device.queue.writeBuffer( this.nodeTransformGPUBuffer, 0, - worldMatrix as Float32Array + worldMatrix.buffer, + worldMatrix.byteOffset, + worldMatrix.byteLength ); for (const child of this.children) { child.updateWorldMatrix(device, worldMatrix); @@ -632,12 +635,18 @@ export class GLTFScene { } export class GLTFSkin { + // Inverse bind matrices parsed from the accessor inverseBindMatrices: Float32Array; inverseBindMatricesUniformBuffer: GPUBuffer; // Nodes of the skin's joints + // [5, 2, 3] means our joint info is at nodes 5, 2, and 3 joints: number[]; - jointGPUBuffer: GPUBuffer; + // Bind Group for this skin's uniform buffer skinBindGroup: GPUBindGroup; + // Static bindGroupLayout shared across all skins + // In a larger shader with more properties, certain bind groups + // would likely have to be combined due to device limitations in the number of bind groups + // allowed within a shader static skinBindGroupLayout: GPUBindGroupLayout; private jointMatrices: Mat4[]; @@ -685,6 +694,7 @@ export class GLTFSkin { inverseBindMatricesAccessor.view.view.byteOffset, inverseBindMatricesAccessor.view.view.byteLength / 4 ); + console.log(this.inverseBindMatrices); this.joints = joints; this.inverseBindMatricesUniformBuffer = device.createBuffer({ size: Float32Array.BYTES_PER_ELEMENT * 16 * joints.length, @@ -712,7 +722,7 @@ export class GLTFSkin { mat4.multiply(globalWorldInverse, nodes[joint].worldMatrix, dstMatrix); const startMatrixAccess = j * 16; const endMatrixAccess = startMatrixAccess + 16; - const inverseBindMatrix: Mat4 = this.inverseBindMatrices.subarray( + const inverseBindMatrix: Mat4 = this.inverseBindMatrices.slice( startMatrixAccess, endMatrixAccess ); @@ -849,6 +859,11 @@ export const convertGLBToJSONAndBinary = async ( for (const attr in prim['attributes']) { const accessor = accessors[prim['attributes'][attr]]; primitiveAttributeMap[attr] = accessor; + if (accessor.structureType > 3) { + throw Error( + 'Vertex attribute accessor accessed an unsupported data type for vertex attribute' + ); + } attributes.push(attr); } meshPrimitives.push( @@ -886,6 +901,7 @@ export const convertGLBToJSONAndBinary = async ( const nodes: GLTFNode[] = []; console.log(jsonChunk.skins); + console.log(skins); // Access each node. If node references a mesh, add mesh to that node const nodeUniformsBindGroupLayout = device.createBindGroupLayout({ diff --git a/src/sample/skinnedMesh/gltf.ts b/src/sample/skinnedMesh/gltf.ts index 32f54560..cfd2fb5b 100644 --- a/src/sample/skinnedMesh/gltf.ts +++ b/src/sample/skinnedMesh/gltf.ts @@ -256,9 +256,7 @@ export interface Sampler { extras?: any; [k: string]: any; } -/** - * The root nodes of a scene. - */ + export interface Scene { nodes?: GlTfId[]; name?: any; diff --git a/src/sample/skinnedMesh/gltf.wgsl b/src/sample/skinnedMesh/gltf.wgsl index dde593ff..de160a8b 100644 --- a/src/sample/skinnedMesh/gltf.wgsl +++ b/src/sample/skinnedMesh/gltf.wgsl @@ -19,15 +19,16 @@ struct VertexOutput { struct CameraUniforms { projMatrix: mat4x4f, viewMatrix: mat4x4f, - modelMatrix: mat4x4f, + model_matrix: mat4x4f, } struct GeneralUniforms { - render_mode: u32 + render_mode: u32, + skin_mode: u32, } struct NodeUniforms { - worldMatrix: mat4x4f, + world_matrix: mat4x4f, } @group(0) @binding(0) var camera_uniforms: CameraUniforms; @@ -43,7 +44,13 @@ fn vertexMain(input: VertexInput) -> VertexOutput { joint_matrices[input.joints[1]] * input.weights[1] + joint_matrices[input.joints[2]] * input.weights[2] + joint_matrices[input.joints[3]] * input.weights[3]; - output.Position = camera_uniforms.projMatrix * camera_uniforms.viewMatrix * camera_uniforms.modelMatrix * vec4(input.position.x, input.position.y, input.position.z, 1.0); + let world_position = vec4(input.position.x, input.position.y, input.position.z, 1.0); + let skinned_position = select( + camera_uniforms.model_matrix * world_position, + camera_uniforms.model_matrix * skin_matrix * node_uniforms.world_matrix * world_position, + general_uniforms.skin_mode == 0 + ); + output.Position = camera_uniforms.projMatrix * camera_uniforms.viewMatrix * skinned_position; output.normal = input.normal; output.joints = vec4(f32(input.joints[0]), f32(input.joints[1]), f32(input.joints[2]), f32(input.joints[3])); // Convert to f32 to avoid flat interpolation error diff --git a/src/sample/skinnedMesh/grid.wgsl b/src/sample/skinnedMesh/grid.wgsl index 57d95e42..5c28caf0 100644 --- a/src/sample/skinnedMesh/grid.wgsl +++ b/src/sample/skinnedMesh/grid.wgsl @@ -19,6 +19,7 @@ struct CameraUniforms { struct GeneralUniforms { render_mode: u32, + skin_mode: u32, } struct BoneUniforms { @@ -41,17 +42,16 @@ fn vertexMain(input: VertexInput) -> VertexOutput { let bone2 = bone_uniforms.bones[u32(input.bone_index[2])]; let bone3 = bone_uniforms.bones[u32(input.bone_index[3])]; // Bone transformed mesh - output.Position = - camera_uniforms.projMatrix * - camera_uniforms.viewMatrix * - camera_uniforms.modelMatrix * + output.Position = select( + camera_uniforms.projMatrix * camera_uniforms.viewMatrix * camera_uniforms.modelMatrix * position, + camera_uniforms.projMatrix * camera_uniforms.viewMatrix * camera_uniforms.modelMatrix * (bone0 * position * input.bone_weight[0] + bone1 * position * input.bone_weight[1] + bone2 * position * input.bone_weight[2] + - bone3 * position * input.bone_weight[3]); + bone3 * position * input.bone_weight[3]), + general_uniforms.skin_mode == 0 + ); - // Normal, unaffected mesh - //output.Position = camera_uniforms.projMatrix * camera_uniforms.viewMatrix * camera_uniforms.modelMatrix * position; //Get unadjusted world coordinates output.world_pos = position.xyz; output.bone_index = input.bone_index; diff --git a/src/sample/skinnedMesh/main.ts b/src/sample/skinnedMesh/main.ts index bd9f98f3..f852890c 100644 --- a/src/sample/skinnedMesh/main.ts +++ b/src/sample/skinnedMesh/main.ts @@ -24,6 +24,11 @@ enum RenderMode { WEIGHTS, } +enum SkinMode { + ON, + OFF +} + const getRotation = (mat: Mat4): Quat => { const out = [0, 0, 0, 0]; const scaling = mat4.getScaling(mat); @@ -105,6 +110,7 @@ const init: SampleInit = async ({ speed: 10, object: 'Whale', renderMode: 'NORMAL', + skinMode: 'ON', }; gui.add(settings, 'object', ['Whale', 'Skinned Grid']).onChange(() => { @@ -122,6 +128,9 @@ const init: SampleInit = async ({ gui.add(settings, 'renderMode', ['NORMAL', 'JOINTS', 'WEIGHTS']).onChange(() => { device.queue.writeBuffer(generalUniformsBuffer, 0, new Uint32Array([RenderMode[settings.renderMode]])); }); + gui.add(settings, 'skinMode', ['ON', 'OFF']).onChange(() => { + device.queue.writeBuffer(generalUniformsBuffer, 4, new Uint32Array([SkinMode[settings.skinMode]])); + }) const cameraFolder = gui.addFolder('Camera Settings'); const cameraXController = cameraFolder.add(settings, 'cameraX', -10, 10).step(0.1); const cameraYController = cameraFolder.add(settings, 'cameraY', -10, 10).step(0.1); @@ -131,7 +140,6 @@ const init: SampleInit = async ({ animFolder.add(settings, 'angle', 0.1, 1.0).step(0.1); animFolder.add(settings, 'speed', 10, 100).step(10); - // Create global resources const depthTexture = device.createTexture({ size: [canvas.width, canvas.height], format: 'depth24plus', @@ -154,7 +162,7 @@ const init: SampleInit = async ({ ); const generalUniformsBuffer = device.createBuffer({ - size: Uint32Array.BYTES_PER_ELEMENT, + size: Uint32Array.BYTES_PER_ELEMENT * 2, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }); @@ -348,12 +356,14 @@ const init: SampleInit = async ({ const origMatrix = origMatrices.get(joint); const m = mat4.rotateX(origMatrix, angle); whaleScene.nodes[joint].source.position = mat4.getTranslation(m); + // Something wrong with scaling whaleScene.nodes[joint].source.position = mat4.getScaling(m); - whaleScene.nodes[joint].source.rotation = getRotation(m); } } + console.log(whaleScene.nodes) + function frame() { // Sample is no longer the active page. if (!pageState.active) return; @@ -420,6 +430,7 @@ const init: SampleInit = async ({ // Updates skins (we index into skins in the renderer, which is not the best approach but hey) animSkin(whaleScene.skins[0], Math.sin(t) * .5) + // Node 6 should be the only node with a drawable mesh so hopefully this works fine whaleScene.skins[0].update(device, whaleScene.nodes[6], whaleScene.nodes)