diff --git a/collada/mesh.py b/collada/mesh.py index a8ad141..0c64029 100644 --- a/collada/mesh.py +++ b/collada/mesh.py @@ -46,6 +46,7 @@ def mesh_vertex_buffer(collada, mesh): source.technique_common.accessor.stride for input, source in chain.from_iterable(by_offset.values()) ) + vertex_table = [] index_table = {} next_output_index = 0 index_buffer = [] @@ -55,7 +56,6 @@ def mesh_vertex_buffer(collada, mesh): for vertex_ix in range(triangles.count * 3): index_table_key = tuple(triangles.p[vertex_ix * p_stride + offset] for offset in used_offsets) - print(index_table_key) if index_table_key in index_table: index_buffer.append(index_table[index_table_key]) continue @@ -67,15 +67,15 @@ def mesh_vertex_buffer(collada, mesh): # emit vertex attributes for new output index in vertex buffer for offset in used_offsets: p_index = triangles.p[vertex_ix * p_stride + offset] + if offset == vertex_input.offset: + vertex_table.append(p_index) for input, source in by_offset[offset]: - print(input.semantic, end=" ") source_stride = source.technique_common.accessor.stride source_index = p_index * source_stride array_slice = source.array_element.floats[source_index:source_index+source_stride] - print(array_slice, end=" ") vertex_buffer.extend(array_slice) - print() + """ print("{") for i in range(triangles.count): print(", ".join(str(index_buffer[i * 3 + j]) for j in range(3)), end=",\n") @@ -85,6 +85,95 @@ def mesh_vertex_buffer(collada, mesh): print(", ".join(str(vertex_buffer[i * vertex_buffer_stride + j]) for j in range(vertex_buffer_stride)), end=",\n") print("}") + """ + # vertex table: + # list indices: (output/direct3d) vertex indices + # list values: (input/collada) vertex indices + return vertex_table + +def filter_tiny(fs, epsilon=0.00001): + return [f if abs(f) > epsilon else 0 for f in fs] + +def matrix_transpose(fs): + return ( + fs[0], fs[4], fs[8], fs[12], + fs[1], fs[5], fs[9], fs[13], + fs[2], fs[6], fs[10], fs[14], + fs[3], fs[7], fs[11], fs[15], + ) + +def matrix_print(fs): + for i, f in enumerate(fs): + print(f"{f:5.01f}f", end=", ") + if i % 4 == 3: + print() + +def skin_vertex_buffer(collada, skin, vertex_table): + inverse_bind_matrix_input, = find_semantics(skin.joints.inputs, "INV_BIND_MATRIX") + inverse_bind_matrix_source = collada.lookup(inverse_bind_matrix_input.source, types.SourceCore) + stride = inverse_bind_matrix_source.technique_common.accessor.stride + count = inverse_bind_matrix_source.technique_common.accessor.count + array = inverse_bind_matrix_source.array_element + assert type(inverse_bind_matrix_source.array_element) == types.FloatArray + assert stride == 16 + assert array.count == count * stride + + # enable to improve inverse bind matrix human-readability + #floats = filter_tiny(array.floats) + + inverse_bind_matrices = [] + print("static const float inverse_bind_matrices[] = {") + for i in range(count): + offset = stride * i + matrix = matrix_transpose(floats[offset:offset+stride]) + matrix_print(matrix) + if i + 1 < count: + print() + print("};") + + ###################################################################### + # vertex weights + ###################################################################### + max_offset = max(i.offset for i in skin.vertex_weights.inputs) + weights_input, = find_semantics(skin.vertex_weights.inputs, "WEIGHT") + weights_source = collada.lookup(weights_input.source, types.SourceCore) + joints_input, = find_semantics(skin.vertex_weights.inputs, "JOINT") + joints_source = collada.lookup(joints_input.source, types.SourceCore) + assert weights_source.technique_common.accessor.stride == 1 + assert joints_source.technique_common.accessor.stride == 1 + + vertex_weights = defaultdict(int) + + v_stride = max_offset + 1 + v_offset = 0 + vertex_influences = [] + for vcount in skin.vertex_weights.vcount: + influences = [] + for vi in range(vcount): + joint_index = skin.vertex_weights.v[v_offset + joints_input.offset] + weight_index = skin.vertex_weights.v[v_offset + weights_input.offset] + pprint(weights_source) + weight = weights_source.array_element.floats[weight_index] + influences.append((joint_index, weight)) + v_offset += v_stride + vertex_influences.append(influences) + + vertex_buffer = [] + for vertex_index in vertex_table: + influences = vertex_influences[vertex_index] + def emit(column): + for i in range(4): + if i >= len(influences): + vertex_buffer.append(0) + else: + vertex_buffer.append(influences[i][column]) + emit(0) # emit joint int4 + emit(1) # emit weight float4 + + for i, v in enumerate(vertex_buffer): + print(v, end=", ") + if i % 8 == 7: + print() if __name__ == "__main__": import sys @@ -92,4 +181,8 @@ if __name__ == "__main__": mesh = collada.library_geometries[0].geometries[0].geometric_element assert type(mesh) is types.Mesh - mesh_vertex_buffer(collada, mesh) + vertex_table = mesh_vertex_buffer(collada, mesh) + + skin = collada.library_controllers[0].controllers[0].control_element + assert type(skin) is types.Skin + skin_vertex_buffer(collada, skin, vertex_table) diff --git a/include/collada.hpp b/include/collada.hpp index 54f50b4..375990c 100644 --- a/include/collada.hpp +++ b/include/collada.hpp @@ -6,7 +6,7 @@ namespace collada { HRESULT LoadEffect(); HRESULT LoadMesh(); - void Render(); + void Render(float t); } #endif diff --git a/models/skinned_cube/cube.DAE b/models/skinned_cube/cube.DAE new file mode 100755 index 0000000..ee61974 --- /dev/null +++ b/models/skinned_cube/cube.DAE @@ -0,0 +1,124 @@ + + + + + + + + -0.5 -0.5 -0.5 + 0.5 -0.5 -0.5 + -0.5 0.5 -0.5 + 0.5 0.5 -0.5 + -0.5 -0.5 0.5 + 0.5 -0.5 0.5 + -0.5 0.5 0.5 + 0.5 0.5 0.5 + + + + + + + + + + + + 0 0 -1 + 0 0 -1 + 0 0 -1 + 0 0 -1 + 0 0 1 + 0 0 1 + 0 0 1 + 0 0 1 + 0 -1 0 + 0 -1 0 + 0 -1 0 + 0 -1 0 + 1 0 0 + 1 0 0 + 1 0 0 + 1 0 0 + 0 1 0 + 0 1 0 + 0 1 0 + 0 1 0 + -1 0 0 + -1 0 0 + -1 0 0 + -1 0 0 + + + + + + + + + + + + 0 0 0 + 1 0 0 + 0 1 0 + 1 1 0 + 0 0 0 + 1 0 0 + 0 1 0 + 1 1 0 + 0 0 0 + 1 0 0 + 0 1 0 + 1 1 0 + + + + + + + + + + + + + + + + +

+ 0 0 9 2 1 11 3 2 10 + 3 2 10 1 3 8 0 0 9 + + 4 4 8 5 5 9 7 6 11 + 7 6 11 6 7 10 4 4 8 + + 0 8 4 1 9 5 5 10 7 + 5 10 7 4 11 6 0 8 4 + + 1 12 0 3 13 1 7 14 3 + 7 14 3 5 15 2 1 12 0 + + 3 16 4 2 17 5 6 18 7 + 6 18 7 7 19 6 3 16 4 + + 2 20 0 0 21 1 4 22 3 + 4 22 3 6 23 2 2 20 0 +

+
+
+
+
+ + + + + + + + + + + +
diff --git a/models/skinned_cube/skinned_cube.DAE b/models/skinned_cube/skinned_cube.DAE index 825b3c8..542a014 100755 --- a/models/skinned_cube/skinned_cube.DAE +++ b/models/skinned_cube/skinned_cube.DAE @@ -6,8 +6,8 @@ OpenCOLLADA for 3ds Max; Version: 1.6; Revision: 68 file:///C:/cygwin/home/bilbo/d3d10/models/skinned_cube/skinned_cube.max - 2026-01-23T23:56:13 - 2026-01-23T23:56:13 + 2026-01-24T13:01:29 + 2026-01-24T13:01:29 Z_UP @@ -126,9 +126,9 @@ - 1 0 0 0 0 0 0 0 0 0.2 0.8 0.2 0.8 0.2 0.8 0.2 0.8 0.4 0.6 0.4 0.6 0.4 0.6 0.4 0.6 0.6 0.4 0.6 0.4 0.6 0.4 0.6 0.4 0.8 0.2 0.8 0.2 0.8 0.2 0.8 0.2 + 1 0.2 0.8 0.2 0.8 0.2 0.8 0.2 0.8 0.4 0.6 0.4 0.6 0.4 0.6 0.4 0.6 0.6 0.4 0.6 0.4 0.6 0.4 0.6 0.4 0.8 0.2 0.8 0.2 0.8 0.2 0.8 0.2 - + @@ -140,8 +140,8 @@ - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 1 1 0 0 1 2 0 0 1 3 0 0 1 4 0 0 1 0 0 5 1 0 0 6 1 0 0 7 1 0 0 8 1 9 0 10 1 11 0 12 1 13 0 14 1 15 0 16 1 17 0 18 1 19 0 20 1 21 0 22 1 23 0 24 1 25 0 26 1 27 0 28 1 29 0 30 1 31 0 32 1 33 0 34 1 35 0 36 1 37 0 38 1 39 0 40 + 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 1 0 2 1 3 0 4 1 5 0 6 1 7 0 8 1 9 0 10 1 11 0 12 1 13 0 14 1 15 0 16 1 17 0 18 1 19 0 20 1 21 0 22 1 23 0 24 1 25 0 26 1 27 0 28 1 29 0 30 1 31 0 32 diff --git a/models/skinned_cube/skinned_cube.max b/models/skinned_cube/skinned_cube.max index fd21ecb..ab2ecc4 100755 Binary files a/models/skinned_cube/skinned_cube.max and b/models/skinned_cube/skinned_cube.max differ diff --git a/src/collada.cpp b/src/collada.cpp index ba7ba52..fe85aa0 100644 --- a/src/collada.cpp +++ b/src/collada.cpp @@ -117,13 +117,87 @@ namespace collada { 54, 55, 53, }; + static const float inverse_bind_matrices[] = { + 0.0f, 0.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + + 0.0f, 0.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + -10.0f, 0.0f, 0.0f, 1.0f, + }; + + static const float joints_weights[] = { + 0, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 0.2, 0.8, 0, 0, + 1, 0, 0, 0, 0.2, 0.8, 0, 0, + 1, 0, 0, 0, 0.4, 0.6, 0, 0, + 1, 0, 0, 0, 0.4, 0.6, 0, 0, + 1, 0, 0, 0, 0.6, 0.4, 0, 0, + 1, 0, 0, 0, 0.6, 0.4, 0, 0, + 1, 0, 0, 0, 0.8, 0.2, 0, 0, + 1, 0, 0, 0, 0.8, 0.2, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 0.2, 0.8, 0, 0, + 1, 0, 0, 0, 0.2, 0.8, 0, 0, + 1, 0, 0, 0, 0.4, 0.6, 0, 0, + 1, 0, 0, 0, 0.4, 0.6, 0, 0, + 1, 0, 0, 0, 0.6, 0.4, 0, 0, + 1, 0, 0, 0, 0.6, 0.4, 0, 0, + 1, 0, 0, 0, 0.8, 0.2, 0, 0, + 1, 0, 0, 0, 0.8, 0.2, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 0.2, 0.8, 0, 0, + 1, 0, 0, 0, 0.2, 0.8, 0, 0, + 1, 0, 0, 0, 0.4, 0.6, 0, 0, + 1, 0, 0, 0, 0.4, 0.6, 0, 0, + 1, 0, 0, 0, 0.6, 0.4, 0, 0, + 1, 0, 0, 0, 0.6, 0.4, 0, 0, + 1, 0, 0, 0, 0.8, 0.2, 0, 0, + 1, 0, 0, 0, 0.8, 0.2, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 0, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 0.2, 0.8, 0, 0, + 1, 0, 0, 0, 0.2, 0.8, 0, 0, + 1, 0, 0, 0, 0.4, 0.6, 0, 0, + 1, 0, 0, 0, 0.4, 0.6, 0, 0, + 1, 0, 0, 0, 0.6, 0.4, 0, 0, + 1, 0, 0, 0, 0.6, 0.4, 0, 0, + 1, 0, 0, 0, 0.8, 0.2, 0, 0, + 1, 0, 0, 0, 0.8, 0.2, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + 1, 0, 0, 0, 1.0, 0, 0, 0, + }; + ID3D10Effect * g_pEffect = NULL; ID3D10EffectTechnique * g_pTechniqueRender = NULL; ID3D10InputLayout * g_pVertexLayout = NULL; - ID3D10Buffer * g_pVertexBuffer; + ID3D10Buffer * g_pVertexBufferPNT; + ID3D10Buffer * g_pVertexBufferJW; ID3D10Buffer * g_pIndexBuffer; + ID3D10EffectMatrixVariable * g_pJointVariable = NULL; + ID3D10EffectMatrixVariable * g_pWorldVariable = NULL; ID3D10EffectMatrixVariable * g_pViewVariable = NULL; ID3D10EffectMatrixVariable * g_pProjectionVariable = NULL; @@ -154,6 +228,8 @@ namespace collada { g_pTechniqueRender = g_pEffect->GetTechniqueByName("Render"); // variables + g_pJointVariable = g_pEffect->GetVariableByName("mJoint")->AsMatrix(); + g_pWorldVariable = g_pEffect->GetVariableByName("World")->AsMatrix(); g_pViewVariable = g_pEffect->GetVariableByName("View")->AsMatrix(); g_pProjectionVariable = g_pEffect->GetVariableByName("Projection")->AsMatrix(); @@ -163,6 +239,8 @@ namespace collada { {"POSITION" , 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0 , D3D10_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL" , 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD" , 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0}, + {"BLENDINDICES", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D10_INPUT_PER_VERTEX_DATA, 0}, + {"BLENDWEIGHT" , 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 16, D3D10_INPUT_PER_VERTEX_DATA, 0}, }; UINT numElements = (sizeof (layout)) / (sizeof (layout[0])); @@ -194,7 +272,19 @@ namespace collada { bd.CPUAccessFlags = 0; bd.MiscFlags = 0; initData.pSysMem = vertex_buffer; - hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pVertexBuffer); + hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pVertexBufferPNT); + if (FAILED(hr)) { + print("CreateBuffer: D3D10_BIND_VERTEX_BUFFER\n"); + return hr; + } + + bd.Usage = D3D10_USAGE_IMMUTABLE; + bd.ByteWidth = (sizeof (joints_weights)); + bd.BindFlags = D3D10_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = 0; + bd.MiscFlags = 0; + initData.pSysMem = joints_weights; + hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pVertexBufferJW); if (FAILED(hr)) { print("CreateBuffer: D3D10_BIND_VERTEX_BUFFER\n"); return hr; @@ -215,20 +305,48 @@ namespace collada { return S_OK; } - void Render() + void Render(float t) { - XMMATRIX World = XMMatrixScaling(0.1, 0.1, 0.1); + XMMATRIX World = XMMatrixScaling(1, 1, 1); + + // XMMATRIX XM_CALLCONV XMMatrixRotationNormal(FXMVECTOR NormalAxis, float Angle) noexcept; + //XMConvertToRadians + XMVECTOR axis = XMVectorSet(0, 1, 0, 0); + XMVECTOR axisZ = XMVectorSet(0, 0, 1, 0); + XMMATRIX joint0 + = XMMatrixRotationNormal(axis, XMConvertToRadians(-90)) + ; + XMMATRIX joint1 + = XMMatrixRotationNormal(axisZ, sin(t)) + * XMMatrixTranslation(10, 0, 0) + * joint0 + ; + + XMMATRIX joint0ibm = XMLoadFloat4x4((XMFLOAT4X4*)&inverse_bind_matrices[0 * 16]); + XMMATRIX joint1ibm = XMLoadFloat4x4((XMFLOAT4X4*)&inverse_bind_matrices[1 * 16]); + XMMATRIX mJoints[2] = { + joint0ibm * joint0, + joint1ibm * joint1, + }; + + g_pJointVariable->SetMatrixArray((float *)mJoints, 0, 2); g_pWorldVariable->SetMatrix((float *)&World); g_pViewVariable->SetMatrix((float *)&g_View); g_pProjectionVariable->SetMatrix((float *)&g_Projection); UINT stride[] = { - 3 * 3 * 4, + 3 * 3 * 4, // position normal texture + 4 * 2 * 4, // joint weight }; - UINT offset[] = { 0 }; + UINT offset[] = { 0, 0 }; + ID3D10Buffer * vertex_buffers[] = { + g_pVertexBufferPNT, + g_pVertexBufferJW, + }; + g_pd3dDevice->IASetInputLayout(g_pVertexLayout); - g_pd3dDevice->IASetVertexBuffers(0, 1, &g_pVertexBuffer, stride, offset); + g_pd3dDevice->IASetVertexBuffers(0, 2, vertex_buffers, stride, offset); g_pd3dDevice->IASetIndexBuffer(g_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0); g_pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); diff --git a/src/effect/collada.fx b/src/effect/collada.fx index f88529e..97ed0fb 100644 --- a/src/effect/collada.fx +++ b/src/effect/collada.fx @@ -2,11 +2,15 @@ matrix World; matrix View; matrix Projection; +matrix mJoint[2]; + struct VS_INPUT { float4 Pos : POSITION; float3 Normal : NORMAL; float2 Tex : TEXCOORD0; + float4 Joint : BLENDINDICES0; + float4 Weight : BLENDWEIGHT0; }; struct PS_INPUT @@ -20,7 +24,15 @@ PS_INPUT VS(VS_INPUT input) { PS_INPUT output; - output.Pos = mul(input.Pos, World); + matrix mSkin + = input.Weight.x * mJoint[int(input.Joint.x)] + + input.Weight.y * mJoint[int(input.Joint.y)] + + input.Weight.z * mJoint[int(input.Joint.z)] + + input.Weight.w * mJoint[int(input.Joint.w)] + ; + + output.Pos = mul(input.Pos, mSkin); + output.Pos = mul(output.Pos, World); output.Pos = mul(output.Pos, View); output.Pos = mul(output.Pos, Projection); diff --git a/src/main.cpp b/src/main.cpp index 27844c4..f0f8d45 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -126,8 +126,8 @@ XMFLOAT4 g_vLightColors[2] = { // -XMVECTOR g_Eye = XMVectorSet(0.0f, 0.0f, -2.0f, 1.0f); -XMVECTOR g_At = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f); +XMVECTOR g_Eye = XMVectorSet(0.0f, -30.0f, 15.0f, 1.0f); +XMVECTOR g_At = XMVectorSet(0.0f, 0.0f, 15.0f, 1.0f); // forward declarations @@ -1710,15 +1710,15 @@ void Update(float t, float dt) // view XMMATRIX mRotateView - = XMMatrixRotationY(deadzone(g_Joystate.thumbLX) * 3.0f * dt) - * XMMatrixRotationX(deadzone(g_Joystate.thumbLY) * 3.0f * dt); + = XMMatrixRotationX(deadzone(g_Joystate.thumbLY) * 3.0f * dt) + * XMMatrixRotationZ(deadzone(g_Joystate.thumbLX) * 3.0f * dt); XMMATRIX mTranslateView = XMMatrixTranslation(deadzone(g_Joystate.thumbRX) * 3.0f * dt, - 0.0f, - deadzone(g_Joystate.thumbRY) * 3.0f * dt); + deadzone(g_Joystate.thumbRY) * 3.0f * dt, + 0.0f); g_Eye = XMVector4Transform(g_Eye, mTranslateView * mRotateView); - XMVECTOR Up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); + XMVECTOR Up = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); g_View = XMMatrixLookAtLH(g_Eye, g_At, Up); @@ -1827,7 +1827,7 @@ void Render(float t, float dt) RenderFont(dt); - collada::Render(); + collada::Render(t); // present g_pSwapChain->Present(0, 0);