diff --git a/CMakeLists.txt b/CMakeLists.txt index e8a62f958..092c541dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,3 @@ include_directories(${ATHENA_INCLUDE_DIR} ${LOG_VISOR_INCLUDE_DIR} ${ANGELSCRIPT set(NOD_LIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/NODLib/include) add_subdirectory(DataSpec) add_subdirectory(Runtime) - -include_directories(include) -add_library(RetroCommon - src/RetroCommon.cpp include/RetroCommon.hpp - src/MREADecompress.cpp) diff --git a/DataSpec/DNACommon/ANCS.hpp b/DataSpec/DNACommon/ANCS.hpp index eab181a5a..20ff8c316 100644 --- a/DataSpec/DNACommon/ANCS.hpp +++ b/DataSpec/DNACommon/ANCS.hpp @@ -1,6 +1,7 @@ #ifndef _DNACOMMON_ANCS_HPP_ #define _DNACOMMON_ANCS_HPP_ +#include #include "DNACommon.hpp" #include "BlenderConnection.hpp" #include "CMDL.hpp" @@ -27,8 +28,8 @@ bool ReadANCSToBlender(HECL::BlenderConnection& conn, const typename PAKRouter::EntryType& entry, const HECL::ProjectPath& masterShader, bool force=false) -{ - /* Extract characters first */ +{ + /* Extract character CMDL/CSKR first */ std::vector> chResInfo; ancs.getCharacterResInfo(chResInfo); for (const auto& info : chResInfo) @@ -40,12 +41,17 @@ bool ReadANCSToBlender(HECL::BlenderConnection& conn, { if (!conn.createBlend(cmdlPath.getAbsolutePath())) return false; - PAKEntryReadStream rs = cmdlE->beginReadStream(*node); - DNACMDL::ReadCMDLToBlender - (conn, rs, pakRouter, entry, masterShader); - const typename PAKRouter::EntryType* cskrE = pakRouter.lookupEntry(info.cskr); - const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf); + typename ANCSDNA::CSKRType cskr; + pakRouter.lookupAndReadDNA(info.cskr, cskr); + typename ANCSDNA::CINFType cinf; + pakRouter.lookupAndReadDNA(info.cinf, cinf); + using RIGPair = std::pair; + RIGPair rigPair(&cskr, &cinf); + + PAKEntryReadStream rs = cmdlE->beginReadStream(*node); + DNACMDL::ReadCMDLToBlender + (conn, rs, pakRouter, *cmdlE, masterShader, &rigPair); conn.saveBlend(); } @@ -56,14 +62,76 @@ bool ReadANCSToBlender(HECL::BlenderConnection& conn, return false; HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut(true); + os.format("import bpy\n" + "from mathutils import Vector\n" + "bpy.context.scene.name = '%s'\n" + "bpy.context.scene.hecl_type = 'ACTOR'\n" + "bpy.context.scene.hecl_mesh_obj = bpy.context.scene.name\n" + "\n" + "# Using 'Blender Game'\n" + "bpy.context.scene.render.engine = 'BLENDER_GAME'\n" + "\n" + "# Clear Scene\n" + "for ob in bpy.data.objects:\n" + " if ob.type != 'LAMP':\n" + " bpy.context.scene.objects.unlink(ob)\n" + " bpy.data.objects.remove(ob)\n" + "\n" + "actor_data = bpy.context.scene.hecl_sact_data\n", + pakRouter.getBestEntryName(entry).c_str()); + + typename ANCSDNA::CINFType cinf; + std::unordered_set cinfsDone; + for (const auto& info : chResInfo) + { + /* Build CINF if needed */ + if (cinfsDone.find(info.cinf) == cinfsDone.end()) + { + pakRouter.lookupAndReadDNA(info.cinf, cinf); + cinf.sendCINFToBlender(os, info.cinf); + cinfsDone.insert(info.cinf); + } + else + os.format("arm_obj = bpy.data.objects['CINF_%08X']\n", info.cinf.toUint32()); + + /* Link CMDL */ + const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl); + HECL::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE); + os.linkBlend(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE), true); + + /* Attach CMDL to CINF */ + os << "if obj.name not in bpy.context.scene.objects:\n" + " bpy.context.scene.objects.link(obj)\n" + "obj.parent = arm_obj\n" + "obj.parent_type = 'ARMATURE'\n" + "\n"; + + /* Provide data to add-on */ + os.format("actor_subtype = actor_data.subtypes.add()\n" + "actor_subtype.name = '%s'\n" + "actor_subtype.linked_armature = arm_obj.name\n" + "actor_subtype.linked_mesh = obj.name\n\n", + info.name.c_str()); + } + /* Get animation primitives */ - std::unordered_set animResInfo; + std::map> animResInfo; ancs.getAnimationResInfo(animResInfo); for (const auto& id : animResInfo) { - const typename PAKRouter::EntryType* animE = pakRouter.lookupEntry(id); + typename ANCSDNA::ANIMType anim; + pakRouter.lookupAndReadDNA(id.second.second, anim); + + os.format("act = bpy.data.actions.new('%s')\n" + "act.use_fake_user = True\n", id.second.first.c_str()); + anim.sendANIMToBlender(os, cinf); + + os.format("actor_action = actor_data.actions.add()\n" + "actor_action.name = '%s'\n", id.second.first.c_str()); } + os.close(); + conn.saveBlend(); return true; } diff --git a/DataSpec/DNACommon/CMDL.hpp b/DataSpec/DNACommon/CMDL.hpp index 5e1d4dc11..69e77e7a0 100644 --- a/DataSpec/DNACommon/CMDL.hpp +++ b/DataSpec/DNACommon/CMDL.hpp @@ -293,12 +293,13 @@ public: } }; -template +template bool ReadCMDLToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReader& reader, PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, - const HECL::ProjectPath& masterShader) + const HECL::ProjectPath& masterShader, + const RIGPAIR* rp=nullptr) { reader.setEndian(Athena::BigEndian); @@ -401,6 +402,9 @@ bool ReadCMDLToBlender(HECL::BlenderConnection& conn, "bm = bmesh.new()\n" "\n", pakRouter.getBestEntryName(entry).c_str()); + if (rp) + os << "dvert_lay = bm.verts.layers.deform.verify()\n"; + /* Link master shader library */ os.format("# Master shader library\n" "with bpy.data.libraries.load('%s', link=True, relative=True) as (data_from, data_to):\n" @@ -520,8 +524,10 @@ bool ReadCMDLToBlender(HECL::BlenderConnection& conn, for (size_t i=0 ; i<=maxIdxs.pos ; ++i) { atVec3f pos = reader.readVec3f(); - os.format("bm.verts.new((%f,%f,%f))\n", + os.format("vert = bm.verts.new((%f,%f,%f))\n", pos.vec[0], pos.vec[1], pos.vec[2]); + if (rp) + rp->first->weightVertex(os, *rp->second, i); } break; } @@ -817,6 +823,9 @@ bool ReadCMDLToBlender(HECL::BlenderConnection& conn, "bm.free()\n" "\n", head.matSetCount); + if (rp) + rp->second->sendVertexGroupsToBlender(os); + return true; } diff --git a/DataSpec/DNACommon/DNACommon.hpp b/DataSpec/DNACommon/DNACommon.hpp index a41424801..16fe6b07a 100644 --- a/DataSpec/DNACommon/DNACommon.hpp +++ b/DataSpec/DNACommon/DNACommon.hpp @@ -571,6 +571,21 @@ public: *nodeOut = nullptr; return nullptr; } + + template + bool lookupAndReadDNA(const typename BRIDGETYPE::PAKType::IDType& id, DNA& out) + { + const NOD::DiscBase::IPartition::Node* node; + const typename BRIDGETYPE::PAKType::Entry* entry = lookupEntry(id, &node); + if (!entry) + { + LogDNACommon.report(LogVisor::Error, "unable to find PAK entry %s", id.toString().c_str()); + return false; + } + PAKEntryReadStream rs = entry->beginReadStream(*node); + out.read(rs); + return true; + } }; /* Resource cooker function */ diff --git a/DataSpec/DNAMP1/ANCS.hpp b/DataSpec/DNAMP1/ANCS.hpp index 1b2bffcea..28983bdec 100644 --- a/DataSpec/DNAMP1/ANCS.hpp +++ b/DataSpec/DNAMP1/ANCS.hpp @@ -1,11 +1,14 @@ #ifndef _DNAMP1_ANCS_HPP_ #define _DNAMP1_ANCS_HPP_ -#include +#include #include "../DNACommon/DNACommon.hpp" #include "../DNACommon/ANCS.hpp" #include "CMDLMaterials.hpp" #include "BlenderConnection.hpp" +#include "CINF.hpp" +#include "CSKR.hpp" +#include "ANIM.hpp" namespace Retro { @@ -14,6 +17,10 @@ namespace DNAMP1 struct ANCS : BigYAML { + using CINFType = CINF; + using CSKRType = CSKR; + using ANIMType = ANIM; + DECL_YAML Value version; @@ -163,7 +170,7 @@ struct ANCS : BigYAML const char* m_typeStr; IMetaAnim(Type type, const char* typeStr) : m_type(type), m_typeStr(typeStr) {} - virtual void gatherPrimitives(std::unordered_set& out)=0; + virtual void gatherPrimitives(std::map>& out)=0; }; struct MetaAnimFactory : BigYAML { @@ -181,9 +188,9 @@ struct ANCS : BigYAML Value unk1; Value unk2; - void gatherPrimitives(std::unordered_set& out) + void gatherPrimitives(std::map>& out) { - out.insert(animId); + out[animIdx] = std::make_pair(animName, animId); } }; struct MetaAnimBlend : IMetaAnim @@ -195,7 +202,7 @@ struct ANCS : BigYAML Value unkFloat; Value unk; - void gatherPrimitives(std::unordered_set& out) + void gatherPrimitives(std::map>& out) { animA.m_anim->gatherPrimitives(out); animB.m_anim->gatherPrimitives(out); @@ -210,7 +217,7 @@ struct ANCS : BigYAML Value unkFloat; Value unk; - void gatherPrimitives(std::unordered_set& out) + void gatherPrimitives(std::map>& out) { animA.m_anim->gatherPrimitives(out); animB.m_anim->gatherPrimitives(out); @@ -229,7 +236,7 @@ struct ANCS : BigYAML }; Vector children; - void gatherPrimitives(std::unordered_set& out) + void gatherPrimitives(std::map>& out) { for (const auto& child : children) child.anim.m_anim->gatherPrimitives(out); @@ -242,7 +249,7 @@ struct ANCS : BigYAML Value animCount; Vector children; - void gatherPrimitives(std::unordered_set& out) + void gatherPrimitives(std::map>& out) { for (const auto& child : children) child.m_anim->gatherPrimitives(out); @@ -359,7 +366,7 @@ struct ANCS : BigYAML } } - void getAnimationResInfo(std::unordered_set& out) const + void getAnimationResInfo(std::map>& out) const { out.clear(); for (const AnimationSet::Animation& ai : animationSet.animations) diff --git a/DataSpec/DNAMP1/ANIM.cpp b/DataSpec/DNAMP1/ANIM.cpp index c86895851..bb8902950 100644 --- a/DataSpec/DNAMP1/ANIM.cpp +++ b/DataSpec/DNAMP1/ANIM.cpp @@ -5,6 +5,72 @@ namespace Retro namespace DNAMP1 { +void ANIM::IANIM::sendANIMToBlender(HECL::BlenderConnection::PyOutStream& os, const CINF& cinf) const +{ + os.format("bone_trans_heads = []\n" + "act.hecl_fps = round(%f)\n", (1.0f / mainInterval)); + + auto kit = chanKeys.begin(); + for (const std::pair& bone : bones) + { + os.format("bone_string = '%s'\n", cinf.getBoneNameFromId(bone.first)->c_str()); + os << "action_group = act.groups.new(bone_string)\n" + "\n" + "crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=0, action_group=bone_string)\n" + "crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=1, action_group=bone_string)\n" + "crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=2, action_group=bone_string)\n" + "crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=3, action_group=bone_string)\n" + "\n"; + + if (bone.second) + os << "bone_parent_head = (0.0,0.0,0.0)\n" + "if arm_obj.data.bones[bone_string].parent is not None:\n" + " bone_parent_head = Vector(arm_obj.data.bones[bone_string].head_local) - Vector(arm_obj.data.bones[bone_string].parent.head_local)\n" + "bone_trans_heads.append(bone_parent_head)\n" + "crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=0, action_group=bone_string)\n" + "crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=1, action_group=bone_string)\n" + "crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=2, action_group=bone_string)\n" + "\n"; + else + os << "bone_trans_heads.append((0,0,0))\n"; + + os << "crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_mode', action_group=bone_string)\n" + "crv.keyframe_points.add()\n" + "crv.keyframe_points[-1].co = (0, 0)\n" + "crv.keyframe_points[-1].interpolation = 'LINEAR'\n" + "\n"; + + const std::vector& rotKeys = *kit++; + auto timeit = times.begin(); + for (const DNAANIM::Value& val : rotKeys) + { + float time = *timeit++; + for (int c=0 ; c<4 ; ++c) + os.format("crv = act.fcurves[%d]\n" + "crv.keyframe_points.add()\n" + "crv.keyframe_points[-1].interpolation = 'LINEAR'\n" + "crv.keyframe_points[-1].co = (%f, %f)\n", + c, time, val.v4.vec[c]); + } + + if (bone.second) + { + const std::vector& transKeys = *kit++; + auto timeit = times.begin(); + for (const DNAANIM::Value& val : transKeys) + { + float time = *timeit++; + for (int c=0 ; c<3 ; ++c) + os.format("crv = act.fcurves[%d+4]\n" + "crv.keyframe_points.add()\n" + "crv.keyframe_points[-1].interpolation = 'LINEAR'\n" + "crv.keyframe_points[-1].co = (%f, %f)\n", + c, time, val.v4.vec[c]); + } + } + } +} + void ANIM::ANIM0::read(Athena::io::IStreamReader& reader) { Header head; @@ -174,6 +240,7 @@ void ANIM::ANIM2::read(Athena::io::IStreamReader& reader) times.push_back(timeAccum); timeAccum += head.interval; } + reader.seek(8); bones.clear(); bones.reserve(head.boneChannelCount); @@ -256,6 +323,8 @@ void ANIM::ANIM2::write(Athena::io::IStreamWriter& writer) const head.write(writer); keyBmp.write(writer); + writer.writeUint32(head.boneChannelCount); + writer.writeUint32(head.boneChannelCount); auto cit = qChannels.begin(); for (const std::pair& bone : bones) { diff --git a/DataSpec/DNAMP1/ANIM.hpp b/DataSpec/DNAMP1/ANIM.hpp index ef324116b..cfe3cac4a 100644 --- a/DataSpec/DNAMP1/ANIM.hpp +++ b/DataSpec/DNAMP1/ANIM.hpp @@ -1,8 +1,10 @@ #ifndef _DNAMP1_ANIM_HPP_ #define _DNAMP1_ANIM_HPP_ +#include "BlenderConnection.hpp" #include "DNAMP1.hpp" #include "../DNACommon/ANIM.hpp" +#include "CINF.hpp" namespace Retro { @@ -25,6 +27,8 @@ struct ANIM : BigDNA std::vector> chanKeys; float mainInterval = 0.0; UniqueID32 evnt; + + void sendANIMToBlender(HECL::BlenderConnection::PyOutStream&, const CINF&) const; }; struct ANIM0 : IANIM @@ -134,6 +138,7 @@ struct ANIM : BigDNA std::unique_ptr m_anim; void read(Athena::io::IStreamReader& reader) { + reader.setEndian(Athena::BigEndian); atUint32 version = reader.readUint32(); switch (version) { @@ -153,9 +158,16 @@ struct ANIM : BigDNA void write(Athena::io::IStreamWriter& writer) const { + writer.setEndian(Athena::BigEndian); writer.writeUint32(m_anim->m_version); m_anim->write(writer); } + + void sendANIMToBlender(HECL::BlenderConnection::PyOutStream& os, const CINF& cinf) const + { + m_anim->sendANIMToBlender(os, cinf); + } + }; } diff --git a/DataSpec/DNAMP1/CINF.hpp b/DataSpec/DNAMP1/CINF.hpp index 1f51501a0..34f24fd52 100644 --- a/DataSpec/DNAMP1/CINF.hpp +++ b/DataSpec/DNAMP1/CINF.hpp @@ -1,6 +1,7 @@ #ifndef _DNAMP1_CINF_HPP_ #define _DNAMP1_CINF_HPP_ +#include "BlenderConnection.hpp" #include "../DNACommon/DNACommon.hpp" namespace Retro @@ -34,6 +35,66 @@ struct CINF : BigDNA Value boneId; }; Vector names; + + atUint32 getBoneIdxFromId(atUint32 id) const + { + atUint32 idx = 0; + for (atUint32 bid : boneIds) + { + if (bid == id) + return idx; + ++idx; + } + return 0; + } + + const std::string* getBoneNameFromId(atUint32 id) const + { + for (const Name& name : names) + if (id == name.boneId) + return &name.name; + return nullptr; + } + + void sendVertexGroupsToBlender(HECL::BlenderConnection::PyOutStream& os) const + { + for (atUint32 bid : boneIds) + { + for (const Name& name : names) + { + if (name.boneId == bid) + { + os.format("obj.vertex_groups.new('%s')\n", name.name.c_str()); + break; + } + } + } + } + + void sendCINFToBlender(HECL::BlenderConnection::PyOutStream& os, const UniqueID32& cinfId) const + { + os.format("arm = bpy.data.armatures.new('CINF_%08X')\n" + "arm_obj = bpy.data.objects.new(arm.name, arm)\n" + "bpy.context.scene.objects.link(arm_obj)\n" + "bpy.context.scene.objects.active = arm_obj\n" + "bpy.ops.object.mode_set(mode='EDIT')\n" + "arm_bone_table = {}\n", + cinfId.toUint32()); + + for (const Bone& bone : bones) + os.format("bone = arm.edit_bones.new('%s')\n" + "bone.head = (%f,%f,%f)\n" + "bone.tail = bone.head\n" + "bone.tail[1] += 0.5\n" + "arm_bone_table[%u] = bone\n", getBoneNameFromId(bone.id)->c_str(), + bone.origin.vec[0], bone.origin.vec[1], bone.origin.vec[2], bone.id); + + for (const Bone& bone : bones) + if (bone.parentId != 2) + os.format("arm_bone_table[%u].parent = arm_bone_table[%u]\n", bone.id, bone.parentId); + + os << "bpy.ops.object.mode_set(mode='OBJECT')\n"; + } }; } diff --git a/DataSpec/DNAMP1/CMDL.hpp b/DataSpec/DNAMP1/CMDL.hpp index 77de4ba42..66348412d 100644 --- a/DataSpec/DNAMP1/CMDL.hpp +++ b/DataSpec/DNAMP1/CMDL.hpp @@ -5,6 +5,8 @@ #include "../DNACommon/CMDL.hpp" #include "CMDLMaterials.hpp" #include "DNAMP1.hpp" +#include "CINF.hpp" +#include "CSKR.hpp" namespace Retro { @@ -23,7 +25,7 @@ struct CMDL HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection(); if (!conn.createBlend(outPath.getAbsolutePath())) return false; - DNACMDL::ReadCMDLToBlender, MaterialSet, 2> + DNACMDL::ReadCMDLToBlender, MaterialSet, std::pair, 2> (conn, rs, pakRouter, entry, dataSpec.getMasterShaderPath()); return conn.saveBlend(); } diff --git a/DataSpec/DNAMP1/CSKR.hpp b/DataSpec/DNAMP1/CSKR.hpp index 8510a24ca..f7903d3c1 100644 --- a/DataSpec/DNAMP1/CSKR.hpp +++ b/DataSpec/DNAMP1/CSKR.hpp @@ -1,7 +1,9 @@ #ifndef _DNAMP1_CSKR_HPP_ #define _DNAMP1_CSKR_HPP_ +#include "BlenderConnection.hpp" #include "../DNACommon/DNACommon.hpp" +#include "CINF.hpp" namespace Retro { @@ -26,6 +28,20 @@ struct CSKR : BigDNA Value vertCount; }; Vector skinningRules; + + void weightVertex(HECL::BlenderConnection::PyOutStream& os, const CINF& cinf, atUint32 idx) const + { + atUint32 accum = 0; + for (const SkinningRule& rule : skinningRules) + { + if (idx >= accum && idx < accum + rule.vertCount) + for (const SkinningRule::Weight& weight : rule.weights) + os.format("vert[dvert_lay][%u] = %f\n", + cinf.getBoneIdxFromId(weight.boneId), + weight.weight); + accum += rule.vertCount; + } + } }; } diff --git a/DataSpec/DNAMP1/DNAMP1.cpp b/DataSpec/DNAMP1/DNAMP1.cpp index c58d35fab..c54cc54bc 100644 --- a/DataSpec/DNAMP1/DNAMP1.cpp +++ b/DataSpec/DNAMP1/DNAMP1.cpp @@ -174,16 +174,16 @@ void PAKBridge::build() ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry) { - switch (entry.type.toUint32()) + switch (entry.type) { case SBIG('STRG'): return {STRG::Extract, nullptr, ".yaml"}; case SBIG('TXTR'): return {TXTR::Extract, nullptr, ".png"}; case SBIG('CMDL'): - return {nullptr, CMDL::Extract, ".blend", 1}; + return {nullptr, CMDL::Extract, ".blend", 2}; case SBIG('ANCS'): - return {nullptr, ANCS::Extract, nullptr}; + return {nullptr, ANCS::Extract, nullptr, 1}; case SBIG('MLVL'): return {MLVL::Extract, nullptr, ".yaml"}; } diff --git a/DataSpec/DNAMP2/CINF.hpp b/DataSpec/DNAMP2/CINF.hpp new file mode 100644 index 000000000..39512e1da --- /dev/null +++ b/DataSpec/DNAMP2/CINF.hpp @@ -0,0 +1,103 @@ +#ifndef _DNAMP2_CINF_HPP_ +#define _DNAMP2_CINF_HPP_ + +#include "BlenderConnection.hpp" +#include "../DNACommon/DNACommon.hpp" + +namespace Retro +{ +namespace DNAMP2 +{ + +struct CINF : BigDNA +{ + DECL_DNA + Value boneCount; + struct Bone : BigDNA + { + DECL_DNA + Value id; + Value parentId; + Value origin; + Value linkedCount; + Vector linked; + }; + Vector bones; + + Value boneIdCount; + Vector boneIds; + + Value nameCount; + struct Name : BigDNA + { + DECL_DNA + String<-1> name; + Value boneId; + }; + Vector names; + + atUint32 getBoneIdxFromId(atUint32 id) const + { + atUint32 idx = 0; + for (atUint32 bid : boneIds) + { + if (bid == id) + return idx; + ++idx; + } + return 0; + } + + const std::string* getBoneNameFromId(atUint32 id) const + { + for (const Name& name : names) + if (id == name.boneId) + return &name.name; + return nullptr; + } + + void sendVertexGroupsToBlender(HECL::BlenderConnection::PyOutStream& os) const + { + for (atUint32 bid : boneIds) + { + for (const Name& name : names) + { + if (name.boneId == bid) + { + os.format("obj.vertex_groups.new('%s')\n", name.name.c_str()); + break; + } + } + } + } + + void sendCINFToBlender(HECL::BlenderConnection::PyOutStream& os, const UniqueID32& cinfId) const + { + os.format("arm = bpy.data.armatures.new('CINF_%08X')\n" + "arm_obj = bpy.data.objects.new(arm.name, arm)\n" + "bpy.context.scene.objects.link(arm_obj)\n" + "bpy.context.scene.objects.active = arm_obj\n" + "bpy.ops.object.mode_set(mode='EDIT')\n" + "arm_bone_table = {}\n", + cinfId.toUint32()); + + for (const Bone& bone : bones) + os.format("bone = arm.edit_bones.new('%s')\n" + "bone.head = (%f,%f,%f)\n" + "bone.tail = bone.head\n" + "bone.tail[1] += 0.5\n" + "arm_bone_table[%u] = bone\n", getBoneNameFromId(bone.id)->c_str(), + bone.origin.vec[0], bone.origin.vec[1], bone.origin.vec[2], bone.id); + + for (const Bone& bone : bones) + if (bone.parentId != 2) + os.format("arm_bone_table[%u].parent = arm_bone_table[%u]\n", bone.id, bone.parentId); + + os << "bpy.ops.object.mode_set(mode='OBJECT')\n"; + } +}; + +} +} + +#endif // _DNAMP2_CINF_HPP_ diff --git a/DataSpec/DNAMP2/CMDL.hpp b/DataSpec/DNAMP2/CMDL.hpp index d37f36dfb..937a36778 100644 --- a/DataSpec/DNAMP2/CMDL.hpp +++ b/DataSpec/DNAMP2/CMDL.hpp @@ -5,6 +5,8 @@ #include "../DNACommon/CMDL.hpp" #include "CMDLMaterials.hpp" #include "DNAMP2.hpp" +#include "CINF.hpp" +#include "CSKR.hpp" namespace Retro { @@ -23,7 +25,7 @@ struct CMDL HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection(); if (!conn.createBlend(outPath.getAbsolutePath())) return false; - DNACMDL::ReadCMDLToBlender, MaterialSet, 4> + DNACMDL::ReadCMDLToBlender, MaterialSet, std::pair, 4> (conn, rs, pakRouter, entry, dataSpec.getMasterShaderPath()); return conn.saveBlend(); } diff --git a/DataSpec/DNAMP2/CMakeLists.txt b/DataSpec/DNAMP2/CMakeLists.txt index f77beb7bc..d23079bfb 100644 --- a/DataSpec/DNAMP2/CMakeLists.txt +++ b/DataSpec/DNAMP2/CMakeLists.txt @@ -1,6 +1,8 @@ make_dnalist(liblist MLVL - CMDLMaterials) + CMDLMaterials + CINF + CSKR) add_library(DNAMP2 DNAMP2.hpp DNAMP2.cpp ${liblist} diff --git a/DataSpec/DNAMP2/CSKR.hpp b/DataSpec/DNAMP2/CSKR.hpp new file mode 100644 index 000000000..b5adfd20b --- /dev/null +++ b/DataSpec/DNAMP2/CSKR.hpp @@ -0,0 +1,50 @@ +#ifndef _DNAMP2_CSKR_HPP_ +#define _DNAMP2_CSKR_HPP_ + +#include "BlenderConnection.hpp" +#include "../DNACommon/DNACommon.hpp" +#include "CINF.hpp" + +namespace Retro +{ +namespace DNAMP2 +{ + +struct CSKR : BigDNA +{ + DECL_DNA + Value skinningRuleCount; + struct SkinningRule : BigDNA + { + DECL_DNA + Value weightCount; + struct Weight : BigDNA + { + DECL_DNA + Value boneId; + Value weight; + }; + Vector weights; + Value vertCount; + }; + Vector skinningRules; + + void weightVertex(HECL::BlenderConnection::PyOutStream& os, const CINF& cinf, atUint32 idx) const + { + atUint32 accum = 0; + for (const SkinningRule& rule : skinningRules) + { + if (idx < accum + rule.vertCount) + for (const SkinningRule::Weight& weight : rule.weights) + os.format("vert[dvert_lay][%u] = %f\n", + cinf.getBoneIdxFromId(weight.boneId), + weight.weight); + accum += rule.vertCount; + } + } +}; + +} +} + +#endif // _DNAMP2_CSKR_HPP_ diff --git a/DataSpec/DNAMP2/DNAMP2.cpp b/DataSpec/DNAMP2/DNAMP2.cpp index fbf550206..21635de30 100644 --- a/DataSpec/DNAMP2/DNAMP2.cpp +++ b/DataSpec/DNAMP2/DNAMP2.cpp @@ -181,7 +181,7 @@ void PAKBridge::build() ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry) { - switch (entry.type.toUint32()) + switch (entry.type) { case SBIG('STRG'): return {STRG::Extract, nullptr, ".yaml"}; diff --git a/DataSpec/DNAMP3/CINF.hpp b/DataSpec/DNAMP3/CINF.hpp new file mode 100644 index 000000000..9a0c53efc --- /dev/null +++ b/DataSpec/DNAMP3/CINF.hpp @@ -0,0 +1,103 @@ +#ifndef _DNAMP3_CINF_HPP_ +#define _DNAMP3_CINF_HPP_ + +#include "BlenderConnection.hpp" +#include "../DNACommon/DNACommon.hpp" + +namespace Retro +{ +namespace DNAMP3 +{ + +struct CINF : BigDNA +{ + DECL_DNA + Value boneCount; + struct Bone : BigDNA + { + DECL_DNA + Value id; + Value parentId; + Value origin; + Value linkedCount; + Vector linked; + }; + Vector bones; + + Value boneIdCount; + Vector boneIds; + + Value nameCount; + struct Name : BigDNA + { + DECL_DNA + String<-1> name; + Value boneId; + }; + Vector names; + + atUint32 getBoneIdxFromId(atUint32 id) const + { + atUint32 idx = 0; + for (atUint32 bid : boneIds) + { + if (bid == id) + return idx; + ++idx; + } + return 0; + } + + const std::string* getBoneNameFromId(atUint32 id) const + { + for (const Name& name : names) + if (id == name.boneId) + return &name.name; + return nullptr; + } + + void sendVertexGroupsToBlender(HECL::BlenderConnection::PyOutStream& os) const + { + for (atUint32 bid : boneIds) + { + for (const Name& name : names) + { + if (name.boneId == bid) + { + os.format("obj.vertex_groups.new('%s')\n", name.name.c_str()); + break; + } + } + } + } + + void sendCINFToBlender(HECL::BlenderConnection::PyOutStream& os, const UniqueID32& cinfId) const + { + os.format("arm = bpy.data.armatures.new('CINF_%08X')\n" + "arm_obj = bpy.data.objects.new(arm.name, arm)\n" + "bpy.context.scene.objects.link(arm_obj)\n" + "bpy.context.scene.objects.active = arm_obj\n" + "bpy.ops.object.mode_set(mode='EDIT')\n" + "arm_bone_table = {}\n", + cinfId.toUint32()); + + for (const Bone& bone : bones) + os.format("bone = arm.edit_bones.new('%s')\n" + "bone.head = (%f,%f,%f)\n" + "bone.tail = bone.head\n" + "bone.tail[1] += 0.5\n" + "arm_bone_table[%u] = bone\n", getBoneNameFromId(bone.id)->c_str(), + bone.origin.vec[0], bone.origin.vec[1], bone.origin.vec[2], bone.id); + + for (const Bone& bone : bones) + if (bone.parentId != 2) + os.format("arm_bone_table[%u].parent = arm_bone_table[%u]\n", bone.id, bone.parentId); + + os << "bpy.ops.object.mode_set(mode='OBJECT')\n"; + } +}; + +} +} + +#endif // _DNAMP3_CINF_HPP_ diff --git a/DataSpec/DNAMP3/CMDL.hpp b/DataSpec/DNAMP3/CMDL.hpp index 1f4e865bc..9d857a2dc 100644 --- a/DataSpec/DNAMP3/CMDL.hpp +++ b/DataSpec/DNAMP3/CMDL.hpp @@ -5,6 +5,8 @@ #include "../DNACommon/CMDL.hpp" #include "CMDLMaterials.hpp" #include "DNAMP3.hpp" +#include "CINF.hpp" +#include "CSKR.hpp" namespace Retro { @@ -23,7 +25,7 @@ struct CMDL HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection(); if (!conn.createBlend(outPath.getAbsolutePath())) return false; - DNACMDL::ReadCMDLToBlender, MaterialSet, 5> + DNACMDL::ReadCMDLToBlender, MaterialSet, std::pair, 5> (conn, rs, pakRouter, entry, dataSpec.getMasterShaderPath()); return conn.saveBlend(); } diff --git a/DataSpec/DNAMP3/CMakeLists.txt b/DataSpec/DNAMP3/CMakeLists.txt index c8e72495a..8421c54f5 100644 --- a/DataSpec/DNAMP3/CMakeLists.txt +++ b/DataSpec/DNAMP3/CMakeLists.txt @@ -1,7 +1,9 @@ make_dnalist(liblist PAK MLVL - CMDLMaterials) + CMDLMaterials + CINF + CSKR) add_library(DNAMP3 DNAMP3.hpp DNAMP3.cpp ${liblist} diff --git a/DataSpec/DNAMP3/CSKR.hpp b/DataSpec/DNAMP3/CSKR.hpp new file mode 100644 index 000000000..fc50d4320 --- /dev/null +++ b/DataSpec/DNAMP3/CSKR.hpp @@ -0,0 +1,50 @@ +#ifndef _DNAMP3_CSKR_HPP_ +#define _DNAMP3_CSKR_HPP_ + +#include "BlenderConnection.hpp" +#include "../DNACommon/DNACommon.hpp" +#include "CINF.hpp" + +namespace Retro +{ +namespace DNAMP3 +{ + +struct CSKR : BigDNA +{ + DECL_DNA + Value skinningRuleCount; + struct SkinningRule : BigDNA + { + DECL_DNA + Value weightCount; + struct Weight : BigDNA + { + DECL_DNA + Value boneId; + Value weight; + }; + Vector weights; + Value vertCount; + }; + Vector skinningRules; + + void weightVertex(HECL::BlenderConnection::PyOutStream& os, const CINF& cinf, atUint32 idx) const + { + atUint32 accum = 0; + for (const SkinningRule& rule : skinningRules) + { + if (idx < accum + rule.vertCount) + for (const SkinningRule::Weight& weight : rule.weights) + os.format("vert[dvert_lay][%u] = %f\n", + cinf.getBoneIdxFromId(weight.boneId), + weight.weight); + accum += rule.vertCount; + } + } +}; + +} +} + +#endif // _DNAMP3_CSKR_HPP_ diff --git a/DataSpec/DNAMP3/DNAMP3.cpp b/DataSpec/DNAMP3/DNAMP3.cpp index dd5a0955c..728844e50 100644 --- a/DataSpec/DNAMP3/DNAMP3.cpp +++ b/DataSpec/DNAMP3/DNAMP3.cpp @@ -55,7 +55,7 @@ void PAKBridge::build() ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry) { - switch (entry.type.toUint32()) + switch (entry.type) { case SBIG('STRG'): return {STRG::Extract, nullptr, ".yaml"};