#include "DataSpec/DNACommon/CMDL.hpp" #include #include "DataSpec/DNAMP1/CMDLMaterials.hpp" #include "DataSpec/DNAMP1/CSKR.hpp" #include "DataSpec/DNAMP1/MREA.hpp" #include "DataSpec/DNAMP2/CMDLMaterials.hpp" #include "DataSpec/DNAMP2/CSKR.hpp" #include "DataSpec/DNAMP3/CMDLMaterials.hpp" #include "DataSpec/DNAMP3/CSKR.hpp" #include #include #include 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(FMT_STRING( "if '{}' in bpy.data.images:\n" " image = bpy.data.images['{}']\n" "else:\n" " image = bpy.data.images.load('''//{}''')\n" " image.name = '{}'\n" "texmap_list.append(image)\n" "\n"), texName, texName, resPathView, texName); } 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 atUint32 sendAdditionalVertsToBlender(hecl::blender::PyOutStream& os, const RigPair& rp, atUint32 baseVert) const { atUint32 addedVerts = 0; atUint32 nextVert = 1; while (nextVert < m_nextOverPos) { for (const auto& [ev, evVec] : m_extraVerts) { for (const std::pair& se : evVec) { if (se.second == nextVert) { os.format(FMT_STRING( "bm.verts.ensure_lookup_table()\n" "orig_vert = bm.verts[{}]\n" "vert = bm.verts.new(orig_vert.co)\n"), ev + baseVert); rp.first.second->weightVertex(os, *rp.second.second, se.first); ++nextVert; ++addedVerts; } } } } return addedVerts; } 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(); } explicit operator bool() const { 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 < vc; ++v) { atUint16 val; val = readVal(m_va.pnMtxIdx); out.pnMtxIdx = std::max(out.pnMtxIdx, atUint8(val)); val = readVal(m_va.texMtxIdx[0]); out.texMtxIdx[0] = std::max(out.texMtxIdx[0], atUint8(val)); val = readVal(m_va.texMtxIdx[1]); out.texMtxIdx[1] = std::max(out.texMtxIdx[1], atUint8(val)); val = readVal(m_va.texMtxIdx[2]); out.texMtxIdx[2] = std::max(out.texMtxIdx[2], atUint8(val)); val = readVal(m_va.texMtxIdx[3]); out.texMtxIdx[3] = std::max(out.texMtxIdx[3], atUint8(val)); val = readVal(m_va.texMtxIdx[4]); out.texMtxIdx[4] = std::max(out.texMtxIdx[4], atUint8(val)); val = readVal(m_va.texMtxIdx[5]); out.texMtxIdx[5] = std::max(out.texMtxIdx[5], atUint8(val)); val = readVal(m_va.texMtxIdx[6]); out.texMtxIdx[6] = std::max(out.texMtxIdx[6], atUint8(val)); val = readVal(m_va.pos); out.pos = std::max(out.pos, val); val = readVal(m_va.norm); out.norm = std::max(out.norm, val); val = readVal(m_va.color0); out.color[0] = std::max(out.color[0], val); val = readVal(m_va.color1); out.color[1] = std::max(out.color[1], val); val = readVal(m_va.uvs[0]); out.uvs[0] = std::max(out.uvs[0], val); val = readVal(m_va.uvs[1]); out.uvs[1] = std::max(out.uvs[1], val); val = readVal(m_va.uvs[2]); out.uvs[2] = std::max(out.uvs[2], val); val = readVal(m_va.uvs[3]); out.uvs[3] = std::max(out.uvs[3], val); val = readVal(m_va.uvs[4]); out.uvs[4] = std::max(out.uvs[4], val); val = readVal(m_va.uvs[5]); out.uvs[5] = std::max(out.uvs[5], val); val = readVal(m_va.uvs[6]); out.uvs[6] = std::max(out.uvs[6], val); } } m_cur = bakCur; } void preReadMaxIdxs(DLPrimVert& out, std::vector& skinOut) { atUint8* bakCur = m_cur; while (*this) { readPrimitive(); atUint16 vc = readVertCount(); for (atUint16 v = 0; v < vc; ++v) { atUint16 val; atUint8 pnMtxVal = readVal(m_va.pnMtxIdx); out.pnMtxIdx = std::max(out.pnMtxIdx, pnMtxVal); val = readVal(m_va.texMtxIdx[0]); out.texMtxIdx[0] = std::max(out.texMtxIdx[0], atUint8(val)); val = readVal(m_va.texMtxIdx[1]); out.texMtxIdx[1] = std::max(out.texMtxIdx[1], atUint8(val)); val = readVal(m_va.texMtxIdx[2]); out.texMtxIdx[2] = std::max(out.texMtxIdx[2], atUint8(val)); val = readVal(m_va.texMtxIdx[3]); out.texMtxIdx[3] = std::max(out.texMtxIdx[3], atUint8(val)); val = readVal(m_va.texMtxIdx[4]); out.texMtxIdx[4] = std::max(out.texMtxIdx[4], atUint8(val)); val = readVal(m_va.texMtxIdx[5]); out.texMtxIdx[5] = std::max(out.texMtxIdx[5], atUint8(val)); val = readVal(m_va.texMtxIdx[6]); out.texMtxIdx[6] = std::max(out.texMtxIdx[6], atUint8(val)); atUint16 posVal = readVal(m_va.pos); out.pos = std::max(out.pos, posVal); val = readVal(m_va.norm); out.norm = std::max(out.norm, val); val = readVal(m_va.color0); out.color[0] = std::max(out.color[0], val); val = readVal(m_va.color1); out.color[1] = std::max(out.color[1], val); val = readVal(m_va.uvs[0]); out.uvs[0] = std::max(out.uvs[0], val); val = readVal(m_va.uvs[1]); out.uvs[1] = std::max(out.uvs[1], val); val = readVal(m_va.uvs[2]); out.uvs[2] = std::max(out.uvs[2], val); val = readVal(m_va.uvs[3]); out.uvs[3] = std::max(out.uvs[3], val); val = readVal(m_va.uvs[4]); out.uvs[4] = std::max(out.uvs[4], val); val = readVal(m_va.uvs[5]); out.uvs[5] = std::max(out.uvs[5], val); val = readVal(m_va.uvs[6]); out.uvs[6] = std::max(out.uvs[6], val); atInt16 skinIdx = m_bankIn[pnMtxVal / 3]; skinOut[posVal] = m_evt.addPosSkinPair(posVal, skinIdx); } } m_cur = bakCur; } }; void InitGeomBlenderContext(hecl::blender::PyOutStream& os, const hecl::ProjectPath& masterShaderPath) { os << "import math\n" "from mathutils import Vector\n" "\n" "# Clear Scene\n" "if len(bpy.data.collections):\n" " bpy.data.collections.remove(bpy.data.collections[0])\n" "\n" "def loop_from_facevert(bm, face, vert_idx):\n" " for loop in face.loops:\n" " if loop.vert[bm.verts.layers.int['CMDLOriginalPosIdxs']] == vert_idx:\n" " return loop\n" "\n" "def loops_from_edgevert(bm, edge, vert):\n" " ret = []\n" " for face in edge.link_faces:\n" " for loop in face.loops:\n" " if loop.vert == vert:\n" " ret.append(loop)\n" " return ret\n" "\n" "def add_triangle(bm, vert_seq, vert_indices, norm_seq, norm_indices, mat_nr, od_list, two_face_vert):\n" " if len(set(vert_indices)) != 3:\n" " return None, None\n" "\n" " ret_mesh = bm\n" " vert_seq.ensure_lookup_table()\n" " verts = [vert_seq[i] for i in vert_indices]\n" "\n" " # Try getting existing face\n" " face = bm.faces.get(verts)\n" "\n" " if face is not None and face.material_index != mat_nr: # Same poly, new material\n" " # Overdraw detected; track copy\n" " od_entry = None\n" " for entry in od_list:\n" " if entry['material'] == mat_nr:\n" " od_entry = entry\n" " if od_entry is None:\n" " bm_cpy = bm.copy()\n" " od_entry = {'material':mat_nr, 'bm':bm_cpy}\n" " bmesh.ops.delete(od_entry['bm'], geom=od_entry['bm'].faces, context='FACES_ONLY')\n" " od_list.append(od_entry)\n" " od_entry['bm'].verts.ensure_lookup_table()\n" " verts = [od_entry['bm'].verts[i] for i in vert_indices]\n" " face = od_entry['bm'].faces.get(verts)\n" " if face is None:\n" " face = od_entry['bm'].faces.new(verts)\n" " else: # Probably a double-sided surface\n" " verts = [od_entry['bm'].verts[i + two_face_vert] for i in vert_indices]\n" " face = od_entry['bm'].faces.get(verts)\n" " if face is None:\n" " face = od_entry['bm'].faces.new(verts)\n" " ret_mesh = od_entry['bm']\n" "\n" " elif face is not None: # Same material, probably double-sided\n" " verts = [vert_seq[i + two_face_vert] for i in vert_indices]\n" " face = bm.faces.get(verts)\n" " if face is None:\n" " face = bm.faces.new(verts)\n" "\n" " else: # Make totally new face\n" " face = bm.faces.new(verts)\n" "\n" " for i in range(3):\n" " face.verts[i][ret_mesh.verts.layers.int['CMDLOriginalPosIdxs']] = vert_indices[i]\n" " face.loops[i][ret_mesh.loops.layers.int['CMDLOriginalNormIdxs']] = norm_indices[i]\n" " face.material_index = mat_nr\n" " face.smooth = True\n" "\n" " return face, ret_mesh\n" "\n" "def expand_lightmap_triangle(lightmap_tri_tracker, uva, uvb, uvc):\n" " result = ([uva[0],uva[1]], [uvb[0],uvb[1]], [uvc[0],uvc[1]])\n" " inst = 0\n" " if uva in lightmap_tri_tracker:\n" " inst = lightmap_tri_tracker[uva]\n" " lightmap_tri_tracker[uva] = inst + 1\n" " if uva == uvb:\n" " result[1][0] += 0.005\n" " if uva == uvc:\n" " result[2][1] -= 0.005\n" " if inst & 0x1 and uva == uvb and uva == uvc:\n" " result[0][0] += 0.005\n" " result[0][1] -= 0.005\n" " return result\n" "\n"; /* Link master shader library */ os.format(FMT_STRING( "# Master shader library\n" "with bpy.data.libraries.load('{}', link=True, relative=True) as (data_from, data_to):\n" " data_to.node_groups = data_from.node_groups\n" "\n"), masterShaderPath.getAbsolutePathUTF8()); } void FinishBlenderMesh(hecl::blender::PyOutStream& os, unsigned matSetCount, int meshIdx) { os << "if 'Render' not in bpy.data.collections:\n" " coll = bpy.data.collections.new('Render')\n" " bpy.context.scene.collection.children.link(coll)\n" "else:\n" " coll = bpy.data.collections['Render']\n"; if (meshIdx < 0) { os << "mesh = bpy.data.meshes.new(bpy.context.scene.name)\n" "obj = bpy.data.objects.new(mesh.name, mesh)\n" "obj.show_transparent = True\n" "coll.objects.link(obj)\n"; os.format(FMT_STRING("mesh.hecl_material_count = {}\n"), matSetCount); } else { os.format(FMT_STRING("mesh = bpy.data.meshes.new(bpy.context.scene.name + '_{:03d}')\n"), meshIdx); os << "obj = bpy.data.objects.new(mesh.name, mesh)\n" "obj.show_transparent = True\n" "coll.objects.link(obj)\n"; os.format(FMT_STRING("mesh.hecl_material_count = {}\n"), matSetCount); } os << "mesh.use_auto_smooth = True\n" "mesh.auto_smooth_angle = math.pi\n" "\n" "for material in materials:\n" " mesh.materials.append(material)\n" "\n" "# Merge OD meshes\n" "for od_entry in od_list:\n" " vert_dict = [{},{}]\n" "\n" " for vert in od_entry['bm'].verts:\n" " if len(vert.link_faces):\n" " if vert.index >= two_face_vert:\n" " use_vert_dict = vert_dict[1]\n" " else:\n" " use_vert_dict = vert_dict[0]\n" " copy_vert = bm.verts.new(vert.co, vert)\n" " use_vert_dict[vert[od_entry['bm'].verts.layers.int['CMDLOriginalPosIdxs']]] = copy_vert\n" " copy_vert[orig_pidx_lay] = vert[od_entry['bm'].verts.layers.int['CMDLOriginalPosIdxs']]\n" "\n" " for face in od_entry['bm'].faces:\n" " if face.verts[0].index >= two_face_vert:\n" " use_vert_dict = vert_dict[1]\n" " else:\n" " use_vert_dict = vert_dict[0]\n" " merge_verts = [use_vert_dict[fv[od_entry['bm'].verts.layers.int['CMDLOriginalPosIdxs']]] for fv in " "face.verts]\n" " try:\n" " if bm.faces.get(merge_verts) is not None:\n" " continue\n" " except:\n" " continue\n" " merge_face = bm.faces.new(merge_verts)\n" " for i in range(len(face.loops)):\n" " old = face.loops[i]\n" " new = merge_face.loops[i]\n" " for j in range(len(od_entry['bm'].loops.layers.uv)):\n" " new[bm.loops.layers.uv[j]] = old[od_entry['bm'].loops.layers.uv[j]]\n" " new[orig_nidx_lay] = old[od_entry['bm'].loops.layers.int['CMDLOriginalNormIdxs']]\n" " merge_face.smooth = True\n" " merge_face.material_index = face.material_index\n" "\n" " od_entry['bm'].free()\n" "\n" "verts_to_del = []\n" "for v in bm.verts:\n" " if len(v.link_faces) == 0:\n" " verts_to_del.append(v)\n" "bmesh.ops.delete(bm, geom=verts_to_del, context='VERTS')\n" "\n" "for edge in bm.edges:\n" " if edge.is_manifold:\n" " pass_count = 0\n" " for vert in edge.verts:\n" " loops = loops_from_edgevert(bm, edge, vert)\n" " norm0 = Vector(norm_list[loops[0][orig_nidx_lay]])\n" " norm1 = Vector(norm_list[loops[1][orig_nidx_lay]])\n" " if norm0.dot(norm1) < 0.9:\n" " pass_count += 1\n" " if pass_count > 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)\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" "\n" "lightmap_tri_tracker = {}\n"; if (rp.first.second) 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 (atUint32 s = 0; s < lastDlSec; ++s) { atUint64 secStart = reader.position(); if (s < matSecCount) { if (!s) { MaterialSet matSet; matSet.read(reader); matSet.ensureTexturesExtracted(pakRouter); GetVertexAttributes(matSet, vertAttribs); } } else { switch (s - matSecCount) { case 0: { /* Positions */ if (SurfaceHeader::UseMatrixSkinning() && rp.first.second) skinIndices.assign(secSizes[s] / 12, -1); break; } case 1: { /* Normals */ break; } case 2: { /* Colors */ break; } case 3: { /* Float UVs */ break; } case 4: { if (surfaceCount) { /* MP3 MREA case */ visitedDLOffsets = true; lastDlSec = 4 + surfaceCount; } else { /* Short UVs */ if (shortUVs) break; /* DL Offsets (here or next section) */ visitedDLOffsets = true; lastDlSec = s + reader.readUint32Big() + 1; break; } [[fallthrough]]; } default: { if (!visitedDLOffsets) { visitedDLOffsets = true; lastDlSec = s + reader.readUint32Big() + 1; break; } /* GX Display List (surface) */ SurfaceHeader sHead; sHead.read(reader); const atInt16* bankIn = nullptr; if (SurfaceHeader::UseMatrixSkinning() && rp.first.second) bankIn = rp.first.second->getMatrixBank(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.second) dl.preReadMaxIdxs(maxIdxs, skinIndices); else dl.preReadMaxIdxs(maxIdxs); } } } if (s < secCount - 1) { reader.seek(secStart + secSizes[s], athena::SeekOrigin::Begin); } } reader.seek(afterHeaderPos, athena::SeekOrigin::Begin); visitedDLOffsets = false; unsigned createdUVLayers = 0; unsigned surfIdx = 0; for (atUint32 s = 0; s < lastDlSec; ++s) { atUint64 secStart = reader.position(); if (s < matSecCount) { MaterialSet matSet; matSet.read(reader); matSet.readToBlender(os, pakRouter, entry, s); if (!s) GetVertexAttributes(matSet, vertAttribs); } else { switch (s - matSecCount) { case 0: { /* Positions */ atUint32 vertCount = maxIdxs.pos + 1; std::vector positions; positions.reserve(vertCount); for (atUint16 i = 0; i <= maxIdxs.pos; ++i) { positions.push_back(reader.readVec3fBig()); const atVec3f& pos = positions.back(); os.format(FMT_STRING("vert = bm.verts.new(({},{},{}))\n"), pos.simd[0], pos.simd[1], pos.simd[2]); if (rp.first.second) { if (SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty()) rp.first.second->weightVertex(os, *rp.second.second, skinIndices[i]); else if (!SurfaceHeader::UseMatrixSkinning()) rp.first.second->weightVertex(os, *rp.second.second, i); } } if (rp.first.second && SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty()) vertCount += extraTracker.sendAdditionalVertsToBlender(os, rp, 0); os.format(FMT_STRING("two_face_vert = {}\n"), vertCount); for (atUint16 i = 0; i <= maxIdxs.pos; ++i) { const atVec3f& pos = positions[i]; os.format(FMT_STRING("vert = bm.verts.new(({},{},{}))\n"), pos.simd[0], pos.simd[1], pos.simd[2]); if (rp.first.second) { if (SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty()) rp.first.second->weightVertex(os, *rp.second.second, skinIndices[i]); else if (!SurfaceHeader::UseMatrixSkinning()) rp.first.second->weightVertex(os, *rp.second.second, i); } } if (rp.first.second && SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty()) extraTracker.sendAdditionalVertsToBlender(os, rp, vertCount); break; } case 1: { /* Normals */ os << "norm_list = []\n"; if (shortNormals) { atUint32 normCount = secSizes[s] / 6; for (atUint32 i = 0; i < normCount; ++i) { float x = reader.readInt16Big() / 16384.0f; float y = reader.readInt16Big() / 16384.0f; float z = reader.readInt16Big() / 16384.0f; os.format(FMT_STRING("norm_list.append(({},{},{}))\n"), x, y, z); } } else { atUint32 normCount = secSizes[s] / 12; for (atUint32 i = 0; i < normCount; ++i) { const atVec3f norm = reader.readVec3fBig(); os.format(FMT_STRING("norm_list.append(({},{},{}))\n"), norm.simd[0], norm.simd[1], norm.simd[2]); } } break; } case 2: { /* Colors */ break; } case 3: { /* Float UVs */ os << "uv_list = []\n"; atUint32 uvCount = secSizes[s] / 8; for (atUint32 i = 0; i < uvCount; ++i) { const atVec2f uv = reader.readVec2fBig(); os.format(FMT_STRING("uv_list.append(({},{}))\n"), uv.simd[0], uv.simd[1]); } break; } case 4: { if (surfaceCount) { /* MP3 MREA case */ visitedDLOffsets = true; } else { /* Short UVs */ os << "suv_list = []\n"; if (shortUVs) { atUint32 uvCount = secSizes[s] / 4; for (atUint32 i = 0; i < uvCount; ++i) { float x = reader.readInt16Big() / 32768.0f; float y = reader.readInt16Big() / 32768.0f; os.format(FMT_STRING("suv_list.append(({},{}))\n"), x, y); } break; } /* DL Offsets (here or next section) */ visitedDLOffsets = true; break; } [[fallthrough]]; } default: { if (!visitedDLOffsets) { visitedDLOffsets = true; break; } /* GX Display List (surface) */ SurfaceHeader sHead; sHead.read(reader); VertexAttributes& curVA = vertAttribs[sHead.matIdx]; unsigned matUVCount = curVA.uvCount; bool matShortUVs = curVA.shortUVs; const atInt16* bankIn = nullptr; if (SurfaceHeader::UseMatrixSkinning() && rp.first.second) bankIn = rp.first.second->getMatrixBank(sHead.skinMatrixBankIdx()); os.format(FMT_STRING("materials[{}].pass_index = {}\n"), sHead.matIdx, surfIdx++); if (matUVCount > createdUVLayers) { for (unsigned l = createdUVLayers; l < matUVCount; ++l) os.format(FMT_STRING("bm.loops.layers.uv.new('UV_{}')\n"), l); createdUVLayers = matUVCount; } atUint32 realDlSize = secSizes[s] - (reader.position() - secStart); DLReader dl(vertAttribs[sHead.matIdx], reader.readUBytes(realDlSize), realDlSize, extraTracker, bankIn); while (dl) { GX::Primitive ptype = dl.readPrimitive(); atUint16 vertCount = dl.readVertCount(); /* First vert */ DLReader::DLPrimVert firstPrimVert = dl.readVert(true); /* 3 Prim Verts to start */ int c = 0; DLReader::DLPrimVert primVerts[3] = {dl.readVert(), dl.readVert(), dl.readVert()}; if (ptype == GX::TRIANGLESTRIP) { atUint8 flip = 0; for (int v = 0; v < vertCount - 2; ++v) { if (flip) { os.format(FMT_STRING( "last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, " "two_face_vert)\n"), primVerts[c % 3].pos, primVerts[(c + 2) % 3].pos, primVerts[(c + 1) % 3].pos, primVerts[c % 3].norm, primVerts[(c + 2) % 3].norm, primVerts[(c + 1) % 3].norm, sHead.matIdx); if (matUVCount) { os << "if last_face is not None:\n"; for (unsigned j = 0; j < matUVCount; ++j) { if (j == 0 && matShortUVs) os.format(FMT_STRING( " uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], " "suv_list[{}])\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[0]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[1]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[2]\n"), primVerts[c % 3].uvs[j], primVerts[(c + 2) % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j], primVerts[c % 3].pos, j, primVerts[(c + 2) % 3].pos, j, primVerts[(c + 1) % 3].pos, j); else os.format(FMT_STRING( " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n"), primVerts[c % 3].pos, j, primVerts[c % 3].uvs[j], primVerts[(c + 2) % 3].pos, j, primVerts[(c + 2) % 3].uvs[j], primVerts[(c + 1) % 3].pos, j, primVerts[(c + 1) % 3].uvs[j]); } } } else { os.format(FMT_STRING( "last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, " "two_face_vert)\n"), primVerts[c % 3].pos, primVerts[(c + 1) % 3].pos, primVerts[(c + 2) % 3].pos, primVerts[c % 3].norm, primVerts[(c + 1) % 3].norm, primVerts[(c + 2) % 3].norm, sHead.matIdx); if (matUVCount) { os << "if last_face is not None:\n"; for (unsigned j = 0; j < matUVCount; ++j) { if (j == 0 && matShortUVs) os.format(FMT_STRING( " uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], " "suv_list[{}])\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[0]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[1]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[2]\n"), primVerts[c % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j], primVerts[(c + 2) % 3].uvs[j], primVerts[c % 3].pos, j, primVerts[(c + 1) % 3].pos, j, primVerts[(c + 2) % 3].pos, j); else os.format(FMT_STRING( " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n"), primVerts[c % 3].pos, j, primVerts[c % 3].uvs[j], primVerts[(c + 1) % 3].pos, j, primVerts[(c + 1) % 3].uvs[j], primVerts[(c + 2) % 3].pos, j, primVerts[(c + 2) % 3].uvs[j]); } } } flip ^= 1; bool peek = (v >= vertCount - 3); /* Advance one prim vert */ primVerts[c % 3] = dl.readVert(peek); ++c; } } else if (ptype == GX::TRIANGLES) { for (int v = 0; v < vertCount; v += 3) { os.format(FMT_STRING( "last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, " "two_face_vert)\n"), primVerts[0].pos, primVerts[1].pos, primVerts[2].pos, primVerts[0].norm, primVerts[1].norm, primVerts[2].norm, sHead.matIdx); if (matUVCount) { os << "if last_face is not None:\n"; for (unsigned j = 0; j < matUVCount; ++j) { if (j == 0 && matShortUVs) os.format(FMT_STRING( " uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], " "suv_list[{}])\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[0]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[1]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[2]\n"), primVerts[0].uvs[j], primVerts[1].uvs[j], primVerts[2].uvs[j], primVerts[0].pos, j, primVerts[1].pos, j, primVerts[2].pos, j); else os.format(FMT_STRING( " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n"), primVerts[0].pos, j, primVerts[0].uvs[j], primVerts[1].pos, j, primVerts[1].uvs[j], primVerts[2].pos, j, primVerts[2].uvs[j]); } } /* Break if done */ if (v + 3 >= 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 - 2; ++v) { os.format(FMT_STRING( "last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, " "two_face_vert)\n"), firstPrimVert.pos, primVerts[c % 3].pos, primVerts[(c + 1) % 3].pos, firstPrimVert.norm, primVerts[c % 3].norm, primVerts[(c + 1) % 3].norm, sHead.matIdx); if (matUVCount) { os << "if last_face is not None:\n"; for (unsigned j = 0; j < matUVCount; ++j) { if (j == 0 && matShortUVs) os.format(FMT_STRING( " uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], " "suv_list[{}])\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[0]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[1]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_tri[2]\n"), firstPrimVert.uvs[j], primVerts[c % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j], firstPrimVert.pos, j, primVerts[c % 3].pos, j, primVerts[(c + 1) % 3].pos, j); else os.format(FMT_STRING( " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n" " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " "uv_list[{}]\n"), firstPrimVert.pos, j, firstPrimVert.uvs[j], primVerts[c % 3].pos, j, primVerts[c % 3].uvs[j], primVerts[(c + 1) % 3].pos, j, primVerts[(c + 1) % 3].uvs[j]); } } /* Break if done */ if (v + 3 >= 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::SeekOrigin::Begin); } } /* Finish Mesh */ FinishBlenderMesh(os, matSetCount, meshIdx); if (rp.first.second) { if (entry.id != 0xB333E1D7) { // This is the beta phazon suit, we want the (incorrect) weight information, but we don't want to keep the CSKR id os.format(FMT_STRING("mesh.cskr_id = '{}'\n"), rp.first.first); } rp.second.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, FMT_STRING("invalid CMDL magic")); return false; } if (head.version != Version) { LogDNACommon.report(logvisor::Error, FMT_STRING("invalid CMDL version")); return false; } /* Open Py Stream and read sections */ hecl::blender::PyOutStream os = conn.beginPythonOut(true); os.format(FMT_STRING( "import bpy\n" "import bmesh\n" "\n" "bpy.context.scene.name = '{}'\n" "bpy.context.scene.hecl_mesh_obj = bpy.context.scene.name\n"), pakRouter.getBestEntryName(entry)); InitGeomBlenderContext(os, dataspec.getMasterShaderPath()); 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, 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, std::pair>& rp); template bool ReadCMDLToBlender, DNAMP2::MaterialSet, std::pair, 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, std::pair>& rp); template bool ReadCMDLToBlender, DNAMP3::MaterialSet, std::pair, 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, std::pair>& rp); template bool ReadCMDLToBlender, DNAMP3::MaterialSet, std::pair, 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, 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 = fmt::format(FMT_STRING("CMDL_{}"), entry.id); /* 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 (atUint32 s = 0; s < lastDlSec; ++s) { atUint64 secStart = reader.position(); if (s < matSecCount) { MaterialSet matSet; matSet.read(reader); matSet.nameTextures(pakRouter, bestName.c_str(), s); } if (s < head.secCount - 1) { reader.seek(secStart + head.secSizes[s], athena::SeekOrigin::Begin); } } } template void NameCMDL, DNAMP1::MaterialSet>( athena::io::IStreamReader& reader, PAKRouter& pakRouter, PAKRouter::EntryType& entry, const SpecBase& dataspec); template static void WriteDLVal(W& 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) { bool skinned = !mesh.skins.empty(); Header head; head.magic = 0xDEADBABE; head.version = Version; head.flags.setSkinned(skinned); head.flags.setShortNormals(!skinned); head.flags.setShortUVs(true); /* This just means there's an (empty) short UV section */ head.aabbMin = mesh.aabbMin.val; head.aabbMax = mesh.aabbMax.val; head.matSetCount = mesh.materialSets.size(); head.secCount = head.matSetCount + 6 + 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; #if 0 matSets.reserve(mesh.materialSets.size()); { 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; for (const Material& mat : mset) { std::string diagName = fmt::format(FMT_STRING("{}:{}"), inPath.getLastComponentUTF8(), mat.name); hecl::Frontend::IR matIR = FE.compileSource(mat.source, diagName); setBackends.emplace_back(); hecl::Backend::GX& matGX = setBackends.back(); matGX.reset(matIR, FE.getDiagnostics()); targetMSet.materials.emplace_back(matGX, mat.iprops, mat.texs, texPaths, mesh.colorLayerCount, false, false); targetMSet.materials.back().binarySize(endOff); targetMSet.head.addMaterialEndOff(endOff); } 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); } } for (const hecl::ProjectPath& path : texPaths) targetMSet.head.addTexture(path); size_t secSz = 0; targetMSet.binarySize(secSz); size_t secSz32 = ROUND_UP_32(secSz); head.secSizes.push_back(secSz32); paddingSizes.push_back(secSz32 - secSz); } } #endif /* Vertex Positions */ size_t secSz = mesh.pos.size() * 12; size_t secSz32 = ROUND_UP_32(secSz); if (secSz32 == 0) secSz32 = 32; head.secSizes.push_back(secSz32); paddingSizes.push_back(secSz32 - secSz); /* Vertex Normals */ secSz = mesh.norm.size() * (skinned ? 12 : 6); secSz32 = ROUND_UP_32(secSz); if (secSz32 == 0) secSz32 = 32; head.secSizes.push_back(secSz32); paddingSizes.push_back(secSz32 - secSz); /* Vertex Colors */ secSz = mesh.color.size() * 4; secSz32 = ROUND_UP_32(secSz); if (secSz32 == 0) secSz32 = 32; head.secSizes.push_back(secSz32); paddingSizes.push_back(secSz32 - secSz); /* UV coords */ secSz = mesh.uv.size() * 8; secSz32 = ROUND_UP_32(secSz); if (secSz32 == 0) secSz32 = 32; head.secSizes.push_back(secSz32); paddingSizes.push_back(secSz32 - secSz); /* LUV coords */ secSz = 0; secSz32 = ROUND_UP_32(secSz); head.secSizes.push_back(secSz32); paddingSizes.push_back(secSz32 - secSz); /* Surface index */ std::vector 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; size_t firstSurfSec = head.secSizes.size(); 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, FMT_STRING("GX DisplayList overflow")); size_t secSz = 64; for (auto it = surf.verts.cbegin(); it != surf.verts.cend();) { atUint16 vertCount = 0; auto itEnd = surf.verts.cend(); for (auto it2 = it; it2 != surf.verts.cend(); ++it2, ++vertCount) if (it2->iPos == 0xffffffff) { if (vertCount == 3) { /* All primitives here on out are triangles */ vertCount = atUint16((surf.verts.cend() - it + 1) * 3 / 4); break; } itEnd = it2; break; } secSz += 3 + vertCount * vertSz; if (itEnd == surf.verts.cend()) break; it = itEnd + 1; } 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) { if (skinned) { writer.writeVec3fBig(norm); } else { for (int i = 0; i < 3; ++i) { int tmpV = int(norm.simd[i] * 16384.f); tmpV = zeus::clamp(-32768, tmpV, 32767); writer.writeInt16Big(atInt16(tmpV)); } } } 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; /* LUV coords */ 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 = GX::TRIANGLES; if (mesh.topology == hecl::HMDLTopology::Triangles) prim = GX::TRIANGLES; else if (mesh.topology == hecl::HMDLTopology::TriStrips) prim = GX::TRIANGLESTRIP; else LogDNACommon.report(logvisor::Fatal, FMT_STRING("unrecognized mesh output mode")); auto surfSizeIt = head.secSizes.begin() + firstSurfSec; for (const Mesh::Surface& surf : mesh.surfaces) { const typename MaterialSet::Material::VAFlags& vaFlags = matSets.at(0).materials.at(surf.materialIdx).getVAFlags(); SurfaceHeader header; header.centroid = surf.centroid; header.matIdx = surf.materialIdx; header.dlSize = (*surfSizeIt++ - 64) | 0x80000000; header.reflectionNormal = surf.reflectionNormal; header.write(writer); GX::Primitive usePrim = prim; for (auto it = surf.verts.cbegin(); it != surf.verts.cend();) { atUint16 vertCount = 0; auto itEnd = surf.verts.cend(); for (auto it2 = it; it2 != surf.verts.cend(); ++it2, ++vertCount) if (it2->iPos == 0xffffffff) { if (vertCount == 3) { /* All primitives here on out are triangles */ usePrim = GX::TRIANGLES; vertCount = atUint16((surf.verts.cend() - it + 1) * 3 / 4); break; } itEnd = it2; break; } /* VAT0 = float normals, float UVs * VAT1 = short normals, float UVs */ writer.writeUByte(usePrim | (skinned ? 0x0 : 0x1)); writer.writeUint16Big(vertCount); for (auto it2 = it; it2 != itEnd; ++it2) { const Mesh::Surface::Vert& vert = *it2; if (vert.iPos == 0xffffffff) continue; 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]); } if (itEnd == surf.verts.cend()) break; it = itEnd + 1; } 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()); for (const std::vector& mset : mesh.materialSets) { matSets.emplace_back(); MaterialSet& targetMSet = matSets.back(); size_t endOff = 0; for (const Material& mat : mset) { ++targetMSet.materialCount; targetMSet.materials.emplace_back(mat); targetMSet.materials.back().binarySize(endOff); targetMSet.materialEndOffs.push_back(endOff); } size_t secSz = 0; targetMSet.binarySize(secSz); 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 = 0; bufs.m_meta.binarySize(secSz); 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) { (void)surf; 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::SeekOrigin::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); 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; } }; template bool WriteMREASecs(std::vector>& secsOut, const hecl::ProjectPath& inPath, const std::vector& meshes, zeus::CAABox& fullAABB, std::vector& meshAABBs) { /* Build material set */ std::vector surfToGlobalMats; MaterialSet matSet; { MaterialPool matPool; size_t surfCount = 0; for (const Mesh& mesh : meshes) surfCount += mesh.surfaces.size(); surfToGlobalMats.reserve(surfCount); 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 auto& chunk : mat.chunks) { if (auto pass = chunk.get_if()) { bool found = false; for (const hecl::ProjectPath& ePath : texPaths) { if (pass->tex == ePath) { found = true; break; } } if (!found) texPaths.push_back(pass->tex); } } auto lightmapped = mat.iprops.find("retro_lightmapped"); bool lm = lightmapped != mat.iprops.cend() && lightmapped->second != 0; matSet.materials.emplace_back(mat, texPaths, mesh.colorLayerCount, lm, false); 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); size_t secSz = 0; matSet.binarySize(secSz); secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); matSet.write(w); } /* Iterate meshes */ auto matIt = surfToGlobalMats.cbegin(); for (const Mesh& mesh : meshes) { zeus::CTransform meshXf(mesh.sceneXf.val.data()); meshXf.basis.transpose(); /* Header */ { MeshHeader meshHeader = {}; meshHeader.visorFlags.setFromBlenderProps(mesh.customProps); memmove(meshHeader.xfMtx, mesh.sceneXf.val.data(), 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; size_t secSz = 0; meshHeader.binarySize(secSz); secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); meshHeader.write(w); } std::vector surfEndOffs; surfEndOffs.reserve(mesh.surfaces.size()); size_t endOff = 0; auto smatIt = matIt; for (const Mesh::Surface& surf : mesh.surfaces) { const typename MaterialSet::Material::VAFlags& vaFlags = matSet.materials.at(*smatIt++).getVAFlags(); size_t vertSz = vaFlags.vertDLSize(); endOff += 96; for (auto it = surf.verts.cbegin(); it != surf.verts.cend();) { atUint16 vertCount = 0; auto itEnd = surf.verts.cend(); for (auto it2 = it; it2 != surf.verts.cend(); ++it2, ++vertCount) if (it2->iPos == 0xffffffff) { if (vertCount == 3) { /* All primitives here on out are triangles */ vertCount = atUint16((surf.verts.cend() - it + 1) * 3 / 4); break; } itEnd = it2; break; } endOff += 3 + vertSz * vertCount; if (itEnd == surf.verts.cend()) break; it = itEnd + 1; } endOff = ROUND_UP_32(endOff); surfEndOffs.push_back(endOff); } /* Positions */ { size_t secSz = ROUND_UP_32(mesh.pos.size() * 12); if (secSz == 0) secSz = 32; secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); for (const hecl::blender::Vector3f& v : mesh.pos) { zeus::CVector3f preXfPos = meshXf * zeus::CVector3f(v); w.writeVec3fBig(preXfPos); } } /* Normals */ { size_t secSz = ROUND_UP_32(mesh.norm.size() * 6); if (secSz == 0) secSz = 32; secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); for (const hecl::blender::Vector3f& v : mesh.norm) { zeus::CVector3f preXfNorm = (meshXf.basis * zeus::CVector3f(v)).normalized(); for (int i = 0; i < 3; ++i) { int tmpV = int(preXfNorm[i] * 16384.f); tmpV = zeus::clamp(-32768, tmpV, 32767); w.writeInt16Big(atInt16(tmpV)); } } } /* Colors */ { size_t secSz = ROUND_UP_32(mesh.color.size() * 4); if (secSz == 0) secSz = 32; secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); for (const hecl::blender::Vector3f& v : mesh.color) { zeus::CColor col((zeus::CVector4f(zeus::CVector3f(v)))); col.writeRGBA8(w); } } /* UVs */ { size_t secSz = ROUND_UP_32(mesh.uv.size() * 8); if (secSz == 0) secSz = 32; secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); for (const hecl::blender::Vector2f& v : mesh.uv) w.writeVec2fBig(v.val); } /* LUVs */ { size_t secSz = ROUND_UP_32(mesh.luv.size() * 4); if (secSz == 0) secSz = 32; secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); for (const hecl::blender::Vector2f& v : mesh.luv) { for (int i = 0; i < 2; ++i) { int tmpV = int(v.val.simd[i] * 32768.f); tmpV = zeus::clamp(-32768, tmpV, 32767); w.writeInt16Big(atInt16(tmpV)); } } } /* 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 */ GX::Primitive prim = GX::TRIANGLES; if (mesh.topology == hecl::HMDLTopology::Triangles) prim = GX::TRIANGLES; else if (mesh.topology == hecl::HMDLTopology::TriStrips) prim = GX::TRIANGLESTRIP; else LogDNACommon.report(logvisor::Fatal, FMT_STRING("unrecognized mesh output mode")); auto surfEndOffIt = surfEndOffs.begin(); size_t lastEndOff = 0; for (const Mesh::Surface& surf : mesh.surfaces) { size_t matIdx = *matIt++; const typename MaterialSet::Material& mat = matSet.materials.at(matIdx); const typename MaterialSet::Material::VAFlags& vaFlags = mat.getVAFlags(); SurfaceHeader header; header.centroid = meshXf * zeus::CVector3f(surf.centroid); header.matIdx = matIdx; uint32_t dlSize = uint32_t(*surfEndOffIt - lastEndOff - 96); header.dlSize = dlSize | 0x80000000; lastEndOff = *surfEndOffIt++; header.reflectionNormal = (meshXf.basis * zeus::CVector3f(surf.reflectionNormal)).normalized(); header.aabbSz = 24; zeus::CAABox aabb(zeus::CVector3f(surf.aabbMin), zeus::CVector3f(surf.aabbMax)); aabb = aabb.getTransformedAABox(meshXf); header.aabb[0] = aabb.min; header.aabb[1] = aabb.max; size_t secSz = 0; header.binarySize(secSz); secSz += dlSize; secSz = ROUND_UP_32(secSz); secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); header.write(w); GX::Primitive usePrim = prim; for (auto it = surf.verts.cbegin(); it != surf.verts.cend();) { atUint16 vertCount = 0; auto itEnd = surf.verts.cend(); for (auto it2 = it; it2 != surf.verts.cend(); ++it2, ++vertCount) if (it2->iPos == 0xffffffff) { if (vertCount == 3) { /* All primitives here on out are triangles */ usePrim = GX::TRIANGLES; vertCount = atUint16((surf.verts.cend() - it + 1) * 3 / 4); break; } itEnd = it2; break; } /* VAT1 = short normals, float UVs * VAT2 = short normals, short UVs */ w.writeUByte(usePrim | (mat.flags.lightmapUVArray() ? 0x2 : 0x1)); w.writeUint16Big(vertCount); for (auto it2 = it; it2 != itEnd; ++it2) { const Mesh::Surface::Vert& vert = *it2; if (vert.iPos == 0xffffffff) continue; atUint32 skinIdx = vert.iBankSkin * 3; WriteDLVal(w, vaFlags.pnMatIdx(), skinIdx); WriteDLVal(w, vaFlags.tex0MatIdx(), skinIdx); WriteDLVal(w, vaFlags.tex1MatIdx(), skinIdx); WriteDLVal(w, vaFlags.tex2MatIdx(), skinIdx); WriteDLVal(w, vaFlags.tex3MatIdx(), skinIdx); WriteDLVal(w, vaFlags.tex4MatIdx(), skinIdx); WriteDLVal(w, vaFlags.tex5MatIdx(), skinIdx); WriteDLVal(w, vaFlags.tex6MatIdx(), skinIdx); WriteDLVal(w, vaFlags.position(), vert.iPos); WriteDLVal(w, vaFlags.normal(), vert.iNorm); WriteDLVal(w, vaFlags.color0(), vert.iColor[0]); WriteDLVal(w, vaFlags.color1(), vert.iColor[1]); WriteDLVal(w, vaFlags.tex0(), vert.iUv[0]); WriteDLVal(w, vaFlags.tex1(), vert.iUv[1]); WriteDLVal(w, vaFlags.tex2(), vert.iUv[2]); WriteDLVal(w, vaFlags.tex3(), vert.iUv[3]); WriteDLVal(w, vaFlags.tex4(), vert.iUv[4]); WriteDLVal(w, vaFlags.tex5(), vert.iUv[5]); WriteDLVal(w, vaFlags.tex6(), vert.iUv[6]); } if (itEnd == surf.verts.cend()) break; it = itEnd + 1; } } } return true; } template bool WriteMREASecs( std::vector>& secsOut, const hecl::ProjectPath& inPath, const std::vector& meshes, zeus::CAABox& fullAABB, std::vector& meshAABBs); 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; { MaterialPool matPool; size_t surfCount = 0; for (const Mesh& mesh : meshes) surfCount += mesh.surfaces.size(); surfToGlobalMats.reserve(surfCount); MaterialSet matSet = {}; size_t endOff = 0; 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; ++matSet.materialCount; matSet.materials.emplace_back(mat); matSet.materials.back().binarySize(endOff); matSet.materialEndOffs.push_back(endOff); } for (const Mesh::Surface& surf : mesh.surfaces) surfToGlobalMats.push_back(meshToGlobalMats[surf.materialIdx]); } } size_t secSz = 0; matSet.binarySize(secSz); secsOut.emplace_back(secSz, 0); athena::io::MemoryWriter w(secsOut.back().data(), secsOut.back().size()); matSet.write(w); } /* Iterate meshes */ auto matIt = surfToGlobalMats.cbegin(); for (const Mesh& mesh : meshes) { zeus::CTransform meshXf(mesh.sceneXf.val.data()); meshXf.basis.transpose(); /* Header */ { MeshHeader meshHeader = {}; meshHeader.visorFlags.setFromBlenderProps(mesh.customProps); memmove(meshHeader.xfMtx, mesh.sceneXf.val.data(), 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; size_t secSz = 0; meshHeader.binarySize(secSz); secsOut.emplace_back(secSz, 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) { (void)surf; endOff += 96; surfEndOffs.push_back(endOff); } /* Metadata */ { size_t secSz = 0; bufs.m_meta.binarySize(secSz); secsOut.emplace_back(secSz, 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; size_t secSz = 0; header.binarySize(secSz); secsOut.emplace_back(secSz, 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); template <> void SurfaceHeader_1::Enumerate(typename Read::StreamT& reader) { /* centroid */ centroid = reader.readVec3fBig(); /* matIdx */ matIdx = reader.readUint32Big(); /* dlSize */ dlSize = reader.readUint32Big(); /* 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::SeekOrigin::Current); /* align */ reader.seekAlign32(); } template <> void SurfaceHeader_1::Enumerate(typename Write::StreamT& writer) { /* centroid */ writer.writeVec3fBig(centroid); /* matIdx */ writer.writeUint32Big(matIdx); /* dlSize */ writer.writeUint32Big(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(); } template <> void SurfaceHeader_1::Enumerate(typename BinarySize::StreamT& s) { s += (aabbSz ? 24 : 0); s += 44; s = (s + 31) & ~31; } template <> void SurfaceHeader_2::Enumerate(typename Read::StreamT& reader) { /* centroid */ centroid = reader.readVec3fBig(); /* matIdx */ matIdx = reader.readUint32Big(); /* dlSize */ dlSize = reader.readUint32Big(); /* 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::SeekOrigin::Current); /* align */ reader.seekAlign32(); } template <> void SurfaceHeader_2::Enumerate(typename Write::StreamT& writer) { /* centroid */ writer.writeVec3fBig(centroid); /* matIdx */ writer.writeUint32Big(matIdx); /* dlSize */ writer.writeUint32Big(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(); } template <> void SurfaceHeader_2::Enumerate(typename BinarySize::StreamT& s) { s += (aabbSz ? 24 : 0); s += 48; s = (s + 31) & ~31; } template <> void SurfaceHeader_3::Enumerate(typename Read::StreamT& reader) { /* centroid */ centroid = reader.readVec3fBig(); /* matIdx */ matIdx = reader.readUint32Big(); /* dlSize */ dlSize = reader.readUint32Big(); /* 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::SeekOrigin::Current); /* unk3 */ unk3 = reader.readUByte(); /* align */ reader.seekAlign32(); } template <> void SurfaceHeader_3::Enumerate(typename Write::StreamT& writer) { /* centroid */ writer.writeVec3fBig(centroid); /* matIdx */ writer.writeUint32Big(matIdx); /* dlSize */ writer.writeUint32Big(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(); } template <> void SurfaceHeader_3::Enumerate(typename BinarySize::StreamT& s) { s += (aabbSz ? 24 : 0); s += 49; s = (s + 31) & ~31; } } // namespace DataSpec::DNACMDL