#pragma once #include "DataSpec/DNACommon/DNACommon.hpp" #include "DataSpec/DNACommon/GX.hpp" #include "DataSpec/DNACommon/CMDL.hpp" #include "DNAMP1.hpp" #include "hecl/Blender/Connection.hpp" namespace DataSpec::DNAMP1 { struct MaterialSet : BigDNA { static constexpr bool OneSection() { return false; } AT_DECL_DNA struct MaterialSetHead : BigDNA { AT_DECL_DNA Value textureCount = 0; Vector textureIDs; Value materialCount = 0; Vector materialEndOffs; void addTexture(const UniqueID32& id) { textureIDs.push_back(id); ++textureCount; } void addMaterialEndOff(atUint32 off) { materialEndOffs.push_back(off); ++materialCount; } template void ensureTexturesExtracted(PAKRouter& pakRouter) const { for (const auto& id : textureIDs) { const nod::Node* node; const PAK::Entry* texEntry = pakRouter.lookupEntry(id, &node); if (!texEntry) continue; hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry); if (txtrPath.isNone()) { txtrPath.makeDirChain(false); PAKEntryReadStream rs = texEntry->beginReadStream(*node); TXTR::Extract(rs, txtrPath); } } } } head; struct Material : BigDNA { AT_DECL_DNA struct Flags : BigDNA { AT_DECL_DNA Value flags = 0; bool konstValuesEnabled() const { return (flags & 0x8) != 0; } void setKonstValuesEnabled(bool enabled) { flags &= ~0x8; flags |= atUint32(enabled) << 3; } bool depthSorting() const { return (flags & 0x10) != 0; } void setDepthSorting(bool enabled) { flags &= ~0x10; flags |= atUint32(enabled) << 4; } bool alphaTest() const { return (flags & 0x20) != 0; } void setAlphaTest(bool enabled) { flags &= ~0x20; flags |= atUint32(enabled) << 5; } bool samusReflection() const { return (flags & 0x40) != 0; } void setSamusReflection(bool enabled) { flags &= ~0x40; flags |= atUint32(enabled) << 6; } bool depthWrite() const { return (flags & 0x80) != 0; } void setDepthWrite(bool enabled) { flags &= ~0x80; flags |= atUint32(enabled) << 7; } bool samusReflectionSurfaceEye() const { return (flags & 0x100) != 0; } void setSamusReflectionSurfaceEye(bool enabled) { flags &= ~0x100; flags |= atUint32(enabled) << 8; } bool shadowOccluderMesh() const { return (flags & 0x200) != 0; } void setShadowOccluderMesh(bool enabled) { flags &= ~0x200; flags |= atUint32(enabled) << 9; } bool samusReflectionIndirectTexture() const { return (flags & 0x400) != 0; } void setSamusReflectionIndirectTexture(bool enabled) { flags &= ~0x400; flags |= atUint32(enabled) << 10; } bool lightmap() const { return (flags & 0x800) != 0; } void setLightmap(bool enabled) { flags &= ~0x800; flags |= atUint32(enabled) << 11; } bool lightmapUVArray() const { return (flags & 0x2000) != 0; } void setLightmapUVArray(bool enabled) { flags &= ~0x2000; flags |= atUint32(enabled) << 13; } atUint16 textureSlots() const { return (flags >> 16) != 0; } void setTextureSlots(atUint16 texslots) { flags &= ~0xffff0000; flags |= atUint32(texslots) << 16; } } flags; const Flags& getFlags() const { return flags; } Value textureCount = 0; Vector textureIdxs; struct VAFlags : BigDNA { AT_DECL_DNA Value vaFlags = 0; GX::AttrType position() const { return GX::AttrType(vaFlags & 0x3); } void setPosition(GX::AttrType val) { vaFlags &= ~0x3; vaFlags |= atUint32(val); } GX::AttrType normal() const { return GX::AttrType(vaFlags >> 2 & 0x3); } void setNormal(GX::AttrType val) { vaFlags &= ~0xC; vaFlags |= atUint32(val) << 2; } GX::AttrType color0() const { return GX::AttrType(vaFlags >> 4 & 0x3); } void setColor0(GX::AttrType val) { vaFlags &= ~0x30; vaFlags |= atUint32(val) << 4; } GX::AttrType color1() const { return GX::AttrType(vaFlags >> 6 & 0x3); } void setColor1(GX::AttrType val) { vaFlags &= ~0xC0; vaFlags |= atUint32(val) << 6; } GX::AttrType tex0() const { return GX::AttrType(vaFlags >> 8 & 0x3); } void setTex0(GX::AttrType val) { vaFlags &= ~0x300; vaFlags |= atUint32(val) << 8; } GX::AttrType tex1() const { return GX::AttrType(vaFlags >> 10 & 0x3); } void setTex1(GX::AttrType val) { vaFlags &= ~0xC00; vaFlags |= atUint32(val) << 10; } GX::AttrType tex2() const { return GX::AttrType(vaFlags >> 12 & 0x3); } void setTex2(GX::AttrType val) { vaFlags &= ~0x3000; vaFlags |= atUint32(val) << 12; } GX::AttrType tex3() const { return GX::AttrType(vaFlags >> 14 & 0x3); } void setTex3(GX::AttrType val) { vaFlags &= ~0xC000; vaFlags |= atUint32(val) << 14; } GX::AttrType tex4() const { return GX::AttrType(vaFlags >> 16 & 0x3); } void setTex4(GX::AttrType val) { vaFlags &= ~0x30000; vaFlags |= atUint32(val) << 16; } GX::AttrType tex5() const { return GX::AttrType(vaFlags >> 18 & 0x3); } void setTex5(GX::AttrType val) { vaFlags &= ~0xC0000; vaFlags |= atUint32(val) << 18; } GX::AttrType tex6() const { return GX::AttrType(vaFlags >> 20 & 0x3); } void setTex6(GX::AttrType val) { vaFlags &= ~0x300000; vaFlags |= atUint32(val) << 20; } GX::AttrType pnMatIdx() const { return GX::AttrType(vaFlags >> 24 & 0x1); } void setPnMatIdx(GX::AttrType val) { vaFlags &= ~0x1000000; vaFlags |= atUint32(val & 0x1) << 24; } GX::AttrType tex0MatIdx() const { return GX::AttrType(vaFlags >> 25 & 0x1); } void setTex0MatIdx(GX::AttrType val) { vaFlags &= ~0x2000000; vaFlags |= atUint32(val & 0x1) << 25; } GX::AttrType tex1MatIdx() const { return GX::AttrType(vaFlags >> 26 & 0x1); } void setTex1MatIdx(GX::AttrType val) { vaFlags &= ~0x4000000; vaFlags |= atUint32(val & 0x1) << 26; } GX::AttrType tex2MatIdx() const { return GX::AttrType(vaFlags >> 27 & 0x1); } void setTex2MatIdx(GX::AttrType val) { vaFlags &= ~0x8000000; vaFlags |= atUint32(val & 0x1) << 27; } GX::AttrType tex3MatIdx() const { return GX::AttrType(vaFlags >> 28 & 0x1); } void setTex3MatIdx(GX::AttrType val) { vaFlags &= ~0x10000000; vaFlags |= atUint32(val & 0x1) << 28; } GX::AttrType tex4MatIdx() const { return GX::AttrType(vaFlags >> 29 & 0x1); } void setTex4MatIdx(GX::AttrType val) { vaFlags &= ~0x20000000; vaFlags |= atUint32(val & 0x1) << 29; } GX::AttrType tex5MatIdx() const { return GX::AttrType(vaFlags >> 30 & 0x1); } void setTex5MatIdx(GX::AttrType val) { vaFlags &= ~0x40000000; vaFlags |= atUint32(val & 0x1) << 30; } GX::AttrType tex6MatIdx() const { return GX::AttrType(vaFlags >> 31 & 0x1); } void setTex6MatIdx(GX::AttrType val) { vaFlags &= ~0x80000000; vaFlags |= atUint32(val & 0x1) << 31; } size_t vertDLSize() const { static size_t ATTR_SZ[] = {0, 1, 1, 2}; size_t ret = 0; ret += ATTR_SZ[position()]; ret += ATTR_SZ[normal()]; ret += ATTR_SZ[color0()]; ret += ATTR_SZ[color1()]; ret += ATTR_SZ[tex0()]; ret += ATTR_SZ[tex1()]; ret += ATTR_SZ[tex2()]; ret += ATTR_SZ[tex3()]; ret += ATTR_SZ[tex4()]; ret += ATTR_SZ[tex5()]; ret += ATTR_SZ[tex6()]; ret += ATTR_SZ[pnMatIdx()]; ret += ATTR_SZ[tex0MatIdx()]; ret += ATTR_SZ[tex1MatIdx()]; ret += ATTR_SZ[tex2MatIdx()]; ret += ATTR_SZ[tex3MatIdx()]; ret += ATTR_SZ[tex4MatIdx()]; ret += ATTR_SZ[tex5MatIdx()]; ret += ATTR_SZ[tex6MatIdx()]; return ret; } } vaFlags; const VAFlags& getVAFlags() const { return vaFlags; } Value uniqueIdx; Vector konstCount; Vector konstColors; using BlendFactor = GX::BlendFactor; Value blendDstFac; Value blendSrcFac; Vector indTexSlot; Value colorChannelCount = 0; struct ColorChannel : BigDNA { AT_DECL_DNA Value flags = 0; bool lighting() const { return (flags & 0x1) != 0; } void setLighting(bool enabled) { flags &= ~0x1; flags |= atUint32(enabled); } bool useAmbient() const { return (flags & 0x2) != 0; } void setUseAmbient(bool enabled) { flags &= ~0x2; flags |= atUint32(enabled) << 1; } bool useMaterial() const { return (flags & 0x4) != 0; } void setUseMaterial(bool enabled) { flags &= ~0x4; flags |= atUint32(enabled) << 2; } atUint8 lightmask() const { return atUint8(flags >> 3 & 0xff); } void setLightmask(atUint8 mask) { flags &= ~0x7f8; flags |= atUint32(mask) << 3; } GX::DiffuseFn diffuseFn() const { return GX::DiffuseFn(flags >> 11 & 0x3); } void setDiffuseFn(GX::DiffuseFn fn) { flags &= ~0x1800; flags |= atUint32(fn) << 11; } GX::AttnFn attenuationFn() const { return GX::AttnFn(flags >> 13 & 0x3); } void setAttenuationFn(GX::AttnFn fn) { flags &= ~0x6000; flags |= atUint32(fn) << 13; } }; Vector colorChannels; Value tevStageCount = 0; struct TEVStage : BigDNA { AT_DECL_DNA Value ciFlags = 0; Value aiFlags = 0; Value ccFlags = 0; Value acFlags = 0; Value pad = 0; Value kaInput = 0; Value kcInput = 0; Value rascInput = 0; GX::TevColorArg colorInA() const { return GX::TevColorArg(ciFlags & 0xf); } void setColorInA(GX::TevColorArg val) { ciFlags &= ~0x1f; ciFlags |= atUint32(val); } GX::TevColorArg colorInB() const { return GX::TevColorArg(ciFlags >> 5 & 0xf); } void setColorInB(GX::TevColorArg val) { ciFlags &= ~0x3e0; ciFlags |= atUint32(val) << 5; } GX::TevColorArg colorInC() const { return GX::TevColorArg(ciFlags >> 10 & 0xf); } void setColorInC(GX::TevColorArg val) { ciFlags &= ~0x7c00; ciFlags |= atUint32(val) << 10; } GX::TevColorArg colorInD() const { return GX::TevColorArg(ciFlags >> 15 & 0xf); } void setColorInD(GX::TevColorArg val) { ciFlags &= ~0xf8000; ciFlags |= atUint32(val) << 15; } GX::TevAlphaArg alphaInA() const { return GX::TevAlphaArg(aiFlags & 0x7); } void setAlphaInA(GX::TevAlphaArg val) { aiFlags &= ~0x1f; aiFlags |= atUint32(val); } GX::TevAlphaArg alphaInB() const { return GX::TevAlphaArg(aiFlags >> 5 & 0x7); } void setAlphaInB(GX::TevAlphaArg val) { aiFlags &= ~0x3e0; aiFlags |= atUint32(val) << 5; } GX::TevAlphaArg alphaInC() const { return GX::TevAlphaArg(aiFlags >> 10 & 0x7); } void setAlphaInC(GX::TevAlphaArg val) { aiFlags &= ~0x7c00; aiFlags |= atUint32(val) << 10; } GX::TevAlphaArg alphaInD() const { return GX::TevAlphaArg(aiFlags >> 15 & 0x7); } void setAlphaInD(GX::TevAlphaArg val) { aiFlags &= ~0xf8000; aiFlags |= atUint32(val) << 15; } GX::TevOp colorOp() const { return GX::TevOp(ccFlags & 0xf); } void setColorOp(GX::TevOp val) { ccFlags &= ~0x1; ccFlags |= atUint32(val); } GX::TevBias colorOpBias() const { return GX::TevBias(ccFlags >> 4 & 0x3); } void setColorOpBias(GX::TevBias val) { ccFlags &= ~0x30; ccFlags |= atUint32(val) << 4; } GX::TevScale colorOpScale() const { return GX::TevScale(ccFlags >> 6 & 0x3); } void setColorOpScale(GX::TevScale val) { ccFlags &= ~0xc0; ccFlags |= atUint32(val) << 6; } bool colorOpClamp() const { return ccFlags >> 8 & 0x1; } void setColorOpClamp(bool val) { ccFlags &= ~0x100; ccFlags |= atUint32(val) << 8; } GX::TevRegID colorOpOutReg() const { return GX::TevRegID(ccFlags >> 9 & 0x3); } void setColorOpOutReg(GX::TevRegID val) { ccFlags &= ~0x600; ccFlags |= atUint32(val) << 9; } GX::TevOp alphaOp() const { return GX::TevOp(acFlags & 0xf); } void setAlphaOp(GX::TevOp val) { acFlags &= ~0x1; acFlags |= atUint32(val); } GX::TevBias alphaOpBias() const { return GX::TevBias(acFlags >> 4 & 0x3); } void setAlphaOpBias(GX::TevBias val) { acFlags &= ~0x30; acFlags |= atUint32(val) << 4; } GX::TevScale alphaOpScale() const { return GX::TevScale(acFlags >> 6 & 0x3); } void setAlphaOpScale(GX::TevScale val) { acFlags &= ~0xc0; acFlags |= atUint32(val) << 6; } bool alphaOpClamp() const { return acFlags >> 8 & 0x1; } void setAlphaOpClamp(bool val) { acFlags &= ~0x100; acFlags |= atUint32(val) << 8; } GX::TevRegID alphaOpOutReg() const { return GX::TevRegID(acFlags >> 9 & 0x3); } void setAlphaOpOutReg(GX::TevRegID val) { acFlags &= ~0x600; acFlags |= atUint32(val) << 9; } GX::TevKColorSel kColorIn() const { return GX::TevKColorSel(kcInput); } void setKColorIn(GX::TevKColorSel val) { kcInput = val; } GX::TevKAlphaSel kAlphaIn() const { return GX::TevKAlphaSel(kaInput); } void setKAlphaIn(GX::TevKAlphaSel val) { kaInput = val; } GX::ChannelID rasIn() const { return GX::ChannelID(rascInput); } void setRASIn(GX::ChannelID id) { rascInput = id; } }; Vector tevStages; struct TEVStageTexInfo : BigDNA { AT_DECL_DNA Value pad = 0; Value texSlot = 0xff; Value tcgSlot = 0xff; }; Vector tevStageTexInfo; Value tcgCount = 0; struct TexCoordGen : BigDNA { AT_DECL_DNA Value flags = 0; GX::TexGenType type() const { return GX::TexGenType(flags & 0xf); } void setType(GX::TexGenType val) { flags &= ~0xf; flags |= atUint32(val); } GX::TexGenSrc source() const { return GX::TexGenSrc(flags >> 4 & 0x1f); } void setSource(GX::TexGenSrc val) { flags &= ~0x1f0; flags |= atUint32(val) << 4; } GX::TexMtx mtx() const { return GX::TexMtx((flags >> 9 & 0x1f) + 30); } void setMtx(GX::TexMtx val) { flags &= ~0x3e00; flags |= (atUint32(val) - 30) << 9; } bool normalize() const { return flags >> 14 & 0x1; } void setNormalize(bool val) { flags &= ~0x4000; flags |= atUint32(val) << 14; } GX::PTTexMtx postMtx() const { return GX::PTTexMtx((flags >> 15 & 0x3f) + 64); } void setPostMtx(GX::PTTexMtx val) { flags &= ~0x1f8000; flags |= (atUint32(val) - 64) << 15; } }; Vector tcgs; Value uvAnimsSize = 4; Value uvAnimsCount = 0; struct UVAnimation : BigDNA { AT_DECL_EXPLICIT_DNA enum class Mode { MvInvNoTranslation, MvInv, Scroll, Rotation, HStrip, VStrip, Model, CylinderEnvironment, Eight } mode; float vals[9]; UVAnimation() = default; UVAnimation(const std::string& gameFunction, const std::vector& gameArgs); }; Vector uvAnims; static void AddTexture(hecl::blender::PyOutStream& out, GX::TexGenSrc type, int mtxIdx, uint32_t texIdx, bool diffuse); static void AddTextureAnim(hecl::blender::PyOutStream& out, MaterialSet::Material::UVAnimation::Mode type, unsigned idx, const float* vals); static void AddKcolor(hecl::blender::PyOutStream& out, const GX::Color& col, unsigned idx); static void AddDynamicColor(hecl::blender::PyOutStream& out, unsigned idx); static void AddDynamicAlpha(hecl::blender::PyOutStream& out, unsigned idx); Material() = default; Material(const hecl::blender::Material& material, std::vector& texPathsOut, int colorCount, bool lightmapUVs, bool matrixSkinning); }; Vector materials; static void RegisterMaterialProps(hecl::blender::PyOutStream& out); static void ConstructMaterial(hecl::blender::PyOutStream& out, const MaterialSet::Material& material, unsigned groupIdx, unsigned matIdx); void readToBlender(hecl::blender::PyOutStream& os, const PAKRouter& pakRouter, const PAKRouter::EntryType& entry, unsigned setIdx) { DNACMDL::ReadMaterialSetToBlender_1_2(os, *this, pakRouter, entry, setIdx); } template void nameTextures(PAKRouter& pakRouter, const char* prefix, int setIdx) const { int matIdx = 0; for (const Material& mat : materials) { int stageIdx = 0; for (const Material::TEVStage& stage : mat.tevStages) { (void)stage; const Material::TEVStageTexInfo& texInfo = mat.tevStageTexInfo[stageIdx]; if (texInfo.texSlot == 0xff) { ++stageIdx; continue; } const nod::Node* node; typename PAKRouter::EntryType* texEntry = (typename PAKRouter::EntryType*)pakRouter.lookupEntry( head.textureIDs[mat.textureIdxs[texInfo.texSlot]], &node); if (texEntry->name.size()) { if (texEntry->name.size() < 5 || texEntry->name.compare(0, 5, "mult_")) texEntry->name = "mult_" + texEntry->name; ++stageIdx; continue; } if (setIdx < 0) texEntry->name = fmt::format(fmt("{}_{}_{}"), prefix, matIdx, stageIdx); else texEntry->name = fmt::format(fmt("{}_{}_{}_{}"), prefix, setIdx, matIdx, stageIdx); if (mat.flags.lightmap() && stageIdx == 0) { texEntry->name += "light"; ++stageIdx; continue; } ++stageIdx; } ++matIdx; } } void ensureTexturesExtracted(PAKRouter& pakRouter) const { head.ensureTexturesExtracted(pakRouter); } }; struct HMDLMaterialSet : BigDNA { static constexpr bool OneSection() { return false; } AT_DECL_DNA Value materialCount = 0; Vector materialEndOffs; struct Material : BigDNA { AT_DECL_DNA MaterialSet::Material::Flags flags; using BlendMaterial = hecl::blender::Material; struct PASS : hecl::TypedRecordBigDNA { AT_DECL_EXPLICIT_DNA Value type; UniqueID32 texId; Value source; Value uvAnimType; Value uvAnimParms[9] = {}; Value alpha; PASS() = default; explicit PASS(const BlendMaterial::PASS& pass) : type(pass.type), texId(pass.tex), source(pass.source), uvAnimType(pass.uvAnimType), alpha(pass.alpha) { std::copy(pass.uvAnimParms.begin(), pass.uvAnimParms.end(), std::begin(uvAnimParms)); } bool shouldNormalizeUv() const { switch (uvAnimType) { case BlendMaterial::UVAnimType::MvInvNoTranslation: case BlendMaterial::UVAnimType::MvInv: case BlendMaterial::UVAnimType::Model: case BlendMaterial::UVAnimType::CylinderEnvironment: return true; default: return false; } } size_t uvAnimParamsCount() const { switch (uvAnimType) { default: return 0; case BlendMaterial::UVAnimType::Scroll: case BlendMaterial::UVAnimType::HStrip: case BlendMaterial::UVAnimType::VStrip: return 4; case BlendMaterial::UVAnimType::Rotation: case BlendMaterial::UVAnimType::CylinderEnvironment: return 2; case BlendMaterial::UVAnimType::Eight: return 9; } } }; struct CLR : hecl::TypedRecordBigDNA { AT_DECL_DNA Value type; Value color; CLR() = default; explicit CLR(const BlendMaterial::CLR& clr) : type(clr.type), color(clr.color.val) {} }; using Chunk = hecl::TypedVariantBigDNA; static unsigned TexMapIdx(BlendMaterial::PassType type) { switch (type) { case BlendMaterial::PassType::Lightmap: return 0; case BlendMaterial::PassType::Diffuse: return 1; case BlendMaterial::PassType::Emissive: return 2; case BlendMaterial::PassType::Specular: return 3; case BlendMaterial::PassType::ExtendedSpecular: return 4; case BlendMaterial::PassType::Reflection: return 5; case BlendMaterial::PassType::Alpha: return 6; case BlendMaterial::PassType::IndirectTex: return 7; default: assert(false && "Unknown pass type"); return 0; } } Value hash; Value shaderType; Value chunkCount; Vector chunks; Value blendMode = BlendMaterial::BlendMode::Opaque; std::pair blendFactors() const { switch (blendMode) { case BlendMaterial::BlendMode::Opaque: default: return {hecl::Backend::BlendFactor::One, hecl::Backend::BlendFactor::Zero}; case BlendMaterial::BlendMode::Alpha: return {hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha}; case BlendMaterial::BlendMode::Additive: return {hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::One}; } } Material() = default; Material(const hecl::blender::Material& mat); }; Vector materials; }; } // namespace DataSpec::DNAMP1 AT_SPECIALIZE_TYPED_VARIANT_BIGDNA(DataSpec::DNAMP1::HMDLMaterialSet::Material::PASS, DataSpec::DNAMP1::HMDLMaterialSet::Material::CLR)