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<atUint8> unk1;
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;

View File

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

View File

@ -1,5 +1,6 @@
#include "CINF.hpp"
#include "hecl/Blender/Connection.hpp"
#include "DataSpec/DNAMP3/DNAMP3.hpp"
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);
os.format(fmt(
@ -66,9 +68,18 @@ void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& c
tailF[2], bone.m_origBone.id);
}
for (const Bone& bone : bones)
if (bone.parentId != 97)
os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
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)
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";
@ -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"),
*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,
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);
}
template <class PAKBridge>
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,
std::function<void(const hecl::SystemChar*)> fileChanged) {
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry, bool force,
hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)> fileChanged) {
if (!force && outPath.isFile())
return true;
@ -179,12 +197,21 @@ bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
CINF cinf;
cinf.read(rs);
cinf.sendCINFToBlender(os, entry.id);
cinf.sendCINFToBlender<PAKBridge>(os, entry.id);
os.centerView();
os.close();
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,
const hecl::blender::Armature& armature) {
std::unordered_map<std::string, atInt32> boneIdMap;

View File

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

View File

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

View File

@ -68,8 +68,7 @@ MREA::StreamReader::StreamReader(athena::io::IStreamReader& source, atUint32 blk
, m_blkCount(blkCount) {
m_blockInfos.reserve(blkCount);
for (atUint32 i = 0; i < blkCount; ++i) {
m_blockInfos.emplace_back();
BlockInfo& info = m_blockInfos.back();
BlockInfo& info = m_blockInfos.emplace_back();
info.read(source);
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
#include "DataSpec/DNACommon/DNACommon.hpp"
#include "../DNAMP2/CINF.hpp"
#include "DataSpec/DNAMP2/CINF.hpp"
namespace DataSpec::DNAMP3 {
struct CINF : DNAMP2::CINF {
Delete expl;
void sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID64& cinfId) const;
static std::string GetCINFArmatureName(const UniqueID64& cinfId);
};
using CINF = DNAMP2::CINF;
} // namespace DataSpec::DNAMP3

View File

@ -6,341 +6,291 @@ using Stream = hecl::blender::PyOutStream;
namespace DataSpec::DNAMP3 {
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 <>
void MaterialSet::Material::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
header.read(reader);
sections.clear();
do {
sections.emplace_back();
sections.back().read(reader);
} while (sections.back().section);
sections.pop_back();
chunks.clear();
do { chunks.emplace_back().read(reader); } while (!chunks.back().holds_alternative<END>());
chunks.pop_back();
}
template <>
void MaterialSet::Material::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
header.write(writer);
for (const SectionFactory& section : sections)
section.write(writer);
writer.writeUBytes((atUint8*)"END ", 4);
for (const auto& chunk : chunks)
chunk.visit([&](auto& arg) { arg.write(writer); });
DNAFourCC(FOURCC('END ')).write(writer);
}
template <>
void MaterialSet::Material::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
header.binarySize(s);
for (const SectionFactory& section : sections)
section.binarySize(s);
for (const auto& chunk : chunks)
chunk.visit([&](auto& arg) { arg.binarySize(s); });
s += 4;
}
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_lightmapped = bpy.props.BoolProperty(name='Retro: Lightmapped')\n"
"bpy.types.Material.retro_opac = bpy.props.IntProperty(name='Retro: OPAC')\n"
"bpy.types.Material.retro_blod = bpy.props.IntProperty(name='Retro: BLOD')\n"
"bpy.types.Material.retro_bloi = bpy.props.IntProperty(name='Retro: BLOI')\n"
"bpy.types.Material.retro_bnif = bpy.props.IntProperty(name='Retro: BNIF')\n"
"bpy.types.Material.retro_xrbr = bpy.props.IntProperty(name='Retro: XRBR')\n"
"bpy.types.Material.retro_solid_white = bpy.props.BoolProperty(name='Retro: Solid White Only')\n"
"bpy.types.Material.retro_reflection_alpha_target = bpy.props.BoolProperty(name='Retro: Reflection Alpha Target')\n"
"bpy.types.Material.retro_solid_color = bpy.props.BoolProperty(name='Retro: Solid Color Only')\n"
"bpy.types.Material.retro_exclude_scan = bpy.props.BoolProperty(name='Retro: Exclude From Scan Visor')\n"
"bpy.types.Material.retro_xray_opaque = bpy.props.BoolProperty(name='Retro: XRay Opaque')\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";
}
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,
const Material& material, unsigned groupIdx, unsigned matIdx) {
unsigned i;
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);
out.format(fmt("new_material = bpy.data.materials.new('MAT_{}_{}')\n"), groupIdx, matIdx);
out << "new_material.use_fake_user = True\n"
"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";
/* Material Flags */
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_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.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 << "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";
}
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";
/* Texmap list */
out << "tex_maps = []\n"
"pnode = None\n"
"anode = None\n"
"rflv_tex_node = None\n";
if (material.header.flags.additiveIncandecence())
out << "pnode.inputs['Add INCA'].default_value = 1\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();
}
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";
/* 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";
}
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;
}
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,
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 PAK::Entry* texEntry = pakRouter.lookupEntry(txtrId, &node);
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry);
if (txtrPath.isNone()) {
txtrPath.makeDirChain(false);
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath);
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;
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
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; }
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
using VAFlags = DNAMP1::MaterialSet::Material::VAFlags;
struct Header : BigDNA {
@ -26,8 +30,18 @@ struct MaterialSet : BigDNA {
struct Flags : BigDNA {
AT_DECL_DNA
Value<atUint32> flags;
bool alphaBlending() const { return (flags & 0x8) != 0; }
void setAlphaBlending(bool enabled) {
bool enableBloom() const { return (flags & 0x1) != 0; }
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 |= atUint32(enabled) << 3;
}
@ -36,8 +50,8 @@ struct MaterialSet : BigDNA {
flags &= ~0x10;
flags |= atUint32(enabled) << 4;
}
bool additiveBlending() const { return (flags & 0x20) != 0; }
void setAdditiveBlending(bool enabled) {
bool additiveIncandecence() const { return (flags & 0x20) != 0; }
void setAdditiveIncandecence(bool enabled) {
flags &= ~0x20;
flags |= atUint32(enabled) << 5;
}
@ -46,6 +60,36 @@ struct MaterialSet : BigDNA {
flags &= ~0x100;
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 */
} flags;
Value<atUint32> uniqueIdx;
@ -58,20 +102,12 @@ struct MaterialSet : BigDNA {
const Header::Flags& getFlags() const { return header.flags; }
const VAFlags& getVAFlags() const { return header.vaFlags; }
struct ISection : BigDNAV {
Delete expl;
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;
enum class ChunkType : atUint32 {
PASS = 'PASS', CLR = 'CLR ', INT = 'INT ', END = 'END '
};
struct SectionPASS : ISection {
SectionPASS() : ISection(ISection::Type::PASS) {}
static SectionPASS* castTo(ISection* sec) {
return sec->m_type == Type::PASS ? static_cast<SectionPASS*>(sec) : nullptr;
}
AT_DECL_DNAV
struct PASS : hecl::TypedRecordBigDNA<ChunkType::PASS> {
AT_DECL_DNA
Value<atUint32> size;
enum class Subtype : atUint32 {
DIFF = SBIG('DIFF'),
@ -93,6 +129,21 @@ struct MaterialSet : BigDNA {
struct Flags : BigDNA {
AT_DECL_DNA
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; }
void setTRANInvert(bool enabled) {
flags &= ~0x10;
@ -104,36 +155,21 @@ struct MaterialSet : BigDNA {
Value<atUint32> uvAnimSize;
struct UVAnimation : BigDNA {
AT_DECL_DNA
Value<atUint16> unk1;
Value<atUint16> unk2;
Value<UVAnimationUVSource> uvSource;
Value<UVAnimationMatrixConfig> mtxConfig;
DNAMP1::MaterialSet::Material::UVAnimation anim;
};
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 {
SectionCLR() : ISection(ISection::Type::CLR) {}
static SectionCLR* castTo(ISection* sec) {
return sec->m_type == Type::CLR ? static_cast<SectionCLR*>(sec) : nullptr;
}
AT_DECL_DNAV
struct CLR : hecl::TypedRecordBigDNA<ChunkType::CLR> {
AT_DECL_DNA
enum class Subtype : atUint32 { CLR = SBIG('CLR '), DIFB = SBIG('DIFB') };
DNAFourCC subtype;
GX::Color color;
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;
CLR() = default;
};
struct SectionINT : ISection {
SectionINT() : ISection(ISection::Type::INT) {}
static SectionINT* castTo(ISection* sec) {
return sec->m_type == Type::INT ? static_cast<SectionINT*>(sec) : nullptr;
}
AT_DECL_DNAV
struct INT : hecl::TypedRecordBigDNA<ChunkType::INT> {
AT_DECL_DNA
enum class Subtype : atUint32 {
OPAC = SBIG('OPAC'),
BLOD = SBIG('BLOD'),
@ -143,16 +179,12 @@ struct MaterialSet : BigDNA {
};
DNAFourCC subtype;
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 {
AT_DECL_EXPLICIT_DNA
std::unique_ptr<ISection> section;
struct END : hecl::TypedRecordBigDNA<ChunkType::END> {
AT_DECL_DNA
};
std::vector<SectionFactory> sections;
using Chunk = hecl::TypedVariantBigDNA<PASS, CLR, INT, END>;
std::vector<Chunk> chunks;
};
Vector<Material, AT_DNA_COUNT(materialCount)> materials;

View File

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

View File

@ -9,6 +9,7 @@
#include "CHAR.hpp"
#include "MREA.hpp"
#include "MAPA.hpp"
#include "PATH.hpp"
#include "SAVW.hpp"
#include "HINT.hpp"
#include "DataSpec/DNACommon/TXTR.hpp"
@ -21,6 +22,8 @@ namespace DataSpec::DNAMP3 {
logvisor::Module Log("urde::DNAMP3");
static bool GetNoShare(std::string_view name) {
if (name == "UniverseArea.pak"sv)
return false;
std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
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));
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();
if (area.areaNameId.isValid())
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")}};
case SBIG('HINT'):
return {HINT::Extract, {_SYS_STR(".yaml")}};
// case SBIG('CMDL'):
// return {CMDL::Extract, {_SYS_STR(".blend")}, 1};
// case SBIG('CHAR'):
// return {CHAR::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2};
// case SBIG('MLVL'):
// return {MLVL::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 3};
// case SBIG('MREA'):
// return {MREA::Extract, {_SYS_STR(".blend")}, 4};
// case SBIG('MAPA'):
// return {MAPA::Extract, {_SYS_STR(".blend")}, 4};
case SBIG('CMDL'):
return {CMDL::Extract, {_SYS_STR(".blend")}, 1};
case SBIG('CINF'):
return {CINF::Extract<PAKBridge>, {_SYS_STR(".blend")}, 1};
case SBIG('CHAR'):
return {CHAR::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2};
case SBIG('MLVL'):
return {MLVL::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 3};
case SBIG('MREA'):
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'):
return {DNAFSM2::ExtractFSM2<UniqueID64>, {_SYS_STR(".yaml")}};
case SBIG('FONT'):

View File

@ -14,16 +14,14 @@ MREA::StreamReader::StreamReader(athena::io::IStreamReader& source, atUint32 blk
m_blkCount = blkCount;
m_blockInfos.reserve(blkCount);
for (atUint32 i = 0; i < blkCount; ++i) {
m_blockInfos.emplace_back();
BlockInfo& info = m_blockInfos.back();
BlockInfo& info = m_blockInfos.emplace_back();
info.read(source);
m_totalDecompLen += info.decompSize;
}
source.seekAlign32();
m_secIdxs.reserve(secIdxCount);
for (atUint32 i = 0; i < secIdxCount; ++i) {
m_secIdxs.emplace_back();
std::pair<DNAFourCC, atUint32>& idx = m_secIdxs.back();
std::pair<DNAFourCC, atUint32>& idx = m_secIdxs.emplace_back();
idx.first.read(source);
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) {
atUint32 bdMagic = rs.readUint32Big();
if (bdMagic != 0xBABEDEAD)
@ -254,5 +261,20 @@ bool MREA::ExtractLayerDeps(PAKEntryReadStream& rs, PAKBridge::Level::Area& area
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 DataSpec

View File

@ -13,6 +13,7 @@ struct MREA {
StreamReader(athena::io::IStreamReader& source, atUint32 blkCount, atUint32 secIdxCount);
std::vector<std::pair<DNAFourCC, atUint32>>::const_iterator beginSecIdxs() { return m_secIdxs.begin(); }
void writeSecIdxs(athena::io::IStreamWriter& writer) const;
bool seekToSection(FourCC sec, const std::vector<atUint32>& secSizes);
};
struct Header : BigDNA {
@ -84,6 +85,8 @@ struct MREA {
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,
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool, hecl::blender::Token& btok,
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