mirror of https://github.com/AxioDL/metaforce.git
Initial MP1 ANIM implementation
This commit is contained in:
parent
f50629fad7
commit
2cc94bf4ed
|
@ -0,0 +1,73 @@
|
|||
#ifndef _DNACOMMON_ANCS_HPP_
|
||||
#define _DNACOMMON_ANCS_HPP_
|
||||
|
||||
#include "DNACommon.hpp"
|
||||
#include "BlenderConnection.hpp"
|
||||
#include "CMDL.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
namespace DNAANCS
|
||||
{
|
||||
|
||||
template <typename IDTYPE>
|
||||
struct CharacterResInfo
|
||||
{
|
||||
std::string name;
|
||||
IDTYPE cmdl;
|
||||
IDTYPE cskr;
|
||||
IDTYPE cinf;
|
||||
};
|
||||
|
||||
template <class PAKRouter, class ANCSDNA, class MaterialSet, atUint32 CMDLVersion>
|
||||
bool ReadANCSToBlender(HECL::BlenderConnection& conn,
|
||||
const ANCSDNA& ancs,
|
||||
const HECL::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter,
|
||||
const typename PAKRouter::EntryType& entry,
|
||||
const HECL::ProjectPath& masterShader,
|
||||
bool force=false)
|
||||
{
|
||||
/* Extract characters first */
|
||||
std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo;
|
||||
ancs.getCharacterResInfo(chResInfo);
|
||||
for (const auto& info : chResInfo)
|
||||
{
|
||||
const NOD::DiscBase::IPartition::Node* node;
|
||||
const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, &node);
|
||||
HECL::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
if (force || cmdlPath.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||
{
|
||||
if (!conn.createBlend(cmdlPath.getAbsolutePath()))
|
||||
return false;
|
||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, CMDLVersion>
|
||||
(conn, rs, pakRouter, entry, masterShader);
|
||||
|
||||
const typename PAKRouter::EntryType* cskrE = pakRouter.lookupEntry(info.cskr);
|
||||
const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf);
|
||||
|
||||
conn.saveBlend();
|
||||
}
|
||||
}
|
||||
|
||||
/* Establish ANCS blend */
|
||||
if (!conn.createBlend(outPath.getAbsolutePath() + ".blend"))
|
||||
return false;
|
||||
HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut(true);
|
||||
|
||||
/* Get animation primitives */
|
||||
std::unordered_set<typename PAKRouter::IDType> animResInfo;
|
||||
ancs.getAnimationResInfo(animResInfo);
|
||||
for (const auto& id : animResInfo)
|
||||
{
|
||||
const typename PAKRouter::EntryType* animE = pakRouter.lookupEntry(id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _DNACOMMON_ANCS_HPP_
|
|
@ -0,0 +1,396 @@
|
|||
#include "ANIM.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
namespace DNAANIM
|
||||
{
|
||||
|
||||
size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector<Channel>& channels)
|
||||
{
|
||||
size_t bitsPerKeyFrame = 0;
|
||||
for (const Channel& chan : channels)
|
||||
{
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::ROTATION:
|
||||
case Channel::TRANSLATION:
|
||||
{
|
||||
bitsPerKeyFrame += chan.q[0];
|
||||
bitsPerKeyFrame += chan.q[1];
|
||||
bitsPerKeyFrame += chan.q[2];
|
||||
break;
|
||||
}
|
||||
case Channel::SCALE:
|
||||
{
|
||||
bitsPerKeyFrame += chan.q[0];
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return (bitsPerKeyFrame * keyFrameCount + 31) / 32 * 4;
|
||||
}
|
||||
|
||||
static inline QuantizedRot QuantizeRotation(const Value& quat, atUint32 div)
|
||||
{
|
||||
float q = M_PI / 2.0 / div;
|
||||
return
|
||||
{
|
||||
{
|
||||
atInt16(asinf(quat.v4.vec[0]) / q),
|
||||
atInt16(asinf(quat.v4.vec[1]) / q),
|
||||
atInt16(asinf(quat.v4.vec[2]) / q),
|
||||
},
|
||||
(quat.v4.vec[3] < 0) ? true : false
|
||||
};
|
||||
}
|
||||
static inline Value DequantizeRotation(const QuantizedRot& v, atUint32 div)
|
||||
{
|
||||
float q = M_PI / 2.0 / div;
|
||||
Value retval =
|
||||
{
|
||||
sinf(v.v[0] * q),
|
||||
sinf(v.v[1] * q),
|
||||
sinf(v.v[2] * q),
|
||||
};
|
||||
retval.v4.vec[3] = sqrtf(MAX((1.0 -
|
||||
(retval.v4.vec[0]*retval.v4.vec[0] +
|
||||
retval.v4.vec[1]*retval.v4.vec[1] +
|
||||
retval.v4.vec[2]*retval.v4.vec[2])), 0.0));
|
||||
retval.v4.vec[3] = v.w ? -retval.v4.vec[3] : retval.v4.vec[3];
|
||||
return retval;
|
||||
}
|
||||
|
||||
atInt16 BitstreamReader::dequantize(const atUint8* data, atUint8 q)
|
||||
{
|
||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
||||
atUint32 bitRem = m_bitCur % 32;
|
||||
|
||||
/* Fill 32 bit buffer with region containing bits */
|
||||
/* Make them least significant */
|
||||
atUint32 tempBuf = HECL::SBig(*(atUint32*)(data + byteCur)) >> bitRem;
|
||||
|
||||
/* If this shift underflows the value, buffer the next 32 bits */
|
||||
/* And tack onto shifted buffer */
|
||||
if ((bitRem + q) > 32)
|
||||
{
|
||||
atUint32 tempBuf2 = HECL::SBig(*(atUint32*)(data + byteCur + 4));
|
||||
tempBuf |= (tempBuf2 << (32 - bitRem));
|
||||
}
|
||||
|
||||
/* Pick out sign */
|
||||
atUint32 sign = (tempBuf >> (q - 1)) & 0x1;
|
||||
if (sign)
|
||||
tempBuf = ~tempBuf;
|
||||
|
||||
/* mask it (excluding sign bit) */
|
||||
atUint32 mask = (1 << (q - 1)) - 1;
|
||||
tempBuf &= mask;
|
||||
|
||||
/* Return delta value */
|
||||
m_bitCur += q;
|
||||
return atInt32(sign ? (tempBuf + 1) * -1 : tempBuf);
|
||||
}
|
||||
|
||||
std::vector<std::vector<Value>>
|
||||
BitstreamReader::read(const atUint8* data,
|
||||
size_t keyFrameCount,
|
||||
const std::vector<Channel>& channels,
|
||||
atUint32 rotDiv,
|
||||
float transMult)
|
||||
{
|
||||
m_bitCur = 0;
|
||||
std::vector<std::vector<Value>> chanKeys;
|
||||
std::vector<QuantizedValue> chanAccum;
|
||||
chanKeys.reserve(channels.size());
|
||||
chanAccum.reserve(channels.size());
|
||||
for (const Channel& chan : channels)
|
||||
{
|
||||
chanAccum.push_back({chan.i[0], chan.i[1], chan.i[2]});
|
||||
QuantizedValue& accum = chanAccum.back();
|
||||
|
||||
chanKeys.emplace_back();
|
||||
std::vector<Value>& keys = chanKeys.back();
|
||||
keys.reserve(keyFrameCount);
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::ROTATION:
|
||||
{
|
||||
QuantizedRot qr = {{chan.i[0], chan.i[1], chan.i[2]}, false};
|
||||
keys.emplace_back(DequantizeRotation(qr, rotDiv));
|
||||
break;
|
||||
}
|
||||
case Channel::TRANSLATION:
|
||||
{
|
||||
keys.push_back({chan.i[0] * transMult, chan.i[1] * transMult, chan.i[2] * transMult});
|
||||
break;
|
||||
}
|
||||
case Channel::SCALE:
|
||||
{
|
||||
keys.push_back({chan.i[0] * transMult});
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
for (size_t f=0 ; f<keyFrameCount ; ++f)
|
||||
{
|
||||
auto kit = chanKeys.begin();
|
||||
auto ait = chanAccum.begin();
|
||||
for (const Channel& chan : channels)
|
||||
{
|
||||
QuantizedValue& p = *ait;
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::ROTATION:
|
||||
{
|
||||
bool wBit = dequantize(data, 1);
|
||||
p[0] += dequantize(data, chan.q[0]);
|
||||
p[1] += dequantize(data, chan.q[1]);
|
||||
p[2] += dequantize(data, chan.q[2]);
|
||||
QuantizedRot qr = {{p[0], p[1], p[2]}, wBit};
|
||||
kit->emplace_back(DequantizeRotation(qr, rotDiv));
|
||||
break;
|
||||
}
|
||||
case Channel::TRANSLATION:
|
||||
{
|
||||
p[0] += dequantize(data, chan.q[0]);
|
||||
p[1] += dequantize(data, chan.q[1]);
|
||||
p[2] += dequantize(data, chan.q[2]);
|
||||
kit->push_back({p[0] * transMult, p[1] * transMult, p[2] * transMult});
|
||||
break;
|
||||
}
|
||||
case Channel::SCALE:
|
||||
{
|
||||
p[0] += dequantize(data, chan.q[0]);
|
||||
kit->push_back({p[0] * transMult});
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
++kit;
|
||||
++ait;
|
||||
}
|
||||
}
|
||||
return chanKeys;
|
||||
}
|
||||
|
||||
void BitstreamWriter::quantize(atUint8* data, atUint8 q, atInt16 val)
|
||||
{
|
||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
||||
atUint32 bitRem = m_bitCur % 32;
|
||||
|
||||
atUint32 masked = val & ((1 << q) - 1);
|
||||
|
||||
/* Fill 32 bit buffer with region containing bits */
|
||||
/* Make them least significant */
|
||||
*(atUint32*)(data + byteCur) =
|
||||
HECL::SBig(HECL::SBig(*(atUint32*)(data + byteCur)) | (masked << bitRem));
|
||||
|
||||
/* If this shift underflows the value, buffer the next 32 bits */
|
||||
/* And tack onto shifted buffer */
|
||||
if ((bitRem + q) > 32)
|
||||
{
|
||||
*(atUint32*)(data + byteCur + 4) =
|
||||
HECL::SBig(HECL::SBig(*(atUint32*)(data + byteCur + 4)) | (masked >> (32 - bitRem)));
|
||||
}
|
||||
|
||||
m_bitCur += q;
|
||||
}
|
||||
|
||||
std::unique_ptr<atUint8[]>
|
||||
BitstreamWriter::write(const std::vector<std::vector<Value>>& chanKeys,
|
||||
size_t keyFrameCount, std::vector<Channel>& channels,
|
||||
atUint32& rotDivOut,
|
||||
float& transMultOut,
|
||||
size_t& sizeOut)
|
||||
{
|
||||
m_bitCur = 0;
|
||||
rotDivOut = 32767; /* Normalized range of values */
|
||||
|
||||
/* Pre-pass to calculate translation multiplier */
|
||||
float maxTransDiff = 0.0;
|
||||
auto kit = chanKeys.begin();
|
||||
for (Channel& chan : channels)
|
||||
{
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::TRANSLATION:
|
||||
{
|
||||
const Value* last = &(*kit)[0];
|
||||
for (auto it=kit->begin() + 1;
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
const Value* current = &*it;
|
||||
maxTransDiff = MAX(maxTransDiff, current->v3.vec[0] - last->v3.vec[0]);
|
||||
maxTransDiff = MAX(maxTransDiff, current->v3.vec[1] - last->v3.vec[1]);
|
||||
maxTransDiff = MAX(maxTransDiff, current->v3.vec[2] - last->v3.vec[2]);
|
||||
last = current;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
++kit;
|
||||
}
|
||||
transMultOut = maxTransDiff / 32767;
|
||||
|
||||
/* Output channel inits */
|
||||
kit = chanKeys.begin();
|
||||
for (Channel& chan : channels)
|
||||
{
|
||||
chan.q[0] = 1;
|
||||
chan.q[1] = 1;
|
||||
chan.q[2] = 1;
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::ROTATION:
|
||||
{
|
||||
QuantizedRot qr = QuantizeRotation((*kit)[0], rotDivOut);
|
||||
chan.i = qr.v;
|
||||
break;
|
||||
}
|
||||
case Channel::TRANSLATION:
|
||||
{
|
||||
chan.i = {atInt16((*kit)[0].v3.vec[0] / transMultOut),
|
||||
atInt16((*kit)[0].v3.vec[1] / transMultOut),
|
||||
atInt16((*kit)[0].v3.vec[2] / transMultOut)};
|
||||
break;
|
||||
}
|
||||
case Channel::SCALE:
|
||||
{
|
||||
chan.i = {atInt16((*kit)[0].scale / transMultOut)};
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
++kit;
|
||||
}
|
||||
|
||||
/* Pre-pass to analyze quantization factors for channels */
|
||||
kit = chanKeys.begin();
|
||||
for (Channel& chan : channels)
|
||||
{
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::ROTATION:
|
||||
{
|
||||
QuantizedRot qrLast = QuantizeRotation((*kit)[0], rotDivOut);
|
||||
for (auto it=kit->begin() + 1;
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
QuantizedRot qrCur = QuantizeRotation(*it, rotDivOut);
|
||||
chan.q[0] = MAX(chan.q[0], ceilf(log2f(qrCur.v[0] - qrLast.v[0])));
|
||||
chan.q[1] = MAX(chan.q[1], ceilf(log2f(qrCur.v[1] - qrLast.v[1])));
|
||||
chan.q[2] = MAX(chan.q[2], ceilf(log2f(qrCur.v[2] - qrLast.v[2])));
|
||||
qrLast = qrCur;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Channel::TRANSLATION:
|
||||
{
|
||||
QuantizedValue last = {atInt16((*kit)[0].v3.vec[0] / transMultOut),
|
||||
atInt16((*kit)[0].v3.vec[1] / transMultOut),
|
||||
atInt16((*kit)[0].v3.vec[2] / transMultOut)};
|
||||
for (auto it=kit->begin() + 1;
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
QuantizedValue cur = {atInt16(it->v3.vec[0] / transMultOut),
|
||||
atInt16(it->v3.vec[1] / transMultOut),
|
||||
atInt16(it->v3.vec[2] / transMultOut)};
|
||||
chan.q[0] = MAX(chan.q[0], ceilf(log2f(cur[0] - last[0])));
|
||||
chan.q[1] = MAX(chan.q[1], ceilf(log2f(cur[1] - last[1])));
|
||||
chan.q[2] = MAX(chan.q[2], ceilf(log2f(cur[2] - last[2])));
|
||||
last = cur;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Channel::SCALE:
|
||||
{
|
||||
atUint16 last = (*kit)[0].scale / transMultOut;
|
||||
for (auto it=kit->begin() + 1;
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
atUint16 cur = it->scale / transMultOut;
|
||||
chan.q[0] = MAX(chan.q[0], ceilf(log2f(cur - last)));
|
||||
last = cur;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
++kit;
|
||||
}
|
||||
|
||||
/* Generate Bitstream */
|
||||
sizeOut = ComputeBitstreamSize(keyFrameCount, channels);
|
||||
atUint8* newData = new atUint8[sizeOut];
|
||||
for (size_t f=0 ; f<keyFrameCount ; ++f)
|
||||
{
|
||||
kit = chanKeys.begin();
|
||||
for (const Channel& chan : channels)
|
||||
{
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::ROTATION:
|
||||
{
|
||||
QuantizedRot qrLast = QuantizeRotation((*kit)[0], rotDivOut);
|
||||
for (auto it=kit->begin() + 1;
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
QuantizedRot qrCur = QuantizeRotation(*it, rotDivOut);
|
||||
quantize(newData, 1, qrCur.w);
|
||||
quantize(newData, chan.q[0], qrCur.v[0] - qrLast.v[0]);
|
||||
quantize(newData, chan.q[1], qrCur.v[1] - qrLast.v[0]);
|
||||
quantize(newData, chan.q[2], qrCur.v[2] - qrLast.v[0]);
|
||||
qrLast = qrCur;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Channel::TRANSLATION:
|
||||
{
|
||||
QuantizedValue last = {atInt16((*kit)[0].v3.vec[0] / transMultOut),
|
||||
atInt16((*kit)[0].v3.vec[1] / transMultOut),
|
||||
atInt16((*kit)[0].v3.vec[2] / transMultOut)};
|
||||
for (auto it=kit->begin() + 1;
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
QuantizedValue cur = {atInt16(it->v3.vec[0] / transMultOut),
|
||||
atInt16(it->v3.vec[1] / transMultOut),
|
||||
atInt16(it->v3.vec[2] / transMultOut)};
|
||||
quantize(newData, chan.q[0], cur[0] - last[0]);
|
||||
quantize(newData, chan.q[1], cur[1] - last[0]);
|
||||
quantize(newData, chan.q[2], cur[2] - last[0]);
|
||||
last = cur;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Channel::SCALE:
|
||||
{
|
||||
atUint16 last = (*kit)[0].scale / transMultOut;
|
||||
for (auto it=kit->begin() + 1;
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
atUint16 cur = it->scale / transMultOut;
|
||||
quantize(newData, chan.q[0], cur - last);
|
||||
last = cur;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
++kit;
|
||||
}
|
||||
}
|
||||
return std::unique_ptr<atUint8[]>(newData);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
#ifndef _DNACOMMON_ANIMBITSTREAM_HPP_
|
||||
#define _DNACOMMON_ANIMBITSTREAM_HPP_
|
||||
|
||||
#include <math.h>
|
||||
#include "DNACommon.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
namespace DNAANIM
|
||||
{
|
||||
|
||||
union Value
|
||||
{
|
||||
atVec3f v3;
|
||||
atVec4f v4;
|
||||
float scale;
|
||||
Value(atVec3f v) : v3(v) {}
|
||||
Value(atVec4f v) : v4(v) {}
|
||||
Value(float v) : scale(v) {}
|
||||
Value(float x, float y, float z)
|
||||
{
|
||||
v3.vec[0] = x;
|
||||
v3.vec[1] = y;
|
||||
v3.vec[2] = z;
|
||||
v4.vec[3] = 0.0;
|
||||
}
|
||||
};
|
||||
struct QuantizedValue
|
||||
{
|
||||
atInt16 v[3];
|
||||
atInt16& operator[] (size_t idx)
|
||||
{return v[idx];}
|
||||
const atInt16& operator[] (size_t idx) const
|
||||
{return v[idx];}
|
||||
};
|
||||
struct QuantizedRot
|
||||
{
|
||||
QuantizedValue v;
|
||||
bool w;
|
||||
};
|
||||
struct Channel
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
ROTATION,
|
||||
TRANSLATION,
|
||||
SCALE
|
||||
} type;
|
||||
QuantizedValue i = {};
|
||||
atUint8 q[3] = {};
|
||||
};
|
||||
|
||||
size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector<Channel>& channels);
|
||||
|
||||
class BitstreamReader
|
||||
{
|
||||
size_t m_bitCur;
|
||||
atInt16 dequantize(const atUint8* data, atUint8 q);
|
||||
public:
|
||||
std::vector<std::vector<Value>>
|
||||
read(const atUint8* data,
|
||||
size_t keyFrameCount,
|
||||
const std::vector<Channel>& channels,
|
||||
atUint32 rotDiv,
|
||||
float transMult);
|
||||
};
|
||||
|
||||
class BitstreamWriter
|
||||
{
|
||||
size_t m_bitCur;
|
||||
void quantize(atUint8* data, atUint8 q, atInt16 val);
|
||||
public:
|
||||
std::unique_ptr<atUint8[]>
|
||||
write(const std::vector<std::vector<Value>>& chanKeys,
|
||||
size_t keyFrameCount, std::vector<Channel>& channels,
|
||||
atUint32& rotDivOut,
|
||||
float& transMultOut,
|
||||
size_t& sizeOut);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _DNACOMMON_ANIMBITSTREAM_HPP_
|
|
@ -5,4 +5,6 @@ add_library(DNACommon
|
|||
${liblist}
|
||||
GX.hpp
|
||||
STRG.hpp STRG.cpp
|
||||
TXTR.hpp TXTR.cpp)
|
||||
TXTR.hpp TXTR.cpp
|
||||
ANCS.hpp
|
||||
ANIM.hpp ANIM.cpp)
|
||||
|
|
|
@ -43,10 +43,10 @@ public:
|
|||
/* PAK 32-bit Unique ID */
|
||||
class UniqueID32 : public BigYAML
|
||||
{
|
||||
uint32_t m_id = 0;
|
||||
uint32_t m_id = 0xffffffff;
|
||||
public:
|
||||
Delete expl;
|
||||
inline operator bool() const {return m_id;}
|
||||
inline operator bool() const {return m_id != 0xffffffff;}
|
||||
inline void read(Athena::io::IStreamReader& reader)
|
||||
{m_id = reader.readUint32();}
|
||||
inline void write(Athena::io::IStreamWriter& writer) const
|
||||
|
@ -70,10 +70,10 @@ public:
|
|||
/* PAK 64-bit Unique ID */
|
||||
class UniqueID64 : public BigDNA
|
||||
{
|
||||
uint64_t m_id = 0;
|
||||
uint64_t m_id = 0xffffffffffffffff;
|
||||
public:
|
||||
Delete expl;
|
||||
inline operator bool() const {return m_id;}
|
||||
inline operator bool() const {return m_id != 0xffffffffffffffff;}
|
||||
inline void read(Athena::io::IStreamReader& reader)
|
||||
{m_id = reader.readUint64();}
|
||||
inline void write(Athena::io::IStreamWriter& writer) const
|
||||
|
@ -102,8 +102,9 @@ class UniqueID128 : public BigDNA
|
|||
};
|
||||
public:
|
||||
Delete expl;
|
||||
UniqueID128() {m_id[0]=0; m_id[1]=0;}
|
||||
inline operator bool() const {return m_id[0] && m_id[1];}
|
||||
UniqueID128() {m_id[0]=0xffffffffffffffff; m_id[1]=0xffffffffffffffff;}
|
||||
inline operator bool() const
|
||||
{return m_id[0] != 0xffffffffffffffff && m_id[1] != 0xffffffffffffffff;}
|
||||
inline void read(Athena::io::IStreamReader& reader)
|
||||
{
|
||||
m_id[0] = reader.readUint64();
|
||||
|
@ -169,6 +170,70 @@ struct CaseInsensitiveCompare
|
|||
#endif
|
||||
};
|
||||
|
||||
/* Word Bitmap reader/writer */
|
||||
struct WordBitmap
|
||||
{
|
||||
std::vector<atUint32> m_words;
|
||||
size_t m_bitCount = 0;
|
||||
void read(Athena::io::IStreamReader& reader, size_t bitCount)
|
||||
{
|
||||
m_bitCount = bitCount;
|
||||
size_t wordCount = (bitCount + 31) / 32;
|
||||
m_words.clear();
|
||||
m_words.reserve(wordCount);
|
||||
for (size_t w=0 ; w<wordCount ; ++w)
|
||||
m_words.push_back(reader.readUint32());
|
||||
}
|
||||
void write(Athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
for (atUint32 word : m_words)
|
||||
writer.writeUint32(word);
|
||||
}
|
||||
size_t getBitCount() const {return m_bitCount;}
|
||||
bool getBit(size_t idx) const
|
||||
{
|
||||
size_t wordIdx = idx / 32;
|
||||
if (wordIdx >= m_words.size())
|
||||
return false;
|
||||
size_t wordCur = idx % 32;
|
||||
return (m_words[wordIdx] >> wordCur) & 0x1;
|
||||
}
|
||||
void setBit(size_t idx)
|
||||
{
|
||||
size_t wordIdx = idx / 32;
|
||||
while (wordIdx >= m_words.size())
|
||||
m_words.push_back(0);
|
||||
size_t wordCur = idx % 32;
|
||||
m_words[wordIdx] |= (1 << wordCur);
|
||||
}
|
||||
void unsetBit(size_t idx)
|
||||
{
|
||||
size_t wordIdx = idx / 32;
|
||||
while (wordIdx >= m_words.size())
|
||||
m_words.push_back(0);
|
||||
size_t wordCur = idx % 32;
|
||||
m_words[wordIdx] &= ~(1 << wordCur);
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
m_words.clear();
|
||||
}
|
||||
|
||||
class Iterator : public std::iterator<std::forward_iterator_tag, bool>
|
||||
{
|
||||
friend class WordBitmap;
|
||||
const WordBitmap& m_bmp;
|
||||
size_t m_idx = 0;
|
||||
Iterator(const WordBitmap& bmp, size_t idx) : m_bmp(bmp), m_idx(idx) {}
|
||||
public:
|
||||
Iterator& operator++() {++m_idx; return *this;}
|
||||
bool operator*() {return m_bmp.getBit(m_idx);}
|
||||
bool operator!=(const Iterator& other) const {return m_idx != other.m_idx;}
|
||||
};
|
||||
Iterator begin() const {return Iterator(*this, 0);}
|
||||
Iterator end() const {return Iterator(*this, m_bitCount);}
|
||||
};
|
||||
|
||||
/* PAK entry stream reader */
|
||||
class PAKEntryReadStream : public Athena::io::IStreamReader
|
||||
{
|
||||
|
@ -255,7 +320,7 @@ struct ResExtractor
|
|||
{
|
||||
std::function<bool(const SpecBase&, PAKEntryReadStream&, const HECL::ProjectPath&)> func_a;
|
||||
std::function<bool(const SpecBase&, PAKEntryReadStream&, const HECL::ProjectPath&, PAKRouter<PAKBRIDGE>&,
|
||||
const typename PAKBRIDGE::PAKType::Entry&)> func_b;
|
||||
const typename PAKBRIDGE::PAKType::Entry&, bool)> func_b;
|
||||
const char* fileExt;
|
||||
unsigned weight;
|
||||
};
|
||||
|
@ -270,6 +335,7 @@ public:
|
|||
using EntryType = typename PAKType::Entry;
|
||||
private:
|
||||
const SpecBase& m_dataSpec;
|
||||
const std::vector<BRIDGETYPE>* m_bridges = nullptr;
|
||||
const HECL::ProjectPath& m_gameWorking;
|
||||
const HECL::ProjectPath& m_gameCooked;
|
||||
HECL::ProjectPath m_sharedWorking;
|
||||
|
@ -287,6 +353,7 @@ public:
|
|||
m_sharedWorking(working, "Shared"), m_sharedCooked(cooked, "Shared") {}
|
||||
void build(std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress)
|
||||
{
|
||||
m_bridges = &bridges;
|
||||
m_uniqueEntries.clear();
|
||||
m_sharedEntries.clear();
|
||||
size_t count = 0;
|
||||
|
@ -362,6 +429,11 @@ public:
|
|||
return HECL::ProjectPath();
|
||||
}
|
||||
|
||||
HECL::ProjectPath getWorking(const typename BRIDGETYPE::PAKType::Entry* entry) const
|
||||
{
|
||||
return getWorking(entry, BRIDGETYPE::LookupExtractor(*entry));
|
||||
}
|
||||
|
||||
HECL::ProjectPath getCooked(const typename BRIDGETYPE::PAKType::Entry* entry) const
|
||||
{
|
||||
if (!m_pak)
|
||||
|
@ -457,7 +529,7 @@ public:
|
|||
if (force || working.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||
{
|
||||
PAKEntryReadStream s = item.second->beginReadStream(*m_node);
|
||||
extractor.func_b(m_dataSpec, s, working, *this, *item.second);
|
||||
extractor.func_b(m_dataSpec, s, working, *this, *item.second, force);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -467,6 +539,38 @@ public:
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
const typename BRIDGETYPE::PAKType::Entry* lookupEntry(const typename BRIDGETYPE::PAKType::IDType& entry,
|
||||
const NOD::DiscBase::IPartition::Node** nodeOut=nullptr)
|
||||
{
|
||||
if (!m_bridges)
|
||||
LogDNACommon.report(LogVisor::FatalError,
|
||||
"PAKRouter::build() must be called before PAKRouter::lookupEntry()");
|
||||
if (m_pak)
|
||||
{
|
||||
const typename BRIDGETYPE::PAKType::Entry* ent = m_pak->lookupEntry(entry);
|
||||
if (ent)
|
||||
{
|
||||
if (nodeOut)
|
||||
*nodeOut = m_node;
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
for (const BRIDGETYPE& bridge : *m_bridges)
|
||||
{
|
||||
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
|
||||
const typename BRIDGETYPE::PAKType::Entry* ent = pak.lookupEntry(entry);
|
||||
if (ent)
|
||||
{
|
||||
if (nodeOut)
|
||||
*nodeOut = &bridge.getNode();
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
if (nodeOut)
|
||||
*nodeOut = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/* Resource cooker function */
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
#ifndef _DNAMP1_ANCS_HPP_
|
||||
#define _DNAMP1_ANCS_HPP_
|
||||
|
||||
#include <unordered_set>
|
||||
#include "../DNACommon/DNACommon.hpp"
|
||||
#include "../DNACommon/ANCS.hpp"
|
||||
#include "CMDLMaterials.hpp"
|
||||
#include "BlenderConnection.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
|
@ -159,6 +163,7 @@ struct ANCS : BigYAML
|
|||
const char* m_typeStr;
|
||||
IMetaAnim(Type type, const char* typeStr)
|
||||
: m_type(type), m_typeStr(typeStr) {}
|
||||
virtual void gatherPrimitives(std::unordered_set<UniqueID32>& out)=0;
|
||||
};
|
||||
struct MetaAnimFactory : BigYAML
|
||||
{
|
||||
|
@ -175,6 +180,11 @@ struct ANCS : BigYAML
|
|||
String<-1> animName;
|
||||
Value<float> unk1;
|
||||
Value<atUint32> unk2;
|
||||
|
||||
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
|
||||
{
|
||||
out.insert(animId);
|
||||
}
|
||||
};
|
||||
struct MetaAnimBlend : IMetaAnim
|
||||
{
|
||||
|
@ -184,6 +194,12 @@ struct ANCS : BigYAML
|
|||
MetaAnimFactory animB;
|
||||
Value<float> unkFloat;
|
||||
Value<atUint8> unk;
|
||||
|
||||
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
|
||||
{
|
||||
animA.m_anim->gatherPrimitives(out);
|
||||
animB.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
};
|
||||
struct MetaAnimPhaseBlend : IMetaAnim
|
||||
{
|
||||
|
@ -193,6 +209,12 @@ struct ANCS : BigYAML
|
|||
MetaAnimFactory animB;
|
||||
Value<float> unkFloat;
|
||||
Value<atUint8> unk;
|
||||
|
||||
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
|
||||
{
|
||||
animA.m_anim->gatherPrimitives(out);
|
||||
animB.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
};
|
||||
struct MetaAnimRandom : IMetaAnim
|
||||
{
|
||||
|
@ -206,6 +228,12 @@ struct ANCS : BigYAML
|
|||
Value<atUint32> probability;
|
||||
};
|
||||
Vector<Child, DNA_COUNT(animCount)> children;
|
||||
|
||||
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
|
||||
{
|
||||
for (const auto& child : children)
|
||||
child.anim.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
};
|
||||
struct MetaAnimSequence : IMetaAnim
|
||||
{
|
||||
|
@ -213,6 +241,12 @@ struct ANCS : BigYAML
|
|||
DECL_YAML
|
||||
Value<atUint32> animCount;
|
||||
Vector<MetaAnimFactory, DNA_COUNT(animCount)> children;
|
||||
|
||||
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
|
||||
{
|
||||
for (const auto& child : children)
|
||||
child.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
};
|
||||
|
||||
struct Animation : BigYAML
|
||||
|
@ -310,14 +344,45 @@ struct ANCS : BigYAML
|
|||
std::vector<AnimationResources> animResources;
|
||||
} animationSet;
|
||||
|
||||
static bool Extract(const SpecBase&, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
|
||||
void getCharacterResInfo(std::vector<DNAANCS::CharacterResInfo<UniqueID32>>& out) const
|
||||
{
|
||||
out.clear();
|
||||
out.reserve(characterSet.characters.size());
|
||||
for (const CharacterSet::CharacterInfo& ci : characterSet.characters)
|
||||
{
|
||||
out.emplace_back();
|
||||
DNAANCS::CharacterResInfo<UniqueID32>& chOut = out.back();
|
||||
chOut.name = ci.name;
|
||||
chOut.cmdl = ci.cmdl;
|
||||
chOut.cskr = ci.cskr;
|
||||
chOut.cinf = ci.cinf;
|
||||
}
|
||||
}
|
||||
|
||||
void getAnimationResInfo(std::unordered_set<UniqueID32>& out) const
|
||||
{
|
||||
out.clear();
|
||||
for (const AnimationSet::Animation& ai : animationSet.animations)
|
||||
ai.metaAnim.m_anim->gatherPrimitives(out);
|
||||
}
|
||||
|
||||
static bool Extract(const SpecBase& dataSpec,
|
||||
PAKEntryReadStream& rs,
|
||||
const HECL::ProjectPath& outPath,
|
||||
PAKRouter<PAKBridge>& pakRouter,
|
||||
const PAK::Entry& entry,
|
||||
bool force)
|
||||
{
|
||||
ANCS ancs;
|
||||
ancs.read(rs);
|
||||
FILE* fp = HECL::Fopen(outPath.getAbsolutePath().c_str(), _S("wb"));
|
||||
FILE* fp = HECL::Fopen((outPath.getAbsolutePath() + ".yaml").c_str(), _S("wb"));
|
||||
ancs.toYAMLFile(fp);
|
||||
fclose(fp);
|
||||
return true;
|
||||
|
||||
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
||||
DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, ANCS, MaterialSet, 2>
|
||||
(conn, ancs, outPath, pakRouter, entry, dataSpec.getMasterShaderPath(), force);
|
||||
return conn.saveBlend();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
#include "ANIM.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
namespace DNAMP1
|
||||
{
|
||||
|
||||
void ANIM::ANIM0::read(Athena::io::IStreamReader& reader)
|
||||
{
|
||||
Header head;
|
||||
head.read(reader);
|
||||
mainInterval = head.interval;
|
||||
|
||||
times.clear();
|
||||
times.reserve(head.keyCount);
|
||||
float timeAccum = 0.0;
|
||||
for (size_t k=0 ; k<head.keyCount ; ++k)
|
||||
{
|
||||
times.push_back(timeAccum);
|
||||
timeAccum += head.interval;
|
||||
}
|
||||
|
||||
std::map<atUint8, atUint32> boneMap;
|
||||
for (size_t b=0 ; b<head.boneSlotCount ; ++b)
|
||||
{
|
||||
atUint8 idx = reader.readUByte();
|
||||
if (idx == 0xff)
|
||||
continue;
|
||||
boneMap[idx] = b;
|
||||
}
|
||||
|
||||
atUint32 boneCount = reader.readUint32();
|
||||
bones.clear();
|
||||
bones.reserve(boneCount);
|
||||
channels.clear();
|
||||
for (size_t b=0 ; b<boneCount ; ++b)
|
||||
{
|
||||
bones.emplace_back(boneMap[b], false);
|
||||
atUint8 idx = reader.readUByte();
|
||||
channels.emplace_back();
|
||||
DNAANIM::Channel& chan = channels.back();
|
||||
chan.type = DNAANIM::Channel::ROTATION;
|
||||
if (idx != 0xff)
|
||||
{
|
||||
bones.back().second = true;
|
||||
channels.emplace_back();
|
||||
DNAANIM::Channel& chan = channels.back();
|
||||
chan.type = DNAANIM::Channel::TRANSLATION;
|
||||
}
|
||||
}
|
||||
|
||||
reader.readUint32();
|
||||
chanKeys.clear();
|
||||
chanKeys.reserve(channels.size());
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
{
|
||||
chanKeys.emplace_back();
|
||||
std::vector<DNAANIM::Value>& keys = chanKeys.back();
|
||||
for (size_t k=0 ; k<head.keyCount ; ++k)
|
||||
keys.emplace_back(reader.readVec4f());
|
||||
|
||||
if (bone.second)
|
||||
chanKeys.emplace_back();
|
||||
}
|
||||
|
||||
reader.readUint32();
|
||||
auto kit = chanKeys.begin();
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
{
|
||||
++kit;
|
||||
if (bone.second)
|
||||
{
|
||||
std::vector<DNAANIM::Value>& keys = *kit++;
|
||||
for (size_t k=0 ; k<head.keyCount ; ++k)
|
||||
keys.emplace_back(reader.readVec3f());
|
||||
}
|
||||
}
|
||||
|
||||
evnt.read(reader);
|
||||
}
|
||||
|
||||
void ANIM::ANIM0::write(Athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
Header head;
|
||||
head.unk0 = 0;
|
||||
head.unk1 = 0;
|
||||
head.unk2 = 0;
|
||||
head.keyCount = times.size();
|
||||
head.duration = head.keyCount * mainInterval;
|
||||
head.interval = mainInterval;
|
||||
|
||||
atUint32 maxId = 0;
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
maxId = MAX(maxId, bone.first);
|
||||
head.boneSlotCount = maxId + 1;
|
||||
head.write(writer);
|
||||
|
||||
for (size_t s=0 ; s<head.boneSlotCount ; ++s)
|
||||
{
|
||||
size_t boneIdx = 0;
|
||||
bool found = false;
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
{
|
||||
if (s == bone.first)
|
||||
{
|
||||
writer.writeUByte(boneIdx);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
++boneIdx;
|
||||
}
|
||||
if (!found)
|
||||
writer.writeUByte(0xff);
|
||||
}
|
||||
|
||||
writer.writeUint32(bones.size());
|
||||
size_t boneIdx = 0;
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
{
|
||||
if (bone.second)
|
||||
writer.writeUByte(boneIdx);
|
||||
else
|
||||
writer.writeUByte(0xff);
|
||||
++boneIdx;
|
||||
}
|
||||
|
||||
writer.writeUint32(bones.size() * head.keyCount);
|
||||
auto cit = chanKeys.begin();
|
||||
atUint32 transKeyCount = 0;
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
{
|
||||
const std::vector<DNAANIM::Value>& keys = *cit++;
|
||||
auto kit = keys.begin();
|
||||
for (size_t k=0 ; k<head.keyCount ; ++k)
|
||||
writer.writeVec4f((*kit++).v4);
|
||||
if (bone.second)
|
||||
{
|
||||
transKeyCount += head.keyCount;
|
||||
++cit;
|
||||
}
|
||||
}
|
||||
|
||||
writer.writeUint32(transKeyCount);
|
||||
cit = chanKeys.begin();
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
{
|
||||
++cit;
|
||||
if (bone.second)
|
||||
{
|
||||
const std::vector<DNAANIM::Value>& keys = *cit++;
|
||||
auto kit = keys.begin();
|
||||
for (size_t k=0 ; k<head.keyCount ; ++k)
|
||||
writer.writeVec3f((*kit++).v3);
|
||||
}
|
||||
}
|
||||
|
||||
evnt.write(writer);
|
||||
}
|
||||
|
||||
void ANIM::ANIM2::read(Athena::io::IStreamReader& reader)
|
||||
{
|
||||
Header head;
|
||||
head.read(reader);
|
||||
evnt = head.evnt;
|
||||
mainInterval = head.interval;
|
||||
|
||||
WordBitmap keyBmp;
|
||||
keyBmp.read(reader, head.keyBitmapBitCount);
|
||||
times.clear();
|
||||
float timeAccum = 0.0;
|
||||
for (bool bit : keyBmp)
|
||||
{
|
||||
if (bit)
|
||||
times.push_back(timeAccum);
|
||||
timeAccum += head.interval;
|
||||
}
|
||||
|
||||
bones.clear();
|
||||
bones.reserve(head.boneChannelCount);
|
||||
channels.clear();
|
||||
channels.reserve(head.boneChannelCount);
|
||||
size_t keyframeCount = 0;
|
||||
for (size_t b=0 ; b<head.boneChannelCount ; ++b)
|
||||
{
|
||||
ChannelDesc desc;
|
||||
desc.read(reader);
|
||||
bones.emplace_back(desc.id, desc.keyCount2);
|
||||
|
||||
if (desc.keyCount1)
|
||||
{
|
||||
channels.emplace_back();
|
||||
DNAANIM::Channel& chan = channels.back();
|
||||
chan.type = DNAANIM::Channel::ROTATION;
|
||||
chan.i[0] = desc.initRX;
|
||||
chan.q[0] = desc.qRX;
|
||||
chan.i[1] = desc.initRY;
|
||||
chan.q[1] = desc.qRY;
|
||||
chan.i[2] = desc.initRZ;
|
||||
chan.q[2] = desc.qRZ;
|
||||
}
|
||||
keyframeCount = MAX(keyframeCount, desc.keyCount1);
|
||||
|
||||
if (desc.keyCount2)
|
||||
{
|
||||
channels.emplace_back();
|
||||
DNAANIM::Channel& chan = channels.back();
|
||||
chan.type = DNAANIM::Channel::TRANSLATION;
|
||||
chan.i[0] = desc.initTX;
|
||||
chan.q[0] = desc.qTX;
|
||||
chan.i[1] = desc.initTY;
|
||||
chan.q[1] = desc.qTY;
|
||||
chan.i[2] = desc.initTZ;
|
||||
chan.q[2] = desc.qTZ;
|
||||
}
|
||||
}
|
||||
|
||||
size_t bsSize = DNAANIM::ComputeBitstreamSize(keyframeCount, channels);
|
||||
std::unique_ptr<atUint8[]> bsData = reader.readUBytes(bsSize);
|
||||
DNAANIM::BitstreamReader bsReader;
|
||||
chanKeys = bsReader.read(bsData.get(), keyframeCount, channels, head.rotDiv, head.translationMult);
|
||||
}
|
||||
|
||||
void ANIM::ANIM2::write(Athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
Header head;
|
||||
head.evnt = evnt;
|
||||
head.unk0 = 1;
|
||||
head.interval = mainInterval;
|
||||
head.unk1 = 3;
|
||||
head.unk2 = 0;
|
||||
head.unk3 = 1;
|
||||
|
||||
WordBitmap keyBmp;
|
||||
size_t frameCount = 0;
|
||||
for (float time : times)
|
||||
{
|
||||
size_t frameIdx = time / mainInterval;
|
||||
while (keyBmp.getBit(frameIdx))
|
||||
++frameIdx;
|
||||
keyBmp.setBit(frameIdx);
|
||||
frameCount = frameIdx + 1;
|
||||
}
|
||||
head.keyBitmapBitCount = frameCount;
|
||||
head.duration = frameCount * mainInterval;
|
||||
head.boneChannelCount = bones.size();
|
||||
|
||||
size_t keyframeCount = times.size();
|
||||
std::vector<DNAANIM::Channel> qChannels = channels;
|
||||
DNAANIM::BitstreamWriter bsWriter;
|
||||
size_t bsSize;
|
||||
std::unique_ptr<atUint8[]> bsData = bsWriter.write(chanKeys, keyframeCount, qChannels,
|
||||
head.rotDiv, head.translationMult, bsSize);
|
||||
|
||||
/* TODO: Figure out proper scratch size computation */
|
||||
head.scratchSize = keyframeCount * channels.size() * 16;
|
||||
|
||||
head.write(writer);
|
||||
keyBmp.write(writer);
|
||||
auto cit = qChannels.begin();
|
||||
for (const std::pair<atUint32, bool>& bone : bones)
|
||||
{
|
||||
ChannelDesc desc;
|
||||
desc.id = bone.first;
|
||||
DNAANIM::Channel& chan = *cit++;
|
||||
desc.keyCount1 = keyframeCount;
|
||||
desc.initRX = chan.i[0];
|
||||
desc.qRX = chan.q[0];
|
||||
desc.initRY = chan.i[1];
|
||||
desc.qRY = chan.q[1];
|
||||
desc.initRZ = chan.i[2];
|
||||
desc.qRZ = chan.q[2];
|
||||
if (bone.second)
|
||||
{
|
||||
DNAANIM::Channel& chan = *cit++;
|
||||
desc.keyCount2 = keyframeCount;
|
||||
desc.initTX = chan.i[0];
|
||||
desc.qTX = chan.q[0];
|
||||
desc.initTY = chan.i[1];
|
||||
desc.qTY = chan.q[1];
|
||||
desc.initTZ = chan.i[2];
|
||||
desc.qTZ = chan.q[2];
|
||||
}
|
||||
desc.write(writer);
|
||||
}
|
||||
|
||||
writer.writeUBytes(bsData.get(), bsSize);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
#ifndef _DNAMP1_ANIM_HPP_
|
||||
#define _DNAMP1_ANIM_HPP_
|
||||
|
||||
#include "DNAMP1.hpp"
|
||||
#include "../DNACommon/ANIM.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
namespace DNAMP1
|
||||
{
|
||||
|
||||
struct ANIM : BigDNA
|
||||
{
|
||||
Delete expl;
|
||||
|
||||
struct IANIM : BigDNA
|
||||
{
|
||||
Delete expl;
|
||||
atUint32 m_version;
|
||||
IANIM(atUint32 version) : m_version(version) {}
|
||||
|
||||
std::vector<std::pair<atUint32, bool>> bones;
|
||||
std::vector<float> times;
|
||||
std::vector<DNAANIM::Channel> channels;
|
||||
std::vector<std::vector<DNAANIM::Value>> chanKeys;
|
||||
float mainInterval = 0.0;
|
||||
UniqueID32 evnt;
|
||||
};
|
||||
|
||||
struct ANIM0 : IANIM
|
||||
{
|
||||
DECL_EXPLICIT_DNA
|
||||
ANIM0() : IANIM(0) {}
|
||||
|
||||
struct Header : BigDNA
|
||||
{
|
||||
DECL_DNA
|
||||
Value<float> duration;
|
||||
Value<atUint32> unk0;
|
||||
Value<float> interval;
|
||||
Value<atUint32> unk1;
|
||||
Value<atUint32> boneSlotCount;
|
||||
Value<atUint32> unk2;
|
||||
Value<atUint32> keyCount;
|
||||
};
|
||||
};
|
||||
|
||||
struct ANIM2 : IANIM
|
||||
{
|
||||
DECL_EXPLICIT_DNA
|
||||
ANIM2() : IANIM(2) {}
|
||||
|
||||
struct Header : BigDNA
|
||||
{
|
||||
DECL_DNA
|
||||
Value<atUint32> scratchSize;
|
||||
UniqueID32 evnt;
|
||||
Value<atUint32> unk0;
|
||||
Value<float> duration;
|
||||
Value<float> interval;
|
||||
Value<atUint32> unk1;
|
||||
Value<atUint32> unk2;
|
||||
Value<atUint32> rotDiv;
|
||||
Value<float> translationMult;
|
||||
Value<atUint32> boneChannelCount;
|
||||
Value<atUint32> unk3;
|
||||
Value<atUint32> keyBitmapBitCount;
|
||||
};
|
||||
|
||||
struct ChannelDesc : BigDNA
|
||||
{
|
||||
Delete expl;
|
||||
Value<atUint32> id = 0;
|
||||
Value<atUint16> keyCount1 = 0;
|
||||
Value<atUint16> initRX = 0;
|
||||
Value<atUint8> qRX = 0;
|
||||
Value<atUint16> initRY = 0;
|
||||
Value<atUint8> qRY = 0;
|
||||
Value<atUint16> initRZ = 0;
|
||||
Value<atUint8> qRZ = 0;
|
||||
Value<atUint16> keyCount2 = 0;
|
||||
Value<atUint16> initTX = 0;
|
||||
Value<atUint8> qTX = 0;
|
||||
Value<atUint16> initTY = 0;
|
||||
Value<atUint8> qTY = 0;
|
||||
Value<atUint16> initTZ = 0;
|
||||
Value<atUint8> qTZ = 0;
|
||||
|
||||
void read(Athena::io::IStreamReader& reader)
|
||||
{
|
||||
id = reader.readUint32();
|
||||
keyCount1 = reader.readUint16();
|
||||
initRX = reader.readUint16();
|
||||
qRX = reader.readUByte();
|
||||
initRY = reader.readUint16();
|
||||
qRY = reader.readUByte();
|
||||
initRZ = reader.readUint16();
|
||||
qRZ = reader.readUByte();
|
||||
keyCount2 = reader.readUint16();
|
||||
if (keyCount2)
|
||||
{
|
||||
initTX = reader.readUint16();
|
||||
qTX = reader.readUByte();
|
||||
initTY = reader.readUint16();
|
||||
qTY = reader.readUByte();
|
||||
initTZ = reader.readUint16();
|
||||
qTZ = reader.readUByte();
|
||||
}
|
||||
}
|
||||
void write(Athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
writer.writeUint32(id);
|
||||
writer.writeUint16(keyCount1);
|
||||
writer.writeUint16(initRX);
|
||||
writer.writeUByte(qRX);
|
||||
writer.writeUint16(initRY);
|
||||
writer.writeUByte(qRY);
|
||||
writer.writeUint16(initRZ);
|
||||
writer.writeUByte(qRZ);
|
||||
writer.writeUint16(keyCount2);
|
||||
if (keyCount2)
|
||||
{
|
||||
writer.writeUint16(initTX);
|
||||
writer.writeUByte(qTX);
|
||||
writer.writeUint16(initTY);
|
||||
writer.writeUByte(qTY);
|
||||
writer.writeUint16(initTZ);
|
||||
writer.writeUByte(qTZ);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<IANIM> m_anim;
|
||||
void read(Athena::io::IStreamReader& reader)
|
||||
{
|
||||
atUint32 version = reader.readUint32();
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
m_anim.reset(new struct ANIM0);
|
||||
m_anim->read(reader);
|
||||
break;
|
||||
case 2:
|
||||
m_anim.reset(new struct ANIM2);
|
||||
m_anim->read(reader);
|
||||
break;
|
||||
default:
|
||||
Log.report(LogVisor::Error, "unrecognized ANIM version");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void write(Athena::io::IStreamWriter& writer) const
|
||||
{
|
||||
writer.writeUint32(m_anim->m_version);
|
||||
m_anim->write(writer);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _DNAMP1_ANIM_HPP_
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef _DNAMP1_CINF_HPP_
|
||||
#define _DNAMP1_CINF_HPP_
|
||||
|
||||
#include "../DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
namespace DNAMP1
|
||||
{
|
||||
|
||||
struct CINF : BigDNA
|
||||
{
|
||||
DECL_DNA
|
||||
Value<atUint32> boneCount;
|
||||
struct Bone : BigDNA
|
||||
{
|
||||
DECL_DNA
|
||||
Value<atUint32> id;
|
||||
Value<atUint32> parentId;
|
||||
Value<atVec3f> origin;
|
||||
Value<atUint32> linkedCount;
|
||||
Vector<atUint32, DNA_COUNT(linkedCount)> linked;
|
||||
};
|
||||
Vector<Bone, DNA_COUNT(boneCount)> bones;
|
||||
|
||||
Value<atUint32> boneIdCount;
|
||||
Vector<atUint32, DNA_COUNT(boneIdCount)> boneIds;
|
||||
|
||||
Value<atUint32> nameCount;
|
||||
struct Name : BigDNA
|
||||
{
|
||||
DECL_DNA
|
||||
String<-1> name;
|
||||
Value<atUint32> boneId;
|
||||
};
|
||||
Vector<Name, DNA_COUNT(nameCount)> names;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _DNAMP1_CINF_HPP_
|
|
@ -17,7 +17,8 @@ struct CMDL
|
|||
PAKEntryReadStream& rs,
|
||||
const HECL::ProjectPath& outPath,
|
||||
PAKRouter<PAKBridge>& pakRouter,
|
||||
const PAK::Entry& entry)
|
||||
const PAK::Entry& entry,
|
||||
bool force)
|
||||
{
|
||||
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
||||
if (!conn.createBlend(outPath.getAbsolutePath()))
|
||||
|
|
|
@ -2,6 +2,9 @@ make_dnalist(liblist
|
|||
PAK
|
||||
MLVL
|
||||
ANCS
|
||||
ANIM
|
||||
CINF
|
||||
CSKR
|
||||
MAPA
|
||||
CMDLMaterials)
|
||||
add_library(DNAMP1
|
||||
|
@ -10,5 +13,6 @@ add_library(DNAMP1
|
|||
PAK.cpp
|
||||
STRG.hpp STRG.cpp
|
||||
ANCS.cpp
|
||||
ANIM.cpp
|
||||
CMDL.hpp
|
||||
CMDLMaterials.cpp)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef _DNAMP1_CSKR_HPP_
|
||||
#define _DNAMP1_CSKR_HPP_
|
||||
|
||||
#include "../DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
namespace DNAMP1
|
||||
{
|
||||
|
||||
struct CSKR : BigDNA
|
||||
{
|
||||
DECL_DNA
|
||||
Value<atUint32> skinningRuleCount;
|
||||
struct SkinningRule : BigDNA
|
||||
{
|
||||
DECL_DNA
|
||||
Value<atUint32> weightCount;
|
||||
struct Weight : BigDNA
|
||||
{
|
||||
DECL_DNA
|
||||
Value<atUint32> boneId;
|
||||
Value<float> weight;
|
||||
};
|
||||
Vector<Weight, DNA_COUNT(weightCount)> weights;
|
||||
Value<atUint32> vertCount;
|
||||
};
|
||||
Vector<SkinningRule, DNA_COUNT(skinningRuleCount)> skinningRules;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _DNAMP1_CSKR_HPP_
|
|
@ -183,7 +183,7 @@ ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
|||
case SBIG('CMDL'):
|
||||
return {nullptr, CMDL::Extract, ".blend", 1};
|
||||
case SBIG('ANCS'):
|
||||
return {ANCS::Extract, nullptr, ".yaml"};
|
||||
return {nullptr, ANCS::Extract, nullptr};
|
||||
case SBIG('MLVL'):
|
||||
return {MLVL::Extract, nullptr, ".yaml"};
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ struct CMDL
|
|||
PAKEntryReadStream& rs,
|
||||
const HECL::ProjectPath& outPath,
|
||||
PAKRouter<PAKBridge>& pakRouter,
|
||||
const DNAMP1::PAK::Entry& entry)
|
||||
const DNAMP1::PAK::Entry& entry,
|
||||
bool force)
|
||||
{
|
||||
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
||||
if (!conn.createBlend(outPath.getAbsolutePath()))
|
||||
|
|
|
@ -17,7 +17,8 @@ struct CMDL
|
|||
PAKEntryReadStream& rs,
|
||||
const HECL::ProjectPath& outPath,
|
||||
PAKRouter<PAKBridge>& pakRouter,
|
||||
const PAK::Entry& entry)
|
||||
const PAK::Entry& entry,
|
||||
bool force)
|
||||
{
|
||||
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
||||
if (!conn.createBlend(outPath.getAbsolutePath()))
|
||||
|
|
Loading…
Reference in New Issue