#include "CMDL.hpp" #include "../DNAMP1/CMDLMaterials.hpp" #include "../DNAMP1/CSKR.hpp" #include "../DNAMP1/MREA.hpp" #include "../DNAMP2/CMDLMaterials.hpp" #include "../DNAMP2/CSKR.hpp" #include "../DNAMP3/CMDLMaterials.hpp" #include "../DNAMP3/CSKR.hpp" #include "zeus/CAABox.hpp" #include "hecl/Blender/Connection.hpp" namespace DataSpec::DNACMDL { template void GetVertexAttributes(const MaterialSet& matSet, std::vector& attributesOut) { attributesOut.clear(); attributesOut.reserve(matSet.materials.size()); for (const typename MaterialSet::Material& mat : matSet.materials) { const typename MaterialSet::Material::VAFlags& vaFlags = mat.getVAFlags(); attributesOut.emplace_back(); VertexAttributes& va = attributesOut.back(); va.pos = vaFlags.position(); va.norm = vaFlags.normal(); va.color0 = vaFlags.color0(); va.color1 = vaFlags.color1(); if ((va.uvs[0] = vaFlags.tex0())) ++va.uvCount; if ((va.uvs[1] = vaFlags.tex1())) ++va.uvCount; if ((va.uvs[2] = vaFlags.tex2())) ++va.uvCount; if ((va.uvs[3] = vaFlags.tex3())) ++va.uvCount; if ((va.uvs[4] = vaFlags.tex4())) ++va.uvCount; if ((va.uvs[5] = vaFlags.tex5())) ++va.uvCount; if ((va.uvs[6] = vaFlags.tex6())) ++va.uvCount; va.pnMtxIdx = vaFlags.pnMatIdx(); if ((va.texMtxIdx[0] = vaFlags.tex0MatIdx())) ++va.texMtxIdxCount; if ((va.texMtxIdx[1] = vaFlags.tex1MatIdx())) ++va.texMtxIdxCount; if ((va.texMtxIdx[2] = vaFlags.tex2MatIdx())) ++va.texMtxIdxCount; if ((va.texMtxIdx[3] = vaFlags.tex3MatIdx())) ++va.texMtxIdxCount; if ((va.texMtxIdx[4] = vaFlags.tex4MatIdx())) ++va.texMtxIdxCount; if ((va.texMtxIdx[5] = vaFlags.tex5MatIdx())) ++va.texMtxIdxCount; if ((va.texMtxIdx[6] = vaFlags.tex6MatIdx())) ++va.texMtxIdxCount; va.shortUVs = mat.getFlags().lightmapUVArray(); } } template void ReadMaterialSetToBlender_1_2(hecl::blender::PyOutStream& os, const MaterialSet& matSet, const PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, unsigned setIdx) { /* Texmaps */ os << "texmap_list = []\n"; for (const UniqueID32& tex : matSet.head.textureIDs) { std::string texName = pakRouter.getBestEntryName(tex); const nod::Node* node; const typename PAKRouter::EntryType* texEntry = pakRouter.lookupEntry(tex, &node); hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry); if (!txtrPath.isNone()) { txtrPath.makeDirChain(false); PAKEntryReadStream rs = texEntry->beginReadStream(*node); TXTR::Extract(rs, txtrPath); } hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, tex); hecl::SystemUTF8Conv resPathView(resPath); os.format("if '%s' in bpy.data.textures:\n" " image = bpy.data.images['%s']\n" " texture = bpy.data.textures[image.name]\n" "else:\n" " image = bpy.data.images.load('''//%s''')\n" " image.name = '%s'\n" " texture = bpy.data.textures.new(image.name, 'IMAGE')\n" " texture.image = image\n" "texmap_list.append(texture)\n" "\n", texName.c_str(), texName.c_str(), resPathView.c_str(), texName.c_str()); } unsigned m=0; for (const typename MaterialSet::Material& mat : matSet.materials) { MaterialSet::ConstructMaterial(os, mat, setIdx, m++); os << "materials.append(new_material)\n"; } } template void ReadMaterialSetToBlender_3(hecl::blender::PyOutStream& os, const MaterialSet& matSet, const PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, unsigned setIdx) { unsigned m=0; for (const typename MaterialSet::Material& mat : matSet.materials) { MaterialSet::ConstructMaterial(os, pakRouter, entry, mat, setIdx, m++); os << "materials.append(new_material)\n"; } } template void ReadMaterialSetToBlender_3, DNAMP3::MaterialSet> (hecl::blender::PyOutStream& os, const DNAMP3::MaterialSet& matSet, const PAKRouter& pakRouter, const PAKRouter::EntryType& entry, unsigned setIdx); class DLReader { public: /* Class used for splitting verts with shared positions but different skinning matrices */ class ExtraVertTracker { std::map>> m_extraVerts; atUint16 m_maxBasePos = 0; atUint16 m_nextOverPos = 1; public: atInt16 addPosSkinPair(atUint16 pos, atInt16 skin) { m_maxBasePos = std::max(m_maxBasePos, pos); auto search = m_extraVerts.find(pos); if (search == m_extraVerts.end()) { m_extraVerts[pos] = {std::make_pair(skin, 0)}; return skin; } std::vector>& vertTrack = search->second; for (const std::pair& s : vertTrack) if (s.first == skin) return vertTrack.front().first; vertTrack.push_back(std::make_pair(skin, m_nextOverPos++)); return vertTrack.front().first; } template void sendAdditionalVertsToBlender(hecl::blender::PyOutStream& os, const RigPair& rp) const { atUint32 nextVert = 1; while (nextVert < m_nextOverPos) { for (const std::pair>>& ev : m_extraVerts) { for (const std::pair& se : ev.second) { if (se.second == nextVert) { os.format("bm.verts.ensure_lookup_table()\n" "orig_vert = bm.verts[%u]\n" "vert = bm.verts.new(orig_vert.co)\n", ev.first); rp.first->weightVertex(os, *rp.second, se.first); ++nextVert; } } } } } atUint16 lookupVertIdx(atUint16 pos, atInt16 skin) const { auto search = m_extraVerts.find(pos); if (search == m_extraVerts.end()) return -1; const std::vector>& vertTrack = search->second; if (vertTrack.front().first == skin) return pos; for (auto it=vertTrack.begin()+1 ; it!=vertTrack.end() ; ++it) if (it->first == skin) return m_maxBasePos + it->second; return -1; } }; private: const VertexAttributes& m_va; std::unique_ptr m_dl; size_t m_dlSize; ExtraVertTracker& m_evt; const atInt16* m_bankIn; atUint8* m_cur; atUint16 readVal(GX::AttrType type) { atUint16 retval = 0; switch (type) { case GX::DIRECT: case GX::INDEX8: if ((m_cur - m_dl.get()) >= intptr_t(m_dlSize)) return 0; retval = *m_cur; ++m_cur; break; case GX::INDEX16: if ((m_cur - m_dl.get() + 1) >= intptr_t(m_dlSize)) return 0; retval = hecl::SBig(*(atUint16*)m_cur); m_cur += 2; break; default: break; } return retval; } public: DLReader(const VertexAttributes& va, std::unique_ptr&& dl, size_t dlSize, ExtraVertTracker& evt, const atInt16* bankIn=nullptr) : m_va(va), m_dl(std::move(dl)), m_dlSize(dlSize), m_evt(evt), m_bankIn(bankIn) { m_cur = m_dl.get(); } operator bool() { return ((m_cur - m_dl.get()) < intptr_t(m_dlSize)) && *m_cur; } GX::Primitive readPrimitive() { return GX::Primitive(*m_cur++ & 0xf8); } GX::Primitive readPrimitiveAndVat(unsigned& vatOut) { atUint8 val = *m_cur++; vatOut = val & 0x7; return GX::Primitive(val & 0xf8); } atUint16 readVertCount() { atUint16 retval = hecl::SBig(*(atUint16*)m_cur); m_cur += 2; return retval; } struct DLPrimVert { atUint16 pos = 0; atUint16 norm = 0; atUint16 color[2] = {0}; atUint16 uvs[7] = {0}; atUint8 pnMtxIdx = 0; atUint8 texMtxIdx[7] = {0}; }; DLPrimVert readVert(bool peek=false) { atUint8* bakCur = m_cur; DLPrimVert retval; retval.pnMtxIdx = readVal(m_va.pnMtxIdx); retval.texMtxIdx[0] = readVal(m_va.texMtxIdx[0]); retval.texMtxIdx[1] = readVal(m_va.texMtxIdx[1]); retval.texMtxIdx[2] = readVal(m_va.texMtxIdx[2]); retval.texMtxIdx[3] = readVal(m_va.texMtxIdx[3]); retval.texMtxIdx[4] = readVal(m_va.texMtxIdx[4]); retval.texMtxIdx[5] = readVal(m_va.texMtxIdx[5]); retval.texMtxIdx[6] = readVal(m_va.texMtxIdx[6]); if (m_bankIn) { atUint16 posIdx = readVal(m_va.pos); atUint8 mtxIdx = retval.pnMtxIdx / 3; atInt16 skinIdx = -1; if (mtxIdx < 10) skinIdx = m_bankIn[mtxIdx]; retval.pos = m_evt.lookupVertIdx(posIdx, skinIdx); } else retval.pos = readVal(m_va.pos); retval.norm = readVal(m_va.norm); retval.color[0] = readVal(m_va.color0); retval.color[1] = readVal(m_va.color1); retval.uvs[0] = readVal(m_va.uvs[0]); retval.uvs[1] = readVal(m_va.uvs[1]); retval.uvs[2] = readVal(m_va.uvs[2]); retval.uvs[3] = readVal(m_va.uvs[3]); retval.uvs[4] = readVal(m_va.uvs[4]); retval.uvs[5] = readVal(m_va.uvs[5]); retval.uvs[6] = readVal(m_va.uvs[6]); if (peek) m_cur = bakCur; return retval; } void preReadMaxIdxs(DLPrimVert& out) { atUint8* bakCur = m_cur; while (*this) { readPrimitive(); atUint16 vc = readVertCount(); for (atUint16 v=0 ; v& skinOut) { atUint8* bakCur = m_cur; while (*this) { readPrimitive(); atUint16 vc = readVertCount(); for (atUint16 v=0 ; v 0:\n" " edge.smooth = False\n" "\n" "bm.to_mesh(mesh)\n" "bm.free()\n" "\n" "# Remove redundant materials\n" "present_mats = set()\n" "for poly in mesh.polygons:\n" " present_mats.add(poly.material_index)\n" "for mat_idx in reversed(range(len(mesh.materials))):\n" " if mat_idx not in present_mats:\n" " mesh.materials.pop(index=mat_idx, update_data=True)\n" "\n" "mesh.update()\n" "\n"; } template atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::IStreamReader& reader, PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const RigPair& rp, bool shortNormals, bool shortUVs, std::vector& vertAttribs, int meshIdx, atUint32 secCount, atUint32 matSetCount, const atUint32* secSizes, atUint32 surfaceCount) { os << "# Begin bmesh\n" "bm = bmesh.new()\n" "\n" "# Overdraw-tracking\n" "od_list = []\n" "\n" "orig_pidx_lay = bm.verts.layers.int.new('CMDLOriginalPosIdxs')\n" "orig_nidx_lay = bm.loops.layers.int.new('CMDLOriginalNormIdxs')\n"; if (rp.first) os << "dvert_lay = bm.verts.layers.deform.verify()\n"; /* Pre-read pass to determine maximum used vert indices */ atUint32 matSecCount = 0; if (matSetCount) matSecCount = MaterialSet::OneSection() ? 1 : matSetCount; bool visitedDLOffsets = false; atUint32 lastDlSec = secCount; atUint64 afterHeaderPos = reader.position(); DLReader::DLPrimVert maxIdxs; std::vector skinIndices; DLReader::ExtraVertTracker extraTracker; for (size_t s=0 ; sgetMatrixBank(sHead.skinMatrixBankIdx()); /* Do max index pre-read */ atUint32 realDlSize = secSizes[s] - (reader.position() - secStart); DLReader dl(vertAttribs[sHead.matIdx], reader.readUBytes(realDlSize), realDlSize, extraTracker, bankIn); if (SurfaceHeader::UseMatrixSkinning() && rp.first) dl.preReadMaxIdxs(maxIdxs, skinIndices); else dl.preReadMaxIdxs(maxIdxs); } } } if (s < secCount - 1) reader.seek(secStart + secSizes[s], athena::Begin); } reader.seek(afterHeaderPos, athena::Begin); visitedDLOffsets = false; unsigned createdUVLayers = 0; unsigned surfIdx = 0; for (size_t s=0 ; sweightVertex(os, *rp.second, skinIndices[i]); else if (!SurfaceHeader::UseMatrixSkinning()) rp.first->weightVertex(os, *rp.second, i); } } if (rp.first && SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty()) extraTracker.sendAdditionalVertsToBlender(os, rp); break; } case 1: { /* Normals */ os << "norm_list = []\n"; if (shortNormals) { size_t normCount = secSizes[s] / 6; for (size_t i=0 ; igetMatrixBank(sHead.skinMatrixBankIdx()); os.format("materials[%u].pass_index = %u\n", sHead.matIdx, surfIdx++); if (matUVCount > createdUVLayers) { for (unsigned l=createdUVLayers ; l= vertCount - 3); /* Advance one prim vert */ primVerts[c%3] = dl.readVert(peek); ++c; } } else if (ptype == GX::TRIANGLES) { for (int v=0 ; v= vertCount) break; /* Advance 3 Prim Verts */ for (int pv=0 ; pv<3 ; ++pv) primVerts[pv] = dl.readVert(); } } else if (ptype == GX::TRIANGLEFAN) { ++c; for (int v=0 ; v= vertCount) break; /* Advance one prim vert */ primVerts[(c+2)%3] = dl.readVert(); ++c; } } os << "\n"; } } } } if (s < secCount - 1) reader.seek(secStart + secSizes[s], athena::Begin); } /* Finish Mesh */ FinishBlenderMesh(os, matSetCount, meshIdx); if (rp.first) rp.second->sendVertexGroupsToBlender(os); return lastDlSec; } template bool ReadCMDLToBlender(hecl::blender::Connection& conn, athena::io::IStreamReader& reader, PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec, const RigPair& rp) { Header head; head.read(reader); if (head.magic != 0xDEADBABE) { LogDNACommon.report(logvisor::Error, "invalid CMDL magic"); return false; } if (head.version != Version) { LogDNACommon.report(logvisor::Error, "invalid CMDL version"); return false; } /* Open Py Stream and read sections */ hecl::blender::PyOutStream os = conn.beginPythonOut(true); os.format("import bpy\n" "import bmesh\n" "\n" "bpy.context.scene.name = '%s'\n" "bpy.context.scene.hecl_mesh_obj = bpy.context.scene.name\n", pakRouter.getBestEntryName(entry).c_str()); InitGeomBlenderContext(os, dataspec.getMasterShaderPath(), false); MaterialSet::RegisterMaterialProps(os); os << "# Materials\n" "materials = []\n" "\n"; std::vector vertAttribs; ReadGeomSectionsToBlender (os, reader, pakRouter, entry, rp, head.flags.shortNormals(), head.flags.shortUVs(), vertAttribs, -1, head.secCount, head.matSetCount, head.secSizes.data()); return true; } template bool ReadCMDLToBlender, DNAMP1::MaterialSet, std::pair, DNACMDL::SurfaceHeader_1, 2> (hecl::blender::Connection& conn, athena::io::IStreamReader& reader, PAKRouter& pakRouter, const PAKRouter::EntryType& entry, const SpecBase& dataspec, const std::pair& rp); template bool ReadCMDLToBlender, DNAMP2::MaterialSet, std::pair, DNACMDL::SurfaceHeader_2, 4> (hecl::blender::Connection& conn, athena::io::IStreamReader& reader, PAKRouter& pakRouter, const PAKRouter::EntryType& entry, const SpecBase& dataspec, const std::pair& rp); template bool ReadCMDLToBlender, DNAMP3::MaterialSet, std::pair, DNACMDL::SurfaceHeader_3, 4> (hecl::blender::Connection& conn, athena::io::IStreamReader& reader, PAKRouter& pakRouter, const PAKRouter::EntryType& entry, const SpecBase& dataspec, const std::pair& rp); template bool ReadCMDLToBlender, DNAMP3::MaterialSet, std::pair, DNACMDL::SurfaceHeader_3, 5> (hecl::blender::Connection& conn, athena::io::IStreamReader& reader, PAKRouter& pakRouter, const PAKRouter::EntryType& entry, const SpecBase& dataspec, const std::pair& rp); template void NameCMDL(athena::io::IStreamReader& reader, PAKRouter& pakRouter, typename PAKRouter::EntryType& entry, const SpecBase& dataspec) { Header head; head.read(reader); std::string bestName = hecl::Format("CMDL_%s", entry.id.toString().c_str()); /* Pre-read pass to determine maximum used vert indices */ atUint32 matSecCount = 0; if (head.matSetCount) matSecCount = MaterialSet::OneSection() ? 1 : head.matSetCount; atUint32 lastDlSec = head.secCount; for (size_t s=0 ; s, DNAMP1::MaterialSet> (athena::io::IStreamReader& reader, PAKRouter& pakRouter, PAKRouter::EntryType& entry, const SpecBase& dataspec); static void WriteDLVal(athena::io::FileWriter& writer, GX::AttrType type, atUint32 val) { switch (type) { case GX::DIRECT: case GX::INDEX8: writer.writeUByte(atUint8(val)); break; case GX::INDEX16: writer.writeUint16Big(atUint16(val)); break; default: break; } } template bool WriteCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const Mesh& mesh) { Header head; head.magic = 0xDEADBABE; head.version = 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; std::vector setBackends; setBackends.reserve(mset.size()); size_t endOff = 0; atUint32 nextGroupIdx = 0; for (const Material& mat : mset) { std::string diagName = hecl::Format("%s:%s", inPath.getLastComponentUTF8().data(), mat.name.c_str()); hecl::Frontend::IR matIR = FE.compileSource(mat.source, diagName); setBackends.emplace_back(); hecl::Backend::GX& matGX = setBackends.back(); matGX.reset(matIR, FE.getDiagnostics()); atUint32 groupIdx = -1; if (matSets.size() == 1) { for (size_t i=0 ; i surfEndOffs; surfEndOffs.reserve(mesh.surfaces.size()); secSz = mesh.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 Mesh::Surface& surf : mesh.surfaces) { size_t vertSz = matSets.at(0).materials.at(surf.materialIdx).getVAFlags().vertDLSize(); if (surf.verts.size() > 65536) LogDNACommon.report(logvisor::Fatal, "GX DisplayList overflow"); size_t secSz = 67 + surf.verts.size() * vertSz; secSz32 = ROUND_UP_32(secSz); if (secSz32 == 0) secSz32 = 32; head.secSizes.push_back(secSz32); paddingSizes.push_back(secSz32 - secSz); endOff += secSz32; 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; } /* Vertex Positions */ for (const atVec3f& pos : mesh.pos) writer.writeVec3fBig(pos); writer.fill(atUint8(0), *padIt); ++padIt; /* Vertex Normals */ for (const atVec3f& norm : mesh.norm) writer.writeVec3fBig(norm); writer.fill(atUint8(0), *padIt); ++padIt; /* Vertex Colors */ for (const atVec3f& col : mesh.color) { GX::Color qCol(col); qCol.write(writer); } writer.fill(atUint8(0), *padIt); ++padIt; /* UV coords */ for (const atVec2f& uv : mesh.uv) writer.writeVec2fBig(uv); 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 */ GX::Primitive prim; if (mesh.topology == hecl::HMDLTopology::Triangles) prim = GX::TRIANGLES; else if (mesh.topology == hecl::HMDLTopology::TriStrips) prim = GX::TRIANGLESTRIP; else LogDNACommon.report(logvisor::Fatal, "unrecognized mesh output mode"); for (const Mesh::Surface& surf : mesh.surfaces) { const typename MaterialSet::Material::VAFlags& vaFlags = matSets.at(0).materials.at(surf.materialIdx).getVAFlags(); size_t vertSz = vaFlags.vertDLSize(); SurfaceHeader header; header.centroid = surf.centroid; header.matIdx = surf.materialIdx; header.dlSize = 3 + surf.verts.size() * vertSz; header.reflectionNormal = surf.reflectionNormal; header.write(writer); writer.writeUByte(prim); writer.writeUint16Big(surf.verts.size()); for (const Mesh::Surface::Vert& vert : surf.verts) { atUint32 skinIdx = vert.iBankSkin * 3; WriteDLVal(writer, vaFlags.pnMatIdx(), skinIdx); WriteDLVal(writer, vaFlags.tex0MatIdx(), skinIdx); WriteDLVal(writer, vaFlags.tex1MatIdx(), skinIdx); WriteDLVal(writer, vaFlags.tex2MatIdx(), skinIdx); WriteDLVal(writer, vaFlags.tex3MatIdx(), skinIdx); WriteDLVal(writer, vaFlags.tex4MatIdx(), skinIdx); WriteDLVal(writer, vaFlags.tex5MatIdx(), skinIdx); WriteDLVal(writer, vaFlags.tex6MatIdx(), skinIdx); WriteDLVal(writer, vaFlags.position(), vert.iPos); WriteDLVal(writer, vaFlags.normal(), vert.iNorm); WriteDLVal(writer, vaFlags.color0(), vert.iColor[0]); WriteDLVal(writer, vaFlags.color1(), vert.iColor[1]); WriteDLVal(writer, vaFlags.tex0(), vert.iUv[0]); WriteDLVal(writer, vaFlags.tex1(), vert.iUv[1]); WriteDLVal(writer, vaFlags.tex2(), vert.iUv[2]); WriteDLVal(writer, vaFlags.tex3(), vert.iUv[3]); WriteDLVal(writer, vaFlags.tex4(), vert.iUv[4]); WriteDLVal(writer, vaFlags.tex5(), vert.iUv[5]); WriteDLVal(writer, vaFlags.tex6(), vert.iUv[6]); } writer.fill(atUint8(0), *padIt); ++padIt; } writer.close(); return true; } template bool WriteCMDL (const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const Mesh& mesh); template bool WriteHMDLCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const Mesh& mesh, hecl::blender::PoolSkinIndex& poolSkinIndex) { 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 + 4 + 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 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 Material& mat : mset) { std::string diagName = hecl::Format("%s:%s", inPath.getLastComponentUTF8().data(), 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) targetMSet.head.addTexture(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::blender::HMDLBuffers bufs = mesh.getHMDLBuffers(false, poolSkinIndex); /* Metadata */ size_t secSz = bufs.m_meta.binarySize(0); 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::blender::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 */ bufs.m_meta.write(writer); 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::blender::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.idxStart = surf.m_start; header.idxCount = surf.m_count; header.skinMtxBankIdx = osurf.skinBankIdx; header.write(writer); writer.fill(atUint8(0), *padIt); ++padIt; } /* Ensure final surface's alignment writes zeros */ writer.seek(-1, athena::Current); writer.writeUByte(0); writer.close(); return true; } template bool WriteHMDLCMDL (const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const Mesh& mesh, hecl::blender::PoolSkinIndex& poolSkinIndex); template bool WriteMREASecs(std::vector>& secsOut, const hecl::ProjectPath& inPath, const std::vector& meshes, zeus::CAABox& fullAABB, std::vector& meshAABBs) { return false; } template bool WriteHMDLMREASecs(std::vector>& secsOut, const hecl::ProjectPath& inPath, const std::vector& meshes, zeus::CAABox& fullAABB, std::vector& meshAABBs) { /* Build material set */ std::vector surfToGlobalMats; { struct MaterialPool { std::vector materials; size_t addMaterial(const Material& mat, bool& newMat) { size_t ret = 0; newMat = false; for (const Material* testMat : materials) { if (mat == *testMat) return ret; ++ret; } materials.push_back(&mat); newMat = true; return ret; } } matPool; size_t surfCount = 0; for (const Mesh& mesh : meshes) surfCount += mesh.surfaces.size(); surfToGlobalMats.reserve(surfCount); MaterialSet matSet; hecl::Frontend::Frontend FE; size_t endOff = 0; std::vector texPaths; for (const Mesh& mesh : meshes) { if (mesh.materialSets.size()) { std::vector meshToGlobalMats; meshToGlobalMats.reserve(mesh.materialSets[0].size()); for (const Material& mat : mesh.materialSets[0]) { bool newMat; size_t idx = matPool.addMaterial(mat, newMat); meshToGlobalMats.push_back(idx); if (!newMat) continue; 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); } std::string diagName = hecl::Format("%s:%s", inPath.getLastComponentUTF8().data(), mat.name.c_str()); matSet.materials.emplace_back(FE, diagName, mat, mat.iprops, texPaths); endOff = matSet.materials.back().binarySize(endOff); matSet.head.addMaterialEndOff(endOff); } for (const Mesh::Surface& surf : mesh.surfaces) surfToGlobalMats.push_back(meshToGlobalMats[surf.materialIdx]); } } for (const hecl::ProjectPath& path : texPaths) matSet.head.addTexture(path); secsOut.emplace_back(matSet.binarySize(0), 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); matSet.write(w); } /* Iterate meshes */ auto matIt = surfToGlobalMats.cbegin(); int meshIdx = 0; for (const Mesh& mesh : meshes) { 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::blender::PoolSkinIndex poolSkinIndex; hecl::blender::HMDLBuffers bufs = mesh.getHMDLBuffers(true, poolSkinIndex); std::vector surfEndOffs; surfEndOffs.reserve(bufs.m_surfaces.size()); size_t endOff = 0; for (const hecl::blender::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::blender::HMDLBuffers::Surface& surf : bufs.m_surfaces) { const Mesh::Surface& osurf = surf.m_origSurf; SurfaceHeader header; header.centroid = meshXf * zeus::CVector3f(osurf.centroid); header.matIdx = *matIt++; 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; } template bool WriteHMDLMREASecs (std::vector>& secsOut, const hecl::ProjectPath& inPath, const std::vector& meshes, zeus::CAABox& fullAABB, std::vector& meshAABBs); void SurfaceHeader_1::read(athena::io::IStreamReader& reader) { /* centroid */ centroid = reader.readVec3fBig(); /* matIdx */ matIdx = reader.readUint32Big(); /* qDiv */ qDiv = reader.readUint16Big(); /* dlSize */ dlSize = reader.readUint16Big(); /* idxStart */ idxStart = reader.readUint32Big(); /* idxCount */ idxCount = reader.readUint32Big(); /* aabbSz */ aabbSz = reader.readUint32Big(); /* reflectionNormal */ reflectionNormal = reader.readVec3fBig(); /* aabb */ size_t remAABB = aabbSz; if (remAABB >= 24) { aabb[0] = reader.readVec3fBig(); aabb[1] = reader.readVec3fBig(); remAABB -= 24; } reader.seek(remAABB, athena::Current); /* align */ reader.seekAlign32(); } void SurfaceHeader_1::write(athena::io::IStreamWriter& writer) const { /* centroid */ writer.writeVec3fBig(centroid); /* matIdx */ writer.writeUint32Big(matIdx); /* qDiv */ writer.writeUint16Big(qDiv); /* dlSize */ writer.writeUint16Big(dlSize); /* idxStart */ writer.writeUint32Big(idxStart); /* idxCount */ writer.writeUint32Big(idxCount); /* aabbSz */ writer.writeUint32Big(aabbSz ? 24 : 0); /* reflectionNormal */ writer.writeVec3fBig(reflectionNormal); /* aabb */ if (aabbSz) { writer.writeVec3fBig(aabb[0]); writer.writeVec3fBig(aabb[1]); } /* align */ writer.seekAlign32(); } size_t SurfaceHeader_1::binarySize(size_t __isz) const { __isz += (aabbSz ? 24 : 0); __isz += 44; __isz = (__isz + 31) & ~31; return __isz; } void SurfaceHeader_2::read(athena::io::IStreamReader& reader) { /* centroid */ centroid = reader.readVec3fBig(); /* matIdx */ matIdx = reader.readUint32Big(); /* qDiv */ qDiv = reader.readUint16Big(); /* dlSize */ dlSize = reader.readUint16Big(); /* idxStart */ idxStart = reader.readUint32Big(); /* idxCount */ idxCount = reader.readUint32Big(); /* aabbSz */ aabbSz = reader.readUint32Big(); /* reflectionNormal */ reflectionNormal = reader.readVec3fBig(); /* skinMtxBankIdx */ skinMtxBankIdx = reader.readInt16Big(); /* surfaceGroup */ surfaceGroup = reader.readUint16Big(); /* aabb */ size_t remAABB = aabbSz; if (remAABB >= 24) { aabb[0] = reader.readVec3fBig(); aabb[1] = reader.readVec3fBig(); remAABB -= 24; } reader.seek(remAABB, athena::Current); /* align */ reader.seekAlign32(); } void SurfaceHeader_2::write(athena::io::IStreamWriter& writer) const { /* centroid */ writer.writeVec3fBig(centroid); /* matIdx */ writer.writeUint32Big(matIdx); /* qDiv */ writer.writeUint16Big(qDiv); /* dlSize */ writer.writeUint16Big(dlSize); /* idxStart */ writer.writeUint32Big(idxStart); /* idxCount */ writer.writeUint32Big(idxCount); /* aabbSz */ writer.writeUint32Big(aabbSz ? 24 : 0); /* reflectionNormal */ writer.writeVec3fBig(reflectionNormal); /* skinMtxBankIdx */ writer.writeInt16Big(skinMtxBankIdx); /* surfaceGroup */ writer.writeUint16Big(surfaceGroup); /* aabb */ if (aabbSz) { writer.writeVec3fBig(aabb[0]); writer.writeVec3fBig(aabb[1]); } /* align */ writer.seekAlign32(); } size_t SurfaceHeader_2::binarySize(size_t __isz) const { __isz += (aabbSz ? 24 : 0); __isz += 48; __isz = (__isz + 31) & ~31; return __isz; } void SurfaceHeader_3::read(athena::io::IStreamReader& reader) { /* centroid */ centroid = reader.readVec3fBig(); /* matIdx */ matIdx = reader.readUint32Big(); /* qDiv */ qDiv = reader.readUint16Big(); /* dlSize */ dlSize = reader.readUint16Big(); /* idxStart */ idxStart = reader.readUint32Big(); /* idxCount */ idxCount = reader.readUint32Big(); /* aabbSz */ aabbSz = reader.readUint32Big(); /* reflectionNormal */ reflectionNormal = reader.readVec3fBig(); /* skinMtxBankIdx */ skinMtxBankIdx = reader.readInt16Big(); /* surfaceGroup */ surfaceGroup = reader.readUint16Big(); /* aabb */ size_t remAABB = aabbSz; if (remAABB >= 24) { aabb[0] = reader.readVec3fBig(); aabb[1] = reader.readVec3fBig(); remAABB -= 24; } reader.seek(remAABB, athena::Current); /* unk3 */ unk3 = reader.readUByte(); /* align */ reader.seekAlign32(); } void SurfaceHeader_3::write(athena::io::IStreamWriter& writer) const { /* centroid */ writer.writeVec3fBig(centroid); /* matIdx */ writer.writeUint32Big(matIdx); /* qDiv */ writer.writeUint16Big(qDiv); /* dlSize */ writer.writeUint16Big(dlSize); /* idxStart */ writer.writeUint32Big(idxStart); /* idxCount */ writer.writeUint32Big(idxCount); /* aabbSz */ writer.writeUint32Big(aabbSz ? 24 : 0); /* reflectionNormal */ writer.writeVec3fBig(reflectionNormal); /* skinMtxBankIdx */ writer.writeInt16Big(skinMtxBankIdx); /* surfaceGroup */ writer.writeUint16Big(surfaceGroup); /* aabb */ if (aabbSz) { writer.writeVec3fBig(aabb[0]); writer.writeVec3fBig(aabb[1]); } /* unk3 */ writer.writeUByte(unk3); /* align */ writer.seekAlign32(); } size_t SurfaceHeader_3::binarySize(size_t __isz) const { __isz += (aabbSz ? 24 : 0); __isz += 49; __isz = (__isz + 31) & ~31; return __isz; } }