chore: implement vertex weights

This commit is contained in:
Jan 2024-08-11 01:14:38 +02:00
parent f35a2f33ff
commit f4092972e8
No known key found for this signature in database
GPG Key ID: 44B581F78FF5C57C
3 changed files with 200 additions and 20 deletions

View File

@ -2749,7 +2749,12 @@ namespace T6
float* tensionData; float* tensionData;
}; };
typedef tdef_align(16) unsigned short r_index16_t; struct XSurfaceTri
{
uint16_t i[3];
};
typedef tdef_align(16) XSurfaceTri XSurfaceTri16;
struct type_align(16) XSurface struct type_align(16) XSurface
{ {
@ -2759,13 +2764,13 @@ namespace T6
uint16_t vertCount; uint16_t vertCount;
uint16_t triCount; uint16_t triCount;
uint16_t baseVertIndex; uint16_t baseVertIndex;
r_index16_t(*triIndices)[3]; XSurfaceTri16* triIndices;
XSurfaceVertexInfo vertInfo; XSurfaceVertexInfo vertInfo;
GfxPackedVertex* verts0; GfxPackedVertex* verts0;
void /*ID3D11Buffer*/* vb0; void /*ID3D11Buffer*/* vb0;
XRigidVertList* vertList; XRigidVertList* vertList;
void /*ID3D11Buffer*/* indexBuffer; void /*ID3D11Buffer*/* indexBuffer;
int partBits[5]; unsigned int partBits[5];
}; };
struct XModelCollSurf_s struct XModelCollSurf_s

View File

@ -20,6 +20,7 @@
#include <format> #include <format>
#include <iostream> #include <iostream>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <numeric>
#include <vector> #include <vector>
using namespace nlohmann; using namespace nlohmann;
@ -412,13 +413,166 @@ namespace
vertex.tangent = Common::Vec3PackUnitVec(wrongTangent); // TODO: Fill with actual value vertex.tangent = Common::Vec3PackUnitVec(wrongTangent); // TODO: Fill with actual value
} }
static size_t GetRigidBoneForVertex(const size_t vertexIndex, const XModelCommon& common)
{
return common.m_bone_weight_data.weights[common.m_vertex_bone_weights[vertexIndex].weightOffset].boneIndex;
}
static std::vector<std::optional<size_t>>
GetRigidBoneIndicesForTris(const std::vector<size_t>& vertexIndices, XSurface& surface, const XModelCommon& common)
{
std::vector<std::optional<size_t>> rigidBoneIndexForTri;
rigidBoneIndexForTri.reserve(surface.triCount);
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
{
const auto& tri = surface.triIndices[triIndex];
const auto vert0Bone = GetRigidBoneForVertex(vertexIndices[tri.i[0]], common);
const auto vert1Bone = GetRigidBoneForVertex(vertexIndices[tri.i[1]], common);
const auto vert2Bone = GetRigidBoneForVertex(vertexIndices[tri.i[2]], common);
const auto hasSameBone = vert0Bone == vert1Bone && vert1Bone == vert2Bone;
if (hasSameBone)
rigidBoneIndexForTri.emplace_back(vert0Bone);
else
rigidBoneIndexForTri.emplace_back(std::nullopt);
}
return rigidBoneIndexForTri;
}
static void ReorderRigidTrisByBoneIndex(const std::vector<size_t>& vertexIndices, XSurface& surface, const XModelCommon& common)
{
const auto rigidBoneIndexForTri = GetRigidBoneIndicesForTris(vertexIndices, surface, common);
std::vector<size_t> triSortList(surface.triCount);
std::ranges::iota(triSortList, 0);
std::ranges::sort(triSortList,
[&rigidBoneIndexForTri](const size_t triIndex0, const size_t triIndex1)
{
const auto rigidBone0 = rigidBoneIndexForTri[triIndex0];
const auto rigidBone1 = rigidBoneIndexForTri[triIndex1];
if (rigidBone0.has_value() != rigidBone1.has_value())
return rigidBone0.has_value();
if (!rigidBone0.has_value())
return true;
return *rigidBone0 < *rigidBone1;
});
std::vector<std::remove_pointer_t<decltype(XSurface::triIndices)>> sortedTris(surface.triCount);
for (auto i = 0u; i < surface.triCount; i++)
memcpy(&sortedTris[i], &surface.triIndices[triSortList[i]], sizeof(std::remove_pointer_t<decltype(XSurface::triIndices)>));
memcpy(surface.triIndices, sortedTris.data(), sizeof(std::remove_pointer_t<decltype(XSurface::triIndices)>) * surface.triCount);
}
void CreateVertListData(XSurface& surface, const std::vector<size_t>& vertexIndices, const XModelCommon& common)
{
ReorderRigidTrisByBoneIndex(vertexIndices, surface, common);
const auto rigidBoneIndexForTri = GetRigidBoneIndicesForTris(vertexIndices, surface, common);
std::vector<XRigidVertList> vertLists;
auto currentVertexTail = 0u;
auto currentTriTail = 0u;
const auto vertexCount = vertexIndices.size();
const auto triCount = static_cast<size_t>(surface.triCount);
const auto boneCount = common.m_bones.size();
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
{
XRigidVertList boneVertList{};
boneVertList.boneOffset = static_cast<uint16_t>(boneIndex * sizeof(DObjSkelMat));
auto currentVertexHead = currentVertexTail;
while (currentVertexHead < vertexCount && GetRigidBoneForVertex(currentVertexHead, common) == boneIndex)
currentVertexHead++;
auto currentTriHead = currentTriTail;
while (currentTriHead < triCount && rigidBoneIndexForTri[currentTriHead] && *rigidBoneIndexForTri[currentTriHead] == boneIndex)
currentTriHead++;
boneVertList.vertCount = static_cast<uint16_t>(currentVertexHead - currentVertexTail);
boneVertList.triOffset = static_cast<uint16_t>(currentTriTail);
boneVertList.triCount = static_cast<uint16_t>(currentTriHead - currentTriTail);
if (boneVertList.triCount > 0 || boneVertList.vertCount > 0)
{
boneVertList.collisionTree = nullptr; // TODO
vertLists.emplace_back(boneVertList);
currentVertexTail = currentVertexHead;
currentTriTail = currentTriHead;
}
}
if (!vertLists.empty())
{
surface.vertListCount = static_cast<unsigned char>(vertLists.size());
surface.vertList = m_memory.Alloc<XRigidVertList>(surface.vertListCount);
memcpy(surface.vertList, vertLists.data(), sizeof(XRigidVertList) * surface.vertListCount);
}
}
void CreateVertsBlendData(XSurface& surface, const std::vector<size_t>& vertexIndices, const XModelCommon& common)
{
// TODO
assert(false);
}
static void ReorderVerticesByWeightCount(std::vector<size_t>& vertexIndices, XSurface& surface, const XModelCommon& common)
{
if (common.m_vertex_bone_weights.empty())
return;
const auto vertexCount = vertexIndices.size();
std::vector<size_t> reorderLookup(vertexCount);
std::ranges::iota(reorderLookup, 0);
std::ranges::sort(reorderLookup,
[&common, &vertexIndices](const size_t& i0, const size_t& i1)
{
const auto& weights0 = common.m_vertex_bone_weights[vertexIndices[i0]];
const auto& weights1 = common.m_vertex_bone_weights[vertexIndices[i1]];
if (weights0.weightCount < weights1.weightCount)
return true;
// If there is only one weight, make sure all vertices of the same bone follow another
if (weights0.weightCount == 1)
{
const auto bone0 = common.m_bone_weight_data.weights[weights0.weightOffset].boneIndex;
const auto bone1 = common.m_bone_weight_data.weights[weights1.weightOffset].boneIndex;
return bone0 < bone1;
}
return false;
});
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
{
auto& triIndices = surface.triIndices[triIndex];
triIndices.i[0] = static_cast<uint16_t>(reorderLookup[triIndices.i[0]]);
triIndices.i[1] = static_cast<uint16_t>(reorderLookup[triIndices.i[1]]);
triIndices.i[2] = static_cast<uint16_t>(reorderLookup[triIndices.i[2]]);
}
for (auto& entry : reorderLookup)
entry = vertexIndices[entry];
vertexIndices = std::move(reorderLookup);
}
bool CreateXSurface(XSurface& surface, const XModelObject& commonObject, const XModelCommon& common) bool CreateXSurface(XSurface& surface, const XModelObject& commonObject, const XModelCommon& common)
{ {
std::vector<GfxPackedVertex> verts; std::vector<size_t> vertexIndices;
std::unordered_map<size_t, size_t> usedVertices; std::unordered_map<size_t, size_t> usedVertices;
surface.triCount = static_cast<uint16_t>(commonObject.m_faces.size()); surface.triCount = static_cast<uint16_t>(commonObject.m_faces.size());
surface.triIndices = m_memory.Alloc<r_index16_t[3]>(commonObject.m_faces.size()); surface.triIndices = m_memory.Alloc<XSurfaceTri>(commonObject.m_faces.size());
const auto faceCount = commonObject.m_faces.size(); const auto faceCount = commonObject.m_faces.size();
for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++) for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++)
@ -432,25 +586,46 @@ namespace
const auto existingVertex = usedVertices.find(vertexIndex); const auto existingVertex = usedVertices.find(vertexIndex);
if (existingVertex == usedVertices.end()) if (existingVertex == usedVertices.end())
{ {
const auto newVertexIndex = verts.size(); const auto newVertexIndex = vertexIndices.size();
tris[faceVertexIndex] = static_cast<r_index16_t>(newVertexIndex); tris.i[faceVertexIndex] = static_cast<uint16_t>(newVertexIndex);
const auto& commonVertex = common.m_vertices[vertexIndex]; vertexIndices.emplace_back(vertexIndex);
GfxPackedVertex vertex{};
CreateVertex(vertex, commonVertex);
verts.emplace_back(vertex);
usedVertices.emplace(vertexIndex, newVertexIndex); usedVertices.emplace(vertexIndex, newVertexIndex);
} }
else else
tris[faceVertexIndex] = static_cast<r_index16_t>(existingVertex->second); tris.i[faceVertexIndex] = static_cast<uint16_t>(existingVertex->second);
} }
} }
surface.vertCount = static_cast<uint16_t>(verts.size()); ReorderVerticesByWeightCount(vertexIndices, surface, common);
surface.verts0 = m_memory.Alloc<GfxPackedVertex>(verts.size());
memcpy(surface.verts0, verts.data(), sizeof(GfxPackedVertex) * verts.size()); const auto vertexCount = vertexIndices.size();
surface.vertCount = static_cast<uint16_t>(vertexCount);
surface.verts0 = m_memory.Alloc<GfxPackedVertex>(vertexCount);
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
{
const auto& commonVertex = common.m_vertices[vertexIndex];
CreateVertex(surface.verts0[vertexIndex], commonVertex);
}
if (!common.m_vertex_bone_weights.empty())
{
// Since bone weights are sorted by weight count, the last must have the highest weight count
const auto hasVertsBlend = common.m_vertex_bone_weights[vertexIndices[vertexIndices.size() - 1]].weightCount > 1;
if (!hasVertsBlend)
CreateVertListData(surface, vertexIndices, common);
else
CreateVertsBlendData(surface, vertexIndices, common);
}
const auto boneCount = common.m_bones.size();
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
{
const auto partBitsIndex = boneIndex / 32u;
const auto shiftValue = 31u - (boneIndex % 32u);
surface.partBits[partBitsIndex] = 1 << (31u - shiftValue);
}
return true; return true;
} }

View File

@ -424,9 +424,9 @@ namespace
const auto& tri = surface.triIndices[triIndex]; const auto& tri = surface.triIndices[triIndex];
XModelFace face{}; XModelFace face{};
face.vertexIndex[0] = tri[0] + surface.baseVertIndex; face.vertexIndex[0] = tri.i[0] + surface.baseVertIndex;
face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[1] = tri.i[1] + surface.baseVertIndex;
face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.vertexIndex[2] = tri.i[2] + surface.baseVertIndex;
object.m_faces.emplace_back(face); object.m_faces.emplace_back(face);
} }
} }