AROTBuilder and initial collision mesh cook integration

This commit is contained in:
Jack Andersen 2016-08-10 11:54:53 -10:00
parent 4519e3abf2
commit 6789cdf064
10 changed files with 439 additions and 40 deletions

View File

@ -0,0 +1,231 @@
#include "AROTBuilder.hpp"
namespace DataSpec
{
static const uint32_t AROTChildCounts[] = { 0, 2, 2, 4, 2, 4, 4, 8 };
size_t AROTBuilder::BitmapPool::addIndices(const std::set<int>& indices)
{
for (size_t i=0 ; i<m_pool.size() ; ++i)
if (m_pool[i] == indices)
return i;
m_pool.push_back(indices);
return m_pool.size() - 1;
}
bool AROTBuilder::Node::addChild(const zeus::CAABox& curAabb, const zeus::CAABox& childAabb, int idx)
{
if (childAabb.intersects(curAabb))
{
childIndices.insert(idx);
if (!curAabb.inside(childAabb))
{
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(Z[0], childAabb, idx);
inZ[1] = childNodes[i*4 + j*2 + 1].addChild(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;
}
return true;
}
return false;
}
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 (childNodes.size())
{
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].nodeCount(sz, idxRefs, bmpPool, curOff);
}
}
}
uint32_t childCount = AROTChildCounts[flags];
nodeOff = curOff;
nodeSz += childCount * 2;
curOff += nodeSz;
idxRefs += childCount;
}
}
}
void AROTBuilder::Node::writeIndirectionTable(athena::io::MemoryWriter& w)
{
if (childIndices.size())
{
w.writeUint32Big(nodeOff);
if (childNodes.size())
{
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].writeIndirectionTable(w);
}
}
}
}
}
}
void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx)
{
if (childIndices.size())
{
w.writeUint16Big(poolIdx);
w.writeUint16Big(flags);
if (childNodes.size())
{
int curIdx = nodeIdx + 1;
int childIndices[8];
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)
{
int ch = i*4 + j*2 + k;
w.writeUint16Big(curIdx);
childIndices[ch] = curIdx;
childNodes[ch].advanceIndex(curIdx);
}
}
}
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)
{
int ch = i*4 + j*2 + k;
childNodes[ch].writeNodes(w, childIndices[ch]);
}
}
}
}
}
}
void AROTBuilder::Node::advanceIndex(int& nodeIdx)
{
if (childIndices.size())
{
++nodeIdx;
if (childNodes.size())
{
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].advanceIndex(nodeIdx);
}
}
}
}
}
}
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(fullAabb, aabb, i);
}
size_t totalNodeCount = 0;
size_t idxRefCount = 0;
size_t curOff = 0;
rootNode.nodeCount(totalNodeCount, idxRefCount, bmpPool, curOff);
size_t bmpWordCount = ROUND_UP_32(meshes.size()) / 32;
size_t arotSz = 64 + bmpWordCount * bmpPool.m_pool.size() * 4 + totalNodeCount * 8 + idxRefCount * 2;
secs.emplace_back(arotSz, 0);
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
w.writeUint32Big('AROT');
w.writeUint32Big(1);
w.writeUint32Big(bmpPool.m_pool.size());
w.writeUint32Big(meshes.size());
w.writeUint32Big(totalNodeCount);
w.writeVec3fBig(fullAabb.min);
w.writeVec3fBig(fullAabb.max);
w.seekAlign32();
std::vector<uint32_t> bmpWords;
bmpWords.reserve(bmpWordCount);
for (const std::set<int>& bmp : bmpPool.m_pool)
{
bmpWords.clear();
bmpWords.resize(bmpWordCount);
auto bmpIt = bmp.cbegin();
if (bmpIt != bmp.cend())
{
int curIdx = 0;
for (int w=0 ; w<bmpWordCount ; ++w)
{
for (int b=0 ; b<32 ; ++b)
{
if (*bmpIt == curIdx)
{
bmpWords[w] |= 1 << b;
++bmpIt;
if (bmpIt == bmp.cend())
break;
}
++curIdx;
}
if (bmpIt == bmp.cend())
break;
}
}
for (uint32_t word : bmpWords)
w.writeUint32Big(word);
}
rootNode.writeIndirectionTable(w);
rootNode.writeNodes(w, 0);
}
}

View File

@ -0,0 +1,43 @@
#ifndef _DNACOMMON_AROTBUILDER_HPP_
#define _DNACOMMON_AROTBUILDER_HPP_
#include "DNACommon.hpp"
#include "zeus/CAABox.hpp"
#include "CMDL.hpp"
#include <set>
namespace DataSpec
{
struct AROTBuilder
{
struct BitmapPool
{
std::vector<std::set<int>> m_pool;
size_t addIndices(const std::set<int>& indices);
} bmpPool;
struct Node
{
std::vector<Node> childNodes;
std::set<int> childIndices;
size_t poolIdx = 0;
uint8_t flags = 0;
size_t nodeOff = 0;
size_t nodeSz = 4;
bool addChild(const zeus::CAABox& curAabb, const zeus::CAABox& childAabb, int idx);
void nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff);
void writeIndirectionTable(athena::io::MemoryWriter& w);
void writeNodes(athena::io::MemoryWriter& w, int nodeIdx);
void advanceIndex(int& nodeIdx);
} rootNode;
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);
};
}
#endif // _DNACOMMON_AROTBUILDER_HPP_

View File

@ -6,6 +6,7 @@
#include "../DNAMP2/CSKR.hpp"
#include "../DNAMP3/CMDLMaterials.hpp"
#include "../DNAMP3/CSKR.hpp"
#include "zeus/CAABox.hpp"
namespace DataSpec
{
@ -1525,7 +1526,7 @@ bool WriteHMDLCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& in
}
}
hecl::HMDLBuffers bufs = mesh.getHMDLBuffers();
hecl::HMDLBuffers bufs = mesh.getHMDLBuffers(false);
/* Metadata */
size_t secSz = bufs.m_meta.binarySize(0);
@ -1636,14 +1637,14 @@ template bool WriteHMDLCMDL<DNAMP1::HMDLMaterialSet, DNACMDL::SurfaceHeader_2, 2
template <class MaterialSet, class SurfaceHeader, class MeshHeader>
bool WriteMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
const std::vector<Mesh>& meshes)
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs)
{
return false;
}
template <class MaterialSet, class SurfaceHeader, class MeshHeader>
bool WriteHMDLMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
const std::vector<Mesh>& meshes)
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs)
{
/* Build material set */
{
@ -1690,11 +1691,91 @@ bool WriteHMDLMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::P
/* Iterate meshes */
for (const Mesh& mesh : meshes)
{
MeshHeader meshHeader = {};
meshHeader.visorFlags.setFromBlenderProps(mesh.customProps);
memmove(meshHeader.xfMtx, &mesh.sceneXf, 48);
memmove(&meshHeader.aabb[0], &mesh.aabbMin, 12);
memmove(&meshHeader.aabb[1], &mesh.aabbMax, 12);
zeus::CTransform meshXf(mesh.sceneXf.val);
meshXf.basis.transpose();
/* Header */
{
MeshHeader meshHeader = {};
meshHeader.visorFlags.setFromBlenderProps(mesh.customProps);
memmove(meshHeader.xfMtx, &mesh.sceneXf, 48);
zeus::CAABox aabb(zeus::CVector3f(mesh.aabbMin), zeus::CVector3f(mesh.aabbMax));
aabb = aabb.getTransformedAABox(meshXf);
meshAABBs.push_back(aabb);
fullAABB.accumulateBounds(aabb);
meshHeader.aabb[0] = aabb.min;
meshHeader.aabb[1] = aabb.max;
secsOut.emplace_back(meshHeader.binarySize(0), 0);
athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size());
meshHeader.write(w);
}
hecl::HMDLBuffers bufs = mesh.getHMDLBuffers(true);
std::vector<size_t> surfEndOffs;
surfEndOffs.reserve(bufs.m_surfaces.size());
size_t endOff = 0;
for (const hecl::HMDLBuffers::Surface& surf : bufs.m_surfaces)
{
endOff += 96;
surfEndOffs.push_back(endOff);
}
/* Metadata */
{
secsOut.emplace_back(bufs.m_meta.binarySize(0), 0);
athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size());
bufs.m_meta.write(w);
}
/* VBO */
{
secsOut.emplace_back(bufs.m_vboSz, 0);
athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size());
w.writeUBytes(bufs.m_vboData.get(), bufs.m_vboSz);
}
/* IBO */
{
secsOut.emplace_back(bufs.m_iboSz, 0);
athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size());
w.writeUBytes(bufs.m_iboData.get(), bufs.m_iboSz);
}
/* Surface index */
{
secsOut.emplace_back((surfEndOffs.size() + 1) * 4, 0);
athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size());
w.writeUint32Big(surfEndOffs.size());
for (size_t off : surfEndOffs)
w.writeUint32Big(off);
}
/* Surfaces */
for (const hecl::HMDLBuffers::Surface& surf : bufs.m_surfaces)
{
const Mesh::Surface& osurf = surf.m_origSurf;
SurfaceHeader header;
header.centroid = meshXf * zeus::CVector3f(osurf.centroid);
header.matIdx = osurf.materialIdx;
header.reflectionNormal = (meshXf.basis * zeus::CVector3f(osurf.reflectionNormal)).normalized();
header.idxStart = surf.m_start;
header.idxCount = surf.m_count;
header.skinMtxBankIdx = osurf.skinBankIdx;
header.aabbSz = 24;
zeus::CAABox aabb(zeus::CVector3f(surf.m_origSurf.aabbMin), zeus::CVector3f(surf.m_origSurf.aabbMax));
aabb = aabb.getTransformedAABox(meshXf);
header.aabb[0] = aabb.min;
header.aabb[1] = aabb.max;
secsOut.emplace_back(header.binarySize(0), 0);
athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size());
header.write(w);
}
}
return true;
@ -1702,7 +1783,7 @@ bool WriteHMDLMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::P
template bool WriteHMDLMREASecs<DNAMP1::HMDLMaterialSet, DNACMDL::SurfaceHeader_2, DNAMP1::MREA::MeshHeader>
(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
const std::vector<Mesh>& meshes);
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs);
void SurfaceHeader_1::read(athena::io::IStreamReader& reader)
{

View File

@ -8,6 +8,7 @@
#include "BlenderConnection.hpp"
#include "GX.hpp"
#include "TXTR.hpp"
#include "zeus/CAABox.hpp"
namespace DataSpec
{
@ -171,11 +172,11 @@ bool WriteHMDLCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& in
template <class MaterialSet, class SurfaceHeader, class MeshHeader>
bool WriteMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
const std::vector<Mesh>& meshes);
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs);
template <class MaterialSet, class SurfaceHeader, class MeshHeader>
bool WriteHMDLMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
const std::vector<Mesh>& meshes);
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs);
}
}

View File

@ -28,8 +28,9 @@ add_library(DNACommon
FONT.hpp FONT.cpp
DGRP.hpp DGRP.cpp
DeafBabe.hpp
BabeDead.hpp
BabeDead.hpp
RigInverter.hpp RigInverter.cpp
AROTBuilder.hpp AROTBuilder.cpp
Tweaks/ITweakGame.hpp
Tweaks/ITweakParticle.hpp
Tweaks/ITweakPlayer.hpp

View File

@ -3,6 +3,8 @@
#include "DeafBabe.hpp"
#include "../DNACommon/BabeDead.hpp"
#include "zeus/Math.hpp"
#include "zeus/CAABox.hpp"
#include "DataSpec/DNACommon/AROTBuilder.hpp"
namespace DataSpec
{
@ -246,14 +248,16 @@ void MREA::MeshHeader::VisorFlags::setFromBlenderProps(const std::unordered_map<
bool MREA::Cook(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const std::vector<DNACMDL::Mesh>& meshes)
const std::vector<DNACMDL::Mesh>& meshes,
const ColMesh& cMesh)
{
return false;
}
bool MREA::PCCook(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const std::vector<DNACMDL::Mesh>& meshes)
const std::vector<DNACMDL::Mesh>& meshes,
const ColMesh& cMesh)
{
/* Discover area layers */
hecl::ProjectPath areaDirPath = inPath.getParentPath();
@ -270,7 +274,7 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
}
}
size_t secCount = 1 + meshes.size() * 4; /* (materials, 4 fixed model secs) */
size_t secCount = 1 + meshes.size() * 5; /* (materials, 5 fixed model secs) */
/* tally up surfaces */
for (const DNACMDL::Mesh& mesh : meshes)
@ -298,8 +302,8 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
secs.reserve(secCount + 2);
/* Header section */
secs.emplace_back(head.binarySize(0), 0);
{
secs.emplace_back(head.binarySize(0), 0);
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
head.write(w);
int i = w.position();
@ -312,37 +316,59 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
secs.emplace_back();
std::vector<uint8_t>& sizesSec = secs.back();
/* Pre-emptively build full AABB and mesh AABBs in world coords */
zeus::CAABox fullAabb;
std::vector<zeus::CAABox> meshAabbs;
meshAabbs.reserve(meshes.size());
/* Models */
if (!DNACMDL::WriteHMDLMREASecs<HMDLMaterialSet, DNACMDL::SurfaceHeader_2, MeshHeader>(secs, inPath, meshes))
if (!DNACMDL::WriteHMDLMREASecs<HMDLMaterialSet, DNACMDL::SurfaceHeader_2, MeshHeader>
(secs, inPath, meshes, fullAabb, meshAabbs))
return false;
/* AROT */
{
AROTBuilder builder;
builder.build(secs, fullAabb, meshAabbs, meshes);
}
/* SCLY */
for (const hecl::ProjectPath& layer : layerScriptPaths)
{
FILE* yamlFile = hecl::Fopen(layer.getAbsolutePath().c_str(), _S("r"));
if (!yamlFile)
DNAMP1::SCLY sclyData;
sclyData.fourCC = FOURCC('SCLY');
sclyData.version = 1;
for (const hecl::ProjectPath& layer : layerScriptPaths)
{
Log.report(logvisor::Fatal, _S("unable to open %s for reading"), layer.getAbsolutePath().c_str());
return false;
}
FILE* yamlFile = hecl::Fopen(layer.getAbsolutePath().c_str(), _S("r"));
if (!yamlFile)
continue;
if (!BigYAML::ValidateFromYAMLFile<DNAMP1::SCLY>(yamlFile))
{
fclose(yamlFile);
continue;
}
athena::io::YAMLDocReader reader;
yaml_parser_set_input_file(reader.getParser(), yamlFile);
if (!reader.parse())
{
Log.report(logvisor::Fatal, _S("unable to parse %s"), layer.getAbsolutePath().c_str());
athena::io::YAMLDocReader reader;
yaml_parser_set_input_file(reader.getParser(), yamlFile);
if (!reader.parse())
{
fclose(yamlFile);
continue;
}
fclose(yamlFile);
return false;
}
fclose(yamlFile);
std::string classStr = reader.readString("DNAType");
if (classStr.empty())
return false;
sclyData.layers.emplace_back();
sclyData.layers.back().read(reader);
sclyData.layerSizes.push_back(sclyData.layers.back().binarySize(0));
}
sclyData.layerCount = sclyData.layers.size();
secs.emplace_back(sclyData.binarySize(0), 0);
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
sclyData.write(w);
}
/* Collision */

View File

@ -122,13 +122,17 @@ struct MREA
PAKRouter<PAKBridge>& pakRouter,
PAK::Entry& entry);
using ColMesh = hecl::BlenderConnection::DataStream::ColMesh;
static bool Cook(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const std::vector<DNACMDL::Mesh>& meshes);
const std::vector<DNACMDL::Mesh>& meshes,
const ColMesh& cMesh);
static bool PCCook(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const std::vector<DNACMDL::Mesh>& meshes);
const std::vector<DNACMDL::Mesh>& meshes,
const ColMesh& cMesh);
};
}

View File

@ -54,6 +54,7 @@ struct SpecBase : hecl::Database::IDataSpec
/* Cook handlers */
using BlendStream = hecl::BlenderConnection::DataStream;
using Mesh = BlendStream::Mesh;
using ColMesh = BlendStream::ColMesh;
using Actor = BlendStream::Actor;
virtual void cookMesh(const hecl::ProjectPath& out, const hecl::ProjectPath& in,

View File

@ -389,20 +389,31 @@ struct SpecMP1 : SpecBase
std::vector<Mesh> meshCompiles;
meshCompiles.reserve(meshes.size());
std::experimental::optional<ColMesh> colMesh;
for (const std::string& mesh : meshes)
{
hecl::SystemStringView meshSys(mesh);
meshCompiles.push_back(ds.compileMesh(fast ? hecl::HMDLTopology::Triangles : hecl::HMDLTopology::TriStrips, -1,
if (!mesh.compare("CMESH"))
{
colMesh = ds.compileColMesh(mesh);
progress(_S("Collision Mesh"));
continue;
}
meshCompiles.push_back(ds.compileMesh(mesh, fast ? hecl::HMDLTopology::Triangles : hecl::HMDLTopology::TriStrips, -1,
[&](int surfCount)
{
progress(hecl::SysFormat(_S("%s %d"), meshSys.c_str(), surfCount).c_str());
}));
}
if (!colMesh)
Log.report(logvisor::Fatal, _S("unable to find mesh named 'CMESH' in %s"), in.getAbsolutePath().c_str());
if (m_pc)
DNAMP1::MREA::PCCook(out, in, meshCompiles);
DNAMP1::MREA::PCCook(out, in, meshCompiles, *colMesh);
else
DNAMP1::MREA::Cook(out, in, meshCompiles);
DNAMP1::MREA::Cook(out, in, meshCompiles, *colMesh);
}
void cookYAML(const hecl::ProjectPath& out, const hecl::ProjectPath& in,

2
hecl

@ -1 +1 @@
Subproject commit e8905db61ac226492210fbb1ed96bda7c33a7110
Subproject commit 6b9594a40c2f286db07b979b6d2e5c6db5c5283a