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

@ -0,0 +1,48 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C>
<option name="INDENT_NAMESPACE_MEMBERS" value="0" />
<option name="NAMESPACE_BRACE_PLACEMENT" value="5" />
<option name="FUNCTION_BRACE_PLACEMENT" value="5" />
<option name="BLOCK_BRACE_PLACEMENT" value="5" />
<option name="SPACE_WITHIN_TEMPLATE_DOUBLE_GT" value="false" />
<option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
</Objective-C>
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="hpp" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
<codeStyleSettings language="ObjectiveC">
<option name="BRACE_STYLE" value="5" />
<option name="CLASS_BRACE_STYLE" value="5" />
<option name="INDENT_CASE_FROM_SWITCH" value="false" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@ -4,9 +4,110 @@ namespace DataSpec
{ {
logvisor::Module Log("AROTBuilder"); 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) 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; return m_pool.size() - 1;
} }
bool AROTBuilder::Node::addChild(int level, const zeus::CAABox& curAabb, const zeus::CAABox& childAabb, int idx) static const uint32_t AROTChildCounts[] = { 0, 2, 2, 4, 2, 4, 4, 8 };
{
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;
}
void AROTBuilder::Node::nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff) void AROTBuilder::Node::nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff)
{ {
if (childIndices.size())
{
sz += 1; sz += 1;
poolIdx = bmpPool.addIndices(childIndices); poolIdx = bmpPool.addIndices(childIndices);
if (poolIdx > 65535) if (poolIdx > 65535)
Log.report(logvisor::Fatal, "AROT bitmap exceeds 16-bit node addressing; area too complex"); Log.report(logvisor::Fatal, "AROT bitmap exceeds 16-bit node addressing; area too complex");
uint32_t childCount = AROTChildCounts[flags]; uint32_t childCount = AROTChildCounts[compSubdivs];
nodeOff = curOff; nodeOff = curOff;
nodeSz = childCount * 2 + 4; nodeSz = childCount * 2 + 4;
curOff += nodeSz; curOff += nodeSz;
if (childNodes.size()) if (childNodes.size())
{ {
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 + ((flags & 0x2) != 0) ; ++j) for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{ {
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k) for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{ {
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) void AROTBuilder::Node::writeIndirectionTable(athena::io::MemoryWriter& w)
{ {
if (childIndices.size())
{
w.writeUint32Big(nodeOff); w.writeUint32Big(nodeOff);
if (childNodes.size()) if (childNodes.size())
{ {
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 + ((flags & 0x2) != 0) ; ++j) for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{ {
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k) for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{ {
childNodes[i*4 + j*2 + k].writeIndirectionTable(w); int idx = k*4 + j*2 + i;
} childNodes[idx].writeIndirectionTable(w);
} }
} }
} }
@ -114,10 +169,9 @@ void AROTBuilder::Node::writeIndirectionTable(athena::io::MemoryWriter& w)
void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx) void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx)
{ {
if (childIndices.size())
{
w.writeUint16Big(poolIdx); w.writeUint16Big(poolIdx);
w.writeUint16Big(flags); w.writeUint16Big(compSubdivs);
if (childNodes.size()) if (childNodes.size())
{ {
int curIdx = nodeIdx + 1; int curIdx = nodeIdx + 1;
@ -126,29 +180,28 @@ void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx)
int childIndices[8]; int childIndices[8];
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 + ((flags & 0x2) != 0) ; ++j) for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{ {
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k) for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{ {
int ch = i*4 + j*2 + k; int idx = k*4 + j*2 + i;
w.writeUint16Big(curIdx); w.writeUint16Big(curIdx);
childIndices[ch] = curIdx; childIndices[idx] = curIdx;
childNodes[ch].advanceIndex(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 + ((flags & 0x2) != 0) ; ++j) for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{ {
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k) for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{ {
int ch = i*4 + j*2 + k; int idx = k*4 + j*2 + i;
childNodes[ch].writeNodes(w, childIndices[ch]); 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) void AROTBuilder::Node::advanceIndex(int& nodeIdx)
{ {
if (childIndices.size())
{
++nodeIdx; ++nodeIdx;
if (childNodes.size()) if (childNodes.size())
{ {
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 + ((flags & 0x2) != 0) ; ++j) for (int j=0 ; j < 1 + ((compSubdivs & 0x2) != 0) ; ++j)
{ {
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k) for (int i=0 ; i < 1 + ((compSubdivs & 0x4) != 0) ; ++i)
{ {
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 else
{ {
totalSz += 36; totalSz += 36;
for (int i=0 ; i < 1 + ((flags & 0x1) != 0) ; ++i) for (int i=0 ; i<8 ; ++i)
{ childNodes[i].colSize(totalSz);
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);
}
}
}
} }
} }
} }
@ -226,90 +269,31 @@ void AROTBuilder::Node::writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB
uint16_t* pflags = reinterpret_cast<uint16_t*>(ptr); uint16_t* pflags = reinterpret_cast<uint16_t*>(ptr);
uint32_t* offsets = reinterpret_cast<uint32_t*>(ptr + 4); uint32_t* offsets = reinterpret_cast<uint32_t*>(ptr + 4);
memset(pflags, 0, sizeof(uint32_t) * 9); 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) const Node& chNode = childNodes[i];
{ BspNodeType type = BspNodeType((flags >> (i * 2)) & 0x3);
for (int k=0 ; k < 1 + ((flags & 0x4) != 0) ; ++k) if (type != BspNodeType::Invalid)
{ offsets[i] = hecl::SBig(uint32_t(chNode.nodeOff - nodeOff - 36));
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));
} }
}
} *pflags = hecl::SBig(flags);
}
*pflags = hecl::SBig(*pflags);
ptr += 36; ptr += 36;
zeus::CAABox X[2]; for (int i=0 ; i<8 ; ++i)
if (flags & 0x1) childNodes[i].writeColNodes(ptr, SplitAABB(curAABB, i));
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]);
} }
} }
}
}
}
}
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, 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) const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes)
{ {
for (int i=0 ; i<meshAabbs.size() ; ++i) /* Recursively split */
{ BspNodeType rootType;
const zeus::CAABox& aabb = meshAabbs[i]; rootNode.addChild(0, 1, meshAabbs, fullAabb, rootType);
rootNode.addChild(0, fullAabb, aabb, i);
}
/* Calculate indexing metrics */
size_t totalNodeCount = 0; size_t totalNodeCount = 0;
size_t idxRefCount = 0; size_t idxRefCount = 0;
size_t curOff = 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 bmpWordCount = ROUND_UP_32(meshes.size()) / 32;
size_t arotSz = 64 + bmpWordCount * bmpPool.m_pool.size() * 4 + totalNodeCount * 8 + idxRefCount * 2; size_t arotSz = 64 + bmpWordCount * bmpPool.m_pool.size() * 4 + totalNodeCount * 8 + idxRefCount * 2;
/* Write header */
secs.emplace_back(arotSz, 0); secs.emplace_back(arotSz, 0);
athena::io::MemoryWriter w(secs.back().data(), secs.back().size()); athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
w.writeUint32Big('AROT'); 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.writeVec3fBig(fullAabb.max);
w.seekAlign32(); w.seekAlign32();
/* Write bitmap */
std::vector<uint32_t> bmpWords; std::vector<uint32_t> bmpWords;
bmpWords.reserve(bmpWordCount); bmpWords.reserve(bmpWordCount);
for (const std::set<int>& bmp : bmpPool.m_pool) 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); w.writeUint32Big(word);
} }
/* Write the rest */
rootNode.writeIndirectionTable(w); rootNode.writeIndirectionTable(w);
rootNode.writeNodes(w, 0); rootNode.writeNodes(w, 0);
} }
std::pair<std::unique_ptr<uint8_t[]>, uint32_t> AROTBuilder::buildCol(const ColMesh& mesh, BspNodeType& rootOut) 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) 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) 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) for (int e=0 ; e<3 ; ++e)
{ {
const ColMesh::Edge& edge = mesh.edges[tri.edges[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)); 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; size_t totalSize = 0;
rootNode.colSize(totalSize); rootNode.colSize(totalSize);
std::unique_ptr<uint8_t[]> ret(new uint8_t[totalSize]); std::unique_ptr<uint8_t[]> ret(new uint8_t[totalSize]);
uint32_t dummy;
rootOut = BspNodeType(rootNode.getColRef(dummy));
uint8_t* ptr = ret.get(); uint8_t* ptr = ret.get();
rootNode.writeColNodes(ptr, fullAabb); rootNode.writeColNodes(ptr, fullAABB);
return {std::move(ret), totalSize}; return {std::move(ret), totalSize};
} }

View File

@ -12,6 +12,8 @@ namespace DataSpec
struct AROTBuilder struct AROTBuilder
{ {
using ColMesh = hecl::BlenderConnection::DataStream::ColMesh;
struct BitmapPool struct BitmapPool
{ {
std::vector<std::set<int>> m_pool; std::vector<std::set<int>> m_pool;
@ -23,12 +25,14 @@ struct AROTBuilder
std::vector<Node> childNodes; std::vector<Node> childNodes;
std::set<int> childIndices; std::set<int> childIndices;
size_t poolIdx = 0; size_t poolIdx = 0;
uint8_t flags = 0; uint16_t flags = 0;
uint16_t compSubdivs = 0;
size_t nodeOff = 0; size_t nodeOff = 0;
size_t nodeSz = 4; size_t nodeSz = 4;
bool addChild(int level, const zeus::CAABox& curAabb, const zeus::CAABox& childAabb, int idx); void addChild(int level, int minChildren, const std::vector<zeus::CAABox>& triBoxes,
const zeus::CAABox& curAABB, BspNodeType& typeOut);
void nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff); void nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff);
void writeIndirectionTable(athena::io::MemoryWriter& w); void writeIndirectionTable(athena::io::MemoryWriter& w);
void writeNodes(athena::io::MemoryWriter& w, int nodeIdx); void writeNodes(athena::io::MemoryWriter& w, int nodeIdx);
@ -36,12 +40,10 @@ struct AROTBuilder
void colSize(size_t& totalSz); void colSize(size_t& totalSz);
void writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB); void writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB);
uint16_t getColRef(uint32_t& offset);
} rootNode; } rootNode;
void build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb, void build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb,
const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes); const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes);
using ColMesh = hecl::BlenderConnection::DataStream::ColMesh;
std::pair<std::unique_ptr<uint8_t[]>, uint32_t> buildCol(const ColMesh& mesh, BspNodeType& rootOut); std::pair<std::unique_ptr<uint8_t[]>, uint32_t> buildCol(const ColMesh& mesh, BspNodeType& rootOut);
}; };

View File

@ -896,6 +896,10 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
return false; return false;
} }
/* Track alpha values for DXT1 eligibility */
bool doDXT1 = (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) &&
width >= 4 && height >= 4;
/* Read and make RGBA */ /* Read and make RGBA */
for (int r=height-1 ; r>=0 ; --r) for (int r=height-1 ; r>=0 ; --r)
{ {
@ -943,6 +947,8 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
bufOut[outbase+1] = rowBuf[inbase+1]; bufOut[outbase+1] = rowBuf[inbase+1];
bufOut[outbase+2] = rowBuf[inbase+2]; bufOut[outbase+2] = rowBuf[inbase+2];
bufOut[outbase+3] = rowBuf[inbase+3]; bufOut[outbase+3] = rowBuf[inbase+3];
if (rowBuf[inbase+3] != 0 && rowBuf[inbase+3] != 255)
doDXT1 = false;
} }
break; break;
case PNG_COLOR_TYPE_PALETTE: case PNG_COLOR_TYPE_PALETTE:
@ -973,6 +979,44 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
} }
} }
/* Do DXT1 compression */
std::unique_ptr<uint8_t[]> compOut;
size_t compLen = 0;
if (doDXT1)
{
int filterWidth = width;
int filterHeight = height;
size_t i;
for (i=0 ; i<numMips ; ++i)
{
compLen += squish::GetStorageRequirements(filterWidth, filterHeight, squish::kDxt1);
if (filterWidth == 4 || filterHeight == 4)
{
++i;
break;
}
filterWidth /= 2;
filterHeight /= 2;
}
numMips = i;
compOut.reset(new uint8_t[compLen]);
filterWidth = width;
filterHeight = height;
const uint8_t* rgbaIn = bufOut.get();
uint8_t* blocksOut = compOut.get();
for (i=0 ; i<numMips ; ++i)
{
int thisLen = squish::GetStorageRequirements(filterWidth, filterHeight, squish::kDxt1);
squish::CompressImage(rgbaIn, filterWidth, filterHeight, blocksOut, squish::kDxt1);
rgbaIn += filterWidth * filterHeight * nComps;
blocksOut += thisLen;
filterWidth /= 2;
filterHeight /= 2;
}
}
/* Do write out */ /* Do write out */
athena::io::FileWriter outf(outPath.getAbsolutePath(), true, false); athena::io::FileWriter outf(outPath.getAbsolutePath(), true, false);
if (outf.hasError()) if (outf.hasError())
@ -983,12 +1027,22 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
return false; return false;
} }
outf.writeInt32Big((paletteBuf && paletteSize) ? 17 : 16); int format;
if (paletteBuf && paletteSize)
format = 17;
else if (compOut)
format = 18;
else
format = 16;
outf.writeInt32Big(format);
outf.writeInt16Big(width); outf.writeInt16Big(width);
outf.writeInt16Big(height); outf.writeInt16Big(height);
outf.writeInt32Big(numMips); outf.writeInt32Big(numMips);
if (paletteBuf && paletteSize) if (paletteBuf && paletteSize)
outf.writeUBytes(paletteBuf.get(), paletteSize); outf.writeUBytes(paletteBuf.get(), paletteSize);
if (compOut)
outf.writeUBytes(compOut.get(), compLen);
else
outf.writeUBytes(bufOut.get(), bufLen); outf.writeUBytes(bufOut.get(), bufLen);
return true; return true;

View File

@ -59,6 +59,132 @@ void MREA::AddCMDLRigPairs(PAKEntryReadStream& rs,
scly.addCMDLRigPairs(pakRouter, addTo); scly.addCMDLRigPairs(pakRouter, addTo);
} }
/* Collision octree dumper */
static void OutputOctreeNode(hecl::BlenderConnection::PyOutStream& os, athena::io::MemoryReader& r,
BspNodeType type, const zeus::CAABox& aabb)
{
if (type == BspNodeType::Branch)
{
u16 flags = r.readUint16Big();
r.readUint16Big();
u32 offsets[8];
for (int i=0 ; i<8 ; ++i)
offsets[i] = r.readUint32Big();
u32 dataStart = r.position();
for (int i=0 ; i<8 ; ++i)
{
r.seek(dataStart + offsets[i], athena::Begin);
int chFlags = (flags >> (i * 2)) & 0x3;
zeus::CAABox pos, neg, res;
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)
res = pos;
else
res = neg;
}
else
{
zeus::CAABox(neg).splitX(neg, pos);
if (i & 1)
res = pos;
else
res = neg;
}
}
else
{
zeus::CAABox(neg).splitY(neg, pos);
if (i & 2)
{
zeus::CAABox(pos).splitX(neg, pos);
if (i & 1)
res = pos;
else
res = neg;
}
else
{
zeus::CAABox(neg).splitX(neg, pos);
if (i & 1)
res = pos;
else
res = neg;
}
}
OutputOctreeNode(os, r, BspNodeType(chFlags), res);
}
}
else if (type == BspNodeType::Leaf)
{
zeus::CVector3f pos = aabb.center();
zeus::CVector3f extent = aabb.extents();
os.format("obj = bpy.data.objects.new('Leaf', None)\n"
"bpy.context.scene.objects.link(obj)\n"
"obj.location = (%f,%f,%f)\n"
"obj.scale = (%f,%f,%f)\n"
"obj.empty_draw_type = 'CUBE'\n"
"obj.layers[1] = True\n"
"obj.layers[0] = False\n",
pos.x, pos.y, pos.z, extent.x, extent.y, extent.z);
}
}
static const uint32_t AROTChildCounts[] = { 0, 2, 2, 4, 2, 4, 4, 8 };
/* AROT octree dumper */
static void OutputOctreeNode(hecl::BlenderConnection::PyOutStream& os, athena::io::MemoryReader& r,
const zeus::CAABox& aabb)
{
r.readUint16Big();
u16 flags = r.readUint16Big();
if (flags)
{
u32 childCount = AROTChildCounts[flags];
r.seek(2 * childCount);
zeus::CAABox Z[2] = {aabb};
if ((flags & 0x1) != 0)
aabb.splitZ(Z[0], Z[1]);
for (int k=0 ; k < 1 + ((flags & 0x1) != 0) ; ++k)
{
zeus::CAABox Y[2] = {Z[0]};
if ((flags & 0x2) != 0)
Z[k].splitY(Y[0], Y[1]);
for (int j=0 ; j < 1 + ((flags & 0x2) != 0) ; ++j)
{
zeus::CAABox X[2] = {Y[0]};
if ((flags & 0x4) != 0)
Y[j].splitX(X[0], X[1]);
for (int i=0 ; i < 1 + ((flags & 0x4) != 0) ; ++i)
{
OutputOctreeNode(os, r, X[i]);
}
}
}
}
else
{
zeus::CVector3f pos = aabb.center();
zeus::CVector3f extent = aabb.extents();
os.format("obj = bpy.data.objects.new('Leaf', None)\n"
"bpy.context.scene.objects.link(obj)\n"
"obj.location = (%f,%f,%f)\n"
"obj.scale = (%f,%f,%f)\n"
"obj.empty_draw_type = 'CUBE'\n"
"obj.layers[1] = True\n"
"obj.layers[0] = False\n",
pos.x, pos.y, pos.z, extent.x, extent.y, extent.z);
}
}
bool MREA::Extract(const SpecBase& dataSpec, bool MREA::Extract(const SpecBase& dataSpec,
PAKEntryReadStream& rs, PAKEntryReadStream& rs,
const hecl::ProjectPath& outPath, const hecl::ProjectPath& outPath,
@ -143,7 +269,8 @@ bool MREA::Extract(const SpecBase& dataSpec,
} }
/* Skip AROT */ /* Skip AROT */
rs.seek(head.secSizes[curSec++], athena::Current); secStart = rs.position();
rs.seek(secStart + head.secSizes[curSec++], athena::Begin);
/* Read SCLY layers */ /* Read SCLY layers */
secStart = rs.position(); secStart = rs.position();
@ -360,6 +487,38 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
{ {
AROTBuilder arotBuilder; AROTBuilder arotBuilder;
arotBuilder.build(secs, fullAabb, meshAabbs, meshes); arotBuilder.build(secs, fullAabb, meshAabbs, meshes);
#if 0
hecl::BlenderConnection& conn = btok.getBlenderConnection();
if (!conn.createBlend(inPath.getWithExtension(_S(".octree.blend"), true), hecl::BlenderConnection::BlendType::Area))
return false;
/* Open Py Stream and read sections */
hecl::BlenderConnection::PyOutStream os = conn.beginPythonOut(true);
os.format("import bpy\n"
"import bmesh\n"
"from mathutils import Vector\n"
"\n"
"bpy.context.scene.name = '%s'\n",
inPath.getLastComponentUTF8().data());
athena::io::MemoryReader reader(secs.back().data(), secs.back().size());
reader.readUint32Big();
reader.readUint32Big();
u32 numMeshBitmaps = reader.readUint32Big();
u32 meshBitCount = reader.readUint32Big();
u32 numNodes = reader.readUint32Big();
auto aabbMin = reader.readVec3fBig();
auto aabbMax = reader.readVec3fBig();
reader.seekAlign32();
reader.seek(ROUND_UP_32(meshBitCount) / 8 * numMeshBitmaps + numNodes * 4);
zeus::CAABox arotAABB(aabbMin, aabbMax);
OutputOctreeNode(os, reader, arotAABB);
os.centerView();
os.close();
conn.saveBlend();
#endif
} }
/* SCLY */ /* SCLY */
@ -396,6 +555,29 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
DeafBabe collision = {}; DeafBabe collision = {};
DeafBabeBuildFromBlender(collision, cMesh); DeafBabeBuildFromBlender(collision, cMesh);
#if 0
hecl::BlenderConnection& conn = btok.getBlenderConnection();
if (!conn.createBlend(inPath.getWithExtension(_S(".octree.blend"), true), hecl::BlenderConnection::BlendType::Area))
return false;
/* Open Py Stream and read sections */
hecl::BlenderConnection::PyOutStream os = conn.beginPythonOut(true);
os.format("import bpy\n"
"import bmesh\n"
"from mathutils import Vector\n"
"\n"
"bpy.context.scene.name = '%s'\n",
inPath.getLastComponentUTF8().data());
athena::io::MemoryReader reader(collision.bspTree.get(), collision.bspSize);
zeus::CAABox colAABB(collision.aabb[0], collision.aabb[1]);
OutputOctreeNode(os, reader, collision.rootNodeType, colAABB);
os.centerView();
os.close();
conn.saveBlend();
#endif
secs.emplace_back(collision.binarySize(0), 0); secs.emplace_back(collision.binarySize(0), 0);
athena::io::MemoryWriter w(secs.back().data(), secs.back().size()); athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
collision.write(w); collision.write(w);

View File

@ -478,13 +478,13 @@ CAreaOctTree::Node CAreaOctTree::Node::GetChild(int idx) const
if (type == ETreeType::Branch) if (type == ETreeType::Branch)
{ {
zeus::CAABox pos, neg, res; zeus::CAABox pos, neg, res;
x0_aabb.splitZ(pos, neg); x0_aabb.splitZ(neg, pos);
if (idx & 4) if (idx & 4)
{ {
pos.splitY(pos, neg); zeus::CAABox(pos).splitY(neg, pos);
if (idx & 2) if (idx & 2)
{ {
pos.splitX(pos, neg); zeus::CAABox(pos).splitX(neg, pos);
if (idx & 1) if (idx & 1)
res = pos; res = pos;
else else
@ -492,7 +492,7 @@ CAreaOctTree::Node CAreaOctTree::Node::GetChild(int idx) const
} }
else else
{ {
neg.splitX(pos, neg); zeus::CAABox(neg).splitX(neg, pos);
if (idx & 1) if (idx & 1)
res = pos; res = pos;
else else
@ -501,10 +501,10 @@ CAreaOctTree::Node CAreaOctTree::Node::GetChild(int idx) const
} }
else else
{ {
neg.splitY(pos, neg); zeus::CAABox(neg).splitY(neg, pos);
if (idx & 2) if (idx & 2)
{ {
pos.splitX(pos, neg); zeus::CAABox(pos).splitX(neg, pos);
if (idx & 1) if (idx & 1)
res = pos; res = pos;
else else
@ -512,7 +512,7 @@ CAreaOctTree::Node CAreaOctTree::Node::GetChild(int idx) const
} }
else else
{ {
neg.splitX(pos, neg); zeus::CAABox(neg).splitX(neg, pos);
if (idx & 1) if (idx & 1)
res = pos; res = pos;
else else

View File

@ -202,7 +202,8 @@ enum class ETexelFormat
RGBA8 = 9, RGBA8 = 9,
CMPR = 10, CMPR = 10,
RGBA8PC = 16, RGBA8PC = 16,
C8PC = 17 C8PC = 17,
CMPRPC = 18,
}; };
class CGraphics class CGraphics

View File

@ -52,6 +52,7 @@ private:
void BuildRGBA8(const void* data, size_t length); void BuildRGBA8(const void* data, size_t length);
void BuildC8(const void* data, size_t length); void BuildC8(const void* data, size_t length);
void BuildC8Font(const void* data, EFontType ftype); void BuildC8Font(const void* data, EFontType ftype);
void BuildDXT1(const void* data, size_t length);
public: public:
CTexture(ETexelFormat, s16, s16, s32); CTexture(ETexelFormat, s16, s16, s32);

View File

@ -786,6 +786,16 @@ void CTexture::BuildC8Font(const void* data, EFontType ftype)
}); });
} }
void CTexture::BuildDXT1(const void* data, size_t length)
{
CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::DXT1,
boo::TextureClampMode::Repeat, data, length).get();
return true;
});
}
CTexture::CTexture(ETexelFormat fmt, s16 w, s16 h, s32 mips) CTexture::CTexture(ETexelFormat fmt, s16 w, s16 h, s32 mips)
: x0_fmt(fmt) : x0_fmt(fmt)
, x4_w(w) , x4_w(w)
@ -850,6 +860,9 @@ CTexture::CTexture(std::unique_ptr<u8[]>&& in, u32 length, bool otex)
BuildC8(owned.get() + 12, length - 12); BuildC8(owned.get() + 12, length - 12);
otex = true; otex = true;
break; break;
case ETexelFormat::CMPRPC:
BuildDXT1(owned.get() + 12, length - 12);
break;
default: default:
Log.report(logvisor::Fatal, "invalid texture type %d for boo", int(x0_fmt)); Log.report(logvisor::Fatal, "invalid texture type %d for boo", int(x0_fmt));
} }

View File

@ -86,6 +86,7 @@ bool CPauseScreen::CheckLoadComplete(const CStateManager& mgr)
if (!x28_pauseScreenInstructions->GetIsFinishedLoading()) if (!x28_pauseScreenInstructions->GetIsFinishedLoading())
return false; return false;
x34_loadedPauseScreenInstructions = x28_pauseScreenInstructions.GetObj(); x34_loadedPauseScreenInstructions = x28_pauseScreenInstructions.GetObj();
x34_loadedPauseScreenInstructions->SetMaxAspect(1.77f);
InitializeFrameGlue(); InitializeFrameGlue();
} }
if (x60_loadTok) if (x60_loadTok)
@ -96,6 +97,7 @@ bool CPauseScreen::CheckLoadComplete(const CStateManager& mgr)
{ {
CMemoryInStream s(x5c_frmePauseScreenBuf.get(), x58_frmePauseScreenBufSz); CMemoryInStream s(x5c_frmePauseScreenBuf.get(), x58_frmePauseScreenBufSz);
x64_frameInsts.push_back(CGuiFrame::CreateFrame(x54_frmePauseScreenId, *g_GuiSys, s, g_SimplePool)); x64_frameInsts.push_back(CGuiFrame::CreateFrame(x54_frmePauseScreenId, *g_GuiSys, s, g_SimplePool));
x64_frameInsts.back()->SetMaxAspect(1.77f);
} }
x5c_frmePauseScreenBuf.reset(); x5c_frmePauseScreenBuf.reset();
x60_loadTok.reset(); x60_loadTok.reset();

View File

@ -1762,6 +1762,9 @@ void CPlayer::ProcessInput(const CFinalInput& input, CStateManager& mgr)
if (input.ControllerIdx() != 0) if (input.ControllerIdx() != 0)
return; return;
if (input.PLTrigger())
Teleport(zeus::CTransform::Translate(-73.1577f, 155.273f, 0.f), mgr, true);
if (x2f8_morphBallState != EPlayerMorphBallState::Morphed) if (x2f8_morphBallState != EPlayerMorphBallState::Morphed)
UpdateScanningState(input, mgr, input.DeltaTime()); UpdateScanningState(input, mgr, input.DeltaTime());
@ -5658,7 +5661,7 @@ float CPlayer::JumpInput(const CFinalInput& input, CStateManager& mgr)
return (vDoubleJumpAccel - (vDoubleJumpAccel - hDoubleJumpAccel) * forwards) * xe8_mass * jumpFactor; return (vDoubleJumpAccel - (vDoubleJumpAccel - hDoubleJumpAccel) * forwards) * xe8_mass * jumpFactor;
} }
return GetGravity() / xe8_mass; return GetGravity() * xe8_mass;
} }
if (ControlMapper::GetDigitalInput(ControlMapper::ECommands::JumpOrBoost, input) || if (ControlMapper::GetDigitalInput(ControlMapper::ECommands::JumpOrBoost, input) ||

2
hecl

@ -1 +1 @@
Subproject commit 97c3a4918e32015988328fda625a46e925fa2764 Subproject commit a6340b03f77f8e753e0f381a335987d7b53747a3

@ -1 +1 @@
Subproject commit d78b1bf64087bf82133b1de71bfca9f9128fbf78 Subproject commit 5b3a0be1354a0542f1001d6b83e143fa633b673f