mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-05-25 02:51:43 +00:00
feat: calculate model collision tree with leafs
This commit is contained in:
@@ -327,8 +327,8 @@ namespace IW3
|
|||||||
|
|
||||||
struct XSurfaceCollisionTree
|
struct XSurfaceCollisionTree
|
||||||
{
|
{
|
||||||
float trans[3];
|
vec3_t trans;
|
||||||
float scale[3];
|
vec3_t scale;
|
||||||
unsigned int nodeCount;
|
unsigned int nodeCount;
|
||||||
XSurfaceCollisionNode* nodes;
|
XSurfaceCollisionNode* nodes;
|
||||||
unsigned int leafCount;
|
unsigned int leafCount;
|
||||||
|
|||||||
@@ -468,8 +468,8 @@ namespace IW4
|
|||||||
|
|
||||||
struct XSurfaceCollisionTree
|
struct XSurfaceCollisionTree
|
||||||
{
|
{
|
||||||
float trans[3];
|
vec3_t trans;
|
||||||
float scale[3];
|
vec3_t scale;
|
||||||
unsigned int nodeCount;
|
unsigned int nodeCount;
|
||||||
XSurfaceCollisionNode* nodes;
|
XSurfaceCollisionNode* nodes;
|
||||||
unsigned int leafCount;
|
unsigned int leafCount;
|
||||||
|
|||||||
@@ -492,8 +492,8 @@ namespace IW5
|
|||||||
|
|
||||||
struct XSurfaceCollisionTree
|
struct XSurfaceCollisionTree
|
||||||
{
|
{
|
||||||
float trans[3];
|
vec3_t trans;
|
||||||
float scale[3];
|
vec3_t scale;
|
||||||
unsigned int nodeCount;
|
unsigned int nodeCount;
|
||||||
XSurfaceCollisionNode* nodes;
|
XSurfaceCollisionNode* nodes;
|
||||||
unsigned int leafCount;
|
unsigned int leafCount;
|
||||||
|
|||||||
@@ -488,8 +488,8 @@ namespace T5
|
|||||||
|
|
||||||
struct XSurfaceCollisionTree
|
struct XSurfaceCollisionTree
|
||||||
{
|
{
|
||||||
float trans[3];
|
vec3_t trans;
|
||||||
float scale[3];
|
vec3_t scale;
|
||||||
unsigned int nodeCount;
|
unsigned int nodeCount;
|
||||||
XSurfaceCollisionNode* nodes;
|
XSurfaceCollisionNode* nodes;
|
||||||
unsigned int leafCount;
|
unsigned int leafCount;
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#include "CollisionTreeCreator.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class Bounds
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Bounds()
|
||||||
|
: m_mins(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
|
||||||
|
m_maxs(std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandFromTri(const std::array<Eigen::Vector3f, 3>& triCoordinates)
|
||||||
|
{
|
||||||
|
for (const auto& vert : triCoordinates)
|
||||||
|
{
|
||||||
|
m_mins[0] = std::min(m_mins[0], vert.x());
|
||||||
|
m_mins[1] = std::min(m_mins[1], vert.y());
|
||||||
|
m_mins[2] = std::min(m_mins[2], vert.z());
|
||||||
|
m_maxs[0] = std::max(m_maxs[0], vert.x());
|
||||||
|
m_maxs[1] = std::max(m_maxs[1], vert.y());
|
||||||
|
m_maxs[2] = std::max(m_maxs[2], vert.z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandFromBounds(const Bounds& otherBounds)
|
||||||
|
{
|
||||||
|
m_mins[0] = std::min(m_mins[0], otherBounds.m_mins[0]);
|
||||||
|
m_mins[1] = std::min(m_mins[1], otherBounds.m_mins[1]);
|
||||||
|
m_mins[2] = std::min(m_mins[2], otherBounds.m_mins[2]);
|
||||||
|
m_maxs[0] = std::min(m_maxs[0], otherBounds.m_maxs[0]);
|
||||||
|
m_maxs[1] = std::min(m_maxs[1], otherBounds.m_maxs[1]);
|
||||||
|
m_maxs[2] = std::min(m_maxs[2], otherBounds.m_maxs[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Eigen::Vector3f GetDelta() const
|
||||||
|
{
|
||||||
|
return m_maxs - m_mins;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] float GetVolume() const
|
||||||
|
{
|
||||||
|
return GetDelta().prod();
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3f m_mins;
|
||||||
|
Eigen::Vector3f m_maxs;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace xmodel
|
||||||
|
{
|
||||||
|
std::unique_ptr<CommonCollisionTree> CreateCollisionTree(const IRigidVertListAccessor& vertList)
|
||||||
|
{
|
||||||
|
auto tree = std::make_unique<CommonCollisionTree>();
|
||||||
|
|
||||||
|
Bounds globalBounds;
|
||||||
|
|
||||||
|
const auto triOffset = vertList.GetTriOffset();
|
||||||
|
const auto triCount = vertList.GetTriCount();
|
||||||
|
std::vector<Bounds> leafBounds;
|
||||||
|
auto lastMergeable = false;
|
||||||
|
|
||||||
|
Bounds prevBounds;
|
||||||
|
for (size_t triNumber = 0; triNumber < triCount; triNumber++)
|
||||||
|
{
|
||||||
|
const auto triIndex = triOffset + triNumber;
|
||||||
|
Bounds triBounds;
|
||||||
|
|
||||||
|
triBounds.ExpandFromTri(vertList.GetCoordinatesForTri(triIndex));
|
||||||
|
globalBounds.ExpandFromBounds(triBounds);
|
||||||
|
|
||||||
|
auto shouldMerge = false;
|
||||||
|
if (lastMergeable)
|
||||||
|
{
|
||||||
|
const auto prevVolume = prevBounds.GetVolume();
|
||||||
|
const auto thisVolume = triBounds.GetVolume();
|
||||||
|
|
||||||
|
prevBounds.ExpandFromBounds(triBounds);
|
||||||
|
const auto combinedVolume = prevBounds.GetVolume();
|
||||||
|
if (combinedVolume <= prevVolume + thisVolume)
|
||||||
|
shouldMerge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldMerge)
|
||||||
|
{
|
||||||
|
leafBounds.back() = prevBounds;
|
||||||
|
assert(!tree->leafs.back().twoTriangles);
|
||||||
|
tree->leafs.back().twoTriangles = 1;
|
||||||
|
lastMergeable = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tree->leafs.emplace_back(CommonCollisionLeaf{
|
||||||
|
.triangleBeginIndex = static_cast<decltype(CommonCollisionLeaf::triangleBeginIndex)>(triIndex),
|
||||||
|
.twoTriangles = 0,
|
||||||
|
});
|
||||||
|
leafBounds.emplace_back(triBounds);
|
||||||
|
lastMergeable = true;
|
||||||
|
prevBounds = triBounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->trans = -globalBounds.m_mins;
|
||||||
|
const auto globalDelta = globalBounds.GetDelta();
|
||||||
|
tree->scale = Eigen::Vector3f(std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max())
|
||||||
|
.cwiseQuotient(globalDelta);
|
||||||
|
|
||||||
|
// TODO: Calculate nodes
|
||||||
|
|
||||||
|
return std::move(tree);
|
||||||
|
}
|
||||||
|
} // namespace xmodel
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace xmodel
|
||||||
|
{
|
||||||
|
struct CommonCollisionAabb
|
||||||
|
{
|
||||||
|
std::array<uint16_t, 3> mins;
|
||||||
|
std::array<uint16_t, 3> maxs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CommonCollisionNode
|
||||||
|
{
|
||||||
|
CommonCollisionAabb aabb;
|
||||||
|
uint16_t childBeginIndex;
|
||||||
|
uint16_t childCount : 15;
|
||||||
|
uint16_t childrenAreLeafs : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CommonCollisionLeaf
|
||||||
|
{
|
||||||
|
uint16_t triangleBeginIndex : 15;
|
||||||
|
uint16_t twoTriangles : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CommonCollisionTree
|
||||||
|
{
|
||||||
|
Eigen::Vector3f trans;
|
||||||
|
Eigen::Vector3f scale;
|
||||||
|
std::vector<CommonCollisionNode> nodes;
|
||||||
|
std::vector<CommonCollisionLeaf> leafs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IRigidVertListAccessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IRigidVertListAccessor() = default;
|
||||||
|
virtual ~IRigidVertListAccessor() = default;
|
||||||
|
IRigidVertListAccessor(const IRigidVertListAccessor& other) = default;
|
||||||
|
IRigidVertListAccessor(IRigidVertListAccessor&& other) noexcept = default;
|
||||||
|
IRigidVertListAccessor& operator=(const IRigidVertListAccessor& other) = default;
|
||||||
|
IRigidVertListAccessor& operator=(IRigidVertListAccessor&& other) noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual size_t GetTriOffset() const = 0;
|
||||||
|
[[nodiscard]] virtual size_t GetTriCount() const = 0;
|
||||||
|
[[nodiscard]] virtual std::array<Eigen::Vector3f, 3> GetCoordinatesForTri(size_t triIndex) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<CommonCollisionTree> CreateCollisionTree(const IRigidVertListAccessor& vertList);
|
||||||
|
} // namespace xmodel
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
#include "XModel/CollisionTreeCreator.h"
|
||||||
#include "XModel/PartClassificationState.h"
|
#include "XModel/PartClassificationState.h"
|
||||||
#include "XModel/TangentData.h"
|
#include "XModel/TangentData.h"
|
||||||
#include "XModel/Tangentspace.h"
|
#include "XModel/Tangentspace.h"
|
||||||
@@ -59,6 +60,47 @@ using namespace GAME;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
constexpr uint16_t XSURFACE_COLLISION_NODE_HAS_LEAFS = 0x8000;
|
||||||
|
constexpr uint16_t XSURFACE_COLLISION_LEAF_TWO_TRIANGLES = 0x8000;
|
||||||
|
|
||||||
|
Eigen::Vector3f ToEigen(const vec3_t& vec)
|
||||||
|
{
|
||||||
|
return Eigen::Vector3f(vec.x, vec.y, vec.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
class RigidVertListAccessor : public xmodel::IRigidVertListAccessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RigidVertListAccessor(const XSurface& surface, const XRigidVertList& rigidVertList)
|
||||||
|
: m_surface(surface),
|
||||||
|
m_rigid_vert_list(rigidVertList)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t GetTriOffset() const override
|
||||||
|
{
|
||||||
|
return m_rigid_vert_list.triOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t GetTriCount() const override
|
||||||
|
{
|
||||||
|
return m_rigid_vert_list.triCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::array<Eigen::Vector3f, 3> GetCoordinatesForTri(const size_t triIndex) const override
|
||||||
|
{
|
||||||
|
return std::array<Eigen::Vector3f, 3>({
|
||||||
|
ToEigen(m_surface.verts0[m_surface.triIndices[triIndex].i[0]].xyz),
|
||||||
|
ToEigen(m_surface.verts0[m_surface.triIndices[triIndex].i[1]].xyz),
|
||||||
|
ToEigen(m_surface.verts0[m_surface.triIndices[triIndex].i[2]].xyz),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const XSurface& m_surface;
|
||||||
|
const XRigidVertList& m_rigid_vert_list;
|
||||||
|
};
|
||||||
|
|
||||||
class XModelLoader final : public AssetCreator<AssetXModel>
|
class XModelLoader final : public AssetCreator<AssetXModel>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -505,6 +547,49 @@ namespace
|
|||||||
const auto shiftValue = 31u - (boneIndex % 32u);
|
const auto shiftValue = 31u - (boneIndex % 32u);
|
||||||
surface.partBits[partBitsIndex] |= 1 << shiftValue;
|
surface.partBits[partBitsIndex] |= 1 << shiftValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] XSurfaceCollisionTree* ConvertCollisionTree(const xmodel::CommonCollisionTree& commonTree) const
|
||||||
|
{
|
||||||
|
auto* tree = m_memory.Alloc<XSurfaceCollisionTree>();
|
||||||
|
tree->trans.x = commonTree.trans.x();
|
||||||
|
tree->trans.y = commonTree.trans.y();
|
||||||
|
tree->trans.z = commonTree.trans.z();
|
||||||
|
tree->scale.x = commonTree.scale.x();
|
||||||
|
tree->scale.y = commonTree.scale.y();
|
||||||
|
tree->scale.z = commonTree.scale.z();
|
||||||
|
tree->nodeCount = static_cast<decltype(XSurfaceCollisionTree::nodeCount)>(commonTree.nodes.size());
|
||||||
|
tree->nodes = m_memory.Alloc<XSurfaceCollisionNode>(tree->nodeCount);
|
||||||
|
for (auto nodeIndex = 0u; nodeIndex < tree->nodeCount; nodeIndex++)
|
||||||
|
{
|
||||||
|
auto& node = tree->nodes[nodeIndex];
|
||||||
|
const auto& commonNode = commonTree.nodes[nodeIndex];
|
||||||
|
|
||||||
|
node.aabb.mins[0] = commonNode.aabb.mins[0];
|
||||||
|
node.aabb.mins[1] = commonNode.aabb.mins[1];
|
||||||
|
node.aabb.mins[2] = commonNode.aabb.mins[2];
|
||||||
|
node.aabb.maxs[0] = commonNode.aabb.maxs[0];
|
||||||
|
node.aabb.maxs[1] = commonNode.aabb.maxs[1];
|
||||||
|
node.aabb.maxs[2] = commonNode.aabb.maxs[2];
|
||||||
|
node.childBeginIndex = commonNode.childBeginIndex;
|
||||||
|
node.childCount = commonNode.childCount;
|
||||||
|
if (commonNode.childrenAreLeafs)
|
||||||
|
node.childCount |= XSURFACE_COLLISION_NODE_HAS_LEAFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->leafCount = commonTree.leafs.size();
|
||||||
|
tree->leafs = m_memory.Alloc<XSurfaceCollisionLeaf>(tree->leafCount);
|
||||||
|
for (auto leafIndex = 0u; leafIndex < tree->leafCount; leafIndex++)
|
||||||
|
{
|
||||||
|
auto& leaf = tree->leafs[leafIndex];
|
||||||
|
const auto& commonLeaf = commonTree.leafs[leafIndex];
|
||||||
|
|
||||||
|
leaf.triangleBeginIndex = commonLeaf.triangleBeginIndex;
|
||||||
|
if (commonLeaf.twoTriangles)
|
||||||
|
leaf.triangleBeginIndex |= XSURFACE_COLLISION_LEAF_TWO_TRIANGLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
void CreateVertListData(XSurface& surface, const std::vector<size_t>& xmodelToCommonVertexIndexLookup, const XModelCommon& common) const
|
void CreateVertListData(XSurface& surface, const std::vector<size_t>& xmodelToCommonVertexIndexLookup, const XModelCommon& common) const
|
||||||
{
|
{
|
||||||
@@ -538,7 +623,10 @@ namespace
|
|||||||
|
|
||||||
if (boneVertList.triCount > 0 || boneVertList.vertCount > 0)
|
if (boneVertList.triCount > 0 || boneVertList.vertCount > 0)
|
||||||
{
|
{
|
||||||
boneVertList.collisionTree = nullptr; // TODO
|
RigidVertListAccessor vertListAccessor(surface, boneVertList);
|
||||||
|
const auto commonCollisionTree = xmodel::CreateCollisionTree(vertListAccessor);
|
||||||
|
boneVertList.collisionTree = ConvertCollisionTree(*commonCollisionTree);
|
||||||
|
|
||||||
vertLists.emplace_back(boneVertList);
|
vertLists.emplace_back(boneVertList);
|
||||||
|
|
||||||
currentVertexTail = currentVertexHead;
|
currentVertexTail = currentVertexHead;
|
||||||
|
|||||||
Reference in New Issue
Block a user