diff --git a/DataSpec/DNACommon/CMDL.hpp b/DataSpec/DNACommon/CMDL.hpp index 7810962b2..49c7433fd 100644 --- a/DataSpec/DNACommon/CMDL.hpp +++ b/DataSpec/DNACommon/CMDL.hpp @@ -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 +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 paddingSizes; + paddingSizes.reserve(head.secCount); + + /* Build material sets */ + std::vector matSets; + matSets.reserve(mesh.materialSets.size()); + { + HECL::Frontend::Frontend FE; + for (const std::vector& mset : mesh.materialSets) + { + matSets.emplace_back(); + MaterialSet& targetMSet = matSets.back(); + std::vector 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 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::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; +} + } } diff --git a/DataSpec/DNAMP1/CMDL.hpp b/DataSpec/DNAMP1/CMDL.hpp index 39c456865..ae4d3b8c2 100644 --- a/DataSpec/DNAMP1/CMDL.hpp +++ b/DataSpec/DNAMP1/CMDL.hpp @@ -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(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(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(tempOut, inPath, mesh)) + return false; + return true; + } }; } diff --git a/DataSpec/DNAMP1/CMDLMaterials.cpp b/DataSpec/DNAMP1/CMDLMaterials.cpp index 749287874..d556b92b6 100644 --- a/DataSpec/DNAMP1/CMDLMaterials.cpp +++ b/DataSpec/DNAMP1/CMDLMaterials.cpp @@ -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& iprops, + const std::vector& 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 gameArgs; + gameArgs.reserve(inst.getChildCount() - 1); + for (int i=1 ; i& 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()); } } diff --git a/DataSpec/DNAMP1/CMDLMaterials.hpp b/DataSpec/DNAMP1/CMDLMaterials.hpp index fea1e3268..b8fb8880a 100644 --- a/DataSpec/DNAMP1/CMDLMaterials.hpp +++ b/DataSpec/DNAMP1/CMDLMaterials.hpp @@ -24,9 +24,10 @@ struct MaterialSet : BigDNA Vector textureIDs; Value materialCount = 0; Vector 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& gameArgs); }; Vector 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 textureCount = 0; + Vector textureIdxs; + + Vector indTexSlot; + + Value uvAnimsSize = 4; + Value uvAnimsCount = 0; + Vector 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& iprops, + const std::vector& texPaths); + }; + Vector materials; +}; + } } diff --git a/DataSpec/SpecMP1.cpp b/DataSpec/SpecMP1.cpp index 667d50025..a4eb3f67c 100644 --- a/DataSpec/SpecMP1.cpp +++ b/DataSpec/SpecMP1.cpp @@ -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()); diff --git a/hecl b/hecl index 0bf9065e4..d4460a397 160000 --- a/hecl +++ b/hecl @@ -1 +1 @@ -Subproject commit 0bf9065e42a62675aefcb139d808c493a0926de8 +Subproject commit d4460a3970f70e56248464533448169f81301ce3