mirror of https://github.com/AxioDL/metaforce.git
The Great Removal
This commit is contained in:
parent
7186f5d4e5
commit
13b1ebb12e
|
@ -14,14 +14,6 @@
|
|||
path = extern/jbus
|
||||
url = ../jbus.git
|
||||
branch = master
|
||||
[submodule "extern/tinyxml2"]
|
||||
path = extern/tinyxml2
|
||||
url = ../tinyxml2.git
|
||||
branch = master
|
||||
[submodule "extern/sanitizers-cmake"]
|
||||
path = extern/sanitizers-cmake
|
||||
url = https://github.com/arsenm/sanitizers-cmake.git
|
||||
branch = master
|
||||
[submodule "extern/discord-rpc"]
|
||||
path = extern/discord-rpc
|
||||
url = https://github.com/discordapp/discord-rpc.git
|
||||
|
@ -34,10 +26,6 @@
|
|||
path = extern/fixNES
|
||||
url = https://github.com/FIX94/fixNES.git
|
||||
branch = master
|
||||
[submodule "extern/libSquish"]
|
||||
path = extern/libSquish
|
||||
url = ../libSquish.git
|
||||
branch = master
|
||||
[submodule "extern/athena"]
|
||||
path = extern/athena
|
||||
url = ../../libAthena/athena.git
|
||||
|
@ -66,3 +54,6 @@
|
|||
[submodule "extern/nativefiledialog"]
|
||||
path = extern/nativefiledialog
|
||||
url = https://github.com/mlabbe/nativefiledialog.git
|
||||
[submodule "extern/optick"]
|
||||
path = extern/optick
|
||||
url = https://github.com/AxioDL/optick.git
|
||||
|
|
|
@ -92,15 +92,6 @@ if(APPLE AND NOT CMAKE_OSX_SYSROOT)
|
|||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
|
||||
option(METAFORCE_CROSSCOMPILING "Don't build tools; attempt package import" OFF)
|
||||
if (METAFORCE_CROSSCOMPILING)
|
||||
set(CMAKE_CROSSCOMPILING On)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
set(HAVE_WORDS_BIGENDIAN_EXITCODE 0 CACHE INTEGER "Makes soxr happy" FORCE)
|
||||
endif()
|
||||
|
||||
# MSVC has a "latest" flag, which always uses the newest standard
|
||||
# when available. GCC and Clang posess no such flag, and must be
|
||||
# manually enforced. CMake, curiously, also doesn't have a "latest"
|
||||
|
@ -346,45 +337,15 @@ if (NOT WIN32)
|
|||
set(ZLIB_LIBRARIES ZLIB::ZLIB CACHE STRING "zlib libraries" FORCE)
|
||||
endif()
|
||||
|
||||
# TODO migrate bintoc
|
||||
include(hecl/ApplicationTools.cmake)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(bintoc
|
||||
SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/bintoc"
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --config Release --target install)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/bintoc/bintocHelpers.cmake)
|
||||
|
||||
add_subdirectory(extern)
|
||||
|
||||
set(DATA_SPEC_LIBS RetroDataSpec AssetNameMap)
|
||||
set(HECL_DATASPEC_DECLS
|
||||
"/* RetroCommon specs */
|
||||
namespace DataSpec
|
||||
{
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP1;
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP1PC;
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP1ORIG;
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP2;
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP2PC;
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP2ORIG;
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP3;
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP3PC;
|
||||
extern hecl::Database::DataSpecEntry SpecEntMP3ORIG;
|
||||
}")
|
||||
set(HECL_DATASPEC_PUSHES
|
||||
" /* RetroCommon */
|
||||
hecl::Database::DATA_SPEC_REGISTRY.reserve(9);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1PC);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1ORIG);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2PC);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2ORIG);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3PC);
|
||||
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3ORIG);")
|
||||
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(hecl EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hecl-full PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
target_include_directories(hecl-light PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
target_link_libraries(hecl-full PRIVATE zeus nod)
|
||||
target_link_libraries(hecl-light PRIVATE zeus nod)
|
||||
|
||||
if(NOT TARGET atdna)
|
||||
# Import native atdna if cross-compiling
|
||||
|
@ -394,65 +355,20 @@ if(NOT TARGET atdna)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
add_subdirectory(assetnameparser EXCLUDE_FROM_ALL)
|
||||
endif ()
|
||||
add_compile_definitions(URDE_ZIP_INPUT_STREAM=1) # Enable CZipInputStream now that zlib header is known
|
||||
add_subdirectory(DataSpec EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(NESEmulator EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(aurora)
|
||||
add_subdirectory(Runtime)
|
||||
add_subdirectory(mpcksum EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(gbalink EXCLUDE_FROM_ALL)
|
||||
|
||||
if (NOT WINDOWS_STORE AND NOT NX)
|
||||
if (APPLE AND EXISTS /opt/local/libexec/qt5)
|
||||
# macports qt5 (build with +universal)
|
||||
set(Qt5Widgets_DIR /opt/local/libexec/qt5)
|
||||
elseif (APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
|
||||
set(QT_HOMEBREW_PATH /usr/local/opt/qt)
|
||||
elseif (APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL arm64)
|
||||
set(QT_HOMEBREW_PATH /opt/homebrew/opt/qt)
|
||||
else ()
|
||||
set(QT_HOMEBREW_PATH "")
|
||||
endif ()
|
||||
|
||||
find_package(Qt6Widgets QUIET PATHS ${QT_HOMEBREW_PATH})
|
||||
find_package(Qt5Widgets QUIET PATHS ${QT_HOMEBREW_PATH})
|
||||
if (Qt6Widgets_FOUND)
|
||||
message(STATUS "Qt6 found, metaforce-gui will be built")
|
||||
add_subdirectory(metaforce-gui EXCLUDE_FROM_ALL)
|
||||
elseif(Qt5Widgets_FOUND)
|
||||
message(STATUS "Qt5 found, metaforce-gui will be built")
|
||||
add_subdirectory(metaforce-gui EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
message(STATUS "Qt5-6 not found, metaforce-gui will not be built")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_BINARY_DIR}/version.h)
|
||||
|
||||
# Packaging logic
|
||||
list(APPEND BINARY_TARGETS metaforce) # hecl visigen
|
||||
list(APPEND BINARY_TARGETS metaforce)
|
||||
set(DSYM_ONLY_TARGETS "")
|
||||
if (TARGET crashpad_handler)
|
||||
list(APPEND BINARY_TARGETS crashpad_handler)
|
||||
endif ()
|
||||
set(BIN_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
#if (TARGET metaforce-gui)
|
||||
# if (APPLE)
|
||||
# # app bundle already has all needed binaries
|
||||
# install(TARGETS metaforce-gui DESTINATION ${BIN_PREFIX})
|
||||
# list(APPEND DSYM_ONLY_TARGETS metaforce-gui)
|
||||
# # we have to rename here, cmake is inflexible about bundle naming
|
||||
# install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND rm -fr Metaforce.app)")
|
||||
# install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND mv metaforce-gui.app Metaforce.app)")
|
||||
# set(BIN_PREFIX "${BIN_PREFIX}/Metaforce.app/Contents/MacOS")
|
||||
# else()
|
||||
# list(APPEND BINARY_TARGETS metaforce-gui)
|
||||
# endif ()
|
||||
#endif ()
|
||||
install(TARGETS ${BINARY_TARGETS} DESTINATION ${BIN_PREFIX})
|
||||
if (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
|
||||
foreach (target IN LISTS BINARY_TARGETS DSYM_ONLY_TARGETS)
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
message(STATUS "32-bit asset name map not found; downloading to '${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin'")
|
||||
file(DOWNLOAD "https://axiodl.com/files/AssetNameMap32.dat"
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin SHOW_PROGRESS EXPECTED_HASH SHA1=90b4e941c192eef41c81e60314f348bc787d1336)
|
|
@ -1,3 +0,0 @@
|
|||
message(STATUS "64-bit asset name map not found; downloading to '${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin'")
|
||||
file(DOWNLOAD "https://axiodl.com/files/AssetNameMap64.dat"
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin SHOW_PROGRESS EXPECTED_HASH SHA1=e49c03c9fff66adccec7af8120dda091636513e2)
|
|
@ -1,95 +0,0 @@
|
|||
#include "AssetNameMap.hpp"
|
||||
#include "athena/Compression.hpp"
|
||||
#include "athena/MemoryReader.hpp"
|
||||
|
||||
extern "C" const uint8_t ASSET_NAME_MP32[];
|
||||
extern "C" const size_t ASSET_NAME_MP32_SZ;
|
||||
extern "C" const size_t ASSET_NAME_MP32_DECOMPRESSED_SZ;
|
||||
extern "C" const uint8_t ASSET_NAME_MP64[];
|
||||
extern "C" const size_t ASSET_NAME_MP64_SZ;
|
||||
extern "C" const size_t ASSET_NAME_MP64_DECOMPRESSED_SZ;
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
FMT_STRING("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;
|
||||
|
||||
Log.report(logvisor::Info, FMT_STRING("Initializing asset name database..."));
|
||||
|
||||
/* First load the 32bit map for MP1/2 */
|
||||
if (ASSET_NAME_MP32_DECOMPRESSED_SZ != 0u) {
|
||||
auto* decompressed = new uint8_t[ASSET_NAME_MP32_DECOMPRESSED_SZ];
|
||||
athena::io::Compression::decompressZlib(ASSET_NAME_MP32, ASSET_NAME_MP32_SZ, decompressed,
|
||||
ASSET_NAME_MP32_DECOMPRESSED_SZ);
|
||||
athena::io::MemoryReader ar(decompressed, ASSET_NAME_MP32_DECOMPRESSED_SZ);
|
||||
LoadAssetMap(ar);
|
||||
delete[](decompressed);
|
||||
} else {
|
||||
Log.report(
|
||||
logvisor::Warning,
|
||||
FMT_STRING("AssetNameMap32 unavailable; Assets will not have proper filenames for most files."));
|
||||
}
|
||||
/* Now load the 64bit map for MP3 */
|
||||
if (ASSET_NAME_MP64_DECOMPRESSED_SZ != 0u) {
|
||||
auto* decompressed = new uint8_t[ASSET_NAME_MP64_DECOMPRESSED_SZ];
|
||||
athena::io::Compression::decompressZlib(ASSET_NAME_MP64, ASSET_NAME_MP64_SZ, decompressed,
|
||||
ASSET_NAME_MP64_DECOMPRESSED_SZ);
|
||||
athena::io::MemoryReader ar(decompressed, ASSET_NAME_MP64_DECOMPRESSED_SZ);
|
||||
LoadAssetMap(ar);
|
||||
delete[](decompressed);
|
||||
} else {
|
||||
Log.report(
|
||||
logvisor::Warning,
|
||||
FMT_STRING("AssetNameMap64 unavailable; Assets will not have proper filenames for most files."));
|
||||
}
|
||||
g_AssetNameMapInit = true;
|
||||
}
|
||||
|
||||
const std::string* TranslateIdToName(const UniqueID32& id) {
|
||||
if (g_AssetNameMap.find(id.toUint64()) == g_AssetNameMap.cend())
|
||||
return nullptr;
|
||||
|
||||
return &g_AssetNameMap[id.toUint64()].name;
|
||||
}
|
||||
|
||||
} // namespace DataSpec::AssetNameMap
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include "DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace DataSpec::AssetNameMap {
|
||||
void InitAssetNameMap();
|
||||
const std::string* TranslateIdToName(const UniqueID32&);
|
||||
const std::string* TranslateIdToName(const UniqueID64&);
|
||||
} // namespace DataSpec::AssetNameMap
|
|
@ -1,9 +0,0 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" const uint8_t ASSET_NAME_MP32[] = {0};
|
||||
extern "C" const size_t ASSET_NAME_MP32_SZ = 0;
|
||||
extern "C" const size_t ASSET_NAME_MP32_DECOMPRESSED_SZ = 0;
|
||||
extern "C" const uint8_t ASSET_NAME_MP64[] = {0};
|
||||
extern "C" const size_t ASSET_NAME_MP64_SZ = 0;
|
||||
extern "C" const size_t ASSET_NAME_MP64_DECOMPRESSED_SZ = 0;
|
|
@ -1,22 +0,0 @@
|
|||
#include <cstdint>
|
||||
#include "hecl/Blender/Connection.hpp"
|
||||
#include "BlenderSupport.hpp"
|
||||
|
||||
extern "C" uint8_t RETRO_MASTER_SHADER[];
|
||||
extern "C" size_t RETRO_MASTER_SHADER_SZ;
|
||||
|
||||
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 << std::string_view((char*)RETRO_MASTER_SHADER, RETRO_MASTER_SHADER_SZ);
|
||||
os << "make_master_shader_library()\n"sv;
|
||||
}
|
||||
return conn.saveBlend();
|
||||
}
|
||||
|
||||
} // namespace DataSpec::Blender
|
|
@ -1,9 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <hecl/hecl.hpp>
|
||||
|
||||
namespace DataSpec::Blender {
|
||||
|
||||
bool BuildMasterShader(const hecl::ProjectPath& path);
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,91 +0,0 @@
|
|||
# Assembles a source/header pair list for use in a DNA library
|
||||
macro(make_dnalist)
|
||||
file(RELATIVE_PATH subdir "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_LIST_DIR}")
|
||||
set(CMAKE_CURRENT_LIST_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_LIST_BINARY_DIR}")
|
||||
foreach (type ${ARGN})
|
||||
get_filename_component(dir ${type} DIRECTORY)
|
||||
if (dir)
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_LIST_BINARY_DIR}/${dir}")
|
||||
set(dir "${dir}/")
|
||||
endif ()
|
||||
get_filename_component(name ${type} NAME)
|
||||
list(APPEND DNA_SOURCES "${subdir}/${dir}atdna_${name}.cpp")
|
||||
list(APPEND DNA_HEADERS "${subdir}/${dir}${name}.hpp")
|
||||
endforeach ()
|
||||
endmacro()
|
||||
|
||||
# Assembles source files together for the main DataSpecCommon library
|
||||
macro(dataspec_add_list rel_path a_list)
|
||||
unset(tmp_list)
|
||||
foreach (path IN LISTS ${a_list})
|
||||
if (IS_ABSOLUTE ${path})
|
||||
list(APPEND tmp_list "${path}")
|
||||
else ()
|
||||
list(APPEND tmp_list "${rel_path}/${path}")
|
||||
endif ()
|
||||
endforeach (path)
|
||||
set(${a_list} "${tmp_list}")
|
||||
endmacro(dataspec_add_list)
|
||||
|
||||
# Each game's DNA library
|
||||
unset(DNA_SOURCES)
|
||||
unset(DNA_HEADERS)
|
||||
include(DNACommon/CMakeLists.txt)
|
||||
include(DNAMP1/CMakeLists.txt)
|
||||
include(DNAMP2/CMakeLists.txt)
|
||||
include(DNAMP3/CMakeLists.txt)
|
||||
|
||||
# Embed master shader script
|
||||
bintoc(RetroMasterShader.cpp Blender/RetroMasterShader.py RETRO_MASTER_SHADER)
|
||||
|
||||
# Download asset name databases
|
||||
add_custom_command(OUTPUT AssetNameMap32.bin COMMAND ${CMAKE_COMMAND} ARGS -P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AssetMap32Download.cmake)
|
||||
bintoc_compress(AssetNameMap32.cpp ${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin ASSET_NAME_MP32)
|
||||
|
||||
add_custom_command(OUTPUT AssetNameMap64.bin COMMAND ${CMAKE_COMMAND} ARGS -P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AssetMap64Download.cmake)
|
||||
bintoc_compress(AssetNameMap64.cpp ${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin ASSET_NAME_MP64)
|
||||
|
||||
# Each game's DataSpec implementation
|
||||
add_library(RetroDataSpec
|
||||
SpecBase.cpp
|
||||
${DNACOMMON_SOURCES}
|
||||
SpecMP1.cpp
|
||||
${DNAMP1_SOURCES}
|
||||
${ScriptObjectsMP1_SOURCES}
|
||||
${DNAMP1_SFX_SOURCES}
|
||||
SpecMP2.cpp
|
||||
${DNAMP2_SOURCES}
|
||||
SpecMP3.cpp
|
||||
${DNAMP3_SOURCES}
|
||||
Blender/BlenderSupport.hpp
|
||||
Blender/BlenderSupport.cpp
|
||||
Blender/RetroMasterShader.py
|
||||
AssetNameMap.hpp
|
||||
AssetNameMap.cpp
|
||||
RetroMasterShader.cpp)
|
||||
add_library(AssetNameMap
|
||||
AssetNameMap32.bin AssetNameMap32.cpp
|
||||
AssetNameMap64.bin AssetNameMap64.cpp)
|
||||
add_library(AssetNameMapNull
|
||||
AssetNameMapNull.cpp)
|
||||
|
||||
get_target_property(HECL_INCLUDES hecl-full INCLUDE_DIRECTORIES)
|
||||
target_include_directories(RetroDataSpec PUBLIC ${HECL_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR})
|
||||
target_link_libraries(RetroDataSpec PUBLIC amuse zeus nod squish ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} lzokay logvisor aurora xxhash)
|
||||
if (COMMAND add_sanitizers)
|
||||
add_sanitizers(RetroDataSpec)
|
||||
endif ()
|
||||
|
||||
# Resolve all DNA sources into target
|
||||
list(LENGTH DNA_SOURCES count)
|
||||
math(EXPR count "${count}-1")
|
||||
foreach (i RANGE ${count})
|
||||
list(GET DNA_SOURCES ${i} src)
|
||||
list(GET DNA_HEADERS ${i} header)
|
||||
target_atdna(RetroDataSpec ${src} ${header})
|
||||
endforeach ()
|
||||
|
||||
add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_PROPERTY:RetroDataSpec,INCLUDE_DIRECTORIES>")
|
|
@ -1,282 +0,0 @@
|
|||
#include "DataSpec/DNACommon/ANCS.hpp"
|
||||
|
||||
#include "DataSpec/DNACommon/CMDL.hpp"
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
#include "DataSpec/DNACommon/RigInverter.hpp"
|
||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
||||
#include "DataSpec/DNAMP1/ANCS.hpp"
|
||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
||||
#include "DataSpec/DNAMP2/ANCS.hpp"
|
||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
||||
#include "DataSpec/DNAMP3/CHAR.hpp"
|
||||
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
|
||||
namespace DataSpec::DNAANCS {
|
||||
|
||||
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
|
||||
bool ReadANCSToBlender(hecl::blender::Token& btok, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
|
||||
std::function<void(const char*)> fileChanged, bool force) {
|
||||
auto& conn = btok.getBlenderConnection();
|
||||
/* Extract character CMDL/CSKR/CINF first */
|
||||
std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo;
|
||||
ancs.getCharacterResInfo(chResInfo);
|
||||
for (const auto& info : chResInfo) {
|
||||
const nod::Node* node;
|
||||
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, &node, true, false)) {
|
||||
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);
|
||||
fileChanged(bestName.c_str());
|
||||
|
||||
typename ANCSDNA::CSKRType cskr;
|
||||
pakRouter.lookupAndReadDNA(info.cskr, cskr);
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(info.cinf, cinf);
|
||||
using RigPair = std::pair<std::pair<typename PAKRouter::IDType, typename ANCSDNA::CSKRType*>,
|
||||
std::pair<typename PAKRouter::IDType, typename ANCSDNA::CINFType*>>;
|
||||
RigPair rigPair({info.cskr, &cskr}, {info.cinf, &cinf});
|
||||
|
||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>(
|
||||
conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
|
||||
|
||||
conn.saveBlend();
|
||||
}
|
||||
}
|
||||
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf, &node, true, false)) {
|
||||
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
|
||||
if (cinfPath.getPathType() == hecl::ProjectPath::Type::None) {
|
||||
PAKEntryReadStream rs = cinfE->beginReadStream(*node);
|
||||
ANCSDNA::CINFType::Extract(dataspec, rs, cinfPath, pakRouter, *cinfE, false, btok, fileChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract attachment CMDL/CSKR/CINFs first */
|
||||
auto attRange = pakRouter.lookupCharacterAttachmentRigs(entry.id);
|
||||
for (auto it = attRange.first; it != attRange.second; ++it) {
|
||||
auto cinfid = it->second.first.cinf;
|
||||
auto cmdlid = it->second.first.cmdl;
|
||||
|
||||
const nod::Node* node;
|
||||
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(cmdlid, &node, true, false)) {
|
||||
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);
|
||||
fileChanged(bestName.c_str());
|
||||
|
||||
const auto* rp = pakRouter.lookupCMDLRigPair(cmdlid);
|
||||
typename ANCSDNA::CSKRType cskr;
|
||||
pakRouter.lookupAndReadDNA(rp->cskr, cskr);
|
||||
typename ANCSDNA::CINFType cinf;
|
||||
pakRouter.lookupAndReadDNA(rp->cinf, cinf);
|
||||
using RigPair = std::pair<std::pair<typename PAKRouter::IDType, typename ANCSDNA::CSKRType*>,
|
||||
std::pair<typename PAKRouter::IDType, typename ANCSDNA::CINFType*>>;
|
||||
RigPair rigPair({rp->cskr, &cskr}, {rp->cinf, &cinf});
|
||||
|
||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>(
|
||||
conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
|
||||
|
||||
conn.saveBlend();
|
||||
}
|
||||
}
|
||||
if (cinfid.isValid()) {
|
||||
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(cinfid, &node, true, false)) {
|
||||
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
|
||||
if (cinfPath.getPathType() == hecl::ProjectPath::Type::None) {
|
||||
PAKEntryReadStream rs = cinfE->beginReadStream(*node);
|
||||
ANCSDNA::CINFType::Extract(dataspec, rs, cinfPath, pakRouter, *cinfE, false, btok, fileChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string bestName = pakRouter.getBestEntryName(entry);
|
||||
fileChanged(bestName.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(FMT_STRING("import bpy\n"
|
||||
"from mathutils import Vector\n"
|
||||
"bpy.context.scene.name = '{}'\n"
|
||||
"bpy.context.scene.hecl_mesh_obj = bpy.context.scene.name\n"
|
||||
"\n"
|
||||
"# Clear Scene\n"
|
||||
"if len(bpy.data.collections):\n"
|
||||
" bpy.data.collections.remove(bpy.data.collections[0])\n"
|
||||
"\n"
|
||||
"actor_data = bpy.context.scene.hecl_sact_data\n"
|
||||
"arm_obj = None\n"),
|
||||
pakRouter.getBestEntryName(entry));
|
||||
|
||||
std::unordered_set<typename PAKRouter::IDType> cinfsDone;
|
||||
for (const auto& info : chResInfo) {
|
||||
/* Provide data to add-on */
|
||||
os.format(FMT_STRING("actor_subtype = actor_data.subtypes.add()\n"
|
||||
"actor_subtype.name = '{}'\n\n"),
|
||||
info.name);
|
||||
|
||||
/* Build CINF if needed */
|
||||
if (cinfsDone.find(info.cinf) == cinfsDone.end()) {
|
||||
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf, nullptr, true, false)) {
|
||||
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
|
||||
os.linkArmature(cinfPath.getAbsolutePath(), fmt::format(FMT_STRING("CINF_{}"), info.cinf));
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.collection.objects.link(obj)\n";
|
||||
}
|
||||
if (cinfsDone.empty()) {
|
||||
firstName = ANCSDNA::CINFType::GetCINFArmatureName(info.cinf);
|
||||
pakRouter.lookupAndReadDNA(info.cinf, firstCinf);
|
||||
}
|
||||
cinfsDone.insert(info.cinf);
|
||||
}
|
||||
os.format(FMT_STRING("arm_obj = bpy.data.objects['CINF_{}']\n"), info.cinf);
|
||||
os << "actor_subtype.linked_armature = arm_obj.name\n";
|
||||
|
||||
/* Link CMDL */
|
||||
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, nullptr, true, false)) {
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
|
||||
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.collection.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(FMT_STRING("overlay.name = '{}'\n"), overlay.first);
|
||||
|
||||
/* Link CMDL */
|
||||
if (const typename PAKRouter::EntryType* cmdlE =
|
||||
pakRouter.lookupEntry(overlay.second.first, nullptr, true, false)) {
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
|
||||
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.collection.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(FMT_STRING("attachment.name = '{}'\n"), it->second.second);
|
||||
|
||||
auto cinfid = it->second.first.cinf;
|
||||
auto cmdlid = it->second.first.cmdl;
|
||||
|
||||
if (cinfid.isValid()) {
|
||||
/* Build CINF if needed */
|
||||
if (cinfsDone.find(cinfid) == cinfsDone.end()) {
|
||||
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(cinfid, nullptr, true, false)) {
|
||||
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
|
||||
os.linkArmature(cinfPath.getAbsolutePath(), fmt::format(FMT_STRING("CINF_{}"), cinfid));
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.collection.objects.link(obj)\n";
|
||||
}
|
||||
if (cinfsDone.empty()) {
|
||||
firstName = ANCSDNA::CINFType::GetCINFArmatureName(cinfid);
|
||||
pakRouter.lookupAndReadDNA(cinfid, firstCinf);
|
||||
}
|
||||
cinfsDone.insert(cinfid);
|
||||
}
|
||||
os.format(FMT_STRING("arm_obj = bpy.data.objects['CINF_{}']\n"), cinfid);
|
||||
os << "attachment.linked_armature = arm_obj.name\n";
|
||||
}
|
||||
|
||||
/* Link CMDL */
|
||||
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(cmdlid, nullptr, true, false)) {
|
||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
||||
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
|
||||
|
||||
/* Attach CMDL to CINF */
|
||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
||||
" bpy.context.scene.collection.objects.link(obj)\n"
|
||||
"obj.parent = arm_obj\n"
|
||||
"obj.parent_type = 'ARMATURE'\n"
|
||||
"attachment.linked_mesh = obj.name\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
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(FMT_STRING("act = bpy.data.actions.new('{}')\n"
|
||||
"act.use_fake_user = True\n"
|
||||
"act.anim_id = '{}'\n"),
|
||||
id.second.name, id.second.animId);
|
||||
anim.sendANIMToBlender(os, inverter, id.second.additive);
|
||||
}
|
||||
|
||||
os.format(FMT_STRING("actor_action = actor_data.actions.add()\n"
|
||||
"actor_action.name = '{}'\n"),
|
||||
id.second.name);
|
||||
|
||||
/* Extract EVNT if present */
|
||||
anim.extractEVNT(id.second, outPath, pakRouter, force);
|
||||
}
|
||||
}
|
||||
conn.saveBlend();
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool
|
||||
ReadANCSToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::ANCS, DNAMP1::MaterialSet, DNACMDL::SurfaceHeader_1, 2>(
|
||||
hecl::blender::Token& btok, 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 char*)> fileChanged, bool force);
|
||||
template bool
|
||||
ReadANCSToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::ANCS, DNAMP2::MaterialSet, DNACMDL::SurfaceHeader_2, 4>(
|
||||
hecl::blender::Token& btok, 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 char*)> fileChanged, bool force);
|
||||
template bool
|
||||
ReadANCSToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::CHAR, DNAMP3::MaterialSet, DNACMDL::SurfaceHeader_3, 4>(
|
||||
hecl::blender::Token& btok, 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 char*)> fileChanged, bool force);
|
||||
|
||||
} // namespace DataSpec::DNAANCS
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "athena/Types.hpp"
|
||||
#include "hecl/Blender/Connection.hpp"
|
||||
|
||||
namespace DataSpec {
|
||||
struct SpecBase;
|
||||
}
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNAANCS {
|
||||
|
||||
using Actor = hecl::blender::Actor;
|
||||
using Armature = Actor::ActorArmature;
|
||||
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;
|
||||
};
|
||||
|
||||
template <typename IDTYPE>
|
||||
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::Token& btok, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
|
||||
std::function<void(const char*)> fileChanged, bool force = false);
|
||||
|
||||
} // namespace DataSpec::DNAANCS
|
|
@ -1,462 +0,0 @@
|
|||
#include "DataSpec/DNACommon/ANIM.hpp"
|
||||
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include <hecl/hecl.hpp>
|
||||
#include <zeus/Global.hpp>
|
||||
#include <zeus/Math.hpp>
|
||||
|
||||
#define DUMP_KEYS 0
|
||||
|
||||
#if DUMP_KEYS
|
||||
#include <cstdio>
|
||||
#include <fmt/format.h>
|
||||
#endif
|
||||
|
||||
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;
|
||||
[[fallthrough]];
|
||||
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;
|
||||
}
|
||||
|
||||
static QuantizedRot QuantizeRotation(const Value& quat, atUint32 div) {
|
||||
float q = float(div) / (M_PIF / 2.0f);
|
||||
zeus::simd_floats f(quat.simd);
|
||||
assert(std::abs(f[1]) <= 1.f && "Out of range quat X component");
|
||||
assert(std::abs(f[2]) <= 1.f && "Out of range quat Y component");
|
||||
assert(std::abs(f[3]) <= 1.f && "Out of range quat Z component");
|
||||
return {{
|
||||
atInt32(std::asin(f[1]) * q),
|
||||
atInt32(std::asin(f[2]) * q),
|
||||
atInt32(std::asin(f[3]) * q),
|
||||
},
|
||||
(f[0] < 0.f)};
|
||||
}
|
||||
|
||||
static 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 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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t f = 0; f < keyFrameCount; ++f) {
|
||||
#if DUMP_KEYS
|
||||
fmt::print(stderr, FMT_STRING("\nFRAME {} {} {}\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;
|
||||
std::fputc('\n', stderr);
|
||||
}
|
||||
#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
|
||||
fmt::print(stderr, FMT_STRING("{} R: {} {} {} {}\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
|
||||
fmt::print(stderr, FMT_STRING("{} T: {} {} {}\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
|
||||
fmt::print(stderr, FMT_STRING("{} S: {} {} {}\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
|
||||
std::fputc('\n', stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
assert(((((val >> 31) & 0x1) == 0x1) || (((masked >> (q - 1)) & 0x1) == 0)) && "Twos compliment fail");
|
||||
|
||||
/* 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 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 maxTransDelta = 0.0f;
|
||||
float maxScaleDelta = 0.0f;
|
||||
auto kit = chanKeys.begin();
|
||||
for (Channel& chan : channels) {
|
||||
switch (chan.type) {
|
||||
case Channel::Type::Translation: {
|
||||
zeus::simd<float> lastVal = {};
|
||||
for (auto it = kit->begin(); it != kit->end(); ++it) {
|
||||
const Value* key = &*it;
|
||||
zeus::simd_floats f(key->simd - lastVal);
|
||||
lastVal = key->simd;
|
||||
maxTransDelta = std::max(maxTransDelta, std::fabs(f[0]));
|
||||
maxTransDelta = std::max(maxTransDelta, std::fabs(f[1]));
|
||||
maxTransDelta = std::max(maxTransDelta, std::fabs(f[2]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Scale: {
|
||||
zeus::simd<float> lastVal = {};
|
||||
for (auto it = kit->begin(); it != kit->end(); ++it) {
|
||||
const Value* key = &*it;
|
||||
zeus::simd_floats f(key->simd - lastVal);
|
||||
lastVal = key->simd;
|
||||
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[0]));
|
||||
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[1]));
|
||||
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[2]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++kit;
|
||||
}
|
||||
transMultOut = maxTransDelta / quantRangeF + FLT_EPSILON;
|
||||
scaleMultOut = maxScaleDelta / quantRangeF + FLT_EPSILON;
|
||||
|
||||
/* Output channel inits */
|
||||
std::vector<QuantizedValue> initVals;
|
||||
initVals.reserve(channels.size());
|
||||
kit = chanKeys.begin();
|
||||
for (Channel& chan : channels) {
|
||||
chan.q[0] = 1;
|
||||
chan.q[1] = 1;
|
||||
chan.q[2] = 1;
|
||||
switch (chan.type) {
|
||||
case Channel::Type::Rotation: {
|
||||
QuantizedRot qr = QuantizeRotation((*kit)[0], rotDivOut);
|
||||
chan.i = qr.v;
|
||||
initVals.push_back(chan.i);
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Translation: {
|
||||
zeus::simd_floats f((*kit)[0].simd);
|
||||
chan.i = {atInt32(f[0] / transMultOut), atInt32(f[1] / transMultOut), atInt32(f[2] / transMultOut)};
|
||||
initVals.push_back(chan.i);
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Scale: {
|
||||
zeus::simd_floats f((*kit)[0].simd);
|
||||
chan.i = {atInt32(f[0] / scaleMultOut), atInt32(f[1] / scaleMultOut), atInt32(f[2] / scaleMultOut)};
|
||||
initVals.push_back(chan.i);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++kit;
|
||||
}
|
||||
|
||||
/* Pre-pass to analyze quantization factors for channels */
|
||||
std::vector<QuantizedValue> lastVals = initVals;
|
||||
kit = chanKeys.begin();
|
||||
auto vit = lastVals.begin();
|
||||
for (Channel& chan : channels) {
|
||||
QuantizedValue& last = *vit++;
|
||||
switch (chan.type) {
|
||||
case Channel::Type::Rotation: {
|
||||
for (auto it = kit->begin() + 1; it != kit->end(); ++it) {
|
||||
QuantizedRot qrCur = QuantizeRotation(*it, rotDivOut);
|
||||
chan.q[0] = std::max(chan.q[0], atUint8(qrCur.v.qFrom(last, 0)));
|
||||
chan.q[1] = std::max(chan.q[1], atUint8(qrCur.v.qFrom(last, 1)));
|
||||
chan.q[2] = std::max(chan.q[2], atUint8(qrCur.v.qFrom(last, 2)));
|
||||
last = qrCur.v;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Translation: {
|
||||
for (auto it = kit->begin() + 1; it != kit->end(); ++it) {
|
||||
zeus::simd_floats f(it->simd);
|
||||
QuantizedValue cur = {atInt32(f[0] / transMultOut), atInt32(f[1] / transMultOut), atInt32(f[2] / transMultOut)};
|
||||
chan.q[0] = std::max(chan.q[0], atUint8(cur.qFrom(last, 0)));
|
||||
chan.q[1] = std::max(chan.q[1], atUint8(cur.qFrom(last, 1)));
|
||||
chan.q[2] = std::max(chan.q[2], atUint8(cur.qFrom(last, 2)));
|
||||
last = cur;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Scale: {
|
||||
for (auto it = kit->begin() + 1; it != kit->end(); ++it) {
|
||||
zeus::simd_floats f(it->simd);
|
||||
QuantizedValue cur = {atInt32(f[0] / scaleMultOut), atInt32(f[1] / scaleMultOut), atInt32(f[2] / scaleMultOut)};
|
||||
chan.q[0] = std::max(chan.q[0], atUint8(cur.qFrom(last, 0)));
|
||||
chan.q[1] = std::max(chan.q[1], atUint8(cur.qFrom(last, 1)));
|
||||
chan.q[2] = std::max(chan.q[2], atUint8(cur.qFrom(last, 2)));
|
||||
last = cur;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++kit;
|
||||
}
|
||||
|
||||
/* Generate Bitstream */
|
||||
sizeOut = ComputeBitstreamSize(keyFrameCount, channels);
|
||||
std::unique_ptr<atUint8[]> newData(new atUint8[sizeOut]);
|
||||
memset(newData.get(), 0, sizeOut);
|
||||
|
||||
lastVals = initVals;
|
||||
for (size_t frame = 0; frame < keyFrameCount; ++frame) {
|
||||
kit = chanKeys.begin();
|
||||
vit = lastVals.begin();
|
||||
for (const Channel& chan : channels) {
|
||||
const Value& val = (*kit++)[frame + 1];
|
||||
QuantizedValue& last = *vit++;
|
||||
switch (chan.type) {
|
||||
case Channel::Type::Rotation: {
|
||||
QuantizedRot qrCur = QuantizeRotation(val, rotDivOut);
|
||||
quantizeBit(newData.get(), qrCur.w);
|
||||
quantize(newData.get(), chan.q[0], qrCur.v[0] - last.v[0]);
|
||||
quantize(newData.get(), chan.q[1], qrCur.v[1] - last.v[1]);
|
||||
quantize(newData.get(), chan.q[2], qrCur.v[2] - last.v[2]);
|
||||
last = qrCur.v;
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Translation: {
|
||||
zeus::simd_floats f(val.simd);
|
||||
QuantizedValue cur = {atInt32(f[0] / transMultOut), atInt32(f[1] / transMultOut), atInt32(f[2] / transMultOut)};
|
||||
quantize(newData.get(), chan.q[0], cur[0] - last[0]);
|
||||
quantize(newData.get(), chan.q[1], cur[1] - last[1]);
|
||||
quantize(newData.get(), chan.q[2], cur[2] - last[2]);
|
||||
last = cur;
|
||||
break;
|
||||
}
|
||||
case Channel::Type::Scale: {
|
||||
zeus::simd_floats f(val.simd);
|
||||
QuantizedValue cur = {atInt32(f[0] / scaleMultOut), atInt32(f[1] / scaleMultOut), atInt32(f[2] / scaleMultOut)};
|
||||
quantize(newData.get(), chan.q[0], cur[0] - last[0]);
|
||||
quantize(newData.get(), chan.q[1], cur[1] - last[1]);
|
||||
quantize(newData.get(), chan.q[2], cur[2] - last[2]);
|
||||
last = cur;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return newData;
|
||||
}
|
||||
|
||||
} // namespace DataSpec::DNAANIM
|
|
@ -1,73 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <athena/Types.hpp>
|
||||
|
||||
namespace DataSpec::DNAANIM {
|
||||
|
||||
struct Value {
|
||||
athena::simd<float> simd;
|
||||
Value() = default;
|
||||
Value(const athena::simd<float>& s) : simd(s) {}
|
||||
Value(const atVec3f& v) : simd(v.simd) {}
|
||||
Value(const atVec4f& v) : simd(v.simd) {}
|
||||
Value(float x, float y, float z) : simd(x, y, z, 0.f) {}
|
||||
Value(float w, float x, float y, float z) : simd(w, x, y, z) {}
|
||||
};
|
||||
struct QuantizedValue {
|
||||
atInt32 v[4];
|
||||
atInt32& operator[](size_t idx) { return v[idx]; }
|
||||