diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index 12765800..cccde825 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -1486,7 +1486,17 @@ void CharacterRenderer::setupModelBuffers(M2ModelGPU& gpuModel) { auto& dst = gpuVerts[i]; dst.position = src.position; std::memcpy(dst.boneWeights, src.boneWeights, 4); - std::memcpy(dst.boneIndices, src.boneIndices, 4); + // Remap bone indices through the bone lookup table. + // M2 vertex bone indices are lookup table indices, not direct bone indices. + for (int j = 0; j < 4; j++) { + uint8_t idx = src.boneIndices[j]; + if (idx < model.boneLookupTable.size()) { + dst.boneIndices[j] = static_cast( + std::min(model.boneLookupTable[idx], 255)); + } else { + dst.boneIndices[j] = 0; + } + } dst.normal = src.normal; dst.texCoords = src.texCoords[0]; // Use first UV set dst.tangent = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); // default diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index d87a6844..921ccad7 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -1158,10 +1158,14 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { vertexData.push_back(w1); vertexData.push_back(w2); vertexData.push_back(w3); - vertexData.push_back(static_cast(std::min(v.boneIndices[0], uint8_t(127)))); - vertexData.push_back(static_cast(std::min(v.boneIndices[1], uint8_t(127)))); - vertexData.push_back(static_cast(std::min(v.boneIndices[2], uint8_t(127)))); - vertexData.push_back(static_cast(std::min(v.boneIndices[3], uint8_t(127)))); + // Remap bone indices through the bone lookup table. + // M2 vertex bone indices are lookup table indices, not direct bone indices. + for (int j = 0; j < 4; j++) { + uint8_t idx = v.boneIndices[j]; + uint16_t actual = (idx < model.boneLookupTable.size()) + ? model.boneLookupTable[idx] : 0; + vertexData.push_back(static_cast(std::min(actual, uint16_t(127)))); + } } // Upload vertex buffer to GPU