mirror of https://github.com/AxioDL/metaforce.git
New code style refactor
This commit is contained in:
parent
41ae32be31
commit
636c82a568
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
IndentWidth: 4
|
||||
BasedOnStyle: LLVM
|
||||
ColumnLimit: 120
|
||||
UseTab: Never
|
||||
---
|
||||
|
@ -8,7 +8,6 @@ DerivePointerAlignment: false
|
|||
PointerAlignment: Left
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
BreakBeforeBraces: Allman
|
||||
IndentCaseLabels: false
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AlignOperands: true
|
||||
|
@ -24,6 +23,6 @@ NamespaceIndentation: None
|
|||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
SortIncludes: false
|
||||
AccessModifierOffset: -4
|
||||
AccessModifierOffset: -2
|
||||
ConstructorInitializerIndentWidth: 0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<mapping directory="$PROJECT_DIR$/discord-rpc" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/hecl" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/hecl-gui" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/hecl-gui/quazip" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/hecl/extern/athena" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/hecl/extern/boo" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/hecl/extern/boo/glslang" vcs="Git" />
|
||||
|
|
|
@ -6,79 +6,70 @@ extern "C" const size_t ASSET_NAME_MP32_SZ;
|
|||
extern "C" const uint8_t ASSET_NAME_MP64[];
|
||||
extern "C" const size_t ASSET_NAME_MP64_SZ;
|
||||
|
||||
namespace DataSpec::AssetNameMap
|
||||
{
|
||||
namespace DataSpec::AssetNameMap {
|
||||
logvisor::Module Log("AssetNameMap");
|
||||
|
||||
struct SAsset
|
||||
{
|
||||
std::string name;
|
||||
std::string directory;
|
||||
hecl::FourCC type;
|
||||
SAsset() = default;
|
||||
SAsset(const hecl::FourCC& typeIn, athena::io::IStreamReader& in)
|
||||
: type(typeIn)
|
||||
{
|
||||
uint32_t nameLen = in.readUint32Big();
|
||||
name = in.readString(nameLen);
|
||||
uint32_t dirLen = in.readUint32Big();
|
||||
directory = in.readString(dirLen);
|
||||
}
|
||||
struct SAsset {
|
||||
std::string name;
|
||||
std::string directory;
|
||||
hecl::FourCC type;
|
||||
SAsset() = default;
|
||||
SAsset(const hecl::FourCC& typeIn, athena::io::IStreamReader& in) : type(typeIn) {
|
||||
uint32_t nameLen = in.readUint32Big();
|
||||
name = in.readString(nameLen);
|
||||
uint32_t dirLen = in.readUint32Big();
|
||||
directory = in.readString(dirLen);
|
||||
}
|
||||
};
|
||||
|
||||
static std::unordered_map<uint64_t, SAsset> g_AssetNameMap;
|
||||
static bool g_AssetNameMapInit = false;
|
||||
|
||||
void LoadAssetMap(athena::io::MemoryReader& ar)
|
||||
{
|
||||
if (!ar.hasError())
|
||||
{
|
||||
hecl::FourCC magic;
|
||||
if (ar.length() >= 4)
|
||||
ar.readBytesToBuf(&magic, 4);
|
||||
if (magic != FOURCC('AIDM'))
|
||||
Log.report(logvisor::Warning, _SYS_STR("Unable to load asset map; Assets will not have proper filenames for most files."));
|
||||
else
|
||||
{
|
||||
uint32_t assetCount = ar.readUint32Big();
|
||||
g_AssetNameMap.reserve(assetCount);
|
||||
for (uint32_t i = 0 ; i<assetCount ; ++i)
|
||||
{
|
||||
hecl::FourCC type;
|
||||
ar.readBytesToBuf(&type, 4);
|
||||
uint64_t id = ar.readUint64Big();
|
||||
g_AssetNameMap[id] = SAsset(type, ar);
|
||||
}
|
||||
}
|
||||
void LoadAssetMap(athena::io::MemoryReader& ar) {
|
||||
if (!ar.hasError()) {
|
||||
hecl::FourCC magic;
|
||||
if (ar.length() >= 4)
|
||||
ar.readBytesToBuf(&magic, 4);
|
||||
if (magic != FOURCC('AIDM'))
|
||||
Log.report(logvisor::Warning,
|
||||
_SYS_STR("Unable to load asset map; Assets will not have proper filenames for most files."));
|
||||
else {
|
||||
uint32_t assetCount = ar.readUint32Big();
|
||||
g_AssetNameMap.reserve(assetCount);
|
||||
for (uint32_t i = 0; i < assetCount; ++i) {
|
||||
hecl::FourCC type;
|
||||
ar.readBytesToBuf(&type, 4);
|
||||
uint64_t id = ar.readUint64Big();
|
||||
g_AssetNameMap[id] = SAsset(type, ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitAssetNameMap()
|
||||
{
|
||||
if (g_AssetNameMapInit)
|
||||
return;
|
||||
void InitAssetNameMap() {
|
||||
if (g_AssetNameMapInit)
|
||||
return;
|
||||
|
||||
Log.report(logvisor::Info, "Initializing asset name database...");
|
||||
Log.report(logvisor::Info, "Initializing asset name database...");
|
||||
|
||||
/* First load the 32bit map for MP1/2 */
|
||||
{
|
||||
athena::io::MemoryReader ar(ASSET_NAME_MP32, ASSET_NAME_MP32_SZ);
|
||||
LoadAssetMap(ar);
|
||||
}
|
||||
/* Now load the 64bit map for MP3 */
|
||||
{
|
||||
athena::io::MemoryReader ar(ASSET_NAME_MP64, ASSET_NAME_MP64_SZ);
|
||||
LoadAssetMap(ar);
|
||||
}
|
||||
g_AssetNameMapInit = true;
|
||||
/* First load the 32bit map for MP1/2 */
|
||||
{
|
||||
athena::io::MemoryReader ar(ASSET_NAME_MP32, ASSET_NAME_MP32_SZ);
|
||||
LoadAssetMap(ar);
|
||||
}
|
||||
/* Now load the 64bit map for MP3 */
|
||||
{
|
||||
athena::io::MemoryReader ar(ASSET_NAME_MP64, ASSET_NAME_MP64_SZ);
|
||||
LoadAssetMap(ar);
|
||||
}
|
||||
g_AssetNameMapInit = true;
|
||||
}
|
||||
|
||||
const std::string* TranslateIdToName(const UniqueID32& id)
|
||||
{
|
||||
if (g_AssetNameMap.find(id.toUint64()) == g_AssetNameMap.end())
|
||||
return nullptr;
|
||||
const std::string* TranslateIdToName(const UniqueID32& id) {
|
||||
if (g_AssetNameMap.find(id.toUint64()) == g_AssetNameMap.end())
|
||||
return nullptr;
|
||||
|
||||
return &g_AssetNameMap[id.toUint64()].name;
|
||||
return &g_AssetNameMap[id.toUint64()].name;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace DataSpec::AssetNameMap
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
#include <string>
|
||||
#include "DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace DataSpec::AssetNameMap
|
||||
{
|
||||
namespace DataSpec::AssetNameMap {
|
||||
void InitAssetNameMap();
|
||||
const std::string* TranslateIdToName(const UniqueID32&);
|
||||
const std::string* TranslateIdToName(const UniqueID64&);
|
||||
}
|
||||
} // namespace DataSpec::AssetNameMap
|
||||
|
|
|
@ -5,20 +5,18 @@
|
|||
extern "C" uint8_t RETRO_MASTER_SHADER[];
|
||||
extern "C" size_t RETRO_MASTER_SHADER_SZ;
|
||||
|
||||
namespace DataSpec::Blender
|
||||
{
|
||||
namespace DataSpec::Blender {
|
||||
|
||||
bool BuildMasterShader(const hecl::ProjectPath& path)
|
||||
{
|
||||
hecl::blender::Connection& conn = hecl::blender::Connection::SharedConnection();
|
||||
if (!conn.createBlend(path, hecl::blender::BlendType::None))
|
||||
return false;
|
||||
{
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
os << RETRO_MASTER_SHADER;
|
||||
os << "make_master_shader_library()\n";
|
||||
}
|
||||
return conn.saveBlend();
|
||||
bool BuildMasterShader(const hecl::ProjectPath& path) {
|
||||
hecl::blender::Connection& conn = hecl::blender::Connection::SharedConnection();
|
||||
if (!conn.createBlend(path, hecl::blender::BlendType::None))
|
||||
return false;
|
||||
{
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
os << RETRO_MASTER_SHADER;
|
||||
os << "make_master_shader_library()\n";
|
||||
}
|
||||
return conn.saveBlend();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace DataSpec::Blender
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
|
||||
#include <hecl/hecl.hpp>
|
||||
|
||||
namespace DataSpec::Blender
|
||||
{
|
||||
namespace DataSpec::Blender {
|
||||
|
||||
bool BuildMasterShader(const hecl::ProjectPath& path);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -7,302 +7,259 @@
|
|||
#include "DataSpec/DNAMP3/CHAR.hpp"
|
||||
#include "hecl/Blender/Connection.hpp"
|
||||
|
||||
namespace DataSpec::DNAANCS
|
||||
{
|
||||
namespace DataSpec::DNAANCS {
|
||||
|
||||
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
|
||||
bool ReadANCSToBlender(hecl::blender::Connection& conn,
|
||||
const ANCSDNA& ancs,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter,
|
||||
const typename PAKRouter::EntryType& entry,
|
||||
const SpecBase& dataspec,
|
||||
std::function<void(const hecl::SystemChar*)> fileChanged,
|
||||
bool force)
|
||||
{
|
||||
/* Extract character CMDL/CSKR first */
|
||||
std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo;
|
||||
ancs.getCharacterResInfo(chResInfo);
|
||||
for (const auto& info : chResInfo)
|
||||
{
|
||||
const nod::Node* node;
|
||||
const typename PAKRouter::EntryType* cmdlE =
|
||||
pakRouter.lookupEntry(info.cmdl, &node, true, false);
|
||||
if (cmdlE)
|
||||
{
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
if (force || cmdlPath.isNone())
|
||||
{
|
||||
cmdlPath.makeDirChain(false);
|
||||
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh))
|
||||
return false;
|
||||
bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
|
||||
std::function<void(const hecl::SystemChar*)> fileChanged, bool force) {
|
||||
/* Extract character CMDL/CSKR first */
|
||||
std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo;
|
||||
ancs.getCharacterResInfo(chResInfo);
|
||||
for (const auto& info : chResInfo) {
|
||||
const nod::Node* node;
|
||||
const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, &node, true, false);
|
||||
if (cmdlE) {
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
if (force || cmdlPath.isNone()) {
|
||||
cmdlPath.makeDirChain(false);
|
||||
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh))
|
||||
return false;
|
||||
|
||||
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
|
||||
hecl::SystemStringConv bestNameView(bestName);
|
||||
fileChanged(bestNameView.c_str());
|
||||
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
|
||||
hecl::SystemStringConv bestNameView(bestName);
|
||||
fileChanged(bestNameView.c_str());
|
||||
|
||||
typename ANCSDNA::CSKRType cskr;
|
||||
pakRouter.lookupAndReadDNA(info.cskr, cskr);
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(info.cinf, cinf);
|
||||
using RigPair = std::pair<typename ANCSDNA::CSKRType*, typename ANCSDNA::CINFType*>;
|
||||
RigPair rigPair(&cskr, &cinf);
|
||||
typename ANCSDNA::CSKRType cskr;
|
||||
pakRouter.lookupAndReadDNA(info.cskr, cskr);
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(info.cinf, cinf);
|
||||
using RigPair = std::pair<typename ANCSDNA::CSKRType*, typename ANCSDNA::CINFType*>;
|
||||
RigPair rigPair(&cskr, &cinf);
|
||||
|
||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>
|
||||
(conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
|
||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>(
|
||||
conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
|
||||
|
||||
conn.saveBlend();
|
||||
}
|
||||
conn.saveBlend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract attachment CMDL/CSKRs first */
|
||||
auto attRange = pakRouter.lookupCharacterAttachmentRigs(entry.id);
|
||||
for (auto it = attRange.first; it != attRange.second; ++it) {
|
||||
auto cmdlid = it->second.first.second;
|
||||
|
||||
const nod::Node* node;
|
||||
const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(cmdlid, &node, true, false);
|
||||
if (cmdlE) {
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
if (force || cmdlPath.isNone()) {
|
||||
cmdlPath.makeDirChain(false);
|
||||
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh))
|
||||
return false;
|
||||
|
||||
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
|
||||
hecl::SystemStringConv bestNameView(bestName);
|
||||
fileChanged(bestNameView.c_str());
|
||||
|
||||
const auto* rp = pakRouter.lookupCMDLRigPair(cmdlid);
|
||||
typename ANCSDNA::CSKRType cskr;
|
||||
pakRouter.lookupAndReadDNA(rp->first, cskr);
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(rp->second, cinf);
|
||||
using RigPair = std::pair<typename ANCSDNA::CSKRType*, typename ANCSDNA::CINFType*>;
|
||||
RigPair rigPair(&cskr, &cinf);
|
||||
|
||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>(
|
||||
conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
|
||||
|
||||
conn.saveBlend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string bestName = pakRouter.getBestEntryName(entry);
|
||||
hecl::SystemStringConv bestNameView(bestName);
|
||||
fileChanged(bestNameView.c_str());
|
||||
|
||||
/* Establish ANCS blend */
|
||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::Actor))
|
||||
return false;
|
||||
|
||||
std::string firstName;
|
||||
typename ANCSDNA::CINFType firstCinf;
|
||||
{
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
|
||||
os.format(
|
||||
"import bpy\n"
|
||||
"from mathutils import Vector\n"
|
||||
"bpy.context.scene.name = '%s'\n"
|
||||
"bpy.context.scene.hecl_mesh_obj = bpy.context.scene.name\n"
|
||||
"\n"
|
||||
"# Using 'Blender Game'\n"
|
||||
"bpy.context.scene.render.engine = 'BLENDER_GAME'\n"
|
||||
"\n"
|
||||
"# Clear Scene\n"
|
||||
"for ob in bpy.data.objects:\n"
|
||||
" if ob.type != 'LAMP' and ob.type != 'CAMERA':\n"
|
||||
" bpy.context.scene.objects.unlink(ob)\n"
|
||||
" bpy.data.objects.remove(ob)\n"
|
||||
"\n"
|
||||
"actor_data = bpy.context.scene.hecl_sact_data\n"
|
||||
"arm_obj = None\n",
|
||||
pakRouter.getBestEntryName(entry).c_str());
|
||||
|
||||
std::unordered_set<typename PAKRouter::IDType> cinfsDone;
|
||||
for (const auto& info : chResInfo) {
|
||||
/* Provide data to add-on */
|
||||
os.format(
|
||||
"actor_subtype = actor_data.subtypes.add()\n"
|
||||
"actor_subtype.name = '%s'\n\n",
|
||||
info.name.c_str());
|
||||
|
||||
/* Build CINF if needed */
|
||||
if (cinfsDone.find(info.cinf) == cinfsDone.end()) {
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(info.cinf, cinf);
|
||||
cinf.sendCINFToBlender(os, info.cinf);
|
||||
if (cinfsDone.empty()) {
|
||||
firstName = ANCSDNA::CINFType::GetCINFArmatureName(info.cinf);
|
||||
firstCinf = cinf;
|
||||
}
|
||||
cinfsDone.insert(info.cinf);
|
||||
} else
|
||||
os.format("arm_obj = bpy.data.objects['CINF_%s']\n", info.cinf.toString().c_str());
|
||||
os << "actor_subtype.linked_armature = arm_obj.name\n";
|
||||
|
||||
/* Link CMDL */
|
||||
const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, nullptr, true, false);
|
||||
if (cmdlE) {
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkBlend(cmdlPath.getAbsolutePathUTF8().data(), pakRouter.getBestEntryName(*cmdlE).data(), true);
|
||||
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.objects.link(obj)\n"
|
||||
"obj.parent = arm_obj\n"
|
||||
"obj.parent_type = 'ARMATURE'\n"
|
||||
"actor_subtype.linked_mesh = obj.name\n\n";
|
||||
}
|
||||
|
||||
/* Link overlays */
|
||||
for (const auto& overlay : info.overlays) {
|
||||
os << "overlay = actor_subtype.overlays.add()\n";
|
||||
os.format("overlay.name = '%s'\n", overlay.first.c_str());
|
||||
|
||||
/* Link CMDL */
|
||||
const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(overlay.second.first, nullptr, true, false);
|
||||
if (cmdlE) {
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkBlend(cmdlPath.getAbsolutePathUTF8().data(), pakRouter.getBestEntryName(*cmdlE).data(), true);
|
||||
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.objects.link(obj)\n"
|
||||
"obj.parent = arm_obj\n"
|
||||
"obj.parent_type = 'ARMATURE'\n"
|
||||
"overlay.linked_mesh = obj.name\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract attachment CMDL/CSKRs first */
|
||||
auto attRange = pakRouter.lookupCharacterAttachmentRigs(entry.id);
|
||||
for (auto it = attRange.first; it != attRange.second; ++it)
|
||||
{
|
||||
auto cmdlid = it->second.first.second;
|
||||
/* Link attachments */
|
||||
for (auto it = attRange.first; it != attRange.second; ++it) {
|
||||
os << "attachment = actor_data.attachments.add()\n";
|
||||
os.format("attachment.name = '%s'\n", it->second.second.c_str());
|
||||
|
||||
const nod::Node* node;
|
||||
const typename PAKRouter::EntryType* cmdlE =
|
||||
pakRouter.lookupEntry(cmdlid, &node, true, false);
|
||||
if (cmdlE)
|
||||
{
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
if (force || cmdlPath.isNone())
|
||||
{
|
||||
cmdlPath.makeDirChain(false);
|
||||
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh))
|
||||
return false;
|
||||
auto cinfid = it->second.first.first;
|
||||
auto cmdlid = it->second.first.second;
|
||||
|
||||
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
|
||||
hecl::SystemStringConv bestNameView(bestName);
|
||||
fileChanged(bestNameView.c_str());
|
||||
if (cinfid) {
|
||||
/* Build CINF if needed */
|
||||
if (cinfsDone.find(cinfid) == cinfsDone.end()) {
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(cinfid, cinf);
|
||||
cinf.sendCINFToBlender(os, cinfid);
|
||||
if (cinfsDone.empty()) {
|
||||
firstName = ANCSDNA::CINFType::GetCINFArmatureName(cinfid);
|
||||
firstCinf = cinf;
|
||||
}
|
||||
cinfsDone.insert(cinfid);
|
||||
} else
|
||||
os.format("arm_obj = bpy.data.objects['CINF_%s']\n", cinfid.toString().c_str());
|
||||
os << "attachment.linked_armature = arm_obj.name\n";
|
||||
}
|
||||
|
||||
const auto* rp = pakRouter.lookupCMDLRigPair(cmdlid);
|
||||
typename ANCSDNA::CSKRType cskr;
|
||||
pakRouter.lookupAndReadDNA(rp->first, cskr);
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(rp->second, cinf);
|
||||
using RigPair = std::pair<typename ANCSDNA::CSKRType*, typename ANCSDNA::CINFType*>;
|
||||
RigPair rigPair(&cskr, &cinf);
|
||||
/* Link CMDL */
|
||||
const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(cmdlid, nullptr, true, false);
|
||||
if (cmdlE) {
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkBlend(cmdlPath.getAbsolutePathUTF8().data(), pakRouter.getBestEntryName(*cmdlE).data(), true);
|
||||
|
||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>
|
||||
(conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
|
||||
|
||||
conn.saveBlend();
|
||||
}
|
||||
}
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.objects.link(obj)\n"
|
||||
"obj.parent = arm_obj\n"
|
||||
"obj.parent_type = 'ARMATURE'\n"
|
||||
"attachment.linked_mesh = obj.name\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string bestName = pakRouter.getBestEntryName(entry);
|
||||
hecl::SystemStringConv bestNameView(bestName);
|
||||
fileChanged(bestNameView.c_str());
|
||||
{
|
||||
hecl::blender::DataStream ds = conn.beginData();
|
||||
std::unordered_map<std::string, hecl::blender::Matrix3f> matrices = ds.getBoneMatrices(firstName);
|
||||
ds.close();
|
||||
DNAANIM::RigInverter<typename ANCSDNA::CINFType> inverter(firstCinf, matrices);
|
||||
|
||||
/* Establish ANCS blend */
|
||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::Actor))
|
||||
return false;
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
os << "import bpy\n"
|
||||
"actor_data = bpy.context.scene.hecl_sact_data\n";
|
||||
|
||||
std::string firstName;
|
||||
typename ANCSDNA::CINFType firstCinf;
|
||||
{
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
/* Get animation primitives */
|
||||
std::map<atUint32, AnimationResInfo<typename PAKRouter::IDType>> animResInfo;
|
||||
ancs.getAnimationResInfo(&pakRouter, animResInfo);
|
||||
for (const auto& id : animResInfo) {
|
||||
typename ANCSDNA::ANIMType anim;
|
||||
if (pakRouter.lookupAndReadDNA(id.second.animId, anim, true)) {
|
||||
os.format(
|
||||
"act = bpy.data.actions.new('%s')\n"
|
||||
"act.use_fake_user = True\n",
|
||||
id.second.name.c_str());
|
||||
anim.sendANIMToBlender(os, inverter, id.second.additive);
|
||||
}
|
||||
|
||||
os.format("import bpy\n"
|
||||
"from mathutils import Vector\n"
|
||||
"bpy.context.scene.name = '%s'\n"
|
||||
"bpy.context.scene.hecl_mesh_obj = bpy.context.scene.name\n"
|
||||
"\n"
|
||||
"# Using 'Blender Game'\n"
|
||||
"bpy.context.scene.render.engine = 'BLENDER_GAME'\n"
|
||||
"\n"
|
||||
"# Clear Scene\n"
|
||||
"for ob in bpy.data.objects:\n"
|
||||
" if ob.type != 'LAMP' and ob.type != 'CAMERA':\n"
|
||||
" bpy.context.scene.objects.unlink(ob)\n"
|
||||
" bpy.data.objects.remove(ob)\n"
|
||||
"\n"
|
||||
"actor_data = bpy.context.scene.hecl_sact_data\n"
|
||||
"arm_obj = None\n",
|
||||
pakRouter.getBestEntryName(entry).c_str());
|
||||
os.format(
|
||||
"actor_action = actor_data.actions.add()\n"
|
||||
"actor_action.name = '%s'\n",
|
||||
id.second.name.c_str());
|
||||
|
||||
std::unordered_set<typename PAKRouter::IDType> cinfsDone;
|
||||
for (const auto& info : chResInfo)
|
||||
{
|
||||
/* Provide data to add-on */
|
||||
os.format("actor_subtype = actor_data.subtypes.add()\n"
|
||||
"actor_subtype.name = '%s'\n\n",
|
||||
info.name.c_str());
|
||||
|
||||
/* Build CINF if needed */
|
||||
if (cinfsDone.find(info.cinf) == cinfsDone.end())
|
||||
{
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(info.cinf, cinf);
|
||||
cinf.sendCINFToBlender(os, info.cinf);
|
||||
if (cinfsDone.empty())
|
||||
{
|
||||
firstName = ANCSDNA::CINFType::GetCINFArmatureName(info.cinf);
|
||||
firstCinf = cinf;
|
||||
}
|
||||
cinfsDone.insert(info.cinf);
|
||||
}
|
||||
else
|
||||
os.format("arm_obj = bpy.data.objects['CINF_%s']\n", info.cinf.toString().c_str());
|
||||
os << "actor_subtype.linked_armature = arm_obj.name\n";
|
||||
|
||||
/* Link CMDL */
|
||||
const typename PAKRouter::EntryType* cmdlE =
|
||||
pakRouter.lookupEntry(info.cmdl, nullptr, true, false);
|
||||
if (cmdlE)
|
||||
{
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkBlend(cmdlPath.getAbsolutePathUTF8().data(),
|
||||
pakRouter.getBestEntryName(*cmdlE).data(), true);
|
||||
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.objects.link(obj)\n"
|
||||
"obj.parent = arm_obj\n"
|
||||
"obj.parent_type = 'ARMATURE'\n"
|
||||
"actor_subtype.linked_mesh = obj.name\n\n";
|
||||
}
|
||||
|
||||
/* Link overlays */
|
||||
for (const auto& overlay : info.overlays)
|
||||
{
|
||||
os << "overlay = actor_subtype.overlays.add()\n";
|
||||
os.format("overlay.name = '%s'\n", overlay.first.c_str());
|
||||
|
||||
/* Link CMDL */
|
||||
const typename PAKRouter::EntryType* cmdlE =
|
||||
pakRouter.lookupEntry(overlay.second.first, nullptr, true, false);
|
||||
if (cmdlE)
|
||||
{
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkBlend(cmdlPath.getAbsolutePathUTF8().data(),
|
||||
pakRouter.getBestEntryName(*cmdlE).data(), true);
|
||||
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.objects.link(obj)\n"
|
||||
"obj.parent = arm_obj\n"
|
||||
"obj.parent_type = 'ARMATURE'\n"
|
||||
"overlay.linked_mesh = obj.name\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Link attachments */
|
||||
for (auto it = attRange.first; it != attRange.second; ++it)
|
||||
{
|
||||
os << "attachment = actor_data.attachments.add()\n";
|
||||
os.format("attachment.name = '%s'\n", it->second.second.c_str());
|
||||
|
||||
auto cinfid = it->second.first.first;
|
||||
auto cmdlid = it->second.first.second;
|
||||
|
||||
if (cinfid)
|
||||
{
|
||||
/* Build CINF if needed */
|
||||
if (cinfsDone.find(cinfid) == cinfsDone.end())
|
||||
{
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(cinfid, cinf);
|
||||
cinf.sendCINFToBlender(os, cinfid);
|
||||
if (cinfsDone.empty())
|
||||
{
|
||||
firstName = ANCSDNA::CINFType::GetCINFArmatureName(cinfid);
|
||||
firstCinf = cinf;
|
||||
}
|
||||
cinfsDone.insert(cinfid);
|
||||
}
|
||||
else
|
||||
os.format("arm_obj = bpy.data.objects['CINF_%s']\n", cinfid.toString().c_str());
|
||||
os << "attachment.linked_armature = arm_obj.name\n";
|
||||
}
|
||||
|
||||
/* Link CMDL */
|
||||
const typename PAKRouter::EntryType* cmdlE =
|
||||
pakRouter.lookupEntry(cmdlid, nullptr, true, false);
|
||||
if (cmdlE)
|
||||
{
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkBlend(cmdlPath.getAbsolutePathUTF8().data(),
|
||||
pakRouter.getBestEntryName(*cmdlE).data(), true);
|
||||
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.objects.link(obj)\n"
|
||||
"obj.parent = arm_obj\n"
|
||||
"obj.parent_type = 'ARMATURE'\n"
|
||||
"attachment.linked_mesh = obj.name\n\n";
|
||||
}
|
||||
}
|
||||
/* Extract EVNT if present */
|
||||
anim.extractEVNT(id.second, outPath, pakRouter, force);
|
||||
}
|
||||
|
||||
{
|
||||
hecl::blender::DataStream ds = conn.beginData();
|
||||
std::unordered_map<std::string,
|
||||
hecl::blender::Matrix3f> matrices = ds.getBoneMatrices(firstName);
|
||||
ds.close();
|
||||
DNAANIM::RigInverter<typename ANCSDNA::CINFType> inverter(firstCinf, matrices);
|
||||
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
os << "import bpy\n"
|
||||
"actor_data = bpy.context.scene.hecl_sact_data\n";
|
||||
|
||||
/* Get animation primitives */
|
||||
std::map<atUint32, AnimationResInfo<typename PAKRouter::IDType>> animResInfo;
|
||||
ancs.getAnimationResInfo(&pakRouter, animResInfo);
|
||||
for (const auto& id : animResInfo)
|
||||
{
|
||||
typename ANCSDNA::ANIMType anim;
|
||||
if (pakRouter.lookupAndReadDNA(id.second.animId, anim, true))
|
||||
{
|
||||
os.format("act = bpy.data.actions.new('%s')\n"
|
||||
"act.use_fake_user = True\n", id.second.name.c_str());
|
||||
anim.sendANIMToBlender(os, inverter, id.second.additive);
|
||||
}
|
||||
|
||||
os.format("actor_action = actor_data.actions.add()\n"
|
||||
"actor_action.name = '%s'\n", id.second.name.c_str());
|
||||
|
||||
/* Extract EVNT if present */
|
||||
anim.extractEVNT(id.second, outPath, pakRouter, force);
|
||||
}
|
||||
}
|
||||
conn.saveBlend();
|
||||
return true;
|
||||
}
|
||||
conn.saveBlend();
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool ReadANCSToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::ANCS, DNAMP1::MaterialSet, DNACMDL::SurfaceHeader_1, 2>
|
||||
(hecl::blender::Connection& conn,
|
||||
const DNAMP1::ANCS& ancs,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter,
|
||||
const typename PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
|
||||
const SpecBase& dataspec,
|
||||
std::function<void(const hecl::SystemChar*)> fileChanged,
|
||||
bool force);
|
||||
template bool ReadANCSToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::ANCS, DNAMP2::MaterialSet, DNACMDL::SurfaceHeader_2, 4>
|
||||
(hecl::blender::Connection& conn,
|
||||
const DNAMP2::ANCS& ancs,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter,
|
||||
const typename PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
|
||||
const SpecBase& dataspec,
|
||||
std::function<void(const hecl::SystemChar*)> fileChanged,
|
||||
bool force);
|
||||
template bool ReadANCSToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::CHAR, DNAMP3::MaterialSet, DNACMDL::SurfaceHeader_3, 4>
|
||||
(hecl::blender::Connection& conn,
|
||||
const DNAMP3::CHAR& ancs,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP3::PAKBridge>& pakRouter,
|
||||
const typename PAKRouter<DNAMP3::PAKBridge>::EntryType& entry,
|
||||
const SpecBase& dataspec,
|
||||
std::function<void(const hecl::SystemChar*)> fileChanged,
|
||||
bool force);
|
||||
template bool
|
||||
ReadANCSToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::ANCS, DNAMP1::MaterialSet, DNACMDL::SurfaceHeader_1, 2>(
|
||||
hecl::blender::Connection& conn, const DNAMP1::ANCS& ancs, const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
|
||||
const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force);
|
||||
template bool
|
||||
ReadANCSToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::ANCS, DNAMP2::MaterialSet, DNACMDL::SurfaceHeader_2, 4>(
|
||||
hecl::blender::Connection& conn, const DNAMP2::ANCS& ancs, const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
|
||||
const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force);
|
||||
template bool
|
||||
ReadANCSToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::CHAR, DNAMP3::MaterialSet, DNACMDL::SurfaceHeader_3, 4>(
|
||||
hecl::blender::Connection& conn, const DNAMP3::CHAR& ancs, const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP3::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP3::PAKBridge>::EntryType& entry,
|
||||
const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force);
|
||||
|
||||
}
|
||||
} // namespace DataSpec::DNAANCS
|
||||
|
|
|
@ -5,41 +5,32 @@
|
|||
#include "CMDL.hpp"
|
||||
#include "RigInverter.hpp"
|
||||
|
||||
namespace DataSpec::DNAANCS
|
||||
{
|
||||
namespace DataSpec::DNAANCS {
|
||||
|
||||
using Actor = hecl::blender::Actor;
|
||||
using Armature = hecl::blender::Armature;
|
||||
using Action = hecl::blender::Action;
|
||||
|
||||
template <typename IDTYPE>
|
||||
struct CharacterResInfo
|
||||
{
|
||||
std::string name;
|
||||
IDTYPE cmdl;
|
||||
IDTYPE cskr;
|
||||
IDTYPE cinf;
|
||||
std::vector<std::pair<std::string, std::pair<IDTYPE, IDTYPE>>> overlays;
|
||||
struct CharacterResInfo {
|
||||
std::string name;
|
||||
IDTYPE cmdl;
|
||||
IDTYPE cskr;
|
||||
IDTYPE cinf;
|
||||
std::vector<std::pair<std::string, std::pair<IDTYPE, IDTYPE>>> overlays;
|
||||
};
|
||||
|
||||
template <typename IDTYPE>
|
||||
struct AnimationResInfo
|
||||
{
|
||||
std::string name;
|
||||
IDTYPE animId;
|
||||
IDTYPE evntId;
|
||||
bool additive;
|
||||
struct AnimationResInfo {
|
||||
std::string name;
|
||||
IDTYPE animId;
|
||||
IDTYPE evntId;
|
||||
bool additive;
|
||||
};
|
||||
|
||||
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
|
||||
bool ReadANCSToBlender(hecl::blender::Connection& conn,
|
||||
const ANCSDNA& ancs,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter,
|
||||
const typename PAKRouter::EntryType& entry,
|
||||
const SpecBase& dataspec,
|
||||
std::function<void(const hecl::SystemChar*)> fileChanged,
|
||||
bool force=false);
|
||||
|
||||
}
|
||||
bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
|
||||
std::function<void(const hecl::SystemChar*)> fileChanged, bool force = false);
|
||||
|
||||
} // namespace DataSpec::DNAANCS
|
||||
|
|
|
@ -3,529 +3,439 @@
|
|||
|
||||
#define DUMP_KEYS 0
|
||||
|
||||
namespace DataSpec::DNAANIM
|
||||
{
|
||||
namespace DataSpec::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::Type::Rotation:
|
||||
bitsPerKeyFrame += 1;
|
||||
case Channel::Type::Translation:
|
||||
case Channel::Type::Scale:
|
||||
bitsPerKeyFrame += chan.q[0];
|
||||
bitsPerKeyFrame += chan.q[1];
|
||||
bitsPerKeyFrame += chan.q[2];
|
||||
break;
|
||||
case Channel::Type::KfHead:
|
||||
bitsPerKeyFrame += 1;
|
||||
break;
|
||||
case Channel::Type::RotationMP3:
|
||||
bitsPerKeyFrame += chan.q[0];
|
||||
bitsPerKeyFrame += chan.q[1];
|
||||
bitsPerKeyFrame += chan.q[2];
|
||||
bitsPerKeyFrame += chan.q[3];
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
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::Type::Rotation:
|
||||
bitsPerKeyFrame += 1;
|
||||
case Channel::Type::Translation:
|
||||
case Channel::Type::Scale:
|
||||
bitsPerKeyFrame += chan.q[0];
|
||||
bitsPerKeyFrame += chan.q[1];
|
||||
bitsPerKeyFrame += chan.q[2];
|
||||
break;
|
||||
case Channel::Type::KfHead:
|
||||
bitsPerKeyFrame += 1;
|
||||
break;
|
||||
case Channel::Type::RotationMP3:
|
||||
bitsPerKeyFrame += chan.q[0];
|
||||
bitsPerKeyFrame += chan.q[1];
|
||||
bitsPerKeyFrame += chan.q[2];
|
||||
bitsPerKeyFrame += chan.q[3];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (bitsPerKeyFrame * keyFrameCount + 31) / 32 * 4;
|
||||
}
|
||||
return (bitsPerKeyFrame * keyFrameCount + 31) / 32 * 4;
|
||||
}
|
||||
|
||||
static inline QuantizedRot QuantizeRotation(const Value& quat, atUint32 div)
|
||||
{
|
||||
float q = M_PIF / 2.0f / float(div);
|
||||
zeus::simd_floats f(quat.simd);
|
||||
return
|
||||
{
|
||||
{
|
||||
atInt32(std::asin(f[1]) / q),
|
||||
atInt32(std::asin(f[2]) / q),
|
||||
atInt32(std::asin(f[3]) / q),
|
||||
},
|
||||
(f[0] < 0.f)
|
||||
};
|
||||
static inline QuantizedRot QuantizeRotation(const Value& quat, atUint32 div) {
|
||||
float q = M_PIF / 2.0f / float(div);
|
||||
zeus::simd_floats f(quat.simd);
|
||||
return {{
|
||||
atInt32(std::asin(f[1]) / q),
|
||||
atInt32(std::asin(f[2]) / q),
|
||||
atInt32(std::asin(f[3]) / q),
|
||||
},
|
||||
(f[0] < 0.f)};
|
||||
}
|
||||
|
||||
static inline Value DequantizeRotation(const QuantizedRot& v, atUint32 div)
|
||||
{
|
||||
float q = M_PIF / 2.0f / float(div);
|
||||
athena::simd_floats f = {
|
||||
0.0f,
|
||||
std::sin(v.v[0] * q),
|
||||
std::sin(v.v[1] * q),
|
||||
std::sin(v.v[2] * q),
|
||||
};
|
||||
f[0] = std::sqrt(std::max((1.0f -
|
||||
(f[1] * f[1] +
|
||||
f[2] * f[2] +
|
||||
f[3] * f[3])), 0.0f));
|
||||
f[0] = v.w ? -f[0] : f[0];
|
||||
Value retval;
|
||||
retval.simd.copy_from(f);
|
||||
return retval;
|
||||
static inline Value DequantizeRotation(const QuantizedRot& v, atUint32 div) {
|
||||
float q = M_PIF / 2.0f / float(div);
|
||||
athena::simd_floats f = {
|
||||
0.0f,
|
||||
std::sin(v.v[0] * q),
|
||||
std::sin(v.v[1] * q),
|
||||
std::sin(v.v[2] * q),
|
||||
};
|
||||
f[0] = std::sqrt(std::max((1.0f - (f[1] * f[1] + f[2] * f[2] + f[3] * f[3])), 0.0f));
|
||||
f[0] = v.w ? -f[0] : f[0];
|
||||
Value retval;
|
||||
retval.simd.copy_from(f);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static inline Value DequantizeRotation_3(const QuantizedRot& v, atUint32 div)
|
||||
{
|
||||
float q = 1.0f / float(div);
|
||||
athena::simd_floats f = {
|
||||
0.0f,
|
||||
v.v[0] * q,
|
||||
v.v[1] * q,
|
||||
v.v[2] * q,
|
||||
};
|
||||
f[0] = std::sqrt(std::max((1.0f -
|
||||
(f[1] * f[1] +
|
||||
f[2] * f[2] +
|
||||
f[3] * f[3])), 0.0f));
|
||||
f[0] = v.w ? -f[0] : f[0];
|
||||
Value retval;
|
||||
retval.simd.copy_from(f);
|
||||
return retval;
|
||||
static inline Value DequantizeRotation_3(const QuantizedRot& v, atUint32 div) {
|
||||
float q = 1.0f / float(div);
|
||||
athena::simd_floats f = {
|
||||
0.0f,
|
||||
v.v[0] * q,
|
||||
v.v[1] * q,
|
||||
v.v[2] * q,
|
||||
};
|
||||
f[0] = std::sqrt(std::max((1.0f - (f[1] * f[1] + f[2] * f[2] + f[3] * f[3])), 0.0f));
|
||||
f[0] = v.w ? -f[0] : f[0];
|
||||
Value retval;
|
||||
retval.simd.copy_from(f);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool BitstreamReader::dequantizeBit(const atUint8* data)
|
||||
{
|
||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
||||
atUint32 bitRem = m_bitCur % 32;
|
||||
bool BitstreamReader::dequantizeBit(const atUint8* data) {
|
||||
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(*reinterpret_cast<const atUint32*>(data + byteCur)) >> bitRem;
|
||||
/* Fill 32 bit buffer with region containing bits */
|
||||
/* Make them least significant */
|
||||
atUint32 tempBuf = hecl::SBig(*reinterpret_cast<const atUint32*>(data + byteCur)) >> bitRem;
|
||||
|
||||
/* That's it */
|
||||
m_bitCur += 1;
|
||||
return tempBuf & 0x1;
|
||||
/* That's it */
|
||||
m_bitCur += 1;
|
||||
return tempBuf & 0x1;
|
||||
}
|
||||
|
||||
atInt32 BitstreamReader::dequantize(const atUint8* data, atUint8 q)
|
||||
{
|
||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
||||
atUint32 bitRem = m_bitCur % 32;
|
||||
atInt32 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(*reinterpret_cast<const atUint32*>(data + byteCur)) >> bitRem;
|
||||
/* Fill 32 bit buffer with region containing bits */
|
||||
/* Make them least significant */
|
||||
atUint32 tempBuf = hecl::SBig(*reinterpret_cast<const 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(*reinterpret_cast<const atUint32*>(data + byteCur + 4));
|
||||
tempBuf |= (tempBuf2 << (32 - 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(*reinterpret_cast<const atUint32*>(data + byteCur + 4));
|
||||
tempBuf |= (tempBuf2 << (32 - bitRem));
|
||||
}
|
||||
|
||||
/* Mask it */
|
||||
atUint32 mask = (1 << q) - 1;
|
||||
tempBuf &= mask;
|
||||
|
||||
/* Sign extend */
|
||||
atUint32 sign = (tempBuf >> (q - 1)) & 0x1;
|
||||
if (sign)
|
||||
tempBuf |= ~0u << q;
|
||||
|
||||
/* Return delta value */
|
||||
m_bitCur += q;
|
||||
return atInt32(tempBuf);
|
||||
}
|
||||
|
||||
std::vector<std::vector<Value>> BitstreamReader::read(const atUint8* data, size_t keyFrameCount,
|
||||
const std::vector<Channel>& channels, atUint32 rotDiv,
|
||||
float transMult, float scaleMult) {
|
||||
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);
|
||||
|
||||
chanKeys.emplace_back();
|
||||
std::vector<Value>& keys = chanKeys.back();
|
||||
keys.reserve(keyFrameCount);
|
||||
switch (chan.type) {
|
||||
case Channel::Type::Rotation: {
|
||||
QuantizedRot qr = {{chan.i[0], chan.i[1], chan.i[2]}, false};
|
||||
keys.emplace_back(DequantizeRotation(qr, rotDiv));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Mask it */
|
||||
atUint32 mask = (1 << q) - 1;
|
||||
tempBuf &= mask;
|
||||
|
||||
/* Sign extend */
|
||||
atUint32 sign = (tempBuf >> (q - 1)) & 0x1;
|
||||
if (sign)
|
||||
tempBuf |= ~0u << q;
|
||||
|
||||
/* Return delta value */
|
||||
m_bitCur += q;
|
||||
return atInt32(tempBuf);
|
||||
}
|
||||
|
||||
std::vector<std::vector<Value>>
|
||||
BitstreamReader::read(const atUint8* data,
|
||||
size_t keyFrameCount,
|
||||
const std::vector<Channel>& channels,
|
||||
atUint32 rotDiv,
|
||||
float transMult,
|
||||
float scaleMult)
|
||||
{
|
||||
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);
|
||||
|
||||
chanKeys.emplace_back();
|
||||
std::vector<Value>& keys = chanKeys.back();
|
||||
keys.reserve(keyFrameCount);
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::Type::Rotation:
|
||||
{
|
||||
QuantizedRot qr = {{chan.i[0], chan.i[1], chan.i[2]}, false};
|
||||
keys.emplace_back(DequantizeRotation(qr, rotDiv));
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Translation:
|
||||
{
|
||||
keys.push_back({chan.i[0] * transMult, chan.i[1] * transMult, chan.i[2] * transMult});
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Scale:
|
||||
{
|
||||
keys.push_back({chan.i[0] * scaleMult, chan.i[1] * scaleMult, chan.i[2] * scaleMult});
|
||||
break;
|
||||
}
|
||||
case Channel::Type::KfHead:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Channel::Type::RotationMP3:
|
||||
{
|
||||
QuantizedRot qr = {{chan.i[1], chan.i[2], chan.i[3]}, bool(chan.i[0] & 0x1)};
|
||||
keys.emplace_back(DequantizeRotation_3(qr, rotDiv));
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
case Channel::Type::Translation: {
|
||||
keys.push_back({chan.i[0] * transMult, chan.i[1] * transMult, chan.i[2] * transMult});
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t f=0 ; f<keyFrameCount ; ++f)
|
||||
{
|
||||
#if DUMP_KEYS
|
||||
fprintf(stderr, "\nFRAME %" PRISize " %u %u\n", f, (m_bitCur / 32) * 4, m_bitCur % 32);
|
||||
int lastId = -1;
|
||||
#endif
|
||||
auto kit = chanKeys.begin();
|
||||
auto ait = chanAccum.begin();
|
||||
for (const Channel& chan : channels)
|
||||
{
|
||||
#if DUMP_KEYS
|
||||
if (chan.id != lastId)
|
||||
{
|
||||
lastId = chan.id;
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
QuantizedValue& p = *ait;
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::Type::Rotation:
|
||||
{
|
||||
bool wBit = dequantizeBit(data);
|
||||
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));
|
||||
#if DUMP_KEYS
|
||||
fprintf(stderr, "%d R: %d %d %d %d\t", chan.id, wBit, p[0], p[1], p[2]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Translation:
|
||||
{
|
||||
atInt32 val1 = dequantize(data, chan.q[0]);
|
||||
p[0] += val1;
|
||||
atInt32 val2 = dequantize(data, chan.q[1]);
|
||||
p[1] += val2;
|
||||
atInt32 val3 = dequantize(data, chan.q[2]);
|
||||
p[2] += val3;
|
||||
kit->push_back({p[0] * transMult, p[1] * transMult, p[2] * transMult});
|
||||
#if DUMP_KEYS
|
||||
fprintf(stderr, "%d T: %d %d %d\t", chan.id, p[0], p[1], p[2]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Scale:
|
||||
{
|
||||
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] * scaleMult, p[1] * scaleMult, p[2] * scaleMult});
|
||||
#if DUMP_KEYS
|
||||
fprintf(stderr, "%d S: %d %d %d\t", chan.id, p[0], p[1], p[2]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case Channel::Type::KfHead:
|
||||
{
|
||||
dequantizeBit(data);
|
||||
break;
|
||||
}
|
||||
case Channel::Type::RotationMP3:
|
||||
{
|
||||
atInt32 val1 = dequantize(data, chan.q[0]);
|
||||
p[0] += val1;
|
||||
atInt32 val2 = dequantize(data, chan.q[1]);
|
||||
p[1] += val2;
|
||||
atInt32 val3 = dequantize(data, chan.q[2]);
|
||||
p[2] += val3;
|
||||
atInt32 val4 = dequantize(data, chan.q[3]);
|
||||
p[3] += val4;
|
||||
QuantizedRot qr = {{p[1], p[2], p[3]}, bool(p[0] & 0x1)};
|
||||
kit->emplace_back(DequantizeRotation_3(qr, rotDiv));
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
++kit;
|
||||
++ait;
|
||||
}
|
||||
#if DUMP_KEYS
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
case Channel::Type::Scale: {
|
||||
keys.push_back({chan.i[0] * scaleMult, chan.i[1] * scaleMult, chan.i[2] * scaleMult});
|
||||
break;
|
||||
}
|
||||
|
||||
return chanKeys;
|
||||
}
|
||||
|
||||
void BitstreamWriter::quantizeBit(atUint8* data, bool val)
|
||||
{
|
||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
||||
atUint32 bitRem = m_bitCur % 32;
|
||||
|
||||
/* Fill 32 bit buffer with region containing bits */
|
||||
/* Make them least significant */
|
||||
*(atUint32*)(data + byteCur) =
|
||||
hecl::SBig(hecl::SBig(*(atUint32*)(data + byteCur)) | (val << bitRem));
|
||||
|
||||
m_bitCur += 1;
|
||||
}
|
||||
|
||||
void BitstreamWriter::quantize(atUint8* data, atUint8 q, atInt32 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)));
|
||||
case Channel::Type::KfHead: {
|
||||
break;
|
||||
}
|
||||
case Channel::Type::RotationMP3: {
|
||||
QuantizedRot qr = {{chan.i[1], chan.i[2], chan.i[3]}, bool(chan.i[0] & 0x1)};
|
||||
keys.emplace_back(DequantizeRotation_3(qr, rotDiv));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_bitCur += q;
|
||||
}
|
||||
|
||||
std::unique_ptr<atUint8[]>
|
||||
BitstreamWriter::write(const std::vector<std::vector<Value>>& chanKeys,
|
||||
size_t keyFrameCount, std::vector<Channel>& channels,
|
||||
atUint32 quantRange,
|
||||
atUint32& rotDivOut,
|
||||
float& transMultOut,
|
||||
float& scaleMultOut,
|
||||
size_t& sizeOut)
|
||||
{
|
||||
m_bitCur = 0;
|
||||
rotDivOut = quantRange; /* Normalized range of values */
|
||||
float quantRangeF = float(quantRange);
|
||||
|
||||
/* Pre-pass to calculate translation multiplier */
|
||||
float maxTransVal = 0.0f;
|
||||
float maxScaleVal = 0.0f;
|
||||
for (size_t f = 0; f < keyFrameCount; ++f) {
|
||||
#if DUMP_KEYS
|
||||
fprintf(stderr, "\nFRAME %" PRISize " %u %u\n", f, (m_bitCur / 32) * 4, m_bitCur % 32);
|
||||
int lastId = -1;
|
||||
#endif
|
||||
auto kit = chanKeys.begin();
|
||||
for (Channel& chan : channels)
|
||||
{
|
||||
switch (chan.type)
|
||||
{
|
||||
case Channel::Type::Translation:
|
||||
{
|
||||
for (auto it=kit->begin();
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
const Value* key = &*it;
|
||||
zeus::simd_floats f(key->simd);
|
||||
maxTransVal = std::max(maxTransVal, std::fabs(f[0]));
|
||||
maxTransVal = std::max(maxTransVal, std::fabs(f[1]));
|
||||
maxTransVal = std::max(maxTransVal, std::fabs(f[2]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Scale:
|
||||
{
|
||||
for (auto it=kit->begin();
|
||||
it != kit->end();
|
||||
++it)
|
||||
{
|
||||
const Value* key = &*it;
|
||||
zeus::simd_floats f(key->simd);
|
||||
maxScaleVal = std::max(maxScaleVal, std::fabs(f[0]));
|
||||
maxScaleVal = std::max(maxScaleVal, std::fabs(f[1]));
|
||||
maxScaleVal = std::max(maxScaleVal, std::fabs(f[2]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
++kit;
|
||||
auto ait = chanAccum.begin();
|
||||
for (const Channel& chan : channels) {
|
||||
#if DUMP_KEYS
|
||||
if (chan.id != lastId) {
|
||||
lastId = chan.id;
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
QuantizedValue& p = *ait;
|
||||
switch (chan.type) {
|
||||
case Channel::Type::Rotation: {
|
||||
bool wBit = dequantizeBit(data);
|
||||
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));
|
||||
#if DUMP_KEYS
|
||||