2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-05-14 02:31:21 +00:00

Fixes for MP3

This commit is contained in:
Jack Andersen 2020-04-09 17:20:20 -10:00
parent e858c929e3
commit 380cc13abb
17 changed files with 1095 additions and 846 deletions

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,8 @@ struct MayaSpline : public BigDNA {
Value<float> amplitude; Value<float> amplitude;
Value<atUint8> unk1; Value<atUint8> unk1;
Value<atUint8> unk2; Value<atUint8> unk2;
Vector<atVec2f, AT_DNA_COUNT(unk1 == 5)> unk1Floats;
Vector<atVec2f, AT_DNA_COUNT(unk2 == 5)> unk2Floats;
}; };
Vector<Knot, AT_DNA_COUNT(knotCount)> knots; Vector<Knot, AT_DNA_COUNT(knotCount)> knots;

View File

@ -141,6 +141,5 @@ zeus::CVector3f RigInverter<CINFType>::restorePosition(atUint32 boneId, const ze
template class RigInverter<DNAMP1::CINF>; template class RigInverter<DNAMP1::CINF>;
template class RigInverter<DNAMP2::CINF>; template class RigInverter<DNAMP2::CINF>;
template class RigInverter<DNAMP3::CINF>;
} // namespace DataSpec::DNAANIM } // namespace DataSpec::DNAANIM

View File

@ -1,5 +1,6 @@
#include "CINF.hpp" #include "CINF.hpp"
#include "hecl/Blender/Connection.hpp" #include "hecl/Blender/Connection.hpp"
#include "DataSpec/DNAMP3/DNAMP3.hpp"
namespace DataSpec::DNAMP2 { namespace DataSpec::DNAMP2 {
@ -41,7 +42,8 @@ void CINF::sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const {
} }
} }
void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const { template <class PAKBridge>
void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const typename PAKBridge::PAKType::IDType& cinfId) const {
DNAANIM::RigInverter<CINF> inverter(*this); DNAANIM::RigInverter<CINF> inverter(*this);
os.format(fmt( os.format(fmt(
@ -66,9 +68,18 @@ void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& c
tailF[2], bone.m_origBone.id); tailF[2], bone.m_origBone.id);
} }
if constexpr (std::is_same_v<PAKBridge, DNAMP3::PAKBridge>) {
if (bones.size()) {
atUint32 nullId = bones[0].parentId;
for (const Bone& bone : bones)
if (bone.parentId != nullId)
os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
}
} else {
for (const Bone& bone : bones) for (const Bone& bone : bones)
if (bone.parentId != 97) if (bone.parentId != 97)
os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId); os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
}
os << "bpy.ops.object.mode_set(mode='OBJECT')\n"; os << "bpy.ops.object.mode_set(mode='OBJECT')\n";
@ -76,8 +87,14 @@ void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& c
os.format(fmt("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"), os.format(fmt("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"),
*getBoneNameFromId(bone.m_origBone.id)); *getBoneNameFromId(bone.m_origBone.id));
} }
template void CINF::sendCINFToBlender<PAKBridge>(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const;
template void CINF::sendCINFToBlender<DNAMP3::PAKBridge>(hecl::blender::PyOutStream& os,
const UniqueID64& cinfId) const;
std::string CINF::GetCINFArmatureName(const UniqueID32& cinfId) { return fmt::format(fmt("CINF_{}"), cinfId); } template <class UniqueID>
std::string CINF::GetCINFArmatureName(const UniqueID& cinfId) { return fmt::format(fmt("CINF_{}"), cinfId); }
template std::string CINF::GetCINFArmatureName(const UniqueID32& cinfId);
template std::string CINF::GetCINFArmatureName(const UniqueID64& cinfId);
int CINF::RecursiveAddArmatureBone(const Armature& armature, const BlenderBone* bone, int parent, int& curId, int CINF::RecursiveAddArmatureBone(const Armature& armature, const BlenderBone* bone, int parent, int& curId,
std::unordered_map<std::string, atInt32>& idMap, std::unordered_map<std::string, atInt32>& idMap,
@ -156,9 +173,10 @@ CINF::CINF(const Armature& armature, std::unordered_map<std::string, atInt32>& i
boneIds.push_back(it->id); boneIds.push_back(it->id);
} }
template <class PAKBridge>
bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok, PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry, bool force,
std::function<void(const hecl::SystemChar*)> fileChanged) { hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)> fileChanged) {
if (!force && outPath.isFile()) if (!force && outPath.isFile())
return true; return true;
@ -179,12 +197,21 @@ bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
CINF cinf; CINF cinf;
cinf.read(rs); cinf.read(rs);
cinf.sendCINFToBlender(os, entry.id); cinf.sendCINFToBlender<PAKBridge>(os, entry.id);
os.centerView(); os.centerView();
os.close(); os.close();
return conn.saveBlend(); return conn.saveBlend();
} }
template bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry,
bool force, hecl::blender::Token& btok,
std::function<void(const hecl::SystemChar*)> fileChanged);
template bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<DNAMP3::PAKBridge>& pakRouter,
const typename DNAMP3::PAKBridge::PAKType::Entry& entry, bool force,
hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)> fileChanged);
bool CINF::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, bool CINF::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath,
const hecl::blender::Armature& armature) { const hecl::blender::Armature& armature) {
std::unordered_map<std::string, atInt32> boneIdMap; std::unordered_map<std::string, atInt32> boneIdMap;

View File

@ -36,8 +36,10 @@ struct CINF : BigDNA {
atUint32 getBoneIdxFromId(atUint32 id) const; atUint32 getBoneIdxFromId(atUint32 id) const;
const std::string* getBoneNameFromId(atUint32 id) const; const std::string* getBoneNameFromId(atUint32 id) const;
void sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const; void sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const;
void sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const; template <class PAKBridge>
static std::string GetCINFArmatureName(const UniqueID32& cinfId); void sendCINFToBlender(hecl::blender::PyOutStream& os, const typename PAKBridge::PAKType::IDType& cinfId) const;
template <class UniqueID>
static std::string GetCINFArmatureName(const UniqueID& cinfId);
CINF() = default; CINF() = default;
using Armature = hecl::blender::Armature; using Armature = hecl::blender::Armature;
@ -48,9 +50,10 @@ struct CINF : BigDNA {
CINF(const Armature& armature, std::unordered_map<std::string, atInt32>& idMap); CINF(const Armature& armature, std::unordered_map<std::string, atInt32>& idMap);
template <class PAKBridge>
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok, PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry, bool force,
std::function<void(const hecl::SystemChar*)> fileChanged); hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)> fileChanged);
static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath,
const hecl::blender::Armature& armature); const hecl::blender::Armature& armature);

View File

@ -25,6 +25,8 @@ namespace DataSpec::DNAMP2 {
logvisor::Module Log("urde::DNAMP2"); logvisor::Module Log("urde::DNAMP2");
static bool GetNoShare(std::string_view name) { static bool GetNoShare(std::string_view name) {
if (name == "FrontEnd.pak"sv)
return false;
std::string lowerName(name); std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
if (!lowerName.compare(0, 7, "metroid")) if (!lowerName.compare(0, 7, "metroid"))
@ -259,7 +261,7 @@ ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const nod::Node& pakNode, con
case SBIG('CMDL'): case SBIG('CMDL'):
return {CMDL::Extract, {_SYS_STR(".blend")}, 1}; return {CMDL::Extract, {_SYS_STR(".blend")}, 1};
case SBIG('CINF'): case SBIG('CINF'):
return {CINF::Extract, {_SYS_STR(".blend")}, 1}; return {CINF::Extract<PAKBridge>, {_SYS_STR(".blend")}, 1};
case SBIG('ANCS'): case SBIG('ANCS'):
return {ANCS::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2}; return {ANCS::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2};
case SBIG('MLVL'): case SBIG('MLVL'):

View File

@ -68,8 +68,7 @@ MREA::StreamReader::StreamReader(athena::io::IStreamReader& source, atUint32 blk
, m_blkCount(blkCount) { , m_blkCount(blkCount) {
m_blockInfos.reserve(blkCount); m_blockInfos.reserve(blkCount);
for (atUint32 i = 0; i < blkCount; ++i) { for (atUint32 i = 0; i < blkCount; ++i) {
m_blockInfos.emplace_back(); BlockInfo& info = m_blockInfos.emplace_back();
BlockInfo& info = m_blockInfos.back();
info.read(source); info.read(source);
m_totalDecompLen += info.decompSize; m_totalDecompLen += info.decompSize;
} }

View File

@ -1,48 +0,0 @@
#include "CINF.hpp"
#include "hecl/Blender/Connection.hpp"
namespace DataSpec::DNAMP3 {
void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID64& cinfId) const {
DNAANIM::RigInverter<CINF> inverter(*this);
os.format(fmt("arm = bpy.data.armatures.new('CINF_{}')\n"
"arm_obj = bpy.data.objects.new(arm.name, arm)\n"
"bpy.context.scene.collection.objects.link(arm_obj)\n"
"bpy.context.view_layer.objects.active = arm_obj\n"
"bpy.ops.object.mode_set(mode='EDIT')\n"
"arm_bone_table = {{}}\n"),
cinfId);
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) {
zeus::simd_floats originF(bone.m_origBone.origin.simd);
zeus::simd_floats tailF(bone.m_tail.mSimd);
os.format(fmt(
"bone = arm.edit_bones.new('{}')\n"
"bone.head = ({},{},{})\n"
"bone.tail = ({},{},{})\n"
"bone.use_inherit_scale = False\n"
"arm_bone_table[{}] = bone\n"),
*getBoneNameFromId(bone.m_origBone.id), originF[0], originF[1], originF[2], tailF[0], tailF[1],
tailF[2], bone.m_origBone.id);
}
if (bones.size()) {
atUint32 nullId = bones[0].parentId;
for (const Bone& bone : bones)
if (bone.parentId != nullId)
os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
}
os << "bpy.ops.object.mode_set(mode='OBJECT')\n";
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones())
os.format(fmt("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"),
*getBoneNameFromId(bone.m_origBone.id));
}
std::string CINF::GetCINFArmatureName(const UniqueID64& cinfId) {
return fmt::format(fmt("CINF_{}"), cinfId);
}
} // namespace DataSpec::DNAMP3

View File

@ -1,14 +1,8 @@
#pragma once #pragma once
#include "DataSpec/DNACommon/DNACommon.hpp" #include "DataSpec/DNACommon/DNACommon.hpp"
#include "../DNAMP2/CINF.hpp" #include "DataSpec/DNAMP2/CINF.hpp"
namespace DataSpec::DNAMP3 { namespace DataSpec::DNAMP3 {
using CINF = DNAMP2::CINF;
struct CINF : DNAMP2::CINF {
Delete expl;
void sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID64& cinfId) const;
static std::string GetCINFArmatureName(const UniqueID64& cinfId);
};
} // namespace DataSpec::DNAMP3 } // namespace DataSpec::DNAMP3

View File

@ -6,341 +6,291 @@ using Stream = hecl::blender::PyOutStream;
namespace DataSpec::DNAMP3 { namespace DataSpec::DNAMP3 {
using Material = MaterialSet::Material; using Material = MaterialSet::Material;
template <>
void MaterialSet::Material::SectionFactory::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
DNAFourCC type;
type.read(reader);
switch (ISection::Type(type.toUint32())) {
case ISection::Type::PASS:
section = std::make_unique<SectionPASS>();
section->read(reader);
break;
case ISection::Type::CLR:
section = std::make_unique<SectionCLR>();
section->read(reader);
break;
case ISection::Type::INT:
section = std::make_unique<SectionINT>();
section->read(reader);
break;
default:
section.reset();
break;
}
}
template <>
void MaterialSet::Material::SectionFactory::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
if (!section)
return;
writer.writeUBytes((atUint8*)&section->m_type, 4);
section->write(writer);
}
template <>
void MaterialSet::Material::SectionFactory::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
s += 4;
section->binarySize(s);
}
template <> template <>
void MaterialSet::Material::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) { void MaterialSet::Material::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
header.read(reader); header.read(reader);
sections.clear(); chunks.clear();
do { do { chunks.emplace_back().read(reader); } while (!chunks.back().holds_alternative<END>());
sections.emplace_back(); chunks.pop_back();
sections.back().read(reader);
} while (sections.back().section);
sections.pop_back();
} }
template <> template <>
void MaterialSet::Material::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) { void MaterialSet::Material::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
header.write(writer); header.write(writer);
for (const SectionFactory& section : sections) for (const auto& chunk : chunks)
section.write(writer); chunk.visit([&](auto& arg) { arg.write(writer); });
writer.writeUBytes((atUint8*)"END ", 4); DNAFourCC(FOURCC('END ')).write(writer);
} }
template <> template <>
void MaterialSet::Material::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) { void MaterialSet::Material::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
header.binarySize(s); header.binarySize(s);
for (const SectionFactory& section : sections) for (const auto& chunk : chunks)
section.binarySize(s); chunk.visit([&](auto& arg) { arg.binarySize(s); });
s += 4; s += 4;
} }
void MaterialSet::RegisterMaterialProps(Stream& out) { void MaterialSet::RegisterMaterialProps(Stream& out) {
out << "bpy.types.Material.retro_alpha_test = bpy.props.BoolProperty(name='Retro: Punchthrough Alpha')\n" out << "bpy.types.Material.retro_enable_bloom = bpy.props.BoolProperty(name='Retro: Enable Bloom')\n"
"bpy.types.Material.retro_force_lighting_stage = bpy.props.BoolProperty(name='Retro: Force Lighting Stage')\n"
"bpy.types.Material.retro_pre_inca_transparency = bpy.props.BoolProperty(name='Retro: Pre-INCA Transparency')\n"
"bpy.types.Material.retro_alpha_test = bpy.props.BoolProperty(name='Retro: Alpha Test')\n"
"bpy.types.Material.retro_shadow_occluder = bpy.props.BoolProperty(name='Retro: Shadow Occluder')\n" "bpy.types.Material.retro_shadow_occluder = bpy.props.BoolProperty(name='Retro: Shadow Occluder')\n"
"bpy.types.Material.retro_lightmapped = bpy.props.BoolProperty(name='Retro: Lightmapped')\n" "bpy.types.Material.retro_solid_white = bpy.props.BoolProperty(name='Retro: Solid White Only')\n"
"bpy.types.Material.retro_opac = bpy.props.IntProperty(name='Retro: OPAC')\n" "bpy.types.Material.retro_reflection_alpha_target = bpy.props.BoolProperty(name='Retro: Reflection Alpha Target')\n"
"bpy.types.Material.retro_blod = bpy.props.IntProperty(name='Retro: BLOD')\n" "bpy.types.Material.retro_solid_color = bpy.props.BoolProperty(name='Retro: Solid Color Only')\n"
"bpy.types.Material.retro_bloi = bpy.props.IntProperty(name='Retro: BLOI')\n" "bpy.types.Material.retro_exclude_scan = bpy.props.BoolProperty(name='Retro: Exclude From Scan Visor')\n"
"bpy.types.Material.retro_bnif = bpy.props.IntProperty(name='Retro: BNIF')\n" "bpy.types.Material.retro_xray_opaque = bpy.props.BoolProperty(name='Retro: XRay Opaque')\n"
"bpy.types.Material.retro_xrbr = bpy.props.IntProperty(name='Retro: XRBR')\n" "bpy.types.Material.retro_xray_alpha_target = bpy.props.BoolProperty(name='Retro: XRay Alpha Target')\n"
"bpy.types.Material.retro_inca_color_mod = bpy.props.BoolProperty(name='Retro: INCA Color Mod')\n"
"\n"; "\n";
} }
void MaterialSet::ConstructMaterial(Stream& out, const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, static void LoadTexture(Stream& out, const UniqueID64& tex,
const Material& material, unsigned groupIdx, unsigned matIdx) { const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry) {
unsigned i; std::string texName = pakRouter.getBestEntryName(tex);
out.format(fmt(
"new_material = bpy.data.materials.new('MAT_{}_{}')\n"
"new_material.use_shadows = True\n"
"new_material.use_transparent_shadows = True\n"
"new_material.diffuse_color = (1.0,1.0,1.0)\n"
"new_material.use_nodes = True\n"
"new_material.blend_method = 'BLEND'\n"
"new_nodetree = new_material.node_tree\n"
"material_node = new_nodetree.nodes['Material']\n"
"final_node = new_nodetree.nodes['Output']\n"
"\n"
"gridder = hecl.Nodegrid(new_nodetree)\n"
"gridder.place_node(final_node, 3)\n"
"gridder.place_node(material_node, 0)\n"
"material_node.material = new_material\n"
"\n"
"texture_nodes = []\n"
"kcolor_nodes = []\n"
"color_combiner_nodes = []\n"
"alpha_combiner_nodes = []\n"
"tex_links = []\n"
"tev_reg_sockets = [None]*4\n"
"\n"),
groupIdx, matIdx);
/* Material Flags */
out.format(fmt(
"new_material.retro_alpha_test = {}\n"
"new_material.retro_shadow_occluder = {}\n"
"new_material.diffuse_color = (1, 1, 1, {})\n"),
material.header.flags.alphaTest() ? "True" : "False",
material.header.flags.shadowOccluderMesh() ? "True" : "False",
material.header.flags.shadowOccluderMesh() ? "0" : "1");
/* Blend factors */
out << "blend_node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
"blend_node.name = 'Blend'\n"
"gridder.place_node(blend_node, 2)\n";
if (material.header.flags.alphaBlending())
out << "blend_node.node_tree = bpy.data.node_groups['HECLBlendOutput']\n";
else if (material.header.flags.additiveBlending())
out << "blend_node.node_tree = bpy.data.node_groups['HECLAdditiveOutput']\n";
else {
out << "blend_node.node_tree = bpy.data.node_groups['HECLOpaqueOutput']\n"
"new_material.blend_method = 'OPAQUE'\n";
}
/* Texmap list */
out << "tex_maps = []\n"
"pnode = None\n"
"anode = None\n"
"rflv_tex_node = None\n";
/* Add PASSes */
i = 0;
unsigned texMapIdx = 0;
unsigned texMtxIdx = 0;
unsigned kColorIdx = 0;
Material::ISection* prevSection = nullptr;
for (const Material::SectionFactory& factory : material.sections) {
factory.section->constructNode(out, pakRouter, entry, prevSection, i++, texMapIdx, texMtxIdx, kColorIdx);
Material::SectionPASS* pass = Material::SectionPASS::castTo(factory.section.get());
if (!pass ||
(pass && Material::SectionPASS::Subtype(pass->subtype.toUint32()) != Material::SectionPASS::Subtype::RFLV))
prevSection = factory.section.get();
}
/* Connect final PASS */
out << "if pnode:\n"
" new_nodetree.links.new(pnode.outputs['Next Color'], final_node.inputs['Color'])\n"
"else:\n"
" new_nodetree.links.new(kcolor_nodes[-1][0].outputs[0], final_node.inputs['Color'])\n"
"if anode:\n"
" new_nodetree.links.new(anode.outputs['Value'], final_node.inputs['Alpha'])\n"
"elif pnode:\n"
" new_nodetree.links.new(pnode.outputs['Next Alpha'], final_node.inputs['Alpha'])\n"
"else:\n"
" new_nodetree.links.new(kcolor_nodes[-1][1].outputs[0], final_node.inputs['Alpha'])\n";
}
void Material::SectionPASS::constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const {
/* Add Texture nodes */
if (txtrId.isValid()) {
std::string texName = pakRouter.getBestEntryName(txtrId);
const nod::Node* node; const nod::Node* node;
const PAK::Entry* texEntry = pakRouter.lookupEntry(txtrId, &node); const typename PAKRouter<PAKBridge>::EntryType* texEntry = pakRouter.lookupEntry(tex, &node);
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry); hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry);
if (txtrPath.isNone()) { if (!txtrPath.isNone()) {
txtrPath.makeDirChain(false); txtrPath.makeDirChain(false);
PAKEntryReadStream rs = texEntry->beginReadStream(*node); PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath); TXTR::Extract(rs, txtrPath);
} }
hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, txtrId); hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, tex);
hecl::SystemUTF8Conv resPathView(resPath); hecl::SystemUTF8Conv resPathView(resPath);
out.format(fmt( out.format(fmt("if '{}' in bpy.data.images:\n"
"if '{}' in bpy.data.textures:\n"
" image = bpy.data.images['{}']\n" " image = bpy.data.images['{}']\n"
" texture = bpy.data.textures[image.name]\n"
"else:\n" "else:\n"
" image = bpy.data.images.load('''//{}''')\n" " image = bpy.data.images.load('''//{}''')\n"
" image.name = '{}'\n" " image.name = '{}'\n"
" texture = bpy.data.textures.new(image.name, 'IMAGE')\n" "\n"), texName, texName, resPathView, texName);
" texture.image = image\n"
"tex_maps.append(texture)\n"
"\n"),
texName, texName, resPathView, texName);
if (uvAnim.size()) {
const UVAnimation& uva = uvAnim[0];
DNAMP1::MaterialSet::Material::AddTexture(out, GX::TexGenSrc(uva.unk1 + (uva.unk1 < 2 ? 0 : 2)), texMtxIdx,
texMapIdx++, false);
DNAMP1::MaterialSet::Material::AddTextureAnim(out, uva.anim.mode, texMtxIdx++, uva.anim.vals);
} else
DNAMP1::MaterialSet::Material::AddTexture(out, GX::TexGenSrc(uvSrc + 4), -1, texMapIdx++, false);
} }
/* Special case for RFLV (environment UV mask) */ void MaterialSet::ConstructMaterial(Stream& out, const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry,
if (Subtype(subtype.toUint32()) == Subtype::RFLV) { const Material& material, unsigned groupIdx, unsigned matIdx) {
if (txtrId.isValid()) out.format(fmt("new_material = bpy.data.materials.new('MAT_{}_{}')\n"), groupIdx, matIdx);
out << "rflv_tex_node = texture_nodes[-1]\n"; out << "new_material.use_fake_user = True\n"
return; "new_material.use_nodes = True\n"
} "new_material.use_backface_culling = True\n"
"new_material.show_transparent_back = False\n"
"new_material.blend_method = 'BLEND'\n"
"new_nodetree = new_material.node_tree\n"
"for n in new_nodetree.nodes:\n"
" new_nodetree.nodes.remove(n)\n"
"\n"
"gridder = hecl.Nodegrid(new_nodetree)\n"
"new_nodetree.nodes.remove(gridder.frames[2])\n"
"\n"
"texture_nodes = []\n"
"kcolors = {}\n"
"kalphas = {}\n"
"tex_links = []\n"
"\n";
/* Add PASS node */ /* Material Flags */
bool linkRAS = false; out.format(fmt(
out << "prev_pnode = pnode\n" "new_material.retro_enable_bloom = {}\n"
"pnode = new_nodetree.nodes.new('ShaderNodeGroup')\n"; "new_material.retro_force_lighting_stage = {}\n"
switch (Subtype(subtype.toUint32())) { "new_material.retro_pre_inca_transparency = {}\n"
case Subtype::DIFF: { "new_material.retro_alpha_test = {}\n"
out << "pnode.node_tree = bpy.data.node_groups['RetroPassDIFF']\n"; "new_material.retro_shadow_occluder = {}\n"
if (txtrId.isValid()) { "new_material.retro_solid_white = {}\n"
out << "new_material.hecl_lightmap = texture.name\n" "new_material.retro_reflection_alpha_target = {}\n"
<< "texture.image.use_fake_user = True\n"; "new_material.retro_solid_color = {}\n"
} "new_material.retro_exclude_scan = {}\n"
linkRAS = true; "new_material.retro_xray_opaque = {}\n"
"new_material.retro_xray_alpha_target = {}\n"
"new_material.retro_inca_color_mod = False\n"),
material.header.flags.enableBloom() ? "True" : "False",
material.header.flags.forceLightingStage() ? "True" : "False",
material.header.flags.preIncaTransparency() ? "True" : "False",
material.header.flags.alphaTest() ? "True" : "False",
material.header.flags.shadowOccluderMesh() ? "True" : "False",
material.header.flags.justWhite() ? "True" : "False",
material.header.flags.reflectionAlphaTarget() ? "True" : "False",
material.header.flags.justSolidColor() ? "True" : "False",
material.header.flags.excludeFromScanVisor() ? "True" : "False",
material.header.flags.xrayOpaque() ? "True" : "False",
material.header.flags.xrayAlphaTarget() ? "True" : "False");
out << "pnode = new_nodetree.nodes.new('ShaderNodeGroup')\n"
"pnode.name = 'Output'\n"
"pnode.node_tree = bpy.data.node_groups['RetroShaderMP3']\n"
"gridder.place_node(pnode, 1)\n";
if (material.header.flags.additiveIncandecence())
out << "pnode.inputs['Add INCA'].default_value = 1\n";
int texMtxIdx = 0;
for (const auto& chunk : material.chunks) {
if (const Material::PASS* pass = chunk.get_if<Material::PASS>()) {
LoadTexture(out, pass->txtrId, pakRouter, entry);
out << "# Texture\n"
"tex_node = new_nodetree.nodes.new('ShaderNodeTexImage')\n"
"texture_nodes.append(tex_node)\n"
"tex_node.image = image\n";
if (!pass->uvAnim.empty()) {
const auto& uva = pass->uvAnim[0];
switch (uva.uvSource) {
case Material::UVAnimationUVSource::Position:
default:
out << "tex_uv_node = new_nodetree.nodes.new('ShaderNodeTexCoord')\n"
"tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['Window'], tex_node.inputs['Vector']))\n";
break;
case Material::UVAnimationUVSource::Normal:
out << "tex_uv_node = new_nodetree.nodes.new('ShaderNodeTexCoord')\n"
"tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['Normal'], tex_node.inputs['Vector']))\n";
break;
case Material::UVAnimationUVSource::UV:
out.format(fmt("tex_uv_node = new_nodetree.nodes.new('ShaderNodeUVMap')\n"
"tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['UV'], tex_node.inputs['Vector']))\n"
"tex_uv_node.uv_map = 'UV_{}'\n"), pass->uvSrc);
break; break;
} }
case Subtype::RIML: out.format(fmt("tex_uv_node.label = 'MTX_{}'\n"), texMtxIdx);
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRIML']\n"; } else {
if (idx == 0) out.format(fmt("tex_uv_node = new_nodetree.nodes.new('ShaderNodeUVMap')\n"
linkRAS = true; "tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['UV'], tex_node.inputs['Vector']))\n"
"tex_uv_node.uv_map = 'UV_{}'\n"), pass->uvSrc);
}
out << "gridder.place_node(tex_uv_node, 0)\n"
"gridder.place_node(tex_node, 0)\n"
"tex_uv_node.location[0] -= 120\n"
"tex_node.location[0] += 120\n"
"tex_node.location[1] += 176\n"
"\n";
if (!pass->uvAnim.empty()) {
const auto& uva = pass->uvAnim[0];
DNAMP1::MaterialSet::Material::AddTextureAnim(out, uva.anim.mode, texMtxIdx++, uva.anim.vals);
}
auto DoSwap = [&]() {
if (pass->flags.swapColorComponent() == Material::SwapColorComponent::Alpha) {
out << "swap_output = tex_node.outputs['Alpha']\n";
} else {
out << "separate_node = new_nodetree.nodes.new('ShaderNodeSeparateRGB')\n"
"gridder.place_node(separate_node, 0, False)\n"
"separate_node.location[0] += 350\n"
"separate_node.location[1] += 350\n"
"new_nodetree.links.new(tex_node.outputs['Color'], separate_node.inputs[0])\n";
out.format(fmt("swap_output = separate_node.outputs[{}]\n"), int(pass->flags.swapColorComponent()));
}
};
using Subtype = Material::PASS::Subtype;
switch (Subtype(pass->subtype.toUint32())) {
case Subtype::DIFF:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['DIFFC'])\n"
"new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['DIFFA'])\n";
break; break;
case Subtype::BLOL: case Subtype::BLOL:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOL']\n"; DoSwap();
if (idx == 0) out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOL'])\n";
linkRAS = true;
break; break;
case Subtype::BLOD: case Subtype::BLOD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOD']\n"; DoSwap();
if (idx == 0) out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOD'])\n";
linkRAS = true;
break; break;
case Subtype::CLR: case Subtype::CLR:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassCLR']\n"; out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['CLR'])\n"
if (idx == 0) "new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['CLRA'])\n";
linkRAS = true;
break; break;
case Subtype::TRAN: case Subtype::TRAN:
if (flags.TRANInvert()) DoSwap();
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTRANInv']\n"; if (pass->flags.TRANInvert())
else out << "invert_node = new_nodetree.nodes.new('ShaderNodeInvert')\n"
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTRAN']\n"; "gridder.place_node(invert_node, 0, False)\n"
"invert_node.location[0] += 400\n"
"invert_node.location[1] += 350\n"
"new_nodetree.links.new(swap_output, invert_node.inputs['Color'])\n"
"swap_output = invert_node.outputs['Color']\n";
out << "new_nodetree.links.new(swap_output, pnode.inputs['TRAN'])\n";
break; break;
case Subtype::INCA: case Subtype::INCA:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassINCA']\n"; out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['INCAC'])\n";
if (pass->flags.alphaContribution()) {
DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['INCAA'])\n";
}
out.format(fmt("new_material.retro_inca_color_mod = {}\n"), pass->flags.INCAColorMod() ? "True" : "False");
break; break;
case Subtype::RFLV: case Subtype::RFLV:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRFLV']\n"; out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['RFLV'])\n";
break; break;
case Subtype::RFLD: case Subtype::RFLD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRFLD']\n" out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['RFLD'])\n"
"if rflv_tex_node:\n" "new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['RFLDA'])\n";
" new_nodetree.links.new(rflv_tex_node.outputs['Color'], pnode.inputs['Mask Color'])\n"
" new_nodetree.links.new(rflv_tex_node.outputs['Value'], pnode.inputs['Mask Alpha'])\n";
break; break;
case Subtype::LRLD: case Subtype::LRLD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassLRLD']\n"; out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['LRLD'])\n";
break; break;
case Subtype::LURD: case Subtype::LURD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassLURD']\n"; out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['LURDC'])\n"
"new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['LURDA'])\n";
break; break;
case Subtype::BLOI: case Subtype::BLOI:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOI']\n"; DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOI'])\n";
break; break;
case Subtype::XRAY: case Subtype::XRAY:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassXRAY']\n"; DoSwap();
break; out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['XRAYC'])\n"
case Subtype::TOON: "new_nodetree.links.new(swap_output, pnode.inputs['XRAYA'])\n";
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTOON']\n";
break; break;
default: default:
Log.report(logvisor::Fatal, fmt("Unknown PASS subtype"));
break; break;
} }
out << "gridder.place_node(pnode, 2)\n"; } else if (const Material::CLR* clr = chunk.get_if<Material::CLR>()) {
using Subtype = Material::CLR::Subtype;
if (txtrId.isValid()) { athena::simd_floats vec4;
out << "new_nodetree.links.new(texture_nodes[-1].outputs['Color'], pnode.inputs['Tex Color'])\n" clr->color.toVec4f().simd.copy_to(vec4);
"new_nodetree.links.new(texture_nodes[-1].outputs['Value'], pnode.inputs['Tex Alpha'])\n"; switch (Subtype(clr->subtype.toUint32())) {
} case Subtype::CLR:
out.format(fmt("pnode.inputs['CLR'].default_value = ({}, {}, {}, 1.0)\n"
if (linkRAS) "pnode.inputs['CLRA'].default_value = {}\n"),
out << "new_nodetree.links.new(material_node.outputs['Color'], pnode.inputs['Prev Color'])\n" vec4[0], vec4[1], vec4[2], vec4[3]);
"new_nodetree.links.new(material_node.outputs['Alpha'], pnode.inputs['Prev Alpha'])\n"; break;
else if (prevSection) {
if (prevSection->m_type == ISection::Type::PASS &&
Subtype(static_cast<const SectionPASS*>(prevSection)->subtype.toUint32()) != Subtype::RFLV)
out << "new_nodetree.links.new(prev_pnode.outputs['Next Color'], pnode.inputs['Prev Color'])\n"
"new_nodetree.links.new(prev_pnode.outputs['Next Alpha'], pnode.inputs['Prev Alpha'])\n";
else if (prevSection->m_type == ISection::Type::CLR)
out << "new_nodetree.links.new(kcolor_nodes[-1][0].outputs[0], pnode.inputs['Prev Color'])\n"
"new_nodetree.links.new(kcolor_nodes[-1][1].outputs[0], pnode.inputs['Prev Alpha'])\n";
}
/* Row Break in gridder */
out << "gridder.row_break(2)\n";
}
void Material::SectionCLR::constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const {
DNAMP1::MaterialSet::Material::AddKcolor(out, color, kColorIdx++);
switch (Subtype(subtype.toUint32())) {
case Subtype::DIFB: case Subtype::DIFB:
out << "kc_node.label += ' DIFB'\n" out.format(fmt("pnode.inputs['DIFBC'].default_value = ({}, {}, {}, 1.0)\n"
"ka_node.label += ' DIFB'\n"; "pnode.inputs['DIFBA'].default_value = {}\n"),
vec4[0], vec4[1], vec4[2], vec4[3]);
break; break;
default: default:
Log.report(logvisor::Fatal, fmt("Unknown CLR subtype"));
break; break;
} }
} } else if (const Material::INT* val = chunk.get_if<Material::INT>()) {
using Subtype = Material::INT::Subtype;
void Material::SectionINT::constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter, switch (Subtype(val->subtype.toUint32())) {
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx, case Subtype::OPAC:
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const { out.format(fmt("pnode.inputs['OPAC'].default_value = {}\n"), val->value / 255.f);
switch (Subtype(subtype.toUint32())) { break;
case Subtype::OPAC: {
GX::Color clr(value);
out.format(fmt(
"anode = new_nodetree.nodes.new('ShaderNodeValue')\n"
"anode.outputs['Value'].default_value = {}\n"),
float(clr[3]) / float(0xff));
out << "gridder.place_node(anode, 1)\n";
} break;
case Subtype::BLOD: case Subtype::BLOD:
out.format(fmt("new_material.retro_blod = {}\n"), value); out.format(fmt("pnode.inputs['BLOD'].default_value = {}\n"), val->value / 255.f);
break; break;
case Subtype::BLOI: case Subtype::BLOI:
out.format(fmt("new_material.retro_bloi = {}\n"), value); out.format(fmt("pnode.inputs['BLOI'].default_value = {}\n"), val->value / 255.f);
break; break;
case Subtype::BNIF: case Subtype::BNIF:
out.format(fmt("new_material.retro_bnif = {}\n"), value); out.format(fmt("pnode.inputs['BNIF'].default_value = {}\n"), val->value / 255.f);
break; break;
case Subtype::XRBR: case Subtype::XRBR:
out.format(fmt("new_material.retro_xrbr = {}\n"), value); out.format(fmt("pnode.inputs['XRBR'].default_value = {}\n"), val->value / 255.f);
break; break;
default: default:
Log.report(logvisor::Fatal, fmt("Unknown INT subtype"));
break; break;
} }
} }
}
}
} // namespace DataSpec::DNAMP3 } // namespace DataSpec::DNAMP3
AT_SPECIALIZE_TYPED_VARIANT_BIGDNA(DataSpec::DNAMP3::MaterialSet::Material::PASS,
DataSpec::DNAMP3::MaterialSet::Material::CLR,
DataSpec::DNAMP3::MaterialSet::Material::INT,
DataSpec::DNAMP3::MaterialSet::Material::END)

View File

@ -18,6 +18,10 @@ struct MaterialSet : BigDNA {
void addMaterialEndOff(atUint32) { ++materialCount; } void addMaterialEndOff(atUint32) { ++materialCount; }
struct Material : BigDNA { struct Material : BigDNA {
enum class SwapColorComponent { Red, Green, Blue, Alpha };
enum class UVAnimationUVSource : atUint16 { Position, Normal, UV };
enum class UVAnimationMatrixConfig : atUint16 { NoMtxNoPost, MtxNoPost, NoMtxPost, MtxPost };
AT_DECL_EXPLICIT_DNA AT_DECL_EXPLICIT_DNA
using VAFlags = DNAMP1::MaterialSet::Material::VAFlags; using VAFlags = DNAMP1::MaterialSet::Material::VAFlags;
struct Header : BigDNA { struct Header : BigDNA {
@ -26,8 +30,18 @@ struct MaterialSet : BigDNA {
struct Flags : BigDNA { struct Flags : BigDNA {
AT_DECL_DNA AT_DECL_DNA
Value<atUint32> flags; Value<atUint32> flags;
bool alphaBlending() const { return (flags & 0x8) != 0; } bool enableBloom() const { return (flags & 0x1) != 0; }
void setAlphaBlending(bool enabled) { void setEnableBloom(bool enabled) {
flags &= ~0x1;
flags |= atUint32(enabled) << 0;
}
bool forceLightingStage() const { return (flags & 0x4) != 0; }
void setForceLightingStage(bool enabled) {
flags &= ~0x4;
flags |= atUint32(enabled) << 2;
}
bool preIncaTransparency() const { return (flags & 0x8) != 0; }
void setPreIncaTransparency(bool enabled) {
flags &= ~0x8; flags &= ~0x8;
flags |= atUint32(enabled) << 3; flags |= atUint32(enabled) << 3;
} }
@ -36,8 +50,8 @@ struct MaterialSet : BigDNA {
flags &= ~0x10; flags &= ~0x10;
flags |= atUint32(enabled) << 4; flags |= atUint32(enabled) << 4;
} }
bool additiveBlending() const { return (flags & 0x20) != 0; } bool additiveIncandecence() const { return (flags & 0x20) != 0; }
void setAdditiveBlending(bool enabled) { void setAdditiveIncandecence(bool enabled) {
flags &= ~0x20; flags &= ~0x20;
flags |= atUint32(enabled) << 5; flags |= atUint32(enabled) << 5;
} }
@ -46,6 +60,36 @@ struct MaterialSet : BigDNA {
flags &= ~0x100; flags &= ~0x100;
flags |= atUint32(enabled) << 8; flags |= atUint32(enabled) << 8;
} }
bool justWhite() const { return (flags & 0x200) != 0; }
void setJustWhite(bool enabled) {
flags &= ~0x200;
flags |= atUint32(enabled) << 9;
}
bool reflectionAlphaTarget() const { return (flags & 0x400) != 0; }
void setReflectionAlphaTarget(bool enabled) {
flags &= ~0x400;
flags |= atUint32(enabled) << 10;
}
bool justSolidColor() const { return (flags & 0x800) != 0; }
void setJustSolidColor(bool enabled) {
flags &= ~0x800;
flags |= atUint32(enabled) << 11;
}
bool excludeFromScanVisor() const { return (flags & 0x4000) != 0; }
void setExcludeFromScanVisor(bool enabled) {
flags &= ~0x4000;
flags |= atUint32(enabled) << 14;
}
bool xrayOpaque() const { return (flags & 0x8000) != 0; }
void setXRayOpaque(bool enabled) {
flags &= ~0x8000;
flags |= atUint32(enabled) << 15;
}
bool xrayAlphaTarget() const { return (flags & 0x10000) != 0; }
void setXRayAlphaTarget(bool enabled) {
flags &= ~0x10000;
flags |= atUint32(enabled) << 16;
}
bool lightmapUVArray() const { return false; } /* For polymorphic compatibility with MP1/2 */ bool lightmapUVArray() const { return false; } /* For polymorphic compatibility with MP1/2 */
} flags; } flags;
Value<atUint32> uniqueIdx; Value<atUint32> uniqueIdx;
@ -58,20 +102,12 @@ struct MaterialSet : BigDNA {
const Header::Flags& getFlags() const { return header.flags; } const Header::Flags& getFlags() const { return header.flags; }
const VAFlags& getVAFlags() const { return header.vaFlags; } const VAFlags& getVAFlags() const { return header.vaFlags; }
struct ISection : BigDNAV { enum class ChunkType : atUint32 {
Delete expl; PASS = 'PASS', CLR = 'CLR ', INT = 'INT ', END = 'END '
enum class Type : atUint32 { PASS = SBIG('PASS'), CLR = SBIG('CLR '), INT = SBIG('INT ') } m_type;
ISection(Type type) : m_type(type) {}
virtual void constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const = 0;
}; };
struct SectionPASS : ISection {
SectionPASS() : ISection(ISection::Type::PASS) {} struct PASS : hecl::TypedRecordBigDNA<ChunkType::PASS> {
static SectionPASS* castTo(ISection* sec) { AT_DECL_DNA
return sec->m_type == Type::PASS ? static_cast<SectionPASS*>(sec) : nullptr;
}
AT_DECL_DNAV
Value<atUint32> size; Value<atUint32> size;
enum class Subtype : atUint32 { enum class Subtype : atUint32 {
DIFF = SBIG('DIFF'), DIFF = SBIG('DIFF'),
@ -93,6 +129,21 @@ struct MaterialSet : BigDNA {
struct Flags : BigDNA { struct Flags : BigDNA {
AT_DECL_DNA AT_DECL_DNA
Value<atUint32> flags; Value<atUint32> flags;
SwapColorComponent swapColorComponent() const { return SwapColorComponent(flags & 0x3); }
void setSwapColorComponent(SwapColorComponent comp) {
flags &= ~0x3;
flags |= atUint32(comp) << 0;
}
bool alphaContribution() const { return (flags & 0x4) != 0; }
void setAlphaContribution(bool enabled) {
flags &= ~0x4;
flags |= atUint32(enabled) << 2;
}
bool INCAColorMod() const { return (flags & 0x8) != 0; }
void setINCAColorMod(bool enabled) {
flags &= ~0x8;
flags |= atUint32(enabled) << 3;
}
bool TRANInvert() const { return (flags & 0x10) != 0; } bool TRANInvert() const { return (flags & 0x10) != 0; }
void setTRANInvert(bool enabled) { void setTRANInvert(bool enabled) {
flags &= ~0x10; flags &= ~0x10;
@ -104,36 +155,21 @@ struct MaterialSet : BigDNA {
Value<atUint32> uvAnimSize; Value<atUint32> uvAnimSize;
struct UVAnimation : BigDNA { struct UVAnimation : BigDNA {
AT_DECL_DNA AT_DECL_DNA
Value<atUint16> unk1; Value<UVAnimationUVSource> uvSource;
Value<atUint16> unk2; Value<UVAnimationMatrixConfig> mtxConfig;
DNAMP1::MaterialSet::Material::UVAnimation anim; DNAMP1::MaterialSet::Material::UVAnimation anim;
}; };
Vector<UVAnimation, AT_DNA_COUNT(uvAnimSize != 0)> uvAnim; Vector<UVAnimation, AT_DNA_COUNT(uvAnimSize != 0)> uvAnim;
void constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const override;
}; };
struct SectionCLR : ISection { struct CLR : hecl::TypedRecordBigDNA<ChunkType::CLR> {
SectionCLR() : ISection(ISection::Type::CLR) {} AT_DECL_DNA
static SectionCLR* castTo(ISection* sec) {
return sec->m_type == Type::CLR ? static_cast<SectionCLR*>(sec) : nullptr;
}
AT_DECL_DNAV
enum class Subtype : atUint32 { CLR = SBIG('CLR '), DIFB = SBIG('DIFB') }; enum class Subtype : atUint32 { CLR = SBIG('CLR '), DIFB = SBIG('DIFB') };
DNAFourCC subtype; DNAFourCC subtype;
GX::Color color; GX::Color color;
CLR() = default;
void constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const override;
}; };
struct SectionINT : ISection { struct INT : hecl::TypedRecordBigDNA<ChunkType::INT> {
SectionINT() : ISection(ISection::Type::INT) {} AT_DECL_DNA
static SectionINT* castTo(ISection* sec) {
return sec->m_type == Type::INT ? static_cast<SectionINT*>(sec) : nullptr;
}
AT_DECL_DNAV
enum class Subtype : atUint32 { enum class Subtype : atUint32 {
OPAC = SBIG('OPAC'), OPAC = SBIG('OPAC'),
BLOD = SBIG('BLOD'), BLOD = SBIG('BLOD'),
@ -143,16 +179,12 @@ struct MaterialSet : BigDNA {
}; };
DNAFourCC subtype; DNAFourCC subtype;
Value<atUint32> value; Value<atUint32> value;
void constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const override;
}; };
struct SectionFactory : BigDNA { struct END : hecl::TypedRecordBigDNA<ChunkType::END> {
AT_DECL_EXPLICIT_DNA AT_DECL_DNA
std::unique_ptr<ISection> section;
}; };
std::vector<SectionFactory> sections; using Chunk = hecl::TypedVariantBigDNA<PASS, CLR, INT, END>;
std::vector<Chunk> chunks;
}; };
Vector<Material, AT_DNA_COUNT(materialCount)> materials; Vector<Material, AT_DNA_COUNT(materialCount)> materials;

View File

@ -14,11 +14,12 @@ set(DNAMP3_SOURCES
DNAMP3.hpp DNAMP3.cpp DNAMP3.hpp DNAMP3.cpp
PAK.cpp PAK.cpp
ANIM.cpp ANIM.cpp
CINF.cpp CINF.hpp
CHAR.cpp CHAR.cpp
CMDL.hpp CMDL.cpp CMDL.hpp CMDL.cpp
CMDLMaterials.cpp CMDLMaterials.cpp
CSKR.cpp CSKR.cpp
PATH.hpp
STRG.hpp STRG.cpp STRG.hpp STRG.cpp
MAPA.hpp MAPA.hpp
MREA.cpp) MREA.cpp)

View File

@ -9,6 +9,7 @@
#include "CHAR.hpp" #include "CHAR.hpp"
#include "MREA.hpp" #include "MREA.hpp"
#include "MAPA.hpp" #include "MAPA.hpp"
#include "PATH.hpp"
#include "SAVW.hpp" #include "SAVW.hpp"
#include "HINT.hpp" #include "HINT.hpp"
#include "DataSpec/DNACommon/TXTR.hpp" #include "DataSpec/DNACommon/TXTR.hpp"
@ -21,6 +22,8 @@ namespace DataSpec::DNAMP3 {
logvisor::Module Log("urde::DNAMP3"); logvisor::Module Log("urde::DNAMP3");
static bool GetNoShare(std::string_view name) { static bool GetNoShare(std::string_view name) {
if (name == "UniverseArea.pak"sv)
return false;
std::string lowerName(name); std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
if (!lowerName.compare(0, 7, "metroid")) if (!lowerName.compare(0, 7, "metroid"))
@ -194,6 +197,16 @@ void PAKBridge::addMAPATransforms(PAKRouter<PAKBridge>& pakRouter,
fmt::format(fmt(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId)); fmt::format(fmt(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId));
for (const MLVL::Area& area : mlvl.areas) { for (const MLVL::Area& area : mlvl.areas) {
{
/* Get PATH transform */
const nod::Node* areaNode;
const PAK::Entry* areaEntry = pakRouter.lookupEntry(area.areaMREAId, &areaNode);
PAKEntryReadStream rs = areaEntry->beginReadStream(*areaNode);
UniqueID64 pathId = MREA::GetPATHId(rs);
if (pathId.isValid())
addTo[pathId] = zeus::CMatrix4f(area.transformMtx[0], area.transformMtx[1], area.transformMtx[2], BottomRow)
.transposed();
}
hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath(); hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath();
if (area.areaNameId.isValid()) if (area.areaNameId.isValid())
pathOverrides[area.areaNameId] = hecl::ProjectPath(areaDirPath, pathOverrides[area.areaNameId] = hecl::ProjectPath(areaDirPath,
@ -236,16 +249,20 @@ ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const nod::Node& pakNode, con
return {SAVWCommon::ExtractSAVW<SAVW>, {_SYS_STR(".yaml")}}; return {SAVWCommon::ExtractSAVW<SAVW>, {_SYS_STR(".yaml")}};
case SBIG('HINT'): case SBIG('HINT'):
return {HINT::Extract, {_SYS_STR(".yaml")}}; return {HINT::Extract, {_SYS_STR(".yaml")}};
// case SBIG('CMDL'): case SBIG('CMDL'):
// return {CMDL::Extract, {_SYS_STR(".blend")}, 1}; return {CMDL::Extract, {_SYS_STR(".blend")}, 1};
// case SBIG('CHAR'): case SBIG('CINF'):
// return {CHAR::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2}; return {CINF::Extract<PAKBridge>, {_SYS_STR(".blend")}, 1};
// case SBIG('MLVL'): case SBIG('CHAR'):
// return {MLVL::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 3}; return {CHAR::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2};
// case SBIG('MREA'): case SBIG('MLVL'):
// return {MREA::Extract, {_SYS_STR(".blend")}, 4}; return {MLVL::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 3};
// case SBIG('MAPA'): case SBIG('MREA'):
// return {MAPA::Extract, {_SYS_STR(".blend")}, 4}; return {MREA::Extract, {_SYS_STR(".blend")}, 4};
case SBIG('MAPA'):
return {MAPA::Extract, {_SYS_STR(".blend")}, 4};
case SBIG('PATH'):
return {PATH::Extract, {_SYS_STR(".blend")}, 5};
case SBIG('FSM2'): case SBIG('FSM2'):
return {DNAFSM2::ExtractFSM2<UniqueID64>, {_SYS_STR(".yaml")}}; return {DNAFSM2::ExtractFSM2<UniqueID64>, {_SYS_STR(".yaml")}};
case SBIG('FONT'): case SBIG('FONT'):

View File

@ -14,16 +14,14 @@ MREA::StreamReader::StreamReader(athena::io::IStreamReader& source, atUint32 blk
m_blkCount = blkCount; m_blkCount = blkCount;
m_blockInfos.reserve(blkCount); m_blockInfos.reserve(blkCount);
for (atUint32 i = 0; i < blkCount; ++i) { for (atUint32 i = 0; i < blkCount; ++i) {
m_blockInfos.emplace_back(); BlockInfo& info = m_blockInfos.emplace_back();
BlockInfo& info = m_blockInfos.back();
info.read(source); info.read(source);
m_totalDecompLen += info.decompSize; m_totalDecompLen += info.decompSize;
} }
source.seekAlign32(); source.seekAlign32();
m_secIdxs.reserve(secIdxCount); m_secIdxs.reserve(secIdxCount);
for (atUint32 i = 0; i < secIdxCount; ++i) { for (atUint32 i = 0; i < secIdxCount; ++i) {
m_secIdxs.emplace_back(); std::pair<DNAFourCC, atUint32>& idx = m_secIdxs.emplace_back();
std::pair<DNAFourCC, atUint32>& idx = m_secIdxs.back();
idx.first.read(source); idx.first.read(source);
idx.second = source.readUint32Big(); idx.second = source.readUint32Big();
} }
@ -39,6 +37,15 @@ void MREA::StreamReader::writeSecIdxs(athena::io::IStreamWriter& writer) const {
} }
} }
bool MREA::StreamReader::seekToSection(FourCC sec, const std::vector<atUint32>& secSizes) {
auto search = std::find_if(m_secIdxs.begin(), m_secIdxs.end(), [sec](const auto& s) { return s.first == sec; });
if (search != m_secIdxs.end()) {
DNAMP2::MREA::StreamReader::seekToSection(search->second, secSizes);
return true;
}
return false;
}
void MREA::ReadBabeDeadToBlender_3(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs) { void MREA::ReadBabeDeadToBlender_3(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs) {
atUint32 bdMagic = rs.readUint32Big(); atUint32 bdMagic = rs.readUint32Big();
if (bdMagic != 0xBABEDEAD) if (bdMagic != 0xBABEDEAD)
@ -254,5 +261,20 @@ bool MREA::ExtractLayerDeps(PAKEntryReadStream& rs, PAKBridge::Level::Area& area
return false; return false;
} }
UniqueID64 MREA::GetPATHId(PAKEntryReadStream& rs) {
/* Do extract */
Header head;
head.read(rs);
rs.seekAlign32();
/* MREA decompression stream */
StreamReader drs(rs, head.compressedBlockCount, head.secIndexCount);
/* Skip to PATH */
if (drs.seekToSection(FOURCC('PFL2'), head.secSizes))
return {drs};
return {};
}
} // namespace DNAMP3 } // namespace DNAMP3
} // namespace DataSpec } // namespace DataSpec

View File

@ -13,6 +13,7 @@ struct MREA {
StreamReader(athena::io::IStreamReader& source, atUint32 blkCount, atUint32 secIdxCount); StreamReader(athena::io::IStreamReader& source, atUint32 blkCount, atUint32 secIdxCount);
std::vector<std::pair<DNAFourCC, atUint32>>::const_iterator beginSecIdxs() { return m_secIdxs.begin(); } std::vector<std::pair<DNAFourCC, atUint32>>::const_iterator beginSecIdxs() { return m_secIdxs.begin(); }
void writeSecIdxs(athena::io::IStreamWriter& writer) const; void writeSecIdxs(athena::io::IStreamWriter& writer) const;
bool seekToSection(FourCC sec, const std::vector<atUint32>& secSizes);
}; };
struct Header : BigDNA { struct Header : BigDNA {
@ -84,6 +85,8 @@ struct MREA {
static void ReadBabeDeadToBlender_3(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs); static void ReadBabeDeadToBlender_3(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs);
static UniqueID64 GetPATHId(PAKEntryReadStream& rs);
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool, hecl::blender::Token& btok, PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool, hecl::blender::Token& btok,
std::function<void(const hecl::SystemChar*)>); std::function<void(const hecl::SystemChar*)>);

6
DataSpec/DNAMP3/PATH.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "DataSpec/DNACommon/PATH.hpp"
namespace DataSpec::DNAMP3 {
using PATH = DNAPATH::PATH<PAKBridge>;
} // namespace DataSpec::DNAMP3

2
hecl

@ -1 +1 @@
Subproject commit b3d91f520ad3a9429b0116f9b531ec12b2e71809 Subproject commit 87ad5cbc81dad92d54c60d388157ca6300475c19