initial HMDL/CMDL implementation

This commit is contained in:
Jack Andersen 2015-11-13 16:28:45 -10:00
parent 9bbe5da6da
commit 79cf5f12ca
6 changed files with 370 additions and 42 deletions

View File

@ -1129,7 +1129,7 @@ bool WriteCMDL(const HECL::ProjectPath& outPath, const HECL::ProjectPath& inPath
false, false, groupIdx);
endOff = targetMSet.materials.back().binarySize(endOff);
targetMSet.addMaterialEndOff(endOff);
targetMSet.head.addMaterialEndOff(endOff);
}
for (const HECL::ProjectPath& path : texPaths)
@ -1139,7 +1139,7 @@ bool WriteCMDL(const HECL::ProjectPath& outPath, const HECL::ProjectPath& inPath
/* TODO: incorporate hecl hashes */
size_t search = relPath.find(_S("TXTR_"));
if (search != HECL::SystemString::npos)
targetMSet.addTexture(relPath.c_str() + search + 5);
targetMSet.head.addTexture(relPath.c_str() + search + 5);
else
LogDNACommon.report(LogVisor::FatalError, "unable to get hash from path");
}
@ -1259,9 +1259,9 @@ bool WriteCMDL(const HECL::ProjectPath& outPath, const HECL::ProjectPath& inPath
/* Surfaces */
GX::Primitive prim;
if (mesh.outputMode == Mesh::OutputTriangles)
if (mesh.topology == HECL::TopologyTriangles)
prim = GX::TRIANGLES;
else if (mesh.outputMode == Mesh::OutputTriStrips)
else if (mesh.topology == HECL::TopologyTriStrips)
prim = GX::TRIANGLESTRIP;
else
LogDNACommon.report(LogVisor::FatalError, "unrecognized mesh output mode");
@ -1313,6 +1313,181 @@ bool WriteCMDL(const HECL::ProjectPath& outPath, const HECL::ProjectPath& inPath
return true;
}
template <class MaterialSet, class SurfaceHeader, atUint32 Version>
bool WriteHMDLCMDL(const HECL::ProjectPath& outPath, const HECL::ProjectPath& inPath, const Mesh& mesh)
{
Header head;
head.magic = 0xDEADBABE;
head.version = 0x10000 | Version;
head.aabbMin = mesh.aabbMin.val;
head.aabbMax = mesh.aabbMax.val;
head.matSetCount = mesh.materialSets.size();
head.secCount = head.matSetCount + 5 + mesh.surfaces.size();
head.secSizes.reserve(head.secCount);
/* Lengths of padding to insert while writing */
std::vector<size_t> paddingSizes;
paddingSizes.reserve(head.secCount);
/* Build material sets */
std::vector<MaterialSet> matSets;
matSets.reserve(mesh.materialSets.size());
{
HECL::Frontend::Frontend FE;
for (const std::vector<Mesh::Material>& mset : mesh.materialSets)
{
matSets.emplace_back();
MaterialSet& targetMSet = matSets.back();
std::vector<HECL::ProjectPath> texPaths;
texPaths.reserve(mset.size()*4);
for (const Mesh::Material& mat : mset)
{
for (const HECL::ProjectPath& path : mat.texs)
{
bool found = false;
for (const HECL::ProjectPath& ePath : texPaths)
{
if (path == ePath)
{
found = true;
break;
}
}
if (!found)
texPaths.push_back(path);
}
}
size_t endOff = 0;
for (const Mesh::Material& mat : mset)
{
std::string diagName = HECL::Format("%s:%s", inPath.getLastComponentUTF8(), mat.name.c_str());
targetMSet.materials.emplace_back(FE, diagName, mat, mat.iprops, texPaths);
endOff = targetMSet.materials.back().binarySize(endOff);
targetMSet.head.addMaterialEndOff(endOff);
}
for (const HECL::ProjectPath& path : texPaths)
{
const HECL::SystemString& relPath = path.getRelativePath();
/* TODO: incorporate hecl hashes */
size_t search = relPath.find(_S("TXTR_"));
if (search != HECL::SystemString::npos)
targetMSet.head.addTexture(relPath.c_str() + search + 5);
else
LogDNACommon.report(LogVisor::FatalError, "unable to get hash from path");
}
size_t secSz = targetMSet.binarySize(0);
size_t secSz32 = ROUND_UP_32(secSz);
head.secSizes.push_back(secSz32);
paddingSizes.push_back(secSz32 - secSz);
}
}
HECL::HMDLBuffers bufs = mesh.getHMDLBuffers();
/* Metadata */
size_t secSz = bufs.m_metaSz;
size_t secSz32 = ROUND_UP_32(secSz);
if (secSz32 == 0)
secSz32 = 32;
head.secSizes.push_back(secSz32);
paddingSizes.push_back(secSz32 - secSz);
/* VBO */
secSz = bufs.m_vboSz;
secSz32 = ROUND_UP_32(secSz);
if (secSz32 == 0)
secSz32 = 32;
head.secSizes.push_back(secSz32);
paddingSizes.push_back(secSz32 - secSz);
/* IBO */
secSz = bufs.m_iboSz;
secSz32 = ROUND_UP_32(secSz);
if (secSz32 == 0)
secSz32 = 32;
head.secSizes.push_back(secSz32);
paddingSizes.push_back(secSz32 - secSz);
/* Surface index */
std::vector<size_t> surfEndOffs;
surfEndOffs.reserve(bufs.m_surfaces.size());
secSz = bufs.m_surfaces.size() * 4 + 4;
secSz32 = ROUND_UP_32(secSz);
if (secSz32 == 0)
secSz32 = 32;
head.secSizes.push_back(secSz32);
paddingSizes.push_back(secSz32 - secSz);
/* Surfaces */
size_t endOff = 0;
for (const HECL::HMDLBuffers::Surface& surf : bufs.m_surfaces)
{
head.secSizes.push_back(64);
paddingSizes.push_back(0);
endOff += 64;
surfEndOffs.push_back(endOff);
}
/* Write sections */
Athena::io::FileWriter writer(outPath.getAbsolutePath());
head.write(writer);
std::vector<size_t>::const_iterator padIt = paddingSizes.cbegin();
/* Material Sets */
for (const MaterialSet& mset : matSets)
{
mset.write(writer);
writer.fill(atUint8(0), *padIt);
++padIt;
}
/* Metadata */
writer.writeUBytes(bufs.m_metaData.get(), bufs.m_metaSz);
writer.fill(atUint8(0), *padIt);
++padIt;
/* VBO */
writer.writeUBytes(bufs.m_vboData.get(), bufs.m_vboSz);
writer.fill(atUint8(0), *padIt);
++padIt;
/* IBO */
writer.writeUBytes(bufs.m_iboData.get(), bufs.m_iboSz);
writer.fill(atUint8(0), *padIt);
++padIt;
/* Surface index */
writer.writeUint32Big(surfEndOffs.size());
for (size_t off : surfEndOffs)
writer.writeUint32Big(off);
writer.fill(atUint8(0), *padIt);
++padIt;
/* Surfaces */
for (const HECL::HMDLBuffers::Surface& surf : bufs.m_surfaces)
{
const Mesh::Surface& osurf = surf.m_origSurf;
SurfaceHeader header;
header.centroid = osurf.centroid;
header.matIdx = osurf.materialIdx;
header.reflectionNormal = osurf.reflectionNormal;
header.unk1 = surf.m_start;
header.unk2 = surf.m_count;
header.write(writer);
writer.fill(atUint8(0), *padIt);
++padIt;
}
writer.close();
return true;
}
}
}

View File

@ -53,7 +53,7 @@ struct CMDL
/* Cook and re-extract test */
HECL::ProjectPath tempOut = outPath.getWithExtension(_S(".recook"), true);
HECL::BlenderConnection::DataStream ds = conn.beginData();
DNACMDL::Mesh mesh = ds.compileMesh(DNACMDL::Mesh::OutputTriStrips, -1);
DNACMDL::Mesh mesh = ds.compileMesh(HECL::TopologyTriStrips, -1);
ds.close();
DNACMDL::WriteCMDL<MaterialSet, DNACMDL::SurfaceHeader_1_2, 2>(tempOut, outPath, mesh);
@ -108,6 +108,34 @@ struct CMDL
return false;
return true;
}
static bool HMDLCook(const HECL::ProjectPath& outPath,
const HECL::ProjectPath& inPath,
const DNACMDL::Mesh& mesh)
{
HECL::ProjectPath tempOut = outPath.getWithExtension(_S(".recook"));
if (mesh.skins.size())
{
if (!DNACMDL::WriteHMDLCMDL<HMDLMaterialSet, DNACMDL::SurfaceHeader_1_2, 2>(tempOut, inPath, mesh))
return false;
/* Output skinning intermediate */
Athena::io::FileWriter writer(outPath.getWithExtension(_S(".skin")).getAbsolutePath());
writer.writeUint32Big(mesh.skinBanks.banks.size());
for (const DNACMDL::Mesh::SkinBanks::Bank& sb : mesh.skinBanks.banks)
{
writer.writeUint32Big(sb.m_boneIdxs.size());
for (uint32_t bind : sb.m_boneIdxs)
writer.writeUint32Big(bind);
}
writer.writeUint32Big(mesh.boneNames.size());
for (const std::string& boneName : mesh.boneNames)
writer.writeString(boneName);
}
else if (!DNACMDL::WriteHMDLCMDL<HMDLMaterialSet, DNACMDL::SurfaceHeader_1_2, 2>(tempOut, inPath, mesh))
return false;
return true;
}
};
}

View File

@ -1139,7 +1139,7 @@ MaterialSet::Material::Material(const HECL::Backend::GX& gx,
{
found = true;
++uvAnimsCount;
uvAnims.emplace_back(tcg);
uvAnims.emplace_back(tcg.m_gameFunction, tcg.m_gameArgs);
uvAnimsSize = uvAnims.back().binarySize(uvAnimsSize);
break;
}
@ -1149,62 +1149,151 @@ MaterialSet::Material::Material(const HECL::Backend::GX& gx,
}
}
MaterialSet::Material::UVAnimation::UVAnimation(const HECL::Backend::GX::TexCoordGen& tcg)
HMDLMaterialSet::Material::Material(HECL::Frontend::Frontend& FE,
const std::string& diagName,
const HECL::BlenderConnection::DataStream::Mesh::Material& mat,
const std::unordered_map<std::string, int32_t>& iprops,
const std::vector<HECL::ProjectPath>& texPaths)
{
if (!tcg.m_gameFunction.compare("RetroUVMode0Node"))
auto search = iprops.find("retro_depth_sort");
if (search != iprops.end())
flags.setDepthSorting(search->second != 0);
search = iprops.find("retro_punchthrough_alpha");
if (search != iprops.end())
flags.setPunchthroughAlpha(search->second != 0);
search = iprops.find("retro_samus_reflection");
if (search != iprops.end())
flags.setSamusReflection(search->second != 0);
search = iprops.find("retro_depth_write");
if (search != iprops.end())
flags.setDepthWrite(search->second != 0);
search = iprops.find("retro_samus_reflection_persp");
if (search != iprops.end())
flags.setSamusReflectionSurfaceEye(search->second != 0);
search = iprops.find("retro_shadow_occluder");
if (search != iprops.end())
flags.setShadowOccluderMesh(search->second != 0);
search = iprops.find("retro_samus_reflection_indirect");
if (search != iprops.end())
flags.setSamusReflectionIndirectTexture(search->second != 0);
search = iprops.find("retro_lightmapped");
if (search != iprops.end())
flags.setLightmap(search->second != 0);
for (const HECL::ProjectPath& path : mat.texs)
{
size_t idx = 0;
for (const HECL::ProjectPath& tPath : texPaths)
{
if (path == tPath)
{
textureIdxs.push_back(idx);
++textureCount;
break;
}
++idx;
}
}
if (flags.samusReflectionIndirectTexture())
indTexSlot.push_back(textureIdxs.size());
heclSource = mat.source;
heclIr = FE.compileSource(mat.source, diagName);
uvAnimsSize = 4;
uvAnimsCount = 0;
for (const HECL::Frontend::IR::Instruction& inst : heclIr.m_instructions)
{
if (inst.m_op != HECL::Frontend::IR::OpCall)
continue;
if (inst.m_call.m_name.compare("Texture"))
continue;
const HECL::Frontend::IR::Instruction& sourceInst = inst.getChildInst(heclIr, 1);
if (sourceInst.m_op != HECL::Frontend::IR::OpCall)
continue;
if (sourceInst.m_call.m_name.compare(0, 11, "RetroUVMode"))
continue;
std::vector<atVec4f> gameArgs;
gameArgs.reserve(inst.getChildCount() - 1);
for (int i=1 ; i<inst.getChildCount() ; ++i)
{
const HECL::Frontend::IR::Instruction& ci = sourceInst.getChildInst(heclIr, i);
gameArgs.push_back(ci.getImmVec());
}
++uvAnimsCount;
uvAnims.emplace_back(sourceInst.m_call.m_name, gameArgs);
uvAnimsSize = uvAnims.back().binarySize(uvAnimsSize);
}
}
MaterialSet::Material::UVAnimation::UVAnimation(const std::string& gameFunction,
const std::vector<atVec4f>& gameArgs)
{
if (!gameFunction.compare("RetroUVMode0Node"))
mode = ANIM_MV_INV_NOTRANS;
else if (!tcg.m_gameFunction.compare("RetroUVMode1Node"))
else if (!gameFunction.compare("RetroUVMode1Node"))
mode = ANIM_MV_INV;
else if (!tcg.m_gameFunction.compare("RetroUVMode2Node"))
else if (!gameFunction.compare("RetroUVMode2Node"))
{
mode = ANIM_SCROLL;
if (tcg.m_gameArgs.size() < 2)
if (gameArgs.size() < 2)
Log.report(LogVisor::FatalError, "Mode2 UV anim requires 2 vector arguments");
vals[0] = tcg.m_gameArgs[0].vec[0];
vals[1] = tcg.m_gameArgs[0].vec[1];
vals[2] = tcg.m_gameArgs[1].vec[0];
vals[3] = tcg.m_gameArgs[1].vec[1];
vals[0] = gameArgs[0].vec[0];
vals[1] = gameArgs[0].vec[1];
vals[2] = gameArgs[1].vec[0];
vals[3] = gameArgs[1].vec[1];
}
else if (!tcg.m_gameFunction.compare("RetroUVMode3Node"))
else if (!gameFunction.compare("RetroUVMode3Node"))
{
mode = ANIM_ROTATION;
if (tcg.m_gameArgs.size() < 2)
if (gameArgs.size() < 2)
Log.report(LogVisor::FatalError, "Mode3 UV anim requires 2 arguments");
vals[0] = tcg.m_gameArgs[0].vec[0];
vals[1] = tcg.m_gameArgs[1].vec[0];
vals[0] = gameArgs[0].vec[0];
vals[1] = gameArgs[1].vec[0];
}
else if (!tcg.m_gameFunction.compare("RetroUVMode4Node"))
else if (!gameFunction.compare("RetroUVMode4Node"))
{
mode = ANIM_HSTRIP;
if (tcg.m_gameArgs.size() < 4)
if (gameArgs.size() < 4)
Log.report(LogVisor::FatalError, "Mode4 UV anim requires 4 arguments");
vals[0] = tcg.m_gameArgs[0].vec[0];
vals[1] = tcg.m_gameArgs[1].vec[0];
vals[2] = tcg.m_gameArgs[2].vec[0];
vals[3] = tcg.m_gameArgs[3].vec[0];
vals[0] = gameArgs[0].vec[0];
vals[1] = gameArgs[1].vec[0];
vals[2] = gameArgs[2].vec[0];
vals[3] = gameArgs[3].vec[0];
}
else if (!tcg.m_gameFunction.compare("RetroUVMode5Node"))
else if (!gameFunction.compare("RetroUVMode5Node"))
{
mode = ANIM_VSTRIP;
if (tcg.m_gameArgs.size() < 4)
if (gameArgs.size() < 4)
Log.report(LogVisor::FatalError, "Mode5 UV anim requires 4 arguments");
vals[0] = tcg.m_gameArgs[0].vec[0];
vals[1] = tcg.m_gameArgs[1].vec[0];
vals[2] = tcg.m_gameArgs[2].vec[0];
vals[3] = tcg.m_gameArgs[3].vec[0];
vals[0] = gameArgs[0].vec[0];
vals[1] = gameArgs[1].vec[0];
vals[2] = gameArgs[2].vec[0];
vals[3] = gameArgs[3].vec[0];
}
else if (!tcg.m_gameFunction.compare("RetroUVMode6Node"))
else if (!gameFunction.compare("RetroUVMode6Node"))
mode = ANIM_MODEL;
else if (!tcg.m_gameFunction.compare("RetroUVMode7Node"))
else if (!gameFunction.compare("RetroUVMode7Node"))
{
mode = ANIM_MODE_WHO_MUST_NOT_BE_NAMED;
if (tcg.m_gameArgs.size() < 2)
if (gameArgs.size() < 2)
Log.report(LogVisor::FatalError, "Mode7 UV anim requires 2 arguments");
vals[0] = tcg.m_gameArgs[0].vec[0];
vals[1] = tcg.m_gameArgs[1].vec[0];
vals[0] = gameArgs[0].vec[0];
vals[1] = gameArgs[1].vec[0];
}
else
Log.report(LogVisor::FatalError, "unsupported UV anim '%s'", tcg.m_gameFunction.c_str());
Log.report(LogVisor::FatalError, "unsupported UV anim '%s'", gameFunction.c_str());
}
}

View File

@ -24,9 +24,10 @@ struct MaterialSet : BigDNA
Vector<UniqueID32, DNA_COUNT(textureCount)> textureIDs;
Value<atUint32> materialCount = 0;
Vector<atUint32, DNA_COUNT(materialCount)> materialEndOffs;
void addTexture(const UniqueID32& id) {textureIDs.push_back(id); ++textureCount;}
void addMaterialEndOff(atUint32 off) {materialEndOffs.push_back(off); ++materialCount;}
} head;
void addTexture(const UniqueID32& id) {head.textureIDs.push_back(id); ++head.textureCount;}
void addMaterialEndOff(atUint32 off) {head.materialEndOffs.push_back(off); ++head.materialCount;}
struct Material : BigDNA
{
@ -370,7 +371,8 @@ struct MaterialSet : BigDNA
}
UVAnimation() = default;
UVAnimation(const HECL::Backend::GX::TexCoordGen& tcg);
UVAnimation(const std::string& gameFunction,
const std::vector<atVec4f>& gameArgs);
};
Vector<UVAnimation, DNA_COUNT(uvAnimsCount)> uvAnims;
@ -455,6 +457,40 @@ struct MaterialSet : BigDNA
};
struct HMDLMaterialSet : BigDNA
{
static constexpr bool OneSection() {return false;}
DECL_DNA
MaterialSet::MaterialSetHead head;
struct Material : BigDNA
{
DECL_DNA
MaterialSet::Material::Flags flags;
Value<atUint32> textureCount = 0;
Vector<atUint32, DNA_COUNT(textureCount)> textureIdxs;
Vector<atUint32, DNA_COUNT(flags.samusReflectionIndirectTexture())> indTexSlot;
Value<atUint32> uvAnimsSize = 4;
Value<atUint32> uvAnimsCount = 0;
Vector<MaterialSet::Material::UVAnimation, DNA_COUNT(uvAnimsCount)> uvAnims;
String<-1> heclSource;
HECL::Frontend::IR heclIr;
Material() = default;
Material(HECL::Frontend::Frontend& FE,
const std::string& diagName,
const HECL::BlenderConnection::DataStream::Mesh::Material& mat,
const std::unordered_map<std::string, int32_t>& iprops,
const std::vector<HECL::ProjectPath>& texPaths);
};
Vector<Material, DNA_COUNT(head.materialCount)> materials;
};
}
}

View File

@ -289,7 +289,7 @@ struct SpecMP1 : SpecBase
void cookMesh(const HECL::ProjectPath& out, const HECL::ProjectPath& in,
BlendStream& ds, bool fast, FCookProgress progress) const
{
Mesh mesh = ds.compileMesh(fast ? Mesh::OutputTriangles : Mesh::OutputTriStrips, -1,
Mesh mesh = ds.compileMesh(fast ? HECL::TopologyTriangles : HECL::TopologyTriStrips, -1,
[&progress](int surfCount)
{
progress(HECL::SysFormat(_S("%d"), surfCount).c_str());

2
hecl

@ -1 +1 @@
Subproject commit 0bf9065e42a62675aefcb139d808c493a0926de8
Subproject commit d4460a3970f70e56248464533448169f81301ce3