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);
} }
for (const Bone& bone : bones) if constexpr (std::is_same_v<PAKBridge, DNAMP3::PAKBridge>) {
if (bone.parentId != 97) if (bones.size()) {
os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId); 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)
if (bone.parentId != 97)
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";
} }
static void LoadTexture(Stream& out, const UniqueID64& tex,
const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry) {
std::string texName = pakRouter.getBestEntryName(tex);
const nod::Node* node;
const typename PAKRouter<PAKBridge>::EntryType* texEntry = pakRouter.lookupEntry(tex, &node);
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry);
if (!txtrPath.isNone()) {
txtrPath.makeDirChain(false);
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath);
}
hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, tex);
hecl::SystemUTF8Conv resPathView(resPath);
out.format(fmt("if '{}' in bpy.data.images:\n"
" image = bpy.data.images['{}']\n"
"else:\n"
" image = bpy.data.images.load('''//{}''')\n"
" image.name = '{}'\n"
"\n"), texName, texName, resPathView, texName);
}
void MaterialSet::ConstructMaterial(Stream& out, const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, void MaterialSet::ConstructMaterial(Stream& out, const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry,
const Material& material, unsigned groupIdx, unsigned matIdx) { const Material& material, unsigned groupIdx, unsigned matIdx) {
unsigned i; out.format(fmt("new_material = bpy.data.materials.new('MAT_{}_{}')\n"), groupIdx, matIdx);
out << "new_material.use_fake_user = True\n"
out.format(fmt( "new_material.use_nodes = True\n"
"new_material = bpy.data.materials.new('MAT_{}_{}')\n" "new_material.use_backface_culling = True\n"
"new_material.use_shadows = True\n" "new_material.show_transparent_back = False\n"
"new_material.use_transparent_shadows = True\n" "new_material.blend_method = 'BLEND'\n"
"new_material.diffuse_color = (1.0,1.0,1.0)\n" "new_nodetree = new_material.node_tree\n"
"new_material.use_nodes = True\n" "for n in new_nodetree.nodes:\n"
"new_material.blend_method = 'BLEND'\n" " new_nodetree.nodes.remove(n)\n"
"new_nodetree = new_material.node_tree\n" "\n"
"material_node = new_nodetree.nodes['Material']\n" "gridder = hecl.Nodegrid(new_nodetree)\n"
"final_node = new_nodetree.nodes['Output']\n" "new_nodetree.nodes.remove(gridder.frames[2])\n"
"\n" "\n"
"gridder = hecl.Nodegrid(new_nodetree)\n" "texture_nodes = []\n"
"gridder.place_node(final_node, 3)\n" "kcolors = {}\n"
"gridder.place_node(material_node, 0)\n" "kalphas = {}\n"
"material_node.material = new_material\n" "tex_links = []\n"
"\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 */ /* Material Flags */
out.format(fmt( out.format(fmt(
"new_material.retro_enable_bloom = {}\n"
"new_material.retro_force_lighting_stage = {}\n"
"new_material.retro_pre_inca_transparency = {}\n"
"new_material.retro_alpha_test = {}\n" "new_material.retro_alpha_test = {}\n"
"new_material.retro_shadow_occluder = {}\n" "new_material.retro_shadow_occluder = {}\n"
"new_material.diffuse_color = (1, 1, 1, {})\n"), "new_material.retro_solid_white = {}\n"
"new_material.retro_reflection_alpha_target = {}\n"
"new_material.retro_solid_color = {}\n"
"new_material.retro_exclude_scan = {}\n"
"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.alphaTest() ? "True" : "False",
material.header.flags.shadowOccluderMesh() ? "True" : "False", material.header.flags.shadowOccluderMesh() ? "True" : "False",
material.header.flags.shadowOccluderMesh() ? "0" : "1"); 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");
/* Blend factors */ out << "pnode = new_nodetree.nodes.new('ShaderNodeGroup')\n"
out << "blend_node = new_nodetree.nodes.new('ShaderNodeGroup')\n" "pnode.name = 'Output'\n"
"blend_node.name = 'Blend'\n" "pnode.node_tree = bpy.data.node_groups['RetroShaderMP3']\n"
"gridder.place_node(blend_node, 2)\n"; "gridder.place_node(pnode, 1)\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 */ if (material.header.flags.additiveIncandecence())
out << "tex_maps = []\n" out << "pnode.inputs['Add INCA'].default_value = 1\n";
"pnode = None\n"
"anode = None\n"
"rflv_tex_node = None\n";
/* Add PASSes */ int texMtxIdx = 0;
i = 0; for (const auto& chunk : material.chunks) {
unsigned texMapIdx = 0; if (const Material::PASS* pass = chunk.get_if<Material::PASS>()) {
unsigned texMtxIdx = 0; LoadTexture(out, pass->txtrId, pakRouter, entry);
unsigned kColorIdx = 0; out << "# Texture\n"
Material::ISection* prevSection = nullptr; "tex_node = new_nodetree.nodes.new('ShaderNodeTexImage')\n"
for (const Material::SectionFactory& factory : material.sections) { "texture_nodes.append(tex_node)\n"
factory.section->constructNode(out, pakRouter, entry, prevSection, i++, texMapIdx, texMtxIdx, kColorIdx); "tex_node.image = image\n";
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 */ if (!pass->uvAnim.empty()) {
out << "if pnode:\n" const auto& uva = pass->uvAnim[0];
" new_nodetree.links.new(pnode.outputs['Next Color'], final_node.inputs['Color'])\n" switch (uva.uvSource) {
"else:\n" case Material::UVAnimationUVSource::Position:
" new_nodetree.links.new(kcolor_nodes[-1][0].outputs[0], final_node.inputs['Color'])\n" default:
"if anode:\n" out << "tex_uv_node = new_nodetree.nodes.new('ShaderNodeTexCoord')\n"
" new_nodetree.links.new(anode.outputs['Value'], final_node.inputs['Alpha'])\n" "tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['Window'], tex_node.inputs['Vector']))\n";
"elif pnode:\n" break;
" new_nodetree.links.new(pnode.outputs['Next Alpha'], final_node.inputs['Alpha'])\n" case Material::UVAnimationUVSource::Normal:
"else:\n" out << "tex_uv_node = new_nodetree.nodes.new('ShaderNodeTexCoord')\n"
" new_nodetree.links.new(kcolor_nodes[-1][1].outputs[0], final_node.inputs['Alpha'])\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;
}
out.format(fmt("tex_uv_node.label = 'MTX_{}'\n"), texMtxIdx);
} else {
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);
}
void Material::SectionPASS::constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter, out << "gridder.place_node(tex_uv_node, 0)\n"
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx, "gridder.place_node(tex_node, 0)\n"
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const { "tex_uv_node.location[0] -= 120\n"
/* Add Texture nodes */ "tex_node.location[0] += 120\n"
if (txtrId.isValid()) { "tex_node.location[1] += 176\n"
std::string texName = pakRouter.getBestEntryName(txtrId); "\n";
const nod::Node* node;
const PAK::Entry* texEntry = pakRouter.lookupEntry(txtrId, &node); if (!pass->uvAnim.empty()) {
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry); const auto& uva = pass->uvAnim[0];
if (txtrPath.isNone()) { DNAMP1::MaterialSet::Material::AddTextureAnim(out, uva.anim.mode, texMtxIdx++, uva.anim.vals);
txtrPath.makeDirChain(false); }
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath); 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;
case Subtype::BLOL:
DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOL'])\n";
break;
case Subtype::BLOD:
DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOD'])\n";
break;
case Subtype::CLR:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['CLR'])\n"
"new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['CLRA'])\n";
break;
case Subtype::TRAN:
DoSwap();
if (pass->flags.TRANInvert())
out << "invert_node = new_nodetree.nodes.new('ShaderNodeInvert')\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;
case Subtype::INCA:
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;
case Subtype::RFLV:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['RFLV'])\n";
break;
case Subtype::RFLD:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['RFLD'])\n"
"new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['RFLDA'])\n";
break;
case Subtype::LRLD:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['LRLD'])\n";
break;
case Subtype::LURD:
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;
case Subtype::BLOI:
DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOI'])\n";
break;
case Subtype::XRAY:
DoSwap();
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['XRAYC'])\n"
"new_nodetree.links.new(swap_output, pnode.inputs['XRAYA'])\n";
break;
default:
Log.report(logvisor::Fatal, fmt("Unknown PASS subtype"));
break;
}
} else if (const Material::CLR* clr = chunk.get_if<Material::CLR>()) {
using Subtype = Material::CLR::Subtype;
athena::simd_floats vec4;
clr->color.toVec4f().simd.copy_to(vec4);
switch (Subtype(clr->subtype.toUint32())) {
case Subtype::CLR:
out.format(fmt("pnode.inputs['CLR'].default_value = ({}, {}, {}, 1.0)\n"
"pnode.inputs['CLRA'].default_value = {}\n"),
vec4[0], vec4[1], vec4[2], vec4[3]);
break;
case Subtype::DIFB:
out.format(fmt("pnode.inputs['DIFBC'].default_value = ({}, {}, {}, 1.0)\n"
"pnode.inputs['DIFBA'].default_value = {}\n"),
vec4[0], vec4[1], vec4[2], vec4[3]);
break;
default:
Log.report(logvisor::Fatal, fmt("Unknown CLR subtype"));
break;
}
} else if (const Material::INT* val = chunk.get_if<Material::INT>()) {
using Subtype = Material::INT::Subtype;
switch (Subtype(val->subtype.toUint32())) {
case Subtype::OPAC:
out.format(fmt("pnode.inputs['OPAC'].default_value = {}\n"), val->value / 255.f);
break;
case Subtype::BLOD:
out.format(fmt("pnode.inputs['BLOD'].default_value = {}\n"), val->value / 255.f);
break;
case Subtype::BLOI:
out.format(fmt("pnode.inputs['BLOI'].default_value = {}\n"), val->value / 255.f);
break;
case Subtype::BNIF:
out.format(fmt("pnode.inputs['BNIF'].default_value = {}\n"), val->value / 255.f);
break;
case Subtype::XRBR:
out.format(fmt("pnode.inputs['XRBR'].default_value = {}\n"), val->value / 255.f);
break;
default:
Log.report(logvisor::Fatal, fmt("Unknown INT subtype"));
break;
}
} }
hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, txtrId);
hecl::SystemUTF8Conv resPathView(resPath);
out.format(fmt(
"if '{}' in bpy.data.textures:\n"
" image = bpy.data.images['{}']\n"
" texture = bpy.data.textures[image.name]\n"
"else:\n"
" image = bpy.data.images.load('''//{}''')\n"
" image.name = '{}'\n"
" texture = bpy.data.textures.new(image.name, 'IMAGE')\n"
" 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) */
if (Subtype(subtype.toUint32()) == Subtype::RFLV) {
if (txtrId.isValid())
out << "rflv_tex_node = texture_nodes[-1]\n";
return;
}
/* Add PASS node */
bool linkRAS = false;
out << "prev_pnode = pnode\n"
"pnode = new_nodetree.nodes.new('ShaderNodeGroup')\n";
switch (Subtype(subtype.toUint32())) {
case Subtype::DIFF: {
out << "pnode.node_tree = bpy.data.node_groups['RetroPassDIFF']\n";
if (txtrId.isValid()) {
out << "new_material.hecl_lightmap = texture.name\n"
<< "texture.image.use_fake_user = True\n";
}
linkRAS = true;
break;
}
case Subtype::RIML:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRIML']\n";
if (idx == 0)
linkRAS = true;
break;
case Subtype::BLOL:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOL']\n";
if (idx == 0)
linkRAS = true;
break;
case Subtype::BLOD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOD']\n";
if (idx == 0)
linkRAS = true;
break;
case Subtype::CLR:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassCLR']\n";
if (idx == 0)
linkRAS = true;
break;
case Subtype::TRAN:
if (flags.TRANInvert())
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTRANInv']\n";
else
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTRAN']\n";
break;
case Subtype::INCA:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassINCA']\n";
break;
case Subtype::RFLV:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRFLV']\n";
break;
case Subtype::RFLD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRFLD']\n"
"if rflv_tex_node:\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;
case Subtype::LRLD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassLRLD']\n";
break;
case Subtype::LURD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassLURD']\n";
break;
case Subtype::BLOI:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOI']\n";
break;
case Subtype::XRAY:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassXRAY']\n";
break;
case Subtype::TOON:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTOON']\n";
break;
default:
break;
}
out << "gridder.place_node(pnode, 2)\n";
if (txtrId.isValid()) {
out << "new_nodetree.links.new(texture_nodes[-1].outputs['Color'], pnode.inputs['Tex Color'])\n"
"new_nodetree.links.new(texture_nodes[-1].outputs['Value'], pnode.inputs['Tex Alpha'])\n";
}
if (linkRAS)
out << "new_nodetree.links.new(material_node.outputs['Color'], pnode.inputs['Prev Color'])\n"
"new_nodetree.links.new(material_node.outputs['Alpha'], pnode.inputs['Prev Alpha'])\n";
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:
out << "kc_node.label += ' DIFB'\n"
"ka_node.label += ' DIFB'\n";
break;
default:
break;
}
}
void Material::SectionINT::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 {
switch (Subtype(subtype.toUint32())) {
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:
out.format(fmt("new_material.retro_blod = {}\n"), value);
break;
case Subtype::BLOI:
out.format(fmt("new_material.retro_bloi = {}\n"), value);
break;
case Subtype::BNIF:
out.format(fmt("new_material.retro_bnif = {}\n"), value);
break;
case Subtype::XRBR:
out.format(fmt("new_material.retro_xrbr = {}\n"), value);
break;
default:
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