mirror of https://github.com/AxioDL/metaforce.git
Migration to new ANIM extract
This commit is contained in:
parent
cd8b6b6ed4
commit
9ee8840b54
|
@ -2,10 +2,10 @@ include_directories(${LIBPNG_INCLUDE_DIR} ${SQUISH_INCLUDE_DIR})
|
|||
|
||||
# Assembles a source/header pair list for use in a DNA library
|
||||
macro(make_dnalist outlist)
|
||||
foreach(type ${ARGN})
|
||||
foreach(type ${ARGN})
|
||||
get_filename_component(dir ${type} DIRECTORY)
|
||||
if(dir)
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${dir}")
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${dir}")
|
||||
set(dir "${dir}/")
|
||||
endif()
|
||||
get_filename_component(name ${type} NAME)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "DNACommon.hpp"
|
||||
#include "BlenderConnection.hpp"
|
||||
#include "CMDL.hpp"
|
||||
#include "RigInverter.hpp"
|
||||
|
||||
namespace DataSpec
|
||||
{
|
||||
|
@ -59,7 +60,7 @@ bool ReadANCSToBlender(hecl::BlenderConnection& conn,
|
|||
|
||||
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
|
||||
hecl::SystemStringView bestNameView(bestName);
|
||||
fileChanged(bestNameView.sys_str().c_str());
|
||||
fileChanged(bestNameView.c_str());
|
||||
|
||||
typename ANCSDNA::CSKRType cskr;
|
||||
pakRouter.lookupAndReadDNA(info.cskr, cskr);
|
||||
|
@ -79,7 +80,7 @@ bool ReadANCSToBlender(hecl::BlenderConnection& conn,
|
|||
|
||||
std::string bestName = pakRouter.getBestEntryName(entry);
|
||||
hecl::SystemStringView bestNameView(bestName);
|
||||
fileChanged(bestNameView.sys_str().c_str());
|
||||
fileChanged(bestNameView.c_str());
|
||||
|
||||
/* Establish ANCS blend */
|
||||
if (!conn.createBlend(outPath, hecl::BlenderConnection::BlendType::Actor))
|
||||
|
@ -163,6 +164,8 @@ bool ReadANCSToBlender(hecl::BlenderConnection& conn,
|
|||
}
|
||||
}
|
||||
|
||||
DNAANIM::RigInverter<typename ANCSDNA::CINFType> inverter(cinf);
|
||||
|
||||
/* Get animation primitives */
|
||||
std::map<atUint32, AnimationResInfo<typename PAKRouter::IDType>> animResInfo;
|
||||
ancs.getAnimationResInfo(animResInfo);
|
||||
|
@ -173,7 +176,7 @@ bool ReadANCSToBlender(hecl::BlenderConnection& conn,
|
|||
{
|
||||
os.format("act = bpy.data.actions.new('%s')\n"
|
||||
"act.use_fake_user = True\n", id.second.name.c_str());
|
||||
anim.sendANIMToBlender(os, cinf, id.second.additive);
|
||||
anim.sendANIMToBlender(os, inverter, id.second.additive);
|
||||
}
|
||||
|
||||
os.format("actor_action = actor_data.actions.add()\n"
|
||||
|
|
|
@ -26,6 +26,7 @@ add_library(DNACommon
|
|||
FONT.hpp FONT.cpp
|
||||
DeafBabe.hpp
|
||||
BabeDead.hpp
|
||||
RigInverter.hpp RigInverter.cpp
|
||||
Tweaks/ITweakGame.hpp
|
||||
Tweaks/ITweakParticle.hpp
|
||||
Tweaks/ITweakPlayer.hpp
|
||||
|
|
|
@ -455,7 +455,7 @@ bool PAKRouter<BRIDGETYPE>::extractResources(const BRIDGETYPE& pakBridge, bool f
|
|||
std::string bestName = getBestEntryName(*item);
|
||||
hecl::SystemStringView bestNameView(bestName);
|
||||
float thisFac = ++count / fsz;
|
||||
progress(bestNameView.sys_str().c_str(), thisFac);
|
||||
progress(bestNameView.c_str(), thisFac);
|
||||
|
||||
const nod::Node* node = m_node.get();
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
#include "RigInverter.hpp"
|
||||
#include "DataSpec/DNAMP1/CINF.hpp"
|
||||
#include "DataSpec/DNAMP2/CINF.hpp"
|
||||
#include "DataSpec/DNAMP3/CINF.hpp"
|
||||
|
||||
namespace DataSpec
|
||||
{
|
||||
namespace DNAANIM
|
||||
{
|
||||
|
||||
template <class CINFType>
|
||||
RigInverter<CINFType>::Bone::Bone(const CINFType& cinf, const typename CINFType::Bone& origBone)
|
||||
: m_origBone(origBone)
|
||||
{
|
||||
atUint32 parentIdx = cinf.getInternalBoneIdxFromId(origBone.parentId);
|
||||
zeus::CVector3f naturalTail = zeus::CVector3f(origBone.origin) + zeus::CVector3f{0.f, 0.5f, 0.f};
|
||||
if (parentIdx != -1)
|
||||
{
|
||||
const typename CINFType::Bone& pBone = cinf.bones[parentIdx];
|
||||
m_parentDelta = zeus::CVector3f(origBone.origin) - zeus::CVector3f(pBone.origin);
|
||||
}
|
||||
|
||||
size_t actualChildren = 0;
|
||||
for (atUint32 chId : origBone.linked)
|
||||
{
|
||||
if (chId == origBone.parentId)
|
||||
continue;
|
||||
atUint32 chIdx = cinf.getInternalBoneIdxFromId(chId);
|
||||
if (chIdx != -1)
|
||||
++actualChildren;
|
||||
}
|
||||
|
||||
if (parentIdx == -1)
|
||||
{
|
||||
/* Root will always use +Y tail */
|
||||
m_tail = naturalTail;
|
||||
}
|
||||
else if (actualChildren)
|
||||
{
|
||||
/* Position tail to average of children */
|
||||
for (atUint32 chId : origBone.linked)
|
||||
{
|
||||
if (chId == origBone.parentId)
|
||||
continue;
|
||||
atUint32 chIdx = cinf.getInternalBoneIdxFromId(chId);
|
||||
if (chIdx != -1)
|
||||
{
|
||||
const typename CINFType::Bone& chBone = cinf.bones[chIdx];
|
||||
m_tail += chBone.origin;
|
||||
}
|
||||
}
|
||||
m_tail /= float(actualChildren);
|
||||
if (m_tail.magSquared() < 0.001f)
|
||||
m_tail = naturalTail;
|
||||
else
|
||||
m_inverter = zeus::CQuaternion(m_tail, naturalTail);
|
||||
}
|
||||
else if (parentIdx != -1)
|
||||
{
|
||||
/* Extrapolate by delta with parent */
|
||||
const typename CINFType::Bone& pBone = cinf.bones[parentIdx];
|
||||
m_tail = zeus::CVector3f(origBone.origin) * 2.f - zeus::CVector3f(pBone.origin);
|
||||
if (m_tail.magSquared() < 0.001f)
|
||||
m_tail = naturalTail;
|
||||
else
|
||||
m_inverter = zeus::CQuaternion(m_tail, naturalTail);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fallback to +Y tail */
|
||||
m_tail = naturalTail;
|
||||
}
|
||||
}
|
||||
|
||||
template <class CINFType>
|
||||
RigInverter<CINFType>::RigInverter(const CINFType& cinf)
|
||||
: m_cinf(cinf)
|
||||
{
|
||||
m_bones.reserve(cinf.bones.size());
|
||||
for (const typename CINFType::Bone& b : cinf.bones)
|
||||
m_bones.emplace_back(cinf, b);
|
||||
}
|
||||
|
||||
template <class CINFType>
|
||||
zeus::CQuaternion
|
||||
RigInverter<CINFType>::transformRotation(atUint32 boneId, const zeus::CQuaternion& origRot) const
|
||||
{
|
||||
for (const Bone& b : m_bones)
|
||||
if (b.m_origBone.id == boneId)
|
||||
return b.m_inverter * origRot;
|
||||
return origRot;
|
||||
}
|
||||
|
||||
template <class CINFType>
|
||||
zeus::CVector3f
|
||||
RigInverter<CINFType>::transformPosition(atUint32 boneId, const zeus::CVector3f& origPos, bool subDelta) const
|
||||
{
|
||||
for (const Bone& b : m_bones)
|
||||
if (b.m_origBone.id == boneId)
|
||||
{
|
||||
zeus::CVector3f localPos = origPos;
|
||||
if (subDelta)
|
||||
localPos -= b.m_parentDelta;
|
||||
return zeus::CQuaternion::rotate(b.m_inverter, localPos);
|
||||
}
|
||||
return origPos;
|
||||
}
|
||||
|
||||
template class RigInverter<DNAMP1::CINF>;
|
||||
template class RigInverter<DNAMP2::CINF>;
|
||||
template class RigInverter<DNAMP3::CINF>;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef __COMMON_RIGINVERTER_HPP__
|
||||
#define __COMMON_RIGINVERTER_HPP__
|
||||
|
||||
#include "zeus/CVector3f.hpp"
|
||||
#include "zeus/CQuaternion.hpp"
|
||||
|
||||
namespace DataSpec
|
||||
{
|
||||
namespace DNAANIM
|
||||
{
|
||||
|
||||
/** One-shot process to invert CINF armature into connected rig,
|
||||
* inverting rotations/translations of ANIM data to match */
|
||||
template <class CINFType>
|
||||
class RigInverter
|
||||
{
|
||||
public:
|
||||
struct Bone
|
||||
{
|
||||
const typename CINFType::Bone& m_origBone;
|
||||
zeus::CQuaternion m_inverter;
|
||||
zeus::CVector3f m_tail;
|
||||
zeus::CVector3f m_parentDelta;
|
||||
Bone(const CINFType& cinf, const typename CINFType::Bone& origBone);
|
||||
};
|
||||
private:
|
||||
const CINFType& m_cinf;
|
||||
std::vector<Bone> m_bones;
|
||||
public:
|
||||
RigInverter(const CINFType& cinf);
|
||||
const CINFType& getCINF() const {return m_cinf;}
|
||||
const std::vector<Bone>& getBones() const {return m_bones;}
|
||||
zeus::CQuaternion transformRotation(atUint32 boneId, const zeus::CQuaternion& origRot) const;
|
||||
zeus::CVector3f transformPosition(atUint32 boneId, const zeus::CVector3f& origPos, bool subDelta) const;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __COMMON_RIGINVERTER_HPP__
|
|
@ -528,8 +528,6 @@ void ANCS::CharacterSet::CharacterInfo::read(athena::io::YAMLDocReader& reader)
|
|||
atUint16 sectionCount = reader.readUint16("sectionCount");
|
||||
name = reader.readString("name");
|
||||
reader.enumerate("cmdl", cmdl);
|
||||
reader.enumerate("cskr", cskr);
|
||||
reader.enumerate("cinf", cinf);
|
||||
|
||||
reader.enumerate("animations", animations);
|
||||
|
||||
|
@ -564,7 +562,6 @@ void ANCS::CharacterSet::CharacterInfo::read(athena::io::YAMLDocReader& reader)
|
|||
if (sectionCount > 3)
|
||||
{
|
||||
reader.enumerate("cmdlOverride", cmdlOverlay);
|
||||
reader.enumerate("cskrOverride", cskrOverlay);
|
||||
}
|
||||
|
||||
animIdxs.clear();
|
||||
|
@ -595,8 +592,6 @@ void ANCS::CharacterSet::CharacterInfo::write(athena::io::YAMLDocWriter& writer)
|
|||
|
||||
writer.writeString("name", name);
|
||||
writer.enumerate("cmdl", cmdl);
|
||||
writer.enumerate("cskr", cskr);
|
||||
writer.enumerate("cinf", cinf);
|
||||
|
||||
writer.enumerate("animations", animations);
|
||||
|
||||
|
@ -628,7 +623,6 @@ void ANCS::CharacterSet::CharacterInfo::write(athena::io::YAMLDocWriter& writer)
|
|||
if (sectionCount > 3)
|
||||
{
|
||||
writer.enumerate("cmdlOverride", cmdlOverlay);
|
||||
writer.enumerate("cskrOverride", cskrOverlay);
|
||||
}
|
||||
|
||||
if (sectionCount > 4)
|
||||
|
@ -648,23 +642,23 @@ void ANCS::AnimationSet::MetaAnimFactory::read(athena::io::IStreamReader& reader
|
|||
switch (type)
|
||||
{
|
||||
case IMetaAnim::Type::Primitive:
|
||||
m_anim.reset(new struct MetaAnimPrimitive(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimPrimitive);
|
||||
m_anim->read(reader);
|
||||
break;
|
||||
case IMetaAnim::Type::Blend:
|
||||
m_anim.reset(new struct MetaAnimBlend(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimBlend);
|
||||
m_anim->read(reader);
|
||||
break;
|
||||
case IMetaAnim::Type::PhaseBlend:
|
||||
m_anim.reset(new struct MetaAnimPhaseBlend(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimPhaseBlend);
|
||||
m_anim->read(reader);
|
||||
break;
|
||||
case IMetaAnim::Type::Random:
|
||||
m_anim.reset(new struct MetaAnimRandom(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimRandom);
|
||||
m_anim->read(reader);
|
||||
break;
|
||||
case IMetaAnim::Type::Sequence:
|
||||
m_anim.reset(new struct MetaAnimSequence(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimSequence);
|
||||
m_anim->read(reader);
|
||||
break;
|
||||
default:
|
||||
|
@ -694,27 +688,27 @@ void ANCS::AnimationSet::MetaAnimFactory::read(athena::io::YAMLDocReader& reader
|
|||
std::transform(type.begin(), type.end(), type.begin(), tolower);
|
||||
if (!type.compare("primitive"))
|
||||
{
|
||||
m_anim.reset(new struct MetaAnimPrimitive(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimPrimitive);
|
||||
m_anim->read(reader);
|
||||
}
|
||||
else if (!type.compare("blend"))
|
||||
{
|
||||
m_anim.reset(new struct MetaAnimBlend(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimBlend);
|
||||
m_anim->read(reader);
|
||||
}
|
||||
else if (!type.compare("phaseblend"))
|
||||
{
|
||||
m_anim.reset(new struct MetaAnimPhaseBlend(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimPhaseBlend);
|
||||
m_anim->read(reader);
|
||||
}
|
||||
else if (!type.compare("random"))
|
||||
{
|
||||
m_anim.reset(new struct MetaAnimRandom(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimRandom);
|
||||
m_anim->read(reader);
|
||||
}
|
||||
else if (!type.compare("sequence"))
|
||||
{
|
||||
m_anim.reset(new struct MetaAnimSequence(m_ancsId));
|
||||
m_anim.reset(new struct MetaAnimSequence);
|
||||
m_anim->read(reader);
|
||||
}
|
||||
else
|
||||
|
@ -743,15 +737,15 @@ void ANCS::AnimationSet::MetaTransFactory::read(athena::io::IStreamReader& reade
|
|||
switch (type)
|
||||
{
|
||||
case IMetaTrans::Type::MetaAnim:
|
||||
m_trans.reset(new struct MetaTransMetaAnim(m_ancsId));
|
||||
m_trans.reset(new struct MetaTransMetaAnim);
|
||||
m_trans->read(reader);
|
||||
break;
|
||||
case IMetaTrans::Type::Trans:
|
||||
m_trans.reset(new struct MetaTransTrans(m_ancsId));
|
||||
m_trans.reset(new struct MetaTransTrans);
|
||||
m_trans->read(reader);
|
||||
break;
|
||||
case IMetaTrans::Type::PhaseTrans:
|
||||
m_trans.reset(new struct MetaTransPhaseTrans(m_ancsId));
|
||||
m_trans.reset(new struct MetaTransPhaseTrans);
|
||||
m_trans->read(reader);
|
||||
break;
|
||||
case IMetaTrans::Type::NoTrans:
|
||||
|
@ -785,17 +779,17 @@ void ANCS::AnimationSet::MetaTransFactory::read(athena::io::YAMLDocReader& reade
|
|||
std::transform(type.begin(), type.end(), type.begin(), tolower);
|
||||
if (!type.compare("metaanim"))
|
||||
{
|
||||
m_trans.reset(new struct MetaTransMetaAnim(m_ancsId));
|
||||
m_trans.reset(new struct MetaTransMetaAnim);
|
||||
m_trans->read(reader);
|
||||
}
|
||||
else if (!type.compare("trans"))
|
||||
{
|
||||
m_trans.reset(new struct MetaTransTrans(m_ancsId));
|
||||
m_trans.reset(new struct MetaTransTrans);
|
||||
m_trans->read(reader);
|
||||
}
|
||||
else if (!type.compare("phasetrans"))
|
||||
{
|
||||
m_trans.reset(new struct MetaTransPhaseTrans(m_ancsId));
|
||||
m_trans.reset(new struct MetaTransPhaseTrans);
|
||||
m_trans->read(reader);
|
||||
}
|
||||
else
|
||||
|
@ -826,23 +820,10 @@ void ANCS::AnimationSet::read(athena::io::IStreamReader& reader)
|
|||
atUint16 sectionCount = reader.readUint16Big();
|
||||
|
||||
atUint32 animationCount = reader.readUint32Big();
|
||||
animations.clear();
|
||||
animations.reserve(animationCount);
|
||||
for (size_t i=0 ; i<animationCount ; ++i)
|
||||
{
|
||||
animations.emplace_back(m_ancsId);
|
||||
animations.back().read(reader);
|
||||
}
|
||||
reader.enumerate(animations, animationCount);
|
||||
|
||||
atUint32 transitionCount = reader.readUint32Big();
|
||||
transitions.clear();
|
||||
transitions.reserve(transitionCount);
|
||||
for (size_t i=0 ; i<transitionCount ; ++i)
|
||||
{
|
||||
transitions.emplace_back(m_ancsId);
|
||||
transitions.back().read(reader);
|
||||
}
|
||||
|
||||
reader.enumerate(transitions, transitionCount);
|
||||
defaultTransition.read(reader);
|
||||
|
||||
additiveAnims.clear();
|
||||
|
@ -857,13 +838,8 @@ void ANCS::AnimationSet::read(athena::io::IStreamReader& reader)
|
|||
halfTransitions.clear();
|
||||
if (sectionCount > 2)
|
||||
{
|
||||
atUint32 halfTransitionCount = reader.readUint32Big();
|
||||
halfTransitions.reserve(halfTransitionCount);
|
||||
for (size_t i=0 ; i<halfTransitionCount ; ++i)
|
||||
{
|
||||
halfTransitions.emplace_back(m_ancsId);
|
||||
halfTransitions.back().read(reader);
|
||||
}
|
||||
atUint32 halfTransitionCount = reader.readUint32Big();
|
||||
reader.enumerate(halfTransitions, halfTransitionCount);
|
||||
}
|
||||
|
||||
animResources.clear();
|
||||
|
@ -961,36 +937,9 @@ void ANCS::AnimationSet::read(athena::io::YAMLDocReader& reader)
|
|||
{
|
||||
atUint16 sectionCount = reader.readUint16("sectionCount");
|
||||
|
||||
size_t animationCount;
|
||||
animations.clear();
|
||||
if (reader.enterSubVector("animations", animationCount))
|
||||
{
|
||||
animations.reserve(animationCount);
|
||||
for (size_t i=0 ; i<animationCount ; ++i)
|
||||
{
|
||||
animations.emplace_back(m_ancsId);
|
||||
reader.enterSubRecord(nullptr);
|
||||
animations.back().read(reader);
|
||||
reader.leaveSubRecord();
|
||||
}
|
||||
reader.leaveSubVector();
|
||||
}
|
||||
|
||||
size_t transitionCount;
|
||||
transitions.clear();
|
||||
if (reader.enterSubVector("transitions", transitionCount))
|
||||
{
|
||||
transitions.reserve(transitionCount);
|
||||
for (size_t i=0 ; i<transitionCount ; ++i)
|
||||
{
|
||||
transitions.emplace_back(m_ancsId);
|
||||
reader.enterSubRecord(nullptr);
|
||||
transitions.back().read(reader);
|
||||
reader.leaveSubRecord();
|
||||
}
|
||||
reader.leaveSubVector();
|
||||
}
|
||||
reader.enumerate("animations", animations);
|
||||
|
||||
reader.enumerate("transitions", transitions);
|
||||
reader.enumerate("defaultTransition", defaultTransition);
|
||||
|
||||
additiveAnims.clear();
|
||||
|
@ -1004,28 +953,14 @@ void ANCS::AnimationSet::read(athena::io::YAMLDocReader& reader)
|
|||
halfTransitions.clear();
|
||||
if (sectionCount > 2)
|
||||
{
|
||||
size_t halfTransitionCount;
|
||||
if (reader.enterSubVector("halfTransitions", halfTransitionCount))
|
||||
{
|
||||
halfTransitions.reserve(halfTransitionCount);
|
||||
for (size_t i=0 ; i<halfTransitionCount ; ++i)
|
||||
{
|
||||
halfTransitions.emplace_back(m_ancsId);
|
||||
reader.enterSubRecord(nullptr);
|
||||
halfTransitions.back().read(reader);
|
||||
reader.leaveSubRecord();
|
||||
}
|
||||
reader.leaveSubVector();
|
||||
}
|
||||
reader.enumerate("halfTransitions", halfTransitions);
|
||||
}
|
||||
|
||||
#if 0
|
||||
animResources.clear();
|
||||
if (sectionCount > 3)
|
||||
{
|
||||
reader.enumerate("animResources", animResources);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ANCS::AnimationSet::write(athena::io::YAMLDocWriter& writer) const
|
||||
|
@ -1059,12 +994,10 @@ void ANCS::AnimationSet::write(athena::io::YAMLDocWriter& writer) const
|
|||
writer.enumerate("halfTransitions", halfTransitions);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (sectionCount > 3)
|
||||
{
|
||||
writer.enumerate("animResources", animResources);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* ANCS::AnimationSet::DNAType()
|
||||
|
|
|
@ -22,8 +22,6 @@ struct ANCS : BigYAML
|
|||
using CSKRType = CSKR;
|
||||
using ANIMType = ANIM;
|
||||
|
||||
ANCS(const UniqueID32& ancsId) : animationSet(ancsId) {}
|
||||
|
||||
DECL_YAML
|
||||
Value<atUint16> version;
|
||||
|
||||
|
@ -33,17 +31,15 @@ struct ANCS : BigYAML
|
|||
Value<atUint16> version;
|
||||
Value<atUint32> characterCount;
|
||||
struct CharacterInfo : BigYAML
|
||||
{
|
||||
{
|
||||
DECL_YAML
|
||||
Delete expl;
|
||||
|
||||
atUint32 idx;
|
||||
std::string name;
|
||||
UniqueID32 cmdl;
|
||||
UniqueID32 _cskrOld;
|
||||
UniqueID32 _cinfOld;
|
||||
AuxiliaryID32 cskr = _S("skin");
|
||||
AuxiliaryID32 cinf = {_S("layout"), _S(".blend")};
|
||||
UniqueID32 cskr;
|
||||
UniqueID32 cinf;
|
||||
|
||||
struct Animation : BigYAML
|
||||
{
|
||||
|
@ -147,8 +143,7 @@ struct ANCS : BigYAML
|
|||
std::vector<Effect> effects;
|
||||
|
||||
UniqueID32 cmdlOverlay;
|
||||
UniqueID32 _cskrOverlayOld;
|
||||
AuxiliaryID32 cskrOverlay = _S("skin");
|
||||
UniqueID32 cskrOverlay;
|
||||
|
||||
std::vector<atUint32> animIdxs;
|
||||
};
|
||||
|
@ -159,10 +154,8 @@ struct ANCS : BigYAML
|
|||
{
|
||||
DECL_YAML
|
||||
Delete expl;
|
||||
const UniqueID32& m_ancsId;
|
||||
AnimationSet(const UniqueID32& ancsId)
|
||||
: m_ancsId(ancsId), defaultTransition(ancsId) {}
|
||||
|
||||
struct MetaAnimPrimitive;
|
||||
struct IMetaAnim : BigYAML
|
||||
{
|
||||
Delete expl;
|
||||
|
@ -176,24 +169,23 @@ struct ANCS : BigYAML
|
|||
Sequence = 4
|
||||
} m_type;
|
||||
const char* m_typeStr;
|
||||
const UniqueID32& m_ancsId;
|
||||
IMetaAnim(Type type, const char* typeStr, const UniqueID32& ancsId)
|
||||
: m_type(type), m_typeStr(typeStr), m_ancsId(ancsId) {}
|
||||
IMetaAnim(Type type, const char* typeStr)
|
||||
: m_type(type), m_typeStr(typeStr) {}
|
||||
virtual void gatherPrimitives(std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out)=0;
|
||||
virtual bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func)=0;
|
||||
};
|
||||
struct MetaAnimFactory : BigYAML
|
||||
{
|
||||
DECL_YAML
|
||||
Delete expl;
|
||||
const UniqueID32& m_ancsId;
|
||||
std::unique_ptr<IMetaAnim> m_anim;
|
||||
MetaAnimFactory(const UniqueID32& ancsId) : m_ancsId(ancsId) {}
|
||||
};
|
||||
struct MetaAnimPrimitive : IMetaAnim
|
||||
{
|
||||
MetaAnimPrimitive(const UniqueID32& ancsId) : IMetaAnim(Type::Primitive, "Primitive", ancsId) {}
|
||||
Delete expl;
|
||||
|
||||
MetaAnimPrimitive() : IMetaAnim(Type::Primitive, "Primitive") {}
|
||||
|
||||
Delete _d;
|
||||
UniqueID32 animId;
|
||||
Value<atUint32> animIdx;
|
||||
String<-1> animName;
|
||||
|
@ -238,13 +230,6 @@ struct ANCS : BigYAML
|
|||
unk1 = __dna_docin.readFloat("unk1");
|
||||
/* unk2 */
|
||||
unk2 = __dna_docin.readUint32("unk2");
|
||||
|
||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(m_ancsId);
|
||||
if (path)
|
||||
{
|
||||
hecl::SystemStringView sysView(animName);
|
||||
animId = path.ensureAuxInfo(sysView.sys_str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void write(athena::io::YAMLDocWriter& __dna_docout) const
|
||||
|
@ -275,11 +260,16 @@ struct ANCS : BigYAML
|
|||
{
|
||||
out[animIdx] = {animName, animId, UniqueID32(), false};
|
||||
}
|
||||
|
||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func)
|
||||
{
|
||||
return func(*this);
|
||||
}
|
||||
};
|
||||
struct MetaAnimBlend : IMetaAnim
|
||||
{
|
||||
MetaAnimBlend(const UniqueID32& ancsId)
|
||||
: IMetaAnim(Type::Blend, "Blend", ancsId), animA(ancsId), animB(ancsId) {}
|
||||
MetaAnimBlend()
|
||||
: IMetaAnim(Type::Blend, "Blend") {}
|
||||
DECL_YAML
|
||||
MetaAnimFactory animA;
|
||||
MetaAnimFactory animB;
|
||||
|
@ -291,11 +281,20 @@ struct ANCS : BigYAML
|
|||
animA.m_anim->gatherPrimitives(out);
|
||||
animB.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
|
||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func)
|
||||
{
|
||||
if (!animA.m_anim->enumeratePrimitives(func))
|
||||
return false;
|
||||
if (!animB.m_anim->enumeratePrimitives(func))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
struct MetaAnimPhaseBlend : IMetaAnim
|
||||
{
|
||||
MetaAnimPhaseBlend(const UniqueID32& ancsId)
|
||||
: IMetaAnim(Type::PhaseBlend, "PhaseBlend", ancsId), animA(ancsId), animB(ancsId) {}
|
||||
MetaAnimPhaseBlend()
|
||||
: IMetaAnim(Type::PhaseBlend, "PhaseBlend") {}
|
||||
DECL_YAML
|
||||
MetaAnimFactory animA;
|
||||
MetaAnimFactory animB;
|
||||
|
@ -307,157 +306,63 @@ struct ANCS : BigYAML
|
|||
animA.m_anim->gatherPrimitives(out);
|
||||
animB.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
|
||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func)
|
||||
{
|
||||
if (!animA.m_anim->enumeratePrimitives(func))
|
||||
return false;
|
||||
if (!animB.m_anim->enumeratePrimitives(func))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
struct MetaAnimRandom : IMetaAnim
|
||||
{
|
||||
MetaAnimRandom(const UniqueID32& ancsId) : IMetaAnim(Type::Random, "Random", ancsId) {}
|
||||
Delete _d;
|
||||
MetaAnimRandom() : IMetaAnim(Type::Random, "Random") {}
|
||||
DECL_YAML
|
||||
Value<atUint32> animCount;
|
||||
struct Child : BigYAML
|
||||
{
|
||||
DECL_YAML
|
||||
MetaAnimFactory anim;
|
||||
Value<atUint32> probability;
|
||||
Child(const UniqueID32& ancsId) : anim(ancsId) {}
|
||||
};
|
||||
Vector<Child, DNA_COUNT(animCount)> children;
|
||||
|
||||
void read(athena::io::IStreamReader& __dna_reader)
|
||||
{
|
||||
/* animCount */
|
||||
animCount = __dna_reader.readUint32Big();
|
||||
/* children */
|
||||
children.clear();
|
||||
children.reserve(animCount);
|
||||
for (size_t i=0 ; i<animCount ; ++i)
|
||||
{
|
||||
children.emplace_back(m_ancsId);
|
||||
children.back().read(__dna_reader);
|
||||
}
|
||||
}
|
||||
|
||||
void write(athena::io::IStreamWriter& __dna_writer) const
|
||||
{
|
||||
/* animCount */
|
||||
__dna_writer.writeUint32Big(animCount);
|
||||
/* children */
|
||||
__dna_writer.enumerate(children);
|
||||
}
|
||||
|
||||
void read(athena::io::YAMLDocReader& __dna_docin)
|
||||
{
|
||||
/* animCount squelched */
|
||||
/* children */
|
||||
size_t childCount;
|
||||
__dna_docin.enterSubVector("children", childCount);
|
||||
animCount = childCount;
|
||||
children.clear();
|
||||
children.reserve(childCount);
|
||||
for (size_t i=0 ; i<childCount ; ++i)
|
||||
{
|
||||
children.emplace_back(m_ancsId);
|
||||
__dna_docin.enterSubRecord(nullptr);
|
||||
children.back().read(__dna_docin);
|
||||
__dna_docin.leaveSubRecord();
|
||||
}
|
||||
__dna_docin.leaveSubVector();
|
||||
}
|
||||
|
||||
void write(athena::io::YAMLDocWriter& __dna_docout) const
|
||||
{
|
||||
/* animCount squelched */
|
||||
/* children */
|
||||
__dna_docout.enumerate("children", children);
|
||||
}
|
||||
|
||||
static const char* DNAType()
|
||||
{
|
||||
return "DataSpec::DNAMP1::ANCS::AnimationSet::MetaAnimRandom";
|
||||
}
|
||||
|
||||
size_t binarySize(size_t __isz) const
|
||||
{
|
||||
__isz = __EnumerateSize(__isz, children);
|
||||
return __isz + 4;
|
||||
}
|
||||
|
||||
void gatherPrimitives(std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out)
|
||||
{
|
||||
for (const auto& child : children)
|
||||
child.anim.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
|
||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func)
|
||||
{
|
||||
for (auto& child : children)
|
||||
if (!child.anim.m_anim->enumeratePrimitives(func))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
struct MetaAnimSequence : IMetaAnim
|
||||
{
|
||||
MetaAnimSequence(const UniqueID32& ancsId) : IMetaAnim(Type::Sequence, "Sequence", ancsId) {}
|
||||
Delete _d;
|
||||
MetaAnimSequence() : IMetaAnim(Type::Sequence, "Sequence") {}
|
||||
DECL_YAML
|
||||
Value<atUint32> animCount;
|
||||
Vector<MetaAnimFactory, DNA_COUNT(animCount)> children;
|
||||
|
||||
void read(athena::io::IStreamReader& __dna_reader)
|
||||
{
|
||||
/* animCount */
|
||||
animCount = __dna_reader.readUint32Big();
|
||||
/* children */
|
||||
children.clear();
|
||||
children.reserve(animCount);
|
||||
for (size_t i=0 ; i<animCount ; ++i)
|
||||
{
|
||||
children.emplace_back(m_ancsId);
|
||||
children.back().read(__dna_reader);
|
||||
}
|
||||
}
|
||||
|
||||
void write(athena::io::IStreamWriter& __dna_writer) const
|
||||
{
|
||||
/* animCount */
|
||||
__dna_writer.writeUint32Big(animCount);
|
||||
/* children */
|
||||
__dna_writer.enumerate(children);
|
||||
}
|
||||
|
||||
void read(athena::io::YAMLDocReader& __dna_docin)
|
||||
{
|
||||
/* animCount squelched */
|
||||
/* children */
|
||||
size_t childCount;
|
||||
__dna_docin.enterSubVector("children", childCount);
|
||||
animCount = childCount;
|
||||
children.clear();
|
||||
children.reserve(childCount);
|
||||
for (size_t i=0 ; i<childCount ; ++i)
|
||||
{
|
||||
children.emplace_back(m_ancsId);
|
||||
__dna_docin.enterSubRecord(nullptr);
|
||||
children.back().read(__dna_docin);
|
||||
__dna_docin.leaveSubRecord();
|
||||
}
|
||||
__dna_docin.leaveSubVector();
|
||||
}
|
||||
|
||||
void write(athena::io::YAMLDocWriter& __dna_docout) const
|
||||
{
|
||||
/* animCount squelched */
|
||||
/* children */
|
||||
__dna_docout.enumerate("children", children);
|
||||
}
|
||||
|
||||
static const char* DNAType()
|
||||
{
|
||||
return "DataSpec::DNAMP1::ANCS::AnimationSet::MetaAnimSequence";
|
||||
}
|
||||
|
||||
size_t binarySize(size_t __isz) const
|
||||
{
|
||||
__isz = __EnumerateSize(__isz, children);
|
||||
return __isz + 4;
|
||||
}
|
||||
|
||||
void gatherPrimitives(std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out)
|
||||
{
|
||||
for (const auto& child : children)
|
||||
child.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
|
||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func)
|
||||
{
|
||||
for (auto& child : children)
|
||||
if (!child.m_anim->enumeratePrimitives(func))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct Animation : BigYAML
|
||||
|
@ -465,7 +370,6 @@ struct ANCS : BigYAML
|
|||
DECL_YAML
|
||||
String<-1> name;
|
||||
MetaAnimFactory metaAnim;
|
||||
Animation(const UniqueID32& ancsId) : metaAnim(ancsId) {}
|
||||
};
|
||||
std::vector<Animation> animations;
|
||||
|
||||
|
@ -481,29 +385,38 @@ struct ANCS : BigYAML
|
|||
NoTrans = 3,
|
||||
} m_type;
|
||||
const char* m_typeStr;
|
||||
const UniqueID32& m_ancsId;
|
||||
IMetaTrans(Type type, const char* typeStr, const UniqueID32& ancsId)
|
||||
: m_type(type), m_typeStr(typeStr), m_ancsId(ancsId) {}
|
||||
IMetaTrans(Type type, const char* typeStr)
|
||||
: m_type(type), m_typeStr(typeStr) {}
|
||||
virtual void gatherPrimitives(std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) {}
|
||||
virtual bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) {return true;}
|
||||
};
|
||||
struct MetaTransFactory : BigYAML
|
||||
{
|
||||
DECL_YAML
|
||||
Delete expl;
|
||||
const UniqueID32& m_ancsId;
|
||||
std::unique_ptr<IMetaTrans> m_trans;
|
||||
MetaTransFactory(const UniqueID32& ancsId) : m_ancsId(ancsId) {}
|
||||
};
|
||||
struct MetaTransMetaAnim : IMetaTrans
|
||||
{
|
||||
MetaTransMetaAnim(const UniqueID32& ancsId)
|
||||
: IMetaTrans(Type::MetaAnim, "MetaAnim", ancsId), anim(ancsId) {}
|
||||
MetaTransMetaAnim()
|
||||
: IMetaTrans(Type::MetaAnim, "MetaAnim") {}
|
||||
DECL_YAML
|
||||
MetaAnimFactory anim;
|
||||
|
||||
void gatherPrimitives(std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out)
|
||||
{
|
||||
anim.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
|
||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func)
|
||||
{
|
||||
return anim.m_anim->enumeratePrimitives(func);
|
||||
}
|
||||
};
|
||||
struct MetaTransTrans : IMetaTrans
|
||||
{
|
||||
MetaTransTrans(const UniqueID32& ancsId)
|
||||
: IMetaTrans(Type::Trans, "Trans", ancsId) {}
|
||||
MetaTransTrans()
|
||||
: IMetaTrans(Type::Trans, "Trans") {}
|
||||
DECL_YAML
|
||||
Value<float> time;
|
||||
Value<atUint32> unk1;
|
||||
|
@ -513,8 +426,8 @@ struct ANCS : BigYAML
|
|||
};
|
||||
struct MetaTransPhaseTrans : IMetaTrans
|
||||
{
|
||||
MetaTransPhaseTrans(const UniqueID32& ancsId)
|
||||
: IMetaTrans(Type::PhaseTrans, "PhaseTrans", ancsId) {}
|
||||
MetaTransPhaseTrans()
|
||||
: IMetaTrans(Type::PhaseTrans, "PhaseTrans") {}
|
||||
DECL_YAML
|
||||
Value<float> time;
|
||||
Value<atUint32> unk1;
|
||||
|
@ -530,7 +443,6 @@ struct ANCS : BigYAML
|
|||
Value<atUint32> animIdxA;
|
||||
Value<atUint32> animIdxB;
|
||||
MetaTransFactory metaTrans;
|
||||
Transition(const UniqueID32& ancsId) : metaTrans(ancsId) {}
|
||||
};
|
||||
std::vector<Transition> transitions;
|
||||
MetaTransFactory defaultTransition;
|
||||
|
@ -552,7 +464,6 @@ struct ANCS : BigYAML
|
|||
DECL_YAML
|
||||
Value<atUint32> animIdx;
|
||||
MetaTransFactory metaTrans;
|
||||
HalfTransition(const UniqueID32& ancsId) : metaTrans(ancsId) {}
|
||||
};
|
||||
std::vector<HalfTransition> halfTransitions;
|
||||
|
||||
|
@ -575,11 +486,11 @@ struct ANCS : BigYAML
|
|||
DNAANCS::CharacterResInfo<UniqueID32>& chOut = out.back();
|
||||
chOut.name = ci.name;
|
||||
chOut.cmdl = ci.cmdl;
|
||||
chOut.cskr = ci._cskrOld;
|
||||
chOut.cinf = ci._cinfOld;
|
||||
chOut.cskr = ci.cskr;
|
||||
chOut.cinf = ci.cinf;
|
||||
|
||||
if (ci.cmdlOverlay)
|
||||
chOut.overlays.emplace_back(FOURCC('OVER'), std::make_pair(ci.cmdlOverlay, ci._cskrOverlayOld));
|
||||
chOut.overlays.emplace_back(FOURCC('OVER'), std::make_pair(ci.cmdlOverlay, ci.cskrOverlay));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -588,6 +499,11 @@ struct ANCS : BigYAML
|
|||
out.clear();
|
||||
for (const AnimationSet::Animation& ai : animationSet.animations)
|
||||
ai.metaAnim.m_anim->gatherPrimitives(out);
|
||||
for (const AnimationSet::Transition& ti : animationSet.transitions)
|
||||
if (ti.metaTrans.m_trans)
|
||||
ti.metaTrans.m_trans->gatherPrimitives(out);
|
||||
if (animationSet.defaultTransition.m_trans)
|
||||
animationSet.defaultTransition.m_trans->gatherPrimitives(out);
|
||||
for (auto& anim : out)
|
||||
{
|
||||
for (const AnimationSet::AnimationResources& res : animationSet.animResources)
|
||||
|
@ -601,17 +517,13 @@ struct ANCS : BigYAML
|
|||
}
|
||||
}
|
||||
|
||||
void fixupPaths(const UniqueID32& ancsId)
|
||||
void enumeratePrimitives(const std::function<bool(AnimationSet::MetaAnimPrimitive& prim)>& func)
|
||||
{
|
||||
for (CharacterSet::CharacterInfo& character : characterSet.characters)
|
||||
{
|
||||
character._cskrOld = character.cskr;
|
||||
character._cinfOld = character.cinf;
|
||||
character.cskr = character.cmdl;
|
||||
character.cinf = ancsId;
|
||||
character._cskrOverlayOld = character.cskrOverlay;
|
||||
character.cskrOverlay = character.cmdlOverlay;
|
||||
}
|
||||
for (const AnimationSet::Animation& ai : animationSet.animations)
|
||||
ai.metaAnim.m_anim->enumeratePrimitives(func);
|
||||
for (const AnimationSet::Transition& ti : animationSet.transitions)
|
||||
ti.metaTrans.m_trans->enumeratePrimitives(func);
|
||||
animationSet.defaultTransition.m_trans->enumeratePrimitives(func);
|
||||
}
|
||||
|
||||
static bool Extract(const SpecBase& dataSpec,
|
||||
|
@ -632,9 +544,8 @@ struct ANCS : BigYAML
|
|||
yamlType == hecl::ProjectPath::Type::None ||
|
||||
blendType == hecl::ProjectPath::Type::None)
|
||||
{
|
||||
ANCS ancs(entry.id);
|
||||
ANCS ancs;
|
||||
ancs.read(rs);
|
||||
ancs.fixupPaths(entry.id);
|
||||
|
||||
if (force || yamlType == hecl::ProjectPath::Type::None)
|
||||
{
|
||||
|
@ -668,9 +579,54 @@ struct ANCS : BigYAML
|
|||
if (!BigYAML::ValidateFromYAMLFile<ANCS>(yamlReader))
|
||||
Log.report(logvisor::Fatal, _S("'%s' is not urde::DNAMP1::ANCS type"),
|
||||
yamlPath.getRelativePath().c_str());
|
||||
ANCS ancs(UniqueID32::kInvalidId);
|
||||
ANCS ancs;
|
||||
ancs.read(yamlReader);
|
||||
|
||||
/* Set Character Resource IDs */
|
||||
for (ANCS::CharacterSet::CharacterInfo& ch : ancs.characterSet.characters)
|
||||
{
|
||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(ch.cmdl);
|
||||
if (path)
|
||||
ch.cskr = path.ensureAuxInfo(_S("skin"));
|
||||
|
||||
for (const DNAANCS::Actor::Subtype& sub : actor.subtypes)
|
||||
{
|
||||
if (!sub.name.compare(ch.name))
|
||||
{
|
||||
if (sub.armature >= 0)
|
||||
{
|
||||
const DNAANCS::Actor::Armature& arm = actor.armatures[sub.armature];
|
||||
hecl::SystemStringView chSysName(arm.name);
|
||||
ch.cinf = inPath.ensureAuxInfo(chSysName.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path = UniqueIDBridge::TranslatePakIdToPath(ch.cmdlOverlay);
|
||||
if (path)
|
||||
ch.cskrOverlay = path.ensureAuxInfo(_S("skin"));
|
||||
}
|
||||
|
||||
/* Set Animation Resource IDs */
|
||||
ancs.enumeratePrimitives([&](AnimationSet::MetaAnimPrimitive& prim) -> bool
|
||||
{
|
||||
hecl::SystemStringView sysStr(prim.animName);
|
||||
prim.animId = inPath.ensureAuxInfo(sysStr.c_str());
|
||||
return true;
|
||||
});
|
||||
|
||||
/* Write out CINF resources */
|
||||
for (const DNAANCS::Actor::Armature& arm : actor.armatures)
|
||||
{
|
||||
hecl::SystemStringView sysStr(arm.name);
|
||||
hecl::ProjectPath pathOut = inPath.getWithExtension(sysStr.c_str(), true);
|
||||
athena::io::FileWriter w(pathOut.getAbsolutePath(), true, false);
|
||||
if (w.hasError())
|
||||
Log.report(logvisor::Fatal, _S("unable to open '%s' for writing"), pathOut.getRelativePath().c_str());
|
||||
CINF cinf(arm);
|
||||
cinf.write(w);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "ANIM.hpp"
|
||||
#include "zeus/CVector3f.hpp"
|
||||
|
||||
namespace DataSpec
|
||||
{
|
||||
|
@ -7,14 +8,14 @@ namespace DNAMP1
|
|||
|
||||
using ANIMOutStream = hecl::BlenderConnection::PyOutStream::ANIMOutStream;
|
||||
|
||||
void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const CINF& cinf) const
|
||||
void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig) const
|
||||
{
|
||||
os.format("act.hecl_fps = round(%f)\n", (1.0f / mainInterval));
|
||||
|
||||
auto kit = chanKeys.begin();
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
{
|
||||
const std::string* bName = cinf.getBoneNameFromId(bone.first);
|
||||
const std::string* bName = rig.getCINF().getBoneNameFromId(bone.first);
|
||||
if (!bName)
|
||||
continue;
|
||||
|
||||
|
@ -29,9 +30,9 @@ void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, co
|
|||
"\n";
|
||||
|
||||
if (bone.second)
|
||||
os << "bone_trans_head = (0.0,0.0,0.0)\n"
|
||||
"if arm_obj.data.bones[bone_string].parent is not None:\n"
|
||||
" bone_trans_head = Vector(arm_obj.data.bones[bone_string].head_local) - Vector(arm_obj.data.bones[bone_string].parent.head_local)\n"
|
||||
os << "#bone_trans_head = (0.0,0.0,0.0)\n"
|
||||
"#if arm_obj.data.bones[bone_string].parent is not None:\n"
|
||||
"# bone_trans_head = Vector(arm_obj.data.bones[bone_string].head_local) - Vector(arm_obj.data.bones[bone_string].parent.head_local)\n"
|
||||
"transCurves = []\n"
|
||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=0, action_group=bone_string))\n"
|
||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=1, action_group=bone_string))\n"
|
||||
|
@ -44,25 +45,57 @@ void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, co
|
|||
"crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
|
||||
"\n";
|
||||
|
||||
const std::vector<DNAANIM::Value>& rotKeys = *kit++;
|
||||
ANIMOutStream ao = os.beginANIMCurve();
|
||||
for (int c=0 ; c<4 ; ++c)
|
||||
|
||||
{
|
||||
auto frameit = frames.begin();
|
||||
ao.changeCurve(ANIMOutStream::CurveType::Rotate, c, rotKeys.size());
|
||||
for (const DNAANIM::Value& val : rotKeys)
|
||||
ao.write(*frameit++, val.v4.vec[c]);
|
||||
const std::vector<DNAANIM::Value>& rotKeys = *kit++;
|
||||
std::vector<zeus::CQuaternion> fixedRotKeys;
|
||||
fixedRotKeys.resize(rotKeys.size());
|
||||
fprintf(stderr, "alloc %d\n", rotKeys.size());
|
||||
|
||||
for (int c=0 ; c<4 ; ++c)
|
||||
{
|
||||
size_t idx = 0;
|
||||
for (const DNAANIM::Value& val : rotKeys)
|
||||
fixedRotKeys.at(idx++)[c] = val.v4.vec[c];
|
||||
}
|
||||
|
||||
for (zeus::CQuaternion& rot : fixedRotKeys)
|
||||
rot = rig.transformRotation(bone.first, rot);
|
||||
|
||||
for (int c=0 ; c<4 ; ++c)
|
||||
{
|
||||
auto frameit = frames.begin();
|
||||
ao.changeCurve(ANIMOutStream::CurveType::Rotate, c, rotKeys.size());
|
||||
for (const zeus::CQuaternion& val : fixedRotKeys)
|
||||
ao.write(*frameit++, val[c]);
|
||||
}
|
||||
|
||||
fprintf(stderr, "size %d\n", fixedRotKeys.size());
|
||||
}
|
||||
|
||||
if (bone.second)
|
||||
{
|
||||
const std::vector<DNAANIM::Value>& transKeys = *kit++;
|
||||
std::vector<zeus::CVector3f> fixedTransKeys;
|
||||
fixedTransKeys.resize(transKeys.size());
|
||||
|
||||
for (int c=0 ; c<3 ; ++c)
|
||||
{
|
||||
size_t idx = 0;
|
||||
for (const DNAANIM::Value& val : transKeys)
|
||||
fixedTransKeys.at(idx++)[c] = val.v3.vec[c];
|
||||
}
|
||||
|
||||
for (zeus::CVector3f& t : fixedTransKeys)
|
||||
t = rig.transformPosition(bone.first, t, true);
|
||||
|
||||
for (int c=0 ; c<3 ; ++c)
|
||||
{
|
||||
auto frameit = frames.begin();
|
||||
ao.changeCurve(ANIMOutStream::CurveType::Translate, c, transKeys.size());
|
||||
for (const DNAANIM::Value& val : transKeys)
|
||||
ao.write(*frameit++, val.v3.vec[c]);
|
||||
ao.changeCurve(ANIMOutStream::CurveType::Translate, c, fixedTransKeys.size());
|
||||
for (const zeus::CVector3f& val : fixedTransKeys)
|
||||
ao.write(*frameit++, val[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "BlenderConnection.hpp"
|
||||
#include "DNAMP1.hpp"
|
||||
#include "../DNACommon/ANIM.hpp"
|
||||
#include "DataSpec/DNACommon/RigInverter.hpp"
|
||||
#include "CINF.hpp"
|
||||
|
||||
namespace DataSpec
|
||||
|
@ -28,7 +29,7 @@ struct ANIM : BigDNA
|
|||
float mainInterval = 0.0;
|
||||
UniqueID32 evnt;
|
||||
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream&, const CINF&) const;
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream&, const DNAANIM::RigInverter<CINF>& rig) const;
|
||||
};
|
||||
|
||||
struct ANIM0 : IANIM
|
||||
|
@ -173,9 +174,9 @@ struct ANIM : BigDNA
|
|||
return m_anim->binarySize(__isz + 4);
|
||||
}
|
||||
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const CINF& cinf, bool) const
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig, bool) const
|
||||
{
|
||||
m_anim->sendANIMToBlender(os, cinf);
|
||||
m_anim->sendANIMToBlender(os, rig);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "BlenderConnection.hpp"
|
||||
#include "../DNACommon/DNACommon.hpp"
|
||||
#include "../DNACommon/RigInverter.hpp"
|
||||
|
||||
namespace DataSpec
|
||||
{
|
||||
|
@ -36,6 +37,18 @@ struct CINF : BigDNA
|
|||
};
|
||||
Vector<Name, DNA_COUNT(nameCount)> names;
|
||||
|
||||
atUint32 getInternalBoneIdxFromId(atUint32 id) const
|
||||
{
|
||||
atUint32 idx = 0;
|
||||
for (const Bone& b : bones)
|
||||
{
|
||||
if (b.id == id)
|
||||
return idx;
|
||||
++idx;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
atUint32 getBoneIdxFromId(atUint32 id) const
|
||||
{
|
||||
atUint32 idx = 0;
|
||||
|
@ -73,6 +86,8 @@ struct CINF : BigDNA
|
|||
|
||||
void sendCINFToBlender(hecl::BlenderConnection::PyOutStream& os, const UniqueID32& cinfId) const
|
||||
{
|
||||
DNAANIM::RigInverter<CINF> inverter(*this);
|
||||
|
||||
os.format("arm = bpy.data.armatures.new('CINF_%08X')\n"
|
||||
"arm_obj = bpy.data.objects.new(arm.name, arm)\n"
|
||||
"bpy.context.scene.objects.link(arm_obj)\n"
|
||||
|
@ -81,14 +96,16 @@ struct CINF : BigDNA
|
|||
"arm_bone_table = {}\n",
|
||||
cinfId.toUint32());
|
||||
|
||||
for (const Bone& bone : bones)
|
||||
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones())
|
||||
os.format("bone = arm.edit_bones.new('%s')\n"
|
||||
"bone.head = (%f,%f,%f)\n"
|
||||
"bone.tail = bone.head\n"
|
||||
"bone.tail[1] += 0.5\n"
|
||||
"bone.tail = (%f,%f,%f)\n"
|
||||
"#bone.tail[1] += 0.5\n"
|
||||
"bone.use_inherit_scale = False\n"
|
||||
"arm_bone_table[%u] = bone\n", getBoneNameFromId(bone.id)->c_str(),
|
||||
bone.origin.vec[0], bone.origin.vec[1], bone.origin.vec[2], bone.id);
|
||||
"arm_bone_table[%u] = bone\n", getBoneNameFromId(bone.m_origBone.id)->c_str(),
|
||||
bone.m_origBone.origin.vec[0], bone.m_origBone.origin.vec[1], bone.m_origBone.origin.vec[2],
|
||||
bone.m_tail[0], bone.m_tail[1], bone.m_tail[2],
|
||||
bone.m_origBone.id);
|
||||
|
||||
for (const Bone& bone : bones)
|
||||
if (bone.parentId != 2)
|
||||
|
@ -96,6 +113,49 @@ struct CINF : BigDNA
|
|||
|
||||
os << "bpy.ops.object.mode_set(mode='OBJECT')\n";
|
||||
}
|
||||
|
||||
CINF() = default;
|
||||
using Armature = hecl::BlenderConnection::DataStream::Actor::Armature;
|
||||
|
||||
int RecursiveAddArmatureBone(const Armature& armature, const Armature::Bone* bone, int parent, int& curId)
|
||||
{
|
||||
bones.emplace_back();
|
||||
names.emplace_back();
|
||||
Bone& boneOut = bones.back();
|
||||
Name& nameOut = names.back();
|
||||
nameOut.name = bone->name;
|
||||
nameOut.boneId = curId;
|
||||
boneOut.id = curId++;
|
||||
boneOut.parentId = parent;
|
||||
boneOut.origin = bone->origin;
|
||||
boneOut.linkedCount = bone->children.size();
|
||||
boneOut.linked.reserve(boneOut.linkedCount);
|
||||
|
||||
const Armature::Bone* child;
|
||||
for (size_t i=0 ; (child = armature.getChild(bone, i)) ; ++i)
|
||||
boneOut.linked.push_back(RecursiveAddArmatureBone(armature, child, boneOut.id, curId));
|
||||
|
||||
return boneOut.id;
|
||||
}
|
||||
|
||||
CINF(const Armature& armature)
|
||||
{
|
||||
bones.reserve(armature.bones.size());
|
||||
names.reserve(armature.bones.size());
|
||||
|
||||
const Armature::Bone* bone = armature.getRoot();
|
||||
int curId = 3;
|
||||
if (bone)
|
||||
RecursiveAddArmatureBone(armature, bone, 2, curId);
|
||||
|
||||
boneCount = bones.size();
|
||||
nameCount = names.size();
|
||||
|
||||
boneIdCount = boneCount;
|
||||
boneIds.reserve(boneIdCount);
|
||||
for (auto it=bones.crbegin() ; it != bones.crend() ; ++it)
|
||||
boneIds.push_back(it->id);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -227,22 +227,22 @@ void PAKBridge::addCMDLRigPairs(PAKRouter<PAKBridge>& pakRouter,
|
|||
if (entry.second->type == FOURCC('ANCS'))
|
||||
{
|
||||
PAKEntryReadStream rs = entry.second->beginReadStream(m_node);
|
||||
ANCS ancs(entry.first);
|
||||
ANCS ancs;
|
||||
ancs.read(rs);
|
||||
for (const ANCS::CharacterSet::CharacterInfo& ci : ancs.characterSet.characters)
|
||||
{
|
||||
addTo[ci.cmdl] = std::make_pair(ci.cskr.getBaseId(), ci.cinf.getBaseId());
|
||||
addTo[ci.cmdl] = std::make_pair(ci.cskr, ci.cinf);
|
||||
PAK::Entry* cmdlEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cmdl);
|
||||
PAK::Entry* cskrEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cskr.getBaseId());
|
||||
PAK::Entry* cinfEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cinf.getBaseId());
|
||||
PAK::Entry* cskrEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cskr);
|
||||
PAK::Entry* cinfEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cinf);
|
||||
cmdlEnt->name = hecl::Format("ANCS_%08X_%s_model", entry.first.toUint32(), ci.name.c_str());
|
||||
cskrEnt->name = hecl::Format("ANCS_%08X_%s_skin", entry.first.toUint32(), ci.name.c_str());
|
||||
cinfEnt->name = hecl::Format("ANCS_%08X_%s_skel", entry.first.toUint32(), ci.name.c_str());
|
||||
if (ci.cmdlOverlay && ci.cskrOverlay)
|
||||
{
|
||||
addTo[ci.cmdlOverlay] = std::make_pair(ci.cskrOverlay.getBaseId(), ci.cinf.getBaseId());
|
||||
addTo[ci.cmdlOverlay] = std::make_pair(ci.cskrOverlay, ci.cinf);
|
||||
PAK::Entry* cmdlEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cmdlOverlay);
|
||||
PAK::Entry* cskrEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cskrOverlay.getBaseId());
|
||||
PAK::Entry* cskrEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cskrOverlay);
|
||||
cmdlEnt->name = hecl::Format("ANCS_%08X_%s_overmodel", entry.first.toUint32(), ci.name.c_str());
|
||||
cskrEnt->name = hecl::Format("ANCS_%08X_%s_overskin", entry.first.toUint32(), ci.name.c_str());
|
||||
}
|
||||
|
|
|
@ -12,12 +12,157 @@ UniqueID32 AnimationParameters::getCINF(PAKRouter<PAKBridge>& pakRouter) const
|
|||
return UniqueID32();
|
||||
const nod::Node* node;
|
||||
const PAK::Entry* ancsEnt = pakRouter.lookupEntry(animationCharacterSet, &node);
|
||||
ANCS ancs(ancsEnt->id);
|
||||
ANCS ancs;
|
||||
{
|
||||
PAKEntryReadStream rs = ancsEnt->beginReadStream(*node);
|
||||
ancs.read(rs);
|
||||
}
|
||||
return ancs.characterSet.characters.at(character).cinf.getBaseId();
|
||||
return ancs.characterSet.characters.at(character).cinf;
|
||||
}
|
||||
|
||||
void ActorParameters::read(athena::io::IStreamReader& __dna_reader)
|
||||
{
|
||||
/* propertyCount */
|
||||
propertyCount = __dna_reader.readUint32Big();
|
||||
/* lightParameters */
|
||||
lightParameters.read(__dna_reader);
|
||||
/* scannableParameters */
|
||||
scannableParameters.read(__dna_reader);
|
||||
/* xrayModel */
|
||||
xrayModel.read(__dna_reader);
|
||||
/* xraySkin */
|
||||
xraySkin.read(__dna_reader);
|
||||
/* thermalModel */
|
||||
thermalModel.read(__dna_reader);
|
||||
/* thermalSkin */
|
||||
thermalSkin.read(__dna_reader);
|
||||
/* unknown1 */
|
||||
unknown1 = __dna_reader.readBool();
|
||||
/* unknown2 */
|
||||
unknown2 = __dna_reader.readFloatBig();
|
||||
/* unknown3 */
|
||||
unknown3 = __dna_reader.readFloatBig();
|
||||
/* visorParameters */
|
||||
visorParameters.read(__dna_reader);
|
||||
/* thermalHeat */
|
||||
thermalHeat = __dna_reader.readBool();
|
||||
/* unknown4 */
|
||||
unknown4 = __dna_reader.readBool();
|
||||
/* unknown5 */
|
||||
unknown5 = __dna_reader.readBool();
|
||||
/* unknown6 */
|
||||
unknown6 = __dna_reader.readFloatBig();
|
||||
}
|
||||
|
||||
void ActorParameters::write(athena::io::IStreamWriter& __dna_writer) const
|
||||
{
|
||||
/* propertyCount */
|
||||
__dna_writer.writeUint32Big(propertyCount);
|
||||
/* lightParameters */
|
||||
lightParameters.write(__dna_writer);
|
||||
/* scannableParameters */
|
||||
scannableParameters.write(__dna_writer);
|
||||
/* xrayModel */
|
||||
xrayModel.write(__dna_writer);
|
||||
/* xraySkin */
|
||||
xraySkin.write(__dna_writer);
|
||||
/* thermalModel */
|
||||
thermalModel.write(__dna_writer);
|
||||
/* thermalSkin */
|
||||
thermalSkin.write(__dna_writer);
|
||||
/* unknown1 */
|
||||
__dna_writer.writeBool(unknown1);
|
||||
/* unknown2 */
|
||||
__dna_writer.writeFloatBig(unknown2);
|
||||
/* unknown3 */
|
||||
__dna_writer.writeFloatBig(unknown3);
|
||||
/* visorParameters */
|
||||
visorParameters.write(__dna_writer);
|
||||
/* thermalHeat */
|
||||
__dna_writer.writeBool(thermalHeat);
|
||||
/* unknown4 */
|
||||
__dna_writer.writeBool(unknown4);
|
||||
/* unknown5 */
|
||||
__dna_writer.writeBool(unknown5);
|
||||
/* unknown6 */
|
||||
__dna_writer.writeFloatBig(unknown6);
|
||||
}
|
||||
|
||||
void ActorParameters::read(athena::io::YAMLDocReader& __dna_docin)
|
||||
{
|
||||
/* propertyCount */
|
||||
propertyCount = __dna_docin.readUint32("propertyCount");
|
||||
/* lightParameters */
|
||||
__dna_docin.enumerate("lightParameters", lightParameters);
|
||||
/* scannableParameters */
|
||||
__dna_docin.enumerate("scannableParameters", scannableParameters);
|
||||
/* xrayModel */
|
||||
__dna_docin.enumerate("xrayModel", xrayModel);
|
||||
/* thermalModel */
|
||||
__dna_docin.enumerate("thermalModel", thermalModel);
|
||||
/* unknown1 */
|
||||
unknown1 = __dna_docin.readBool("unknown1");
|
||||
/* unknown2 */
|
||||
unknown2 = __dna_docin.readFloat("unknown2");
|
||||
/* unknown3 */
|
||||
unknown3 = __dna_docin.readFloat("unknown3");
|
||||
/* visorParameters */
|
||||
__dna_docin.enumerate("visorParameters", visorParameters);
|
||||
/* thermalHeat */
|
||||
thermalHeat = __dna_docin.readBool("thermalHeat");
|
||||
/* unknown4 */
|
||||
unknown4 = __dna_docin.readBool("unknown4");
|
||||
/* unknown5 */
|
||||
unknown5 = __dna_docin.readBool("unknown5");
|
||||
/* unknown6 */
|
||||
unknown6 = __dna_docin.readFloat("unknown6");
|
||||
}
|
||||
|
||||
void ActorParameters::write(athena::io::YAMLDocWriter& __dna_docout) const
|
||||
{
|
||||
/* propertyCount */
|
||||
__dna_docout.writeUint32("propertyCount", propertyCount);
|
||||
/* lightParameters */
|
||||
__dna_docout.enumerate("lightParameters", lightParameters);
|
||||
/* scannableParameters */
|
||||
__dna_docout.enumerate("scannableParameters", scannableParameters);
|
||||
/* xrayModel */
|
||||
__dna_docout.enumerate("xrayModel", xrayModel);
|
||||
/* thermalModel */
|
||||
__dna_docout.enumerate("thermalModel", thermalModel);
|
||||
/* unknown1 */
|
||||
__dna_docout.writeBool("unknown1", unknown1);
|
||||
/* unknown2 */
|
||||
__dna_docout.writeFloat("unknown2", unknown2);
|
||||
/* unknown3 */
|
||||
__dna_docout.writeFloat("unknown3", unknown3);
|
||||
/* visorParameters */
|
||||
__dna_docout.enumerate("visorParameters", visorParameters);
|
||||
/* thermalHeat */
|
||||
__dna_docout.writeBool("thermalHeat", thermalHeat);
|
||||
/* unknown4 */
|
||||
__dna_docout.writeBool("unknown4", unknown4);
|
||||
/* unknown5 */
|
||||
__dna_docout.writeBool("unknown5", unknown5);
|
||||
/* unknown6 */
|
||||
__dna_docout.writeFloat("unknown6", unknown6);
|
||||
}
|
||||
|
||||
const char* ActorParameters::DNAType()
|
||||
{
|
||||
return "ActorParameters";
|
||||
}
|
||||
|
||||
size_t ActorParameters::binarySize(size_t __isz) const
|
||||
{
|
||||
__isz = lightParameters.binarySize(__isz);
|
||||
__isz = scannableParameters.binarySize(__isz);
|
||||
__isz = xrayModel.binarySize(__isz);
|
||||
__isz = xraySkin.binarySize(__isz);
|
||||
__isz = thermalModel.binarySize(__isz);
|
||||
__isz = thermalSkin.binarySize(__isz);
|
||||
__isz = visorParameters.binarySize(__isz);
|
||||
return __isz + 20;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -350,13 +350,14 @@ struct VisorParameters : BigYAML
|
|||
struct ActorParameters : BigYAML
|
||||
{
|
||||
DECL_YAML
|
||||
Delete _d;
|
||||
Value<atUint32> propertyCount;
|
||||
LightParameters lightParameters;
|
||||
ScannableParameters scannableParameters;
|
||||
UniqueID32 xrayModel;
|
||||
AuxiliaryID32 xraySkin = _S("skin");
|
||||
UniqueID32 xraySkin;
|
||||
UniqueID32 thermalModel;
|
||||
AuxiliaryID32 thermalSkin = _S("skin");
|
||||
UniqueID32 thermalSkin;
|
||||
Value<bool> unknown1;
|
||||
Value<float> unknown2;
|
||||
Value<float> unknown3;
|
||||
|
|
|
@ -243,9 +243,6 @@ void ANCS::CharacterSet::CharacterInfo::read(athena::io::YAMLDocReader& reader)
|
|||
idx = reader.readUint32("idx");
|
||||
atUint16 sectionCount = reader.readUint16("sectionCount");
|
||||
name = reader.readString("name");
|
||||
reader.enumerate("cmdl", cmdl);
|
||||
reader.enumerate("cskr", cskr);
|
||||
reader.enumerate("cinf", cinf);
|
||||
|
||||
reader.enumerate("animations", animations);
|
||||
|
||||
|
@ -280,7 +277,6 @@ void ANCS::CharacterSet::CharacterInfo::read(athena::io::YAMLDocReader& reader)
|
|||
if (sectionCount > 3)
|
||||
{
|
||||
reader.enumerate("cmdlOverlay", cmdlOverlay);
|
||||
reader.enumerate("cskrOverlay", cskrOverlay);
|
||||
}
|
||||
|
||||
animIdxs.clear();
|
||||
|
@ -321,8 +317,6 @@ void ANCS::CharacterSet::CharacterInfo::write(athena::io::YAMLDocWriter& writer)
|
|||
|
||||
writer.writeString("name", name);
|
||||
writer.enumerate("cmdl", cmdl);
|
||||
writer.enumerate("cskr", cskr);
|
||||
writer.enumerate("cinf", cinf);
|
||||
|
||||
writer.enumerate("animations", animations);
|
||||
|
||||
|
@ -355,7 +349,6 @@ void ANCS::CharacterSet::CharacterInfo::write(athena::io::YAMLDocWriter& writer)
|
|||
if (sectionCount > 3)
|
||||
{
|
||||
writer.enumerate("cmdlOverlay", cmdlOverlay);
|
||||
writer.enumerate("cskrOverlay", cskrOverlay);
|
||||
}
|
||||
|
||||
if (sectionCount > 4)
|
||||
|
@ -381,23 +374,10 @@ void ANCS::AnimationSet::read(athena::io::IStreamReader& reader)
|
|||
atUint16 sectionCount = reader.readUint16Big();
|
||||
|
||||
atUint32 animationCount = reader.readUint32Big();
|
||||
animations.clear();
|
||||
animations.reserve(animationCount);
|
||||
for (atUint32 i=0 ; i<animationCount ; ++i)
|
||||
{
|
||||
animations.emplace_back(m_ancsId);
|
||||
animations.back().read(reader);
|
||||
}
|
||||
reader.enumerate(animations, animationCount);
|
||||
|
||||
atUint32 transitionCount = reader.readUint32Big();
|
||||
transitions.clear();
|
||||
transitions.reserve(transitionCount);
|
||||
for (atUint32 i=0 ; i<transitionCount ; ++i)
|
||||
{
|
||||
transitions.emplace_back(m_ancsId);
|
||||
transitions.back().read(reader);
|
||||
}
|
||||
|
||||
reader.enumerate(transitions, transitionCount);
|
||||
defaultTransition.read(reader);
|
||||
|
||||
additiveAnims.clear();
|
||||
|
@ -412,14 +392,8 @@ void ANCS::AnimationSet::read(athena::io::IStreamReader& reader)
|
|||
halfTransitions.clear();
|
||||
if (sectionCount > 2)
|
||||
{
|
||||
atUint32 halfTransitionCount = reader.readUint32Big();
|
||||
halfTransitions.clear();
|
||||
halfTransitions.reserve(halfTransitionCount);
|
||||
for (atUint32 i=0 ; i<halfTransitionCount ; ++i)
|
||||
{
|
||||
halfTransitions.emplace_back(m_ancsId);
|
||||
halfTransitions.back().read(reader);
|
||||
}
|
||||
atUint32 halfTransitionCount = reader.readUint32Big();
|
||||
reader.enumerate(halfTransitions, halfTransitionCount);
|
||||
}
|
||||
|
||||
evnts.clear();
|
||||
|
@ -517,32 +491,9 @@ void ANCS::AnimationSet::read(athena::io::YAMLDocReader& reader)
|
|||
{
|
||||
atUint16 sectionCount = reader.readUint16("sectionCount");
|
||||
|
||||
size_t animationCount;
|
||||
reader.enterSubVector("animations", animationCount);
|
||||
animations.clear();
|
||||
animations.reserve(animationCount);
|
||||
for (size_t i=0 ; i<animationCount ; ++i)
|
||||
{
|
||||
animations.emplace_back(m_ancsId);
|
||||
reader.enterSubRecord(nullptr);
|
||||
animations.back().read(reader);
|
||||
reader.leaveSubRecord();
|
||||
}
|
||||
reader.leaveSubVector();
|
||||
|
||||
size_t transitionCount;
|
||||
reader.enterSubVector("transitions", transitionCount);
|
||||
transitions.clear();
|
||||
transitions.reserve(transitionCount);
|
||||
for (size_t i=0 ; i<transitionCount ; ++i)
|
||||
{
|
||||
transitions.emplace_back(m_ancsId);
|
||||
reader.enterSubRecord(nullptr);
|
||||
transitions.back().read(reader);
|
||||
reader.leaveSubRecord();
|
||||
}
|
||||
reader.leaveSubVector();
|
||||
reader.enumerate("animations", animations);
|
||||
|
||||
reader.enumerate("transitions", transitions);
|
||||
reader.enumerate("defaultTransition", defaultTransition);
|
||||
|
||||
additiveAnims.clear();
|
||||
|
@ -556,17 +507,7 @@ void ANCS::AnimationSet::read(athena::io::YAMLDocReader& reader)
|
|||
halfTransitions.clear();
|
||||
if (sectionCount > 2)
|
||||
{
|
||||
size_t halfTransitionCount;
|
||||
reader.enterSubVector("halfTransitions", halfTransitionCount);
|
||||
halfTransitions.reserve(halfTransitionCount);
|
||||
for (size_t i=0 ; i<halfTransitionCount ; ++i)
|
||||
{
|
||||
halfTransitions.emplace_back(m_ancsId);
|
||||
reader.enterSubRecord(nullptr);
|
||||
halfTransitions.back().read(reader);
|
||||
reader.leaveSubRecord();
|
||||
}
|
||||
reader.leaveSubVector();
|
||||
reader.enumerate("halfTransitions", halfTransitions);
|
||||
}
|
||||
|
||||
evnts.clear();
|
||||
|
|
|
@ -22,8 +22,6 @@ struct ANCS : BigYAML
|
|||
using CSKRType = CSKR;
|
||||
using ANIMType = ANIM;
|
||||
|
||||
ANCS(const UniqueID32& ancsId) : animationSet(ancsId) {}
|
||||
|
||||
DECL_YAML
|
||||
Value<atUint16> version;
|
||||
|
||||
|
@ -41,10 +39,8 @@ struct ANCS : BigYAML
|
|||
atUint32 idx;
|
||||
std::string name;
|
||||
UniqueID32 cmdl;
|
||||
UniqueID32 _cskrOld;
|
||||
UniqueID32 _cinfOld;
|
||||
AuxiliaryID32 cskr = _S("skin");
|
||||
AuxiliaryID32 cinf = {_S("layout"), _S("skin")};
|
||||
UniqueID32 cskr;
|
||||
UniqueID32 cinf;
|
||||
|
||||
struct Animation : BigYAML
|
||||
{
|
||||
|
@ -90,8 +86,7 @@ struct ANCS : BigYAML
|
|||
std::vector<Effect> effects;
|
||||
|
||||
UniqueID32 cmdlOverlay;
|
||||
UniqueID32 _cskrOverlayOld;
|
||||
AuxiliaryID32 cskrOverlay = _S("skin");
|
||||
UniqueID32 cskrOverlay;
|
||||
|
||||
std::vector<atUint32> animIdxs;
|
||||
|
||||
|
@ -113,8 +108,6 @@ struct ANCS : BigYAML
|
|||
{
|
||||
DECL_YAML
|
||||
Delete expl;
|
||||
const UniqueID32& m_ancsId;
|
||||
AnimationSet(const UniqueID32& ancsId) : m_ancsId(ancsId), defaultTransition(ancsId) {}
|
||||
|
||||
using MP1AnimationSet = DNAMP1::ANCS::AnimationSet;
|
||||
|
||||
|
@ -204,11 +197,11 @@ struct ANCS : BigYAML
|
|||
DNAANCS::CharacterResInfo<UniqueID32>& chOut = out.back();
|
||||
chOut.name = ci.name;
|
||||
chOut.cmdl = ci.cmdl;
|
||||
chOut.cskr = ci._cskrOld;
|
||||
chOut.cinf = ci._cinfOld;
|
||||
chOut.cskr = ci.cskr;
|
||||
chOut.cinf = ci.cinf;
|
||||
|
||||
if (ci.cmdlOverlay)
|
||||
chOut.overlays.emplace_back(FOURCC('OVER'), std::make_pair(ci.cmdlOverlay, ci._cskrOverlayOld));
|
||||
chOut.overlays.emplace_back(FOURCC('OVER'), std::make_pair(ci.cmdlOverlay, ci.cskrOverlay));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,6 +210,11 @@ struct ANCS : BigYAML
|
|||
out.clear();
|
||||
for (const DNAMP1::ANCS::AnimationSet::Animation& ai : animationSet.animations)
|
||||
ai.metaAnim.m_anim->gatherPrimitives(out);
|
||||
for (const DNAMP1::ANCS::AnimationSet::Transition& ti : animationSet.transitions)
|
||||
if (ti.metaTrans.m_trans)
|
||||
ti.metaTrans.m_trans->gatherPrimitives(out);
|
||||
if (animationSet.defaultTransition.m_trans)
|
||||
animationSet.defaultTransition.m_trans->gatherPrimitives(out);
|
||||
}
|
||||
|
||||
static bool Extract(const SpecBase& dataSpec,
|
||||
|
@ -237,7 +235,7 @@ struct ANCS : BigYAML
|
|||
yamlType == hecl::ProjectPath::Type::None ||
|
||||
blendType == hecl::ProjectPath::Type::None)
|
||||
{
|
||||
ANCS ancs(entry.id);
|
||||
ANCS ancs;
|
||||
ancs.read(rs);
|
||||
|
||||
if (force || yamlType == hecl::ProjectPath::Type::None)
|
||||
|
|
|
@ -7,17 +7,17 @@ namespace DNAMP2
|
|||
|
||||
using ANIMOutStream = hecl::BlenderConnection::PyOutStream::ANIMOutStream;
|
||||
|
||||
void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const CINF& cinf) const
|
||||
void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig) const
|
||||
{
|
||||
os.format("act.hecl_fps = round(%f)\n", (1.0f / mainInterval));
|
||||
|
||||
auto kit = chanKeys.begin();
|
||||
for (const std::pair<atUint32, std::tuple<bool,bool,bool>>& bone : bones)
|
||||
{
|
||||
const std::string* bName = cinf.getBoneNameFromId(bone.first);
|
||||
const std::string* bName = rig.getCINF().getBoneNameFromId(bone.first);
|
||||
if (!bName)
|
||||
continue;
|
||||
|
||||
|
||||
os.format("bone_string = '%s'\n", bName->c_str());
|
||||
os << "action_group = act.groups.new(bone_string)\n"
|
||||
"\n";
|
||||
|
@ -31,9 +31,9 @@ void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, co
|
|||
"\n";
|
||||
|
||||
if (std::get<1>(bone.second))
|
||||
os << "bone_trans_head = (0.0,0.0,0.0)\n"
|
||||
"if arm_obj.data.bones[bone_string].parent is not None:\n"
|
||||
" bone_trans_head = Vector(arm_obj.data.bones[bone_string].head_local) - Vector(arm_obj.data.bones[bone_string].parent.head_local)\n"
|
||||
os << "#bone_trans_head = (0.0,0.0,0.0)\n"
|
||||
"#if arm_obj.data.bones[bone_string].parent is not None:\n"
|
||||
"# bone_trans_head = Vector(arm_obj.data.bones[bone_string].head_local) - Vector(arm_obj.data.bones[bone_string].parent.head_local)\n"
|
||||
"transCurves = []\n"
|
||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=0, action_group=bone_string))\n"
|
||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=1, action_group=bone_string))\n"
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "BlenderConnection.hpp"
|
||||
#include "DNAMP2.hpp"
|
||||
#include "../DNACommon/ANIM.hpp"
|
||||
#include "../DNACommon/RigInverter.hpp"
|
||||
#include "CINF.hpp"
|
||||
|
||||
namespace DataSpec
|
||||
|
@ -27,7 +28,7 @@ struct ANIM : BigDNA
|
|||
std::vector<std::vector<DNAANIM::Value>> chanKeys;
|
||||
float mainInterval = 0.0;
|
||||
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream&, const CINF&) const;
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream&, const DNAANIM::RigInverter<CINF>& rig) const;
|
||||
};
|
||||
|
||||
struct ANIM0 : IANIM
|
||||
|
@ -210,9 +211,9 @@ struct ANIM : BigDNA
|
|||
return m_anim->binarySize(__isz + 4);
|
||||
}
|
||||
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const CINF& cinf, bool) const
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig, bool) const
|
||||
{
|
||||
m_anim->sendANIMToBlender(os, cinf);
|
||||
m_anim->sendANIMToBlender(os, rig);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -36,6 +36,18 @@ struct CINF : BigDNA
|
|||
Value<atUint32> boneId;
|
||||
};
|
||||
Vector<Name, DNA_COUNT(nameCount)> names;
|
||||
|
||||
atUint32 getInternalBoneIdxFromId(atUint32 id) const
|
||||
{
|
||||
atUint32 idx = 0;
|
||||
for (const Bone& b : bones)
|
||||
{
|
||||
if (b.id == id)
|
||||
return idx;
|
||||
++idx;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
atUint32 getBoneIdxFromId(atUint32 id) const
|
||||
{
|
||||
|
|
|
@ -198,13 +198,13 @@ void PAKBridge::addCMDLRigPairs(PAKRouter<PAKBridge>& pakRouter,
|
|||
if (entry.second->type == FOURCC('ANCS'))
|
||||
{
|
||||
PAKEntryReadStream rs = entry.second->beginReadStream(m_node);
|
||||
ANCS ancs(entry.first);
|
||||
ANCS ancs;
|
||||
ancs.read(rs);
|
||||
for (const ANCS::CharacterSet::CharacterInfo& ci : ancs.characterSet.characters)
|
||||
{
|
||||
addTo[ci.cmdl] = std::make_pair(ci.cskr.getBaseId(), ci.cinf.getBaseId());
|
||||
addTo[ci.cmdl] = std::make_pair(ci.cskr, ci.cinf);
|
||||
if (ci.cmdlOverlay)
|
||||
addTo[ci.cmdlOverlay] = std::make_pair(ci.cskrOverlay.getBaseId(), ci.cinf.getBaseId());
|
||||
addTo[ci.cmdlOverlay] = std::make_pair(ci.cskrOverlay, ci.cinf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace DNAMP3
|
|||
|
||||
using ANIMOutStream = hecl::BlenderConnection::PyOutStream::ANIMOutStream;
|
||||
|
||||
void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const CINF& cinf, bool additive) const
|
||||
void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig, bool additive) const
|
||||
{
|
||||
os.format("act.hecl_fps = round(%f)\n"
|
||||
"act.hecl_additive = %s\n",
|
||||
|
@ -18,7 +18,7 @@ void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, co
|
|||
auto kit = chanKeys.begin() + 1;
|
||||
for (const std::pair<atUint32, std::tuple<bool,bool,bool>>& bone : bones)
|
||||
{
|
||||
const std::string* bName = cinf.getBoneNameFromId(bone.first);
|
||||
const std::string* bName = rig.getCINF().getBoneNameFromId(bone.first);
|
||||
if (!bName)
|
||||
{
|
||||
if (std::get<0>(bone.second))
|
||||
|
@ -45,11 +45,11 @@ void ANIM::IANIM::sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, co
|
|||
if (std::get<1>(bone.second))
|
||||
{
|
||||
if (!additive)
|
||||
os << "bone_trans_head = (0.0,0.0,0.0)\n"
|
||||
"if arm_obj.data.bones[bone_string].parent is not None:\n"
|
||||
" bone_trans_head = Vector(arm_obj.data.bones[bone_string].head_local) - Vector(arm_obj.data.bones[bone_string].parent.head_local)\n";
|
||||
os << "#bone_trans_head = (0.0,0.0,0.0)\n"
|
||||
"#if arm_obj.data.bones[bone_string].parent is not None:\n"
|
||||
"# bone_trans_head = Vector(arm_obj.data.bones[bone_string].head_local) - Vector(arm_obj.data.bones[bone_string].parent.head_local)\n";
|
||||
else
|
||||
os << "bone_trans_head = (0.0,0.0,0.0)\n";
|
||||
os << "#bone_trans_head = (0.0,0.0,0.0)\n";
|
||||
os << "transCurves = []\n"
|
||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=0, action_group=bone_string))\n"
|
||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=1, action_group=bone_string))\n"
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "BlenderConnection.hpp"
|
||||
#include "DNAMP3.hpp"
|
||||
#include "../DNACommon/ANIM.hpp"
|
||||
#include "../DNACommon/RigInverter.hpp"
|
||||
#include "CINF.hpp"
|
||||
|
||||
namespace DataSpec
|
||||
|
@ -27,7 +28,7 @@ struct ANIM : BigDNA
|
|||
std::vector<std::vector<DNAANIM::Value>> chanKeys;
|
||||
float mainInterval = 0.0;
|
||||
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream&, const CINF&, bool additive) const;
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream&, const DNAANIM::RigInverter<CINF>& rig, bool additive) const;
|
||||
};
|
||||
|
||||
struct ANIM0 : IANIM
|
||||
|
@ -110,9 +111,9 @@ struct ANIM : BigDNA
|
|||
return m_anim->binarySize(__isz + 4);
|
||||
}
|
||||
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const CINF& cinf, bool additive) const
|
||||
void sendANIMToBlender(hecl::BlenderConnection::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig, bool additive) const
|
||||
{
|
||||
m_anim->sendANIMToBlender(os, cinf, additive);
|
||||
m_anim->sendANIMToBlender(os, rig, additive);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -245,7 +245,7 @@ struct SpecMP1 : SpecBase
|
|||
int prog = 0;
|
||||
ctx.progressCB = [&](const std::string& name) {
|
||||
hecl::SystemStringView nameView(name);
|
||||
progress(_S("MP1 Root"), nameView.sys_str().c_str(), 3, prog / (float)m_nonPaks.size());
|
||||
progress(_S("MP1 Root"), nameView.c_str(), 3, prog / (float)m_nonPaks.size());
|
||||
};
|
||||
for (const nod::Node* node : m_nonPaks)
|
||||
{
|
||||
|
@ -269,7 +269,7 @@ struct SpecMP1 : SpecBase
|
|||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(msgLock);
|
||||
progress(sysName.sys_str().c_str(), _S(""), compIdx, 0.0);
|
||||
progress(sysName.c_str(), _S(""), compIdx, 0.0);
|
||||
}
|
||||
hecl::SystemString pakName = sysName.sys_str();
|
||||
process.addLambdaTransaction([&, pakName](hecl::BlenderToken& btok)
|
||||
|
|
|
@ -227,7 +227,7 @@ struct SpecMP2 : SpecBase
|
|||
int prog = 0;
|
||||
ctx.progressCB = [&](const std::string& name) {
|
||||
hecl::SystemStringView nameView(name);
|
||||
progress(_S("MP2 Root"), nameView.sys_str().c_str(), 3, prog / (float)m_nonPaks.size());
|
||||
progress(_S("MP2 Root"), nameView.c_str(), 3, prog / (float)m_nonPaks.size());
|
||||
};
|
||||
for (const nod::Node* node : m_nonPaks)
|
||||
{
|
||||
|
@ -251,7 +251,7 @@ struct SpecMP2 : SpecBase
|
|||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(msgLock);
|
||||
progress(sysName.sys_str().c_str(), _S(""), compIdx, 0.0);
|
||||
progress(sysName.c_str(), _S(""), compIdx, 0.0);
|
||||
}
|
||||
hecl::SystemString pakName = sysName.sys_str();
|
||||
process.addLambdaTransaction([&, pakName](hecl::BlenderToken& btok)
|
||||
|
|
|
@ -333,7 +333,7 @@ struct SpecMP3 : SpecBase
|
|||
[&](const std::string& name)
|
||||
{
|
||||
hecl::SystemStringView nameView(name);
|
||||
progress(currentTarget.c_str(), nameView.sys_str().c_str(), compIdx, prog / (float)nodeCount);
|
||||
progress(currentTarget.c_str(), nameView.c_str(), compIdx, prog / (float)nodeCount);
|
||||
}};
|
||||
if (doMP3)
|
||||
{
|
||||
|
@ -382,7 +382,7 @@ struct SpecMP3 : SpecBase
|
|||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(msgLock);
|
||||
progress(sysName.sys_str().c_str(), _S(""), compIdx, 0.0);
|
||||
progress(sysName.c_str(), _S(""), compIdx, 0.0);
|
||||
}
|
||||
hecl::SystemString pakName = sysName.sys_str();
|
||||
process.addLambdaTransaction([&, pakName](hecl::BlenderToken& btok)
|
||||
|
@ -442,7 +442,7 @@ struct SpecMP3 : SpecBase
|
|||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(msgLock);
|
||||
progress(sysName.sys_str().c_str(), _S(""), compIdx, 0.0);
|
||||
progress(sysName.c_str(), _S(""), compIdx, 0.0);
|
||||
}
|
||||
hecl::SystemString pakName = sysName.sys_str();
|
||||
process.addLambdaTransaction([&, pakName](hecl::BlenderToken& btok)
|
||||
|
|
|
@ -146,12 +146,36 @@ void ProjectResourceFactoryBase::BackgroundIndexRecursiveProc(const hecl::Projec
|
|||
}
|
||||
else if (pathTag.type == SBIG('ANCS'))
|
||||
{
|
||||
hecl::ProjectPath subPath(path, ".|layout");
|
||||
SObjectTag pathTag = TagFromPath(subPath, m_backgroundBlender);
|
||||
if (pathTag)
|
||||
hecl::BlenderConnection& conn = m_backgroundBlender.getBlenderConnection();
|
||||
if (!conn.openBlend(path) || conn.getBlendType() != hecl::BlenderConnection::BlendType::Actor)
|
||||
continue;
|
||||
|
||||
hecl::BlenderConnection::DataStream ds = conn.beginData();
|
||||
std::vector<std::string> armatureNames = ds.getArmatureNames();
|
||||
std::vector<std::string> actionNames = ds.getActionNames();
|
||||
|
||||
for (const std::string& arm : armatureNames)
|
||||
{
|
||||
m_tagToPath[pathTag] = path;
|
||||
WriteTag(cacheWriter, pathTag, path);
|
||||
hecl::SystemStringView sysStr(arm);
|
||||
hecl::ProjectPath subPath = path.ensureAuxInfo(sysStr.c_str());
|
||||
SObjectTag pathTag = TagFromPath(subPath, m_backgroundBlender);
|
||||
if (pathTag)
|
||||
{
|
||||
m_tagToPath[pathTag] = path;
|
||||
WriteTag(cacheWriter, pathTag, path);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& act : actionNames)
|
||||
{
|
||||
hecl::SystemStringView sysStr(act);
|
||||
hecl::ProjectPath subPath = path.ensureAuxInfo(sysStr.c_str());
|
||||
SObjectTag pathTag = TagFromPath(subPath, m_backgroundBlender);
|
||||
if (pathTag)
|
||||
{
|
||||
m_tagToPath[pathTag] = path;
|
||||
WriteTag(cacheWriter, pathTag, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ SObjectTag ProjectResourceFactoryMP1::TagFromPath(const hecl::ProjectPath& path,
|
|||
}
|
||||
return {SBIG('CMDL'), path.hash().val32()};
|
||||
case hecl::BlenderConnection::BlendType::Actor:
|
||||
if (!path.getAuxInfo().compare(_S("layout")))
|
||||
if (path.getAuxInfo().size())
|
||||
return {SBIG('CINF'), path.hash().val32()};
|
||||
return {SBIG('ANCS'), path.hash().val32()};
|
||||
case hecl::BlenderConnection::BlendType::Area:
|
||||
|
|
2
hecl
2
hecl
|
@ -1 +1 @@
|
|||
Subproject commit 22d1a5a98969e82bc314e9908f7f0d677eb4a9d5
|
||||
Subproject commit 71e97f724085893d27eaab6da71abf02aa37cb1b
|
2
specter
2
specter
|
@ -1 +1 @@
|
|||
Subproject commit ff3410b26607bca871dc15e464235c67b47770a2
|
||||
Subproject commit a1d60af72c17c4df4152f36766ec9e39c9c56200
|
Loading…
Reference in New Issue