Reorganized collision data classes & added basic collision editor window with an OBB tree visualization

This commit is contained in:
Aruki 2019-02-12 02:50:07 -07:00
parent ff021dcdda
commit 4e1560a99c
32 changed files with 896 additions and 439 deletions

2
externals/LibCommon vendored

@ -1 +1 @@
Subproject commit 3c6a40742551d7afd0737d1293d036df69f34ec6
Subproject commit fe3ccf42b71382683f976bb0509a8e68578e1deb

View File

@ -110,8 +110,8 @@ HEADERS += \
Resource/Script/CScriptTemplate.h \
Resource/Script/EVolumeShape.h \
Resource/StringTable/CStringTable.h \
Resource/CCollisionMesh.h \
Resource/CCollisionMeshGroup.h \
Resource/Collision/CCollisionMesh.h \
Resource/Collision/CCollisionMeshGroup.h \
Resource/CFont.h \
Resource/CLight.h \
Resource/CMaterial.h \
@ -174,7 +174,7 @@ HEADERS += \
Render/EDepthGroup.h \
Scene/CScriptAttachNode.h \
ScriptExtra/CSandwormExtra.h \
Resource/CCollisionMaterial.h \
Resource/Collision/CCollisionMaterial.h \
GameProject/CGameProject.h \
GameProject/CPackage.h \
GameProject/CGameExporter.h \
@ -256,7 +256,11 @@ HEADERS += \
Resource/Scan/SScanParametersMP1.h \
Resource/Scan/ELogbookCategory.h \
Resource/Cooker/CScanCooker.h \
NCoreTests.h
NCoreTests.h \
Resource/Collision/SCollisionIndexData.h \
Resource/Collision/CCollisionRenderData.h \
Resource/Collision/SOBBTreeNode.h \
Resource/Collision/CCollidableOBBTree.h
# Source Files
SOURCES += \
@ -287,7 +291,7 @@ SOURCES += \
Resource/Model/SSurface.cpp \
Resource/Script/CScriptObject.cpp \
Resource/Script/CScriptTemplate.cpp \
Resource/CCollisionMesh.cpp \
Resource/Collision/CCollisionMesh.cpp \
Resource/CFont.cpp \
Resource/CLight.cpp \
Resource/CMaterial.cpp \
@ -333,7 +337,7 @@ SOURCES += \
Resource/Model/EVertexAttribute.cpp \
Scene/CScriptAttachNode.cpp \
ScriptExtra/CSandwormExtra.cpp \
Resource/CCollisionMaterial.cpp \
Resource/Collision/CCollisionMaterial.cpp \
GameProject/CGameProject.cpp \
GameProject/CGameExporter.cpp \
GameProject/CResourceStore.cpp \
@ -375,7 +379,8 @@ SOURCES += \
Resource/Cooker/CStringCooker.cpp \
Resource/Scan/CScan.cpp \
Resource/Cooker/CScanCooker.cpp \
NCoreTests.cpp
NCoreTests.cpp \
Resource/Collision/CCollisionRenderData.cpp
# Codegen
CODEGEN_DIR = $$EXTERNALS_DIR/CodeGen

View File

@ -1,7 +1,7 @@
#ifndef SVIEWINFO
#define SVIEWINFO
#include "Core/Resource/CCollisionMaterial.h"
#include "Core/Resource/Collision/CCollisionMaterial.h"
#include "Core/Scene/FShowFlags.h"
#include <Common/Math/CFrustumPlanes.h>
#include <Common/Math/CMatrix4f.h>
@ -11,20 +11,24 @@ struct SCollisionRenderSettings
{
uint64 HighlightMask;
uint64 HideMask;
int BoundingHierarchyRenderDepth;
CCollisionMaterial HideMaterial;
bool DrawWireframe;
bool DrawBackfaces;
bool DrawAreaCollisionBounds;
bool DrawBoundingHierarchy;
bool TintWithSurfaceColor;
bool TintUnwalkableTris;
SCollisionRenderSettings()
: HighlightMask(0)
, HideMask(0)
, BoundingHierarchyRenderDepth(0)
, DrawWireframe(true)
, DrawBackfaces(false)
, DrawAreaCollisionBounds(true)
, DrawBoundingHierarchy(false)
, TintWithSurfaceColor(true)
, TintUnwalkableTris(true) {}
};

View File

@ -19,8 +19,6 @@ CGameArea::~CGameArea()
{
ClearTerrain();
delete mpCollision;
for (uint32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++)
delete mScriptLayers[iSCLY];

View File

@ -2,10 +2,10 @@
#define CGAMEAREA_H
#include "Core/Resource/CResource.h"
#include "Core/Resource/CCollisionMeshGroup.h"
#include "Core/Resource/CLight.h"
#include "Core/Resource/CMaterialSet.h"
#include "Core/Resource/CPoiToWorld.h"
#include "Core/Resource/Collision/CCollisionMeshGroup.h"
#include "Core/Resource/Model/CModel.h"
#include "Core/Resource/Model/CStaticModel.h"
#include <Common/BasicTypes.h>
@ -51,7 +51,7 @@ class CGameArea : public CResource
std::vector<CScriptLayer*> mScriptLayers;
std::unordered_map<uint32, CScriptObject*> mObjectMap;
// Collision
CCollisionMeshGroup *mpCollision;
std::unique_ptr<CCollisionMeshGroup> mpCollision;
// Lights
std::vector<std::vector<CLight*>> mLightLayers;
// Path Mesh
@ -93,7 +93,7 @@ public:
inline uint32 NumStaticModels() const { return mStaticWorldModels.size(); }
inline CModel* TerrainModel(uint32 iMdl) const { return mWorldModels[iMdl]; }
inline CStaticModel* StaticModel(uint32 iMdl) const { return mStaticWorldModels[iMdl]; }
inline CCollisionMeshGroup* Collision() const { return mpCollision; }
inline CCollisionMeshGroup* Collision() const { return mpCollision.get(); }
inline uint32 NumScriptLayers() const { return mScriptLayers.size(); }
inline CScriptLayer* ScriptLayer(uint32 Index) const { return mScriptLayers[Index]; }
inline uint32 NumLightLayers() const { return mLightLayers.size(); }

View File

@ -1,169 +0,0 @@
#include "CCollisionMesh.h"
#include "Core/Render/CRenderer.h"
#include "Core/Render/CDrawUtil.h"
#include <Common/Macros.h>
CCollisionMesh::CCollisionMesh()
{
mVBO.SetVertexDesc(EVertexAttribute::Position | EVertexAttribute::Normal);
mVertexCount = 0;
mLineCount = 0;
mFaceCount = 0;
mBuffered = false;
mIBO.SetPrimitiveType(GL_TRIANGLES);
}
CCollisionMesh::~CCollisionMesh()
{
if (mBuffered)
{
mIBO.Clear();
mVBO.Clear();
mBuffered = false;
}
}
void CCollisionMesh::BufferGL()
{
if (mBuffered)
{
mIBO.Clear();
mVBO.Clear();
mBuffered = false;
}
// Create new list of collision faces sorted by material index
std::vector<CCollisionFace> SortedTris = mCollisionFaces;
std::sort(SortedTris.begin(), SortedTris.end(), [](CCollisionFace& rLeft, CCollisionFace& rRight) -> bool {
return rLeft.MaterialIdx < rRight.MaterialIdx;
});
// Add all the relevant indices to the IBO
mVBO.Reserve(SortedTris.size() * 3);
mIBO.Reserve(SortedTris.size() * 3);
mMaterialOffsets.reserve(mMaterials.size());
uint32 CurMat = 0;
for (uint32 iTri = 0; iTri < SortedTris.size(); iTri++)
{
uint16 Verts[3];
CCollisionFace *pFace = &SortedTris[iTri];
CCollisionLine *pLineA = GetLine(pFace->Lines[0]);
CCollisionLine *pLineB = GetLine(pFace->Lines[1]);
Verts[0] = pLineA->Vertices[0];
Verts[1] = pLineA->Vertices[1];
// Check if we've reached a new material
if (pFace->MaterialIdx != CurMat)
{
while (CurMat != pFace->MaterialIdx)
{
mMaterialOffsets.push_back(mIBO.GetSize());
CurMat++;
}
}
// We have two vertex indices; the last one is one of the ones on line B, but we're not sure which one
if ((pLineB->Vertices[0] != Verts[0]) &&
(pLineB->Vertices[0] != Verts[1]))
Verts[2] = pLineB->Vertices[0];
else
Verts[2] = pLineB->Vertices[1];
// Some faces have a property that indicates they need to be inverted
if (GetMaterial(pFace->MaterialIdx) & eCF_FlippedTri)
{
uint16 V0 = Verts[0];
Verts[0] = Verts[2];
Verts[2] = V0;
}
// Generate vertices - we don't share vertices between triangles in order to get the generated normals looking correct
CCollisionVertex& rVert0 = mCollisionVertices[Verts[0]];
CCollisionVertex& rVert1 = mCollisionVertices[Verts[1]];
CCollisionVertex& rVert2 = mCollisionVertices[Verts[2]];
CVector3f V0toV1 = (rVert1.Pos - rVert0.Pos).Normalized();
CVector3f V0toV2 = (rVert2.Pos - rVert0.Pos).Normalized();
CVector3f FaceNormal = V0toV1.Cross(V0toV2).Normalized();
for (uint32 iVtx = 0; iVtx < 3; iVtx++)
{
CVertex Vtx;
Vtx.Position = mCollisionVertices[ Verts[iVtx] ].Pos;
Vtx.Normal = FaceNormal;
uint16 Index = mVBO.AddVertex(Vtx);
mIBO.AddIndex(Index);
}
}
while (CurMat != mMaterials.size())
{
mMaterialOffsets.push_back(mIBO.GetSize());
CurMat++;
}
ASSERT(mMaterialOffsets.size() == mMaterials.size());
// Buffer, and done
mVBO.Buffer();
mIBO.Buffer();
mBuffered = true;
}
void CCollisionMesh::Draw()
{
if (!mBuffered) BufferGL();
mVBO.Bind();
mIBO.DrawElements();
mVBO.Unbind();
}
void CCollisionMesh::DrawMaterial(uint32 MatIdx, bool Wireframe)
{
if (!mBuffered) BufferGL();
ASSERT(MatIdx < mMaterials.size());
mVBO.Bind();
uint32 StartIdx = (MatIdx == 0 ? 0 : mMaterialOffsets[MatIdx - 1]);
uint32 NumElements = mMaterialOffsets[MatIdx] - StartIdx;
if (Wireframe)
{
CDrawUtil::UseColorShader(CColor::skBlack);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
mIBO.DrawElements(StartIdx, NumElements);
if (Wireframe)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
mVBO.Unbind();
}
void CCollisionMesh::DrawWireframe()
{
CDrawUtil::UseColorShader(CColor::skBlack);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
Draw();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
CCollisionMesh::CCollisionVertex* CCollisionMesh::GetVertex(uint16 Index)
{
return &mCollisionVertices[Index];
}
CCollisionMesh::CCollisionLine* CCollisionMesh::GetLine(uint16 Index)
{
return &mCollisionLines[Index];
}
CCollisionMesh::CCollisionFace* CCollisionMesh::GetFace(uint16 Index)
{
return &mCollisionFaces[Index];
}

View File

@ -1,89 +0,0 @@
#ifndef CCOLLISIONMESH_H
#define CCOLLISIONMESH_H
#include "CCollisionMaterial.h"
#include "CResource.h"
#include "Core/OpenGL/CVertexBuffer.h"
#include "Core/OpenGL/CIndexBuffer.h"
#include <Common/Math/CAABox.h>
class CCollisionMesh
{
friend class CCollisionLoader;
class CCollisionOctree
{
friend class CCollisionLoader;
struct SOctreeNode {};
struct SLeaf : public SOctreeNode
{
CAABox AABox;
std::vector<uint16> FaceIndices;
};
struct SBranch : public SOctreeNode
{
uint16 Flags;
SOctreeNode *pChildren[8];
};
SOctreeNode* mpRoot;
};
class CCollisionVertex
{
public:
uint32 MaterialIdx;
CVector3f Pos;
};
class CCollisionLine
{
public:
uint32 MaterialIdx;
uint16 Vertices[2];
};
class CCollisionFace
{
public:
uint32 MaterialIdx;
uint16 Lines[3];
};
CVertexBuffer mVBO;
CIndexBuffer mIBO;
uint32 mVertexCount;
uint32 mLineCount;
uint32 mFaceCount;
bool mBuffered;
CAABox mAABox;
CCollisionOctree *mpOctree;
std::vector<CCollisionMaterial> mMaterials;
std::vector<CCollisionVertex> mCollisionVertices;
std::vector<CCollisionLine> mCollisionLines;
std::vector<CCollisionFace> mCollisionFaces;
std::vector<uint32> mMaterialOffsets;
bool mOctreeLoaded;
CCollisionVertex *GetVertex(uint16 Index);
CCollisionLine *GetLine(uint16 Index);
CCollisionFace *GetFace(uint16 Index);
public:
CCollisionMesh();
~CCollisionMesh();
void BufferGL();
void Draw();
void DrawMaterial(uint32 MatIdx, bool Wireframe);
void DrawWireframe();
inline uint32 NumMaterials() const { return mMaterials.size(); }
inline CCollisionMaterial& GetMaterial(uint32 Index) { return mMaterials[Index]; }
inline const CAABox& BoundingBox() const { return mAABox; }
};
#endif // CCOLLISIONMESH_H

View File

@ -0,0 +1,30 @@
#ifndef CCOLLIDABLEOBBTREE_H
#define CCOLLIDABLEOBBTREE_H
#include "CCollisionMesh.h"
#include "SOBBTreeNode.h"
/** A collision mesh with an OBB tree for spatial queries. Represents one mesh from a DCLN file */
class CCollidableOBBTree : public CCollisionMesh
{
friend class CCollisionLoader;
std::unique_ptr<SOBBTreeNode> mpOBBTree;
public:
virtual void BuildRenderData() override
{
if (!mRenderData.IsBuilt())
{
mRenderData.BuildRenderData(mIndexData);
mRenderData.BuildBoundingHierarchyRenderData(mpOBBTree.get());
}
}
/** Accessors */
inline SOBBTreeNode* GetOBBTree() const
{
return mpOBBTree.get();
}
};
#endif // CCOLLIDABLEOBBTREE_H

View File

@ -0,0 +1,9 @@
#include "CCollisionMesh.h"
void CCollisionMesh::BuildRenderData()
{
if (!mRenderData.IsBuilt())
{
mRenderData.BuildRenderData(mIndexData);
}
}

View File

@ -0,0 +1,44 @@
#ifndef CCOLLISIONMESH_H
#define CCOLLISIONMESH_H
#include "CCollisionMaterial.h"
#include "CCollisionRenderData.h"
#include "SCollisionIndexData.h"
#include <Common/Math/CAABox.h>
/** Base class of collision geometry */
class CCollisionMesh
{
friend class CCollisionLoader;
protected:
CAABox mAABox;
SCollisionIndexData mIndexData;
CCollisionRenderData mRenderData;
public:
virtual void BuildRenderData();
/** Accessors */
inline CAABox Bounds() const
{
return mAABox;
}
inline const SCollisionIndexData& GetIndexData() const
{
return mIndexData;
}
inline const CCollisionRenderData& GetRenderData() const
{
return mRenderData;
}
inline CCollisionRenderData& GetRenderData()
{
return mRenderData;
}
};
#endif // CCOLLISIONMESH_H

View File

@ -1,9 +1,10 @@
#ifndef CCOLLISIONMESHGROUP_H
#define CCOLLISIONMESHGROUP_H
#include "CResource.h"
#include "CCollisionMesh.h"
#include "TResPtr.h"
#include "Core/Resource/CResource.h"
#include "Core/Resource/TResPtr.h"
#include <Common/Math/CTransform4f.h>
#include <vector>
class CCollisionMeshGroup : public CResource
@ -24,16 +25,22 @@ public:
inline CCollisionMesh* MeshByIndex(uint32 Index) const { return mMeshes[Index]; }
inline void AddMesh(CCollisionMesh *pMesh) { mMeshes.push_back(pMesh); }
inline void BuildRenderData()
{
for (auto It = mMeshes.begin(); It != mMeshes.end(); It++)
(*It)->BuildRenderData();
}
inline void Draw()
{
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
(*it)->Draw();
(*it)->GetRenderData().Render(false);
}
inline void DrawWireframe()
{
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
(*it)->DrawWireframe();
(*it)->GetRenderData().Render(true);
}
};

View File

@ -0,0 +1,261 @@
#include "CCollisionRenderData.h"
#include <Core/Render/CDrawUtil.h>
/** Build from collision data */
void CCollisionRenderData::BuildRenderData(const SCollisionIndexData& kIndexData)
{
// Clear any existing data
if (mBuilt)
{
mVertexBuffer.Clear();
mIndexBuffer.Clear();
mWireframeIndexBuffer.Clear();
mMaterialIndexOffsets.clear();
mMaterialWireIndexOffsets.clear();
mBuilt = false;
}
mIndexBuffer.SetPrimitiveType(GL_TRIANGLES);
mWireframeIndexBuffer.SetPrimitiveType(GL_LINES);
// Build list of triangle indices sorted by material index
std::vector<uint16> SortedTris(kIndexData.TriangleMaterialIndices.size(), 0);
for (uint16 i=0; i<SortedTris.size(); i++)
{
SortedTris[i] = i;
}
std::sort(SortedTris.begin(), SortedTris.end(), [kIndexData](uint16 Left, uint16 Right) -> bool {
return kIndexData.TriangleMaterialIndices[Left] < kIndexData.TriangleMaterialIndices[Right];
});
mVertexBuffer.Reserve(SortedTris.size() * 3);
mIndexBuffer.Reserve(SortedTris.size() * 3);
mWireframeIndexBuffer.Reserve(SortedTris.size() * 6);
mMaterialIndexOffsets.reserve(kIndexData.Materials.size());
uint8 CurrentMatIdx = 0xFF;
for (uint i=0; i < SortedTris.size(); i++)
{
uint TriIdx = SortedTris[i];
uint8 MaterialIdx = kIndexData.TriangleMaterialIndices[TriIdx];
const CCollisionMaterial& kMaterial = kIndexData.Materials[MaterialIdx];
if (MaterialIdx != CurrentMatIdx)
{
// Note some collision materials have no geometry associated with them as
// some materials are exclusively used with edges/vertices.
ASSERT( CurrentMatIdx < MaterialIdx || CurrentMatIdx == 0xFF );
while (CurrentMatIdx != MaterialIdx)
{
mMaterialIndexOffsets.push_back( mIndexBuffer.GetSize() );
CurrentMatIdx++;
}
}
uint16 LineA = kIndexData.TriangleIndices[ (TriIdx*3)+0 ];
uint16 LineB = kIndexData.TriangleIndices[ (TriIdx*3)+1 ];
uint16 LineAVertA = kIndexData.EdgeIndices[ (LineA*2)+0 ];
uint16 LineAVertB = kIndexData.EdgeIndices[ (LineA*2)+1 ];
uint16 LineBVertA = kIndexData.EdgeIndices[ (LineB*2)+0 ];
uint16 LineBVertB = kIndexData.EdgeIndices[ (LineB*2)+1 ];
uint16 VertIdx0 = LineAVertA;
uint16 VertIdx1 = LineAVertB;
uint16 VertIdx2 = (LineBVertA != LineAVertA && LineBVertA != LineAVertB ? LineBVertA : LineBVertB);
// Reverse vertex order if material indicates tri is flipped
if (kMaterial & eCF_FlippedTri)
{
uint16 Tmp = VertIdx0;
VertIdx0 = VertIdx2;
VertIdx2 = Tmp;
}
// Generate vertex data
const CVector3f& kVert0 = kIndexData.Vertices[VertIdx0];
const CVector3f& kVert1 = kIndexData.Vertices[VertIdx1];
const CVector3f& kVert2 = kIndexData.Vertices[VertIdx2];
CVector3f V0toV1 = (kVert1 - kVert0);
CVector3f V0toV2 = (kVert2 - kVert0);
CVector3f TriNormal = V0toV1.Cross(V0toV2).Normalized();
uint16 Index0 = mVertexBuffer.Size();
uint16 Index1 = Index0 + 1;
uint16 Index2 = Index1 + 1;
CVertex Vtx;
Vtx.Normal = TriNormal;
Vtx.Position = kVert0;
mVertexBuffer.AddVertex(Vtx);
Vtx.Position = kVert1;
mVertexBuffer.AddVertex(Vtx);
Vtx.Position = kVert2;
mVertexBuffer.AddVertex(Vtx);
mIndexBuffer.AddIndex(Index0);
mIndexBuffer.AddIndex(Index1);
mIndexBuffer.AddIndex(Index2);
mWireframeIndexBuffer.AddIndex(Index0);
mWireframeIndexBuffer.AddIndex(Index1);
mWireframeIndexBuffer.AddIndex(Index1);
mWireframeIndexBuffer.AddIndex(Index2);
mWireframeIndexBuffer.AddIndex(Index2);
mWireframeIndexBuffer.AddIndex(Index0);
}
// Fill the rest of the material offsets, adding an extra index at the end
for (; CurrentMatIdx <= kIndexData.Materials.size(); CurrentMatIdx++)
{
mMaterialIndexOffsets.push_back( mIndexBuffer.GetSize() );
}
// Done
mVertexBuffer.Buffer();
mIndexBuffer.Buffer();
mWireframeIndexBuffer.Buffer();
mBuilt = true;
}
void CCollisionRenderData::BuildBoundingHierarchyRenderData(const SOBBTreeNode* pOBBTree)
{
if (mBoundingHierarchyBuilt)
{
mBoundingVertexBuffer.Clear();
mBoundingIndexBuffer.Clear();
mBoundingDepthOffsets.clear();
mBoundingHierarchyBuilt = false;
}
mBoundingIndexBuffer.SetPrimitiveType(GL_LINES);
// Iterate through the OBB tree, building a list of nodes as we go.
// We iterate through this using a breadth-first search in order to group together
// OBBs in the same depth level in the index buffer. This allows us to render a
// subset of the bounding hierarchy based on a max depth level.
std::vector<const SOBBTreeNode*> TreeNodes;
TreeNodes.push_back(pOBBTree);
uint NodeIdx = 0;
while (NodeIdx < TreeNodes.size())
{
// Keep track of the current depth level and iterate through it
mBoundingDepthOffsets.push_back(mBoundingIndexBuffer.GetSize());
uint DepthLevel = TreeNodes.size();
mBoundingVertexBuffer.Reserve(8 * (DepthLevel - NodeIdx));
mBoundingIndexBuffer.Reserve(24 * (DepthLevel - NodeIdx));
for (; NodeIdx < DepthLevel; NodeIdx++)
{
const SOBBTreeNode* pkNode = TreeNodes[NodeIdx];
// Append children
if (pkNode->NodeType == EOBBTreeNodeType::Branch)
{
const SOBBTreeBranch* pkBranch = static_cast<const SOBBTreeBranch*>(pkNode);
TreeNodes.push_back(pkBranch->pLeft.get());
TreeNodes.push_back(pkBranch->pRight.get());
}
// Create a new transform with the radii combined in as a scale matrie
CTransform4f CombinedTransform =
pkNode->Transform * CTransform4f::ScaleMatrix(pkNode->Radii);
// Transform a 1x1x1 unit cube using the transform...
static const CVector3f skUnitCubeVertices[] = {
CVector3f(-1, -1, -1),
CVector3f(-1, -1, 1),
CVector3f(-1, 1, -1),
CVector3f(-1, 1, 1),
CVector3f( 1, -1, -1),
CVector3f( 1, -1, 1),
CVector3f( 1, 1, -1),
CVector3f( 1, 1, 1)
};
for (uint i=0; i<8; i++)
{
CVector3f Transformed = CombinedTransform * skUnitCubeVertices[i];
mBoundingVertexBuffer.AddVertex( CVertex(Transformed) );
}
// Add corresponding indices
static const uint16 skUnitCubeWireIndices[24] = {
0, 1,
1, 3,
3, 2,
2, 0,
4, 5,
5, 7,
7, 6,
6, 4,
0, 4,
1, 5,
2, 6,
3, 7
};
uint FirstIndex = mBoundingIndexBuffer.GetSize();
for (uint i=0; i<24; i++)
{
mBoundingIndexBuffer.AddIndex(skUnitCubeWireIndices[i] + FirstIndex);
}
}
}
// Add an extra index at the end...
mBoundingDepthOffsets.push_back(mBoundingIndexBuffer.GetSize());
// Done
mBoundingVertexBuffer.Buffer();
mBoundingIndexBuffer.Buffer();
mBoundingHierarchyBuilt = true;
}
/** Render */
void CCollisionRenderData::Render(bool Wireframe, int MaterialIndex /*= -1*/)
{
mVertexBuffer.Bind();
//@todo get these ugly OpenGL calls outta here
if (Wireframe)
{
CDrawUtil::UseColorShader(CColor::skBlack);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
if (MaterialIndex >= 0)
{
ASSERT( MaterialIndex < mMaterialIndexOffsets.size()-1 );
uint FirstIndex = mMaterialIndexOffsets[MaterialIndex];
uint NumIndices = mMaterialIndexOffsets[MaterialIndex+1] - FirstIndex;
mIndexBuffer.DrawElements(FirstIndex, NumIndices);
}
else
{
mIndexBuffer.DrawElements();
}
if (Wireframe)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
mVertexBuffer.Unbind();
}
void CCollisionRenderData::RenderBoundingHierarchy(int MaxDepthLevel /*= -1*/)
{
mBoundingVertexBuffer.Bind();
CDrawUtil::UseColorShader(CColor::skBlue);
glLineWidth(1.f);
uint FirstIndex = mBoundingDepthOffsets[0];
uint LastIndex = (MaxDepthLevel > 0 ?
mBoundingDepthOffsets[MaxDepthLevel] :
mBoundingIndexBuffer.GetSize());
uint NumIndices = LastIndex - FirstIndex;
mBoundingIndexBuffer.DrawElements(FirstIndex, NumIndices);
mBoundingVertexBuffer.Unbind();
}

View File

@ -0,0 +1,58 @@
#ifndef CCOLLISIONRENDERDATA_H
#define CCOLLISIONRENDERDATA_H
#include "SCollisionIndexData.h"
#include "SOBBTreeNode.h"
#include "Core/OpenGL/CVertexBuffer.h"
#include "Core/OpenGL/CIndexBuffer.h"
class CCollidableOBBTree;
/** Data for rendering a collision model */
class CCollisionRenderData
{
/** Vertex/index buffer for the collision geometry */
CVertexBuffer mVertexBuffer;
CIndexBuffer mIndexBuffer;
CIndexBuffer mWireframeIndexBuffer;
/** Index buffer offset for the start of each collision material.
* This has an extra index at the end, which is the end index for the last material. */
std::vector<uint> mMaterialIndexOffsets;
std::vector<uint> mMaterialWireIndexOffsets;
/** Cached vertex/index buffer for the bounding hierarchy (octree or OBB tree) */
CVertexBuffer mBoundingVertexBuffer;
CIndexBuffer mBoundingIndexBuffer;
/** Index buffer offset for different depth levels of the bounding hierarchy.
* This allows you to i.e. render only the first (n) levels of the hierarchy. */
std::vector<uint> mBoundingDepthOffsets;
/** Whether render data has been built */
bool mBuilt;
/** Whether bounding hierarchy render data has been built */
bool mBoundingHierarchyBuilt;
public:
/** Default constructor */
CCollisionRenderData()
: mBuilt(false)
, mBoundingHierarchyBuilt(false)
{}
/** Build from collision data */
void BuildRenderData(const SCollisionIndexData& kIndexData);
void BuildBoundingHierarchyRenderData(const SOBBTreeNode* pOBBTree);
/** Render */
void Render(bool Wireframe, int MaterialIndex = -1);
void RenderBoundingHierarchy(int MaxDepthLevel = -1);
/** Accessors */
inline bool IsBuilt() const { return mBuilt; }
inline int MaxBoundingHierarchyDepth() const { return mBoundingHierarchyBuilt ? mBoundingDepthOffsets.size() - 1 : 0; }
};
#endif // CCOLLISIONRENDERDATA_H

View File

@ -0,0 +1,20 @@
#ifndef SCOLLISIONINDEXDATA_H
#define SCOLLISIONINDEXDATA_H
#include "CCollisionMaterial.h"
#include <Common/Math/CVector3f.h>
/** Common index data found in all collision file formats */
struct SCollisionIndexData
{
std::vector<CCollisionMaterial> Materials;
std::vector<uint8> VertexMaterialIndices;
std::vector<uint8> EdgeMaterialIndices;
std::vector<uint8> TriangleMaterialIndices;
std::vector<uint16> EdgeIndices;
std::vector<uint16> TriangleIndices;
std::vector<uint16> UnknownData;
std::vector<CVector3f> Vertices;
};
#endif // SCOLLISIONINDEXDATA_H

View File

@ -0,0 +1,43 @@
#ifndef SOBBTREENODE_H
#define SOBBTREENODE_H
#include <Common/BasicTypes.h>
#include <Common/Math/CTransform4f.h>
#include <Common/Math/CVector3f.h>
#include <memory>
enum class EOBBTreeNodeType : uint8
{
Branch = 0,
Leaf = 1
};
struct SOBBTreeNode
{
CTransform4f Transform;
CVector3f Radii;
EOBBTreeNodeType NodeType;
};
struct SOBBTreeBranch : public SOBBTreeNode
{
std::unique_ptr<SOBBTreeNode> pLeft;
std::unique_ptr<SOBBTreeNode> pRight;
SOBBTreeBranch() : SOBBTreeNode()
{
NodeType = EOBBTreeNodeType::Branch;
}
};
struct SOBBTreeLeaf : public SOBBTreeNode
{
std::vector<uint16> TriangleIndices;
SOBBTreeLeaf() : SOBBTreeNode()
{
NodeType = EOBBTreeNodeType::Leaf;
}
};
#endif // SOBBTREENODE_H

View File

@ -620,7 +620,8 @@ void CAreaLoader::LoadSectionDataBuffers()
void CAreaLoader::ReadCollision()
{
mpSectionMgr->ToSection(mCollisionBlockNum);
mpArea->mpCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
CCollisionMeshGroup* pAreaCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
mpArea->mpCollision = std::unique_ptr<CCollisionMeshGroup>(pAreaCollision);
}
void CAreaLoader::ReadPATH()

View File

@ -6,6 +6,7 @@ CCollisionLoader::CCollisionLoader()
{
}
#if 0
CCollisionMesh::CCollisionOctree* CCollisionLoader::ParseOctree(IInputStream& /*rSrc*/)
{
return nullptr;
@ -20,172 +21,180 @@ CCollisionMesh::CCollisionOctree::SLeaf* CCollisionLoader::ParseOctreeLeaf(IInpu
{
return nullptr;
}
#endif
void CCollisionLoader::ParseOBBNode(IInputStream& rDCLN)
SOBBTreeNode* CCollisionLoader::ParseOBBNode(IInputStream& DCLN)
{
bool b = false;
SOBBTreeNode* pOut = nullptr;
while (b == false)
CTransform4f Transform(DCLN);
CVector3f Radius(DCLN);
bool IsLeaf = DCLN.ReadBool();
if (IsLeaf)
{
rDCLN.Seek(0x3C, SEEK_CUR);
b = (rDCLN.ReadByte() == 1);
if (!b) ParseOBBNode(rDCLN);
SOBBTreeLeaf* pLeaf = new SOBBTreeLeaf;
uint NumTris = DCLN.ReadLong();
pLeaf->TriangleIndices.resize(NumTris);
for (uint i=0; i<NumTris; i++)
pLeaf->TriangleIndices[i] = DCLN.ReadShort();
pOut = pLeaf;
}
else
{
SOBBTreeBranch* pBranch = new SOBBTreeBranch;
pBranch->pLeft = std::unique_ptr<SOBBTreeNode>( ParseOBBNode(DCLN) );
pBranch->pRight = std::unique_ptr<SOBBTreeNode>( ParseOBBNode(DCLN) );
pOut = pBranch;
}
uint32 NumFaces = rDCLN.ReadLong();
rDCLN.Seek(NumFaces * 2, SEEK_CUR);
pOut->Transform = Transform;
pOut->Radii = Radius;
return pOut;
}
void CCollisionLoader::ReadPropertyFlags(IInputStream& rSrc)
void CCollisionLoader::LoadCollisionMaterial(IInputStream& Src, CCollisionMaterial& OutMaterial)
{
CCollisionMaterial Material;
uint64 RawFlags = (mVersion <= EGame::Prime ? rSrc.ReadLong() : rSrc.ReadLongLong());
Material.mRawFlags = RawFlags;
uint64 RawFlags = (mVersion <= EGame::Prime ? Src.ReadLong() : Src.ReadLongLong());
OutMaterial.mRawFlags = RawFlags;
if (mVersion <= EGame::Prime)
{
if (RawFlags & 0x00000001) Material |= eCF_Unknown;
if (RawFlags & 0x00000002) Material |= eCF_Stone;
if (RawFlags & 0x00000004) Material |= eCF_Metal;
if (RawFlags & 0x00000008) Material |= eCF_Grass;
if (RawFlags & 0x00000010) Material |= eCF_Ice;
if (RawFlags & 0x00000040) Material |= eCF_MetalGrating;
if (RawFlags & 0x00000080) Material |= eCF_Phazon;
if (RawFlags & 0x00000100) Material |= eCF_Dirt;
if (RawFlags & 0x00000200) Material |= eCF_Lava;
if (RawFlags & 0x00000800) Material |= eCF_Snow;
if (RawFlags & 0x00001000) Material |= eCF_SlowMud;
if (RawFlags & 0x00004000) Material |= eCF_Mud;
if (RawFlags & 0x00008000) Material |= eCF_Glass;
if (RawFlags & 0x00010000) Material |= eCF_Shield;
if (RawFlags & 0x00020000) Material |= eCF_Sand;
if (RawFlags & 0x00040000) Material |= eCF_ShootThru;
if (RawFlags & 0x00200000) Material |= eCF_CameraThru;
if (RawFlags & 0x00400000) Material |= eCF_Wood;
if (RawFlags & 0x00800000) Material |= eCF_Organic;
if (RawFlags & 0x02000000) Material |= eCF_FlippedTri;
if (RawFlags & 0x08000000) Material |= eCF_ScanThru;
if (RawFlags & 0x10000000) Material |= eCF_AiWalkThru;
if (RawFlags & 0x20000000) Material |= eCF_Ceiling;
if (RawFlags & 0x40000000) Material |= eCF_Wall;
if (RawFlags & 0x80000000) Material |= eCF_Floor;
if (RawFlags & 0x00000001) OutMaterial |= eCF_Unknown;
if (RawFlags & 0x00000002) OutMaterial |= eCF_Stone;
if (RawFlags & 0x00000004) OutMaterial |= eCF_Metal;
if (RawFlags & 0x00000008) OutMaterial |= eCF_Grass;
if (RawFlags & 0x00000010) OutMaterial |= eCF_Ice;
if (RawFlags & 0x00000040) OutMaterial |= eCF_MetalGrating;
if (RawFlags & 0x00000080) OutMaterial |= eCF_Phazon;
if (RawFlags & 0x00000100) OutMaterial |= eCF_Dirt;
if (RawFlags & 0x00000200) OutMaterial |= eCF_Lava;
if (RawFlags & 0x00000800) OutMaterial |= eCF_Snow;
if (RawFlags & 0x00001000) OutMaterial |= eCF_SlowMud;
if (RawFlags & 0x00004000) OutMaterial |= eCF_Mud;
if (RawFlags & 0x00008000) OutMaterial |= eCF_Glass;
if (RawFlags & 0x00010000) OutMaterial |= eCF_Shield;
if (RawFlags & 0x00020000) OutMaterial |= eCF_Sand;
if (RawFlags & 0x00040000) OutMaterial |= eCF_ShootThru;
if (RawFlags & 0x00200000) OutMaterial |= eCF_CameraThru;
if (RawFlags & 0x00400000) OutMaterial |= eCF_Wood;
if (RawFlags & 0x00800000) OutMaterial |= eCF_Organic;
if (RawFlags & 0x02000000) OutMaterial |= eCF_FlippedTri;
if (RawFlags & 0x08000000) OutMaterial |= eCF_ScanThru;
if (RawFlags & 0x10000000) OutMaterial |= eCF_AiWalkThru;
if (RawFlags & 0x20000000) OutMaterial |= eCF_Ceiling;
if (RawFlags & 0x40000000) OutMaterial |= eCF_Wall;
if (RawFlags & 0x80000000) OutMaterial |= eCF_Floor;
}
else if (mVersion <= EGame::Corruption)
{
if (RawFlags & 0x00000001) Material |= eCF_Unknown;
if (RawFlags & 0x00000002) Material |= eCF_Stone;
if (RawFlags & 0x00000004) Material |= eCF_Metal;
if (RawFlags & 0x00000008) Material |= eCF_Grass;
if (RawFlags & 0x00000010) Material |= eCF_Ice;
if (RawFlags & 0x00000040) Material |= eCF_MetalGrating;
if (RawFlags & 0x00000080) Material |= eCF_Phazon;
if (RawFlags & 0x00000100) Material |= eCF_Dirt;
if (RawFlags & 0x00000200) Material |= eCF_AltMetal;
if (RawFlags & 0x00000400) Material |= eCF_Glass;
if (RawFlags & 0x00000800) Material |= eCF_Snow;
if (RawFlags & 0x00001000) Material |= eCF_Fabric;
if (RawFlags & 0x00010000) Material |= eCF_Shield;
if (RawFlags & 0x00020000) Material |= eCF_Sand;
if (RawFlags & 0x00040000) Material |= eCF_MothSeedOrganics;
if (RawFlags & 0x00080000) Material |= eCF_Web;
if (RawFlags & 0x00100000) Material |= eCF_ShootThru;
if (RawFlags & 0x00200000) Material |= eCF_CameraThru;
if (RawFlags & 0x00400000) Material |= eCF_Wood;
if (RawFlags & 0x00800000) Material |= eCF_Organic;
if (RawFlags & 0x01000000) Material |= eCF_FlippedTri;
if (RawFlags & 0x02000000) Material |= eCF_Rubber;
if (RawFlags & 0x08000000) Material |= eCF_ScanThru;
if (RawFlags & 0x10000000) Material |= eCF_AiWalkThru;
if (RawFlags & 0x20000000) Material |= eCF_Ceiling;
if (RawFlags & 0x40000000) Material |= eCF_Wall;
if (RawFlags & 0x80000000) Material |= eCF_Floor;
if (RawFlags & 0x00000001) OutMaterial |= eCF_Unknown;
if (RawFlags & 0x00000002) OutMaterial |= eCF_Stone;
if (RawFlags & 0x00000004) OutMaterial |= eCF_Metal;
if (RawFlags & 0x00000008) OutMaterial |= eCF_Grass;
if (RawFlags & 0x00000010) OutMaterial |= eCF_Ice;
if (RawFlags & 0x00000040) OutMaterial |= eCF_MetalGrating;
if (RawFlags & 0x00000080) OutMaterial |= eCF_Phazon;
if (RawFlags & 0x00000100) OutMaterial |= eCF_Dirt;
if (RawFlags & 0x00000200) OutMaterial |= eCF_AltMetal;
if (RawFlags & 0x00000400) OutMaterial |= eCF_Glass;
if (RawFlags & 0x00000800) OutMaterial |= eCF_Snow;
if (RawFlags & 0x00001000) OutMaterial |= eCF_Fabric;
if (RawFlags & 0x00010000) OutMaterial |= eCF_Shield;
if (RawFlags & 0x00020000) OutMaterial |= eCF_Sand;
if (RawFlags & 0x00040000) OutMaterial |= eCF_MothSeedOrganics;
if (RawFlags & 0x00080000) OutMaterial |= eCF_Web;
if (RawFlags & 0x00100000) OutMaterial |= eCF_ShootThru;
if (RawFlags & 0x00200000) OutMaterial |= eCF_CameraThru;
if (RawFlags & 0x00400000) OutMaterial |= eCF_Wood;
if (RawFlags & 0x00800000) OutMaterial |= eCF_Organic;
if (RawFlags & 0x01000000) OutMaterial |= eCF_FlippedTri;
if (RawFlags & 0x02000000) OutMaterial |= eCF_Rubber;
if (RawFlags & 0x08000000) OutMaterial |= eCF_ScanThru;
if (RawFlags & 0x10000000) OutMaterial |= eCF_AiWalkThru;
if (RawFlags & 0x20000000) OutMaterial |= eCF_Ceiling;
if (RawFlags & 0x40000000) OutMaterial |= eCF_Wall;
if (RawFlags & 0x80000000) OutMaterial |= eCF_Floor;
if (RawFlags & 0x0001000000000000) Material |= eCF_AiBlock;
if (RawFlags & 0x0400000000000000) Material |= eCF_JumpNotAllowed;
if (RawFlags & 0x0001000000000000) OutMaterial |= eCF_AiBlock;
if (RawFlags & 0x0400000000000000) OutMaterial |= eCF_JumpNotAllowed;
}
else if (mVersion == EGame::DKCReturns)
{
if (RawFlags & 0x10000000) Material |= eCF_FlippedTri;
if (RawFlags & 0x10000000) OutMaterial |= eCF_FlippedTri;
}
}
mpMesh->mMaterials.push_back(Material);
void CCollisionLoader::LoadCollisionIndices(IInputStream& File, SCollisionIndexData& OutData)
{
// Materials
uint NumMaterials = File.ReadLong();
OutData.Materials.resize( NumMaterials );
for (uint i=0; i<NumMaterials; i++)
{
LoadCollisionMaterial(File, OutData.Materials[i]);
}
void CCollisionLoader::LoadCollisionIndices(IInputStream &rFile, bool BuildAABox)
// Property indices for vertices/edges/triangles
uint VertexMaterialCount = File.ReadLong();
OutData.VertexMaterialIndices.resize(VertexMaterialCount);
File.ReadBytes(OutData.VertexMaterialIndices.data(), VertexMaterialCount);
uint32 EdgeMaterialCount = File.ReadLong();
OutData.EdgeMaterialIndices.resize(EdgeMaterialCount);
File.ReadBytes(OutData.EdgeMaterialIndices.data(), EdgeMaterialCount);
uint32 TriMaterialCount = File.ReadLong();
OutData.TriangleMaterialIndices.resize(TriMaterialCount);
File.ReadBytes(OutData.TriangleMaterialIndices.data(), TriMaterialCount);
// Edges
uint NumEdges = File.ReadLong();
OutData.EdgeIndices.resize( NumEdges * 2 );
for (uint i=0; i<OutData.EdgeIndices.size(); i++)
{
// Properties
uint32 PropSetCount = rFile.ReadLong();
for (uint32 iProp = 0; iProp < PropSetCount; iProp++)
ReadPropertyFlags(rFile);
// Property indices for vertices/lines/faces
uint32 VtxIndexCount = rFile.ReadLong();
std::vector<uint8> VtxIndices(VtxIndexCount);
rFile.ReadBytes(VtxIndices.data(), VtxIndices.size());
uint32 LineIndexCount = rFile.ReadLong();
std::vector<uint8> LineIndices(LineIndexCount);
rFile.ReadBytes(LineIndices.data(), LineIndices.size());
uint32 FaceIndexCount = rFile.ReadLong();
std::vector<uint8> FaceIndices(FaceIndexCount);
rFile.ReadBytes(FaceIndices.data(), FaceIndices.size());
// Lines
mpMesh->mLineCount = rFile.ReadLong();
mpMesh->mCollisionLines.resize(mpMesh->mLineCount);
for (uint32 iLine = 0; iLine < mpMesh->mLineCount; iLine++)
{
CCollisionMesh::CCollisionLine *pLine = &mpMesh->mCollisionLines[iLine];
pLine->Vertices[0] = rFile.ReadShort();
pLine->Vertices[1] = rFile.ReadShort();
pLine->MaterialIdx = LineIndices[iLine];
OutData.EdgeIndices[i] = File.ReadShort();
}
// Faces
mpMesh->mFaceCount = rFile.ReadLong() / 3; // Not sure why they store it this way. It's inconsistent.
mpMesh->mCollisionFaces.resize(mpMesh->mFaceCount);
// Triangles
uint NumTris = File.ReadLong();
OutData.TriangleIndices.resize( NumTris );
for (uint32 iFace = 0; iFace < mpMesh->mFaceCount; iFace++)
for (uint i=0; i<NumTris; i++)
{
CCollisionMesh::CCollisionFace *pFace = &mpMesh->mCollisionFaces[iFace];
pFace->Lines[0] = rFile.ReadShort();
pFace->Lines[1] = rFile.ReadShort();
pFace->Lines[2] = rFile.ReadShort();
pFace->MaterialIdx = FaceIndices[iFace];
OutData.TriangleIndices[i] = File.ReadShort();
}
// Echoes introduces a new data chunk; don't know what it is yet, skipping for now
if (mVersion >= EGame::Echoes)
{
uint32 UnknownCount = rFile.ReadLong();
rFile.Seek(UnknownCount * 2, SEEK_CUR);
uint UnknownCount = File.ReadLong();
File.Skip(UnknownCount * 2);
}
// Vertices
mpMesh->mVertexCount = rFile.ReadLong();
mpMesh->mCollisionVertices.resize(mpMesh->mVertexCount);
CAABox Bounds;
uint NumVertices = File.ReadLong();
OutData.Vertices.resize(NumVertices);
for (uint32 iVtx = 0; iVtx < mpMesh->mVertexCount; iVtx++)
for (uint32 i=0; i<NumVertices; i++)
{
CCollisionMesh::CCollisionVertex *pVtx = &mpMesh->mCollisionVertices[iVtx];
pVtx->Pos = CVector3f(rFile);
pVtx->MaterialIdx = VtxIndices[iVtx];
if (BuildAABox) Bounds.ExpandBounds(pVtx->Pos);
OutData.Vertices[i].Read(File);
}
if (BuildAABox) mpMesh->mAABox = Bounds;
}
// ************ STATIC ************
CCollisionMeshGroup* CCollisionLoader::LoadAreaCollision(IInputStream& rMREA)
{
if (!rMREA.IsValid()) return nullptr;
CCollisionLoader loader;
rMREA.Skip(0x8); // Skipping unknown value + collion section size
rMREA.Seek(0x8, SEEK_CUR);
// Validate magic
uint32 DeafBabe = rMREA.ReadLong();
if (DeafBabe != 0xDEAFBABE)
{
@ -193,22 +202,22 @@ CCollisionMeshGroup* CCollisionLoader::LoadAreaCollision(IInputStream& rMREA)
return nullptr;
}
loader.mVersion = GetFormatVersion(rMREA.ReadLong());
loader.mpGroup = new CCollisionMeshGroup;
loader.mpMesh = new CCollisionMesh;
CCollisionLoader Loader;
Loader.mVersion = GetFormatVersion(rMREA.ReadLong());
Loader.mpMesh = new CCollisionMesh;
// Octree - structure is known, but not coding this right now
loader.mpMesh->mAABox = CAABox(rMREA);
rMREA.Seek(0x4, SEEK_CUR);
Loader.mpMesh->mAABox = CAABox(rMREA);
rMREA.Skip(0x4);
uint32 OctreeSize = rMREA.ReadLong();
rMREA.Seek(OctreeSize, SEEK_CUR); // Skipping the octree for now
loader.mpMesh->mOctreeLoaded = false;
rMREA.Skip(OctreeSize); // Skipping the octree for now
// Read collision indices and return
loader.LoadCollisionIndices(rMREA, false);
loader.mpGroup->AddMesh(loader.mpMesh);
return loader.mpGroup;
Loader.LoadCollisionIndices(rMREA, Loader.mpMesh->mIndexData);
CCollisionMeshGroup* pOut = new CCollisionMeshGroup();
pOut->AddMesh(Loader.mpMesh);
return pOut;
}
CCollisionMeshGroup* CCollisionLoader::LoadDCLN(IInputStream& rDCLN, CResourceEntry *pEntry)
@ -220,32 +229,45 @@ CCollisionMeshGroup* CCollisionLoader::LoadDCLN(IInputStream& rDCLN, CResourceEn
uint32 NumMeshes = rDCLN.ReadLong();
for (uint32 iMesh = 0; iMesh < NumMeshes; iMesh++)
for (uint32 MeshIdx = 0; MeshIdx < NumMeshes; MeshIdx++)
{
uint32 DeafBabe = rDCLN.ReadLong();
if (DeafBabe != 0xDEAFBABE)
{
errorf("%s [0x%X]: Invalid collision magic: 0x%08X", *rDCLN.GetSourceString(), rDCLN.Tell() - 4, DeafBabe);
Loader.mpGroup.Delete();
delete Loader.mpGroup;
return nullptr;
}
Loader.mVersion = GetFormatVersion(rDCLN.ReadLong());
Loader.mpMesh = new CCollisionMesh;
Loader.mpMesh->mOctreeLoaded = false;
Loader.mpMesh = new CCollidableOBBTree;
if (Loader.mVersion == EGame::DKCReturns)
Loader.mpMesh->mAABox = CAABox(rDCLN);
// Read indices and return
rDCLN.Seek(0x4, SEEK_CUR);
Loader.LoadCollisionIndices(rDCLN, Loader.mVersion != EGame::DKCReturns);
Loader.LoadCollisionIndices(rDCLN, Loader.mpMesh->mIndexData);
Loader.mpGroup->AddMesh(Loader.mpMesh);
// Build bounding box
if (Loader.mVersion != EGame::DKCReturns)
{
Loader.mpMesh->mAABox = CAABox::skInfinite;
for (uint i=0; i<Loader.mpMesh->mIndexData.Vertices.size(); i++)
{
Loader.mpMesh->mAABox.ExpandBounds(
Loader.mpMesh->mIndexData.Vertices[i]
);
}
}
// Parse OBB tree
Loader.ParseOBBNode(rDCLN);
CCollidableOBBTree* pOBBTree = static_cast<CCollidableOBBTree*>(Loader.mpMesh);
pOBBTree->mpOBBTree = std::unique_ptr<SOBBTreeNode>( Loader.ParseOBBNode(rDCLN) );
}
return Loader.mpGroup;
}

View File

@ -1,8 +1,9 @@
#ifndef CCOLLISIONLOADER_H
#define CCOLLISIONLOADER_H
#include "Core/Resource/CCollisionMesh.h"
#include "Core/Resource/CCollisionMeshGroup.h"
#include "Core/Resource/Collision/CCollisionMesh.h"
#include "Core/Resource/Collision/CCollisionMeshGroup.h"
#include "Core/Resource/Collision/CCollidableOBBTree.h"
#include <Common/EGame.h>
class CCollisionLoader
@ -12,12 +13,16 @@ class CCollisionLoader
EGame mVersion;
CCollisionLoader();
#if 0
CCollisionMesh::CCollisionOctree* ParseOctree(IInputStream& rSrc);
CCollisionMesh::CCollisionOctree::SBranch* ParseOctreeBranch(IInputStream& rSrc);
CCollisionMesh::CCollisionOctree::SLeaf* ParseOctreeLeaf(IInputStream& rSrc);
void ParseOBBNode(IInputStream& rDCLN);
void ReadPropertyFlags(IInputStream& rSrc);
void LoadCollisionIndices(IInputStream& rFile, bool BuildAABox);
#endif
SOBBTreeNode* ParseOBBNode(IInputStream& DCLN);
void LoadCollisionMaterial(IInputStream& Src, CCollisionMaterial& OutMaterial);
void LoadCollisionIndices(IInputStream& File, SCollisionIndexData& OutData);
public:
static CCollisionMeshGroup* LoadAreaCollision(IInputStream& rMREA);

View File

@ -1,7 +1,6 @@
#ifndef RESOURCES_H
#define RESOURCES_H
#include "CCollisionMeshGroup.h"
#include "CDependencyGroup.h"
#include "CFont.h"
#include "CPoiToWorld.h"
@ -13,6 +12,7 @@
#include "Core/Resource/Animation/CSkeleton.h"
#include "Core/Resource/Animation/CSkin.h"
#include "Core/Resource/Area/CGameArea.h"
#include "Core/Resource/Collision/CCollisionMeshGroup.h"
#include "Core/Resource/Model/CModel.h"
#include "Core/Resource/Scan/CScan.h"
#include "Core/Resource/StringTable/CStringTable.h"

View File

@ -3,8 +3,8 @@
#include "CScriptTemplate.h"
#include "Core/Resource/Area/CGameArea.h"
#include "Core/Resource/Collision/CCollisionMeshGroup.h"
#include "Core/Resource/Model/CModel.h"
#include "Core/Resource/CCollisionMeshGroup.h"
#include "Core/Resource/Script/Property/Properties.h"
class CScriptLayer;

View File

@ -4,7 +4,7 @@
#include "Core/Resource/Script/Property/Properties.h"
#include "EVolumeShape.h"
#include "Core/Resource/Model/CModel.h"
#include "Core/Resource/CCollisionMeshGroup.h"
#include "Core/Resource/Collision/CCollisionMeshGroup.h"
#include <Common/BasicTypes.h>
#include <Common/CFourCC.h>
#include <list>

View File

@ -39,7 +39,7 @@ void CCollisionNode::Draw(FRenderOptions /*Options*/, int /*ComponentIndex*/, ER
glDepthMask(GL_TRUE);
// Turn off backface culling
EGame Game = mpScene->ActiveArea()->Game();
EGame Game = mpCollision->Game();
bool ForceDisableBackfaceCull = (rkViewInfo.CollisionSettings.DrawBackfaces || Game == EGame::DKCReturns) && glIsEnabled(GL_CULL_FACE);
if (ForceDisableBackfaceCull)
@ -47,35 +47,48 @@ void CCollisionNode::Draw(FRenderOptions /*Options*/, int /*ComponentIndex*/, ER
CColor BaseTint = TintColor(rkViewInfo);
for (uint32 iMesh = 0; iMesh < mpCollision->NumMeshes(); iMesh++)
for (uint32 MeshIdx = 0; MeshIdx < mpCollision->NumMeshes(); MeshIdx++)
{
CCollisionMesh *pMesh = mpCollision->MeshByIndex(iMesh);
CCollisionMesh *pMesh = mpCollision->MeshByIndex(MeshIdx);
CCollisionRenderData& RenderData = pMesh->GetRenderData();
const SCollisionIndexData& kIndexData = pMesh->GetIndexData();
for (uint32 iMat = 0; iMat < pMesh->NumMaterials(); iMat++)
for (int MatIdx = 0; MatIdx < (int) kIndexData.Materials.size(); MatIdx++)
{
CCollisionMaterial& rMat = pMesh->GetMaterial(iMat);
const CCollisionMaterial& kMat = kIndexData.Materials[MatIdx];
if (rkViewInfo.CollisionSettings.HideMaterial & rMat)
if (rkViewInfo.CollisionSettings.HideMaterial & kMat)
continue;
if (rkViewInfo.CollisionSettings.HideMask != 0 && (rMat.RawFlags() & rkViewInfo.CollisionSettings.HideMask) != 0)
if (rkViewInfo.CollisionSettings.HideMask != 0 && (kMat.RawFlags() & rkViewInfo.CollisionSettings.HideMask) != 0)
continue;
CColor Tint = BaseTint;
if (rkViewInfo.CollisionSettings.HighlightMask != 0 && (rMat.RawFlags() & rkViewInfo.CollisionSettings.HighlightMask) == rkViewInfo.CollisionSettings.HighlightMask)
if (rkViewInfo.CollisionSettings.HighlightMask != 0 && (kMat.RawFlags() & rkViewInfo.CollisionSettings.HighlightMask) == rkViewInfo.CollisionSettings.HighlightMask)
Tint *= CColor::skRed;
else if (Game != EGame::DKCReturns && rkViewInfo.CollisionSettings.TintWithSurfaceColor)
Tint *= rMat.SurfaceColor(Game);
Tint *= kMat.SurfaceColor(Game);
bool IsFloor = (rkViewInfo.CollisionSettings.TintUnwalkableTris ? rMat.IsFloor() : true) || Game == EGame::DKCReturns;
bool IsUnstandable = (rkViewInfo.CollisionSettings.TintUnwalkableTris ? rMat.IsUnstandable(Game) : false) && Game != EGame::DKCReturns;
bool IsFloor = (rkViewInfo.CollisionSettings.TintUnwalkableTris ? kMat.IsFloor() : true) || Game == EGame::DKCReturns;
bool IsUnstandable = (rkViewInfo.CollisionSettings.TintUnwalkableTris ? kMat.IsUnstandable(Game) : false) && Game != EGame::DKCReturns;
CDrawUtil::UseCollisionShader(IsFloor, IsUnstandable, Tint);
pMesh->DrawMaterial(iMat, false);
RenderData.Render(false, MatIdx);
if (rkViewInfo.CollisionSettings.DrawWireframe)
pMesh->DrawMaterial(iMat, true);
RenderData.Render(true, MatIdx);
}
}
// Render bounding hierarchy
if (rkViewInfo.CollisionSettings.DrawBoundingHierarchy)
{
int Depth = rkViewInfo.CollisionSettings.BoundingHierarchyRenderDepth;
for (uint MeshIdx = 0; MeshIdx < mpCollision->NumMeshes(); MeshIdx++)
{
mpCollision->MeshByIndex(MeshIdx)->GetRenderData().RenderBoundingHierarchy(Depth);
}
}
@ -86,8 +99,13 @@ void CCollisionNode::Draw(FRenderOptions /*Options*/, int /*ComponentIndex*/, ER
// Draw collision bounds for area collision
// note: right now checking parent is the best way to check whether this node is area collision instead of actor collision
// actor collision will have a script node parent whereas area collision will have a root node parent
if (rkViewInfo.CollisionSettings.DrawAreaCollisionBounds && Parent()->NodeType() == ENodeType::Root && Game != EGame::DKCReturns)
CDrawUtil::DrawWireCube( mpCollision->MeshByIndex(0)->BoundingBox(), CColor::skRed );
if (rkViewInfo.CollisionSettings.DrawAreaCollisionBounds)
{
if (Parent() && Parent()->NodeType() == ENodeType::Root && Game != EGame::DKCReturns)
{
CDrawUtil::DrawWireCube( mpCollision->MeshByIndex(0)->Bounds(), CColor::skRed );
}
}
}
void CCollisionNode::RayAABoxIntersectTest(CRayCollisionTester& /*rTester*/, const SViewInfo& /*rkViewInfo*/)
@ -106,4 +124,9 @@ SRayIntersection CCollisionNode::RayNodeIntersectTest(const CRay& /*rkRay*/, uin
void CCollisionNode::SetCollision(CCollisionMeshGroup *pCollision)
{
mpCollision = pCollision;
if (mpCollision)
{
mpCollision->BuildRenderData();
}
}

View File

@ -2,7 +2,7 @@
#define CCOLLISIONNODE_H
#include "CSceneNode.h"
#include "Core/Resource/CCollisionMeshGroup.h"
#include "Core/Resource/Collision/CCollisionMeshGroup.h"
class CCollisionNode : public CSceneNode
{

View File

@ -3,12 +3,15 @@
#include "CBasicViewport.h"
#include "CProgressDialog.h"
#include "CProjectSettingsDialog.h"
#include "Editor/CharacterEditor/CCharacterEditor.h"
#include "Editor/CollisionEditor/CCollisionEditor.h"
#include "Editor/ModelEditor/CModelEditorWindow.h"
#include "Editor/ScanEditor/CScanEditor.h"
#include "Editor/StringEditor/CStringEditor.h"
#include "Editor/ResourceBrowser/CResourceBrowser.h"
#include "Editor/WorldEditor/CWorldEditor.h"
#include <Common/Macros.h>
#include <Common/CTimer.h>
#include <Core/GameProject/CGameProject.h>
@ -176,6 +179,12 @@ void CEditorApplication::EditResource(CResourceEntry *pEntry)
break;
}
case EResourceType::DynamicCollision:
{
pEd = new CCollisionEditor((CCollisionMeshGroup*) pRes, mpWorldEditor);
break;
}
}
if (pEd)

View File

@ -0,0 +1,28 @@
#include "CCollisionEditor.h"
#include "ui_CCollisionEditor.h"
CCollisionEditor::CCollisionEditor(CCollisionMeshGroup* pCollisionMesh, QWidget* pParent /*= 0*/)
: IEditor(pParent)
, mpUI(new Ui::CCollisionEditor)
{
mpUI = std::make_unique<Ui::CCollisionEditor>();
mpUI->setupUi(this);
mpCollisionMesh = pCollisionMesh;
mpScene = std::make_unique<CScene>();
mpCollisionNode = std::make_unique<CCollisionNode>(mpScene.get(), -1);
mpCollisionNode->SetCollision(mpCollisionMesh);
mpUI->Viewport->SetNode(mpCollisionNode.get());
CCamera& rCamera = mpUI->Viewport->Camera();
rCamera.SetMoveSpeed(0.5f);
rCamera.SetPitch(-0.3f);
// rCamera.SetMoveMode(ECameraMoveMode::Orbit);
// rCamera.SetOrbit(
}
CCollisionEditorViewport* CCollisionEditor::Viewport() const
{
return mpUI->Viewport;
}

View File

@ -0,0 +1,34 @@
#ifndef CCOLLISIONEDITOR_H
#define CCOLLISIONEDITOR_H
#include "Editor/IEditor.h"
#include "CCollisionEditorViewport.h"
#include <Core/Scene/CCollisionNode.h>
#include <Core/Scene/CScene.h>
namespace Ui {
class CCollisionEditor;
}
/** Editor window for dynamic collision (DCLN assets) */
class CCollisionEditor : public IEditor
{
Q_OBJECT
/** Qt UI */
std::unique_ptr<Ui::CCollisionEditor> mpUI;
/** Collision mesh being edited */
TResPtr<CCollisionMeshGroup> mpCollisionMesh;
/** Scene data */
std::unique_ptr<CScene> mpScene;
std::unique_ptr<CCollisionNode> mpCollisionNode;
public:
/** Constructor/destructor */
explicit CCollisionEditor(CCollisionMeshGroup* pCollisionMesh, QWidget* pParent = 0);
virtual CCollisionEditorViewport* Viewport() const override;
};
#endif // CCOLLISIONEDITOR_H

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CCollisionEditor</class>
<widget class="QMainWindow" name="CCollisionEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="CCollisionEditorViewport" name="Viewport" native="true"/>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<customwidgets>
<customwidget>
<class>CCollisionEditorViewport</class>
<extends>QWidget</extends>
<header>Editor/CollisionEditor/CCollisionEditorViewport.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,44 @@
#include "CCollisionEditorViewport.h"
/** Constructor */
CCollisionEditorViewport::CCollisionEditorViewport(QWidget* pParent /*= 0*/)
: CBasicViewport(pParent)
{
mpRenderer = std::make_unique<CRenderer>();
mpRenderer->SetViewportSize(width(), height());
mpRenderer->SetClearColor(CColor(0.3f, 0.3f, 0.3f));
mpRenderer->ToggleGrid(true);
mViewInfo.ShowFlags = EShowFlag::WorldCollision | EShowFlag::ObjectCollision;
mViewInfo.pRenderer = mpRenderer.get();
mViewInfo.pScene = nullptr;
mViewInfo.GameMode = false;
mViewInfo.CollisionSettings.DrawBoundingHierarchy = true;
}
/** Update the collision node to render in the scene */
void CCollisionEditorViewport::SetNode(CCollisionNode* pNode)
{
mpCollisionNode = pNode;
}
/** CBasicViewport interface */
void CCollisionEditorViewport::Paint()
{
mpRenderer->BeginFrame();
mCamera.LoadMatrices();
//mGrid.AddToRenderer(mpRenderer.get(), mViewInfo);
if (mpCollisionNode)
{
mpCollisionNode->AddToRenderer(mpRenderer.get(), mViewInfo);
}
mpRenderer->RenderBuckets(mViewInfo);
mpRenderer->EndFrame();
}
void CCollisionEditorViewport::OnResize()
{
mpRenderer->SetViewportSize(width(), height());
}

View File

@ -0,0 +1,30 @@
#ifndef CCOLLISIONEDITORVIEWPORT_H
#define CCOLLISIONEDITORVIEWPORT_H
#include "Editor/CBasicViewport.h"
#include "Editor/CGridRenderable.h"
#include <Core/Scene/CCollisionNode.h>
#include <memory>
/** Preview viewport for the collision editor */
class CCollisionEditorViewport : public CBasicViewport
{
Q_OBJECT
std::unique_ptr<CRenderer> mpRenderer;
CCollisionNode* mpCollisionNode;
CGridRenderable mGrid;
public:
/** Constructor */
CCollisionEditorViewport(QWidget* pParent = 0);
/** Update the collision node to render in the scene */
void SetNode(CCollisionNode* pNode);
/** CBasicViewport interface */
virtual void Paint() override;
virtual void OnResize() override;
};
#endif // CCOLLISIONEDITORVIEWPORT_H

View File

@ -204,7 +204,9 @@ HEADERS += \
StringEditor/CStringMimeData.h \
ScanEditor/CScanEditor.h \
Undo/ICreateDeleteResourceCommand.h \
Undo/CSaveStoreCommand.h
Undo/CSaveStoreCommand.h \
CollisionEditor/CCollisionEditor.h \
CollisionEditor/CCollisionEditorViewport.h
# Source Files
SOURCES += \
@ -280,7 +282,9 @@ SOURCES += \
IEditor.cpp \
StringEditor/CStringDelegate.cpp \
CTweakEditor.cpp \
ScanEditor/CScanEditor.cpp
ScanEditor/CScanEditor.cpp \
CollisionEditor/CCollisionEditor.cpp \
CollisionEditor/CCollisionEditorViewport.cpp
# UI Files
FORMS += \
@ -308,7 +312,8 @@ FORMS += \
CGeneratePropertyNamesDialog.ui \
StringEditor/CStringEditor.ui \
CTweakEditor.ui \
ScanEditor/CScanEditor.ui
ScanEditor/CScanEditor.ui \
CollisionEditor/CCollisionEditor.ui
# Codegen
CODEGEN_DIR = $$EXTERNALS_DIR/CodeGen