Initial MP1 ANIM implementation

This commit is contained in:
Jack Andersen 2015-08-11 13:32:02 -10:00
parent f50629fad7
commit 2cc94bf4ed
15 changed files with 1277 additions and 16 deletions

View File

@ -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_

396
DataSpec/DNACommon/ANIM.cpp Normal file
View File

@ -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);
}
}
}

View File

@ -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_

View File

@ -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)

View File

@ -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 */

View File

@ -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();
}
};

290
DataSpec/DNAMP1/ANIM.cpp Normal file
View File

@ -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);
}
}
}

164
DataSpec/DNAMP1/ANIM.hpp Normal file
View File

@ -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_

42
DataSpec/DNAMP1/CINF.hpp Normal file
View File

@ -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_

View File

@ -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()))

View File

@ -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)

34
DataSpec/DNAMP1/CSKR.hpp Normal file
View File

@ -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_

View File

@ -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"};
}

View File

@ -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()))

View File

@ -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()))