From 2cc94bf4edd4e86aebe4bf92500e730f2ac91f07 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Tue, 11 Aug 2015 13:32:02 -1000 Subject: [PATCH] Initial MP1 ANIM implementation --- DataSpec/DNACommon/ANCS.hpp | 73 ++++++ DataSpec/DNACommon/ANIM.cpp | 396 ++++++++++++++++++++++++++++++ DataSpec/DNACommon/ANIM.hpp | 84 +++++++ DataSpec/DNACommon/CMakeLists.txt | 4 +- DataSpec/DNACommon/DNACommon.hpp | 120 ++++++++- DataSpec/DNAMP1/ANCS.hpp | 71 +++++- DataSpec/DNAMP1/ANIM.cpp | 290 ++++++++++++++++++++++ DataSpec/DNAMP1/ANIM.hpp | 164 +++++++++++++ DataSpec/DNAMP1/CINF.hpp | 42 ++++ DataSpec/DNAMP1/CMDL.hpp | 3 +- DataSpec/DNAMP1/CMakeLists.txt | 4 + DataSpec/DNAMP1/CSKR.hpp | 34 +++ DataSpec/DNAMP1/DNAMP1.cpp | 2 +- DataSpec/DNAMP2/CMDL.hpp | 3 +- DataSpec/DNAMP3/CMDL.hpp | 3 +- 15 files changed, 1277 insertions(+), 16 deletions(-) create mode 100644 DataSpec/DNACommon/ANCS.hpp create mode 100644 DataSpec/DNACommon/ANIM.cpp create mode 100644 DataSpec/DNACommon/ANIM.hpp create mode 100644 DataSpec/DNAMP1/ANIM.cpp create mode 100644 DataSpec/DNAMP1/ANIM.hpp create mode 100644 DataSpec/DNAMP1/CINF.hpp create mode 100644 DataSpec/DNAMP1/CSKR.hpp diff --git a/DataSpec/DNACommon/ANCS.hpp b/DataSpec/DNACommon/ANCS.hpp new file mode 100644 index 000000000..eab181a5a --- /dev/null +++ b/DataSpec/DNACommon/ANCS.hpp @@ -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 +struct CharacterResInfo +{ + std::string name; + IDTYPE cmdl; + IDTYPE cskr; + IDTYPE cinf; +}; + +template +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> 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 + (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 animResInfo; + ancs.getAnimationResInfo(animResInfo); + for (const auto& id : animResInfo) + { + const typename PAKRouter::EntryType* animE = pakRouter.lookupEntry(id); + } + + return true; +} + +} +} + +#endif // _DNACOMMON_ANCS_HPP_ diff --git a/DataSpec/DNACommon/ANIM.cpp b/DataSpec/DNACommon/ANIM.cpp new file mode 100644 index 000000000..4796763de --- /dev/null +++ b/DataSpec/DNACommon/ANIM.cpp @@ -0,0 +1,396 @@ +#include "ANIM.hpp" + +namespace Retro +{ +namespace DNAANIM +{ + +size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector& 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> +BitstreamReader::read(const atUint8* data, + size_t keyFrameCount, + const std::vector& channels, + atUint32 rotDiv, + float transMult) +{ + m_bitCur = 0; + std::vector> chanKeys; + std::vector 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& 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 ; femplace_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 +BitstreamWriter::write(const std::vector>& chanKeys, + size_t keyFrameCount, std::vector& 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 ; fbegin() + 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(newData); +} + +} +} diff --git a/DataSpec/DNACommon/ANIM.hpp b/DataSpec/DNACommon/ANIM.hpp new file mode 100644 index 000000000..88d22c256 --- /dev/null +++ b/DataSpec/DNACommon/ANIM.hpp @@ -0,0 +1,84 @@ +#ifndef _DNACOMMON_ANIMBITSTREAM_HPP_ +#define _DNACOMMON_ANIMBITSTREAM_HPP_ + +#include +#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& channels); + +class BitstreamReader +{ + size_t m_bitCur; + atInt16 dequantize(const atUint8* data, atUint8 q); +public: + std::vector> + read(const atUint8* data, + size_t keyFrameCount, + const std::vector& channels, + atUint32 rotDiv, + float transMult); +}; + +class BitstreamWriter +{ + size_t m_bitCur; + void quantize(atUint8* data, atUint8 q, atInt16 val); +public: + std::unique_ptr + write(const std::vector>& chanKeys, + size_t keyFrameCount, std::vector& channels, + atUint32& rotDivOut, + float& transMultOut, + size_t& sizeOut); +}; + +} +} + +#endif // _DNACOMMON_ANIMBITSTREAM_HPP_ diff --git a/DataSpec/DNACommon/CMakeLists.txt b/DataSpec/DNACommon/CMakeLists.txt index 0c357b864..838a8be64 100644 --- a/DataSpec/DNACommon/CMakeLists.txt +++ b/DataSpec/DNACommon/CMakeLists.txt @@ -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) diff --git a/DataSpec/DNACommon/DNACommon.hpp b/DataSpec/DNACommon/DNACommon.hpp index 73436b96f..a41424801 100644 --- a/DataSpec/DNACommon/DNACommon.hpp +++ b/DataSpec/DNACommon/DNACommon.hpp @@ -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 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= 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 + { + 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 func_a; std::function&, - 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* 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& bridges, std::function 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 */ diff --git a/DataSpec/DNAMP1/ANCS.hpp b/DataSpec/DNAMP1/ANCS.hpp index c535a707b..1b2bffcea 100644 --- a/DataSpec/DNAMP1/ANCS.hpp +++ b/DataSpec/DNAMP1/ANCS.hpp @@ -1,7 +1,11 @@ #ifndef _DNAMP1_ANCS_HPP_ #define _DNAMP1_ANCS_HPP_ +#include #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& out)=0; }; struct MetaAnimFactory : BigYAML { @@ -175,6 +180,11 @@ struct ANCS : BigYAML String<-1> animName; Value unk1; Value unk2; + + void gatherPrimitives(std::unordered_set& out) + { + out.insert(animId); + } }; struct MetaAnimBlend : IMetaAnim { @@ -184,6 +194,12 @@ struct ANCS : BigYAML MetaAnimFactory animB; Value unkFloat; Value unk; + + void gatherPrimitives(std::unordered_set& out) + { + animA.m_anim->gatherPrimitives(out); + animB.m_anim->gatherPrimitives(out); + } }; struct MetaAnimPhaseBlend : IMetaAnim { @@ -193,6 +209,12 @@ struct ANCS : BigYAML MetaAnimFactory animB; Value unkFloat; Value unk; + + void gatherPrimitives(std::unordered_set& out) + { + animA.m_anim->gatherPrimitives(out); + animB.m_anim->gatherPrimitives(out); + } }; struct MetaAnimRandom : IMetaAnim { @@ -206,6 +228,12 @@ struct ANCS : BigYAML Value probability; }; Vector children; + + void gatherPrimitives(std::unordered_set& 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 animCount; Vector children; + + void gatherPrimitives(std::unordered_set& out) + { + for (const auto& child : children) + child.m_anim->gatherPrimitives(out); + } }; struct Animation : BigYAML @@ -310,14 +344,45 @@ struct ANCS : BigYAML std::vector animResources; } animationSet; - static bool Extract(const SpecBase&, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) + void getCharacterResInfo(std::vector>& out) const + { + out.clear(); + out.reserve(characterSet.characters.size()); + for (const CharacterSet::CharacterInfo& ci : characterSet.characters) + { + out.emplace_back(); + DNAANCS::CharacterResInfo& chOut = out.back(); + chOut.name = ci.name; + chOut.cmdl = ci.cmdl; + chOut.cskr = ci.cskr; + chOut.cinf = ci.cinf; + } + } + + void getAnimationResInfo(std::unordered_set& 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& 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, ANCS, MaterialSet, 2> + (conn, ancs, outPath, pakRouter, entry, dataSpec.getMasterShaderPath(), force); + return conn.saveBlend(); } }; diff --git a/DataSpec/DNAMP1/ANIM.cpp b/DataSpec/DNAMP1/ANIM.cpp new file mode 100644 index 000000000..c86895851 --- /dev/null +++ b/DataSpec/DNAMP1/ANIM.cpp @@ -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 boneMap; + for (size_t b=0 ; b& bone : bones) + { + chanKeys.emplace_back(); + std::vector& keys = chanKeys.back(); + for (size_t k=0 ; k& bone : bones) + { + ++kit; + if (bone.second) + { + std::vector& keys = *kit++; + for (size_t k=0 ; k& bone : bones) + maxId = MAX(maxId, bone.first); + head.boneSlotCount = maxId + 1; + head.write(writer); + + for (size_t s=0 ; s& 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& 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& bone : bones) + { + const std::vector& keys = *cit++; + auto kit = keys.begin(); + for (size_t k=0 ; k& bone : bones) + { + ++cit; + if (bone.second) + { + const std::vector& keys = *cit++; + auto kit = keys.begin(); + for (size_t k=0 ; k 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 qChannels = channels; + DNAANIM::BitstreamWriter bsWriter; + size_t bsSize; + std::unique_ptr 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& 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); +} + +} +} diff --git a/DataSpec/DNAMP1/ANIM.hpp b/DataSpec/DNAMP1/ANIM.hpp new file mode 100644 index 000000000..ef324116b --- /dev/null +++ b/DataSpec/DNAMP1/ANIM.hpp @@ -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> bones; + std::vector times; + std::vector channels; + std::vector> chanKeys; + float mainInterval = 0.0; + UniqueID32 evnt; + }; + + struct ANIM0 : IANIM + { + DECL_EXPLICIT_DNA + ANIM0() : IANIM(0) {} + + struct Header : BigDNA + { + DECL_DNA + Value duration; + Value unk0; + Value interval; + Value unk1; + Value boneSlotCount; + Value unk2; + Value keyCount; + }; + }; + + struct ANIM2 : IANIM + { + DECL_EXPLICIT_DNA + ANIM2() : IANIM(2) {} + + struct Header : BigDNA + { + DECL_DNA + Value scratchSize; + UniqueID32 evnt; + Value unk0; + Value duration; + Value interval; + Value unk1; + Value unk2; + Value rotDiv; + Value translationMult; + Value boneChannelCount; + Value unk3; + Value keyBitmapBitCount; + }; + + struct ChannelDesc : BigDNA + { + Delete expl; + Value id = 0; + Value keyCount1 = 0; + Value initRX = 0; + Value qRX = 0; + Value initRY = 0; + Value qRY = 0; + Value initRZ = 0; + Value qRZ = 0; + Value keyCount2 = 0; + Value initTX = 0; + Value qTX = 0; + Value initTY = 0; + Value qTY = 0; + Value initTZ = 0; + Value 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 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_ diff --git a/DataSpec/DNAMP1/CINF.hpp b/DataSpec/DNAMP1/CINF.hpp new file mode 100644 index 000000000..1f51501a0 --- /dev/null +++ b/DataSpec/DNAMP1/CINF.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 boneCount; + struct Bone : BigDNA + { + DECL_DNA + Value id; + Value parentId; + Value origin; + Value linkedCount; + Vector linked; + }; + Vector bones; + + Value boneIdCount; + Vector boneIds; + + Value nameCount; + struct Name : BigDNA + { + DECL_DNA + String<-1> name; + Value boneId; + }; + Vector names; +}; + +} +} + +#endif // _DNAMP1_CINF_HPP_ diff --git a/DataSpec/DNAMP1/CMDL.hpp b/DataSpec/DNAMP1/CMDL.hpp index 8904ef50e..77de4ba42 100644 --- a/DataSpec/DNAMP1/CMDL.hpp +++ b/DataSpec/DNAMP1/CMDL.hpp @@ -17,7 +17,8 @@ struct CMDL PAKEntryReadStream& rs, const HECL::ProjectPath& outPath, PAKRouter& pakRouter, - const PAK::Entry& entry) + const PAK::Entry& entry, + bool force) { HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection(); if (!conn.createBlend(outPath.getAbsolutePath())) diff --git a/DataSpec/DNAMP1/CMakeLists.txt b/DataSpec/DNAMP1/CMakeLists.txt index 2ccdc2bec..c308280a4 100644 --- a/DataSpec/DNAMP1/CMakeLists.txt +++ b/DataSpec/DNAMP1/CMakeLists.txt @@ -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) diff --git a/DataSpec/DNAMP1/CSKR.hpp b/DataSpec/DNAMP1/CSKR.hpp new file mode 100644 index 000000000..8510a24ca --- /dev/null +++ b/DataSpec/DNAMP1/CSKR.hpp @@ -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 skinningRuleCount; + struct SkinningRule : BigDNA + { + DECL_DNA + Value weightCount; + struct Weight : BigDNA + { + DECL_DNA + Value boneId; + Value weight; + }; + Vector weights; + Value vertCount; + }; + Vector skinningRules; +}; + +} +} + +#endif // _DNAMP1_CSKR_HPP_ diff --git a/DataSpec/DNAMP1/DNAMP1.cpp b/DataSpec/DNAMP1/DNAMP1.cpp index a091b7be5..c58d35fab 100644 --- a/DataSpec/DNAMP1/DNAMP1.cpp +++ b/DataSpec/DNAMP1/DNAMP1.cpp @@ -183,7 +183,7 @@ ResExtractor 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"}; } diff --git a/DataSpec/DNAMP2/CMDL.hpp b/DataSpec/DNAMP2/CMDL.hpp index 2471ca1d7..d37f36dfb 100644 --- a/DataSpec/DNAMP2/CMDL.hpp +++ b/DataSpec/DNAMP2/CMDL.hpp @@ -17,7 +17,8 @@ struct CMDL PAKEntryReadStream& rs, const HECL::ProjectPath& outPath, PAKRouter& pakRouter, - const DNAMP1::PAK::Entry& entry) + const DNAMP1::PAK::Entry& entry, + bool force) { HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection(); if (!conn.createBlend(outPath.getAbsolutePath())) diff --git a/DataSpec/DNAMP3/CMDL.hpp b/DataSpec/DNAMP3/CMDL.hpp index 8b0a56481..1f4e865bc 100644 --- a/DataSpec/DNAMP3/CMDL.hpp +++ b/DataSpec/DNAMP3/CMDL.hpp @@ -17,7 +17,8 @@ struct CMDL PAKEntryReadStream& rs, const HECL::ProjectPath& outPath, PAKRouter& pakRouter, - const PAK::Entry& entry) + const PAK::Entry& entry, + bool force) { HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection(); if (!conn.createBlend(outPath.getAbsolutePath()))