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}
|
${liblist}
|
||||||
GX.hpp
|
GX.hpp
|
||||||
STRG.hpp STRG.cpp
|
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 */
|
/* PAK 32-bit Unique ID */
|
||||||
class UniqueID32 : public BigYAML
|
class UniqueID32 : public BigYAML
|
||||||
{
|
{
|
||||||
uint32_t m_id = 0;
|
uint32_t m_id = 0xffffffff;
|
||||||
public:
|
public:
|
||||||
Delete expl;
|
Delete expl;
|
||||||
inline operator bool() const {return m_id;}
|
inline operator bool() const {return m_id != 0xffffffff;}
|
||||||
inline void read(Athena::io::IStreamReader& reader)
|
inline void read(Athena::io::IStreamReader& reader)
|
||||||
{m_id = reader.readUint32();}
|
{m_id = reader.readUint32();}
|
||||||
inline void write(Athena::io::IStreamWriter& writer) const
|
inline void write(Athena::io::IStreamWriter& writer) const
|
||||||
|
@ -70,10 +70,10 @@ public:
|
||||||
/* PAK 64-bit Unique ID */
|
/* PAK 64-bit Unique ID */
|
||||||
class UniqueID64 : public BigDNA
|
class UniqueID64 : public BigDNA
|
||||||
{
|
{
|
||||||
uint64_t m_id = 0;
|
uint64_t m_id = 0xffffffffffffffff;
|
||||||
public:
|
public:
|
||||||
Delete expl;
|
Delete expl;
|
||||||
inline operator bool() const {return m_id;}
|
inline operator bool() const {return m_id != 0xffffffffffffffff;}
|
||||||
inline void read(Athena::io::IStreamReader& reader)
|
inline void read(Athena::io::IStreamReader& reader)
|
||||||
{m_id = reader.readUint64();}
|
{m_id = reader.readUint64();}
|
||||||
inline void write(Athena::io::IStreamWriter& writer) const
|
inline void write(Athena::io::IStreamWriter& writer) const
|
||||||
|
@ -102,8 +102,9 @@ class UniqueID128 : public BigDNA
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
Delete expl;
|
Delete expl;
|
||||||
UniqueID128() {m_id[0]=0; m_id[1]=0;}
|
UniqueID128() {m_id[0]=0xffffffffffffffff; m_id[1]=0xffffffffffffffff;}
|
||||||
inline operator bool() const {return m_id[0] && m_id[1];}
|
inline operator bool() const
|
||||||
|
{return m_id[0] != 0xffffffffffffffff && m_id[1] != 0xffffffffffffffff;}
|
||||||
inline void read(Athena::io::IStreamReader& reader)
|
inline void read(Athena::io::IStreamReader& reader)
|
||||||
{
|
{
|
||||||
m_id[0] = reader.readUint64();
|
m_id[0] = reader.readUint64();
|
||||||
|
@ -169,6 +170,70 @@ struct CaseInsensitiveCompare
|
||||||
#endif
|
#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 */
|
/* PAK entry stream reader */
|
||||||
class PAKEntryReadStream : public Athena::io::IStreamReader
|
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&)> func_a;
|
||||||
std::function<bool(const SpecBase&, PAKEntryReadStream&, const HECL::ProjectPath&, PAKRouter<PAKBRIDGE>&,
|
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;
|
const char* fileExt;
|
||||||
unsigned weight;
|
unsigned weight;
|
||||||
};
|
};
|
||||||
|
@ -270,6 +335,7 @@ public:
|
||||||
using EntryType = typename PAKType::Entry;
|
using EntryType = typename PAKType::Entry;
|
||||||
private:
|
private:
|
||||||
const SpecBase& m_dataSpec;
|
const SpecBase& m_dataSpec;
|
||||||
|
const std::vector<BRIDGETYPE>* m_bridges = nullptr;
|
||||||
const HECL::ProjectPath& m_gameWorking;
|
const HECL::ProjectPath& m_gameWorking;
|
||||||
const HECL::ProjectPath& m_gameCooked;
|
const HECL::ProjectPath& m_gameCooked;
|
||||||
HECL::ProjectPath m_sharedWorking;
|
HECL::ProjectPath m_sharedWorking;
|
||||||
|
@ -287,6 +353,7 @@ public:
|
||||||
m_sharedWorking(working, "Shared"), m_sharedCooked(cooked, "Shared") {}
|
m_sharedWorking(working, "Shared"), m_sharedCooked(cooked, "Shared") {}
|
||||||
void build(std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress)
|
void build(std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress)
|
||||||
{
|
{
|
||||||
|
m_bridges = &bridges;
|
||||||
m_uniqueEntries.clear();
|
m_uniqueEntries.clear();
|
||||||
m_sharedEntries.clear();
|
m_sharedEntries.clear();
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
@ -362,6 +429,11 @@ public:
|
||||||
return HECL::ProjectPath();
|
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
|
HECL::ProjectPath getCooked(const typename BRIDGETYPE::PAKType::Entry* entry) const
|
||||||
{
|
{
|
||||||
if (!m_pak)
|
if (!m_pak)
|
||||||
|
@ -457,7 +529,7 @@ public:
|
||||||
if (force || working.getPathType() == HECL::ProjectPath::PT_NONE)
|
if (force || working.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||||
{
|
{
|
||||||
PAKEntryReadStream s = item.second->beginReadStream(*m_node);
|
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;
|
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 */
|
/* Resource cooker function */
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#ifndef _DNAMP1_ANCS_HPP_
|
#ifndef _DNAMP1_ANCS_HPP_
|
||||||
#define _DNAMP1_ANCS_HPP_
|
#define _DNAMP1_ANCS_HPP_
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
#include "../DNACommon/DNACommon.hpp"
|
#include "../DNACommon/DNACommon.hpp"
|
||||||
|
#include "../DNACommon/ANCS.hpp"
|
||||||
|
#include "CMDLMaterials.hpp"
|
||||||
|
#include "BlenderConnection.hpp"
|
||||||
|
|
||||||
namespace Retro
|
namespace Retro
|
||||||
{
|
{
|
||||||
|
@ -159,6 +163,7 @@ struct ANCS : BigYAML
|
||||||
const char* m_typeStr;
|
const char* m_typeStr;
|
||||||
IMetaAnim(Type type, const char* typeStr)
|
IMetaAnim(Type type, const char* typeStr)
|
||||||
: m_type(type), m_typeStr(typeStr) {}
|
: m_type(type), m_typeStr(typeStr) {}
|
||||||
|
virtual void gatherPrimitives(std::unordered_set<UniqueID32>& out)=0;
|
||||||
};
|
};
|
||||||
struct MetaAnimFactory : BigYAML
|
struct MetaAnimFactory : BigYAML
|
||||||
{
|
{
|
||||||
|
@ -175,6 +180,11 @@ struct ANCS : BigYAML
|
||||||
String<-1> animName;
|
String<-1> animName;
|
||||||
Value<float> unk1;
|
Value<float> unk1;
|
||||||
Value<atUint32> unk2;
|
Value<atUint32> unk2;
|
||||||
|
|
||||||
|
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
|
||||||
|
{
|
||||||
|
out.insert(animId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct MetaAnimBlend : IMetaAnim
|
struct MetaAnimBlend : IMetaAnim
|
||||||
{
|
{
|
||||||
|
@ -184,6 +194,12 @@ struct ANCS : BigYAML
|
||||||
MetaAnimFactory animB;
|
MetaAnimFactory animB;
|
||||||
Value<float> unkFloat;
|
Value<float> unkFloat;
|
||||||
Value<atUint8> unk;
|
Value<atUint8> unk;
|
||||||
|
|
||||||
|
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
|
||||||
|
{
|
||||||
|
animA.m_anim->gatherPrimitives(out);
|
||||||
|
animB.m_anim->gatherPrimitives(out);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct MetaAnimPhaseBlend : IMetaAnim
|
struct MetaAnimPhaseBlend : IMetaAnim
|
||||||
{
|
{
|
||||||
|
@ -193,6 +209,12 @@ struct ANCS : BigYAML
|
||||||
MetaAnimFactory animB;
|
MetaAnimFactory animB;
|
||||||
Value<float> unkFloat;
|
Value<float> unkFloat;
|
||||||
Value<atUint8> unk;
|
Value<atUint8> unk;
|
||||||
|
|
||||||
|
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
|
||||||
|
{
|
||||||
|
animA.m_anim->gatherPrimitives(out);
|
||||||
|
animB.m_anim->gatherPrimitives(out);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct MetaAnimRandom : IMetaAnim
|
struct MetaAnimRandom : IMetaAnim
|
||||||
{
|
{
|
||||||
|
@ -206,6 +228,12 @@ struct ANCS : BigYAML
|
||||||
Value<atUint32> probability;
|
Value<atUint32> probability;
|
||||||
};
|
};
|
||||||
Vector<Child, DNA_COUNT(animCount)> children;
|
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
|
struct MetaAnimSequence : IMetaAnim
|
||||||
{
|
{
|
||||||
|
@ -213,6 +241,12 @@ struct ANCS : BigYAML
|
||||||
DECL_YAML
|
DECL_YAML
|
||||||
Value<atUint32> animCount;
|
Value<atUint32> animCount;
|
||||||
Vector<MetaAnimFactory, DNA_COUNT(animCount)> children;
|
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
|
struct Animation : BigYAML
|
||||||
|
@ -310,14 +344,45 @@ struct ANCS : BigYAML
|
||||||
std::vector<AnimationResources> animResources;
|
std::vector<AnimationResources> animResources;
|
||||||
} animationSet;
|
} 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 ancs;
|
||||||
ancs.read(rs);
|
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);
|
ancs.toYAMLFile(fp);
|
||||||
fclose(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,
|
PAKEntryReadStream& rs,
|
||||||
const HECL::ProjectPath& outPath,
|
const HECL::ProjectPath& outPath,
|
||||||
PAKRouter<PAKBridge>& pakRouter,
|
PAKRouter<PAKBridge>& pakRouter,
|
||||||
const PAK::Entry& entry)
|
const PAK::Entry& entry,
|
||||||
|
bool force)
|
||||||
{
|
{
|
||||||
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
||||||
if (!conn.createBlend(outPath.getAbsolutePath()))
|
if (!conn.createBlend(outPath.getAbsolutePath()))
|
||||||
|
|
|
@ -2,6 +2,9 @@ make_dnalist(liblist
|
||||||
PAK
|
PAK
|
||||||
MLVL
|
MLVL
|
||||||
ANCS
|
ANCS
|
||||||
|
ANIM
|
||||||
|
CINF
|
||||||
|
CSKR
|
||||||
MAPA
|
MAPA
|
||||||
CMDLMaterials)
|
CMDLMaterials)
|
||||||
add_library(DNAMP1
|
add_library(DNAMP1
|
||||||
|
@ -10,5 +13,6 @@ add_library(DNAMP1
|
||||||
PAK.cpp
|
PAK.cpp
|
||||||
STRG.hpp STRG.cpp
|
STRG.hpp STRG.cpp
|
||||||
ANCS.cpp
|
ANCS.cpp
|
||||||
|
ANIM.cpp
|
||||||
CMDL.hpp
|
CMDL.hpp
|
||||||
CMDLMaterials.cpp)
|
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'):
|
case SBIG('CMDL'):
|
||||||
return {nullptr, CMDL::Extract, ".blend", 1};
|
return {nullptr, CMDL::Extract, ".blend", 1};
|
||||||
case SBIG('ANCS'):
|
case SBIG('ANCS'):
|
||||||
return {ANCS::Extract, nullptr, ".yaml"};
|
return {nullptr, ANCS::Extract, nullptr};
|
||||||
case SBIG('MLVL'):
|
case SBIG('MLVL'):
|
||||||
return {MLVL::Extract, nullptr, ".yaml"};
|
return {MLVL::Extract, nullptr, ".yaml"};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ struct CMDL
|
||||||
PAKEntryReadStream& rs,
|
PAKEntryReadStream& rs,
|
||||||
const HECL::ProjectPath& outPath,
|
const HECL::ProjectPath& outPath,
|
||||||
PAKRouter<PAKBridge>& pakRouter,
|
PAKRouter<PAKBridge>& pakRouter,
|
||||||
const DNAMP1::PAK::Entry& entry)
|
const DNAMP1::PAK::Entry& entry,
|
||||||
|
bool force)
|
||||||
{
|
{
|
||||||
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
||||||
if (!conn.createBlend(outPath.getAbsolutePath()))
|
if (!conn.createBlend(outPath.getAbsolutePath()))
|
||||||
|
|
|
@ -17,7 +17,8 @@ struct CMDL
|
||||||
PAKEntryReadStream& rs,
|
PAKEntryReadStream& rs,
|
||||||
const HECL::ProjectPath& outPath,
|
const HECL::ProjectPath& outPath,
|
||||||
PAKRouter<PAKBridge>& pakRouter,
|
PAKRouter<PAKBridge>& pakRouter,
|
||||||
const PAK::Entry& entry)
|
const PAK::Entry& entry,
|
||||||
|
bool force)
|
||||||
{
|
{
|
||||||
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
|
||||||
if (!conn.createBlend(outPath.getAbsolutePath()))
|
if (!conn.createBlend(outPath.getAbsolutePath()))
|
||||||
|
|
Loading…
Reference in New Issue