2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-12-08 21:07:42 +00:00

Octree generation fixes

This commit is contained in:
Jack Andersen
2017-12-01 19:50:05 -10:00
parent 7cd3e8f502
commit 357b001cac
14 changed files with 518 additions and 216 deletions

View File

@@ -4,9 +4,110 @@ namespace DataSpec
{
logvisor::Module Log("AROTBuilder");
#define AROT_MAX_LEVEL 7
#define AROT_MAX_LEVEL 6
#define COLLISION_MIN_NODE_TRIANGLES 16
static const uint32_t AROTChildCounts[] = { 0, 2, 2, 4, 2, 4, 4, 8 };
static zeus::CAABox SplitAABB(const zeus::CAABox& aabb, int i)
{
zeus::CAABox pos, neg;
aabb.splitZ(neg, pos);
if (i & 4)
{
zeus::CAABox(pos).splitY(neg, pos);
if (i & 2)
{
zeus::CAABox(pos).splitX(neg, pos);
if (i & 1)
return pos;
else
return neg;
}
else
{
zeus::CAABox(neg).splitX(neg, pos);
if (i & 1)
return pos;
else
return neg;
}
}
else
{
zeus::CAABox(neg).splitY(neg, pos);
if (i & 2)
{
zeus::CAABox(pos).splitX(neg, pos);
if (i & 1)
return pos;
else
return neg;
}
else
{
zeus::CAABox(neg).splitX(neg, pos);
if (i & 1)
return pos;
else
return neg;
}
}
}
void AROTBuilder::Node::addChild(int level, int minChildren, const std::vector<zeus::CAABox>& triBoxes,
const zeus::CAABox& curAABB, BspNodeType& typeOut)
{
/* Gather intersecting faces */
for (int i=0 ; i<triBoxes.size() ; ++i)
if (triBoxes[i].intersects(curAABB))
childIndices.insert(i);
/* Return early if empty, triangle intersection below performance threshold, or at max level */
if (childIndices.empty())
{
typeOut = BspNodeType::Invalid;
return;
}
else if (childIndices.size() < minChildren || level == AROT_MAX_LEVEL)
{
typeOut = BspNodeType::Leaf;
return;
}
/* Subdivide */
typeOut = BspNodeType::Branch;
childNodes.resize(8);
for (int i=0 ; i<8 ; ++i)
{
BspNodeType chType;
childNodes[i].addChild(level + 1, minChildren, triBoxes, SplitAABB(curAABB, i), chType);
flags |= int(chType) << (i * 2);
}
/* Unsubdivide */
compSubdivs = 0;
if (childNodes[0].childIndices != childNodes[1].childIndices ||
childNodes[4].childIndices != childNodes[5].childIndices ||
childNodes[2].childIndices != childNodes[3].childIndices ||
childNodes[6].childIndices != childNodes[7].childIndices)
compSubdivs |= 0x4;
if (childNodes[0].childIndices != childNodes[2].childIndices ||
childNodes[1].childIndices != childNodes[3].childIndices ||
childNodes[4].childIndices != childNodes[6].childIndices ||
childNodes[5].childIndices != childNodes[7].childIndices)
compSubdivs |= 0x2;
if (childNodes[0].childIndices != childNodes[4].childIndices ||
childNodes[1].childIndices != childNodes[5].childIndices ||
childNodes[2].childIndices != childNodes[6].childIndices ||
childNodes[3].childIndices != childNodes[7].childIndices)
compSubdivs |= 0x1;
if (!compSubdivs)
{
typeOut = BspNodeType::Leaf;
childNodes = std::vector<Node>();
flags = 0;
}
}
size_t AROTBuilder::BitmapPool::addIndices(const std::set<int>& indices)
{
@@ -17,95 +118,49 @@ size_t AROTBuilder::BitmapPool::addIndices(const std::set<int>& indices)
return m_pool.size() - 1;
}
bool AROTBuilder::Node::addChild(int level, const zeus::CAABox& curAabb, const zeus::CAABox& childAabb, int idx)
{
if (childAabb.intersects(curAabb))
{
childIndices.insert(idx);
if (!curAabb.inside(childAabb) && level < AROT_MAX_LEVEL)
{
childNodes.resize(8);
zeus::CAABox X[2];
curAabb.splitX(X[0], X[1]);
bool inX[2] = {};
for (int i=0 ; i<2 ; ++i)
{
zeus::CAABox Y[2];
X[i].splitY(Y[0], Y[1]);
bool inY[2] = {};
for (int j=0 ; j<2 ; ++j)
{
zeus::CAABox Z[2];
Y[j].splitZ(Z[0], Z[1]);
bool inZ[2] = {};
inZ[0] = childNodes[i*4 + j*2].addChild(level + 1, Z[0], childAabb, idx);
inZ[1] = childNodes[i*4 + j*2 + 1].addChild(level + 1, Z[1], childAabb, idx);
if (inZ[0] ^ inZ[1])
flags |= 0x4;
if (inZ[0] | inZ[1])
inY[j] = true;
}
if (inY[0] ^ inY[1])
flags |= 0x2;
if (inY[0] | inY[1])
inX[i] = true;
}
if (inX[0] ^ inX[1])
flags |= 0x1;
if (!flags)
childNodes.clear();
}
return true;
}
return false;
}
static const uint32_t AROTChildCounts[] = { 0, 2, 2, 4, 2, 4, 4, 8 };
void AROTBuilder::Node::nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff)
{
if (childIndices.size())
{
sz += 1;
poolIdx = bmpPool.addIndices(childIndices);
if (poolIdx > 65535)
Log.report(logvisor::Fatal, "AROT bitmap exceeds 16-bit node addressing; area too complex");
sz += 1;
poolIdx = bmpPool.addIndices(childIndices);
if (poolIdx > 65535)
Log.report(logvisor::Fatal, "AROT bitmap exceeds 16-bit node addressing; area too complex");
uint32_t childCount = AROTChildCounts[flags];
nodeOff = curOff;
nodeSz = childCount * 2 + 4;
curOff += nodeSz;
if (childNodes.size())
uint32_t childCount = AROTChildCounts[compSubdivs];
nodeOff = curOff;
nodeSz = childCount * 2 + 4;
curOff += nodeSz;
if (childNodes.size())
{
for (int k=0 ; k < 1 + ((compSubdivs & 0x1) != 0) ; ++k)
{
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i)
for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k)
{
childNodes[i*4 + j*2 + k].nodeCount(sz, idxRefs, bmpPool, curOff);
}
int idx = k*4 + j*2 + i;
childNodes[idx].nodeCount(sz, idxRefs, bmpPool, curOff);
}
}
idxRefs += childCount;
}
idxRefs += childCount;
}
}
void AROTBuilder::Node::writeIndirectionTable(athena::io::MemoryWriter& w)
{
if (childIndices.size())
w.writeUint32Big(nodeOff);
if (childNodes.size())
{
w.writeUint32Big(nodeOff);
if (childNodes.size())
for (int k=0 ; k < 1 + ((compSubdivs & 0x1) != 0) ; ++k)
{
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i)
for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k)
{
childNodes[i*4 + j*2 + k].writeIndirectionTable(w);
}
int idx = k*4 + j*2 + i;
childNodes[idx].writeIndirectionTable(w);
}
}
}
@@ -114,41 +169,39 @@ void AROTBuilder::Node::writeIndirectionTable(athena::io::MemoryWriter& w)
void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx)
{
if (childIndices.size())
w.writeUint16Big(poolIdx);
w.writeUint16Big(compSubdivs);
if (childNodes.size())
{
w.writeUint16Big(poolIdx);
w.writeUint16Big(flags);
if (childNodes.size())
int curIdx = nodeIdx + 1;
if (curIdx > 65535)
Log.report(logvisor::Fatal, "AROT node exceeds 16-bit node addressing; area too complex");
int childIndices[8];
for (int k=0 ; k < 1 + ((compSubdivs & 0x1) != 0) ; ++k)
{
int curIdx = nodeIdx + 1;
if (curIdx > 65535)
Log.report(logvisor::Fatal, "AROT node exceeds 16-bit node addressing; area too complex");
int childIndices[8];
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i)
for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k)
{
int ch = i*4 + j*2 + k;
w.writeUint16Big(curIdx);
childIndices[ch] = curIdx;
childNodes[ch].advanceIndex(curIdx);
}
int idx = k*4 + j*2 + i;
w.writeUint16Big(curIdx);
childIndices[idx] = curIdx;
childNodes[idx].advanceIndex(curIdx);
}
}
}
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i)
for (int k=0 ; k < 1 + ((compSubdivs & 0x1) != 0) ; ++k)
{
for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k)
{
int ch = i*4 + j*2 + k;
childNodes[ch].writeNodes(w, childIndices[ch]);
}
int idx = k*4 + j*2 + i;
childNodes[idx].writeNodes(w, childIndices[idx]);
}
}
}
@@ -157,19 +210,17 @@ void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx)
void AROTBuilder::Node::advanceIndex(int& nodeIdx)
{
if (childIndices.size())
++nodeIdx;
if (childNodes.size())
{
++nodeIdx;
if (childNodes.size())
for (int k=0 ; k < 1 + ((compSubdivs & 0x1) != 0) ; ++k)
{
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i)
for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k)
{
childNodes[i*4 + j*2 + k].advanceIndex(nodeIdx);
}
int idx = k*4 + j*2 + i;
childNodes[idx].advanceIndex(nodeIdx);
}
}
}
@@ -188,16 +239,8 @@ void AROTBuilder::Node::colSize(size_t& totalSz)
else
{
totalSz += 36;
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i)
{
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
{
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k)
{
childNodes[i*4 + j*2 + k].colSize(totalSz);
}
}
}
for (int i=0 ; i<8 ; ++i)
childNodes[i].colSize(totalSz);
}
}
}
@@ -226,90 +269,31 @@ void AROTBuilder::Node::writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB
uint16_t* pflags = reinterpret_cast<uint16_t*>(ptr);
uint32_t* offsets = reinterpret_cast<uint32_t*>(ptr + 4);
memset(pflags, 0, sizeof(uint32_t) * 9);
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i)
for (int i=0 ; i<8 ; ++i)
{
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
{
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k)
{
int idx = i*4 + j*2 + k;
uint32_t thisOffset;
uint16_t thisFlags = childNodes[idx].getColRef(thisOffset);
if (thisFlags)
{
*pflags |= thisFlags << (idx * 2);
offsets[idx] = hecl::SBig(uint32_t(thisOffset - nodeOff - 36));
}
}
}
const Node& chNode = childNodes[i];
BspNodeType type = BspNodeType((flags >> (i * 2)) & 0x3);
if (type != BspNodeType::Invalid)
offsets[i] = hecl::SBig(uint32_t(chNode.nodeOff - nodeOff - 36));
}
*pflags = hecl::SBig(*pflags);
*pflags = hecl::SBig(flags);
ptr += 36;
zeus::CAABox X[2];
if (flags & 0x1)
curAABB.splitX(X[0], X[1]);
else
{
X[0] = curAABB;
X[1] = curAABB;
}
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i)
{
zeus::CAABox Y[2];
if (flags & 0x2)
X[i].splitY(Y[0], Y[1]);
else
{
Y[0] = X[i];
Y[1] = X[i];
}
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
{
zeus::CAABox Z[2];
if (flags & 0x4)
Y[j].splitZ(Z[0], Z[1]);
else
{
Z[0] = Y[j];
Z[1] = Y[j];
}
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k)
{
int idx = i*4 + j*2 + k;
childNodes[idx].writeColNodes(ptr, Z[k]);
}
}
}
for (int i=0 ; i<8 ; ++i)
childNodes[i].writeColNodes(ptr, SplitAABB(curAABB, i));
}
}
}
uint16_t AROTBuilder::Node::getColRef(uint32_t& offset)
{
if (childIndices.size())
{
offset = nodeOff;
if (childNodes.empty())
return 2;
else
return 1;
}
return 0;
}
void AROTBuilder::build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb,
const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes)
{
for (int i=0 ; i<meshAabbs.size() ; ++i)
{
const zeus::CAABox& aabb = meshAabbs[i];
rootNode.addChild(0, fullAabb, aabb, i);
}
/* Recursively split */
BspNodeType rootType;
rootNode.addChild(0, 1, meshAabbs, fullAabb, rootType);
/* Calculate indexing metrics */
size_t totalNodeCount = 0;
size_t idxRefCount = 0;
size_t curOff = 0;
@@ -317,6 +301,7 @@ void AROTBuilder::build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAA
size_t bmpWordCount = ROUND_UP_32(meshes.size()) / 32;
size_t arotSz = 64 + bmpWordCount * bmpPool.m_pool.size() * 4 + totalNodeCount * 8 + idxRefCount * 2;
/* Write header */
secs.emplace_back(arotSz, 0);
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
w.writeUint32Big('AROT');
@@ -328,6 +313,7 @@ void AROTBuilder::build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAA
w.writeVec3fBig(fullAabb.max);
w.seekAlign32();
/* Write bitmap */
std::vector<uint32_t> bmpWords;
bmpWords.reserve(bmpWordCount);
for (const std::set<int>& bmp : bmpPool.m_pool)
@@ -361,20 +347,25 @@ void AROTBuilder::build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAA
w.writeUint32Big(word);
}
/* Write the rest */
rootNode.writeIndirectionTable(w);
rootNode.writeNodes(w, 0);
}
std::pair<std::unique_ptr<uint8_t[]>, uint32_t> AROTBuilder::buildCol(const ColMesh& mesh, BspNodeType& rootOut)
{
zeus::CAABox fullAabb;
/* Accumulate total AABB */
zeus::CAABox fullAABB;
for (const auto& vert : mesh.verts)
fullAabb.accumulateBounds(zeus::CVector3f(vert));
fullAABB.accumulateBounds(zeus::CVector3f(vert));
int t = 0;
/* Predetermine triangle AABBs */
std::vector<zeus::CAABox> triBoxes;
triBoxes.reserve(mesh.trianges.size());
for (const ColMesh::Triangle& tri : mesh.trianges)
{
zeus::CAABox aabb;
triBoxes.emplace_back();
zeus::CAABox& aabb = triBoxes.back();
for (int e=0 ; e<3 ; ++e)
{
const ColMesh::Edge& edge = mesh.edges[tri.edges[e]];
@@ -384,17 +375,17 @@ std::pair<std::unique_ptr<uint8_t[]>, uint32_t> AROTBuilder::buildCol(const ColM
aabb.accumulateBounds(zeus::CVector3f(vert));
}
}
rootNode.addChild(0, fullAabb, aabb, t);
++t;
}
/* Recursively split */
rootNode.addChild(0, COLLISION_MIN_NODE_TRIANGLES, triBoxes, fullAABB, rootOut);
/* Calculate offsets and write out */
size_t totalSize = 0;
rootNode.colSize(totalSize);
std::unique_ptr<uint8_t[]> ret(new uint8_t[totalSize]);
uint32_t dummy;
rootOut = BspNodeType(rootNode.getColRef(dummy));
uint8_t* ptr = ret.get();
rootNode.writeColNodes(ptr, fullAabb);
rootNode.writeColNodes(ptr, fullAABB);
return {std::move(ret), totalSize};
}