From eb9efbf8c3058a8ee8faa7fbad3d60116a297b1e Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Tue, 17 Mar 2020 23:12:43 -0700 Subject: [PATCH] Initial PATH support for MP2 --- DataSpec/DNACommon/AROTBuilder.cpp | 8 +- DataSpec/DNACommon/AROTBuilder.hpp | 7 +- DataSpec/DNACommon/CMakeLists.txt | 2 + DataSpec/DNACommon/PATH.cpp | 222 ++++++++++++++++++++++++++++ DataSpec/DNACommon/PATH.hpp | 74 ++++++++++ DataSpec/DNAMP1/CMakeLists.txt | 1 - DataSpec/DNAMP1/PATH.cpp | 223 +---------------------------- DataSpec/DNAMP1/PATH.hpp | 76 +--------- DataSpec/DNAMP2/CMakeLists.txt | 1 + DataSpec/DNAMP2/DNAMP2.cpp | 27 ++++ DataSpec/DNAMP2/DNAMP2.hpp | 3 + DataSpec/DNAMP2/MREA.cpp | 17 +++ DataSpec/DNAMP2/MREA.hpp | 2 + DataSpec/DNAMP2/PATH.cpp | 26 ++++ DataSpec/DNAMP2/PATH.hpp | 14 ++ DataSpec/SpecMP1.cpp | 2 +- DataSpec/SpecMP2.cpp | 7 +- Runtime/MP1/MP1.hpp | 2 +- 18 files changed, 411 insertions(+), 303 deletions(-) create mode 100644 DataSpec/DNACommon/PATH.cpp create mode 100644 DataSpec/DNACommon/PATH.hpp create mode 100644 DataSpec/DNAMP2/PATH.cpp create mode 100644 DataSpec/DNAMP2/PATH.hpp diff --git a/DataSpec/DNACommon/AROTBuilder.cpp b/DataSpec/DNACommon/AROTBuilder.cpp index 52315572a..6cdc4a351 100644 --- a/DataSpec/DNACommon/AROTBuilder.cpp +++ b/DataSpec/DNACommon/AROTBuilder.cpp @@ -272,10 +272,10 @@ void AROTBuilder::Node::pathCountNodesAndLookups(size_t& nodeCount, size_t& look } } -void AROTBuilder::Node::pathWrite(DNAMP1::PATH& path, const zeus::CAABox& curAABB) { +void AROTBuilder::Node::pathWrite(DNAPATH::PATH& path, const zeus::CAABox& curAABB) { if (childNodes.empty()) { path.octree.emplace_back(); - DNAMP1::PATH::OctreeNode& n = path.octree.back(); + DNAPATH::PATH::OctreeNode& n = path.octree.back(); n.isLeaf = 1; n.aabb[0] = curAABB.min; n.aabb[1] = curAABB.max; @@ -401,12 +401,12 @@ std::pair, uint32_t> AROTBuilder::buildCol(const ColM return {std::move(ret), totalSize}; } -void AROTBuilder::buildPath(DNAMP1::PATH& path) { +void AROTBuilder::buildPath(DNAPATH::PATH& path) { /* Accumulate total AABB and gather region boxes */ std::vector regionBoxes; regionBoxes.reserve(path.regions.size()); zeus::CAABox fullAABB; - for (const DNAMP1::PATH::Region& r : path.regions) { + for (const DNAPATH::PATH::Region& r : path.regions) { regionBoxes.emplace_back(r.aabb[0], r.aabb[1]); fullAABB.accumulateBounds(regionBoxes.back()); } diff --git a/DataSpec/DNACommon/AROTBuilder.hpp b/DataSpec/DNACommon/AROTBuilder.hpp index 1ecabbce7..2ec6f4a57 100644 --- a/DataSpec/DNACommon/AROTBuilder.hpp +++ b/DataSpec/DNACommon/AROTBuilder.hpp @@ -7,10 +7,11 @@ #include namespace DataSpec { -namespace DNAMP1 { +namespace DNAPATH { struct PATH; } + struct AROTBuilder { using ColMesh = hecl::blender::ColMesh; @@ -42,13 +43,13 @@ struct AROTBuilder { void writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB); void pathCountNodesAndLookups(size_t& nodeCount, size_t& lookupCount); - void pathWrite(DNAMP1::PATH& path, const zeus::CAABox& curAABB); + void pathWrite(DNAPATH::PATH& path, const zeus::CAABox& curAABB); } rootNode; void build(std::vector>& secs, const zeus::CAABox& fullAabb, const std::vector& meshAabbs, const std::vector& meshes); std::pair, uint32_t> buildCol(const ColMesh& mesh, BspNodeType& rootOut); - void buildPath(DNAMP1::PATH& path); + void buildPath(DNAPATH::PATH& path); }; } // namespace DataSpec diff --git a/DataSpec/DNACommon/CMakeLists.txt b/DataSpec/DNACommon/CMakeLists.txt index 39a616532..4d77c84cf 100644 --- a/DataSpec/DNACommon/CMakeLists.txt +++ b/DataSpec/DNACommon/CMakeLists.txt @@ -4,6 +4,7 @@ make_dnalist(CMDL FSM2 MAPA MAPU + PATH MayaSpline EGMC SAVWCommon @@ -19,6 +20,7 @@ set(DNACOMMON_SOURCES CMDL.cpp MAPA.cpp MAPU.cpp + PATH.hpp PATH.cpp STRG.hpp STRG.cpp TXTR.hpp TXTR.cpp ANCS.hpp ANCS.cpp diff --git a/DataSpec/DNACommon/PATH.cpp b/DataSpec/DNACommon/PATH.cpp new file mode 100644 index 000000000..16a499d9e --- /dev/null +++ b/DataSpec/DNACommon/PATH.cpp @@ -0,0 +1,222 @@ +#include "PATH.hpp" +#include "hecl/Blender/Connection.hpp" +#include "zeus/CAABox.hpp" +#include "DataSpec/DNACommon/AROTBuilder.hpp" + +namespace DataSpec::DNAPATH { + +#define DUMP_OCTREE 0 + +#if DUMP_OCTREE +/* octree dumper */ +static void OutputOctreeNode(hecl::blender::PyOutStream& os, int idx, const zeus::CAABox& aabb) { + const zeus::CVector3f pos = aabb.center(); + const zeus::CVector3f extent = aabb.extents(); + os.format( + "obj = bpy.data.objects.new('Leaf_%d', None)\n" + "bpy.context.scene.collection.objects.link(obj)\n" + "obj.location = (%f,%f,%f)\n" + "obj.scale = (%f,%f,%f)\n" + "obj.empty_display_type = 'CUBE'\n" + "obj.layers[1] = True\n" + "obj.layers[0] = False\n", idx, + pos.x(), pos.y(), pos.z(), extent.x(), extent.y(), extent.z()); +} +#endif + +void PATH::sendToBlender(hecl::blender::Connection& conn, std::string_view entryName, const zeus::CMatrix4f* xf, + const std::string& areaPath) { + /* Open Py Stream and read sections */ + hecl::blender::PyOutStream os = conn.beginPythonOut(true); + os << + "import bpy\n" + "import bmesh\n" + "from mathutils import Vector, Matrix\n" + "\n" + "bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')\n" + "bpy.types.Material.retro_path_type_mask = bpy.props.IntProperty(name='Retro: Path Type Mask')\n" + "\n" + "material_dict = {}\n" + "material_index = []\n" + "def make_ground_material(idxMask):\n" + " mat = bpy.data.materials.new('Ground %X' % idxMask)\n" + " mat.diffuse_color = (0.8, 0.460, 0.194, 1.0)\n" + " return mat\n" + "def make_flyer_material(idxMask):\n" + " mat = bpy.data.materials.new('Flyer %X' % idxMask)\n" + " mat.diffuse_color = (0.016, 0.8, 0.8, 1.0)\n" + " return mat\n" + "def make_swimmer_material(idxMask):\n" + " mat = bpy.data.materials.new('Swimmer %X' % idxMask)\n" + " mat.diffuse_color = (0.074, 0.293, 0.8, 1.0)\n" + " return mat\n" + "def select_material(meshIdxMask, meshTypeMask):\n" + " key = (meshIdxMask, meshTypeMask)\n" + " if key in material_index:\n" + " return material_index.index(key)\n" + " elif key in material_dict:\n" + " material_index.append(key)\n" + " return len(material_index)-1\n" + " else:\n" + " if meshTypeMask == 0x2:\n" + " mat = make_flyer_material(meshIdxMask)\n" + " elif meshTypeMask == 0x4:\n" + " mat = make_swimmer_material(meshIdxMask)\n" + " else:\n" + " mat = make_ground_material(meshIdxMask)\n" + " mat.retro_path_idx_mask = meshIdxMask\n" + " mat.retro_path_type_mask = meshTypeMask\n" + " material_dict[key] = mat\n" + " material_index.append(key)\n" + " return len(material_index)-1\n" + "\n"; + os.format(fmt("bpy.context.scene.name = '{}'\n"), entryName); + os << + "# Clear Scene\n" + "if len(bpy.data.collections):\n" + " bpy.data.collections.remove(bpy.data.collections[0])\n" + "\n" + "bm = bmesh.new()\n" + "height_lay = bm.faces.layers.float.new('Height')\n"; + + for (const Node& n : nodes) { + zeus::simd_floats f(n.position.simd); + os.format(fmt("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]); + } + + os << "bm.verts.ensure_lookup_table()\n"; + + for (const Region& r : regions) { + os << "tri_verts = []\n"; + for (atUint32 i = 0; i < r.nodeCount; ++i) + os.format(fmt("tri_verts.append(bm.verts[{}])\n"), r.nodeStart + i); + + os.format(fmt( + "face = bm.faces.get(tri_verts)\n" + "if face is None:\n" + " face = bm.faces.new(tri_verts)\n" + " face.normal_flip()\n" + "face.material_index = select_material(0x{:04X}, 0x{:04X})\n" + "face.smooth = False\n" + "face[height_lay] = {}\n" + "\n"), + r.meshIndexMask, r.meshTypeMask, r.height); + +#if 0 + const zeus::CVector3f center = xf->multiplyOneOverW(r.centroid); + zeus::CAABox aabb(xf->multiplyOneOverW(r.aabb[0]), xf->multiplyOneOverW(r.aabb[1])); + os.format(fmt("aabb = bpy.data.objects.new('AABB', None)\n") + "aabb.location = (%f,%f,%f)\n" + "aabb.scale = (%f,%f,%f)\n" + "aabb.empty_display_type = 'CUBE'\n" + "bpy.context.scene.collection.objects.link(aabb)\n" + "centr = bpy.data.objects.new('Center', None)\n" + "centr.location = (%f,%f,%f)\n" + "bpy.context.scene.collection.objects.link(centr)\n", + aabb.min[0] + (aabb.max[0] - aabb.min[0]) / 2.f, + aabb.min[1] + (aabb.max[1] - aabb.min[1]) / 2.f, + aabb.min[2] + (aabb.max[2] - aabb.min[2]) / 2.f, + (aabb.max[0] - aabb.min[0]) / 2.f, + (aabb.max[1] - aabb.min[1]) / 2.f, + (aabb.max[2] - aabb.min[2]) / 2.f, + center.x(), center.y(), center.z()); +#endif + } + +#if 0 + for (const Node& n : nodes) { + zeus::simd_floats f(n.position.simd); + zeus::simd_floats no(n.position.simd + n.normal.simd); + os.format(fmt("v = bm.verts.new((%f,%f,%f))\n") + "v2 = bm.verts.new((%f,%f,%f))\n" + "bm.edges.new((v, v2))\n", f[0], f[1], f[2], no[0], no[1], no[2]); + } +#endif + + os << "bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)\n" + "path_mesh = bpy.data.meshes.new('PATH')\n" + "bm.to_mesh(path_mesh)\n" + "path_mesh_obj = bpy.data.objects.new(path_mesh.name, path_mesh)\n" + "\n" + "for mat_name in material_index:\n" + " mat = material_dict[mat_name]\n" + " path_mesh.materials.append(mat)\n" + "\n" + "bpy.context.scene.collection.objects.link(path_mesh_obj)\n" + "path_mesh_obj.display_type = 'SOLID'\n" + "bpy.context.scene.hecl_path_obj = path_mesh_obj.name\n" + "\n"; + + if (xf) { + const zeus::CMatrix4f& w = *xf; + zeus::simd_floats xfMtxF[4]; + for (int i = 0; i < 4; ++i) + w.m[i].mSimd.copy_to(xfMtxF[i]); + os.format(fmt( + "mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n" + "mtxd = mtx.decompose()\n" + "path_mesh_obj.rotation_mode = 'QUATERNION'\n" + "path_mesh_obj.location = mtxd[0]\n" + "path_mesh_obj.rotation_quaternion = mtxd[1]\n" + "path_mesh_obj.scale = mtxd[2]\n"), + xfMtxF[0][0], xfMtxF[1][0], xfMtxF[2][0], xfMtxF[3][0], xfMtxF[0][1], xfMtxF[1][1], xfMtxF[2][1], xfMtxF[3][1], + xfMtxF[0][2], xfMtxF[1][2], xfMtxF[2][2], xfMtxF[3][2]); + } + +#if DUMP_OCTREE + { + int idx = 0; + for (const auto& n : octree) { + if (n.isLeaf) + OutputOctreeNode(os, idx, zeus::CAABox(n.aabb[0], n.aabb[1])); + ++idx; + } + } +#endif + + os.linkBackground(fmt::format(fmt("//{}"), areaPath)); + os.centerView(); + os.close(); +} + +bool PATH::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, + const PathMesh& mesh, hecl::blender::Token& btok) { + athena::io::MemoryReader r(mesh.data.data(), mesh.data.size()); + PATH path; + path.read(r); + if (!path.regions.empty()) { + AROTBuilder octreeBuilder; + octreeBuilder.buildPath(path); + } else { + path.octreeNodeCount = 1; + path.octree.emplace_back(); + OctreeNode& n = path.octree.back(); + n.isLeaf = 1; + n.aabb[0] = zeus::CVector3f{FLT_MAX, FLT_MAX, FLT_MAX}; + n.aabb[1] = zeus::CVector3f{-FLT_MAX, -FLT_MAX, -FLT_MAX}; + for (int i = 0; i < 8; ++i) + n.children[i] = 0xffffffff; + } + +#if DUMP_OCTREE + { + hecl::blender::Connection& conn = btok.getBlenderConnection(); + if (!conn.createBlend(inPath.getWithExtension(_SYS_STR(".octree.blend"), true), hecl::blender::BlendType::PathMesh)) + return false; + + zeus::CMatrix4f xf; + path.sendToBlender(conn, "PATH"sv, &xf); + conn.saveBlend(); + } +#endif + + athena::io::FileWriter w(outPath.getAbsolutePath()); + path.write(w); + int64_t rem = w.position() % 32; + if (rem) + for (int64_t i = 0; i < 32 - rem; ++i) + w.writeUByte(0xff); + return true; +} + +} // namespace DataSpec::DNAMP1 \ No newline at end of file diff --git a/DataSpec/DNACommon/PATH.hpp b/DataSpec/DNACommon/PATH.hpp new file mode 100644 index 000000000..67dc2e64d --- /dev/null +++ b/DataSpec/DNACommon/PATH.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include "DataSpec/DNACommon/DNACommon.hpp" +#include "DataSpec/DNACommon/PAK.hpp" + +namespace DataSpec::DNAPATH { +struct PATH : BigDNA { + using PathMesh = hecl::blender::PathMesh; + + AT_DECL_DNA + Value version; + + struct Node : BigDNA { + AT_DECL_DNA + Value position; + Value normal; + }; + Value nodeCount; + Vector nodes; + + struct Link : BigDNA { + AT_DECL_DNA + Value nodeIdx; + Value regionIdx; + Value width2d; + Value oneOverWidth2d; + }; + Value linkCount; + Vector links; + + struct Region : BigDNA { + AT_DECL_DNA + Value nodeCount; + Value nodeStart; + Value linkCount; + Value linkStart; + Value meshIndexMask; + Value meshTypeMask; + Value height; + Value normal; + Value regionIdx; + Value centroid; + Value aabb[2]; + Value regionIdxPtr; + }; + Value regionCount; + Vector regions; + + Vector bitmap1; + Vector bitmap2; + Vector bitmap3; + + Value octreeRegionLookupCount; + Vector octreeRegionLookup; + + struct OctreeNode : BigDNA { + AT_DECL_DNA + Value isLeaf; + Value aabb[2]; + Value centroid; + Value children[8]; + Value regionCount; + Value regionStart; + }; + Value octreeNodeCount; + Vector octree; + + void sendToBlender(hecl::blender::Connection& conn, std::string_view entryName, const zeus::CMatrix4f* xf, + const std::string& areaPath); + + static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, + const PathMesh& mesh, hecl::blender::Token& btok); +}; +} // namespace DataSpec::DNAMP1 diff --git a/DataSpec/DNAMP1/CMakeLists.txt b/DataSpec/DNAMP1/CMakeLists.txt index 3520e76c4..12f703bbb 100644 --- a/DataSpec/DNAMP1/CMakeLists.txt +++ b/DataSpec/DNAMP1/CMakeLists.txt @@ -11,7 +11,6 @@ make_dnalist(PAK CINF CSKR EVNT - PATH CMDLMaterials MREA DeafBabe diff --git a/DataSpec/DNAMP1/PATH.cpp b/DataSpec/DNAMP1/PATH.cpp index 506995484..68dd16fdf 100644 --- a/DataSpec/DNAMP1/PATH.cpp +++ b/DataSpec/DNAMP1/PATH.cpp @@ -1,184 +1,8 @@ -#include "PATH.hpp" -#include "hecl/Blender/Connection.hpp" -#include "zeus/CAABox.hpp" +#include "DataSpec/DNAMP1/PATH.hpp" #include "DataSpec/DNACommon/AROTBuilder.hpp" +#include "hecl/Blender/Connection.hpp" namespace DataSpec::DNAMP1 { - -#define DUMP_OCTREE 0 - -#if DUMP_OCTREE -/* octree dumper */ -static void OutputOctreeNode(hecl::blender::PyOutStream& os, int idx, const zeus::CAABox& aabb) { - const zeus::CVector3f pos = aabb.center(); - const zeus::CVector3f extent = aabb.extents(); - os.format( - "obj = bpy.data.objects.new('Leaf_%d', None)\n" - "bpy.context.scene.collection.objects.link(obj)\n" - "obj.location = (%f,%f,%f)\n" - "obj.scale = (%f,%f,%f)\n" - "obj.empty_display_type = 'CUBE'\n" - "obj.layers[1] = True\n" - "obj.layers[0] = False\n", idx, - pos.x(), pos.y(), pos.z(), extent.x(), extent.y(), extent.z()); -} -#endif - -void PATH::sendToBlender(hecl::blender::Connection& conn, std::string_view entryName, const zeus::CMatrix4f* xf, - const std::string& areaPath) { - /* Open Py Stream and read sections */ - hecl::blender::PyOutStream os = conn.beginPythonOut(true); - os << - "import bpy\n" - "import bmesh\n" - "from mathutils import Vector, Matrix\n" - "\n" - "bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')\n" - "bpy.types.Material.retro_path_type_mask = bpy.props.IntProperty(name='Retro: Path Type Mask')\n" - "\n" - "material_dict = {}\n" - "material_index = []\n" - "def make_ground_material(idxMask):\n" - " mat = bpy.data.materials.new('Ground %X' % idxMask)\n" - " mat.diffuse_color = (0.8, 0.460, 0.194, 1.0)\n" - " return mat\n" - "def make_flyer_material(idxMask):\n" - " mat = bpy.data.materials.new('Flyer %X' % idxMask)\n" - " mat.diffuse_color = (0.016, 0.8, 0.8, 1.0)\n" - " return mat\n" - "def make_swimmer_material(idxMask):\n" - " mat = bpy.data.materials.new('Swimmer %X' % idxMask)\n" - " mat.diffuse_color = (0.074, 0.293, 0.8, 1.0)\n" - " return mat\n" - "def select_material(meshIdxMask, meshTypeMask):\n" - " key = (meshIdxMask, meshTypeMask)\n" - " if key in material_index:\n" - " return material_index.index(key)\n" - " elif key in material_dict:\n" - " material_index.append(key)\n" - " return len(material_index)-1\n" - " else:\n" - " if meshTypeMask == 0x2:\n" - " mat = make_flyer_material(meshIdxMask)\n" - " elif meshTypeMask == 0x4:\n" - " mat = make_swimmer_material(meshIdxMask)\n" - " else:\n" - " mat = make_ground_material(meshIdxMask)\n" - " mat.retro_path_idx_mask = meshIdxMask\n" - " mat.retro_path_type_mask = meshTypeMask\n" - " material_dict[key] = mat\n" - " material_index.append(key)\n" - " return len(material_index)-1\n" - "\n"; - os.format(fmt("bpy.context.scene.name = '{}'\n"), entryName); - os << - "# Clear Scene\n" - "if len(bpy.data.collections):\n" - " bpy.data.collections.remove(bpy.data.collections[0])\n" - "\n" - "bm = bmesh.new()\n" - "height_lay = bm.faces.layers.float.new('Height')\n"; - - for (const Node& n : nodes) { - zeus::simd_floats f(n.position.simd); - os.format(fmt("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]); - } - - os << "bm.verts.ensure_lookup_table()\n"; - - for (const Region& r : regions) { - os << "tri_verts = []\n"; - for (atUint32 i = 0; i < r.nodeCount; ++i) - os.format(fmt("tri_verts.append(bm.verts[{}])\n"), r.nodeStart + i); - - os.format(fmt( - "face = bm.faces.get(tri_verts)\n" - "if face is None:\n" - " face = bm.faces.new(tri_verts)\n" - " face.normal_flip()\n" - "face.material_index = select_material(0x{:04X}, 0x{:04X})\n" - "face.smooth = False\n" - "face[height_lay] = {}\n" - "\n"), - r.meshIndexMask, r.meshTypeMask, r.height); - -#if 0 - const zeus::CVector3f center = xf->multiplyOneOverW(r.centroid); - zeus::CAABox aabb(xf->multiplyOneOverW(r.aabb[0]), xf->multiplyOneOverW(r.aabb[1])); - os.format(fmt("aabb = bpy.data.objects.new('AABB', None)\n") - "aabb.location = (%f,%f,%f)\n" - "aabb.scale = (%f,%f,%f)\n" - "aabb.empty_display_type = 'CUBE'\n" - "bpy.context.scene.collection.objects.link(aabb)\n" - "centr = bpy.data.objects.new('Center', None)\n" - "centr.location = (%f,%f,%f)\n" - "bpy.context.scene.collection.objects.link(centr)\n", - aabb.min[0] + (aabb.max[0] - aabb.min[0]) / 2.f, - aabb.min[1] + (aabb.max[1] - aabb.min[1]) / 2.f, - aabb.min[2] + (aabb.max[2] - aabb.min[2]) / 2.f, - (aabb.max[0] - aabb.min[0]) / 2.f, - (aabb.max[1] - aabb.min[1]) / 2.f, - (aabb.max[2] - aabb.min[2]) / 2.f, - center.x(), center.y(), center.z()); -#endif - } - -#if 0 - for (const Node& n : nodes) { - zeus::simd_floats f(n.position.simd); - zeus::simd_floats no(n.position.simd + n.normal.simd); - os.format(fmt("v = bm.verts.new((%f,%f,%f))\n") - "v2 = bm.verts.new((%f,%f,%f))\n" - "bm.edges.new((v, v2))\n", f[0], f[1], f[2], no[0], no[1], no[2]); - } -#endif - - os << "bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)\n" - "path_mesh = bpy.data.meshes.new('PATH')\n" - "bm.to_mesh(path_mesh)\n" - "path_mesh_obj = bpy.data.objects.new(path_mesh.name, path_mesh)\n" - "\n" - "for mat_name in material_index:\n" - " mat = material_dict[mat_name]\n" - " path_mesh.materials.append(mat)\n" - "\n" - "bpy.context.scene.collection.objects.link(path_mesh_obj)\n" - "path_mesh_obj.display_type = 'SOLID'\n" - "bpy.context.scene.hecl_path_obj = path_mesh_obj.name\n" - "\n"; - - if (xf) { - const zeus::CMatrix4f& w = *xf; - zeus::simd_floats xfMtxF[4]; - for (int i = 0; i < 4; ++i) - w.m[i].mSimd.copy_to(xfMtxF[i]); - os.format(fmt( - "mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n" - "mtxd = mtx.decompose()\n" - "path_mesh_obj.rotation_mode = 'QUATERNION'\n" - "path_mesh_obj.location = mtxd[0]\n" - "path_mesh_obj.rotation_quaternion = mtxd[1]\n" - "path_mesh_obj.scale = mtxd[2]\n"), - xfMtxF[0][0], xfMtxF[1][0], xfMtxF[2][0], xfMtxF[3][0], xfMtxF[0][1], xfMtxF[1][1], xfMtxF[2][1], xfMtxF[3][1], - xfMtxF[0][2], xfMtxF[1][2], xfMtxF[2][2], xfMtxF[3][2]); - } - -#if DUMP_OCTREE - { - int idx = 0; - for (const auto& n : octree) { - if (n.isLeaf) - OutputOctreeNode(os, idx, zeus::CAABox(n.aabb[0], n.aabb[1])); - ++idx; - } - } -#endif - - os.linkBackground(fmt::format(fmt("//{}"), areaPath)); - os.centerView(); - os.close(); -} - bool PATH::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, PAKRouter& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok, std::function fileChanged) { @@ -200,45 +24,4 @@ bool PATH::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl: path.sendToBlender(conn, pakRouter.getBestEntryName(entry, false), xf, areaPath); return conn.saveBlend(); } - -bool PATH::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, - const PathMesh& mesh, hecl::blender::Token& btok) { - athena::io::MemoryReader r(mesh.data.data(), mesh.data.size()); - PATH path; - path.read(r); - if (!path.regions.empty()) { - AROTBuilder octreeBuilder; - octreeBuilder.buildPath(path); - } else { - path.octreeNodeCount = 1; - path.octree.emplace_back(); - OctreeNode& n = path.octree.back(); - n.isLeaf = 1; - n.aabb[0] = zeus::CVector3f{FLT_MAX, FLT_MAX, FLT_MAX}; - n.aabb[1] = zeus::CVector3f{-FLT_MAX, -FLT_MAX, -FLT_MAX}; - for (int i = 0; i < 8; ++i) - n.children[i] = 0xffffffff; - } - -#if DUMP_OCTREE - { - hecl::blender::Connection& conn = btok.getBlenderConnection(); - if (!conn.createBlend(inPath.getWithExtension(_SYS_STR(".octree.blend"), true), hecl::blender::BlendType::PathMesh)) - return false; - - zeus::CMatrix4f xf; - path.sendToBlender(conn, "PATH"sv, &xf); - conn.saveBlend(); - } -#endif - - athena::io::FileWriter w(outPath.getAbsolutePath()); - path.write(w); - int64_t rem = w.position() % 32; - if (rem) - for (int64_t i = 0; i < 32 - rem; ++i) - w.writeUByte(0xff); - return true; -} - -} // namespace DataSpec::DNAMP1 \ No newline at end of file +} \ No newline at end of file diff --git a/DataSpec/DNAMP1/PATH.hpp b/DataSpec/DNAMP1/PATH.hpp index 284a27fa0..5b954559b 100644 --- a/DataSpec/DNAMP1/PATH.hpp +++ b/DataSpec/DNAMP1/PATH.hpp @@ -1,79 +1,11 @@ #pragma once - -#include "DataSpec/DNACommon/DNACommon.hpp" -#include "DataSpec/DNACommon/PAK.hpp" -#include "DNAMP1.hpp" +#include "DataSpec/DNACommon/PATH.hpp" +#include "DataSpec/DNAMP1/DNAMP1.hpp" namespace DataSpec::DNAMP1 { -struct PATH : BigDNA { - using PathMesh = hecl::blender::PathMesh; - - AT_DECL_DNA - Value version; - - struct Node : BigDNA { - AT_DECL_DNA - Value position; - Value normal; - }; - Value nodeCount; - Vector nodes; - - struct Link : BigDNA { - AT_DECL_DNA - Value nodeIdx; - Value regionIdx; - Value width2d; - Value oneOverWidth2d; - }; - Value linkCount; - Vector links; - - struct Region : BigDNA { - AT_DECL_DNA - Value nodeCount; - Value nodeStart; - Value linkCount; - Value linkStart; - Value meshIndexMask; - Value meshTypeMask; - Value height; - Value normal; - Value regionIdx; - Value centroid; - Value aabb[2]; - Value regionIdxPtr; - }; - Value regionCount; - Vector regions; - - Vector bitmap1; - Vector bitmap2; - Vector bitmap3; - - Value octreeRegionLookupCount; - Vector octreeRegionLookup; - - struct OctreeNode : BigDNA { - AT_DECL_DNA - Value isLeaf; - Value aabb[2]; - Value centroid; - Value children[8]; - Value regionCount; - Value regionStart; - }; - Value octreeNodeCount; - Vector octree; - - void sendToBlender(hecl::blender::Connection& conn, std::string_view entryName, const zeus::CMatrix4f* xf, - const std::string& areaPath); - +struct PATH : DNAPATH::PATH { static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, PAKRouter& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok, std::function fileChanged); - - static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, - const PathMesh& mesh, hecl::blender::Token& btok); }; -} // namespace DataSpec::DNAMP1 +} // namespace DataSpec::DNAMP1 \ No newline at end of file diff --git a/DataSpec/DNAMP2/CMakeLists.txt b/DataSpec/DNAMP2/CMakeLists.txt index 504d14541..84b43fe51 100644 --- a/DataSpec/DNAMP2/CMakeLists.txt +++ b/DataSpec/DNAMP2/CMakeLists.txt @@ -23,6 +23,7 @@ set(DNAMP2_SOURCES MREA.cpp MAPA.hpp MAPU.hpp + PATH.hpp PATH.cpp AFSM.hpp STRG.hpp STRG.cpp) diff --git a/DataSpec/DNAMP2/DNAMP2.cpp b/DataSpec/DNAMP2/DNAMP2.cpp index d95d84fe1..679a0e8cf 100644 --- a/DataSpec/DNAMP2/DNAMP2.cpp +++ b/DataSpec/DNAMP2/DNAMP2.cpp @@ -8,6 +8,7 @@ #include "MREA.hpp" #include "MAPA.hpp" #include "MAPU.hpp" +#include "PATH.hpp" #include "AFSM.hpp" #include "SAVW.hpp" #include "AGSC.hpp" @@ -171,6 +172,18 @@ void PAKBridge::addCMDLRigPairs(PAKRouter& pakRouter, CharacterAssoci } } +void PAKBridge::addPATHToMREA(PAKRouter& pakRouter, + std::unordered_map& pathToMrea) const { + for (const auto& [id, entry] : m_pak.m_entries) { + if (entry.type == FOURCC('MREA')) { + PAKEntryReadStream rs = entry.beginReadStream(m_node); + UniqueID32 pathID = MREA::GetPATHId(rs); + if (pathID.isValid()) + pathToMrea[pathID] = id; + } + } +} + static const atVec4f BottomRow = {{0.f, 0.f, 0.f, 1.f}}; void PAKBridge::addMAPATransforms(PAKRouter& pakRouter, @@ -190,6 +203,18 @@ void PAKBridge::addMAPATransforms(PAKRouter& pakRouter, fmt::format(fmt(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId)); for (const MLVL::Area& area : mlvl.areas) { +#if 0 + { + /* Get PATH transform */ + const nod::Node* areaNode; + const PAK::Entry* areaEntry = pakRouter.lookupEntry(area.areaMREAId, &areaNode); + PAKEntryReadStream rs = areaEntry->beginReadStream(*areaNode); + UniqueID32 pathId = MREA::GetPATHId(rs); + if (pathId.isValid()) + addTo[pathId] = zeus::CMatrix4f(area.transformMtx[0], area.transformMtx[1], area.transformMtx[2], BottomRow) + .transposed(); + } +#endif hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath(); if (area.areaNameId.isValid()) pathOverrides[area.areaNameId] = hecl::ProjectPath(areaDirPath, @@ -247,6 +272,8 @@ ResExtractor PAKBridge::LookupExtractor(const nod::Node& pakNode, con return {MAPA::Extract, {_SYS_STR(".blend")}, 4}; case SBIG('MAPU'): return {MAPU::Extract, {_SYS_STR(".blend")}, 5}; +// case SBIG('PATH'): +// return {PATH::Extract, {_SYS_STR(".blend")}, 5}; case SBIG('FSM2'): return {DNAFSM2::ExtractFSM2, {_SYS_STR(".yaml")}}; case SBIG('FONT'): diff --git a/DataSpec/DNAMP2/DNAMP2.hpp b/DataSpec/DNAMP2/DNAMP2.hpp index 279bba2c3..ee62edc12 100644 --- a/DataSpec/DNAMP2/DNAMP2.hpp +++ b/DataSpec/DNAMP2/DNAMP2.hpp @@ -31,6 +31,9 @@ public: void addCMDLRigPairs(PAKRouter& pakRouter, CharacterAssociations& charAssoc) const; + void addPATHToMREA(PAKRouter& pakRouter, + std::unordered_map& pathToMrea) const; + void addMAPATransforms(PAKRouter& pakRouter, std::unordered_map& addTo, std::unordered_map& pathOverrides) const; }; diff --git a/DataSpec/DNAMP2/MREA.cpp b/DataSpec/DNAMP2/MREA.cpp index ba2bd6adb..01c39e07a 100644 --- a/DataSpec/DNAMP2/MREA.cpp +++ b/DataSpec/DNAMP2/MREA.cpp @@ -296,5 +296,22 @@ bool MREA::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl: return conn.saveBlend(); } +UniqueID32 MREA::GetPATHId(PAKEntryReadStream& rs) { + /* Do extract */ + Header head; + head.read(rs); + rs.seekAlign32(); + + /* Skip to PATH */ + atUint32 curSec = 0; + atUint64 secStart = rs.position(); + while (curSec != head.pathSecIdx) + secStart += head.secSizes[curSec++]; + if (!head.secSizes[curSec]) + return {}; + rs.seek(secStart, athena::SeekOrigin::Begin); + return {rs}; +} + } // namespace DNAMP2 } // namespace DataSpec diff --git a/DataSpec/DNAMP2/MREA.hpp b/DataSpec/DNAMP2/MREA.hpp index 5e678d5b7..6dd18b551 100644 --- a/DataSpec/DNAMP2/MREA.hpp +++ b/DataSpec/DNAMP2/MREA.hpp @@ -118,6 +118,8 @@ struct MREA { Value aabb[2]; }; + static UniqueID32 GetPATHId(PAKEntryReadStream& rs); + static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, PAKRouter& pakRouter, const DNAMP2::PAK::Entry& entry, bool, hecl::blender::Token& btok, std::function); diff --git a/DataSpec/DNAMP2/PATH.cpp b/DataSpec/DNAMP2/PATH.cpp new file mode 100644 index 000000000..058fd5243 --- /dev/null +++ b/DataSpec/DNAMP2/PATH.cpp @@ -0,0 +1,26 @@ +#include "DataSpec/DNAMP2/PATH.hpp" +#include "hecl/Blender/Connection.hpp" + +namespace DataSpec::DNAMP2 { +bool PATH::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, + PAKRouter& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok, + std::function fileChanged) { + PATH path; + path.read(rs); + hecl::blender::Connection& conn = btok.getBlenderConnection(); + if (!conn.createBlend(outPath, hecl::blender::BlendType::PathMesh)) + return false; + + std::string areaPath; + for (const auto& ent : hecl::DirectoryEnumerator(outPath.getParentPath().getAbsolutePath())) { + if (hecl::StringUtils::BeginsWith(ent.m_name, _SYS_STR("!area_"))) { + areaPath = hecl::SystemUTF8Conv(ent.m_name).str(); + break; + } + } + + const zeus::CMatrix4f* xf = pakRouter.lookupMAPATransform(entry.id); + path.sendToBlender(conn, pakRouter.getBestEntryName(entry, false), xf, areaPath); + return conn.saveBlend(); +} +} \ No newline at end of file diff --git a/DataSpec/DNAMP2/PATH.hpp b/DataSpec/DNAMP2/PATH.hpp new file mode 100644 index 000000000..fd8e0ab3e --- /dev/null +++ b/DataSpec/DNAMP2/PATH.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "DataSpec/DNACommon/PATH.hpp" +#include "DataSpec/DNAMP2/DNAMP2.hpp" + +namespace DataSpec::DNAMP2 { +struct PATH : DNAPATH::PATH { + static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, + PAKRouter& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok, + std::function fileChanged); + + static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, + const PathMesh& mesh, hecl::blender::Token& btok); +}; +} \ No newline at end of file diff --git a/DataSpec/SpecMP1.cpp b/DataSpec/SpecMP1.cpp index ddf973351..4e0915763 100644 --- a/DataSpec/SpecMP1.cpp +++ b/DataSpec/SpecMP1.cpp @@ -661,7 +661,7 @@ struct SpecMP1 : SpecBase { hecl::blender::Token& btok, FCookProgress progress) override { PathMesh mesh = ds.compilePathMesh(); ds.close(); - DNAMP1::PATH::Cook(out, in, mesh, btok); + DNAPATH::PATH::Cook(out, in, mesh, btok); } void cookActor(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast, diff --git a/DataSpec/SpecMP2.cpp b/DataSpec/SpecMP2.cpp index 47a8e96cb..c6b76e313 100644 --- a/DataSpec/SpecMP2.cpp +++ b/DataSpec/SpecMP2.cpp @@ -9,6 +9,7 @@ #include "DNAMP2/MAPA.hpp" #include "DNAMP1/CSNG.hpp" #include "DNACommon/MAPU.hpp" +#include "DNACommon/PATH.hpp" #include "hecl/ClientProcess.hpp" #include "hecl/Blender/Connection.hpp" @@ -287,7 +288,11 @@ struct SpecMP2 : SpecBase { hecl::blender::Token& btok, FCookProgress progress) override {} void cookPathMesh(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast, - hecl::blender::Token& btok, FCookProgress progress) override {} + hecl::blender::Token& btok, FCookProgress progress) override { + PathMesh mesh = ds.compilePathMesh(); + ds.close(); + DNAPATH::PATH::Cook(out, in, mesh, btok); + } void cookActor(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast, hecl::blender::Token& btok, FCookProgress progress) override {} diff --git a/Runtime/MP1/MP1.hpp b/Runtime/MP1/MP1.hpp index 397031000..cf25985ec 100644 --- a/Runtime/MP1/MP1.hpp +++ b/Runtime/MP1/MP1.hpp @@ -4,7 +4,7 @@ #define MP1_USE_BOO 0 #endif #ifndef MP1_VARIABLE_DELTA_TIME -#define MP1_VARIABLE_DELTA_TIME 0 +#define MP1_VARIABLE_DELTA_TIME 1 #endif #include "IMain.hpp"