metaforce/DataSpec/DNAMP1/ANCS.cpp

1443 lines
45 KiB
C++

#include "ANCS.hpp"
#include "hecl/Blender/Connection.hpp"
namespace DataSpec {
extern hecl::Database::DataSpecEntry SpecEntMP1;
extern hecl::Database::DataSpecEntry SpecEntMP1PC;
namespace DNAMP1 {
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::Enumerate<BigDNA::Read>(
athena::io::IStreamReader& reader) {
parmType = reader.readUint32Big();
weightFunction = reader.readUint32Big();
weight = reader.readFloatBig();
switch (DataType(parmType)) {
case DataType::Int32:
range[0].int32 = reader.readInt32Big();
range[1].int32 = reader.readInt32Big();
break;
case DataType::UInt32:
case DataType::Enum:
range[0].uint32 = reader.readUint32Big();
range[1].uint32 = reader.readUint32Big();
break;
case DataType::Float:
range[0].float32 = reader.readFloatBig();
range[1].float32 = reader.readFloatBig();
break;
case DataType::Bool:
range[0].bool1 = reader.readBool();
range[1].bool1 = reader.readBool();
break;
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::Enumerate<BigDNA::Write>(
athena::io::IStreamWriter& writer) {
writer.writeUint32Big(parmType);
writer.writeUint32Big(weightFunction);
writer.writeFloatBig(weight);
switch (DataType(parmType)) {
case DataType::Int32:
writer.writeInt32Big(range[0].int32);
writer.writeInt32Big(range[1].int32);
break;
case DataType::UInt32:
case DataType::Enum:
writer.writeUint32Big(range[0].uint32);
writer.writeUint32Big(range[1].uint32);
break;
case DataType::Float:
writer.writeFloatBig(range[0].float32);
writer.writeFloatBig(range[1].float32);
break;
case DataType::Bool:
writer.writeBool(range[0].bool1);
writer.writeBool(range[1].bool1);
break;
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
__isz += 12;
switch (DataType(parmType)) {
case DataType::Int32:
case DataType::UInt32:
case DataType::Enum:
case DataType::Float:
__isz += 8;
break;
case DataType::Bool:
__isz += 2;
break;
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::Enumerate<BigDNA::ReadYaml>(
athena::io::YAMLDocReader& reader) {
parmType = reader.readUint32("parmType");
weightFunction = reader.readUint32("weightFunction");
weight = reader.readFloat("weight");
size_t parmValCount;
if (auto v = reader.enterSubVector("range", parmValCount)) {
switch (DataType(parmType)) {
case DataType::Int32:
range[0].int32 = reader.readInt32();
range[1].int32 = reader.readInt32();
break;
case DataType::UInt32:
case DataType::Enum:
range[0].uint32 = reader.readUint32();
range[1].uint32 = reader.readUint32();
break;
case DataType::Float:
range[0].float32 = reader.readFloat();
range[1].float32 = reader.readFloat();
break;
case DataType::Bool:
range[0].bool1 = reader.readBool();
range[1].bool1 = reader.readBool();
break;
default:
break;
}
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::Enumerate<BigDNA::WriteYaml>(
athena::io::YAMLDocWriter& writer) {
writer.writeUint32("parmType", parmType);
writer.writeUint32("weightFunction", weightFunction);
writer.writeFloat("weight", weight);
if (auto v = writer.enterSubVector("range")) {
switch (DataType(parmType)) {
case DataType::Int32:
writer.writeInt32(range[0].int32);
writer.writeInt32(range[1].int32);
break;
case DataType::UInt32:
case DataType::Enum:
writer.writeUint32(range[0].uint32);
writer.writeUint32(range[1].uint32);
break;
case DataType::Float:
writer.writeFloat(range[0].float32);
writer.writeFloat(range[1].float32);
break;
case DataType::Bool:
writer.writeBool(range[0].bool1);
writer.writeBool(range[1].bool1);
break;
}
}
}
std::string_view ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::DNAType() {
return "DNAMP1::ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo"sv;
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::Enumerate<BigDNA::Read>(
athena::io::IStreamReader& reader) {
id = reader.readUint32Big();
atUint32 parmInfoCount = reader.readUint32Big();
atUint32 animInfoCount = reader.readUint32Big();
reader.enumerate(parmInfos, parmInfoCount);
animInfos.clear();
animInfos.reserve(animInfoCount);
reader.enumerate<AnimInfo>(animInfos, animInfoCount,
[this, parmInfoCount](athena::io::IStreamReader& reader, AnimInfo& ai) {
ai.id = reader.readUint32Big();
ai.parmVals.reserve(parmInfoCount);
for (const ParmInfo& pi : parmInfos) {
switch (ParmInfo::DataType(pi.parmType)) {
case ParmInfo::DataType::Int32:
ai.parmVals.emplace_back(reader.readInt32Big());
break;
case ParmInfo::DataType::UInt32:
case ParmInfo::DataType::Enum:
ai.parmVals.emplace_back(reader.readUint32Big());
break;
case ParmInfo::DataType::Float:
ai.parmVals.emplace_back(reader.readFloatBig());
break;
case ParmInfo::DataType::Bool:
ai.parmVals.emplace_back(reader.readBool());
break;
default:
break;
}
}
});
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::Enumerate<BigDNA::Write>(
athena::io::IStreamWriter& writer) {
writer.writeUint32Big(id);
writer.writeUint32Big(parmInfos.size());
writer.writeUint32Big(animInfos.size());
for (const ParmInfo& pi : parmInfos)
pi.write(writer);
for (const AnimInfo& ai : animInfos) {
writer.writeUint32Big(ai.id);
auto it = ai.parmVals.begin();
for (const ParmInfo& pi : parmInfos) {
ParmInfo::Parm pVal;
if (it != ai.parmVals.end())
pVal = *it++;
switch (ParmInfo::DataType(pi.parmType)) {
case ParmInfo::DataType::Int32:
writer.writeInt32Big(pVal.int32);
break;
case ParmInfo::DataType::UInt32:
case ParmInfo::DataType::Enum:
writer.writeUint32Big(pVal.uint32);
break;
case ParmInfo::DataType::Float:
writer.writeFloatBig(pVal.float32);
break;
case ParmInfo::DataType::Bool:
writer.writeBool(pVal.bool1);
break;
default:
break;
}
}
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
__isz += 12;
for (const ParmInfo& pi : parmInfos)
pi.binarySize(__isz);
__isz += animInfos.size() * 4;
for (const ParmInfo& pi : parmInfos) {
switch (ParmInfo::DataType(pi.parmType)) {
case ParmInfo::DataType::Int32:
case ParmInfo::DataType::UInt32:
case ParmInfo::DataType::Enum:
case ParmInfo::DataType::Float:
__isz += animInfos.size() * 4;
break;
case ParmInfo::DataType::Bool:
__isz += animInfos.size();
break;
default:
break;
}
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::Enumerate<BigDNA::ReadYaml>(
athena::io::YAMLDocReader& reader) {
id = reader.readUint32("id");
size_t parmInfoCount = reader.enumerate("parmInfos", parmInfos);
reader.enumerate<AnimInfo>("animInfos", animInfos,
[this, parmInfoCount](athena::io::YAMLDocReader& reader, AnimInfo& ai) {
ai.id = reader.readUint32("id");
ai.parmVals.reserve(parmInfoCount);
size_t parmValCount;
if (auto v = reader.enterSubVector("parms", parmValCount)) {
for (const ParmInfo& pi : parmInfos) {
switch (ParmInfo::DataType(pi.parmType)) {
case ParmInfo::DataType::Int32:
ai.parmVals.emplace_back(reader.readInt32());
break;
case ParmInfo::DataType::UInt32:
case ParmInfo::DataType::Enum:
ai.parmVals.emplace_back(reader.readUint32());
break;
case ParmInfo::DataType::Float:
ai.parmVals.emplace_back(reader.readFloat());
break;
case ParmInfo::DataType::Bool:
ai.parmVals.emplace_back(reader.readBool());
break;
default:
break;
}
}
}
});
}
template <>
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::Enumerate<BigDNA::WriteYaml>(
athena::io::YAMLDocWriter& writer) {
writer.writeUint32("id", id);
writer.enumerate("parmInfos", parmInfos);
writer.enumerate<AnimInfo>("animInfos", animInfos, [this](athena::io::YAMLDocWriter& writer, const AnimInfo& ai) {
writer.writeUint32("id", ai.id);
auto it = ai.parmVals.begin();
if (auto v = writer.enterSubVector("parms")) {
for (const ParmInfo& pi : parmInfos) {
ParmInfo::Parm pVal;
if (it != ai.parmVals.end())
pVal = *it++;
switch (ParmInfo::DataType(pi.parmType)) {
case ParmInfo::DataType::Int32:
writer.writeInt32(pVal.int32);
break;
case ParmInfo::DataType::UInt32:
case ParmInfo::DataType::Enum:
writer.writeUint32(pVal.uint32);
break;
case ParmInfo::DataType::Float:
writer.writeFloat(pVal.float32);
break;
case ParmInfo::DataType::Bool:
writer.writeBool(pVal.bool1);
break;
default:
break;
}
}
}
});
}
std::string_view ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::DNAType() {
return "DNAMP1::ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState"sv;
}
template <>
void ANCS::CharacterSet::CharacterInfo::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
idx = reader.readUint32Big();
atUint16 sectionCount = reader.readUint16Big();
name = reader.readString();
cmdl.read(reader);
cskr.read(reader);
cinf.read(reader);
atUint32 animationCount = reader.readUint32Big();
reader.enumerate(animations, animationCount);
pasDatabase.read(reader);
atUint32 partCount = reader.readUint32Big();
reader.enumerate(partResData.part, partCount);
atUint32 swhcCount = reader.readUint32Big();
reader.enumerate(partResData.swhc, swhcCount);
atUint32 unkCount = reader.readUint32Big();
reader.enumerate(partResData.unk, unkCount);
partResData.elsc.clear();
if (sectionCount > 5) {
atUint32 elscCount = reader.readUint32Big();
reader.enumerate(partResData.elsc, elscCount);
}
unk1 = reader.readUint32Big();
animAABBs.clear();
if (sectionCount > 1) {
atUint32 aabbCount = reader.readUint32Big();
reader.enumerate(animAABBs, aabbCount);
}
effects.clear();
if (sectionCount > 2) {
atUint32 effectCount = reader.readUint32Big();
reader.enumerate(effects, effectCount);
}
if (sectionCount > 3) {
cmdlIce.read(reader);
cskrIce.read(reader);
}
animIdxs.clear();
if (sectionCount > 4) {
atUint32 aidxCount = reader.readUint32Big();
reader.enumerateBig(animIdxs, aidxCount);
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
writer.writeUint32Big(idx);
atUint16 sectionCount;
if (partResData.elsc.size())
sectionCount = 6;
else if (animIdxs.size())
sectionCount = 5;
else if (cmdlIce.isValid())
sectionCount = 4;
else if (effects.size())
sectionCount = 3;
else if (animAABBs.size())
sectionCount = 2;
else
sectionCount = 1;
writer.writeUint16Big(sectionCount);
writer.writeString(name);
cmdl.write(writer);
cskr.write(writer);
cinf.write(writer);
writer.writeUint32Big(animations.size());
writer.enumerate(animations);
pasDatabase.write(writer);
writer.writeUint32Big(partResData.part.size());
writer.enumerate(partResData.part);
writer.writeUint32Big(partResData.swhc.size());
writer.enumerate(partResData.swhc);
writer.writeUint32Big(partResData.unk.size());
writer.enumerate(partResData.unk);
if (sectionCount > 5) {
writer.writeUint32Big(partResData.elsc.size());
writer.enumerate(partResData.elsc);
}
writer.writeUint32Big(unk1);
if (sectionCount > 1) {
writer.writeUint32Big(animAABBs.size());
writer.enumerate(animAABBs);
}
if (sectionCount > 2) {
writer.writeUint32Big(effects.size());
writer.enumerate(effects);
}
if (sectionCount > 3) {
cmdlIce.write(writer);
cskrIce.write(writer);
}
if (sectionCount > 4) {
writer.writeUint32Big(animIdxs.size());
for (atUint32 idx : animIdxs)
writer.writeUint32Big(idx);
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
__isz += 6;
atUint16 sectionCount;
if (partResData.elsc.size())
sectionCount = 6;
else if (animIdxs.size())
sectionCount = 5;
else if (cmdlIce.isValid())
sectionCount = 4;
else if (effects.size())
sectionCount = 3;
else if (animAABBs.size())
sectionCount = 2;
else
sectionCount = 1;
__isz += name.size() + 1;
__isz += 12;
__isz += 4;
for (const Animation& a : animations)
a.binarySize(__isz);
pasDatabase.binarySize(__isz);
__isz += 4;
for (const UniqueID32& id : partResData.part)
id.binarySize(__isz);
__isz += 4;
for (const UniqueID32& id : partResData.swhc)
id.binarySize(__isz);
__isz += 4;
for (const UniqueID32& id : partResData.unk)
id.binarySize(__isz);
if (sectionCount > 5) {
__isz += 4;
for (const UniqueID32& id : partResData.elsc)
id.binarySize(__isz);
}
__isz += 4;
if (sectionCount > 1) {
__isz += 4;
for (const ActionAABB& aabb : animAABBs)
aabb.binarySize(__isz);
}
if (sectionCount > 2) {
__isz += 4;
for (const Effect& e : effects)
e.binarySize(__isz);
}
if (sectionCount > 3)
__isz += 8;
if (sectionCount > 4)
__isz += 4 + animIdxs.size() * 4;
}
template <>
void ANCS::CharacterSet::CharacterInfo::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
idx = reader.readUint32("idx");
atUint16 sectionCount = reader.readUint16("sectionCount");
name = reader.readString("name");
reader.enumerate("cmdl", cmdl);
reader.enumerate("animations", animations);
reader.enumerate("pasDatabase", pasDatabase);
reader.enumerate("part", partResData.part);
reader.enumerate("swhc", partResData.swhc);
reader.enumerate("unk", partResData.unk);
partResData.elsc.clear();
if (sectionCount > 5) {
reader.enumerate("elsc", partResData.elsc);
}
unk1 = reader.readUint32("unk1");
animAABBs.clear();
if (sectionCount > 1) {
reader.enumerate("part", animAABBs);
}
effects.clear();
if (sectionCount > 2) {
reader.enumerate("effects", effects);
}
if (sectionCount > 3) {
reader.enumerate("cmdlIce", cmdlIce);
}
animIdxs.clear();
if (sectionCount > 4) {
reader.enumerate("animIdxs", animIdxs);
}
}
template <>
void ANCS::CharacterSet::CharacterInfo::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
writer.writeUint32("idx", idx);
atUint16 sectionCount;
if (partResData.elsc.size())
sectionCount = 6;
else if (animIdxs.size())
sectionCount = 5;
else if (cmdlIce.isValid())
sectionCount = 4;
else if (effects.size())
sectionCount = 3;
else if (animAABBs.size())
sectionCount = 2;
else
sectionCount = 1;
writer.writeUint16("sectionCount", sectionCount);
writer.writeString("name", name);
writer.enumerate("cmdl", cmdl);
writer.enumerate("animations", animations);
writer.enumerate("pasDatabase", pasDatabase);
writer.enumerate("part", partResData.part);
writer.enumerate("swhc", partResData.swhc);
writer.enumerate("unk", partResData.unk);
if (sectionCount > 5) {
writer.enumerate("elsc", partResData.elsc);
}
writer.writeUint32("unk1", unk1);
if (sectionCount > 1) {
writer.enumerate("animAABBs", animAABBs);
}
if (sectionCount > 2) {
writer.enumerate("effects", effects);
}
if (sectionCount > 3) {
writer.enumerate("cmdlIce", cmdlIce);
}
if (sectionCount > 4) {
writer.enumerate("animIdxs", animIdxs);
}
}
std::string_view ANCS::CharacterSet::CharacterInfo::DNAType() { return "DNAMP1::ANCS::CharacterSet::CharacterInfo"sv; }
template <>
void ANCS::AnimationSet::MetaAnimFactory::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
const auto type = IMetaAnim::Type(reader.readUint32Big());
switch (type) {
case IMetaAnim::Type::Primitive:
m_anim = std::make_unique<MetaAnimPrimitive>();
m_anim->read(reader);
break;
case IMetaAnim::Type::Blend:
m_anim = std::make_unique<MetaAnimBlend>();
m_anim->read(reader);
break;
case IMetaAnim::Type::PhaseBlend:
m_anim = std::make_unique<MetaAnimPhaseBlend>();
m_anim->read(reader);
break;
case IMetaAnim::Type::Random:
m_anim = std::make_unique<MetaAnimRandom>();
m_anim->read(reader);
break;
case IMetaAnim::Type::Sequence:
m_anim = std::make_unique<MetaAnimSequence>();
m_anim->read(reader);
break;
default:
m_anim.reset();
break;
}
}
template <>
void ANCS::AnimationSet::MetaAnimFactory::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
if (!m_anim)
return;
writer.writeInt32Big(atUint32(m_anim->m_type));
m_anim->write(writer);
}
template <>
void ANCS::AnimationSet::MetaAnimFactory::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
if (!m_anim)
return;
__isz += 4;
m_anim->binarySize(__isz);
}
template <>
void ANCS::AnimationSet::MetaAnimFactory::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
std::string type = reader.readString("type");
std::transform(type.begin(), type.end(), type.begin(), tolower);
if (type == "primitive") {
m_anim = std::make_unique<MetaAnimPrimitive>();
m_anim->read(reader);
} else if (type == "blend") {
m_anim = std::make_unique<MetaAnimBlend>();
m_anim->read(reader);
} else if (type == "phaseblend") {
m_anim = std::make_unique<MetaAnimPhaseBlend>();
m_anim->read(reader);
} else if (type == "random") {
m_anim = std::make_unique<MetaAnimRandom>();
m_anim->read(reader);
} else if (type == "sequence") {
m_anim = std::make_unique<MetaAnimSequence>();
m_anim->read(reader);
} else {
m_anim.reset();
}
}
template <>
void ANCS::AnimationSet::MetaAnimFactory::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
if (!m_anim)
return;
writer.writeString("type", m_anim->m_typeStr);
m_anim->write(writer);
}
std::string_view ANCS::AnimationSet::MetaAnimFactory::DNAType() {
return "DNAMP1::ANCS::AnimationSet::MetaAnimFactory"sv;
}
template <>
void ANCS::AnimationSet::MetaTransFactory::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
IMetaTrans::Type type(IMetaTrans::Type(reader.readUint32Big()));
switch (type) {
case IMetaTrans::Type::MetaAnim:
m_trans = std::make_unique<MetaTransMetaAnim>();
m_trans->read(reader);
break;
case IMetaTrans::Type::Trans:
m_trans = std::make_unique<MetaTransTrans>();
m_trans->read(reader);
break;
case IMetaTrans::Type::PhaseTrans:
m_trans = std::make_unique<MetaTransPhaseTrans>();
m_trans->read(reader);
break;
case IMetaTrans::Type::NoTrans:
default:
m_trans.reset();
break;
}
}
template <>
void ANCS::AnimationSet::MetaTransFactory::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
if (!m_trans) {
writer.writeInt32Big(atUint32(IMetaTrans::Type::NoTrans));
return;
}
writer.writeInt32Big(atUint32(m_trans->m_type));
m_trans->write(writer);
}
template <>
void ANCS::AnimationSet::MetaTransFactory::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
__isz += 4;
if (!m_trans)
return;
m_trans->binarySize(__isz);
}
template <>
void ANCS::AnimationSet::MetaTransFactory::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
std::string type = reader.readString("type");
std::transform(type.begin(), type.end(), type.begin(), tolower);
if (type == "metaanim") {
m_trans = std::make_unique<MetaTransMetaAnim>();
m_trans->read(reader);
} else if (type == "trans") {
m_trans = std::make_unique<MetaTransTrans>();
m_trans->read(reader);
} else if (type == "phasetrans") {
m_trans = std::make_unique<MetaTransPhaseTrans>();
m_trans->read(reader);
} else {
m_trans.reset();
}
}
template <>
void ANCS::AnimationSet::MetaTransFactory::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
if (!m_trans) {
writer.writeString("type", "NoTrans");
return;
}
writer.writeString("type", m_trans->m_typeStr ? m_trans->m_typeStr : "NoTrans");
m_trans->write(writer);
}
std::string_view ANCS::AnimationSet::MetaTransFactory::DNAType() {
return "DNAMP1::ANCS::AnimationSet::MetaTransFactory"sv;
}
template <>
void ANCS::AnimationSet::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
atUint16 sectionCount = reader.readUint16Big();
atUint32 animationCount = reader.readUint32Big();
reader.enumerate(animations, animationCount);
atUint32 transitionCount = reader.readUint32Big();
reader.enumerate(transitions, transitionCount);
defaultTransition.read(reader);
additiveAnims.clear();
if (sectionCount > 1) {
atUint32 additiveAnimCount = reader.readUint32Big();
reader.enumerate(additiveAnims, additiveAnimCount);
additiveDefaultFadeInDur = reader.readFloatBig();
additiveDefaultFadeOutDur = reader.readFloatBig();
}
halfTransitions.clear();
if (sectionCount > 2) {
atUint32 halfTransitionCount = reader.readUint32Big();
reader.enumerate(halfTransitions, halfTransitionCount);
}
animResources.clear();
if (sectionCount > 3) {
atUint32 animResourcesCount = reader.readUint32Big();
reader.enumerate(animResources, animResourcesCount);
}
}
template <>
void ANCS::AnimationSet::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
atUint16 sectionCount;
if (animResources.size())
sectionCount = 4;
else if (halfTransitions.size())
sectionCount = 3;
else if (additiveAnims.size())
sectionCount = 2;
else
sectionCount = 1;
writer.writeUint16Big(sectionCount);
writer.writeUint32Big(animations.size());
writer.enumerate(animations);
writer.writeUint32Big(transitions.size());
writer.enumerate(transitions);
defaultTransition.write(writer);
if (sectionCount > 1) {
writer.writeUint32Big(additiveAnims.size());
writer.enumerate(additiveAnims);
writer.writeFloatBig(additiveDefaultFadeInDur);
writer.writeFloatBig(additiveDefaultFadeOutDur);
}
if (sectionCount > 2) {
writer.writeUint32Big(halfTransitions.size());
writer.enumerate(halfTransitions);
}
if (sectionCount > 3) {
writer.writeUint32Big(animResources.size());
writer.enumerate(animResources);
}
}
template <>
void ANCS::AnimationSet::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
atUint16 sectionCount;
if (animResources.size())
sectionCount = 4;
else if (halfTransitions.size())
sectionCount = 3;
else if (additiveAnims.size())
sectionCount = 2;
else
sectionCount = 1;
__isz += 6;
for (const Animation& a : animations)
a.binarySize(__isz);
__isz += 4;
for (const Transition& t : transitions)
t.binarySize(__isz);
defaultTransition.binarySize(__isz);
if (sectionCount > 1) {
__isz += 4;
for (const AdditiveAnimationInfo& aa : additiveAnims)
aa.binarySize(__isz);
__isz += 8;
}
if (sectionCount > 2) {
__isz += 4;
for (const HalfTransition& ht : halfTransitions)
ht.binarySize(__isz);
}
if (sectionCount > 3) {
__isz += 4;
for (const AnimationResources& ar : animResources)
ar.binarySize(__isz);
}
}
template <>
void ANCS::AnimationSet::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
atUint16 sectionCount = reader.readUint16("sectionCount");
reader.enumerate("animations", animations);
reader.enumerate("transitions", transitions);
reader.enumerate("defaultTransition", defaultTransition);
additiveAnims.clear();
if (sectionCount > 1) {
reader.enumerate("additiveAnims", additiveAnims);
additiveDefaultFadeInDur = reader.readFloat("additiveDefaultFadeInDur");
additiveDefaultFadeOutDur = reader.readFloat("additiveDefaultFadeOutDur");
}
halfTransitions.clear();
if (sectionCount > 2) {
reader.enumerate("halfTransitions", halfTransitions);
}
animResources.clear();
if (sectionCount > 3) {
reader.enumerate("animResources", animResources);
}
}
template <>
void ANCS::AnimationSet::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
atUint16 sectionCount;
if (animResources.size())
sectionCount = 4;
else if (halfTransitions.size())
sectionCount = 3;
else if (additiveAnims.size())
sectionCount = 2;
else
sectionCount = 1;
writer.writeUint16("sectionCount", sectionCount);
writer.enumerate("animations", animations);
writer.enumerate("transitions", transitions);
writer.enumerate("defaultTransition", defaultTransition);
if (sectionCount > 1) {
writer.enumerate("additiveAnims", additiveAnims);
writer.writeFloat("additiveDefaultFadeInDur", additiveDefaultFadeInDur);
writer.writeFloat("additiveDefaultFadeOutDur", additiveDefaultFadeOutDur);
}
if (sectionCount > 2) {
writer.enumerate("halfTransitions", halfTransitions);
}
if (sectionCount > 3) {
writer.enumerate("animResources", animResources);
}
}
void ANCS::AnimationSet::MetaAnimPrimitive::gatherPrimitives(
PAKRouter<PAKBridge>* pakRouter, std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) {
if (!pakRouter) {
out[animIdx] = {animName, animId, UniqueID32(), false};
return;
}
const nod::Node* node;
const PAK::Entry* entry = pakRouter->lookupEntry(animId, &node, true);
if (!entry) {
out[animIdx] = {animName, animId, UniqueID32(), false};
return;
}
PAKEntryReadStream rs = entry->beginReadStream(*node);
out[animIdx] = {animName, animId, ANIM::GetEVNTId(rs), false};
}
std::string_view ANCS::AnimationSet::DNAType() { return "DNAMP1::ANCS::AnimationSet"sv; }
bool ANCS::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 char*)> fileChanged) {
hecl::ProjectPath yamlPath = outPath.getWithExtension(".yaml", true);
hecl::ProjectPath::Type yamlType = yamlPath.getPathType();
hecl::ProjectPath blendPath = outPath.getWithExtension(".blend", true);
hecl::ProjectPath::Type blendType = blendPath.getPathType();
ANCS ancs;
ancs.read(rs);
if (force || yamlType == hecl::ProjectPath::Type::None || blendType == hecl::ProjectPath::Type::None) {
if (force || yamlType == hecl::ProjectPath::Type::None) {
athena::io::FileWriter writer(yamlPath.getAbsolutePath());
athena::io::ToYAMLStream(ancs, writer);
}
if (force || blendType == hecl::ProjectPath::Type::None) {
DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, ANCS, MaterialSet, DNACMDL::SurfaceHeader_1, 2>(
btok, ancs, blendPath, pakRouter, entry, dataSpec, fileChanged, force);
}
}
return true;
}
bool ANCS::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNAANCS::Actor& actor) {
/* Search for yaml */
hecl::ProjectPath yamlPath = inPath.getWithExtension(".yaml", true);
if (!yamlPath.isFile())
Log.report(logvisor::Fatal, FMT_STRING("'{}' not found as file"), yamlPath.getRelativePath());
athena::io::FileReader reader(yamlPath.getAbsolutePath());
if (!reader.isOpen())
Log.report(logvisor::Fatal, FMT_STRING("can't open '{}' for reading"), yamlPath.getRelativePath());
if (!athena::io::ValidateFromYAMLStream<ANCS>(reader)) {
Log.report(logvisor::Fatal, FMT_STRING("'{}' is not DNAMP1::ANCS type"), yamlPath.getRelativePath());
}
athena::io::YAMLDocReader yamlReader;
if (!yamlReader.parse(&reader)) {
Log.report(logvisor::Fatal, FMT_STRING("unable to parse '{}'"), yamlPath.getRelativePath());
}
ANCS ancs;
ancs.read(yamlReader);
/* Set Character Resource IDs */
for (ANCS::CharacterSet::CharacterInfo& ch : ancs.characterSet.characters) {
ch.cmdl = UniqueID32{};
ch.cskr = UniqueID32{};
ch.cinf = UniqueID32{};
ch.cmdlIce = UniqueID32Zero{};
ch.cskrIce = UniqueID32Zero{};
int subtypeIdx = 0;
ch.animAABBs.clear();
for (const DNAANCS::Actor::Subtype& sub : actor.subtypes) {
if (sub.name == ch.name) {
if (!sub.cskrId.empty()) {
ch.cskr = inPath.ensureAuxInfo(fmt::format(FMT_STRING("{}_{}.CSKR"), ch.name, sub.cskrId));
} else {
ch.cskr = inPath.ensureAuxInfo(fmt::format(FMT_STRING("{}.CSKR"), ch.name));
}
/* Add subtype AABBs */
ch.animAABBs.reserve(actor.actions.size());
for (const DNAANCS::Action& act : actor.actions) {
const auto& sourceAABB = act.subtypeAABBs[subtypeIdx];
ch.animAABBs.emplace_back();
auto& destAABB = ch.animAABBs.back();
destAABB.name = act.name;
destAABB.aabb[0] = sourceAABB.first.val;
destAABB.aabb[1] = sourceAABB.second.val;
}
if (sub.armature >= 0) {
const DNAANCS::Armature& arm = actor.armatures[sub.armature];
ch.cinf = arm.path;
ch.cmdl = sub.mesh;
auto search = std::find_if(sub.overlayMeshes.cbegin(), sub.overlayMeshes.cend(),
[](const auto& p) { return p.name == "ICE"; });
if (search != sub.overlayMeshes.cend()) {
ch.cmdlIce = search->mesh;
if (!search->cskrId.empty()) {
ch.cskrIce = inPath.ensureAuxInfo(
fmt::format(FMT_STRING("{}.{}_{}.CSKR"), ch.name, search->name, search->cskrId));
} else {
ch.cskrIce = inPath.ensureAuxInfo(fmt::format(FMT_STRING("{}.{}.CSKR"), ch.name, search->name));
}
}
}
break;
}
++subtypeIdx;
}
std::sort(ch.animAABBs.begin(), ch.animAABBs.end(),
[](const ANCS::CharacterSet::CharacterInfo::ActionAABB& a,
const ANCS::CharacterSet::CharacterInfo::ActionAABB& b) { return a.name < b.name; });
}
/* Set Animation Resource IDs */
ancs.enumeratePrimitives([&](AnimationSet::MetaAnimPrimitive& prim) {
for (const DNAANCS::Action& act : actor.actions) {
if (act.name == prim.animName) {
hecl::ProjectPath pathOut;
if (!act.animId.empty()) {
pathOut = inPath.ensureAuxInfo(fmt::format(FMT_STRING("{}_{}.ANIM"), prim.animName, act.animId));
} else {
inPath.ensureAuxInfo(fmt::format(FMT_STRING("{}.ANIM"), prim.animName));
}
prim.animId = pathOut;
break;
}
}
return true;
});
/* Gather ANIM resources */
hecl::DirectoryEnumerator dEnum(inPath.getParentPath().getAbsolutePath());
ancs.animationSet.animResources.reserve(actor.actions.size());
for (const DNAANCS::Action& act : actor.actions) {
hecl::ProjectPath pathOut;
if (!act.animId.empty()) {
pathOut = inPath.ensureAuxInfo(fmt::format(FMT_STRING("{}_{}.ANIM"), act.name, act.animId));
} else {
pathOut = inPath.ensureAuxInfo(fmt::format(FMT_STRING("{}.ANIM"), act.name));
}
ancs.animationSet.animResources.emplace_back();
ancs.animationSet.animResources.back().animId = pathOut;
/* Check for associated EVNT YAML */
std::string testPrefix(
inPath.getWithExtension(fmt::format(FMT_STRING(".{}_"), act.name).c_str(), true).getLastComponent());
hecl::ProjectPath evntYamlPath;
for (const auto& ent : dEnum) {
if (hecl::StringUtils::BeginsWith(ent.m_name, testPrefix.c_str()) &&
hecl::StringUtils::EndsWith(ent.m_name, ".evnt.yaml")) {
evntYamlPath = hecl::ProjectPath(inPath.getParentPath(), ent.m_name);
break;
}
}
if (evntYamlPath.isFile()) {
evntYamlPath = evntYamlPath.ensureAuxInfo("");
ancs.animationSet.animResources.back().evntId = evntYamlPath;
}
}
/* Write out ANCS */
athena::io::TransactionalFileWriter w(outPath.getAbsolutePath());
ancs.write(w);
return true;
}
static const std::regex regCskrNameId(R"((.*)_[0-9a-fA-F]{8}\.CSKR)",
std::regex::ECMAScript | std::regex::optimize);
static const std::regex regCskrName(R"((.*)\.CSKR)", std::regex::ECMAScript | std::regex::optimize);
bool ANCS::CookCSKR(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNAANCS::Actor& actor,
const std::function<bool(const hecl::ProjectPath& modelPath)>& modelCookFunc) {
auto auxInfo = inPath.getAuxInfo();
std::match_results<std::string_view::const_iterator> match;
if (!std::regex_search(auxInfo.begin(), auxInfo.end(), match, regCskrNameId) &&
!std::regex_search(auxInfo.begin(), auxInfo.end(), match, regCskrName))
return false;
std::string subName = match[1].str();
std::string overName;
auto dotPos = subName.rfind('.');
if (dotPos != std::string::npos) {
overName = std::string(subName.begin() + dotPos + 1, subName.end());
subName = std::string(subName.begin(), subName.begin() + dotPos);
}
/* Build bone ID map */
std::unordered_map<std::string, atInt32> boneIdMap;
for (const DNAANCS::Armature& arm : actor.armatures) {
CINF cinf(*arm.armature, boneIdMap);
}
const DNAANCS::Actor::Subtype* subtype = nullptr;
if (subName != "ATTACH") {
for (const DNAANCS::Actor::Subtype& sub : actor.subtypes) {
if (sub.name == subName) {
subtype = &sub;
break;
}
}
if (!subtype)
Log.report(logvisor::Fatal, FMT_STRING("unable to find subtype '{}'"), subName);
}
const hecl::ProjectPath* modelPath = nullptr;
if (subName == "ATTACH") {
const DNAANCS::Actor::Attachment* attachment = nullptr;
for (const DNAANCS::Actor::Attachment& att : actor.attachments) {
if (att.name == overName) {
attachment = &att;
break;
}
}
if (!attachment)
Log.report(logvisor::Fatal, FMT_STRING("unable to find attachment '{}'"), overName);
modelPath = &attachment->mesh;
} else if (overName.empty()) {
modelPath = &subtype->mesh;
} else {
for (const auto& overlay : subtype->overlayMeshes)
if (overlay.name == overName) {
modelPath = &overlay.mesh;
break;
}
}
if (!modelPath)
Log.report(logvisor::Fatal, FMT_STRING("unable to resolve model path of {}:{}"), subName, overName);
if (!modelPath->isFile())
Log.report(logvisor::Fatal, FMT_STRING("unable to resolve '{}'"), modelPath->getRelativePath());
hecl::ProjectPath skinIntPath = modelPath->getCookedPath(SpecEntMP1).getWithExtension(".skinint");
if (!skinIntPath.isFileOrGlob() || skinIntPath.getModtime() < modelPath->getModtime())
if (!modelCookFunc(*modelPath))
Log.report(logvisor::Fatal, FMT_STRING("unable to cook '{}'"), modelPath->getRelativePath());
std::vector<std::pair<std::vector<std::pair<uint32_t, float>>, uint32_t>> skins;
uint32_t posCount = 0;
uint32_t normCount = 0;
athena::io::FileReader skinIO(skinIntPath.getAbsolutePath(), 1024 * 32, false);
if (!skinIO.hasError()) {
std::vector<std::string> boneNames;
uint32_t boneNameCount = skinIO.readUint32Big();
boneNames.reserve(boneNameCount);
for (uint32_t i = 0; i < boneNameCount; ++i)
boneNames.push_back(skinIO.readString());
uint32_t skinCount = skinIO.readUint32Big();
skins.resize(skinCount);
for (uint32_t i = 0; i < skinCount; ++i) {
std::pair<std::vector<std::pair<uint32_t, float>>, uint32_t>& virtualBone = skins[i];
uint32_t bindCount = skinIO.readUint32Big();
virtualBone.first.reserve(bindCount);
for (uint32_t j = 0; j < bindCount; ++j) {
uint32_t bIdx = skinIO.readUint32Big();
float weight = skinIO.readFloatBig();
const std::string& name = boneNames[bIdx];
auto search = boneIdMap.find(name);
if (search == boneIdMap.cend())
Log.report(logvisor::Fatal, FMT_STRING("unable to find bone '{}' in {}"), name, inPath.getRelativePath());
virtualBone.first.emplace_back(search->second, weight);
}
virtualBone.second = skinIO.readUint32Big();
}
posCount = skinIO.readUint32Big();
normCount = skinIO.readUint32Big();
skinIO.close();
}
athena::io::TransactionalFileWriter skinOut(outPath.getAbsolutePath());
skinOut.writeUint32Big(skins.size());
for (auto& virtuaBone : skins) {
skinOut.writeUint32Big(virtuaBone.first.size());
for (auto& bind : virtuaBone.first) {
skinOut.writeUint32Big(bind.first);
skinOut.writeFloatBig(bind.second);
}
skinOut.writeUint32Big(virtuaBone.second);
}
skinOut.writeUint32Big(0xffffffff);
skinOut.writeUint32Big(posCount);
skinOut.writeUint32Big(0xffffffff);
skinOut.writeUint32Big(normCount);
return true;
}
bool ANCS::CookCSKRPC(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNAANCS::Actor& actor,
const std::function<bool(const hecl::ProjectPath& modelPath)>& modelCookFunc) {
auto auxInfo = inPath.getAuxInfo();
std::match_results<std::string_view::const_iterator> match;
if (!std::regex_search(auxInfo.begin(), auxInfo.end(), match, regCskrNameId) &&
!std::regex_search(auxInfo.begin(), auxInfo.end(), match, regCskrName))
return false;
std::string subName = match[1].str();
std::string overName;
auto dotPos = subName.rfind('.');
if (dotPos != std::string::npos) {
overName = std::string(subName.begin() + dotPos + 1, subName.end());
subName = std::string(subName.begin(), subName.begin() + dotPos);
}
/* Build bone ID map */
std::unordered_map<std::string, atInt32> boneIdMap;
for (const DNAANCS::Armature& arm : actor.armatures) {
CINF cinf(*arm.armature, boneIdMap);
}
const DNAANCS::Actor::Subtype* subtype = nullptr;
if (subName != "ATTACH") {
for (const DNAANCS::Actor::Subtype& sub : actor.subtypes) {
if (sub.name == subName) {
subtype = &sub;
break;
}
}
if (!subtype)
Log.report(logvisor::Fatal, FMT_STRING("unable to find subtype '{}'"), subName);
}
const hecl::ProjectPath* modelPath = nullptr;
if (subName == "ATTACH") {
const DNAANCS::Actor::Attachment* attachment = nullptr;
for (const DNAANCS::Actor::Attachment& att : actor.attachments) {
if (att.name == overName) {
attachment = &att;
break;
}
}
if (!attachment)
Log.report(logvisor::Fatal, FMT_STRING("unable to find attachment '{}'"), overName);
modelPath = &attachment->mesh;
} else if (overName.empty()) {
modelPath = &subtype->mesh;
} else {
for (const auto& overlay : subtype->overlayMeshes)
if (overlay.name == overName) {
modelPath = &overlay.mesh;
break;
}
}
if (!modelPath)
Log.report(logvisor::Fatal, FMT_STRING("unable to resolve model path of {}:{}"), subName, overName);
if (!modelPath->isFile())
Log.report(logvisor::Fatal, FMT_STRING("unable to resolve '{}'"), modelPath->getRelativePath());
hecl::ProjectPath skinIntPath = modelPath->getCookedPath(SpecEntMP1PC).getWithExtension(".skinint");
if (!skinIntPath.isFileOrGlob() || skinIntPath.getModtime() < modelPath->getModtime())
if (!modelCookFunc(*modelPath))
Log.report(logvisor::Fatal, FMT_STRING("unable to cook '{}'"), modelPath->getRelativePath());
uint32_t bankCount = 0;
std::vector<std::vector<uint32_t>> skinBanks;
std::vector<std::string> boneNames;
std::vector<std::vector<std::pair<uint32_t, float>>> skins;
atUint64 uniquePoolIndexLen = 0;
std::unique_ptr<atUint8[]> uniquePoolIndexData;
athena::io::FileReader skinIO(skinIntPath.getAbsolutePath(), 1024 * 32, false);
if (!skinIO.hasError()) {
bankCount = skinIO.readUint32Big();
skinBanks.reserve(bankCount);
for (uint32_t i = 0; i < bankCount; ++i) {
skinBanks.emplace_back();
std::vector<uint32_t>& bonesOut = skinBanks.back();
uint32_t boneCount = skinIO.readUint32Big();
bonesOut.reserve(boneCount);
for (uint32_t j = 0; j < boneCount; ++j) {
uint32_t idx = skinIO.readUint32Big();
bonesOut.push_back(idx);
}
}
uint32_t boneNameCount = skinIO.readUint32Big();
boneNames.reserve(boneNameCount);
for (uint32_t i = 0; i < boneNameCount; ++i)
boneNames.push_back(skinIO.readString());
uint32_t skinCount = skinIO.readUint32Big();
skins.resize(skinCount);
for (uint32_t i = 0; i < skinCount; ++i) {
std::vector<std::pair<uint32_t, float>>& virtualBone = skins[i];
uint32_t bindCount = skinIO.readUint32Big();
virtualBone.reserve(bindCount);
for (uint32_t j = 0; j < bindCount; ++j) {
uint32_t bIdx = skinIO.readUint32Big();
float weight = skinIO.readFloatBig();
const std::string& name = boneNames[bIdx];
auto search = boneIdMap.find(name);
if (search == boneIdMap.cend())
Log.report(logvisor::Fatal, FMT_STRING("unable to find bone '{}' in {}"), name, inPath.getRelativePath());
virtualBone.emplace_back(search->second, weight);
}
}
uniquePoolIndexLen = skinIO.length() - skinIO.position();
uniquePoolIndexData = skinIO.readUBytes(uniquePoolIndexLen);
skinIO.close();
}
athena::io::TransactionalFileWriter skinOut(outPath.getAbsolutePath());
skinOut.writeUint32Big(bankCount);
for (const std::vector<uint32_t>& bank : skinBanks) {
skinOut.writeUint32Big(bank.size());
for (uint32_t bIdx : bank) {
const std::string& name = boneNames[bIdx];
auto search = boneIdMap.find(name);
if (search == boneIdMap.cend())
Log.report(logvisor::Fatal, FMT_STRING("unable to find bone '{}' in {}"), name, inPath.getRelativePath());
skinOut.writeUint32Big(search->second);
}
}
skinOut.writeUint32Big(skins.size());
for (auto& virtuaBone : skins) {
skinOut.writeUint32Big(virtuaBone.size());
for (auto& bind : virtuaBone) {
skinOut.writeUint32Big(bind.first);
skinOut.writeFloatBig(bind.second);
}
}
if (uniquePoolIndexLen)
skinOut.writeUBytes(uniquePoolIndexData.get(), uniquePoolIndexLen);
return true;
}
static const std::regex regAnimNameId(R"((.*)_[0-9a-fA-F]{8}\.ANIM)",
std::regex::ECMAScript | std::regex::optimize);
static const std::regex regAnimName(R"((.*)\.ANIM)", std::regex::ECMAScript | std::regex::optimize);
bool ANCS::CookANIM(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNAANCS::Actor& actor,
hecl::blender::DataStream& ds, bool pc) {
auto auxInfo = inPath.getAuxInfo();
std::match_results<std::string_view::const_iterator> match;
if (!std::regex_search(auxInfo.begin(), auxInfo.end(), match, regAnimNameId) &&
!std::regex_search(auxInfo.begin(), auxInfo.end(), match, regAnimName))
return false;
std::string actName = match[1].str();
DNAANCS::Action action = ds.compileActionChannelsOnly(actName);
if (!actor.armatures.size())
Log.report(logvisor::Fatal, FMT_STRING("0 armatures in {}"), inPath.getRelativePath());
/* Build bone ID map */
std::unordered_map<std::string, atInt32> boneIdMap;
std::optional<CINF> rigCinf;
std::optional<DNAANIM::RigInverter<CINF>> rigInv;
for (const DNAANCS::Armature& arm : actor.armatures) {
if (!rigInv) {
rigCinf.emplace(*arm.armature, boneIdMap);
auto matrices = ds.getBoneMatrices(arm.name);
rigInv.emplace(*rigCinf, matrices);
} else {
CINF cinf(*arm.armature, boneIdMap);
}
}
ANIM anim(action, boneIdMap, *rigInv, pc);
/* Check for associated EVNT YAML */
std::string testPrefix(
inPath.getWithExtension(fmt::format(FMT_STRING(".{}_"), actName).c_str(), true).getLastComponent());
hecl::ProjectPath evntYamlPath;
for (const auto& ent : hecl::DirectoryEnumerator(inPath.getParentPath().getAbsolutePath())) {
if (hecl::StringUtils::BeginsWith(ent.m_name, testPrefix.c_str()) &&
hecl::StringUtils::EndsWith(ent.m_name, ".evnt.yaml")) {
evntYamlPath = hecl::ProjectPath(inPath.getParentPath(), ent.m_name);
break;
}
}
if (evntYamlPath.isFile()) {
evntYamlPath = evntYamlPath.ensureAuxInfo("");
anim.m_anim->evnt = evntYamlPath;
}
/* Write out ANIM resource */
athena::io::TransactionalFileWriter w(outPath.getAbsolutePath());
anim.write(w);
return true;
}
} // namespace DNAMP1
} // namespace DataSpec