mirror of
https://github.com/AxioDL/metaforce.git
synced 2025-12-10 02:27:42 +00:00
The Great Removal
This commit is contained in:
15
.gitmodules
vendored
15
.gitmodules
vendored
@@ -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]; }
|
||||
atInt32 operator[](size_t idx) const { return v[idx]; }
|
||||
|
||||
int qFrom(const QuantizedValue& other, size_t idx) const {
|
||||
atInt32 delta = v[idx] - other.v[idx];
|
||||
atInt32 absDelta = std::abs(delta);
|
||||
if (absDelta == 0)
|
||||
return 1;
|
||||
int ret = int(std::ceil(std::log2(absDelta))) + 1;
|
||||
if (delta > 0 && (delta >> (ret - 1)))
|
||||
++ret;
|
||||
assert(ret <= 24 && "Bad q value");
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
struct QuantizedRot {
|
||||
QuantizedValue v;
|
||||
bool w;
|
||||
};
|
||||
struct Channel {
|
||||
enum class Type { Rotation, Translation, Scale, KfHead, RotationMP3 } type;
|
||||
atInt32 id = -1;
|
||||
QuantizedValue i = {};
|
||||
atUint8 q[4] = {};
|
||||
};
|
||||
|
||||
size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector<Channel>& channels);
|
||||
|
||||
class BitstreamReader {
|
||||
size_t m_bitCur;
|
||||
atInt32 dequantize(const atUint8* data, atUint8 q);
|
||||
bool dequantizeBit(const atUint8* data);
|
||||
|
||||
public:
|
||||
std::vector<std::vector<Value>> read(const atUint8* data, size_t keyFrameCount, const std::vector<Channel>& channels,
|
||||
atUint32 rotDiv, float transMult, float scaleMult);
|
||||
};
|
||||
|
||||
class BitstreamWriter {
|
||||
size_t m_bitCur;
|
||||
void quantize(atUint8* data, atUint8 q, atInt32 val);
|
||||
void quantizeBit(atUint8* data, bool val);
|
||||
|
||||
public:
|
||||
std::unique_ptr<atUint8[]> 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);
|
||||
};
|
||||
|
||||
} // namespace DataSpec::DNAANIM
|
||||
@@ -1,440 +0,0 @@
|
||||
#include "AROTBuilder.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "hecl/Blender/Connection.hpp"
|
||||
#include "PATH.hpp"
|
||||
|
||||
namespace DataSpec {
|
||||
logvisor::Module Log("AROTBuilder");
|
||||
|
||||
constexpr s32 AROT_MAX_LEVEL = 10;
|
||||
constexpr s32 AROT_MIN_MODELS = 8;
|
||||
constexpr s32 COLLISION_MIN_NODE_TRIANGLES = 8;
|
||||
constexpr s32 PATH_MIN_NODE_REGIONS = 16;
|
||||
constexpr float AROT_MIN_SUBDIV = 8.f;
|
||||
|
||||
static zeus::CAABox SplitAABB(const zeus::CAABox& aabb, int i) {
|
||||
zeus::CAABox pos, neg;
|
||||
aabb.splitZ(neg, pos);
|
||||
if (i & 4) {
|
||||
zeus::CAABox(pos).splitY(neg, pos);
|
||||
if (i & 2) {
|
||||
zeus::CAABox(pos).splitX(neg, pos);
|
||||
if (i & 1)
|
||||
return pos;
|
||||
else
|
||||
return neg;
|
||||
} else {
|
||||
zeus::CAABox(neg).splitX(neg, pos);
|
||||
if (i & 1)
|
||||
return pos;
|
||||
else
|
||||
return neg;
|
||||
}
|
||||
} else {
|
||||
zeus::CAABox(neg).splitY(neg, pos);
|
||||
if (i & 2) {
|
||||
zeus::CAABox(pos).splitX(neg, pos);
|
||||
if (i & 1)
|
||||
return pos;
|
||||
else
|
||||
return neg;
|
||||
} else {
|
||||
zeus::CAABox(neg).splitX(neg, pos);
|
||||
if (i & 1)
|
||||
return pos;
|
||||
else
|
||||
return neg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AROTBuilder::Node::mergeSets(int a, int b) {
|
||||
childNodes[a].childIndices.insert(childNodes[b].childIndices.cbegin(), childNodes[b].childIndices.cend());
|
||||
childNodes[b].childIndices = childNodes[a].childIndices;
|
||||
}
|
||||
|
||||
bool AROTBuilder::Node::compareSets(int a, int b) const {
|
||||
return childNodes[a].childIndices != childNodes[b].childIndices;
|
||||
}
|
||||
|
||||
void AROTBuilder::Node::addChild(int level, int minChildren, const std::vector<zeus::CAABox>& triBoxes,
|
||||
const zeus::CAABox& curAABB, BspNodeType& typeOut) {
|
||||
/* Gather intersecting faces */
|
||||
for (size_t i = 0; i < triBoxes.size(); ++i)
|
||||
if (triBoxes[i].intersects(curAABB))
|
||||
childIndices.insert(i);
|
||||
|
||||
zeus::CVector3f extents = curAABB.extents();
|
||||
|
||||
/* Return early if empty, triangle intersection below performance threshold, or at max level */
|
||||
if (childIndices.empty()) {
|
||||
typeOut = BspNodeType::Invalid;
|
||||
return;
|
||||
} else if (childIndices.size() < minChildren || level == AROT_MAX_LEVEL ||
|
||||
std::max(extents.x(), std::max(extents.y(), extents.z())) < AROT_MIN_SUBDIV) {
|
||||
typeOut = BspNodeType::Leaf;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Subdivide */
|
||||
typeOut = BspNodeType::Branch;
|
||||
childNodes.resize(8);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
BspNodeType chType;
|
||||
childNodes[i].addChild(level + 1, minChildren, triBoxes, SplitAABB(curAABB, i), chType);
|
||||
flags |= int(chType) << (i * 2);
|
||||
}
|
||||
|
||||
/* Unsubdivide minimum axis dimensions */
|
||||
if (extents.x() < AROT_MIN_SUBDIV) {
|
||||
mergeSets(0, 1);
|
||||
mergeSets(4, 5);
|
||||
mergeSets(2, 3);
|
||||
mergeSets(6, 7);
|
||||
}
|
||||
if (extents.y() < AROT_MIN_SUBDIV) {
|
||||
mergeSets(0, 2);
|
||||
mergeSets(1, 3);
|
||||
mergeSets(4, 6);
|
||||
mergeSets(5, 7);
|
||||
}
|
||||
if (extents.z() < AROT_MIN_SUBDIV) {
|
||||
mergeSets(0, 4);
|
||||
mergeSets(1, 5);
|
||||
mergeSets(2, 6);
|
||||
mergeSets(3, 7);
|
||||
}
|
||||
|
||||
/* Unsubdivide */
|
||||
compSubdivs = 0;
|
||||
if (compareSets(0, 1) || compareSets(4, 5) || compareSets(2, 3) || compareSets(6, 7))
|
||||
compSubdivs |= 0x1;
|
||||
if (compareSets(0, 2) || compareSets(1, 3) || compareSets(4, 6) || compareSets(5, 7))
|
||||
compSubdivs |= 0x2;
|
||||
if (compareSets(0, 4) || compareSets(1, 5) || compareSets(2, 6) || compareSets(3, 7))
|
||||
compSubdivs |= 0x4;
|
||||
|
||||
if (!compSubdivs) {
|
||||
typeOut = BspNodeType::Leaf;
|
||||
childNodes = std::vector<Node>();
|
||||
flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t AROTBuilder::BitmapPool::addIndices(const std::set<int>& indices) {
|
||||
for (size_t i = 0; i < m_pool.size(); ++i)
|
||||
if (m_pool[i] == indices)
|
||||
return i;
|
||||
m_pool.push_back(indices);
|
||||
return m_pool.size() - 1;
|
||||
}
|
||||
|
||||
constexpr std::array<uint32_t, 8> AROTChildCounts{
|
||||
0, 2, 2, 4, 2, 4, 4, 8,
|
||||
};
|
||||
|
||||
void AROTBuilder::Node::nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff) {
|
||||
sz += 1;
|
||||
poolIdx = bmpPool.addIndices(childIndices);
|
||||
if (poolIdx > 65535)
|
||||
Log.report(logvisor::Fatal, FMT_STRING("AROT bitmap exceeds 16-bit node addressing; area too complex"));
|
||||
|
||||
uint32_t childCount = AROTChildCounts[compSubdivs];
|
||||
nodeOff = curOff;
|
||||
nodeSz = childCount * 2 + 4;
|
||||
curOff += nodeSz;
|
||||
if (childNodes.size()) {
|
||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
||||
int idx = k * 4 + j * 2 + i;
|
||||
childNodes[idx].nodeCount(sz, idxRefs, bmpPool, curOff);
|
||||
}
|
||||
}
|
||||
}
|
||||
idxRefs += childCount;
|
||||
}
|
||||
}
|
||||
|
||||
void AROTBuilder::Node::writeIndirectionTable(athena::io::MemoryWriter& w) {
|
||||
w.writeUint32Big(nodeOff);
|
||||
if (childNodes.size()) {
|
||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
||||
int idx = k * 4 + j * 2 + i;
|
||||
childNodes[idx].writeIndirectionTable(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx) {
|
||||
w.writeUint16Big(poolIdx);
|
||||
w.writeUint16Big(compSubdivs);
|
||||
|
||||
if (childNodes.size()) {
|
||||
int curIdx = nodeIdx + 1;
|
||||
if (curIdx > 65535)
|
||||
Log.report(logvisor::Fatal, FMT_STRING("AROT node exceeds 16-bit node addressing; area too complex"));
|
||||
|
||||
std::array<int, 8> childIndices;
|
||||
|
||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
||||
int idx = k * 4 + j * 2 + i;
|
||||
w.writeUint16Big(curIdx);
|
||||
childIndices[idx] = curIdx;
|
||||
childNodes[idx].advanceIndex(curIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
||||
int idx = k * 4 + j * 2 + i;
|
||||
childNodes[idx].writeNodes(w, childIndices[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AROTBuilder::Node::advanceIndex(int& nodeIdx) {
|
||||
++nodeIdx;
|
||||
if (childNodes.size()) {
|
||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
||||
int idx = k * 4 + j * 2 + i;
|
||||
childNodes[idx].advanceIndex(nodeIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AROTBuilder::Node::colSize(size_t& totalSz) {
|
||||
if (childIndices.size()) {
|
||||
nodeOff = totalSz;
|
||||
if (childNodes.empty()) {
|
||||
totalSz += 26 + childIndices.size() * 2;
|
||||
} else {
|
||||
totalSz += 36;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
childNodes[i].colSize(totalSz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AROTBuilder::Node::writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB) {
|
||||
if (childIndices.size()) {
|
||||
if (childNodes.empty()) {
|
||||
float* aabbOut = reinterpret_cast<float*>(ptr);
|
||||
aabbOut[0] = hecl::SBig(curAABB.min[0]);
|
||||
aabbOut[1] = hecl::SBig(curAABB.min[1]);
|
||||
aabbOut[2] = hecl::SBig(curAABB.min[2]);
|
||||
aabbOut[3] = hecl::SBig(curAABB.max[0]);
|
||||
aabbOut[4] = hecl::SBig(curAABB.max[1]);
|
||||
aabbOut[5] = hecl::SBig(curAABB.max[2]);
|
||||
athena::io::MemoryWriter w(ptr + 24, INT32_MAX);
|
||||
w.writeUint16Big(childIndices.size());
|
||||
for (int idx : childIndices)
|
||||
w.writeUint16Big(idx);
|
||||
ptr += 26 + childIndices.size() * 2;
|
||||
} else {
|
||||
uint16_t* pflags = reinterpret_cast<uint16_t*>(ptr);
|
||||
uint32_t* offsets = reinterpret_cast<uint32_t*>(ptr + 4);
|
||||
memset(pflags, 0, sizeof(uint32_t) * 9);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const Node& chNode = childNodes[i];
|
||||
BspNodeType type = BspNodeType((flags >> (i * 2)) & 0x3);
|
||||
if (type != BspNodeType::Invalid)
|
||||
offsets[i] = hecl::SBig(uint32_t(chNode.nodeOff - nodeOff - 36));
|
||||
}
|
||||
|
||||
*pflags = hecl::SBig(flags);
|
||||
ptr += 36;
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
childNodes[i].writeColNodes(ptr, SplitAABB(curAABB, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AROTBuilder::Node::pathCountNodesAndLookups(size_t& nodeCount, size_t& lookupCount) {
|
||||
++nodeCount;
|
||||
if (childNodes.empty()) {
|
||||
lookupCount += childIndices.size();
|
||||
} else {
|
||||
for (int i = 0; i < 8; ++i)
|
||||
childNodes[i].pathCountNodesAndLookups(nodeCount, lookupCount);
|
||||
}
|
||||
}
|
||||
|
||||
template <class PAKBridge>
|
||||
void AROTBuilder::Node::pathWrite(DNAPATH::PATH<PAKBridge>& path, const zeus::CAABox& curAABB) {
|
||||
if (childNodes.empty()) {
|
||||
auto& n = path.octree.emplace_back();
|
||||
n.isLeaf = 1;
|
||||
n.aabb[0] = curAABB.min;
|
||||
n.aabb[1] = curAABB.max;
|
||||
n.centroid = curAABB.center();
|
||||
std::fill(std::begin(n.children), std::end(n.children), 0xFFFFFFFF);
|
||||
n.regionCount = childIndices.size();
|
||||
n.regionStart = path.octreeRegionLookup.size();
|
||||
for (int r : childIndices)
|
||||
path.octreeRegionLookup.push_back(r);
|
||||
} else {
|
||||
std::array<atUint32, 8> children;
|
||||
for (size_t i = 0; i < children.size(); ++i) {
|
||||
/* Head recursion (first node will be a leaf) */
|
||||
childNodes[i].pathWrite(path, SplitAABB(curAABB, static_cast<int>(i)));
|
||||
children[i] = path.octree.size() - 1;
|
||||
}
|
||||
|
||||
auto& n = path.octree.emplace_back();
|
||||
n.isLeaf = 0;
|
||||
n.aabb[0] = curAABB.min;
|
||||
n.aabb[1] = curAABB.max;
|
||||
n.centroid = curAABB.center();
|
||||
std::copy(children.cbegin(), children.cend(), std::begin(n.children));
|
||||
n.regionCount = 0;
|
||||
n.regionStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP1::PAKBridge>& path, const zeus::CAABox& curAABB);
|
||||
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP2::PAKBridge>& path, const zeus::CAABox& curAABB);
|
||||
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP3::PAKBridge>& path, const zeus::CAABox& curAABB);
|
||||
|
||||
void AROTBuilder::build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb,
|
||||
const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes) {
|
||||
/* Recursively split */
|
||||
BspNodeType rootType;
|
||||
rootNode.addChild(0, AROT_MIN_MODELS, meshAabbs, fullAabb, rootType);
|
||||
|
||||
/* Calculate indexing metrics */
|
||||
size_t totalNodeCount = 0;
|
||||
size_t idxRefCount = 0;
|
||||
size_t curOff = 0;
|
||||
rootNode.nodeCount(totalNodeCount, idxRefCount, bmpPool, curOff);
|
||||
size_t bmpWordCount = ROUND_UP_32(meshes.size()) / 32;
|
||||
size_t arotSz = 64 + bmpWordCount * bmpPool.m_pool.size() * 4 + totalNodeCount * 8 + idxRefCount * 2;
|
||||
|
||||
/* Write header */
|
||||
secs.emplace_back(arotSz, 0);
|
||||
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
|
||||
w.writeUint32Big('AROT');
|
||||
w.writeUint32Big(1);
|
||||
w.writeUint32Big(bmpPool.m_pool.size());
|
||||
w.writeUint32Big(meshes.size());
|
||||
w.writeUint32Big(totalNodeCount);
|
||||
w.writeVec3fBig(fullAabb.min);
|
||||
w.writeVec3fBig(fullAabb.max);
|
||||
w.seekAlign32();
|
||||
|
||||
/* Write bitmap */
|
||||
std::vector<uint32_t> bmpWords;
|
||||
bmpWords.reserve(bmpWordCount);
|
||||
for (const std::set<int>& bmp : bmpPool.m_pool) {
|
||||
bmpWords.clear();
|
||||
bmpWords.resize(bmpWordCount);
|
||||
|
||||
auto bmpIt = bmp.cbegin();
|
||||
if (bmpIt != bmp.cend()) {
|
||||
int curIdx = 0;
|
||||
for (size_t word = 0; word < bmpWordCount; ++word) {
|
||||
for (u32 b = 0; b < 32; ++b) {
|
||||
if (*bmpIt == curIdx) {
|
||||
bmpWords[word] |= 1U << b;
|
||||
++bmpIt;
|
||||
if (bmpIt == bmp.cend()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++curIdx;
|
||||
}
|
||||
if (bmpIt == bmp.cend()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t word : bmpWords)
|
||||
w.writeUint32Big(word);
|
||||
}
|
||||
|
||||
/* Write the rest */
|
||||
rootNode.writeIndirectionTable(w);
|
||||
rootNode.writeNodes(w, 0);
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<uint8_t[]>, uint32_t> AROTBuilder::buildCol(const ColMesh& mesh, BspNodeType& rootOut) {
|
||||
/* Accumulate total AABB */
|
||||
zeus::CAABox fullAABB;
|
||||
for (const auto& vert : mesh.verts)
|
||||
fullAABB.accumulateBounds(zeus::CVector3f(vert));
|
||||
|
||||
/* Predetermine triangle AABBs */
|
||||
std::vector<zeus::CAABox> triBoxes;
|
||||
triBoxes.reserve(mesh.trianges.size());
|
||||
for (const ColMesh::Triangle& tri : mesh.trianges) {
|
||||
zeus::CAABox& aabb = triBoxes.emplace_back();
|
||||
for (const u32 edgeIdx : tri.edges) {
|
||||
const ColMesh::Edge& edge = mesh.edges[edgeIdx];
|
||||
for (const u32 vertIdx : edge.verts) {
|
||||
const auto& vert = mesh.verts[vertIdx];
|
||||
aabb.accumulateBounds(zeus::CVector3f(vert));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Recursively split */
|
||||
rootNode.addChild(0, COLLISION_MIN_NODE_TRIANGLES, triBoxes, fullAABB, rootOut);
|
||||
|
||||
/* Calculate offsets and write out */
|
||||
size_t totalSize = 0;
|
||||
rootNode.colSize(totalSize);
|
||||
std::unique_ptr<uint8_t[]> ret(new uint8_t[totalSize]);
|
||||
uint8_t* ptr = ret.get();
|
||||
rootNode.writeColNodes(ptr, fullAABB);
|
||||
|
||||
return {std::move(ret), totalSize};
|
||||
}
|
||||
|
||||
template <class PAKBridge>
|
||||
void AROTBuilder::buildPath(DNAPATH::PATH<PAKBridge>& path) {
|
||||
/* Accumulate total AABB and gather region boxes */
|
||||
std::vector<zeus::CAABox> regionBoxes;
|
||||
regionBoxes.reserve(path.regions.size());
|
||||
zeus::CAABox fullAABB;
|
||||
for (const auto& r : path.regions)
|
||||
fullAABB.accumulateBounds(regionBoxes.emplace_back(r.aabb[0], r.aabb[1]));
|
||||
|
||||
/* Recursively split */
|
||||
BspNodeType dontCare;
|
||||
rootNode.addChild(0, PATH_MIN_NODE_REGIONS, regionBoxes, fullAABB, dontCare);
|
||||
|
||||
/* Write out */
|
||||
size_t nodeCount = 0;
|
||||
size_t lookupCount = 0;
|
||||
rootNode.pathCountNodesAndLookups(nodeCount, lookupCount);
|
||||
path.octreeNodeCount = nodeCount;
|
||||
path.octree.reserve(nodeCount);
|
||||
path.octreeRegionLookupCount = lookupCount;
|
||||
path.octreeRegionLookup.reserve(lookupCount);
|
||||
rootNode.pathWrite(path, fullAABB);
|
||||
}
|
||||
|
||||
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP1::PAKBridge>& path);
|
||||
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP2::PAKBridge>& path);
|
||||
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP3::PAKBridge>& path);
|
||||
|
||||
} // namespace DataSpec
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "DNACommon.hpp"
|
||||
#include "DeafBabe.hpp"
|
||||
#include "zeus/CAABox.hpp"
|
||||
#include "CMDL.hpp"
|
||||
#include <athena/MemoryWriter.hpp>
|
||||
#include <set>
|
||||
|
||||
namespace DataSpec {
|
||||
namespace DNAPATH {
|
||||
template <class PAKBridge>
|
||||
struct PATH;
|
||||
}
|
||||
|
||||
struct AROTBuilder {
|
||||
using ColMesh = hecl::blender::ColMesh;
|
||||
|
||||
struct BitmapPool {
|
||||
std::vector<std::set<int>> m_pool;
|
||||
size_t addIndices(const std::set<int>& indices);
|
||||
} bmpPool;
|
||||
|
||||
struct Node {
|
||||
std::vector<Node> childNodes;
|
||||
std::set<int> childIndices;
|
||||
size_t poolIdx = 0;
|
||||
uint16_t flags = 0;
|
||||
uint16_t compSubdivs = 0;
|
||||
|
||||
size_t nodeOff = 0;
|
||||
size_t nodeSz = 4;
|
||||
|
||||
void addChild(int level, int minChildren, const std::vector<zeus::CAABox>& triBoxes, const zeus::CAABox& curAABB,
|
||||
BspNodeType& typeOut);
|
||||
void mergeSets(int a, int b);
|
||||
bool compareSets(int a, int b) const;
|
||||
void nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff);
|
||||
void writeIndirectionTable(athena::io::MemoryWriter& w);
|
||||
void writeNodes(athena::io::MemoryWriter& w, int nodeIdx);
|
||||
void advanceIndex(int& nodeIdx);
|
||||
|
||||
void colSize(size_t& totalSz);
|
||||
void writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB);
|
||||
|
||||
void pathCountNodesAndLookups(size_t& nodeCount, size_t& lookupCount);
|
||||
template <class PAKBridge>
|
||||
void pathWrite(DNAPATH::PATH<PAKBridge>& path, const zeus::CAABox& curAABB);
|
||||
} rootNode;
|
||||
|
||||
void build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb,
|
||||
const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes);
|
||||
std::pair<std::unique_ptr<uint8_t[]>, uint32_t> buildCol(const ColMesh& mesh, BspNodeType& rootOut);
|
||||
template <class PAKBridge>
|
||||
void buildPath(DNAPATH::PATH<PAKBridge>& path);
|
||||
};
|
||||
|
||||
} // namespace DataSpec
|
||||
@@ -1,66 +0,0 @@
|
||||
#include "DataSpec/DNACommon/ATBL.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
#include <athena/FileReader.hpp>
|
||||
#include <athena/FileWriter.hpp>
|
||||
#include <athena/YAMLDocReader.hpp>
|
||||
#include <athena/YAMLDocWriter.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace DataSpec::DNAAudio {
|
||||
|
||||
bool ATBL::Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
||||
uint32_t idxCount = rs.readUint32Big();
|
||||
athena::io::YAMLDocWriter w("ATBL");
|
||||
for (uint32_t i = 0; i < idxCount; ++i) {
|
||||
uint16_t idx = rs.readUint16Big();
|
||||
if (idx == 0xffff)
|
||||
continue;
|
||||
w.writeUint16(fmt::format(FMT_STRING("0x{:04X}"), i), idx);
|
||||
}
|
||||
|
||||
athena::io::FileWriter fw(outPath.getAbsolutePath());
|
||||
w.finish(&fw);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ATBL::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileReader r(inPath.getAbsolutePath());
|
||||
if (r.hasError())
|
||||
return false;
|
||||
|
||||
athena::io::YAMLDocReader dr;
|
||||
if (!dr.parse(&r))
|
||||
return false;
|
||||
|
||||
unsigned long maxI = 0;
|
||||
for (const auto& pair : dr.getRootNode()->m_mapChildren) {
|
||||
unsigned long i = strtoul(pair.first.c_str(), nullptr, 0);
|
||||
maxI = std::max(maxI, i);
|
||||
}
|
||||
|
||||
std::vector<uint16_t> vecOut(maxI + 1, 0xffff);
|
||||
for (const auto& pair : dr.getRootNode()->m_mapChildren) {
|
||||
unsigned long i = strtoul(pair.first.c_str(), nullptr, 0);
|
||||
vecOut[i] = hecl::SBig(uint16_t(strtoul(pair.second->m_scalarString.c_str(), nullptr, 0)));
|
||||
}
|
||||
|
||||
athena::io::FileWriter w(outPath.getAbsolutePath());
|
||||
if (w.hasError())
|
||||
return false;
|
||||
w.writeUint32Big(uint32_t(vecOut.size()));
|
||||
w.writeBytes(vecOut.data(), vecOut.size() * 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace DataSpec::DNAAudio
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace DataSpec {
|
||||
class PAKEntryReadStream;
|
||||
}
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNAAudio {
|
||||
|
||||
class ATBL {
|
||||
public:
|
||||
static bool Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
static bool Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath);
|
||||
};
|
||||
|
||||
} // namespace DataSpec::DNAAudio
|
||||
@@ -1,161 +0,0 @@
|
||||
#include "DataSpec/DNACommon/BabeDead.hpp"
|
||||
|
||||
#include "DataSpec/DNAMP1/MREA.hpp"
|
||||
#include "DataSpec/DNAMP3/MREA.hpp"
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
#include <zeus/CTransform.hpp>
|
||||
#include <zeus/Math.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace DataSpec {
|
||||
|
||||
template <class BabeDeadLight>
|
||||
void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLight& light, unsigned s, unsigned l) {
|
||||
switch (light.lightType) {
|
||||
case BabeDeadLight::LightType::LocalAmbient:
|
||||
case BabeDeadLight::LightType::LocalAmbient2:
|
||||
os.format(FMT_STRING("bg_node.inputs[0].default_value = ({},{},{},1.0)\n"
|
||||
"bg_node.inputs[1].default_value = {}\n"),
|
||||
light.color.simd[0], light.color.simd[1], light.color.simd[2], light.q / 8.f);
|
||||
return;
|
||||
case BabeDeadLight::LightType::Directional:
|
||||
os.format(FMT_STRING("lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'SUN')\n"
|
||||
"lamp.color = ({},{},{})\n"
|
||||
"lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n"
|
||||
"lamp_obj.rotation_mode = 'QUATERNION'\n"
|
||||
"lamp_obj.rotation_quaternion = Vector((0,0,-1)).rotation_difference(Vector(({},{},{})))\n"
|
||||
"lamp.use_shadow = {}\n"
|
||||
"\n"),
|
||||
s, l, light.color.simd[0], light.color.simd[1], light.color.simd[2], light.direction.simd[0],
|
||||
light.direction.simd[1], light.direction.simd[2], light.castShadows ? "True" : "False");
|
||||
return;
|
||||
case BabeDeadLight::LightType::Custom:
|
||||
os.format(FMT_STRING("lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'POINT')\n"
|
||||
"lamp.color = ({},{},{})\n"
|
||||
"lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n"
|
||||
"lamp.shadow_soft_size = 1.0\n"
|
||||
"lamp.use_shadow = {}\n"
|
||||
"\n"),
|
||||
s, l, light.color.simd[0], light.color.simd[1], light.color.simd[2],
|
||||
light.castShadows ? "True" : "False");
|
||||
break;
|
||||
case BabeDeadLight::LightType::Spot:
|
||||
case BabeDeadLight::LightType::Spot2:
|
||||
os.format(FMT_STRING("lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'SPOT')\n"
|
||||
"lamp.color = ({},{},{})\n"
|
||||
"lamp.spot_size = {:.6g}\n"
|
||||
"lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n"
|
||||
"lamp_obj.rotation_mode = 'QUATERNION'\n"
|
||||
"lamp_obj.rotation_quaternion = Vector((0,0,-1)).rotation_difference(Vector(({},{},{})))\n"
|
||||
"lamp.shadow_soft_size = 0.5\n"
|
||||
"lamp.use_shadow = {}\n"
|
||||
"\n"),
|
||||
s, l, light.color.simd[0], light.color.simd[1], light.color.simd[2], zeus::degToRad(light.spotCutoff),
|
||||
light.direction.simd[0], light.direction.simd[1], light.direction.simd[2],
|
||||
light.castShadows ? "True" : "False");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
os.format(FMT_STRING("lamp.retro_layer = {}\n"
|
||||
"lamp.retro_origtype = {}\n"
|
||||
"lamp.falloff_type = 'INVERSE_COEFFICIENTS'\n"
|
||||
"lamp.constant_coefficient = 0\n"
|
||||
"lamp.use_nodes = True\n"
|
||||
"falloff_node = lamp.node_tree.nodes.new('ShaderNodeLightFalloff')\n"
|
||||
"lamp.energy = 0.0\n"
|
||||
"falloff_node.inputs[0].default_value = {}\n"
|
||||
"hue_sat_node = lamp.node_tree.nodes.new('ShaderNodeHueSaturation')\n"
|
||||
"hue_sat_node.inputs[1].default_value = 1.25\n"
|
||||
"hue_sat_node.inputs[4].default_value = ({},{},{},1.0)\n"
|
||||
"lamp.node_tree.links.new(hue_sat_node.outputs[0], lamp.node_tree.nodes['Emission'].inputs[0])\n"
|
||||
"lamp_obj.location = ({},{},{})\n"
|
||||
"bpy.context.scene.collection.objects.link(lamp_obj)\n"
|
||||
"\n"),
|
||||
s, unsigned(light.lightType), light.q / 8.f, light.color.simd[0], light.color.simd[1], light.color.simd[2],
|
||||
light.position.simd[0], light.position.simd[1], light.position.simd[2]);
|
||||
|
||||
switch (light.falloff) {
|
||||
case BabeDeadLight::Falloff::Constant:
|
||||
os << "falloff_node.inputs[0].default_value *= 150.0\n"
|
||||
"lamp.node_tree.links.new(falloff_node.outputs[2], lamp.node_tree.nodes['Emission'].inputs[1])\n";
|
||||
if (light.q > FLT_EPSILON)
|
||||
os.format(FMT_STRING("lamp.constant_coefficient = 2.0 / {}\n"), light.q);
|
||||
break;
|
||||
case BabeDeadLight::Falloff::Linear:
|
||||
os << "lamp.node_tree.links.new(falloff_node.outputs[1], lamp.node_tree.nodes['Emission'].inputs[1])\n";
|
||||
if (light.q > FLT_EPSILON)
|
||||
os.format(FMT_STRING("lamp.linear_coefficient = 250 / {}\n"), light.q);
|
||||
break;
|
||||
case BabeDeadLight::Falloff::Quadratic:
|
||||
os << "lamp.node_tree.links.new(falloff_node.outputs[0], lamp.node_tree.nodes['Emission'].inputs[1])\n";
|
||||
if (light.q > FLT_EPSILON)
|
||||
os.format(FMT_STRING("lamp.quadratic_coefficient = 25000 / {}\n"), light.q);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template void ReadBabeDeadLightToBlender<DNAMP1::MREA::BabeDeadLight>(hecl::blender::PyOutStream& os,
|
||||
const DNAMP1::MREA::BabeDeadLight& light,
|
||||
unsigned s, unsigned l);
|
||||
template void ReadBabeDeadLightToBlender<DNAMP3::MREA::BabeDeadLight>(hecl::blender::PyOutStream& os,
|
||||
const DNAMP3::MREA::BabeDeadLight& light,
|
||||
unsigned s, unsigned l);
|
||||
|
||||
template <class BabeDeadLight>
|
||||
void WriteBabeDeadLightFromBlender(BabeDeadLight& lightOut, const hecl::blender::Light& lightIn) {
|
||||
using InterType = hecl::blender::Light::Type;
|
||||
switch (lightIn.type) {
|
||||
case InterType::Ambient:
|
||||
lightOut.lightType = BabeDeadLight::LightType::LocalAmbient;
|
||||
break;
|
||||
case InterType::Directional:
|
||||
lightOut.lightType = BabeDeadLight::LightType::Directional;
|
||||
break;
|
||||
case InterType::Custom:
|
||||
default:
|
||||
lightOut.lightType = BabeDeadLight::LightType::Custom;
|
||||
break;
|
||||
case InterType::Spot:
|
||||
lightOut.lightType = BabeDeadLight::LightType::Spot;
|
||||
break;
|
||||
}
|
||||
|
||||
if (lightIn.type == InterType::Ambient) {
|
||||
lightOut.falloff = BabeDeadLight::Falloff::Constant;
|
||||
lightOut.q = lightIn.energy * 8.f;
|
||||
} else if (lightIn.linear > lightIn.constant && lightIn.linear > lightIn.quadratic) {
|
||||
lightOut.falloff = BabeDeadLight::Falloff::Linear;
|
||||
lightOut.q = 250.f / lightIn.linear;
|
||||
} else if (lightIn.quadratic > lightIn.constant && lightIn.quadratic > lightIn.linear) {
|
||||
lightOut.falloff = BabeDeadLight::Falloff::Quadratic;
|
||||
lightOut.q = 25000.f / lightIn.quadratic;
|
||||
} else {
|
||||
lightOut.falloff = BabeDeadLight::Falloff::Constant;
|
||||
lightOut.q = 2.f / lightIn.constant;
|
||||
}
|
||||
|
||||
lightOut.color = lightIn.color;
|
||||
lightOut.spotCutoff = zeus::radToDeg(lightIn.spotCutoff);
|
||||
lightOut.castShadows = lightIn.shadow;
|
||||
lightOut.position.simd[0] = lightIn.sceneXf[0].simd[3];
|
||||
lightOut.position.simd[1] = lightIn.sceneXf[1].simd[3];
|
||||
lightOut.position.simd[2] = lightIn.sceneXf[2].simd[3];
|
||||
|
||||
zeus::CTransform lightXf(&lightIn.sceneXf[0]);
|
||||
lightOut.direction = (lightXf.basis.transposed() * zeus::CVector3f(0.f, 0.f, -1.f)).normalized();
|
||||
}
|
||||
|
||||
template void WriteBabeDeadLightFromBlender<DNAMP1::MREA::BabeDeadLight>(DNAMP1::MREA::BabeDeadLight& lightOut,
|
||||
const hecl::blender::Light& lightIn);
|
||||
template void WriteBabeDeadLightFromBlender<DNAMP3::MREA::BabeDeadLight>(DNAMP3::MREA::BabeDeadLight& lightOut,
|
||||
const hecl::blender::Light& lightIn);
|
||||
|
||||
} // namespace DataSpec
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace hecl::blender {
|
||||
struct Light;
|
||||
class PyOutStream;
|
||||
} // namespace hecl::blender
|
||||
|
||||
namespace DataSpec {
|
||||
|
||||
template <class BabeDeadLight>
|
||||
void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLight& light, unsigned s, unsigned l);
|
||||
|
||||
template <class BabeDeadLight>
|
||||
void WriteBabeDeadLightFromBlender(BabeDeadLight& lightOut, const hecl::blender::Light& lightIn);
|
||||
|
||||
} // namespace DataSpec
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,168 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNACommon/GX.hpp"
|
||||
#include "DataSpec/DNACommon/TXTR.hpp"
|
||||
|
||||
#include <athena/DNA.hpp>
|
||||
#include <athena/Types.hpp>
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace zeus {
|
||||
class CAABox;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNACMDL {
|
||||
|
||||
using Mesh = hecl::blender::Mesh;
|
||||
using Material = hecl::blender::Material;
|
||||
|
||||
struct Header : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atUint32> magic;
|
||||
Value<atUint32> version;
|
||||
struct Flags : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atUint32> flags = 0;
|
||||
bool skinned() const { return (flags & 0x1) != 0; }
|
||||
void setSkinned(bool val) {
|
||||
flags &= ~0x1;
|
||||
flags |= val;
|
||||
}
|
||||
bool shortNormals() const { return (flags & 0x2) != 0; }
|
||||
void setShortNormals(bool val) {
|
||||
flags &= ~0x2;
|
||||
flags |= val << 1;
|
||||
}
|
||||
bool shortUVs() const { return (flags & 0x4) != 0; }
|
||||
void setShortUVs(bool val) {
|
||||
flags &= ~0x4;
|
||||
flags |= val << 2;
|
||||
}
|
||||
} flags;
|
||||
Value<atVec3f> aabbMin;
|
||||
Value<atVec3f> aabbMax;
|
||||
Value<atUint32> secCount;
|
||||
Value<atUint32> matSetCount;
|
||||
Vector<atUint32, AT_DNA_COUNT(secCount)> secSizes;
|
||||
Align<32> align;
|
||||
};
|
||||
|
||||
struct SurfaceHeader_1 : BigDNA {
|
||||
AT_DECL_EXPLICIT_DNA
|
||||
Value<atVec3f> centroid;
|
||||
Value<atUint32> matIdx = 0;
|
||||
Value<atUint32> dlSize = 0;
|
||||
Value<atUint32> idxStart = 0; /* Actually used by game to stash CCubeModel pointer */
|
||||
Value<atUint32> idxCount = 0; /* Actually used by game to stash next CCubeSurface pointer */
|
||||
Value<atUint32> aabbSz = 0;
|
||||
Value<atVec3f> reflectionNormal;
|
||||
Value<atVec3f> aabb[2];
|
||||
Align<32> align;
|
||||
|
||||
static constexpr bool UseMatrixSkinning() { return false; }
|
||||
static constexpr atInt16 skinMatrixBankIdx() { return -1; }
|
||||
};
|
||||
|
||||
struct SurfaceHeader_2 : BigDNA {
|
||||
AT_DECL_EXPLICIT_DNA
|
||||
Value<atVec3f> centroid;
|
||||
Value<atUint32> matIdx = 0;
|
||||
Value<atUint32> dlSize = 0;
|
||||
Value<atUint32> idxStart = 0; /* Actually used by game to stash CCubeModel pointer */
|
||||
Value<atUint32> idxCount = 0; /* Actually used by game to stash next CCubeSurface pointer */
|
||||
Value<atUint32> aabbSz = 0;
|
||||
Value<atVec3f> reflectionNormal;
|
||||
Value<atInt16> skinMtxBankIdx;
|
||||
Value<atUint16> surfaceGroup;
|
||||
Value<atVec3f> aabb[2];
|
||||
Align<32> align;
|
||||
|
||||
static constexpr bool UseMatrixSkinning() { return false; }
|
||||
atInt16 skinMatrixBankIdx() const { return skinMtxBankIdx; }
|
||||
};
|
||||
|
||||
struct SurfaceHeader_3 : BigDNA {
|
||||
AT_DECL_EXPLICIT_DNA
|
||||
Value<atVec3f> centroid;
|
||||
Value<atUint32> matIdx = 0;
|
||||
Value<atUint32> dlSize = 0;
|
||||
Value<atUint32> idxStart = 0; /* Actually used by game to stash CCubeModel pointer */
|
||||
Value<atUint32> idxCount = 0; /* Actually used by game to stash next CCubeSurface pointer */
|
||||
Value<atUint32> aabbSz = 0;
|
||||
Value<atVec3f> reflectionNormal;
|
||||
Value<atInt16> skinMtxBankIdx;
|
||||
Value<atUint16> surfaceGroup;
|
||||
Value<atVec3f> aabb[2];
|
||||
Value<atUint8> unk3;
|
||||
Align<32> align;
|
||||
|
||||
static constexpr bool UseMatrixSkinning() { return true; }
|
||||
atInt16 skinMatrixBankIdx() const { return skinMtxBankIdx; }
|
||||
};
|
||||
|
||||
struct VertexAttributes {
|
||||
GX::AttrType pos = GX::NONE;
|
||||
GX::AttrType norm = GX::NONE;
|
||||
GX::AttrType color0 = GX::NONE;
|
||||
GX::AttrType color1 = GX::NONE;
|
||||
unsigned uvCount = 0;
|
||||
GX::AttrType uvs[7] = {GX::NONE};
|
||||
GX::AttrType pnMtxIdx = GX::NONE;
|
||||
unsigned texMtxIdxCount = 0;
|
||||
GX::AttrType texMtxIdx[7] = {GX::NONE};
|
||||
bool shortUVs;
|
||||
};
|
||||
|
||||
template <class MaterialSet>
|
||||
void GetVertexAttributes(const MaterialSet& matSet, std::vector<VertexAttributes>& attributesOut);
|
||||
|
||||
template <class PAKRouter, class MaterialSet>
|
||||
void ReadMaterialSetToBlender_1_2(hecl::blender::PyOutStream& os, const MaterialSet& matSet, const PAKRouter& pakRouter,
|
||||
const typename PAKRouter::EntryType& entry, unsigned setIdx);
|
||||
|
||||
template <class PAKRouter, class MaterialSet>
|
||||
void ReadMaterialSetToBlender_3(hecl::blender::PyOutStream& os, const MaterialSet& matSet, const PAKRouter& pakRouter,
|
||||
const typename PAKRouter::EntryType& entry, unsigned setIdx);
|
||||
|
||||
void InitGeomBlenderContext(hecl::blender::PyOutStream& os, const hecl::ProjectPath& masterShaderPath);
|
||||
void FinishBlenderMesh(hecl::blender::PyOutStream& os, unsigned matSetCount, int meshIdx);
|
||||
|
||||
template <class PAKRouter, class MaterialSet, class RigPair, class SurfaceHeader>
|
||||
atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::IStreamReader& reader,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const RigPair& rp,
|
||||
bool shortNormals, bool shortUVs, std::vector<VertexAttributes>& vertAttribs,
|
||||
int meshIdx, atUint32 secCount, atUint32 matSetCount, const atUint32* secSizes,
|
||||
atUint32 surfaceCount = 0);
|
||||
|
||||
template <class PAKRouter, class MaterialSet, class RigPair, class SurfaceHeader, atUint32 Version>
|
||||
bool ReadCMDLToBlender(hecl::blender::Connection& conn, athena::io::IStreamReader& reader, PAKRouter& pakRouter,
|
||||
const typename PAKRouter::EntryType& entry, const SpecBase& dataspec, const RigPair& rp);
|
||||
|
||||
template <class PAKRouter, class MaterialSet>
|
||||
void NameCMDL(athena::io::IStreamReader& reader, PAKRouter& pakRouter, typename PAKRouter::EntryType& entry,
|
||||
const SpecBase& dataspec);
|
||||
|
||||
template <class MaterialSet, class SurfaceHeader, atUint32 Version>
|
||||
bool WriteCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const Mesh& mesh);
|
||||
|
||||
template <class MaterialSet, class SurfaceHeader, atUint32 Version>
|
||||
bool WriteHMDLCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const Mesh& mesh,
|
||||
hecl::blender::PoolSkinIndex& poolSkinIndex);
|
||||
|
||||
template <class MaterialSet, class SurfaceHeader, class MeshHeader>
|
||||
bool WriteMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
|
||||
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs);
|
||||
|
||||
template <class MaterialSet, class SurfaceHeader, class MeshHeader>
|
||||
bool WriteHMDLMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
|
||||
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs);
|
||||
|
||||
} // namespace DataSpec::DNACMDL
|
||||
@@ -1,60 +0,0 @@
|
||||
make_dnalist(CMDL
|
||||
FONT
|
||||
DGRP
|
||||
FSM2
|
||||
MAPA
|
||||
MAPU
|
||||
PATH
|
||||
MayaSpline
|
||||
EGMC
|
||||
SAVWCommon
|
||||
ParticleCommon
|
||||
MetaforceVersionInfo
|
||||
Tweaks/ITweakPlayerGun)
|
||||
|
||||
set(DNACOMMON_SOURCES
|
||||
DNACommon.hpp DNACommon.cpp
|
||||
PAK.hpp PAK.cpp
|
||||
GX.hpp GX.cpp
|
||||
FSM2.hpp FSM2.cpp
|
||||
MLVL.hpp MLVL.cpp
|
||||
CMDL.cpp
|
||||
MAPA.cpp
|
||||
MAPU.cpp
|
||||
PATH.hpp PATH.cpp
|
||||
STRG.hpp STRG.cpp
|
||||
TXTR.hpp TXTR.cpp
|
||||
ANCS.hpp ANCS.cpp
|
||||
ANIM.hpp ANIM.cpp
|
||||
PART.hpp PART.cpp
|
||||
SWHC.hpp SWHC.cpp
|
||||
CRSC.hpp CRSC.cpp
|
||||
ELSC.hpp ELSC.cpp
|
||||
WPSC.hpp WPSC.cpp
|
||||
DPSC.hpp DPSC.cpp
|
||||
ParticleCommon.cpp
|
||||
FONT.cpp
|
||||
DGRP.cpp
|
||||
ATBL.hpp ATBL.cpp
|
||||
DeafBabe.hpp DeafBabe.cpp
|
||||
BabeDead.hpp BabeDead.cpp
|
||||
RigInverter.hpp RigInverter.cpp
|
||||
AROTBuilder.hpp AROTBuilder.cpp
|
||||
OBBTreeBuilder.hpp OBBTreeBuilder.cpp
|
||||
MetaforceVersionInfo.hpp
|
||||
Tweaks/ITweak.hpp
|
||||
Tweaks/TweakWriter.hpp
|
||||
Tweaks/ITweakGame.hpp
|
||||
Tweaks/ITweakParticle.hpp
|
||||
Tweaks/ITweakPlayer.hpp
|
||||
Tweaks/ITweakPlayerControl.hpp
|
||||
Tweaks/ITweakGunRes.hpp
|
||||
Tweaks/ITweakPlayerRes.hpp
|
||||
Tweaks/ITweakGui.hpp
|
||||
Tweaks/ITweakSlideShow.hpp
|
||||
Tweaks/ITweakTargeting.hpp
|
||||
Tweaks/ITweakAutoMapper.hpp
|
||||
Tweaks/ITweakBall.hpp
|
||||
Tweaks/ITweakGuiColors.hpp)
|
||||
|
||||
dataspec_add_list(DNACommon DNACOMMON_SOURCES)
|
||||
@@ -1,50 +0,0 @@
|
||||
#include "DataSpec/DNACommon/CRSC.hpp"
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
namespace DataSpec::DNAParticle {
|
||||
|
||||
template struct PPImpl<_CRSM<UniqueID32>>;
|
||||
template struct PPImpl<_CRSM<UniqueID64>>;
|
||||
|
||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_CRSM<UniqueID32>>)
|
||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_CRSM<UniqueID64>>)
|
||||
|
||||
template <>
|
||||
std::string_view PPImpl<_CRSM<UniqueID32>>::DNAType() {
|
||||
return "CRSM<UniqueID32>"sv;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string_view PPImpl<_CRSM<UniqueID64>>::DNAType() {
|
||||
return "CRSM<UniqueID64>"sv;
|
||||
}
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractCRSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
||||
if (writer.isOpen()) {
|
||||
CRSM<IDType> crsm;
|
||||
crsm.read(rs);
|
||||
athena::io::ToYAMLStream(crsm, writer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template bool ExtractCRSM<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
template bool ExtractCRSM<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteCRSM(const CRSM<IDType>& crsm, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
||||
if (w.hasError())
|
||||
return false;
|
||||
crsm.write(w);
|
||||
int64_t rem = w.position() % 32;
|
||||
if (rem)
|
||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
||||
w.writeUByte(0xff);
|
||||
return true;
|
||||
}
|
||||
template bool WriteCRSM<UniqueID32>(const CRSM<UniqueID32>& crsm, const hecl::ProjectPath& outPath);
|
||||
template bool WriteCRSM<UniqueID64>(const CRSM<UniqueID64>& crsm, const hecl::ProjectPath& outPath);
|
||||
} // namespace DataSpec::DNAParticle
|
||||
@@ -1,222 +0,0 @@
|
||||
#ifndef ENTRY
|
||||
#define ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef RES_ENTRY
|
||||
#define RES_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef U32_ENTRY
|
||||
#define U32_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef FLOAT_ENTRY
|
||||
#define FLOAT_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
RES_ENTRY('NODP', NODP)
|
||||
RES_ENTRY('DEFS', DEFS)
|
||||
RES_ENTRY('CRTS', CRTS)
|
||||
RES_ENTRY('MTLS', MTLS)
|
||||
RES_ENTRY('GRAS', GRAS)
|
||||
RES_ENTRY('ICEE', ICEE)
|
||||
RES_ENTRY('GOOO', GOOO)
|
||||
RES_ENTRY('WODS', WODS)
|
||||
RES_ENTRY('WATR', WATR)
|
||||
RES_ENTRY('1MUD', _1MUD)
|
||||
RES_ENTRY('1LAV', _1LAV)
|
||||
RES_ENTRY('1SAN', _1SAN)
|
||||
RES_ENTRY('1PRJ', _1PRJ)
|
||||
RES_ENTRY('DCHR', DCHR)
|
||||
RES_ENTRY('DCHS', DCHS)
|
||||
RES_ENTRY('DCSH', DCSH)
|
||||
RES_ENTRY('DENM', DENM)
|
||||
RES_ENTRY('DESP', DESP)
|
||||
RES_ENTRY('DESH', DESH)
|
||||
RES_ENTRY('BTLE', BTLE)
|
||||
RES_ENTRY('WASP', WASP)
|
||||
RES_ENTRY('TALP', TALP)
|
||||
RES_ENTRY('PTGM', PTGM)
|
||||
RES_ENTRY('SPIR', SPIR)
|
||||
RES_ENTRY('FPIR', FPIR)
|
||||
RES_ENTRY('FFLE', FFLE)
|
||||
RES_ENTRY('PARA', PARA)
|
||||
RES_ENTRY('BMON', BMON)
|
||||
RES_ENTRY('BFLR', BFLR)
|
||||
RES_ENTRY('PBOS', PBOS)
|
||||
RES_ENTRY('IBOS', IBOS)
|
||||
RES_ENTRY('1SVA', _1SVA)
|
||||
RES_ENTRY('1RPR', _1RPR)
|
||||
RES_ENTRY('1MTR', _1MTR)
|
||||
RES_ENTRY('1PDS', _1PDS)
|
||||
RES_ENTRY('1FLB', _1FLB)
|
||||
RES_ENTRY('1DRN', _1DRN)
|
||||
RES_ENTRY('1MRE', _1MRE)
|
||||
RES_ENTRY('CHOZ', CHOZ)
|
||||
RES_ENTRY('JZAP', JZAP)
|
||||
RES_ENTRY('1ISE', _1ISE)
|
||||
RES_ENTRY('1BSE', _1BSE)
|
||||
RES_ENTRY('1ATB', _1ATB)
|
||||
RES_ENTRY('1ATA', _1ATA)
|
||||
RES_ENTRY('BTSP', BTSP)
|
||||
RES_ENTRY('WWSP', WWSP)
|
||||
RES_ENTRY('TASP', TASP)
|
||||
RES_ENTRY('TGSP', TGSP)
|
||||
RES_ENTRY('SPSP', SPSP)
|
||||
RES_ENTRY('FPSP', FPSP)
|
||||
RES_ENTRY('FFSP', FFSP)
|
||||
RES_ENTRY('PSSP', PSSP)
|
||||
RES_ENTRY('BMSP', BMSP)
|
||||
RES_ENTRY('BFSP', BFSP)
|
||||
RES_ENTRY('PBSP', PBSP)
|
||||
RES_ENTRY('IBSP', IBSP)
|
||||
RES_ENTRY('2SVA', _2SVA)
|
||||
RES_ENTRY('2RPR', _2RPR)
|
||||
RES_ENTRY('2MTR', _2MTR)
|
||||
RES_ENTRY('2PDS', _2PDS)
|
||||
RES_ENTRY('2FLB', _2FLB)
|
||||
RES_ENTRY('2DRN', _2DRN)
|
||||
RES_ENTRY('2MRE', _2MRE)
|
||||
RES_ENTRY('CHSP', CHSP)
|
||||
RES_ENTRY('JZSP', JZSP)
|
||||
RES_ENTRY('3ISE', _3ISE)
|
||||
RES_ENTRY('3BSE', _3BSE)
|
||||
RES_ENTRY('3ATB', _3ATB)
|
||||
RES_ENTRY('3ATA', _3ATA)
|
||||
RES_ENTRY('BTSH', BTSH)
|
||||
RES_ENTRY('WWSH', WWSH)
|
||||
RES_ENTRY('TASH', TASH)
|
||||
RES_ENTRY('TGSH', TGSH)
|
||||
RES_ENTRY('SPSH', SPSH)
|
||||
RES_ENTRY('FPSH', FPSH)
|
||||
RES_ENTRY('FFSH', FFSH)
|
||||
RES_ENTRY('PSSH', PSSH)
|
||||
RES_ENTRY('BMSH', BMSH)
|
||||
RES_ENTRY('BFSH', BFSH)
|
||||
RES_ENTRY('PBSH', PBSH)
|
||||
RES_ENTRY('IBSH', IBSH)
|
||||
RES_ENTRY('3SVA', _3SVA)
|
||||
RES_ENTRY('3RPR', _3RPR)
|
||||
RES_ENTRY('3MTR', _3MTR)
|
||||
RES_ENTRY('3PDS', _3PDS)
|
||||
RES_ENTRY('3FLB', _3FLB)
|
||||
RES_ENTRY('3DRN', _3DRN)
|
||||
RES_ENTRY('3MRE', _3MRE)
|
||||
RES_ENTRY('CHSH', CHSH)
|
||||
RES_ENTRY('JZSH', JZSH)
|
||||
RES_ENTRY('5ISE', _5ISE)
|
||||
RES_ENTRY('5BSE', _5BSE)
|
||||
RES_ENTRY('5ATB', _5ATB)
|
||||
RES_ENTRY('5ATA', _5ATA)
|
||||
RES_ENTRY('NCDL', NCDL)
|
||||
RES_ENTRY('DDCL', DDCL)
|
||||
RES_ENTRY('CODL', CODL)
|
||||
RES_ENTRY('MEDL', MEDL)
|
||||
RES_ENTRY('GRDL', GRDL)
|
||||
RES_ENTRY('ICDL', ICDL)
|
||||
RES_ENTRY('GODL', GODL)
|
||||
RES_ENTRY('WODL', WODL)
|
||||
RES_ENTRY('WTDL', WTDL)
|
||||
RES_ENTRY('3MUD', _3MUD)
|
||||
RES_ENTRY('3LAV', _3LAV)
|
||||
RES_ENTRY('3SAN', _3SAN)
|
||||
RES_ENTRY('CHDL', CHDL)
|
||||
RES_ENTRY('ENDL', ENDL)
|
||||
|
||||
U32_ENTRY('NSFX', NSFX)
|
||||
U32_ENTRY('DSFX', DSFX)
|
||||
U32_ENTRY('CSFX', CSFX)
|
||||
U32_ENTRY('MSFX', MSFX)
|
||||
U32_ENTRY('GRFX', GRFX)
|
||||
U32_ENTRY('ICFX', ICFX)
|
||||
U32_ENTRY('GOFX', GOFX)
|
||||
U32_ENTRY('WSFX', WSFX)
|
||||
U32_ENTRY('WTFX', WTFX)
|
||||
U32_ENTRY('2MUD', _2MUD)
|
||||
U32_ENTRY('2LAV', _2LAV)
|
||||
U32_ENTRY('2SAN', _2SAN)
|
||||
U32_ENTRY('2PRJ', _2PRJ)
|
||||
U32_ENTRY('DCFX', DCFX)
|
||||
U32_ENTRY('DSHX', DSHX)
|
||||
U32_ENTRY('DEFX', DEFX)
|
||||
U32_ENTRY('ESFX', ESFX)
|
||||
U32_ENTRY('SHFX', SHFX)
|
||||
U32_ENTRY('BEFX', BEFX)
|
||||
U32_ENTRY('WWFX', WWFX)
|
||||
U32_ENTRY('TAFX', TAFX)
|
||||
U32_ENTRY('GTFX', GTFX)
|
||||
U32_ENTRY('SPFX', SPFX)
|
||||
U32_ENTRY('FPFX', FPFX)
|
||||
U32_ENTRY('FFFX', FFFX)
|
||||
U32_ENTRY('PAFX', PAFX)
|
||||
U32_ENTRY('BMFX', BMFX)
|
||||
U32_ENTRY('BFFX', BFFX)
|
||||
U32_ENTRY('PBFX', PBFX)
|
||||
U32_ENTRY('IBFX', IBFX)
|
||||
U32_ENTRY('4SVA', _4SVA)
|
||||
U32_ENTRY('4RPR', _4RPR)
|
||||
U32_ENTRY('4MTR', _4MTR)
|
||||
U32_ENTRY('4PDS', _4PDS)
|
||||
U32_ENTRY('4FLB', _4FLB)
|
||||
U32_ENTRY('4DRN', _4DRN)
|
||||
U32_ENTRY('4MRE', _4MRE)
|
||||
U32_ENTRY('CZFX', CZFX)
|
||||
U32_ENTRY('JZAS', JZAS)
|
||||
U32_ENTRY('2ISE', _2ISE)
|
||||
U32_ENTRY('2BSE', _2BSE)
|
||||
U32_ENTRY('2ATB', _2ATB)
|
||||
U32_ENTRY('2ATA', _2ATA)
|
||||
U32_ENTRY('BSFX', BSFX)
|
||||
U32_ENTRY('TSFX', TSFX)
|
||||
U32_ENTRY('GSFX', GSFX)
|
||||
U32_ENTRY('SSFX', SSFX)
|
||||
U32_ENTRY('FSFX', FSFX)
|
||||
U32_ENTRY('SFFX', SFFX)
|
||||
U32_ENTRY('PSFX', PSFX)
|
||||
U32_ENTRY('SBFX', SBFX)
|
||||
U32_ENTRY('PBSX', PBSX)
|
||||
U32_ENTRY('IBSX', IBSX)
|
||||
U32_ENTRY('5SVA', _5SVA)
|
||||
U32_ENTRY('5RPR', _5RPR)
|
||||
U32_ENTRY('5MTR', _5MTR)
|
||||
U32_ENTRY('5PDS', _5PDS)
|
||||
U32_ENTRY('5FLB', _5FLB)
|
||||
U32_ENTRY('5DRN', _5DRN)
|
||||
U32_ENTRY('5MRE', _5MRE)
|
||||
U32_ENTRY('JZPS', JZPS)
|
||||
U32_ENTRY('4ISE', _4ISE)
|
||||
U32_ENTRY('4BSE', _4BSE)
|
||||
U32_ENTRY('4ATB', _4ATB)
|
||||
U32_ENTRY('4ATA', _4ATA)
|
||||
U32_ENTRY('BHFX', BHFX)
|
||||
U32_ENTRY('WHFX', WHFX)
|
||||
U32_ENTRY('THFX', THFX)
|
||||
U32_ENTRY('GHFX', GHFX)
|
||||
U32_ENTRY('FHFX', FHFX)
|
||||
U32_ENTRY('HFFX', HFFX)
|
||||
U32_ENTRY('PHFX', PHFX)
|
||||
U32_ENTRY('MHFX', MHFX)
|
||||
U32_ENTRY('HBFX', HBFX)
|
||||
U32_ENTRY('PBHX', PBHX)
|
||||
U32_ENTRY('IBHX', IBHX)
|
||||
U32_ENTRY('6SVA', _6SVA)
|
||||
U32_ENTRY('6RPR', _6RPR)
|
||||
U32_ENTRY('6MTR', _6MTR)
|
||||
U32_ENTRY('6PDS', _6PDS)
|
||||
U32_ENTRY('6FLB', _6FLB)
|
||||
U32_ENTRY('6DRN', _6DRN)
|
||||
U32_ENTRY('6MRE', _6MRE)
|
||||
U32_ENTRY('CHFX', CHFX)
|
||||
U32_ENTRY('JZHS', JZHS)
|
||||
U32_ENTRY('6ISE', _6ISE)
|
||||
U32_ENTRY('6BSE', _6BSE)
|
||||
U32_ENTRY('6ATB', _6ATB)
|
||||
U32_ENTRY('6ATA', _6ATA)
|
||||
|
||||
FLOAT_ENTRY('RNGE', x30_RNGE)
|
||||
FLOAT_ENTRY('FOFF', x34_FOFF)
|
||||
|
||||
#undef ENTRY
|
||||
#undef RES_ENTRY
|
||||
#undef U32_ENTRY
|
||||
#undef FLOAT_ENTRY
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
||||
|
||||
#include <athena/DNA.hpp>
|
||||
|
||||
namespace DataSpec {
|
||||
class PAKEntryReadStream;
|
||||
}
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNAParticle {
|
||||
|
||||
template <class IDType>
|
||||
struct _CRSM {
|
||||
static constexpr ParticleType Type = ParticleType::CRSM;
|
||||
#define RES_ENTRY(name, identifier) ChildResourceFactory<IDType> identifier;
|
||||
#define U32_ENTRY(name, identifier) uint32_t identifier = ~0;
|
||||
#define FLOAT_ENTRY(name, identifier) float identifier = 0.f;
|
||||
#include "CRSC.def"
|
||||
|
||||
template <typename _Func>
|
||||
void constexpr Enumerate(_Func f) {
|
||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
||||
#include "CRSC.def"
|
||||
}
|
||||
|
||||
template <typename _Func>
|
||||
bool constexpr Lookup(FourCC fcc, _Func f) {
|
||||
switch (fcc.toUint32()) {
|
||||
#define ENTRY(name, identifier) \
|
||||
case SBIG(name): \
|
||||
f(identifier); \
|
||||
return true;
|
||||
#include "CRSC.def"
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
template <class IDType>
|
||||
using CRSM = PPImpl<_CRSM<IDType>>;
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractCRSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteCRSM(const CRSM<IDType>& crsm, const hecl::ProjectPath& outPath);
|
||||
} // namespace DataSpec::DNAParticle
|
||||
@@ -1,40 +0,0 @@
|
||||
#include "DataSpec/DNACommon/DGRP.hpp"
|
||||
|
||||
#include <athena/DNAYaml.hpp>
|
||||
#include <athena/FileWriter.hpp>
|
||||
#include <athena/IStreamWriter.hpp>
|
||||
|
||||
#include <hecl/hecl.hpp>
|
||||
|
||||
namespace DataSpec::DNADGRP {
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractDGRP(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
||||
if (writer.isOpen()) {
|
||||
DGRP<IDType> dgrp;
|
||||
dgrp.read(rs);
|
||||
athena::io::ToYAMLStream(dgrp, writer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template bool ExtractDGRP<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
template bool ExtractDGRP<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteDGRP(const DGRP<IDType>& dgrp, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
||||
if (w.hasError())
|
||||
return false;
|
||||
dgrp.write(w);
|
||||
int64_t rem = w.position() % 32;
|
||||
if (rem)
|
||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
||||
w.writeUByte(0xff);
|
||||
return true;
|
||||
}
|
||||
template bool WriteDGRP<UniqueID32>(const DGRP<UniqueID32>& dgrp, const hecl::ProjectPath& outPath);
|
||||
template bool WriteDGRP<UniqueID64>(const DGRP<UniqueID64>& dgrp, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNADGRP
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
namespace DataSpec::DNADGRP {
|
||||
|
||||
template <class IDType>
|
||||
struct AT_SPECIALIZE_PARMS(DataSpec::UniqueID32, DataSpec::UniqueID64) DGRP : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
Value<atUint32> dependCount;
|
||||
struct ObjectTag : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
DNAFourCC type;
|
||||
Value<IDType> id;
|
||||
|
||||
bool validate() const {
|
||||
if (!id.isValid())
|
||||
return false;
|
||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(id);
|
||||
return path && !path.isNone();
|
||||
}
|
||||
};
|
||||
|
||||
Vector<ObjectTag, AT_DNA_COUNT(dependCount)> depends;
|
||||
|
||||
void validateDeps() {
|
||||
std::vector<ObjectTag> newDeps;
|
||||
newDeps.reserve(depends.size());
|
||||
for (const ObjectTag& tag : depends)
|
||||
if (tag.validate())
|
||||
newDeps.push_back(tag);
|
||||
depends = std::move(newDeps);
|
||||
dependCount = atUint32(depends.size());
|
||||
}
|
||||
};
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractDGRP(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
template <class IDType>
|
||||
bool WriteDGRP(const DGRP<IDType>& dgrp, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNADGRP
|
||||
@@ -1,199 +0,0 @@
|
||||
#include "DNACommon.hpp"
|
||||
#include "PAK.hpp"
|
||||
#include "boo/ThreadLocalPtr.hpp"
|
||||
|
||||
namespace DataSpec {
|
||||
|
||||
logvisor::Module LogDNACommon("DataSpec::DNACommon");
|
||||
ThreadLocalPtr<SpecBase> g_curSpec;
|
||||
ThreadLocalPtr<PAKRouterBase> g_PakRouter;
|
||||
ThreadLocalPtr<hecl::blender::Token> g_ThreadBlenderToken;
|
||||
ThreadLocalPtr<hecl::Database::Project> UniqueIDBridge::s_Project;
|
||||
UniqueID32 UniqueID32::kInvalidId;
|
||||
|
||||
template <class IDType>
|
||||
hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const IDType& id, bool silenceWarnings) {
|
||||
/* Try PAKRouter first (only available at extract) */
|
||||
PAKRouterBase* pakRouter = g_PakRouter.get();
|
||||
if (pakRouter) {
|
||||
hecl::ProjectPath path = pakRouter->getWorking(id, silenceWarnings);
|
||||
if (path)
|
||||
return path;
|
||||
}
|
||||
|
||||
/* Try project cache second (populated with paths read from YAML resources) */
|
||||
hecl::Database::Project* project = s_Project.get();
|
||||
if (!project) {
|
||||
if (pakRouter) {
|
||||
if (hecl::VerbosityLevel >= 1 && !silenceWarnings && id.isValid())
|
||||
LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to translate {} to path"), id);
|
||||
return {};
|
||||
}
|
||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("g_PakRouter or s_Project must be set to non-null before "
|
||||
"calling UniqueIDBridge::TranslatePakIdToPath"));
|
||||
return {};
|
||||
}
|
||||
|
||||
const hecl::ProjectPath* search = project->lookupBridgePath(id.toUint64());
|
||||
if (!search) {
|
||||
if (hecl::VerbosityLevel >= 1 && !silenceWarnings && id.isValid())
|
||||
LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to translate {} to path"), id);
|
||||
return {};
|
||||
}
|
||||
return *search;
|
||||
}
|
||||
template hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const UniqueID32& id, bool silenceWarnings);
|
||||
template hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const UniqueID64& id, bool silenceWarnings);
|
||||
template hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const UniqueID128& id, bool silenceWarnings);
|
||||
|
||||
template <class IDType>
|
||||
hecl::ProjectPath UniqueIDBridge::MakePathFromString(std::string_view str) {
|
||||
if (str.empty())
|
||||
return {};
|
||||
hecl::Database::Project* project = s_Project.get();
|
||||
if (!project)
|
||||
LogDNACommon.report(logvisor::Fatal,
|
||||
FMT_STRING("UniqueIDBridge::setGlobalProject must be called before MakePathFromString"));
|
||||
hecl::ProjectPath path = hecl::ProjectPath(*project, str);
|
||||
project->addBridgePathToCache(IDType(path).toUint64(), path);
|
||||
return path;
|
||||
}
|
||||
template hecl::ProjectPath UniqueIDBridge::MakePathFromString<UniqueID32>(std::string_view str);
|
||||
template hecl::ProjectPath UniqueIDBridge::MakePathFromString<UniqueID64>(std::string_view str);
|
||||
|
||||
void UniqueIDBridge::SetThreadProject(hecl::Database::Project& project) { s_Project.reset(&project); }
|
||||
|
||||
/** PAK 32-bit Unique ID */
|
||||
template <>
|
||||
void UniqueID32::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
||||
assign(reader.readUint32Big());
|
||||
}
|
||||
template <>
|
||||
void UniqueID32::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
||||
writer.writeUint32Big(m_id);
|
||||
}
|
||||
template <>
|
||||
void UniqueID32::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
|
||||
*this = UniqueIDBridge::MakePathFromString<UniqueID32>(reader.readString());
|
||||
}
|
||||
template <>
|
||||
void UniqueID32::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& writer) {
|
||||
if (!isValid())
|
||||
return;
|
||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(*this);
|
||||
if (!path)
|
||||
return;
|
||||
writer.writeString(path.getEncodableString());
|
||||
}
|
||||
template <>
|
||||
void UniqueID32::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
||||
s += 4;
|
||||
}
|
||||
|
||||
std::string UniqueID32::toString() const { return fmt::format(FMT_STRING("{}"), *this); }
|
||||
|
||||
template <>
|
||||
void UniqueID32Zero::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
||||
UniqueID32::Enumerate<BigDNA::Read>(reader);
|
||||
}
|
||||
template <>
|
||||
void UniqueID32Zero::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
||||
writer.writeUint32Big(isValid() ? m_id : 0);
|
||||
}
|
||||
template <>
|
||||
void UniqueID32Zero::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
|
||||
UniqueID32::Enumerate<BigDNA::ReadYaml>(reader);
|
||||
}
|
||||
template <>
|
||||
void UniqueID32Zero::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& writer) {
|
||||
UniqueID32::Enumerate<BigDNA::WriteYaml>(writer);
|
||||
}
|
||||
template <>
|
||||
void UniqueID32Zero::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
||||
UniqueID32::Enumerate<BigDNA::BinarySize>(s);
|
||||
}
|
||||
|
||||
/** PAK 64-bit Unique ID */
|
||||
template <>
|
||||
void UniqueID64::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
||||
assign(reader.readUint64Big());
|
||||
}
|
||||
template <>
|
||||
void UniqueID64::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
||||
writer.writeUint64Big(m_id);
|
||||
}
|
||||
template <>
|
||||
void UniqueID64::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
|
||||
*this = UniqueIDBridge::MakePathFromString<UniqueID64>(reader.readString());
|
||||
}
|
||||
template <>
|
||||
void UniqueID64::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& writer) {
|
||||
if (!isValid())
|
||||
return;
|
||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(*this);
|
||||
if (!path)
|
||||
return;
|
||||
writer.writeString(path.getEncodableString());
|
||||
}
|
||||
template <>
|
||||
void UniqueID64::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
||||
s += 8;
|
||||
}
|
||||
|
||||
std::string UniqueID64::toString() const { return fmt::format(FMT_STRING("{}"), *this); }
|
||||
|
||||
/** PAK 128-bit Unique ID */
|
||||
template <>
|
||||
void UniqueID128::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
||||
m_id.id[0] = reader.readUint64Big();
|
||||
m_id.id[1] = reader.readUint64Big();
|
||||
}
|
||||
template <>
|
||||
void UniqueID128::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
||||
writer.writeUint64Big(m_id.id[0]);
|
||||
writer.writeUint64Big(m_id.id[1]);
|
||||
}
|
||||
template <>
|
||||
void UniqueID128::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
|
||||
*this = UniqueIDBridge::MakePathFromString<UniqueID128>(reader.readString());
|
||||
}
|
||||
template <>
|
||||
void UniqueID128::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& writer) {
|
||||
if (!isValid())
|
||||
return;
|
||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(*this);
|
||||
if (!path)
|
||||
return;
|
||||
writer.writeString(path.getEncodableString());
|
||||
}
|
||||
template <>
|
||||
void UniqueID128::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
||||
s += 16;
|
||||
}
|
||||
|
||||
std::string UniqueID128::toString() const { return fmt::format(FMT_STRING("{}"), *this); }
|
||||
|
||||
/** Word Bitmap reader/writer */
|
||||
void WordBitmap::read(athena::io::IStreamReader& reader, size_t bitCount) {
|
||||
m_bitCount = bitCount;
|
||||
size_t wordCount = (bitCount + 31) / 32;
|
||||
m_words.clear();
|
||||
m_words.reserve(wordCount);
|
||||
for (size_t w = 0; w < wordCount; ++w)
|
||||
m_words.push_back(reader.readUint32Big());
|
||||
}
|
||||
void WordBitmap::write(athena::io::IStreamWriter& writer) const {
|
||||
for (atUint32 word : m_words)
|
||||
writer.writeUint32Big(word);
|
||||
}
|
||||
void WordBitmap::binarySize(size_t& __isz) const { __isz += m_words.size() * 4; }
|
||||
|
||||
hecl::ProjectPath GetPathBeginsWith(const hecl::DirectoryEnumerator& dEnum, const hecl::ProjectPath& parentPath,
|
||||
std::string_view test) {
|
||||
for (const auto& ent : dEnum)
|
||||
if (hecl::StringUtils::BeginsWith(ent.m_name, test))
|
||||
return hecl::ProjectPath(parentPath, ent.m_name);
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace DataSpec
|
||||
@@ -1,394 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include "logvisor/logvisor.hpp"
|
||||
#include "athena/DNAYaml.hpp"
|
||||
#include "hecl/Database.hpp"
|
||||
#include "../SpecBase.hpp"
|
||||
#include "boo/ThreadLocalPtr.hpp"
|
||||
#include "zeus/CColor.hpp"
|
||||
|
||||
namespace DataSpec {
|
||||
struct SpecBase;
|
||||
|
||||
extern logvisor::Module LogDNACommon;
|
||||
extern ThreadLocalPtr<SpecBase> g_curSpec;
|
||||
extern ThreadLocalPtr<class PAKRouterBase> g_PakRouter;
|
||||
extern ThreadLocalPtr<hecl::blender::Token> g_ThreadBlenderToken;
|
||||
|
||||
/* This comes up a great deal */
|
||||
using BigDNA = athena::io::DNA<athena::Endian::Big>;
|
||||
using BigDNAV = athena::io::DNAV<athena::Endian::Big>;
|
||||
using BigDNAVYaml = athena::io::DNAVYaml<athena::Endian::Big>;
|
||||
|
||||
/** FourCC with DNA read/write */
|
||||
using DNAFourCC = hecl::DNAFourCC;
|
||||
|
||||
class DNAColor final : public BigDNA, public zeus::CColor {
|
||||
public:
|
||||
DNAColor() = default;
|
||||
DNAColor(const zeus::CColor& color) : zeus::CColor(color) {}
|
||||
AT_DECL_EXPLICIT_DNA_YAML
|
||||
};
|
||||
template <>
|
||||
inline void DNAColor::Enumerate<BigDNA::Read>(typename Read::StreamT& _r) {
|
||||
zeus::CColor::readRGBABig(_r);
|
||||
}
|
||||
template <>
|
||||
inline void DNAColor::Enumerate<BigDNA::Write>(typename Write::StreamT& _w) {
|
||||
zeus::CColor::writeRGBABig(_w);
|
||||
}
|
||||
template <>
|
||||
inline void DNAColor::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& _r) {
|
||||
size_t count;
|
||||
if (auto v = _r.enterSubVector(count)) {
|
||||
zeus::simd_floats f;
|
||||
f[0] = (count >= 1) ? _r.readFloat() : 0.f;
|
||||
f[1] = (count >= 2) ? _r.readFloat() : 0.f;
|
||||
f[2] = (count >= 3) ? _r.readFloat() : 0.f;
|
||||
f[3] = (count >= 4) ? _r.readFloat() : 0.f;
|
||||
mSimd.copy_from(f);
|
||||
}
|
||||
}
|
||||
template <>
|
||||
inline void DNAColor::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& _w) {
|
||||
if (auto v = _w.enterSubVector()) {
|
||||
zeus::simd_floats f(mSimd);
|
||||
_w.writeFloat(f[0]);
|
||||
_w.writeFloat(f[1]);
|
||||
_w.writeFloat(f[2]);
|
||||
_w.writeFloat(f[3]);
|
||||
}
|
||||
}
|
||||
template <>
|
||||
inline void DNAColor::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& _s) {
|
||||
_s += 16;
|
||||
}
|
||||
|
||||
using FourCC = hecl::FourCC;
|
||||
class UniqueID32;
|
||||
class UniqueID64;
|
||||
class UniqueID128;
|
||||
|
||||
/** Common virtual interface for runtime ambiguity resolution */
|
||||
class PAKRouterBase {
|
||||
protected:
|
||||
const SpecBase& m_dataSpec;
|
||||
|
||||
public:
|
||||
PAKRouterBase(const SpecBase& dataSpec) : m_dataSpec(dataSpec) {}
|
||||
hecl::Database::Project& getProject() const { return m_dataSpec.getProject(); }
|
||||
virtual hecl::ProjectPath getWorking(const UniqueID32&, bool silenceWarnings = false) const {
|
||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID32 specialization"));
|
||||
return hecl::ProjectPath();
|
||||
}
|
||||
virtual hecl::ProjectPath getWorking(const UniqueID64&, bool silenceWarnings = false) const {
|
||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID64 specialization"));
|
||||
return hecl::ProjectPath();
|
||||
}
|
||||
virtual hecl::ProjectPath getWorking(const UniqueID128&, bool silenceWarnings = false) const {
|
||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID128 specialization"));
|
||||
return hecl::ProjectPath();
|
||||
}
|
||||
};
|
||||
|
||||
/** Globally-accessed manager allowing UniqueID* classes to directly
|
||||
* lookup destination paths of resources */
|
||||
class UniqueIDBridge {
|
||||
friend class UniqueID32;
|
||||
friend class UniqueID64;
|
||||
|
||||
static ThreadLocalPtr<hecl::Database::Project> s_Project;
|
||||
|
||||
public:
|
||||
template <class IDType>
|
||||
static hecl::ProjectPath TranslatePakIdToPath(const IDType& id, bool silenceWarnings = false);
|
||||
template <class IDType>
|
||||
static hecl::ProjectPath MakePathFromString(std::string_view str);
|
||||
|
||||
static void SetThreadProject(hecl::Database::Project& project);
|
||||
};
|
||||
|
||||
/** PAK 32-bit Unique ID */
|
||||
class UniqueID32 : public BigDNA {
|
||||
protected:
|
||||
uint32_t m_id = 0xffffffff;
|
||||
|
||||
public:
|
||||
using value_type = uint32_t;
|
||||
static UniqueID32 kInvalidId;
|
||||
AT_DECL_EXPLICIT_DNA_YAML
|
||||
bool isValid() const noexcept { return m_id != 0xffffffff && m_id != 0; }
|
||||
void assign(uint32_t id) noexcept { m_id = id ? id : 0xffffffff; }
|
||||
|
||||
UniqueID32& operator=(const hecl::ProjectPath& path) noexcept {
|
||||
assign(path.parsedHash32());
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const UniqueID32& other) const noexcept { return m_id != other.m_id; }
|
||||
bool operator==(const UniqueID32& other) const noexcept { return m_id == other.m_id; }
|
||||
bool operator<(const UniqueID32& other) const noexcept { return m_id < other.m_id; }
|
||||
uint32_t toUint32() const noexcept { return m_id; }
|
||||
uint64_t toUint64() const noexcept { return m_id; }
|
||||
std::string toString() const;
|
||||
void clear() noexcept { m_id = 0xffffffff; }
|
||||
|
||||
UniqueID32() noexcept = default;
|
||||
UniqueID32(uint32_t idin) noexcept { assign(idin); }
|
||||
UniqueID32(athena::io::IStreamReader& reader) { read(reader); }
|
||||
UniqueID32(const hecl::ProjectPath& path) noexcept { *this = path; }
|
||||
UniqueID32(const char* hexStr) noexcept {
|
||||
char copy[9];
|
||||
strncpy(copy, hexStr, 8);
|
||||
copy[8] = '\0';
|
||||
assign(strtoul(copy, nullptr, 16));
|
||||
}
|
||||
|
||||
static constexpr size_t BinarySize() noexcept { return 4; }
|
||||
};
|
||||
|
||||
/** PAK 32-bit Unique ID - writes zero when invalid */
|
||||
class UniqueID32Zero : public UniqueID32 {
|
||||
public:
|
||||
AT_DECL_DNA_YAML
|
||||
Delete __d2;
|
||||
using UniqueID32::UniqueID32;
|
||||
};
|
||||
|
||||
/** PAK 64-bit Unique ID */
|
||||
class UniqueID64 : public BigDNA {
|
||||
uint64_t m_id = 0xffffffffffffffff;
|
||||
|
||||
public:
|
||||
using value_type = uint64_t;
|
||||
AT_DECL_EXPLICIT_DNA_YAML
|
||||
bool isValid() const noexcept { return m_id != 0xffffffffffffffff && m_id != 0; }
|
||||
void assign(uint64_t id) noexcept { m_id = id ? id : 0xffffffffffffffff; }
|
||||
|
||||
UniqueID64& operator=(const hecl::ProjectPath& path) noexcept {
|
||||
assign(path.hash().val64());
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const UniqueID64& other) const noexcept { return m_id != other.m_id; }
|
||||
bool operator==(const UniqueID64& other) const noexcept { return m_id == other.m_id; }
|
||||
bool operator<(const UniqueID64& other) const noexcept { return m_id < other.m_id; }
|
||||
uint64_t toUint64() const noexcept { return m_id; }
|
||||
std::string toString() const;
|
||||
void clear() noexcept { m_id = 0xffffffffffffffff; }
|
||||
|
||||
UniqueID64() noexcept = default;
|
||||
UniqueID64(uint64_t idin) noexcept { assign(idin); }
|
||||
UniqueID64(athena::io::IStreamReader& reader) { read(reader); }
|
||||
UniqueID64(const hecl::ProjectPath& path) noexcept { *this = path; }
|
||||
UniqueID64(const char* hexStr) noexcept {
|
||||
char copy[17];
|
||||
std::strncpy(copy, hexStr, 16);
|
||||
copy[16] = '\0';
|
||||
assign(std::strtoull(copy, nullptr, 16));
|
||||
}
|
||||
|
||||
static constexpr size_t BinarySize() noexcept { return 8; }
|
||||
};
|
||||
|
||||
/** PAK 128-bit Unique ID */
|
||||
class UniqueID128 : public BigDNA {
|
||||
public:
|
||||
union Value {
|
||||
uint64_t id[2];
|
||||
#if __SSE__
|
||||
__m128i id128;
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
Value m_id;
|
||||
|
||||
public:
|
||||
using value_type = uint64_t;
|
||||
AT_DECL_EXPLICIT_DNA_YAML
|
||||
UniqueID128() noexcept {
|
||||
m_id.id[0] = 0xffffffffffffffff;
|
||||
m_id.id[1] = 0xffffffffffffffff;
|
||||
}
|
||||
UniqueID128(uint64_t idin) noexcept {
|
||||
m_id.id[0] = idin;
|
||||
m_id.id[1] = 0;
|
||||
}
|
||||
bool isValid() const noexcept {
|
||||
return m_id.id[0] != 0xffffffffffffffff && m_id.id[0] != 0 && m_id.id[1] != 0xffffffffffffffff && m_id.id[1] != 0;
|
||||
}
|
||||
|
||||
UniqueID128& operator=(const hecl::ProjectPath& path) noexcept {
|
||||
m_id.id[0] = path.hash().val64();
|
||||
m_id.id[1] = 0;
|
||||
return *this;
|
||||
}
|
||||
UniqueID128(const hecl::ProjectPath& path) noexcept { *this = path; }
|
||||
|
||||
bool operator!=(const UniqueID128& other) const noexcept {
|
||||
#if __SSE__
|
||||
__m128i vcmp = _mm_cmpeq_epi32(m_id.id128, other.m_id.id128);
|
||||
int vmask = _mm_movemask_epi8(vcmp);
|
||||
return vmask != 0xffff;
|
||||
#else
|
||||
return (m_id.id[0] != other.m_id.id[0]) || (m_id.id[1] != other.m_id.id[1]);
|
||||
#endif
|
||||
}
|
||||
bool operator==(const UniqueID128& other) const noexcept {
|
||||
#if __SSE__
|
||||
__m128i vcmp = _mm_cmpeq_epi32(m_id.id128, other.m_id.id128);
|
||||
int vmask = _mm_movemask_epi8(vcmp);
|
||||
return vmask == 0xffff;
|
||||
#else
|
||||
return (m_id.id[0] == other.m_id.id[0]) && (m_id.id[1] == other.m_id.id[1]);
|
||||
#endif
|
||||
}
|
||||
bool operator<(const UniqueID128& other) const noexcept {
|
||||
return m_id.id[0] < other.m_id.id[0] || (m_id.id[0] == other.m_id.id[0] && m_id.id[1] < other.m_id.id[1]);
|
||||
}
|
||||
void clear() noexcept {
|
||||
m_id.id[0] = 0xffffffffffffffff;
|
||||
m_id.id[1] = 0xffffffffffffffff;
|
||||
}
|
||||
uint64_t toUint64() const noexcept { return m_id.id[0]; }
|
||||
uint64_t toHighUint64() const noexcept { return m_id.id[0]; }
|
||||
uint64_t toLowUint64() const noexcept { return m_id.id[1]; }
|
||||
std::string toString() const;
|
||||
|
||||
static constexpr size_t BinarySize() noexcept { return 16; }
|
||||
};
|
||||
|
||||
/** Casts ID type to its null-zero equivalent */
|
||||
template <class T>
|
||||
using CastIDToZero = typename std::conditional_t<std::is_same_v<T, UniqueID32>, UniqueID32Zero, T>;
|
||||
|
||||
/** Word Bitmap reader/writer */
|
||||
class WordBitmap {
|
||||
std::vector<atUint32> m_words;
|
||||
size_t m_bitCount = 0;
|
||||
|
||||
public:
|
||||
void read(athena::io::IStreamReader& reader, size_t bitCount);
|
||||
void write(athena::io::IStreamWriter& writer) const;
|
||||
void reserve(size_t bitCount) { m_words.reserve((bitCount + 31) / 32); }
|
||||
void binarySize(size_t& __isz) const;
|
||||
size_t getBitCount() const { return m_bitCount; }
|
||||
bool getBit(size_t idx) const {
|
||||
size_t wordIdx = idx / 32;
|
||||
if (wordIdx >= m_words.size())
|
||||
return false;
|
||||
size_t wordCur = idx % 32;
|
||||
return (m_words[wordIdx] >> wordCur) & 0x1;
|
||||
}
|
||||
void setBit(size_t idx) {
|
||||
size_t wordIdx = idx / 32;
|
||||
while (wordIdx >= m_words.size())
|
||||
m_words.push_back(0);
|
||||
size_t wordCur = idx % 32;
|
||||
m_words[wordIdx] |= (1 << wordCur);
|
||||
m_bitCount = std::max(m_bitCount, idx + 1);
|
||||
}
|
||||
void unsetBit(size_t idx) {
|
||||
size_t wordIdx = idx / 32;
|
||||
while (wordIdx >= m_words.size())
|
||||
m_words.push_back(0);
|
||||
size_t wordCur = idx % 32;
|
||||
m_words[wordIdx] &= ~(1 << wordCur);
|
||||
m_bitCount = std::max(m_bitCount, idx + 1);
|
||||
}
|
||||
void clear() {
|
||||
m_words.clear();
|
||||
m_bitCount = 0;
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
friend class WordBitmap;
|
||||
const WordBitmap& m_bmp;
|
||||
size_t m_idx = 0;
|
||||
Iterator(const WordBitmap& bmp, size_t idx) : m_bmp(bmp), m_idx(idx) {}
|
||||
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = bool;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = bool*;
|
||||
using reference = bool&;
|
||||
|
||||
Iterator& operator++() {
|
||||
++m_idx;
|
||||
return *this;
|
||||
}
|
||||
bool operator*() const { return m_bmp.getBit(m_idx); }
|
||||
bool operator!=(const Iterator& other) const { return m_idx != other.m_idx; }
|
||||
};
|
||||
Iterator begin() const { return Iterator(*this, 0); }
|
||||
Iterator end() const { return Iterator(*this, m_bitCount); }
|
||||
};
|
||||
|
||||
/** Resource cooker function */
|
||||
using ResCooker = std::function<bool(const hecl::ProjectPath&, const hecl::ProjectPath&)>;
|
||||
|
||||
/** Mappings of resources involved in extracting characters */
|
||||
template <class IDType>
|
||||
struct CharacterAssociations {
|
||||
struct RigPair {
|
||||
IDType cskr, cinf;
|
||||
};
|
||||
struct ModelRigPair {
|
||||
IDType cinf, cmdl;
|
||||
};
|
||||
/* CMDL -> (CSKR, CINF) */
|
||||
std::unordered_map<IDType, RigPair> m_cmdlRigs;
|
||||
/* CSKR -> ANCS */
|
||||
std::unordered_map<IDType, std::pair<IDType, std::string>> m_cskrToCharacter;
|
||||
/* ANCS -> (CINF, CMDL) */
|
||||
std::unordered_multimap<IDType, std::pair<ModelRigPair, std::string>> m_characterToAttachmentRigs;
|
||||
using MultimapIteratorPair =
|
||||
std::pair<typename std::unordered_multimap<IDType, std::pair<ModelRigPair, std::string>>::const_iterator,
|
||||
typename std::unordered_multimap<IDType, std::pair<ModelRigPair, std::string>>::const_iterator>;
|
||||
void addAttachmentRig(IDType character, IDType cinf, IDType cmdl, const char* name) {
|
||||
auto range = m_characterToAttachmentRigs.equal_range(character);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
if (it->second.second == name)
|
||||
return;
|
||||
m_characterToAttachmentRigs.insert(std::make_pair(character, std::make_pair(ModelRigPair{cinf, cmdl}, name)));
|
||||
}
|
||||
};
|
||||
|
||||
hecl::ProjectPath GetPathBeginsWith(const hecl::DirectoryEnumerator& dEnum, const hecl::ProjectPath& parentPath,
|
||||
std::string_view test);
|
||||
inline hecl::ProjectPath GetPathBeginsWith(const hecl::ProjectPath& parentPath, std::string_view test) {
|
||||
return GetPathBeginsWith(hecl::DirectoryEnumerator(parentPath.getAbsolutePath()), parentPath, test);
|
||||
}
|
||||
|
||||
} // namespace DataSpec
|
||||
|
||||
/* Hash template-specializations for UniqueID types */
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<DataSpec::DNAFourCC> {
|
||||
size_t operator()(const DataSpec::DNAFourCC& fcc) const noexcept { return fcc.toUint32(); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<DataSpec::UniqueID32> {
|
||||
size_t operator()(const DataSpec::UniqueID32& id) const noexcept { return id.toUint32(); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<DataSpec::UniqueID64> {
|
||||
size_t operator()(const DataSpec::UniqueID64& id) const noexcept { return id.toUint64(); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<DataSpec::UniqueID128> {
|
||||
size_t operator()(const DataSpec::UniqueID128& id) const noexcept { return id.toHighUint64() ^ id.toLowUint64(); }
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
FMT_CUSTOM_FORMATTER(DataSpec::UniqueID32, "{:08X}", obj.toUint32())
|
||||
FMT_CUSTOM_FORMATTER(DataSpec::UniqueID32Zero, "{:08X}", obj.toUint32())
|
||||
FMT_CUSTOM_FORMATTER(DataSpec::UniqueID64, "{:016X}", obj.toUint64())
|
||||
FMT_CUSTOM_FORMATTER(DataSpec::UniqueID128, "{:016X}{:016X}", obj.toHighUint64(), obj.toLowUint64())
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "DataSpec/DNACommon/DPSC.hpp"
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
namespace DataSpec::DNAParticle {
|
||||
|
||||
template struct PPImpl<_DPSM<UniqueID32>>;
|
||||
template struct PPImpl<_DPSM<UniqueID64>>;
|
||||
|
||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_DPSM<UniqueID32>>)
|
||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_DPSM<UniqueID64>>)
|
||||
|
||||
template <>
|
||||
std::string_view PPImpl<_DPSM<UniqueID32>>::DNAType() {
|
||||
return "DPSM<UniqueID32>"sv;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string_view PPImpl<_DPSM<UniqueID64>>::DNAType() {
|
||||
return "DPSM<UniqueID64>"sv;
|
||||
}
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractDPSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
||||
if (writer.isOpen()) {
|
||||
DPSM<IDType> dpsm;
|
||||
dpsm.read(rs);
|
||||
athena::io::ToYAMLStream(dpsm, writer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template bool ExtractDPSM<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
template bool ExtractDPSM<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteDPSM(const DPSM<IDType>& dpsm, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
||||
if (w.hasError())
|
||||
return false;
|
||||
dpsm.write(w);
|
||||
int64_t rem = w.position() % 32;
|
||||
if (rem)
|
||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
||||
w.writeUByte(0xff);
|
||||
return true;
|
||||
}
|
||||
template bool WriteDPSM<UniqueID32>(const DPSM<UniqueID32>& dpsm, const hecl::ProjectPath& outPath);
|
||||
template bool WriteDPSM<UniqueID64>(const DPSM<UniqueID64>& dpsm, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNAParticle
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef ENTRY
|
||||
#define ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
ENTRY('1LFT', x0_quad.x0_LFT)
|
||||
ENTRY('1SZE', x0_quad.x4_SZE)
|
||||
ENTRY('1ROT', x0_quad.x8_ROT)
|
||||
ENTRY('1OFF', x0_quad.xc_OFF)
|
||||
ENTRY('1CLR', x0_quad.x10_CLR)
|
||||
ENTRY('1TEX', x0_quad.x14_TEX)
|
||||
ENTRY('1ADD', x0_quad.x18_ADD)
|
||||
ENTRY('2LFT', x1c_quad.x0_LFT)
|
||||
ENTRY('2SZE', x1c_quad.x4_SZE)
|
||||
ENTRY('2ROT', x1c_quad.x8_ROT)
|
||||
ENTRY('2OFF', x1c_quad.xc_OFF)
|
||||
ENTRY('2CLR', x1c_quad.x10_CLR)
|
||||
ENTRY('2TEX', x1c_quad.x14_TEX)
|
||||
ENTRY('2ADD', x1c_quad.x18_ADD)
|
||||
ENTRY('DMDL', x38_DMDL)
|
||||
ENTRY('DLFT', x48_DLFT)
|
||||
ENTRY('DMOP', x4c_DMOP)
|
||||
ENTRY('DMRT', x50_DMRT)
|
||||
ENTRY('DMSC', x54_DMSC)
|
||||
ENTRY('DMCL', x58_DMCL)
|
||||
ENTRY('DMAB', x5c_24_DMAB)
|
||||
ENTRY('DMOO', x5c_25_DMOO)
|
||||
|
||||
#undef ENTRY
|
||||
@@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
||||
|
||||
#include <athena/FileWriter.hpp>
|
||||
|
||||
namespace DataSpec {
|
||||
class PAKEntryReadStream;
|
||||
}
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNAParticle {
|
||||
|
||||
template <class IDType>
|
||||
struct _DPSM {
|
||||
static constexpr ParticleType Type = ParticleType::DPSM;
|
||||
|
||||
struct SQuadDescr {
|
||||
IntElementFactory x0_LFT;
|
||||
RealElementFactory x4_SZE;
|
||||
RealElementFactory x8_ROT;
|
||||
VectorElementFactory xc_OFF;
|
||||
ColorElementFactory x10_CLR;
|
||||
UVElementFactory<IDType> x14_TEX;
|
||||
bool x18_ADD = false;
|
||||
};
|
||||
|
||||
SQuadDescr x0_quad;
|
||||
SQuadDescr x1c_quad;
|
||||
ChildResourceFactory<IDType> x38_DMDL;
|
||||
IntElementFactory x48_DLFT;
|
||||
VectorElementFactory x4c_DMOP;
|
||||
VectorElementFactory x50_DMRT;
|
||||
VectorElementFactory x54_DMSC;
|
||||
ColorElementFactory x58_DMCL;
|
||||
|
||||
bool x5c_24_DMAB = false;
|
||||
bool x5c_25_DMOO = false;
|
||||
|
||||
template <typename _Func>
|
||||
void constexpr Enumerate(_Func f) {
|
||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
||||
#include "DPSC.def"
|
||||
}
|
||||
|
||||
template <typename _Func>
|
||||
bool constexpr Lookup(FourCC fcc, _Func f) {
|
||||
switch (fcc.toUint32()) {
|
||||
#define ENTRY(name, identifier) \
|
||||
case SBIG(name): \
|
||||
f(identifier); \
|
||||
return true;
|
||||
#include "DPSC.def"
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
template <class IDType>
|
||||
using DPSM = PPImpl<_DPSM<IDType>>;
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractDPSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteDPSM(const DPSM<IDType>& dpsm, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNAParticle
|
||||
@@ -1,258 +0,0 @@
|
||||
#include "DataSpec/DNACommon/DeafBabe.hpp"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "DataSpec/DNACommon/AROTBuilder.hpp"
|
||||
#include "DataSpec/DNAMP1/DeafBabe.hpp"
|
||||
#include "DataSpec/DNAMP1/DCLN.hpp"
|
||||
#include "DataSpec/DNAMP2/DeafBabe.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
#include <zeus/Global.hpp>
|
||||
|
||||
namespace DataSpec {
|
||||
|
||||
template <class DEAFBABE>
|
||||
void DeafBabeSendToBlender(hecl::blender::PyOutStream& os, const DEAFBABE& db, bool isDcln, atInt32 idx) {
|
||||
os << "material_index = []\n"
|
||||
"col_bm = bmesh.new()\n";
|
||||
for (const atVec3f& vert : db.verts) {
|
||||
zeus::simd_floats f(vert.simd);
|
||||
os.format(FMT_STRING("col_bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
|
||||
}
|
||||
|
||||
os << "col_bm.verts.ensure_lookup_table()\n";
|
||||
|
||||
int triIdx = 0;
|
||||
for (const typename DEAFBABE::Triangle& tri : db.triangleEdgeConnections) {
|
||||
const typename DEAFBABE::Material& triMat = db.materials[db.triMats[triIdx++]];
|
||||
const typename DEAFBABE::Edge& edge0 = db.edgeVertConnections[tri.edges[0]];
|
||||
const typename DEAFBABE::Edge& edge1 = db.edgeVertConnections[tri.edges[1]];
|
||||
const typename DEAFBABE::Edge& edge2 = db.edgeVertConnections[tri.edges[2]];
|
||||
if (!edge0.verts[0] && !edge1.verts[0] && !edge2.verts[0])
|
||||
break;
|
||||
|
||||
int vindices[3];
|
||||
vindices[2] =
|
||||
(edge1.verts[0] != edge0.verts[0] && edge1.verts[0] != edge0.verts[1]) ? edge1.verts[0] : edge1.verts[1];
|
||||
|
||||
if (triMat.flipFace()) {
|
||||
vindices[0] = edge0.verts[1];
|
||||
vindices[1] = edge0.verts[0];
|
||||
} else {
|
||||
vindices[0] = edge0.verts[0];
|
||||
vindices[1] = edge0.verts[1];
|
||||
}
|
||||
|
||||
os << "tri_verts = []\n";
|
||||
os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[0]);
|
||||
os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[1]);
|
||||
os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[2]);
|
||||
|
||||
os.format(FMT_STRING("face = col_bm.faces.get(tri_verts)\n"
|
||||
"if face is None:\n"
|
||||
" face = col_bm.faces.new(tri_verts)\n"
|
||||
"else:\n"
|
||||
" face = face.copy()\n"
|
||||
" for i in range(3):\n"
|
||||
" face.verts[i].co = tri_verts[i].co\n"
|
||||
" col_bm.verts.ensure_lookup_table()\n"
|
||||
"face.material_index = select_material(0x{:016X}"
|
||||
")\n"
|
||||
"face.smooth = False\n"
|
||||
"\n"),
|
||||
atUint64(triMat.material));
|
||||
}
|
||||
|
||||
db.insertNoClimb(os);
|
||||
|
||||
if (isDcln)
|
||||
os.format(FMT_STRING("col_mesh = bpy.data.meshes.new('CMESH_{}')\n"), idx);
|
||||
else
|
||||
os << "col_mesh = bpy.data.meshes.new('CMESH')\n";
|
||||
|
||||
os << "col_bm.to_mesh(col_mesh)\n"
|
||||
"col_mesh_obj = bpy.data.objects.new(col_mesh.name, col_mesh)\n"
|
||||
"\n"
|
||||
"for mat_name in material_index:\n"
|
||||
" mat = material_dict[mat_name]\n"
|
||||
" col_mesh.materials.append(mat)\n"
|
||||
"\n"
|
||||
"if 'Collision' not in bpy.data.collections:\n"
|
||||
" coll = bpy.data.collections.new('Collision')\n"
|
||||
" bpy.context.scene.collection.children.link(coll)\n"
|
||||
"else:\n"
|
||||
" coll = bpy.data.collections['Collision']\n"
|
||||
"coll.objects.link(col_mesh_obj)\n"
|
||||
"bpy.context.view_layer.objects.active = col_mesh_obj\n"
|
||||
"bpy.ops.object.mode_set(mode='EDIT')\n"
|
||||
"bpy.ops.mesh.tris_convert_to_quads()\n"
|
||||
"bpy.ops.object.mode_set(mode='OBJECT')\n"
|
||||
"bpy.context.view_layer.objects.active = None\n"
|
||||
"col_mesh_obj.display_type = 'SOLID'\n"
|
||||
"\n";
|
||||
}
|
||||
|
||||
template void DeafBabeSendToBlender<DNAMP1::DeafBabe>(hecl::blender::PyOutStream& os, const DNAMP1::DeafBabe& db,
|
||||
bool isDcln, atInt32 idx);
|
||||
template void DeafBabeSendToBlender<DNAMP2::DeafBabe>(hecl::blender::PyOutStream& os, const DNAMP2::DeafBabe& db,
|
||||
bool isDcln, atInt32 idx);
|
||||
template void DeafBabeSendToBlender<DNAMP1::DCLN::Collision>(hecl::blender::PyOutStream& os,
|
||||
const DNAMP1::DCLN::Collision& db, bool isDcln,
|
||||
atInt32 idx);
|
||||
|
||||
template <class DEAFBABE>
|
||||
static void PopulateAreaFields(
|
||||
DEAFBABE& db, const hecl::blender::ColMesh& colMesh, const zeus::CAABox& fullAABB,
|
||||
std::enable_if_t<std::is_same<DEAFBABE, DNAMP1::DeafBabe>::value || std::is_same<DEAFBABE, DNAMP2::DeafBabe>::value,
|
||||
int>* = 0) {
|
||||
AROTBuilder builder;
|
||||
auto octree = builder.buildCol(colMesh, db.rootNodeType);
|
||||
static_cast<std::unique_ptr<atUint8[]>&>(db.bspTree) = std::move(octree.first);
|
||||
db.bspSize = octree.second;
|
||||
|
||||
db.unk1 = 0x1000000;
|
||||
size_t dbSize = 0;
|
||||
db.binarySize(dbSize);
|
||||
db.length = dbSize - 8;
|
||||
db.magic = 0xDEAFBABE;
|
||||
db.version = 3;
|
||||
db.aabb[0] = fullAABB.min;
|
||||
db.aabb[1] = fullAABB.max;
|
||||
}
|
||||
|
||||
template <class DEAFBABE>
|
||||
static void PopulateAreaFields(DEAFBABE& db, const hecl::blender::ColMesh& colMesh, const zeus::CAABox& fullAABB,
|
||||
std::enable_if_t<std::is_same<DEAFBABE, DNAMP1::DCLN::Collision>::value, int>* = 0) {
|
||||
db.magic = 0xDEAFBABE;
|
||||
db.version = 2;
|
||||
db.memSize = 0;
|
||||
}
|
||||
|
||||
class MaterialPool {
|
||||
std::unordered_map<u64, int> m_materials;
|
||||
|
||||
public:
|
||||
template <class M, class V>
|
||||
int AddOrLookup(const M& mat, V& vec) {
|
||||
auto search = m_materials.find(mat.material);
|
||||
if (search != m_materials.end())
|
||||
return search->second;
|
||||
auto idx = int(vec.size());
|
||||
vec.push_back(mat);
|
||||
m_materials[mat.material] = idx;
|
||||
return idx;
|
||||
}
|
||||
};
|
||||
|
||||
template <class DEAFBABE>
|
||||
void DeafBabeBuildFromBlender(DEAFBABE& db, const hecl::blender::ColMesh& colMesh) {
|
||||
using BlendMat = hecl::blender::ColMesh::Material;
|
||||
|
||||
auto MakeMat = [](const BlendMat& mat, bool flipFace) -> typename DEAFBABE::Material {
|
||||
typename DEAFBABE::Material dbMat = {};
|
||||
dbMat.setUnknown(mat.unknown);
|
||||
dbMat.setSurfaceStone(mat.surfaceStone);
|
||||
dbMat.setSurfaceMetal(mat.surfaceMetal);
|
||||
dbMat.setSurfaceGrass(mat.surfaceGrass);
|
||||
dbMat.setSurfaceIce(mat.surfaceIce);
|
||||
dbMat.setPillar(mat.pillar);
|
||||
dbMat.setSurfaceMetalGrating(mat.surfaceMetalGrating);
|
||||
dbMat.setSurfacePhazon(mat.surfacePhazon);
|
||||
dbMat.setSurfaceDirt(mat.surfaceDirt);
|
||||
dbMat.setSurfaceLava(mat.surfaceLava);
|
||||
dbMat.setSurfaceSPMetal(mat.surfaceSPMetal);
|
||||
dbMat.setSurfaceLavaStone(mat.surfaceLavaStone);
|
||||
dbMat.setSurfaceSnow(mat.surfaceSnow);
|
||||
dbMat.setSurfaceMudSlow(mat.surfaceMudSlow);
|
||||
dbMat.setSurfaceFabric(mat.surfaceFabric);
|
||||
dbMat.setHalfPipe(mat.halfPipe);
|
||||
dbMat.setSurfaceMud(mat.surfaceMud);
|
||||
dbMat.setSurfaceGlass(mat.surfaceGlass);
|
||||
dbMat.setUnused3(mat.unused3);
|
||||
dbMat.setUnused4(mat.unused4);
|
||||
dbMat.setSurfaceShield(mat.surfaceShield);
|
||||
dbMat.setSurfaceSand(mat.surfaceSand);
|
||||
dbMat.setSurfaceMothOrSeedOrganics(mat.surfaceMothOrSeedOrganics);
|
||||
dbMat.setSurfaceWeb(mat.surfaceWeb);
|
||||
dbMat.setProjectilePassthrough(mat.projPassthrough);
|
||||
dbMat.setSolid(mat.solid);
|
||||
dbMat.setNoPlatformCollision(mat.noPlatformCollision);
|
||||
dbMat.setCameraPassthrough(mat.camPassthrough);
|
||||
dbMat.setSurfaceWood(mat.surfaceWood);
|
||||
dbMat.setSurfaceOrganic(mat.surfaceOrganic);
|
||||
dbMat.setNoEdgeCollision(mat.noEdgeCollision);
|
||||
dbMat.setSurfaceRubber(mat.surfaceRubber);
|
||||
dbMat.setSeeThrough(mat.seeThrough);
|
||||
dbMat.setScanPassthrough(mat.scanPassthrough);
|
||||
dbMat.setAiPassthrough(mat.aiPassthrough);
|
||||
dbMat.setCeiling(mat.ceiling);
|
||||
dbMat.setWall(mat.wall);
|
||||
dbMat.setFloor(mat.floor);
|
||||
dbMat.setAiBlock(mat.aiBlock);
|
||||
dbMat.setJumpNotAllowed(mat.jumpNotAllowed);
|
||||
dbMat.setSpiderBall(mat.spiderBall);
|
||||
dbMat.setScrewAttackWallJump(mat.screwAttackWallJump);
|
||||
dbMat.setFlipFace(flipFace);
|
||||
return dbMat;
|
||||
};
|
||||
|
||||
MaterialPool matPool;
|
||||
db.materials.reserve(colMesh.materials.size() * 2);
|
||||
|
||||
zeus::CAABox fullAABB;
|
||||
|
||||
db.verts.reserve(colMesh.verts.size());
|
||||
db.vertMats.resize(colMesh.verts.size());
|
||||
for (const auto& vert : colMesh.verts) {
|
||||
fullAABB.accumulateBounds(zeus::CVector3f(vert));
|
||||
db.verts.push_back(vert);
|
||||
}
|
||||
db.vertMatsCount = colMesh.verts.size();
|
||||
db.vertCount = colMesh.verts.size();
|
||||
|
||||
db.edgeVertConnections.reserve(colMesh.edges.size());
|
||||
db.edgeMats.resize(colMesh.edges.size());
|
||||
for (const auto& edge : colMesh.edges) {
|
||||
db.edgeVertConnections.emplace_back();
|
||||
db.edgeVertConnections.back().verts[0] = edge.verts[0];
|
||||
db.edgeVertConnections.back().verts[1] = edge.verts[1];
|
||||
}
|
||||
db.edgeMatsCount = colMesh.edges.size();
|
||||
db.edgeVertsCount = colMesh.edges.size();
|
||||
|
||||
db.triMats.reserve(colMesh.trianges.size());
|
||||
db.triangleEdgeConnections.reserve(colMesh.trianges.size());
|
||||
for (const auto& tri : colMesh.trianges) {
|
||||
int triMatIdx = matPool.AddOrLookup(MakeMat(colMesh.materials[tri.matIdx], tri.flip), db.materials);
|
||||
db.triMats.push_back(triMatIdx);
|
||||
|
||||
db.triangleEdgeConnections.emplace_back();
|
||||
db.triangleEdgeConnections.back().edges[0] = tri.edges[0];
|
||||
db.triangleEdgeConnections.back().edges[1] = tri.edges[1];
|
||||
db.triangleEdgeConnections.back().edges[2] = tri.edges[2];
|
||||
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
db.edgeMats[tri.edges[e]] = triMatIdx;
|
||||
for (int v = 0; v < 2; ++v)
|
||||
db.vertMats[colMesh.edges[e].verts[v]] = triMatIdx;
|
||||
}
|
||||
}
|
||||
db.triMatsCount = colMesh.trianges.size();
|
||||
db.triangleEdgesCount = colMesh.trianges.size() * 3;
|
||||
|
||||
db.materialCount = db.materials.size();
|
||||
|
||||
PopulateAreaFields(db, colMesh, fullAABB);
|
||||
}
|
||||
|
||||
template void DeafBabeBuildFromBlender<DNAMP1::DeafBabe>(DNAMP1::DeafBabe& db, const hecl::blender::ColMesh& colMesh);
|
||||
template void DeafBabeBuildFromBlender<DNAMP2::DeafBabe>(DNAMP2::DeafBabe& db, const hecl::blender::ColMesh& colMesh);
|
||||
template void DeafBabeBuildFromBlender<DNAMP1::DCLN::Collision>(DNAMP1::DCLN::Collision& db,
|
||||
const hecl::blender::ColMesh& colMesh);
|
||||
|
||||
} // namespace DataSpec
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <athena/Types.hpp>
|
||||
|
||||
namespace hecl::blender {
|
||||
class PyOutStream;
|
||||
struct ColMesh;
|
||||
} // namespace hecl::blender
|
||||
|
||||
namespace DataSpec {
|
||||
|
||||
enum class BspNodeType : atUint32 { Invalid, Branch, Leaf };
|
||||
|
||||
template <class DEAFBABE>
|
||||
void DeafBabeSendToBlender(hecl::blender::PyOutStream& os, const DEAFBABE& db, bool isDcln = false, atInt32 idx = -1);
|
||||
|
||||
template <class DEAFBABE>
|
||||
void DeafBabeBuildFromBlender(DEAFBABE& db, const hecl::blender::ColMesh& colMesh);
|
||||
|
||||
} // namespace DataSpec
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace DataSpec::DNACommon {
|
||||
struct EGMC : public BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atUint32> count;
|
||||
|
||||
struct Object : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atUint32> mesh;
|
||||
Value<atUint32> instanceId;
|
||||
};
|
||||
|
||||
Vector<Object, AT_DNA_COUNT(count)> objects;
|
||||
};
|
||||
} // namespace DataSpec::DNACommon
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "DataSpec/DNACommon/ELSC.hpp"
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
namespace DataSpec::DNAParticle {
|
||||
|
||||
template struct PPImpl<_ELSM<UniqueID32>>;
|
||||
template struct PPImpl<_ELSM<UniqueID64>>;
|
||||
|
||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_ELSM<UniqueID32>>)
|
||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_ELSM<UniqueID64>>)
|
||||
|
||||
template <>
|
||||
std::string_view ELSM<UniqueID32>::DNAType() {
|
||||
return "ELSM<UniqueID32>"sv;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string_view ELSM<UniqueID64>::DNAType() {
|
||||
return "ELSM<UniqueID64>"sv;
|
||||
}
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractELSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
||||
if (writer.isOpen()) {
|
||||
ELSM<IDType> elsm;
|
||||
elsm.read(rs);
|
||||
athena::io::ToYAMLStream(elsm, writer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template bool ExtractELSM<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
template bool ExtractELSM<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteELSM(const ELSM<IDType>& elsm, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
||||
if (w.hasError())
|
||||
return false;
|
||||
elsm.write(w);
|
||||
int64_t rem = w.position() % 32;
|
||||
if (rem)
|
||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
||||
w.writeUByte(0xff);
|
||||
return true;
|
||||
}
|
||||
template bool WriteELSM<UniqueID32>(const ELSM<UniqueID32>& gpsm, const hecl::ProjectPath& outPath);
|
||||
template bool WriteELSM<UniqueID64>(const ELSM<UniqueID64>& gpsm, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNAParticle
|
||||
@@ -1,56 +0,0 @@
|
||||
#ifndef ENTRY
|
||||
#define ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef INT_ENTRY
|
||||
#define INT_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef REAL_ENTRY
|
||||
#define REAL_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef COLOR_ENTRY
|
||||
#define COLOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef EMITTER_ENTRY
|
||||
#define EMITTER_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef RES_ENTRY
|
||||
#define RES_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
#ifndef BOOL_ENTRY
|
||||
#define BOOL_ENTRY(name, identifier) ENTRY(name, identifier)
|
||||
#endif
|
||||
|
||||
INT_ENTRY('LIFE', x0_LIFE)
|
||||
INT_ENTRY('SLIF', x4_SLIF)
|
||||
REAL_ENTRY('GRAT', x8_GRAT)
|
||||
INT_ENTRY('SCNT', xc_SCNT)
|
||||
INT_ENTRY('SSEG', x10_SSEG)
|
||||
COLOR_ENTRY('COLR', x14_COLR)
|
||||
EMITTER_ENTRY('IEMT', x18_IEMT)
|
||||
EMITTER_ENTRY('FEMT', x1c_FEMT)
|
||||
REAL_ENTRY('AMPL', x20_AMPL)
|
||||
REAL_ENTRY('AMPD', x24_AMPD)
|
||||
REAL_ENTRY('LWD1', x28_LWD1)
|
||||
REAL_ENTRY('LWD2', x2c_LWD2)
|
||||
REAL_ENTRY('LWD3', x30_LWD3)
|
||||
COLOR_ENTRY('LCL1', x34_LCL1)
|
||||
COLOR_ENTRY('LCL2', x38_LCL2)
|
||||
COLOR_ENTRY('LCL3', x3c_LCL3)
|
||||
RES_ENTRY('SSWH', x40_SSWH)
|
||||
RES_ENTRY('GPSM', x50_GPSM)
|
||||
RES_ENTRY('EPSM', x60_EPSM)
|
||||
BOOL_ENTRY('ZERY', x70_ZERY)
|
||||
|
||||
#undef ENTRY
|
||||
#undef INT_ENTRY
|
||||
#undef REAL_ENTRY
|
||||
#undef COLOR_ENTRY
|
||||
#undef EMITTER_ENTRY
|
||||
#undef RES_ENTRY
|
||||
#undef BOOL_ENTRY
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
#include <athena/FileWriter.hpp>
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNAParticle {
|
||||
|
||||
template <class IDType>
|
||||
struct _ELSM {
|
||||
static constexpr ParticleType Type = ParticleType::ELSM;
|
||||
|
||||
#define INT_ENTRY(name, identifier) IntElementFactory identifier;
|
||||
#define REAL_ENTRY(name, identifier) RealElementFactory identifier;
|
||||
#define COLOR_ENTRY(name, identifier) ColorElementFactory identifier;
|
||||
#define EMITTER_ENTRY(name, identifier) EmitterElementFactory identifier;
|
||||
#define RES_ENTRY(name, identifier) ChildResourceFactory<IDType> identifier;
|
||||
#define BOOL_ENTRY(name, identifier) bool identifier = false;
|
||||
#include "ELSC.def"
|
||||
|
||||
template <typename _Func>
|
||||
void constexpr Enumerate(_Func f) {
|
||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
||||
#include "ELSC.def"
|
||||
}
|
||||
|
||||
template <typename _Func>
|
||||
bool constexpr Lookup(FourCC fcc, _Func f) {
|
||||
switch (fcc.toUint32()) {
|
||||
#define ENTRY(name, identifier) \
|
||||
case SBIG(name): \
|
||||
f(identifier); \
|
||||
return true;
|
||||
#include "ELSC.def"
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
template <class IDType>
|
||||
using ELSM = PPImpl<_ELSM<IDType>>;
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractELSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteELSM(const ELSM<IDType>& elsm, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNAParticle
|
||||
@@ -1,235 +0,0 @@
|
||||
#include "DataSpec/DNACommon/FONT.hpp"
|
||||
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
#include <logvisor/logvisor.hpp>
|
||||
|
||||
namespace DataSpec::DNAFont {
|
||||
logvisor::Module LogModule("DataSpec::DNAFont");
|
||||
|
||||
template <class IDType>
|
||||
void FONT<IDType>::_read(athena::io::IStreamReader& __dna_reader) {
|
||||
/* magic */
|
||||
DNAFourCC magic;
|
||||
magic.read(__dna_reader);
|
||||
if (magic != SBIG('FONT')) {
|
||||
LogModule.report(logvisor::Fatal, FMT_STRING("Invalid FONT magic '{}'"), magic);
|
||||
return;
|
||||
}
|
||||
/* version */
|
||||
version = __dna_reader.readUint32Big();
|
||||
/* unknown1 */
|
||||
unknown1 = __dna_reader.readUint32Big();
|
||||
/* lineHeight */
|
||||
lineHeight = __dna_reader.readInt32Big();
|
||||
/* verticalOffset */
|
||||
verticalOffset = __dna_reader.readInt32Big();
|
||||
/* lineMargin */
|
||||
lineMargin = __dna_reader.readInt32Big();
|
||||
/* unknown2 */
|
||||
unknown2 = __dna_reader.readBool();
|
||||
/* unknown3 */
|
||||
unknown3 = __dna_reader.readBool();
|
||||
/* unknown4 */
|
||||
unknown4 = __dna_reader.readUint32Big();
|
||||
/* fontSize */
|
||||
fontSize = __dna_reader.readUint32Big();
|
||||
/* name */
|
||||
name = __dna_reader.readString(-1);
|
||||
/* textureId */
|
||||
textureId.read(__dna_reader);
|
||||
/* textureFormat */
|
||||
textureFormat = __dna_reader.readUint32Big();
|
||||
/* glyphCount */
|
||||
glyphCount = __dna_reader.readUint32Big();
|
||||
/* glyphs */
|
||||
for (atUint32 i = 0; i < glyphCount; i++) {
|
||||
if (version < 4)
|
||||
glyphs.emplace_back(new GlyphMP1);
|
||||
else
|
||||
glyphs.emplace_back(new GlyphMP2);
|
||||
glyphs.back()->read(__dna_reader);
|
||||
}
|
||||
/* kerningInfoCount */
|
||||
kerningInfoCount = __dna_reader.readUint32Big();
|
||||
/* kerningInfo */
|
||||
__dna_reader.enumerate(kerningInfo, kerningInfoCount);
|
||||
}
|
||||
|
||||
template <class IDType>
|
||||
void FONT<IDType>::_write(athena::io::IStreamWriter& __dna_writer) const {
|
||||
/* magic */
|
||||
__dna_writer.writeBytes((atInt8*)"FONT", 4);
|
||||
/* version */
|
||||
__dna_writer.writeUint32Big(version);
|
||||
/* unknown1 */
|
||||
__dna_writer.writeUint32Big(unknown1);
|
||||
/* lineHeight */
|
||||
__dna_writer.writeInt32Big(lineHeight);
|
||||
/* verticalOffset */
|
||||
__dna_writer.writeInt32Big(verticalOffset);
|
||||
/* lineMargin */
|
||||
__dna_writer.writeInt32Big(lineMargin);
|
||||
/* unknown2 */
|
||||
__dna_writer.writeBool(unknown2);
|
||||
/* unknown3 */
|
||||
__dna_writer.writeBool(unknown3);
|
||||
/* unknown4 */
|
||||
__dna_writer.writeUint32Big(unknown4);
|
||||
/* fontSize */
|
||||
__dna_writer.writeUint32Big(fontSize);
|
||||
/* name */
|
||||
__dna_writer.writeString(name, -1);
|
||||
/* textureId */
|
||||
textureId.write(__dna_writer);
|
||||
/* textureFormat */
|
||||
__dna_writer.writeUint32Big(textureFormat);
|
||||
/* glyphCount */
|
||||
__dna_writer.writeUint32Big(glyphCount);
|
||||
/* glyphs */
|
||||
for (const std::unique_ptr<IGlyph>& glyph : glyphs)
|
||||
glyph->write(__dna_writer);
|
||||
/* kerningInfoCount */
|
||||
__dna_writer.writeUint32Big(kerningInfoCount);
|
||||
/* kerningInfo */
|
||||
__dna_writer.enumerate(kerningInfo);
|
||||
}
|
||||
|
||||
template <class IDType>
|
||||
void FONT<IDType>::_read(athena::io::YAMLDocReader& __dna_docin) {
|
||||
/* version */
|
||||
version = __dna_docin.readUint32("version");
|
||||
/* unknown1 */
|
||||
unknown1 = __dna_docin.readUint32("unknown1");
|
||||
/* lineHeight */
|
||||
lineHeight = __dna_docin.readInt32("lineHeight");
|
||||
/* verticalOffset */
|
||||
verticalOffset = __dna_docin.readInt32("verticalOffset");
|
||||
/* lineMargin */
|
||||
lineMargin = __dna_docin.readInt32("lineMargin");
|
||||
/* unknown2 */
|
||||
unknown2 = __dna_docin.readBool("unknown2");
|
||||
/* unknown3 */
|
||||
unknown3 = __dna_docin.readBool("unknown3");
|
||||
/* unknown4 */
|
||||
unknown4 = __dna_docin.readUint32("unknown4");
|
||||
/* fontSize */
|
||||
fontSize = __dna_docin.readUint32("fontSize");
|
||||
/* name */
|
||||
name = __dna_docin.readString("name");
|
||||
/* textureId */
|
||||
__dna_docin.enumerate("textureId", textureId);
|
||||
/* textureFormat */
|
||||
textureFormat = __dna_docin.readUint32("textureFormat");
|
||||
/* glyphCount */
|
||||
/* glyphs */
|
||||
size_t count;
|
||||
if (auto v = __dna_docin.enterSubVector("glyphs", count)) {
|
||||
glyphCount = count;
|
||||
for (atUint32 i = 0; i < glyphCount; i++) {
|
||||
if (version < 4)
|
||||
glyphs.emplace_back(new GlyphMP1);
|
||||
else
|
||||
glyphs.emplace_back(new GlyphMP2);
|
||||
|
||||
if (auto rec = __dna_docin.enterSubRecord())
|
||||
glyphs.back()->read(__dna_docin);
|
||||
}
|
||||
}
|
||||
/* kerningInfoCount squelched */
|
||||
/* kerningInfo */
|
||||
kerningInfoCount = __dna_docin.enumerate("kerningInfo", kerningInfo);
|
||||
}
|
||||
|
||||
template <class IDType>
|
||||
void FONT<IDType>::_write(athena::io::YAMLDocWriter& __dna_docout) const {
|
||||
/* version */
|
||||
__dna_docout.writeUint32("version", version);
|
||||
/* unknown1 */
|
||||
__dna_docout.writeUint32("unknown1", unknown1);
|
||||
/* lineHeight */
|
||||
__dna_docout.writeInt32("lineHeight", lineHeight);
|
||||
/* verticalOffset */
|
||||
__dna_docout.writeInt32("verticalOffset", verticalOffset);
|
||||
/* lineMargin */
|
||||
__dna_docout.writeInt32("lineMargin", lineMargin);
|
||||
/* unknown2 */
|
||||
__dna_docout.writeBool("unknown2", unknown2);
|
||||
/* unknown3 */
|
||||
__dna_docout.writeBool("unknown3", unknown3);
|
||||
/* unknown4 */
|
||||
__dna_docout.writeUint32("unknown4", unknown4);
|
||||
/* fontSize */
|
||||
__dna_docout.writeUint32("fontSize", fontSize);
|
||||
/* name */
|
||||
__dna_docout.writeString("name", name);
|
||||
/* textureId */
|
||||
__dna_docout.enumerate("textureId", textureId);
|
||||
/* textureFormat */
|
||||
__dna_docout.writeUint32("textureFormat", textureFormat);
|
||||
/* glyphCount squelched */
|
||||
/* glyphs */
|
||||
if (auto v = __dna_docout.enterSubVector("glyphs"))
|
||||
for (const std::unique_ptr<IGlyph>& glyph : glyphs)
|
||||
if (auto rec = __dna_docout.enterSubRecord())
|
||||
glyph->write(__dna_docout);
|
||||
/* kerningInfoCount squelched */
|
||||
/* kerningInfo */
|
||||
__dna_docout.enumerate("kerningInfo", kerningInfo);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string_view FONT<UniqueID32>::DNAType() {
|
||||
return "FONT<UniqueID32>"sv;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string_view FONT<UniqueID64>::DNAType() {
|
||||
return "FONT<UniqueID64>"sv;
|
||||
}
|
||||
|
||||
template <class IDType>
|
||||
void FONT<IDType>::_binarySize(size_t& __isz) const {
|
||||
__isz += name.size() + 1;
|
||||
textureId.binarySize(__isz);
|
||||
for (const std::unique_ptr<IGlyph>& glyph : glyphs)
|
||||
glyph->binarySize(__isz);
|
||||
for (const KerningInfo& k : kerningInfo)
|
||||
k.binarySize(__isz);
|
||||
__isz += 46;
|
||||
}
|
||||
|
||||
AT_SUBSPECIALIZE_DNA_YAML(FONT<UniqueID32>)
|
||||
AT_SUBSPECIALIZE_DNA_YAML(FONT<UniqueID64>)
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractFONT(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
||||
if (writer.isOpen()) {
|
||||
FONT<IDType> font;
|
||||
font.read(rs);
|
||||
athena::io::ToYAMLStream(font, writer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template bool ExtractFONT<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
template bool ExtractFONT<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteFONT(const FONT<IDType>& font, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
||||
if (w.hasError())
|
||||
return false;
|
||||
font.write(w);
|
||||
int64_t rem = w.position() % 32;
|
||||
if (rem)
|
||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
||||
w.writeUByte(0xff);
|
||||
return true;
|
||||
}
|
||||
template bool WriteFONT<UniqueID32>(const FONT<UniqueID32>& font, const hecl::ProjectPath& outPath);
|
||||
template bool WriteFONT<UniqueID64>(const FONT<UniqueID64>& font, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNAFont
|
||||
@@ -1,127 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
|
||||
#include <athena/FileWriter.hpp>
|
||||
|
||||
namespace DataSpec {
|
||||
class PAKEntryReadStream;
|
||||
}
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNAFont {
|
||||
struct GlyphRect : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
Value<float> left;
|
||||
Value<float> top;
|
||||
Value<float> right;
|
||||
Value<float> bottom;
|
||||
};
|
||||
struct IGlyph : BigDNAVYaml {
|
||||
AT_DECL_DNA_YAMLV
|
||||
Value<atUint16> m_character;
|
||||
GlyphRect m_glyphRect;
|
||||
|
||||
atUint16 character() const { return m_character; }
|
||||
float left() const { return m_glyphRect.left; }
|
||||
float top() const { return m_glyphRect.top; }
|
||||
float right() const { return m_glyphRect.right; }
|
||||
float bottom() const { return m_glyphRect.bottom; }
|
||||
GlyphRect rect() const { return m_glyphRect; }
|
||||
|
||||
virtual atInt32 layer() const { return 0; }
|
||||
virtual atInt32 leftPadding() const = 0;
|
||||
virtual atInt32 advance() const = 0;
|
||||
virtual atInt32 rightPadding() const = 0;
|
||||
virtual atInt32 width() const = 0;
|
||||
virtual atInt32 height() const = 0;
|
||||
virtual atInt32 baseline() const = 0;
|
||||
virtual atInt32 kerningIndex() const = 0;
|
||||
};
|
||||
|
||||
struct GlyphMP1 : IGlyph {
|
||||
AT_DECL_DNA_YAMLV
|
||||
Value<atInt32> m_leftPadding;
|
||||
Value<atInt32> m_advance;
|
||||
Value<atInt32> m_rightPadding;
|
||||
Value<atInt32> m_width;
|
||||
Value<atInt32> m_height;
|
||||
Value<atInt32> m_baseline;
|
||||
Value<atInt32> m_kerningIndex;
|
||||
|
||||
atInt32 leftPadding() const override { return m_leftPadding; }
|
||||
atInt32 advance() const override { return m_advance; }
|
||||
atInt32 rightPadding() const override { return m_rightPadding; }
|
||||
atInt32 width() const override { return m_width; }
|
||||
atInt32 height() const override { return m_height; }
|
||||
atInt32 baseline() const override { return m_baseline; }
|
||||
atInt32 kerningIndex() const override { return m_kerningIndex; }
|
||||
};
|
||||
|
||||
struct GlyphMP2 : IGlyph {
|
||||
AT_DECL_DNA_YAMLV
|
||||
Value<atInt8> m_layer;
|
||||
Value<atInt8> m_leftPadding;
|
||||
Value<atInt8> m_advance;
|
||||
Value<atInt8> m_rightPadding;
|
||||
Value<atInt8> m_width;
|
||||
Value<atInt8> m_height;
|
||||
Value<atInt8> m_baseline;
|
||||
Value<atInt16> m_kerningIndex;
|
||||
|
||||
atInt32 layer() const override { return m_layer; }
|
||||
atInt32 leftPadding() const override { return m_leftPadding; }
|
||||
atInt32 advance() const override { return m_advance; }
|
||||
atInt32 rightPadding() const override { return m_rightPadding; }
|
||||
atInt32 width() const override { return m_width; }
|
||||
atInt32 height() const override { return m_height; }
|
||||
atInt32 baseline() const override { return m_baseline; }
|
||||
atInt32 kerningIndex() const override { return m_kerningIndex; }
|
||||
};
|
||||
|
||||
struct KerningInfo : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
Value<atUint16> thisChar;
|
||||
Value<atUint16> nextChar;
|
||||
Value<atInt32> adjust;
|
||||
};
|
||||
|
||||
template <class IDType>
|
||||
struct AT_SPECIALIZE_PARMS(DataSpec::UniqueID32, DataSpec::UniqueID64) FONT : BigDNA {
|
||||
AT_DECL_EXPLICIT_DNA_YAML
|
||||
AT_SUBDECL_DNA
|
||||
Value<atUint32> version;
|
||||
Value<atUint32> unknown1;
|
||||
Value<atInt32> lineHeight;
|
||||
Value<atInt32> verticalOffset;
|
||||
Value<atInt32> lineMargin;
|
||||
Value<bool> unknown2;
|
||||
Value<bool> unknown3;
|
||||
Value<atUint32> unknown4;
|
||||
Value<atUint32> fontSize; // in points
|
||||
String<-1> name;
|
||||
Value<IDType> textureId;
|
||||
Value<atUint32> textureFormat;
|
||||
Value<atUint32> glyphCount;
|
||||
std::vector<std::unique_ptr<IGlyph>> glyphs;
|
||||
Value<atUint32> kerningInfoCount;
|
||||
Vector<KerningInfo, AT_DNA_COUNT(kerningInfoCount)> kerningInfo;
|
||||
|
||||
void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {
|
||||
g_curSpec->flattenDependencies(textureId, pathsOut);
|
||||
}
|
||||
};
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractFONT(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteFONT(const FONT<IDType>& font, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNAFont
|
||||
@@ -1,82 +0,0 @@
|
||||
#include "DataSpec/DNACommon/FSM2.hpp"
|
||||
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
#include <athena/FileWriter.hpp>
|
||||
#include <athena/Global.hpp>
|
||||
#include <athena/IStreamWriter.hpp>
|
||||
|
||||
#include <logvisor/logvisor.hpp>
|
||||
|
||||
namespace DataSpec::DNAFSM2 {
|
||||
logvisor::Module LogDNAFSM2("DataSpec::DNAFSM2");
|
||||
|
||||
template <class IDType>
|
||||
template <class Op>
|
||||
void FSM2<IDType>::Enumerate(typename Op::StreamT& s) {
|
||||
Do<Op>(athena::io::PropId{"header"}, header, s);
|
||||
if (header.magic != SBIG('FSM2')) {
|
||||
LogDNAFSM2.report(logvisor::Fatal, FMT_STRING("Invalid FSM2 magic '{}' expected 'FSM2'"), header.magic);
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.version == 1) {
|
||||
if (!detail)
|
||||
detail.reset(new FSMV1);
|
||||
Do<Op>(athena::io::PropId{"detail"}, static_cast<FSMV1&>(*detail), s);
|
||||
} else if (header.version == 2) {
|
||||
if (!detail)
|
||||
detail.reset(new FSMV2);
|
||||
Do<Op>(athena::io::PropId{"detail"}, static_cast<FSMV2&>(*detail), s);
|
||||
} else {
|
||||
LogDNAFSM2.report(logvisor::Fatal, FMT_STRING("Invalid FSM2 version '{}'"), header.version);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AT_SPECIALIZE_DNA(FSM2<UniqueID32>)
|
||||
AT_SPECIALIZE_DNA(FSM2<UniqueID64>)
|
||||
|
||||
template <>
|
||||
std::string_view FSM2<UniqueID32>::DNAType() {
|
||||
return "FSM2<UniqueID32>"sv;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string_view FSM2<UniqueID64>::DNAType() {
|
||||
return "FSM2<UniqueID64>"sv;
|
||||
}
|
||||
|
||||
template struct FSM2<UniqueID32>;
|
||||
template struct FSM2<UniqueID64>;
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractFSM2(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
||||
if (writer.isOpen()) {
|
||||
FSM2<IDType> fsm2;
|
||||
fsm2.read(rs);
|
||||
athena::io::ToYAMLStream(fsm2, writer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template bool ExtractFSM2<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
template bool ExtractFSM2<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
|
||||
template <class IDType>
|
||||
bool WriteFSM2(const FSM2<IDType>& fsm2, const hecl::ProjectPath& outPath) {
|
||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
||||
if (w.hasError())
|
||||
return false;
|
||||
fsm2.write(w);
|
||||
int64_t rem = w.position() % 32;
|
||||
if (rem)
|
||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
||||
w.writeUByte(0xff);
|
||||
return true;
|
||||
}
|
||||
template bool WriteFSM2<UniqueID32>(const FSM2<UniqueID32>& fsm2, const hecl::ProjectPath& outPath);
|
||||
template bool WriteFSM2<UniqueID64>(const FSM2<UniqueID64>& fsm2, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNAFSM2
|
||||
@@ -1,147 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
|
||||
#include <athena/DNA.hpp>
|
||||
|
||||
namespace DataSpec {
|
||||
class PAKEntryReadStream;
|
||||
}
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNAFSM2 {
|
||||
struct IFSM : BigDNAVYaml {
|
||||
Delete _d;
|
||||
};
|
||||
|
||||
template <class IDType>
|
||||
struct AT_SPECIALIZE_PARMS(DataSpec::UniqueID32, DataSpec::UniqueID64) FSM2 : BigDNA {
|
||||
struct Header : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
DNAFourCC magic = FOURCC('FSM2');
|
||||
Value<atUint32> version;
|
||||
} header;
|
||||
|
||||
struct CommonStruct : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<atUint32> unknown;
|
||||
};
|
||||
|
||||
struct FSMV1 : IFSM {
|
||||
AT_DECL_DNA_YAMLV
|
||||
Value<atUint32> stateCount;
|
||||
Value<atUint32> unknown1Count;
|
||||
Value<atUint32> unknown2Count;
|
||||
Value<atUint32> unknown3Count;
|
||||
struct State : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<atUint32> unknownCount;
|
||||
Vector<CommonStruct, AT_DNA_COUNT(unknownCount)> unknown;
|
||||
};
|
||||
|
||||
struct Unknown1 : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<float> unknown1;
|
||||
Value<atUint32> unknown2Count;
|
||||
Vector<CommonStruct, AT_DNA_COUNT(unknown2Count)> unknown2;
|
||||
Value<atUint8> unknown3;
|
||||
};
|
||||
|
||||
struct Unknown2 : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<atUint32> unknownCount;
|
||||
Vector<CommonStruct, AT_DNA_COUNT(unknownCount)> unknown;
|
||||
};
|
||||
|
||||
struct Unknown3 : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<atUint32> unknownCount;
|
||||
Vector<CommonStruct, AT_DNA_COUNT(unknownCount)> unknown;
|
||||
Value<IDType> fsmId;
|
||||
};
|
||||
|
||||
Vector<State, AT_DNA_COUNT(stateCount)> states;
|
||||
Vector<Unknown1, AT_DNA_COUNT(unknown1Count)> unknown1;
|
||||
Vector<Unknown2, AT_DNA_COUNT(unknown2Count)> unknown2;
|
||||
Vector<Unknown3, AT_DNA_COUNT(unknown3Count)> unknown3;
|
||||
};
|
||||
|
||||
struct FSMV2 : IFSM {
|
||||
AT_DECL_DNA_YAMLV
|
||||
Value<atUint32> stateCount;
|
||||
Value<atUint32> unknown1Count;
|
||||
Value<atUint32> unknown2Count;
|
||||
Value<atUint32> unknown3Count;
|
||||
struct State : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<atUint32> unknown1;
|
||||
Value<atUint32> unknown2;
|
||||
Value<atUint32> unknown3;
|
||||
Value<atUint32> unknown4;
|
||||
Value<atUint32> unknown5Count;
|
||||
Vector<CommonStruct, AT_DNA_COUNT(unknown5Count)> unknown5;
|
||||
};
|
||||
|
||||
struct Unknown1 : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<atUint32> unknown1;
|
||||
Value<atUint32> unknown2;
|
||||
Value<atUint32> unknown3;
|
||||
Value<atUint32> unknown4;
|
||||
Value<float> unknown5;
|
||||
Value<atUint32> unknown6Count;
|
||||
Vector<CommonStruct, AT_DNA_COUNT(unknown6Count)> unknown6;
|
||||
Value<atUint8> unknown7;
|
||||
};
|
||||
|
||||
struct Unknown2 : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<atUint32> unknown1;
|
||||
Value<atUint32> unknown2;
|
||||
Value<atUint32> unknown3;
|
||||
Value<atUint32> unknown4;
|
||||
Value<atUint32> unknown5Count;
|
||||
Vector<CommonStruct, AT_DNA_COUNT(unknown5Count)> unknown5;
|
||||
};
|
||||
|
||||
struct Unknown3 : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
String<-1> name;
|
||||
Value<atUint32> unknown1;
|
||||
Value<atUint32> unknown2;
|
||||
Value<atUint32> unknown3;
|
||||
Value<atUint32> unknown4;
|
||||
Value<atUint32> unknown5Count;
|
||||
Vector<CommonStruct, AT_DNA_COUNT(unknown5Count)> unknown5;
|
||||
Value<IDType> fsmId;
|
||||
};
|
||||
|
||||
Vector<State, AT_DNA_COUNT(stateCount)> states;
|
||||
Vector<Unknown1, AT_DNA_COUNT(unknown1Count)> unknown1;
|
||||
Vector<Unknown2, AT_DNA_COUNT(unknown2Count)> unknown2;
|
||||
Vector<Unknown3, AT_DNA_COUNT(unknown3Count)> unknown3;
|
||||
};
|
||||
|
||||
std::unique_ptr<IFSM> detail;
|
||||
AT_DECL_EXPLICIT_DNA_YAML
|
||||
};
|
||||
|
||||
template <class IDType>
|
||||
bool ExtractFSM2(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
||||
template <class IDType>
|
||||
bool WriteFSM2(const FSM2<IDType>& fsm2, const hecl::ProjectPath& outPath);
|
||||
|
||||
} // namespace DataSpec::DNAFSM2
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "GX.hpp"
|
||||
|
||||
namespace GX {
|
||||
|
||||
template <>
|
||||
void Color::Enumerate<athena::io::DNA<athena::Endian::Big>::Read>(Read::StreamT& reader) {
|
||||
reader.readUBytesToBuf(&num, 4);
|
||||
}
|
||||
template <>
|
||||
void Color::Enumerate<athena::io::DNA<athena::Endian::Big>::Write>(Write::StreamT& writer) {
|
||||
writer.writeUBytes(reinterpret_cast<const atUint8*>(&num), 4);
|
||||
}
|
||||
template <>
|
||||
void Color::Enumerate<athena::io::DNA<athena::Endian::Big>::BinarySize>(BinarySize::StreamT& s) {
|
||||
s += 4;
|
||||
}
|
||||
|
||||
} // namespace GX
|
||||
@@ -1,298 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <athena/DNA.hpp>
|
||||
|
||||
namespace GX {
|
||||
enum AttrType { NONE, DIRECT, INDEX8, INDEX16 };
|
||||
|
||||
enum TevColorArg {
|
||||
CC_CPREV = 0,
|
||||
CC_APREV = 1,
|
||||
CC_C0 = 2,
|
||||
CC_A0 = 3,
|
||||
CC_C1 = 4,
|
||||
CC_A1 = 5,
|
||||
CC_C2 = 6,
|
||||
CC_A2 = 7,
|
||||
CC_TEXC = 8,
|
||||
CC_TEXA = 9,
|
||||
CC_RASC = 10,
|
||||
CC_RASA = 11,
|
||||
CC_ONE = 12,
|
||||
CC_HALF = 13,
|
||||
CC_KONST = 14,
|
||||
CC_ZERO = 15,
|
||||
};
|
||||
|
||||
enum TevAlphaArg {
|
||||
CA_APREV = 0,
|
||||
CA_A0 = 1,
|
||||
CA_A1 = 2,
|
||||
CA_A2 = 3,
|
||||
CA_TEXA = 4,
|
||||
CA_RASA = 5,
|
||||
CA_KONST = 6,
|
||||
CA_ZERO = 7,
|
||||
};
|
||||
|
||||
enum TevKColorSel {
|
||||
TEV_KCSEL_8_8 = 0x00,
|
||||
TEV_KCSEL_7_8 = 0x01,
|
||||
TEV_KCSEL_6_8 = 0x02,
|
||||
TEV_KCSEL_5_8 = 0x03,
|
||||
TEV_KCSEL_4_8 = 0x04,
|
||||
TEV_KCSEL_3_8 = 0x05,
|
||||
TEV_KCSEL_2_8 = 0x06,
|
||||
TEV_KCSEL_1_8 = 0x07,
|
||||
|
||||
TEV_KCSEL_1 = TEV_KCSEL_8_8,
|
||||
TEV_KCSEL_3_4 = TEV_KCSEL_6_8,
|
||||
TEV_KCSEL_1_2 = TEV_KCSEL_4_8,
|
||||
TEV_KCSEL_1_4 = TEV_KCSEL_2_8,
|
||||
|
||||
TEV_KCSEL_K0 = 0x0C,
|
||||
TEV_KCSEL_K1 = 0x0D,
|
||||
TEV_KCSEL_K2 = 0x0E,
|
||||
TEV_KCSEL_K3 = 0x0F,
|
||||
TEV_KCSEL_K0_R = 0x10,
|
||||
TEV_KCSEL_K1_R = 0x11,
|
||||
TEV_KCSEL_K2_R = 0x12,
|
||||
TEV_KCSEL_K3_R = 0x13,
|
||||
TEV_KCSEL_K0_G = 0x14,
|
||||
TEV_KCSEL_K1_G = 0x15,
|
||||
TEV_KCSEL_K2_G = 0x16,
|
||||
TEV_KCSEL_K3_G = 0x17,
|
||||
TEV_KCSEL_K0_B = 0x18,
|
||||
TEV_KCSEL_K1_B = 0x19,
|
||||
TEV_KCSEL_K2_B = 0x1A,
|
||||
TEV_KCSEL_K3_B = 0x1B,
|
||||
TEV_KCSEL_K0_A = 0x1C,
|
||||
TEV_KCSEL_K1_A = 0x1D,
|
||||
TEV_KCSEL_K2_A = 0x1E,
|
||||
TEV_KCSEL_K3_A = 0x1F
|
||||
};
|
||||
|
||||
enum TevKAlphaSel {
|
||||
TEV_KASEL_8_8 = 0x00,
|
||||
TEV_KASEL_7_8 = 0x01,
|
||||
TEV_KASEL_6_8 = 0x02,
|
||||
TEV_KASEL_5_8 = 0x03,
|
||||
TEV_KASEL_4_8 = 0x04,
|
||||
TEV_KASEL_3_8 = 0x05,
|
||||
TEV_KASEL_2_8 = 0x06,
|
||||
TEV_KASEL_1_8 = 0x07,
|
||||
|
||||
TEV_KASEL_1 = TEV_KASEL_8_8,
|
||||
TEV_KASEL_3_4 = TEV_KASEL_6_8,
|
||||
TEV_KASEL_1_2 = TEV_KASEL_4_8,
|
||||
TEV_KASEL_1_4 = TEV_KASEL_2_8,
|
||||
|
||||
TEV_KASEL_K0_R = 0x10,
|
||||
TEV_KASEL_K1_R = 0x11,
|
||||
TEV_KASEL_K2_R = 0x12,
|
||||
TEV_KASEL_K3_R = 0x13,
|
||||
TEV_KASEL_K0_G = 0x14,
|
||||
TEV_KASEL_K1_G = 0x15,
|
||||
TEV_KASEL_K2_G = 0x16,
|
||||
TEV_KASEL_K3_G = 0x17,
|
||||
TEV_KASEL_K0_B = 0x18,
|
||||
TEV_KASEL_K1_B = 0x19,
|
||||
TEV_KASEL_K2_B = 0x1A,
|
||||
TEV_KASEL_K3_B = 0x1B,
|
||||
TEV_KASEL_K0_A = 0x1C,
|
||||
TEV_KASEL_K1_A = 0x1D,
|
||||
TEV_KASEL_K2_A = 0x1E,
|
||||
TEV_KASEL_K3_A = 0x1F
|
||||
};
|
||||
|
||||
enum TevOp {
|
||||
TEV_ADD = 0,
|
||||
TEV_SUB = 1,
|
||||
TEV_COMP_R8_GT = 8,
|
||||
TEV_COMP_R8_EQ = 9,
|
||||
TEV_COMP_GR16_GT = 10,
|
||||
TEV_COMP_GR16_EQ = 11,
|
||||
TEV_COMP_BGR24_GT = 12,
|
||||
TEV_COMP_BGR24_EQ = 13,
|
||||
TEV_COMP_RGB8_GT = 14,
|
||||
TEV_COMP_RGB8_EQ = 15,
|
||||
TEV_COMP_A8_GT = TEV_COMP_RGB8_GT,
|
||||
TEV_COMP_A8_EQ = TEV_COMP_RGB8_EQ
|
||||
};
|
||||
|
||||
enum TevBias {
|
||||
TB_ZERO = 0,
|
||||
TB_ADDHALF = 1,
|
||||
TB_SUBHALF = 2,
|
||||
};
|
||||
|
||||
enum TevScale { CS_SCALE_1 = 0, CS_SCALE_2 = 1, CS_SCALE_4 = 2, CS_DIVIDE_2 = 3 };
|
||||
|
||||
enum TexGenType {
|
||||
TG_MTX3x4 = 0,
|
||||
TG_MTX2x4,
|
||||
TG_BUMP0,
|
||||
TG_BUMP1,
|
||||
TG_BUMP2,
|
||||
TG_BUMP3,
|
||||
TG_BUMP4,
|
||||
TG_BUMP5,
|
||||
TG_BUMP6,
|
||||
TG_BUMP7,
|
||||
TG_SRTG
|
||||
};
|
||||
|
||||
enum TexGenSrc {
|
||||
TG_POS = 0,
|
||||
TG_NRM,
|
||||
TG_BINRM,
|
||||
TG_TANGENT,
|
||||
TG_TEX0,
|
||||
TG_TEX1,
|
||||
TG_TEX2,
|
||||
TG_TEX3,
|
||||
TG_TEX4,
|
||||
TG_TEX5,
|
||||
TG_TEX6,
|
||||
TG_TEX7,
|
||||
TG_TEXCOORD0,
|
||||
TG_TEXCOORD1,
|
||||
TG_TEXCOORD2,
|
||||
TG_TEXCOORD3,
|
||||
TG_TEXCOORD4,
|
||||
TG_TEXCOORD5,
|
||||
TG_TEXCOORD6,
|
||||
TG_COLOR0,
|
||||
TG_COLOR1
|
||||
};
|
||||
|
||||
enum TexMtx {
|
||||
TEXMTX0 = 30,
|
||||
TEXMTX1 = 33,
|
||||
TEXMTX2 = 36,
|
||||
TEXMTX3 = 39,
|
||||
TEXMTX4 = 42,
|
||||
TEXMTX5 = 45,
|
||||
TEXMTX6 = 48,
|
||||
TEXMTX7 = 51,
|
||||
TEXMTX8 = 54,
|
||||
TEXMTX9 = 57,
|
||||
IDENTITY = 60
|
||||
};
|
||||
|
||||
enum PTTexMtx {
|
||||
PTTEXMTX0 = 64,
|
||||
PTTEXMTX1 = 67,
|
||||
PTTEXMTX2 = 70,
|
||||
PTTEXMTX3 = 73,
|
||||
PTTEXMTX4 = 76,
|
||||
PTTEXMTX5 = 79,
|
||||
PTTEXMTX6 = 82,
|
||||
PTTEXMTX7 = 85,
|
||||
PTTEXMTX8 = 88,
|
||||
PTTEXMTX9 = 91,
|
||||
PTTEXMTX10 = 94,
|
||||
PTTEXMTX11 = 97,
|
||||
PTTEXMTX12 = 100,
|
||||
PTTEXMTX13 = 103,
|
||||
PTTEXMTX14 = 106,
|
||||
PTTEXMTX15 = 109,
|
||||
PTTEXMTX16 = 112,
|
||||
PTTEXMTX17 = 115,
|
||||
PTTEXMTX18 = 118,
|
||||
PTTEXMTX19 = 121,
|
||||
PTIDENTITY = 125
|
||||
};
|
||||
|
||||
enum TevRegID { TEVPREV = 0, TEVREG0 = 1, TEVREG1 = 2, TEVREG2 = 3, TEVLAZY = 5 };
|
||||
|
||||
enum DiffuseFn { DF_NONE = 0, DF_SIGN, DF_CLAMP };
|
||||
|
||||
enum AttnFn { AF_SPEC = 0, AF_SPOT = 1, AF_NONE };
|
||||
|
||||
enum Primitive {
|
||||
POINTS = 0xb8,
|
||||
LINES = 0xa8,
|
||||
LINESTRIP = 0xb0,
|
||||
TRIANGLES = 0x90,
|
||||
TRIANGLESTRIP = 0x98,
|
||||
TRIANGLEFAN = 0xa0,
|
||||
QUADS = 0x80
|
||||
};
|
||||
|
||||
enum ChannelID {
|
||||
GX_COLOR0,
|
||||
GX_COLOR1,
|
||||
GX_ALPHA0,
|
||||
GX_ALPHA1,
|
||||
GX_COLOR0A0,
|
||||
GX_COLOR1A1,
|
||||
GX_COLOR_ZERO,
|
||||
GX_ALPHA_BUMP,
|
||||
GX_ALPHA_BUMPN,
|
||||
GX_COLOR_NULL = 0xff
|
||||
};
|
||||
|
||||
enum BlendFactor : uint16_t {
|
||||
BL_ZERO,
|
||||
BL_ONE,
|
||||
BL_SRCCLR,
|
||||
BL_INVSRCCLR,
|
||||
BL_SRCALPHA,
|
||||
BL_INVSRCALPHA,
|
||||
BL_DSTALPHA,
|
||||
BL_INVDSTALPHA
|
||||
};
|
||||
|
||||
struct Color : athena::io::DNA<athena::Endian::Big> {
|
||||
union {
|
||||
uint8_t color[4];
|
||||
uint32_t num = 0;
|
||||
};
|
||||
Color() = default;
|
||||
Color& operator=(const atVec4f& vec) {
|
||||
athena::simd_floats f(vec.simd);
|
||||
color[0] = uint8_t(std::min(std::max(f[0] * 255.f, 0.f), 255.f));
|
||||
color[1] = uint8_t(std::min(std::max(f[1] * 255.f, 0.f), 255.f));
|
||||
color[2] = uint8_t(std::min(std::max(f[2] * 255.f, 0.f), 255.f));
|
||||
color[3] = uint8_t(std::min(std::max(f[3] * 255.f, 0.f), 255.f));
|
||||
return *this;
|
||||
}
|
||||
Color& operator=(const atVec3f& vec) {
|
||||
athena::simd_floats f(vec.simd);
|
||||
color[0] = uint8_t(std::min(std::max(f[0] * 255.f, 0.f), 255.f));
|
||||
color[1] = uint8_t(std::min(std::max(f[1] * 255.f, 0.f), 255.f));
|
||||
color[2] = uint8_t(std::min(std::max(f[2] * 255.f, 0.f), 255.f));
|
||||
color[3] = 0xff;
|
||||
return *this;
|
||||
}
|
||||
Color& operator=(uint8_t val) {
|
||||
color[0] = val;
|
||||
color[1] = val;
|
||||
color[2] = val;
|
||||
color[3] = val;
|
||||
return *this;
|
||||
}
|
||||
atVec4f toVec4f() const {
|
||||
atVec4f out;
|
||||
athena::simd_floats f;
|
||||
f[0] = color[0] / 255.f;
|
||||
f[1] = color[1] / 255.f;
|
||||
f[2] = color[2] / 255.f;
|
||||
f[3] = color[3] / 255.f;
|
||||
out.simd.copy_from(f);
|
||||
return out;
|
||||
}
|
||||
Color(const atVec4f& vec) { *this = vec; }
|
||||
Color(const atVec3f& vec) { *this = vec; }
|
||||
Color(uint8_t val) { *this = val; }
|
||||
bool operator==(const Color& other) const { return num == other.num; }
|
||||
bool operator!=(const Color& other) const { return num != other.num; }
|
||||
uint8_t operator[](size_t idx) const { return color[idx]; }
|
||||
uint8_t& operator[](size_t idx) { return color[idx]; }
|
||||
AT_DECL_EXPLICIT_DNA
|
||||
};
|
||||
|
||||
} // namespace GX
|
||||
@@ -1,426 +0,0 @@
|
||||
#include "DataSpec/DNACommon/MAPA.hpp"
|
||||
|
||||
#include "DataSpec/DNACommon/GX.hpp"
|
||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
||||
#include "DataSpec/DNAMP1/MAPA.hpp"
|
||||
#include "DataSpec/DNAMP2/MAPA.hpp"
|
||||
#include "DataSpec/DNAMP3/MAPA.hpp"
|
||||
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
#include <logvisor/logvisor.hpp>
|
||||
#include <zeus/CAABox.hpp>
|
||||
|
||||
namespace DataSpec::DNAMAPA {
|
||||
|
||||
static logvisor::Module Log("DNAMAPA");
|
||||
|
||||
template <>
|
||||
void MAPA::Enumerate<BigDNA::Read>(typename Read::StreamT& __dna_reader) {
|
||||
/* magic */
|
||||
magic = __dna_reader.readUint32Big();
|
||||
if (magic != 0xDEADD00D) {
|
||||
LogDNACommon.report(logvisor::Error, FMT_STRING("invalid MAPA magic"));
|
||||
return;
|
||||
}
|
||||
/* version */
|
||||
version = __dna_reader.readUint32Big();
|
||||
if (version == 2)
|
||||
header = std::make_unique<HeaderMP1>();
|
||||
else if (version == 3)
|
||||
header = std::make_unique<HeaderMP2>();
|
||||
else if (version == 5)
|
||||
header = std::make_unique<HeaderMP3>();
|
||||
else {
|
||||
LogDNACommon.report(logvisor::Error, FMT_STRING("invalid MAPA version"));
|
||||
return;
|
||||
}
|
||||
|
||||
header->read(__dna_reader);
|
||||
|
||||
for (atUint32 i = 0; i < header->mappableObjectCount(); i++) {
|
||||
std::unique_ptr<IMappableObject> mo = nullptr;
|
||||
if (version != 5) {
|
||||
mo = std::make_unique<MappableObjectMP1_2>();
|
||||
} else {
|
||||
mo = std::make_unique<MappableObjectMP3>();
|
||||
}
|
||||
mo->read(__dna_reader);
|
||||
mappableObjects.push_back(std::move(mo));
|
||||
}
|
||||
|
||||
/* vertices */
|
||||
__dna_reader.enumerateBig(vertices, header->vertexCount());
|
||||
/* surfaceHeaders */
|
||||
__dna_reader.enumerate(surfaceHeaders, header->surfaceCount());
|
||||
/* surfaces */
|
||||
__dna_reader.enumerate(surfaces, header->surfaceCount());
|
||||
}
|
||||
|
||||
template <>
|
||||
void MAPA::Enumerate<BigDNA::Write>(typename Write::StreamT& __dna_writer) {
|
||||
/* magic */
|
||||
__dna_writer.writeUint32Big(magic);
|
||||
/* version */
|
||||
__dna_writer.writeUint32Big(version);
|
||||
header->write(__dna_writer);
|
||||
|
||||
/* mappableObjects */
|
||||
for (const std::unique_ptr<IMappableObject>& mo : mappableObjects)
|
||||
mo->write(__dna_writer);
|
||||
/* vertices */
|
||||
__dna_writer.enumerateBig(vertices);
|
||||
/* surfaceHeaders */
|
||||
__dna_writer.enumerate(surfaceHeaders);
|
||||
/* surfaces */
|
||||
__dna_writer.enumerate(surfaces);
|
||||
}
|
||||
|
||||
template <>
|
||||
void MAPA::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
||||
header->binarySize(s);
|
||||
|
||||
for (const std::unique_ptr<IMappableObject>& mo : mappableObjects)
|
||||
mo->binarySize(s);
|
||||
|
||||
s += vertices.size() * 12;
|
||||
for (const SurfaceHeader& sh : surfaceHeaders)
|
||||
sh.binarySize(s);
|
||||
for (const Surface& su : surfaces)
|
||||
su.binarySize(s);
|
||||
s += 8;
|
||||
}
|
||||
|
||||
static const char* RetroMapVisModes[] = {"ALWAYS", "MAPSTATIONORVISIT", "VISIT", "NEVER"};
|
||||
|
||||
static const char* RetroMapObjVisModes[] = {"ALWAYS", "MAPSTATIONORVISIT", "VISIT", "NEVER", "MAPSTATIONORVISIT2"};
|
||||
|
||||
template <typename PAKRouter>
|
||||
bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force) {
|
||||
if (!force && outPath.isFile())
|
||||
return true;
|
||||
|
||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::MapArea))
|
||||
return false;
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
|
||||
os << "import bpy, bmesh\n"
|
||||
"from mathutils import Matrix\n"
|
||||
"\n"
|
||||
"bpy.types.Object.retro_mappable_type = bpy.props.IntProperty(name='Retro: MAPA object type', default=-1)\n"
|
||||
"bpy.types.Object.retro_mappable_sclyid = bpy.props.StringProperty(name='Retro: MAPA object SCLY ID')\n"
|
||||
"bpy.types.Scene.retro_map_vis_mode = bpy.props.EnumProperty(items=[('ALWAYS', 'Always', 'Always Visible', 0),"
|
||||
"('MAPSTATIONORVISIT', 'Map Station or Visit', 'Visible after Map Station or Visit', 1),"
|
||||
"('VISIT', 'Visit', 'Visible after Visit', 2),"
|
||||
"('NEVER', 'Never', 'Never Visible', 3)],"
|
||||
"name='Retro: Map Visibility Mode')\n"
|
||||
"bpy.types.Object.retro_mapobj_vis_mode = bpy.props.EnumProperty(items=[('ALWAYS', 'Always', 'Always Visible', "
|
||||
"0),"
|
||||
"('MAPSTATIONORVISIT', 'Map Station or Visit', 'Visible after Map Station or Visit', 1),"
|
||||
"('VISIT', 'Visit', 'Visible after Door Visit', 2),"
|
||||
"('NEVER', 'Never', 'Never Visible', 3),"
|
||||
"('MAPSTATIONORVISIT2', 'Map Station or Visit 2', 'Visible after Map Station or Visit', 4)],"
|
||||
"name='Retro: Map Object Visibility Mode')\n"
|
||||
"\n"
|
||||
"# Clear Scene\n"
|
||||
"if len(bpy.data.collections):\n"
|
||||
" bpy.data.collections.remove(bpy.data.collections[0])\n"
|
||||
"\n"
|
||||
"def add_triangle(bm, verts):\n"
|
||||
" verts = [bm.verts[vi] for vi in verts]\n"
|
||||
" face = bm.faces.get(verts)\n"
|
||||
" if face:\n"
|
||||
" face = face.copy()\n"
|
||||
" bm.verts.ensure_lookup_table()\n"
|
||||
" face.normal_flip()\n"
|
||||
" else:\n"
|
||||
" bm.faces.new(verts)\n"
|
||||
"\n"
|
||||
"def add_border(bm, verts):\n"
|
||||
" verts = [bm.verts[vi] for vi in verts]\n"
|
||||
" edge = bm.edges.get(verts)\n"
|
||||
" if not edge:\n"
|
||||
" edge = bm.edges.new(verts)\n"
|
||||
" edge.seam = True\n"
|
||||
"\n";
|
||||
|
||||
os.format(FMT_STRING("bpy.context.scene.name = 'MAPA_{}'\n"
|
||||
"bpy.context.scene.retro_map_vis_mode = '{}'\n"),
|
||||
entry.id, RetroMapVisModes[mapa.header->visMode()]);
|
||||
|
||||
/* Add empties representing MappableObjects */
|
||||
int moIdx = 0;
|
||||
for (const std::unique_ptr<MAPA::IMappableObject>& mo : mapa.mappableObjects) {
|
||||
if (mapa.version < 5) {
|
||||
const MAPA::MappableObjectMP1_2* moMP12 = static_cast<const MAPA::MappableObjectMP1_2*>(mo.get());
|
||||
zeus::simd_floats mtxF[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
moMP12->transformMtx[i].simd.copy_to(mtxF[i]);
|
||||
os.format(FMT_STRING("obj = bpy.data.objects.new('MAPOBJ_{:02d}', None)\n"
|
||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
||||
"obj.retro_mappable_type = {}\n"
|
||||
"obj.retro_mapobj_vis_mode = '{}'\n"
|
||||
"obj.retro_mappable_sclyid = '0x{:08X}'\n"
|
||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
||||
"mtxd = mtx.decompose()\n"
|
||||
"obj.rotation_mode = 'QUATERNION'\n"
|
||||
"obj.location = mtxd[0]\n"
|
||||
"obj.rotation_quaternion = mtxd[1]\n"
|
||||
"obj.scale = mtxd[2]\n"),
|
||||
moIdx, int(moMP12->type), RetroMapObjVisModes[moMP12->visMode], moMP12->sclyId, mtxF[0][0], mtxF[0][1],
|
||||
mtxF[0][2], mtxF[0][3], mtxF[1][0], mtxF[1][1], mtxF[1][2], mtxF[1][3], mtxF[2][0], mtxF[2][1],
|
||||
mtxF[2][2], mtxF[2][3]);
|
||||
++moIdx;
|
||||
continue;
|
||||
} else {
|
||||
const MAPA::MappableObjectMP3* moMP3 = static_cast<const MAPA::MappableObjectMP3*>(mo.get());
|
||||
zeus::simd_floats mtxF[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
moMP3->transformMtx[i].simd.copy_to(mtxF[i]);
|
||||
os.format(FMT_STRING("obj = bpy.data.objects.new('MAPOBJ_{:02d}', None)\n"
|
||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
||||
"obj.retro_mappable_type = {}\n"
|
||||
"obj.retro_mapobj_vis_mode = '{}'\n"
|
||||
"obj.retro_mappable_sclyid = '0x{:08X}'\n"
|
||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
||||
"mtxd = mtx.decompose()\n"
|
||||
"obj.rotation_mode = 'QUATERNION'\n"
|
||||
"obj.location = mtxd[0]\n"
|
||||
"obj.rotation_quaternion = mtxd[1]\n"
|
||||
"obj.scale = mtxd[2]\n"),
|
||||
moIdx, int(moMP3->type), RetroMapObjVisModes[moMP3->visMode], moMP3->sclyId, mtxF[0][0], mtxF[0][1],
|
||||
mtxF[0][2], mtxF[0][3], mtxF[1][0], mtxF[1][1], mtxF[1][2], mtxF[1][3], mtxF[2][0], mtxF[2][1],
|
||||
mtxF[2][2], mtxF[2][3]);
|
||||
++moIdx;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
os << "# Begin bmesh\n"
|
||||
"bm = bmesh.new()\n"
|
||||
"\n";
|
||||
|
||||
/* Read in verts */
|
||||
for (const atVec3f& vert : mapa.vertices) {
|
||||
zeus::simd_floats f(vert.simd);
|
||||
os.format(FMT_STRING("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
|
||||
}
|
||||
os << "bm.verts.ensure_lookup_table()\n";
|
||||
|
||||
/* Read in surfaces */
|
||||
for (const typename MAPA::Surface& surf : mapa.surfaces) {
|
||||
for (const typename MAPA::Surface::Primitive& prim : surf.primitives) {
|
||||
auto iit = prim.indices.cbegin();
|
||||
|
||||
/* 3 Prim Verts to start */
|
||||
int c = 0;
|
||||
unsigned int primVerts[3] = {*iit++, *iit++, *iit++};
|
||||
|
||||
if (GX::Primitive(prim.type) == GX::TRIANGLESTRIP) {
|
||||
atUint8 flip = 0;
|
||||
for (size_t v = 0; v < prim.indexCount - 2; ++v) {
|
||||
if (flip) {
|
||||
os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[c % 3], primVerts[(c + 2) % 3],
|
||||
primVerts[(c + 1) % 3]);
|
||||
} else {
|
||||
os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[c % 3], primVerts[(c + 1) % 3],
|
||||
primVerts[(c + 2) % 3]);
|
||||
}
|
||||
flip ^= 1;
|
||||
|
||||
/* Break if done */
|
||||
if (iit == prim.indices.cend())
|
||||
break;
|
||||
|
||||
bool peek = (v >= prim.indexCount - 3);
|
||||
|
||||
/* Advance one prim vert */
|
||||
if (peek)
|
||||
primVerts[c % 3] = *iit;
|
||||
else
|
||||
primVerts[c % 3] = *iit++;
|
||||
++c;
|
||||
}
|
||||
} else if (GX::Primitive(prim.type) == GX::TRIANGLES) {
|
||||
for (size_t v = 0; v < prim.indexCount; v += 3) {
|
||||
os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[0], primVerts[1], primVerts[2]);
|
||||
|
||||
/* Break if done */
|
||||
if (v + 3 >= prim.indexCount)
|
||||
break;
|
||||
|
||||
/* Advance 3 Prim Verts */
|
||||
for (int pv = 0; pv < 3; ++pv)
|
||||
primVerts[pv] = *iit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const typename MAPA::Surface::Border& border : surf.borders) {
|
||||
auto iit = border.indices.cbegin();
|
||||
for (size_t i = 0; i < border.indexCount - 1; ++i) {
|
||||
os.format(FMT_STRING("add_border(bm, ({},{}))\n"), *iit, *(iit + 1));
|
||||
++iit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os << "mesh = bpy.data.meshes.new('MAP')\n"
|
||||
"obj = bpy.data.objects.new(mesh.name, mesh)\n"
|
||||
"bm.to_mesh(mesh)\n"
|
||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
||||
"bm.free()\n";
|
||||
|
||||
const zeus::CMatrix4f* tmpMtx = pakRouter.lookupMAPATransform(entry.id);
|
||||
const zeus::CMatrix4f& mtx = tmpMtx ? *tmpMtx : zeus::skIdentityMatrix4f;
|
||||
os.format(FMT_STRING("mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
||||
"mtxd = mtx.decompose()\n"
|
||||
"obj.rotation_mode = 'QUATERNION'\n"
|
||||
"obj.location = mtxd[0]\n"
|
||||
"obj.rotation_quaternion = mtxd[1]\n"
|
||||
"obj.scale = mtxd[2]\n"),
|
||||
mtx[0][0], mtx[1][0], mtx[2][0], mtx[3][0], mtx[0][1], mtx[1][1], mtx[2][1], mtx[3][1], mtx[0][2],
|
||||
mtx[1][2], mtx[2][2], mtx[3][2]);
|
||||
|
||||
/* World background */
|
||||
hecl::ProjectPath worldDir = outPath.getParentPath().getParentPath();
|
||||
for (const auto& ent : hecl::DirectoryEnumerator(worldDir.getAbsolutePath())) {
|
||||
if (hecl::StringUtils::BeginsWith(ent.m_name, "!world") &&
|
||||
hecl::StringUtils::EndsWith(ent.m_name, ".blend")) {
|
||||
os.linkBackground(fmt::format(FMT_STRING("//../{}"), ent.m_name), "World"sv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
os.centerView();
|
||||
os.close();
|
||||
conn.saveBlend();
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool ReadMAPAToBlender<PAKRouter<DNAMP1::PAKBridge>>(hecl::blender::Connection& conn, const MAPA& mapa,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter,
|
||||
const PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
|
||||
bool force);
|
||||
|
||||
template bool ReadMAPAToBlender<PAKRouter<DNAMP2::PAKBridge>>(hecl::blender::Connection& conn, const MAPA& mapa,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter,
|
||||
const PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
|
||||
bool force);
|
||||
|
||||
template bool ReadMAPAToBlender<PAKRouter<DNAMP3::PAKBridge>>(hecl::blender::Connection& conn, const MAPA& mapa,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP3::PAKBridge>& pakRouter,
|
||||
const PAKRouter<DNAMP3::PAKBridge>::EntryType& entry,
|
||||
bool force);
|
||||
|
||||
template <typename MAPAType>
|
||||
bool Cook(const hecl::blender::MapArea& mapaIn, const hecl::ProjectPath& out) {
|
||||
if (mapaIn.verts.size() >= 256) {
|
||||
Log.report(logvisor::Error, FMT_STRING("MAPA {} vertex range exceeded [{}/{}]"), out.getRelativePath(),
|
||||
mapaIn.verts.size(), 255);
|
||||
return false;
|
||||
}
|
||||
|
||||
MAPAType mapa;
|
||||
mapa.magic = 0xDEADD00D;
|
||||
mapa.version = MAPAType::Version();
|
||||
|
||||
zeus::CAABox aabb;
|
||||
for (const hecl::blender::Vector3f& vert : mapaIn.verts)
|
||||
aabb.accumulateBounds(vert.val);
|
||||
|
||||
mapa.header = std::make_unique<typename MAPAType::Header>();
|
||||
typename MAPAType::Header& header = static_cast<typename MAPAType::Header&>(*mapa.header);
|
||||
header.unknown1 = 0;
|
||||
header.mapVisMode = mapaIn.visType;
|
||||
header.boundingBox[0] = aabb.min;
|
||||
header.boundingBox[1] = aabb.max;
|
||||
header.moCount = mapaIn.pois.size();
|
||||
header.vtxCount = mapaIn.verts.size();
|
||||
header.surfCount = mapaIn.surfaces.size();
|
||||
|
||||
mapa.mappableObjects.reserve(mapaIn.pois.size());
|
||||
for (const hecl::blender::MapArea::POI& poi : mapaIn.pois) {
|
||||
mapa.mappableObjects.push_back(std::make_unique<typename MAPAType::MappableObject>());
|
||||
typename MAPAType::MappableObject& mobj =
|
||||
static_cast<typename MAPAType::MappableObject&>(*mapa.mappableObjects.back());
|
||||
mobj.type = MAPA::IMappableObject::Type(poi.type);
|
||||
mobj.visMode = poi.visMode;
|
||||
mobj.sclyId = poi.objid;
|
||||
mobj.transformMtx[0] = poi.xf.val[0];
|
||||
mobj.transformMtx[1] = poi.xf.val[1];
|
||||
mobj.transformMtx[2] = poi.xf.val[2];
|
||||
}
|
||||
|
||||
mapa.vertices.reserve(mapaIn.verts.size());
|
||||
for (const hecl::blender::Vector3f& vert : mapaIn.verts)
|
||||
mapa.vertices.push_back(vert.val);
|
||||
|
||||
size_t offsetCur = 0;
|
||||
for (const auto& mo : mapa.mappableObjects)
|
||||
mo->binarySize(offsetCur);
|
||||
offsetCur += mapa.vertices.size() * 12;
|
||||
offsetCur += mapaIn.surfaces.size() * 32;
|
||||
|
||||
mapa.surfaceHeaders.reserve(mapaIn.surfaces.size());
|
||||
mapa.surfaces.reserve(mapaIn.surfaces.size());
|
||||
for (const hecl::blender::MapArea::Surface& surfIn : mapaIn.surfaces) {
|
||||
mapa.surfaceHeaders.emplace_back();
|
||||
DNAMAPA::MAPA::SurfaceHeader& surfHead = mapa.surfaceHeaders.back();
|
||||
mapa.surfaces.emplace_back();
|
||||
DNAMAPA::MAPA::Surface& surf = mapa.surfaces.back();
|
||||
|
||||
surf.primitiveCount = 1;
|
||||
surf.primitives.emplace_back();
|
||||
DNAMAPA::MAPA::Surface::Primitive& prim = surf.primitives.back();
|
||||
prim.type = GX::TRIANGLESTRIP;
|
||||
prim.indexCount = surfIn.count;
|
||||
prim.indices.reserve(surfIn.count);
|
||||
auto itBegin = mapaIn.indices.begin() + surfIn.start;
|
||||
auto itEnd = itBegin + surfIn.count;
|
||||
for (auto it = itBegin; it != itEnd; ++it)
|
||||
prim.indices.push_back(*it);
|
||||
|
||||
surf.borderCount = surfIn.borders.size();
|
||||
surf.borders.reserve(surfIn.borders.size());
|
||||
for (const auto& borderIn : surfIn.borders) {
|
||||
surf.borders.emplace_back();
|
||||
DNAMAPA::MAPA::Surface::Border& border = surf.borders.back();
|
||||
border.indexCount = borderIn.second;
|
||||
border.indices.reserve(borderIn.second);
|
||||
auto it2Begin = mapaIn.indices.begin() + borderIn.first;
|
||||
auto it2End = it2Begin + borderIn.second;
|
||||
for (auto it = it2Begin; it != it2End; ++it)
|
||||
border.indices.push_back(*it);
|
||||
}
|
||||
|
||||
surfHead.normal = surfIn.normal.val;
|
||||
surfHead.centroid = surfIn.centerOfMass;
|
||||
surfHead.polyOff = offsetCur;
|
||||
offsetCur += 4;
|
||||
prim.binarySize(offsetCur);
|
||||
surfHead.edgeOff = offsetCur;
|
||||
offsetCur += 4;
|
||||
for (const auto& border : surf.borders)
|
||||
border.binarySize(offsetCur);
|
||||
}
|
||||
|
||||
athena::io::FileWriter f(out.getAbsolutePath());
|
||||
mapa.write(f);
|
||||
int64_t rem = f.position() % 32;
|
||||
if (rem)
|
||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
||||
f.writeBytes((atInt8*)"\xff", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool Cook<DNAMP1::MAPA>(const hecl::blender::MapArea& mapa, const hecl::ProjectPath& out);
|
||||
template bool Cook<DNAMP2::MAPA>(const hecl::blender::MapArea& mapa, const hecl::ProjectPath& out);
|
||||
template bool Cook<DNAMP3::MAPA>(const hecl::blender::MapArea& mapa, const hecl::ProjectPath& out);
|
||||
|
||||
} // namespace DataSpec::DNAMAPA
|
||||
@@ -1,177 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace hecl::blender {
|
||||
class Connection;
|
||||
struct MapArea;
|
||||
} // namespace hecl::blender
|
||||
|
||||
namespace DataSpec::DNAMAPA {
|
||||
struct MAPA : BigDNA {
|
||||
AT_DECL_EXPLICIT_DNA
|
||||
Value<atUint32> magic;
|
||||
Value<atUint32> version;
|
||||
struct IMAPAHeader : BigDNAV {
|
||||
Delete _d;
|
||||
virtual atUint32 visMode() const = 0;
|
||||
virtual atUint32 mappableObjectCount() const = 0;
|
||||
virtual atUint32 vertexCount() const = 0;
|
||||
virtual atUint32 surfaceCount() const = 0;
|
||||
};
|
||||
|
||||
struct HeaderMP1 : IMAPAHeader {
|
||||
AT_DECL_DNAV
|
||||
Value<atUint32> unknown1 = 0;
|
||||
Value<atUint32> mapVisMode = 0;
|
||||
Value<atVec3f> boundingBox[2] = {};
|
||||
Value<atUint32> moCount = 0;
|
||||
Value<atUint32> vtxCount = 0;
|
||||
Value<atUint32> surfCount = 0;
|
||||
atUint32 visMode() const override { return mapVisMode; }
|
||||
atUint32 mappableObjectCount() const override { return moCount; }
|
||||
atUint32 vertexCount() const override { return vtxCount; }
|
||||
atUint32 surfaceCount() const override { return surfCount; }
|
||||
};
|
||||
|
||||
struct HeaderMP2 : IMAPAHeader {
|
||||
AT_DECL_DNAV
|
||||
Value<atUint32> unknown1 = 0;
|
||||
Value<atUint32> mapVisMode = 0;
|
||||
Value<atVec3f> boundingBox[2] = {};
|
||||
Value<atUint32> unknown3 = 0;
|
||||
Value<atUint32> unknown4 = 0;
|
||||
Value<atUint32> unknown5 = 0;
|
||||
Value<atUint32> moCount = 0;
|
||||
Value<atUint32> vtxCount = 0;
|
||||
Value<atUint32> surfCount = 0;
|
||||
atUint32 visMode() const override { return mapVisMode; }
|
||||
atUint32 mappableObjectCount() const override { return moCount; }
|
||||
atUint32 vertexCount() const override { return vtxCount; }
|
||||
atUint32 surfaceCount() const override { return surfCount; }
|
||||
};
|
||||
|
||||
struct HeaderMP3 : IMAPAHeader {
|
||||
AT_DECL_DNAV
|
||||
Value<atUint32> unknown1 = 0;
|
||||
Value<atUint32> mapVisMode = 0;
|
||||
Value<atVec3f> boundingBox[2] = {};
|
||||
Value<atUint32> unknown3 = 0;
|
||||
Value<atUint32> unknown4 = 0;
|
||||
Value<atUint32> unknown5 = 0;
|
||||
Value<atUint32> unknown6 = 0;
|
||||
Value<atUint32> moCount = 0;
|
||||
Value<atUint32> vtxCount = 0;
|
||||
Value<atUint32> surfCount = 0;
|
||||
Value<atUint32> internalNameLength = 0;
|
||||
Value<atUint32> unknown7 = 0;
|
||||
String<AT_DNA_COUNT(internalNameLength)> internalName;
|
||||
atUint32 visMode() const override { return mapVisMode; }
|
||||
atUint32 mappableObjectCount() const override { return moCount; }
|
||||
atUint32 vertexCount() const override { return vtxCount; }
|
||||
atUint32 surfaceCount() const override { return surfCount; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IMAPAHeader> header;
|
||||
|
||||
struct IMappableObject : BigDNAV {
|
||||
Delete _d;
|
||||
enum class Type : atUint32 {
|
||||
BlueDoor = 0,
|
||||
ShieldDoor = 1,
|
||||
IceDoor = 2,
|
||||
WaveDoor = 3,
|
||||
PlasmaDoor = 4,
|
||||
BigDoor1 = 5,
|
||||
BigDoor2 = 6,
|
||||
IceDoorCeiling = 7,
|
||||
IceDoorFloor = 8,
|
||||
WaveDoorCeiling = 9,
|
||||
WaveDoorFloor = 10,
|
||||
IceDoorFloor2 = 13,
|
||||
WaveDoorFloor2 = 14,
|
||||
DownArrowYellow = 27, /* Maintenance Tunnel */
|
||||
UpArrowYellow = 28, /* Phazon Processing Center */
|
||||
DownArrowGreen = 29, /* Elevator A */
|
||||
UpArrowGreen = 30, /* Elite Control Access */
|
||||
DownArrowRed = 31, /* Elevator B */
|
||||
UpArrowRed = 32, /* Fungal Hall Access */
|
||||
TransportLift = 33,
|
||||
SaveStation = 34,
|
||||
MissileStation = 37
|
||||
};
|
||||
};
|
||||
|
||||
struct MappableObjectMP1_2 : IMappableObject {
|
||||
AT_DECL_DNAV
|
||||
Value<Type> type;
|
||||
Value<atUint32> visMode;
|
||||
Value<atUint32> sclyId;
|
||||
Value<atInt32> seek1 = -1;
|
||||
Value<atVec4f> transformMtx[3];
|
||||
Value<atInt32> seek2[4] = {-1, -1, -1, -1};
|
||||
};
|
||||
|
||||
struct MappableObjectMP3 : IMappableObject {
|
||||
AT_DECL_DNAV
|
||||
Value<Type> type;
|
||||
Value<atUint32> visMode;
|
||||
Value<atUint32> sclyId;
|
||||
Buffer<AT_DNA_COUNT(0x10)> unknownHash;
|
||||
Value<atInt32> seek1 = -1;
|
||||
Value<atVec4f> transformMtx[3];
|
||||
Value<atInt32> seek2[4] = {-1, -1, -1, -1};
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<IMappableObject>> mappableObjects;
|
||||
Vector<atVec3f, AT_DNA_COUNT(header->vertexCount())> vertices;
|
||||
|
||||
struct SurfaceHeader : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atVec3f> normal;
|
||||
Value<atVec3f> centroid;
|
||||
Value<atUint32> polyOff;
|
||||
Value<atUint32> edgeOff;
|
||||
};
|
||||
|
||||
Vector<SurfaceHeader, AT_DNA_COUNT(header->surfaceCount())> surfaceHeaders;
|
||||
|
||||
struct Surface : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atUint32> primitiveCount;
|
||||
struct Primitive : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atUint32> type;
|
||||
Value<atUint32> indexCount;
|
||||
Vector<atUint8, AT_DNA_COUNT(indexCount)> indices;
|
||||
Align<4> align;
|
||||
};
|
||||
Vector<Primitive, AT_DNA_COUNT(primitiveCount)> primitives;
|
||||
Value<atUint32> borderCount;
|
||||
struct Border : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atUint32> indexCount;
|
||||
Vector<atUint8, AT_DNA_COUNT(indexCount)> indices;
|
||||
Align<4> align;
|
||||
};
|
||||
Vector<Border, AT_DNA_COUNT(borderCount)> borders;
|
||||
};
|
||||
|
||||
Vector<Surface, AT_DNA_COUNT(header->surfaceCount())> surfaces;
|
||||
};
|
||||
|
||||
template <typename PAKRouter>
|
||||
bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force);
|
||||
|
||||
template <typename MAPAType>
|
||||
bool Cook(const hecl::blender::MapArea& mapa, const hecl::ProjectPath& out);
|
||||
|
||||
} // namespace DataSpec::DNAMAPA
|
||||
@@ -1,146 +0,0 @@
|
||||
#include "DataSpec/DNACommon/MAPU.hpp"
|
||||
|
||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
||||
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
#include <zeus/Global.hpp>
|
||||
|
||||
namespace DataSpec::DNAMAPU {
|
||||
|
||||
template <typename PAKRouter>
|
||||
bool ReadMAPUToBlender(hecl::blender::Connection& conn, const MAPU& mapu, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force) {
|
||||
if (!force && outPath.isFile())
|
||||
return true;
|
||||
|
||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::MapUniverse))
|
||||
return false;
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
|
||||
os << "import bpy\n"
|
||||
"from mathutils import Matrix\n"
|
||||
"\n"
|
||||
"# Clear Scene\n"
|
||||
"if len(bpy.data.collections):\n"
|
||||
" bpy.data.collections.remove(bpy.data.collections[0])\n"
|
||||
"\n"
|
||||
"bpy.types.Object.retro_mapworld_color = bpy.props.FloatVectorProperty(name='Retro: MapWorld Color',"
|
||||
" description='Sets map world color', subtype='COLOR', size=4, min=0.0, max=1.0)\n"
|
||||
"bpy.types.Object.retro_mapworld_path = bpy.props.StringProperty(name='Retro: MapWorld Path',"
|
||||
" description='Sets path to World root')\n"
|
||||
"\n";
|
||||
|
||||
hecl::ProjectPath hexPath = pakRouter.getWorking(mapu.hexMapa);
|
||||
os.linkMesh(hexPath.getAbsolutePath(), "MAP");
|
||||
os << "hexMesh = bpy.data.objects['MAP'].data\n";
|
||||
|
||||
for (const MAPU::World& wld : mapu.worlds) {
|
||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(wld.mlvl);
|
||||
const MAPU::Transform& wldXf = wld.transform;
|
||||
zeus::simd_floats wldXfF[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
wldXf.xf[i].simd.copy_to(wldXfF[i]);
|
||||
zeus::simd_floats hexColorF(wld.hexColor.mSimd);
|
||||
os.format(FMT_STRING("wldObj = bpy.data.objects.new('{}', None)\n"
|
||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
||||
"mtxd = mtx.decompose()\n"
|
||||
"wldObj.rotation_mode = 'QUATERNION'\n"
|
||||
"wldObj.location = mtxd[0]\n"
|
||||
"wldObj.rotation_quaternion = mtxd[1]\n"
|
||||
"wldObj.scale = mtxd[2]\n"
|
||||
"wldObj.retro_mapworld_color = ({}, {}, {}, {})\n"
|
||||
"wldObj.retro_mapworld_path = '''{}'''\n"
|
||||
"bpy.context.scene.collection.objects.link(wldObj)\n"),
|
||||
wld.name, wldXfF[0][0], wldXfF[0][1], wldXfF[0][2], wldXfF[0][3], wldXfF[1][0], wldXfF[1][1],
|
||||
wldXfF[1][2], wldXfF[1][3], wldXfF[2][0], wldXfF[2][1], wldXfF[2][2], wldXfF[2][3], hexColorF[0],
|
||||
hexColorF[1], hexColorF[2], hexColorF[3], path.getParentPath().getRelativePath());
|
||||
int idx = 0;
|
||||
for (const MAPU::Transform& hexXf : wld.hexTransforms) {
|
||||
zeus::simd_floats hexXfF[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
hexXf.xf[i].simd.copy_to(hexXfF[i]);
|
||||
os.format(FMT_STRING("obj = bpy.data.objects.new('{}_{}', hexMesh)\n"
|
||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
||||
"mtxd = mtx.decompose()\n"
|
||||
"obj.rotation_mode = 'QUATERNION'\n"
|
||||
"obj.location = mtxd[0]\n"
|
||||
"obj.rotation_quaternion = mtxd[1]\n"
|
||||
"obj.scale = mtxd[2]\n"
|
||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
||||
"obj.parent = wldObj\n"),
|
||||
wld.name, idx++, hexXfF[0][0], hexXfF[0][1], hexXfF[0][2], hexXfF[0][3], hexXfF[1][0], hexXfF[1][1],
|
||||
hexXfF[1][2], hexXfF[1][3], hexXfF[2][0], hexXfF[2][1], hexXfF[2][2], hexXfF[2][3]);
|
||||
}
|
||||
}
|
||||
|
||||
os << "for screen in bpy.data.screens:\n"
|
||||
" for area in screen.areas:\n"
|
||||
" for space in area.spaces:\n"
|
||||
" if space.type == 'VIEW_3D':\n"
|
||||
" space.clip_end = 8000.0\n";
|
||||
|
||||
os.centerView();
|
||||
os.close();
|
||||
conn.saveBlend();
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool ReadMAPUToBlender<PAKRouter<DNAMP1::PAKBridge>>(hecl::blender::Connection& conn, const MAPU& mapu,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter,
|
||||
const PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
|
||||
bool force);
|
||||
|
||||
template bool ReadMAPUToBlender<PAKRouter<DNAMP2::PAKBridge>>(hecl::blender::Connection& conn, const MAPU& mapu,
|
||||
const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter,
|
||||
const PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
|
||||
bool force);
|
||||
|
||||
bool MAPU::Cook(const hecl::blender::MapUniverse& mapuIn, const hecl::ProjectPath& out) {
|
||||
MAPU mapu;
|
||||
|
||||
mapu.magic = 0xABCDEF01;
|
||||
mapu.version = 1;
|
||||
mapu.hexMapa = mapuIn.hexagonPath;
|
||||
|
||||
mapu.worldCount = mapuIn.worlds.size();
|
||||
mapu.worlds.reserve(mapuIn.worlds.size());
|
||||
for (const hecl::blender::MapUniverse::World& wld : mapuIn.worlds) {
|
||||
mapu.worlds.emplace_back();
|
||||
MAPU::World& wldOut = mapu.worlds.back();
|
||||
wldOut.name = wld.name;
|
||||
for (const auto& ent : wld.worldPath.enumerateDir()) {
|
||||
if (hecl::StringUtils::BeginsWith(ent.m_name, "!world") &&
|
||||
hecl::StringUtils::EndsWith(ent.m_name, ".blend")) {
|
||||
wldOut.mlvl = hecl::ProjectPath(wld.worldPath, ent.m_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
wldOut.transform.xf[0] = wld.xf.val[0];
|
||||
wldOut.transform.xf[1] = wld.xf.val[1];
|
||||
wldOut.transform.xf[2] = wld.xf.val[2];
|
||||
wldOut.hexCount = wld.hexagons.size();
|
||||
wldOut.hexTransforms.reserve(wld.hexagons.size());
|
||||
for (const hecl::blender::Matrix4f& mtx : wld.hexagons) {
|
||||
wldOut.hexTransforms.emplace_back();
|
||||
MAPU::Transform& xf = wldOut.hexTransforms.back();
|
||||
xf.xf[0] = mtx.val[0];
|
||||
xf.xf[1] = mtx.val[1];
|
||||
xf.xf[2] = mtx.val[2];
|
||||
}
|
||||
wldOut.hexColor = zeus::CColor(wld.color.val);
|
||||
}
|
||||
|
||||
athena::io::FileWriter f(out.getAbsolutePath());
|
||||
mapu.write(f);
|
||||
int64_t rem = f.position() % 32;
|
||||
if (rem)
|
||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
||||
f.writeBytes((atInt8*)"\xff", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace DataSpec::DNAMAPU
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace hecl::blender {
|
||||
class Connection;
|
||||
struct MapUniverse;
|
||||
} // namespace hecl::blender
|
||||
|
||||
namespace DataSpec::DNAMAPU {
|
||||
struct MAPU : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<uint32_t> magic;
|
||||
Value<uint32_t> version;
|
||||
UniqueID32 hexMapa;
|
||||
Value<uint32_t> worldCount;
|
||||
struct Transform : BigDNA {
|
||||
AT_DECL_DNA
|
||||
Value<atVec4f> xf[3];
|
||||
};
|
||||
struct World : BigDNA {
|
||||
AT_DECL_DNA
|
||||
String<-1> name;
|
||||
UniqueID32 mlvl;
|
||||
Transform transform;
|
||||
Value<uint32_t> hexCount;
|
||||
Vector<Transform, AT_DNA_COUNT(hexCount)> hexTransforms;
|
||||
DNAColor hexColor;
|
||||
};
|
||||
Vector<World, AT_DNA_COUNT(worldCount)> worlds;
|
||||
|
||||
static bool Cook(const hecl::blender::MapUniverse& mapu, const hecl::ProjectPath& out);
|
||||
};
|
||||
|
||||
template <typename PAKRouter>
|
||||
bool ReadMAPUToBlender(hecl::blender::Connection& conn, const MAPU& mapu, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force);
|
||||
|
||||
} // namespace DataSpec::DNAMAPU
|
||||
@@ -1,109 +0,0 @@
|
||||
#include "DataSpec/DNACommon/MLVL.hpp"
|
||||
|
||||
#include "DataSpec/DNAMP1/MLVL.hpp"
|
||||
#include "DataSpec/DNAMP2/MLVL.hpp"
|
||||
#include "DataSpec/DNAMP3/MLVL.hpp"
|
||||
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
#include <zeus/Global.hpp>
|
||||
|
||||
namespace DataSpec::DNAMLVL {
|
||||
|
||||
template <class PAKRouter, typename MLVL>
|
||||
bool ReadMLVLToBlender(hecl::blender::Connection& conn, const MLVL& mlvl, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force,
|
||||
std::function<void(const char*)> fileChanged) {
|
||||
hecl::ProjectPath blendPath = outPath.getWithExtension(".blend", true);
|
||||
if (!force && blendPath.isFile())
|
||||
return true;
|
||||
|
||||
/* Create World Blend */
|
||||
if (!conn.createBlend(blendPath, hecl::blender::BlendType::World))
|
||||
return false;
|
||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
||||
os << "import bpy\n"
|
||||
"import bmesh\n"
|
||||
"from mathutils import Matrix\n"
|
||||
"\n"
|
||||
"bpy.context.scene.name = 'World'\n"
|
||||
"\n"
|
||||
"# Clear Scene\n"
|
||||
"if len(bpy.data.collections):\n"
|
||||
" bpy.data.collections.remove(bpy.data.collections[0])\n";
|
||||
|
||||
/* Insert area empties */
|
||||
int areaIdx = 0;
|
||||
for (const auto& area : mlvl.areas) {
|
||||
const typename PAKRouter::EntryType* mreaEntry = pakRouter.lookupEntry(area.areaMREAId);
|
||||
|
||||
os.AABBToBMesh(area.aabb[0], area.aabb[1]);
|
||||
zeus::simd_floats xfMtxF[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
area.transformMtx[i].simd.copy_to(xfMtxF[i]);
|
||||
os.format(FMT_STRING("box_mesh = bpy.data.meshes.new('''{}''')\n"
|
||||
"bm.to_mesh(box_mesh)\n"
|
||||
"bm.free()\n"
|
||||
"box = bpy.data.objects.new(box_mesh.name, box_mesh)\n"
|
||||
"bpy.context.scene.collection.objects.link(box)\n"
|
||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
||||
"mtxd = mtx.decompose()\n"
|
||||
"box.rotation_mode = 'QUATERNION'\n"
|
||||
"box.location = mtxd[0]\n"
|
||||
"box.rotation_quaternion = mtxd[1]\n"
|
||||
"box.scale = mtxd[2]\n"),
|
||||
*mreaEntry->unique.m_areaName, xfMtxF[0][0], xfMtxF[0][1], xfMtxF[0][2], xfMtxF[0][3], xfMtxF[1][0], xfMtxF[1][1],
|
||||
xfMtxF[1][2], xfMtxF[1][3], xfMtxF[2][0], xfMtxF[2][1], xfMtxF[2][2], xfMtxF[2][3]);
|
||||
|
||||
/* Insert dock planes */
|
||||
int dockIdx = 0;
|
||||
for (const auto& dock : area.docks) {
|
||||
os << "bm = bmesh.new()\n";
|
||||
zeus::CVector3f pvAvg;
|
||||
for (const atVec3f& pv : dock.planeVerts)
|
||||
pvAvg += pv;
|
||||
pvAvg /= zeus::CVector3f(dock.planeVerts.size());
|
||||
int idx = 0;
|
||||
for (const atVec3f& pv : dock.planeVerts) {
|
||||
const zeus::CVector3f pvRel = zeus::CVector3f(pv) - pvAvg;
|
||||
os.format(FMT_STRING("bm.verts.new(({},{},{}))\n"
|
||||
"bm.verts.ensure_lookup_table()\n"),
|
||||
pvRel[0], pvRel[1], pvRel[2]);
|
||||
if (idx)
|
||||
os << "bm.edges.new((bm.verts[-2], bm.verts[-1]))\n";
|
||||
++idx;
|
||||
}
|
||||
os << "bm.edges.new((bm.verts[-1], bm.verts[0]))\n";
|
||||
os.format(FMT_STRING("dockMesh = bpy.data.meshes.new('DOCK_{:02d}_{:02d}')\n"), areaIdx, dockIdx);
|
||||
os << "dockObj = bpy.data.objects.new(dockMesh.name, dockMesh)\n"
|
||||
"bpy.context.scene.collection.objects.link(dockObj)\n"
|
||||
"bm.to_mesh(dockMesh)\n"
|
||||
"bm.free()\n"
|
||||
"dockObj.parent = box\n";
|
||||
os.format(FMT_STRING("dockObj.location = ({},{},{})\n"), float(pvAvg[0]), float(pvAvg[1]), float(pvAvg[2]));
|
||||
++dockIdx;
|
||||
}
|
||||
++areaIdx;
|
||||
}
|
||||
|
||||
os.centerView();
|
||||
os.close();
|
||||
conn.saveBlend();
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool ReadMLVLToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::MLVL>(
|
||||
hecl::blender::Connection& conn, const DNAMP1::MLVL& mlvl, const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter, const PAKRouter<DNAMP1::PAKBridge>::EntryType& entry, bool force,
|
||||
std::function<void(const char*)> fileChanged);
|
||||
|
||||
template bool ReadMLVLToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::MLVL>(
|
||||
hecl::blender::Connection& conn, const DNAMP2::MLVL& mlvl, const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter, const PAKRouter<DNAMP2::PAKBridge>::EntryType& entry, bool force,
|
||||
std::function<void(const char*)> fileChanged);
|
||||
|
||||
template bool ReadMLVLToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::MLVL>(
|
||||
hecl::blender::Connection& conn, const DNAMP3::MLVL& mlvl, const hecl::ProjectPath& outPath,
|
||||
PAKRouter<DNAMP3::PAKBridge>& pakRouter, const PAKRouter<DNAMP3::PAKBridge>::EntryType& entry, bool force,
|
||||
std::function<void(const char*)> fileChanged);
|
||||
|
||||
} // namespace DataSpec::DNAMLVL
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace hecl {
|
||||
class ProjectPath;
|
||||
}
|
||||
|
||||
namespace hecl::blender {
|
||||
class Connection;
|
||||
}
|
||||
|
||||
namespace DataSpec::DNAMLVL {
|
||||
|
||||
template <class PAKRouter, typename MLVL>
|
||||
bool ReadMLVLToBlender(hecl::blender::Connection& conn, const MLVL& mlvl, const hecl::ProjectPath& outPath,
|
||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force,
|
||||
std::function<void(const char*)> fileChanged);
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace DataSpec {
|
||||
struct MayaSpline : public BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
Value<atUint8> preInf;
|
||||
Value<atUint8> postInf;
|
||||
Value<atUint32> knotCount;
|
||||
struct Knot : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
Value<float> time;
|
||||
Value<float> amplitude;
|
||||
Value<atUint8> unk1;
|
||||
Value<atUint8> unk2;
|
||||
Vector<atVec2f, AT_DNA_COUNT(unk1 == 5)> unk1Floats;
|
||||
Vector<atVec2f, AT_DNA_COUNT(unk2 == 5)> unk2Floats;
|
||||
};
|
||||
|
||||
Vector<Knot, AT_DNA_COUNT(knotCount)> knots;
|
||||
Value<atUint8> clampMode;
|
||||
Value<float> minAmp;
|
||||
Value<float> maxAmp;
|
||||
};
|
||||
} // namespace DataSpec
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
||||
|
||||
namespace DataSpec {
|
||||
enum class ERegion { Invalid = -1, NTSC_U = 'E', PAL = 'P', NTSC_J = 'J' };
|
||||
enum class EGame {
|
||||
Invalid = 0,
|
||||
MetroidPrime1,
|
||||
MetroidPrime2,
|
||||
MetroidPrime3,
|
||||
};
|
||||
|
||||
struct MetaforceVersionInfo : BigDNA {
|
||||
AT_DECL_DNA_YAML
|
||||
|
||||
String<-1> version;
|
||||
Value<ERegion> region;
|
||||
Value<EGame> game;
|
||||
Value<bool> isTrilogy;
|
||||
};
|
||||
} // namespace DataSpec
|
||||
@@ -1,258 +0,0 @@
|
||||
#include "DataSpec/DNACommon/OBBTreeBuilder.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "DataSpec/DNAMP1/DCLN.hpp"
|
||||
|
||||
#include <athena/Types.hpp>
|
||||
#include <gmm/gmm.h>
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
#include <zeus/CTransform.hpp>
|
||||
|
||||
namespace DataSpec {
|
||||
|
||||
using ColMesh = hecl::blender::ColMesh;
|
||||
|
||||
struct FittedOBB {
|
||||
zeus::CTransform xf;
|
||||
zeus::CVector3f he;
|
||||
};
|
||||
|
||||
static std::vector<int> MakeRootTriangleIndex(const ColMesh& mesh) {
|
||||
std::vector<int> ret;
|
||||
ret.reserve(mesh.trianges.size());
|
||||
for (size_t i = 0; i < mesh.trianges.size(); ++i)
|
||||
ret.push_back(i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::unordered_set<uint32_t> GetTriangleVerts(const ColMesh& mesh, int triIdx) {
|
||||
const ColMesh::Triangle& T = mesh.trianges[triIdx];
|
||||
std::unordered_set<uint32_t> verts;
|
||||
verts.insert(mesh.edges[T.edges[0]].verts[0]);
|
||||
verts.insert(mesh.edges[T.edges[0]].verts[1]);
|
||||
verts.insert(mesh.edges[T.edges[1]].verts[0]);
|
||||
verts.insert(mesh.edges[T.edges[1]].verts[1]);
|
||||
verts.insert(mesh.edges[T.edges[2]].verts[0]);
|
||||
verts.insert(mesh.edges[T.edges[2]].verts[1]);
|
||||
return verts;
|
||||
}
|
||||
|
||||
// method to set the OBB parameters which produce a box oriented according to
|
||||
// the covariance matrix C, which just containts the points pnts
|
||||
static FittedOBB BuildFromCovarianceMatrix(gmm::dense_matrix<float>& C, const ColMesh& mesh,
|
||||
const std::vector<int>& index) {
|
||||
FittedOBB ret;
|
||||
|
||||
// extract the eigenvalues and eigenvectors from C
|
||||
gmm::dense_matrix<float> eigvec(3, 3);
|
||||
std::vector<float> eigval(3);
|
||||
using namespace gmm;
|
||||
using MAT1 = gmm::dense_matrix<float>;
|
||||
gmm::symmetric_qr_algorithm(C, eigval, eigvec, default_tol_for_qr);
|
||||
|
||||
// find the right, up and forward vectors from the eigenvectors
|
||||
zeus::CVector3f r(eigvec(0, 0), eigvec(1, 0), eigvec(2, 0));
|
||||
zeus::CVector3f f(eigvec(0, 1), eigvec(1, 1), eigvec(2, 1));
|
||||
zeus::CVector3f u(eigvec(0, 2), eigvec(1, 2), eigvec(2, 2));
|
||||
r.normalize();
|
||||
f.normalize();
|
||||
u.normalize();
|
||||
|
||||
// set the rotation matrix using the eigvenvectors
|
||||
ret.xf.basis[0] = r;
|
||||
ret.xf.basis[1] = f;
|
||||
ret.xf.basis[2] = u;
|
||||
ret.xf.orthonormalize();
|
||||
|
||||
// now build the bounding box extents in the rotated frame
|
||||
zeus::CVector3f minim(1e10f, 1e10f, 1e10f), maxim(-1e10f, -1e10f, -1e10f);
|
||||
for (int triIdx : index) {
|
||||
std::unordered_set<uint32_t> verts = GetTriangleVerts(mesh, triIdx);
|
||||
for (uint32_t v : verts) {
|
||||
const zeus::CVector3f& p = mesh.verts[v].val;
|
||||
zeus::CVector3f p_prime(ret.xf.basis[0].dot(p), ret.xf.basis[1].dot(p), ret.xf.basis[2].dot(p));
|
||||
minim = zeus::min(minim, p_prime);
|
||||
maxim = zeus::max(maxim, p_prime);
|
||||
}
|
||||
}
|
||||
|
||||
// set the center of the OBB to be the average of the
|
||||
// minimum and maximum, and the extents be half of the
|
||||
// difference between the minimum and maximum
|
||||
zeus::CVector3f center = (maxim + minim) * 0.5f;
|
||||
ret.xf.origin = ret.xf.basis * center;
|
||||
ret.he = (maxim - minim) * 0.5f;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// builds an OBB from triangles specified as an array of
|
||||
// points with integer indices into the point array. Forms
|
||||
// the covariance matrix for the triangles, then uses the
|
||||
// method build_from_covariance_matrix() method to fit
|
||||
// the box. ALL points will be fit in the box, regardless
|
||||
// of whether they are indexed by a triangle or not.
|
||||
static FittedOBB FitOBB(const ColMesh& mesh, const std::vector<int>& index) {
|
||||
float Ai, Am = 0.0;
|
||||
zeus::CVector3f mu, mui;
|
||||
gmm::dense_matrix<float> C(3, 3);
|
||||
float cxx = 0.0, cxy = 0.0, cxz = 0.0, cyy = 0.0, cyz = 0.0, czz = 0.0;
|
||||
|
||||
// loop over the triangles this time to find the
|
||||
// mean location
|
||||
for (int i : index) {
|
||||
std::unordered_set<uint32_t> verts = GetTriangleVerts(mesh, i);
|
||||
auto it = verts.begin();
|
||||
zeus::CVector3f p = mesh.verts[*it++].val;
|
||||
zeus::CVector3f q = mesh.verts[*it++].val;
|
||||
zeus::CVector3f r = mesh.verts[*it++].val;
|
||||
mui = (p + q + r) / 3.f;
|
||||
Ai = (q - p).cross(r - p).magnitude() / 2.f;
|
||||
mu += mui * Ai;
|
||||
Am += Ai;
|
||||
|
||||
// these bits set the c terms to Am*E[xx], Am*E[xy], Am*E[xz]....
|
||||
cxx += (9.0 * mui.x() * mui.x() + p.x() * p.x() + q.x() * q.x() + r.x() * r.x()) * (Ai / 12.0);
|
||||
cxy += (9.0 * mui.x() * mui.y() + p.x() * p.y() + q.x() * q.y() + r.x() * r.y()) * (Ai / 12.0);
|
||||
cxz += (9.0 * mui.x() * mui.z() + p.x() * p.z() + q.x() * q.z() + r.x() * r.z()) * (Ai / 12.0);
|
||||
cyy += (9.0 * mui.y() * mui.y() + p.y() * p.y() + q.y() * q.y() + r.y() * r.y()) * (Ai / 12.0);
|
||||
cyz += (9.0 * mui.y() * mui.z() + p.y() * p.z() + q.y() * q.z() + r.y() * r.z()) * (Ai / 12.0);
|
||||
}
|
||||
|
||||
if (zeus::close_enough(Am, 0.f))
|
||||
return {};
|
||||
|
||||
// divide out the Am fraction from the average position and
|
||||
// covariance terms
|
||||
mu = mu / Am;
|
||||
cxx /= Am;
|
||||
cxy /= Am;
|
||||
cxz /= Am;
|
||||
cyy /= Am;
|
||||
cyz /= Am;
|
||||
czz /= Am;
|
||||
|
||||
// now subtract off the E[x]*E[x], E[x]*E[y], ... terms
|
||||
cxx -= mu.x() * mu.x();
|
||||
cxy -= mu.x() * mu.y();
|
||||
cxz -= mu.x() * mu.z();
|
||||
cyy -= mu.y() * mu.y();
|
||||
cyz -= mu.y() * mu.z();
|
||||
czz -= mu.z() * mu.z();
|
||||
|
||||
// now build the covariance matrix
|
||||
C(0, 0) = cxx;
|
||||
C(0, 1) = cxy;
|
||||
C(0, 2) = cxz;
|
||||
C(1, 0) = cxy;
|
||||
C(1, 1) = cyy;
|
||||
C(1, 2) = cyz;
|
||||
C(2, 0) = cxz;
|
||||
C(2, 1) = cyz;
|
||||
C(2, 2) = czz;
|
||||
|
||||
// set the obb parameters from the covariance matrix
|
||||
return BuildFromCovarianceMatrix(C, mesh, index);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
static void MakeLeaf(const ColMesh& mesh, const std::vector<int>& index, Node& n) {
|
||||
n.left.reset();
|
||||
n.right.reset();
|
||||
n.isLeaf = true;
|
||||
n.leafData = std::make_unique<typename Node::LeafData>();
|
||||
n.leafData->triangleIndexCount = atUint32(index.size());
|
||||
n.leafData->triangleIndices.reserve(n.leafData->triangleIndexCount);
|
||||
for (int i : index)
|
||||
n.leafData->triangleIndices.push_back(i);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
static std::unique_ptr<Node> RecursiveMakeNode(const ColMesh& mesh, const std::vector<int>& index) {
|
||||
// calculate root OBB
|
||||
FittedOBB obb = FitOBB(mesh, index);
|
||||
|
||||
// make results row-major and also invert the rotation basis
|
||||
obb.xf.basis.transpose();
|
||||
|
||||
std::unique_ptr<Node> n = std::make_unique<Node>();
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
n->xf[i] = zeus::CVector4f{obb.xf.basis[i]};
|
||||
n->xf[i].simd[3] = float(obb.xf.origin[i]);
|
||||
}
|
||||
n->halfExtent = obb.he;
|
||||
|
||||
// terminate branch when volume < 1.0
|
||||
if (obb.he[0] * obb.he[1] * obb.he[2] < 1.f) {
|
||||
MakeLeaf(mesh, index, *n);
|
||||
return n;
|
||||
}
|
||||
|
||||
n->isLeaf = false;
|
||||
|
||||
std::vector<int> indexNeg[3];
|
||||
std::vector<int> indexPos[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
// subdivide negative side
|
||||
indexNeg[c].reserve(index.size());
|
||||
for (int i : index) {
|
||||
std::unordered_set<uint32_t> verts = GetTriangleVerts(mesh, i);
|
||||
for (uint32_t vtx : verts) {
|
||||
zeus::CVector3f v = mesh.verts[vtx].val;
|
||||
v = obb.xf.basis * (v - obb.xf.origin);
|
||||
if (v[c] < 0.f) {
|
||||
indexNeg[c].push_back(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// subdivide positive side
|
||||
indexPos[c].reserve(index.size());
|
||||
for (int i : index) {
|
||||
std::unordered_set<uint32_t> verts = GetTriangleVerts(mesh, i);
|
||||
for (uint32_t vtx : verts) {
|
||||
zeus::CVector3f v = mesh.verts[vtx].val;
|
||||
v = obb.xf.basis * (v - obb.xf.origin);
|
||||
if (v[c] >= 0.f) {
|
||||
indexPos[c].push_back(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t idxMin = index.size();
|
||||
int minComp = -1;
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
size_t test = std::max(indexNeg[c].size(), indexPos[c].size());
|
||||
if (test < idxMin && test < index.size() * 3 / 4) {
|
||||
minComp = c;
|
||||
idxMin = test;
|
||||
}
|
||||
}
|
||||
|
||||
if (minComp == -1) {
|
||||
MakeLeaf(mesh, index, *n);
|
||||
return n;
|
||||
}
|
||||
|
||||
n->left = RecursiveMakeNode<Node>(mesh, indexNeg[minComp]);
|
||||
n->right = RecursiveMakeNode<Node>(mesh, indexPos[minComp]);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
std::unique_ptr<Node> OBBTreeBuilder::buildCol(const ColMesh& mesh) {
|
||||
std::vector<int> root = MakeRootTriangleIndex(mesh);
|
||||
return RecursiveMakeNode<Node>(mesh, root);
|
||||
}
|
||||
|
||||
template std::unique_ptr<DNAMP1::DCLN::Collision::Node>
|
||||
OBBTreeBuilder::buildCol<DNAMP1::DCLN::Collision::Node>(const ColMesh& mesh);
|
||||
|
||||
} // namespace DataSpec
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <hecl/Blender/Connection.hpp>
|
||||
|
||||
namespace DataSpec {
|
||||
|
||||
struct OBBTreeBuilder {
|
||||
using ColMesh = hecl::blender::ColMesh;
|
||||
template <typename Node>
|
||||
static std::unique_ptr<Node> buildCol(const ColMesh& mesh);
|
||||
};
|
||||
|
||||
} // namespace DataSpec
|
||||
@@ -1,679 +0,0 @@
|
||||
#include "DataSpec/DNACommon/PAK.hpp"
|
||||
|
||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
||||
|
||||
namespace DataSpec {
|
||||
|
||||
template <class PAKBRIDGE>
|
||||
void UniqueResult::checkEntry(const PAKBRIDGE& pakBridge, const typename PAKBRIDGE::PAKType::Entry& entry) {
|
||||
UniqueResult::Type resultType = UniqueResult::Type::NotFound;
|
||||
bool foundOneLayer = false;
|
||||
const std::string* levelName = nullptr;
|
||||
typename PAKBRIDGE::PAKType::IDType useLevelId;
|
||||
typename PAKBRIDGE::PAKType::IDType useAreaId;
|
||||
unsigned layerIdx = 0;
|
||||
for (const auto& [levelId, level] : pakBridge.m_levelDeps) {
|
||||
if (entry.id == levelId || level.resources.find(entry.id) != level.resources.end()) {
|
||||
levelName = &level.name;
|
||||
resultType = UniqueResult::Type::Level;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const auto& [areaId, area] : level.areas) {
|
||||
unsigned l = 0;
|
||||
for (const auto& layer : area.layers) {
|
||||
if (layer.resources.find(entry.id) != layer.resources.end()) {
|
||||
if (foundOneLayer) {
|
||||
if (useAreaId == areaId) {
|
||||
resultType = UniqueResult::Type::Area;
|
||||
} else if (useLevelId == levelId) {
|
||||
resultType = UniqueResult::Type::Level;
|
||||
break;
|
||||
} else {
|
||||
m_type = UniqueResult::Type::Pak;
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
} else
|
||||
resultType = UniqueResult::Type::Layer;
|
||||
levelName = &level.name;
|
||||
useLevelId = levelId;
|
||||
useAreaId = areaId;
|
||||
layerIdx = l;
|
||||
foundOneLayer = true;
|
||||
}
|
||||
++l;
|
||||
}
|
||||
if (area.resources.find(entry.id) != area.resources.end()) {
|
||||
if (foundOneLayer) {
|
||||
if (useAreaId == areaId) {
|
||||
resultType = UniqueResult::Type::Area;
|
||||
} else if (useLevelId == levelId) {
|
||||
resultType = UniqueResult::Type::Level;
|
||||
break;
|
||||
} else {
|
||||
m_type = UniqueResult::Type::Pak;
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
} else
|
||||
resultType = UniqueResult::Type::Area;
|
||||
levelName = &level.name;
|
||||
useLevelId = levelId;
|
||||
useAreaId = areaId;
|
||||
foundOneLayer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_type = resultType;
|
||||
m_levelName = levelName;
|
||||
if (resultType == UniqueResult::Type::Layer || resultType == UniqueResult::Type::Area) {
|
||||
const typename PAKBRIDGE::Level::Area& area = pakBridge.m_levelDeps.at(useLevelId).areas.at(useAreaId);
|
||||
m_areaName = &area.name;
|
||||
if (resultType == UniqueResult::Type::Layer) {
|
||||
const typename PAKBRIDGE::Level::Area::Layer& layer = area.layers[layerIdx];
|
||||
m_layerName = &layer.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void UniqueResult::checkEntry(const DNAMP1::PAKBridge& pakBridge,
|
||||
const DNAMP1::PAKBridge::PAKType::Entry& entry);
|
||||
template void UniqueResult::checkEntry(const DNAMP2::PAKBridge& pakBridge,
|
||||
const DNAMP2::PAKBridge::PAKType::Entry& entry);
|
||||
template void UniqueResult::checkEntry(const DNAMP3::PAKBridge& pakBridge,
|
||||
const DNAMP3::PAKBridge::PAKType::Entry& entry);
|
||||
|
||||
hecl::ProjectPath UniqueResult::uniquePath(const hecl::ProjectPath& pakPath) const {
|
||||
if (m_type == Type::Pak)
|
||||
return pakPath;
|
||||
|
||||
hecl::ProjectPath levelDir;
|
||||
if (m_levelName)
|
||||
levelDir.assign(pakPath, *m_levelName);
|
||||
else
|
||||
levelDir = pakPath;
|
||||
|
||||
if (m_type == Type::Area) {
|
||||
hecl::ProjectPath areaDir(levelDir, *m_areaName);
|
||||
return areaDir;
|
||||
} else if (m_type == Type::Layer) {
|
||||
hecl::ProjectPath areaDir(levelDir, *m_areaName);
|
||||
hecl::ProjectPath layerDir(areaDir, *m_layerName);
|
||||
return layerDir;
|
||||
}
|
||||
|
||||
return levelDir;
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
void PAKRouter<BRIDGETYPE>::build(std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress) {
|
||||
m_bridges = &bridges;
|
||||
m_bridgePaths.clear();
|
||||
|
||||
m_uniqueEntries.clear();
|
||||
m_sharedEntries.clear();
|
||||
m_charAssoc.m_cmdlRigs.clear();
|
||||
size_t count = 0;
|
||||
float bridgesSz = bridges.size();
|
||||
|
||||
/* Route entries unique/shared per-pak */
|
||||
size_t bridgeIdx = 0;
|
||||
for (BRIDGETYPE& bridge : bridges) {
|
||||
const auto& name = bridge.getName();
|
||||
|
||||
std::string_view::const_iterator extit = name.end() - 4;
|
||||
std::string baseName(name.begin(), extit);
|
||||
|
||||
m_bridgePaths.emplace_back(
|
||||
std::make_pair(hecl::ProjectPath(m_gameWorking, baseName), hecl::ProjectPath(m_gameCooked, baseName)));
|
||||
|
||||
/* Index this PAK */
|
||||
bridge.build();
|
||||
|
||||
/* Add to global entry lookup */
|
||||
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
|
||||
for (const auto& entry : pak.m_entries) {
|
||||
if (!pak.m_noShare) {
|
||||
auto sSearch = m_sharedEntries.find(entry.first);
|
||||
if (sSearch != m_sharedEntries.end())
|
||||
continue;
|
||||
auto uSearch = m_uniqueEntries.find(entry.first);
|
||||
if (uSearch != m_uniqueEntries.end()) {
|
||||
m_uniqueEntries.erase(uSearch);
|
||||
m_sharedEntries[entry.first] = std::make_pair(bridgeIdx, &entry.second);
|
||||
} else
|
||||
m_uniqueEntries[entry.first] = std::make_pair(bridgeIdx, &entry.second);
|
||||
} else
|
||||
m_uniqueEntries[entry.first] = std::make_pair(bridgeIdx, &entry.second);
|
||||
}
|
||||
|
||||
/* Add RigPairs to global map */
|
||||
bridge.addCMDLRigPairs(*this, m_charAssoc);
|
||||
|
||||
progress(++count / bridgesSz);
|
||||
++bridgeIdx;
|
||||
}
|
||||
|
||||
/* Add named resources to catalog YAML files */
|
||||
for (BRIDGETYPE& bridge : bridges) {
|
||||
athena::io::YAMLDocWriter catalogWriter;
|
||||
|
||||
enterPAKBridge(bridge);
|
||||
|
||||
/* Add MAPA transforms to global map */
|
||||
bridge.addMAPATransforms(*this, m_mapaTransforms, m_overrideEntries);
|
||||
|
||||
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
|
||||
for (const auto& namedEntry : pak.m_nameEntries) {
|
||||
if (namedEntry.name == "holo_cinf")
|
||||
continue; /* Problematic corner case */
|
||||
if (auto rec = catalogWriter.enterSubRecord(namedEntry.name)) {
|
||||
hecl::ProjectPath working = getWorking(namedEntry.id);
|
||||
if (working.getAuxInfo().size()) {
|
||||
if (auto v = catalogWriter.enterSubVector()) {
|
||||
catalogWriter.writeString(working.getRelativePath());
|
||||
catalogWriter.writeString(working.getAuxInfo());
|
||||
}
|
||||
} else
|
||||
catalogWriter.writeString(working.getRelativePath());
|
||||
}
|
||||
}
|
||||
|
||||
/* Write catalog */
|
||||
intptr_t curBridgeIdx = reinterpret_cast<intptr_t>(m_curBridgeIdx.get());
|
||||
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].first;
|
||||
pakPath.makeDirChain(true);
|
||||
athena::io::FileWriter writer(hecl::ProjectPath(pakPath, "!catalog.yaml").getAbsolutePath());
|
||||
catalogWriter.finish(&writer);
|
||||
}
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
void PAKRouter<BRIDGETYPE>::enterPAKBridge(const BRIDGETYPE& pakBridge) {
|
||||
g_PakRouter.reset(this);
|
||||
auto pit = m_bridgePaths.begin();
|
||||
size_t bridgeIdx = 0;
|
||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
||||
if (&bridge == &pakBridge) {
|
||||
m_pak.reset(&pakBridge.getPAK());
|
||||
m_node.reset(&pakBridge.getNode());
|
||||
m_curBridgeIdx.reset(reinterpret_cast<void*>(bridgeIdx));
|
||||
return;
|
||||
}
|
||||
++pit;
|
||||
++bridgeIdx;
|
||||
}
|
||||
LogDNACommon.report(logvisor::Fatal,
|
||||
FMT_STRING("PAKBridge provided to PAKRouter::enterPAKBridge() was not part of build()"));
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCharacterWorking(const EntryType* entry) const {
|
||||
auto characterSearch = m_charAssoc.m_cskrToCharacter.find(entry->id);
|
||||
if (characterSearch != m_charAssoc.m_cskrToCharacter.cend()) {
|
||||
hecl::ProjectPath characterPath = getWorking(characterSearch->second.first);
|
||||
if (entry->type == FOURCC('EVNT')) {
|
||||
std::string extension(characterSearch->second.second);
|
||||
return characterPath.getWithExtension((std::string(".") + extension.c_str()).c_str(), true);
|
||||
}
|
||||
return characterPath.ensureAuxInfo(characterSearch->second.second);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const EntryType* entry,
|
||||
const ResExtractor<BRIDGETYPE>& extractor) const {
|
||||
if (!entry)
|
||||
return hecl::ProjectPath();
|
||||
|
||||
auto overrideSearch = m_overrideEntries.find(entry->id);
|
||||
if (overrideSearch != m_overrideEntries.end())
|
||||
return overrideSearch->second;
|
||||
|
||||
const PAKType* pak = m_pak.get();
|
||||
intptr_t curBridgeIdx = reinterpret_cast<intptr_t>(m_curBridgeIdx.get());
|
||||
if (pak && pak->m_noShare) {
|
||||
const EntryType* singleSearch = pak->lookupEntry(entry->id);
|
||||
if (singleSearch) {
|
||||
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].first;
|
||||
std::string entName = getBestEntryName(*entry);
|
||||
std::string auxInfo;
|
||||
if (extractor.fileExts[0] && !extractor.fileExts[1])
|
||||
entName += extractor.fileExts[0];
|
||||
else if (extractor.fileExts[0])
|
||||
entName += ".*";
|
||||
else if (hecl::ProjectPath chWork = getCharacterWorking(entry))
|
||||
return chWork;
|
||||
return hecl::ProjectPath(pakPath, entName).ensureAuxInfo(auxInfo);
|
||||
}
|
||||
}
|
||||
|
||||
auto uniqueSearch = m_uniqueEntries.find(entry->id);
|
||||
if (uniqueSearch != m_uniqueEntries.end()) {
|
||||
const BRIDGETYPE& bridge = m_bridges->at(uniqueSearch->second.first);
|
||||
const hecl::ProjectPath& pakPath = m_bridgePaths[uniqueSearch->second.first].first;
|
||||
std::string entName = getBestEntryName(*entry);
|
||||
std::string auxInfo;
|
||||
if (extractor.fileExts[0] && !extractor.fileExts[1])
|
||||
entName += extractor.fileExts[0];
|
||||
else if (extractor.fileExts[0])
|
||||
entName += ".*";
|
||||
else if (hecl::ProjectPath chWork = getCharacterWorking(entry))
|
||||
return chWork;
|
||||
if (bridge.getPAK().m_noShare) {
|
||||
return hecl::ProjectPath(pakPath, entName).ensureAuxInfo(auxInfo);
|
||||
} else {
|
||||
hecl::ProjectPath uniquePath = entry->unique.uniquePath(pakPath);
|
||||
return hecl::ProjectPath(uniquePath, entName).ensureAuxInfo(auxInfo);
|
||||
}
|
||||
}
|
||||
|
||||
auto sharedSearch = m_sharedEntries.find(entry->id);
|
||||
if (sharedSearch != m_sharedEntries.end()) {
|
||||
std::string entBase = getBestEntryName(*entry);
|
||||
std::string auxInfo;
|
||||
std::string entName = entBase;
|
||||
if (extractor.fileExts[0] && !extractor.fileExts[1])
|
||||
entName += extractor.fileExts[0];
|
||||
else if (extractor.fileExts[0])
|
||||
entName += ".*";
|
||||
else if (hecl::ProjectPath chWork = getCharacterWorking(entry))
|
||||
return chWork;
|
||||
hecl::ProjectPath sharedPath(m_sharedWorking, entName);
|
||||
return sharedPath.ensureAuxInfo(auxInfo);
|
||||
}
|
||||
|
||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("Unable to find entry {}"), entry->id);
|
||||
return hecl::ProjectPath();
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const EntryType* entry) const {
|
||||
if (!entry)
|
||||
return hecl::ProjectPath();
|
||||
return getWorking(entry, BRIDGETYPE::LookupExtractor(*m_node.get(), *m_pak.get(), *entry));
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const IDType& id, bool silenceWarnings) const {
|
||||
return getWorking(lookupEntry(id, nullptr, silenceWarnings, false));
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCooked(const EntryType* entry) const {
|
||||
if (!entry)
|
||||
return hecl::ProjectPath();
|
||||
|
||||
auto overrideSearch = m_overrideEntries.find(entry->id);
|
||||
if (overrideSearch != m_overrideEntries.end()) {
|
||||
return overrideSearch->second.getCookedPath(
|
||||
*m_dataSpec.overrideDataSpec(overrideSearch->second, m_dataSpec.getDataSpecEntry()));
|
||||
}
|
||||
|
||||
const PAKType* pak = m_pak.get();
|
||||
intptr_t curBridgeIdx = reinterpret_cast<intptr_t>(m_curBridgeIdx.get());
|
||||
if (pak && pak->m_noShare) {
|
||||
const EntryType* singleSearch = pak->lookupEntry(entry->id);
|
||||
if (singleSearch) {
|
||||
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].second;
|
||||
return hecl::ProjectPath(pakPath, getBestEntryName(*entry));
|
||||
}
|
||||
}
|
||||
auto uniqueSearch = m_uniqueEntries.find(entry->id);
|
||||
if (uniqueSearch != m_uniqueEntries.end()) {
|
||||
const BRIDGETYPE& bridge = m_bridges->at(uniqueSearch->second.first);
|
||||
const hecl::ProjectPath& pakPath = m_bridgePaths[uniqueSearch->second.first].second;
|
||||
if (bridge.getPAK().m_noShare) {
|
||||
return hecl::ProjectPath(pakPath, getBestEntryName(*entry));
|
||||
} else {
|
||||
hecl::ProjectPath uniquePath = entry->unique.uniquePath(pakPath);
|
||||
return hecl::ProjectPath(uniquePath, getBestEntryName(*entry));
|
||||
}
|
||||
}
|
||||
auto sharedSearch = m_sharedEntries.find(entry->id);
|
||||
if (sharedSearch != m_sharedEntries.end()) {
|
||||
return hecl::ProjectPath(m_sharedCooked, getBestEntryName(*entry));
|
||||
}
|
||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("Unable to find entry {}"), entry->id);
|
||||
return hecl::ProjectPath();
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCooked(const IDType& id, bool silenceWarnings) const {
|
||||
return getCooked(lookupEntry(id, nullptr, silenceWarnings, false));
|
||||
}
|
||||
|
||||
template <class BRIDGETYPE>
|
||||
std::string PAKRouter<BRIDGETYPE>::getResourceRelativePath(const EntryType& a, const IDType& b) const {
|
||||
const nod::Node* node = m_node.get();
|
||||
const PAKType* pak = m_pak.get();
|
||||
if (!pak)
|
||||
LogDNACommon.report(
|
||||
logvisor::Fatal,
|
||||
FMT_STRING("PAKRouter::enterPAKBridge() must be called before PAKRouter::getResourceRelativePath()"));
|
||||
const typename BRIDGETYPE::PAKType::Entry* be = lookupEntry(b);
|
||||
if (!be)
|
||||
return std::string();
|
||||
hecl::ProjectPath aPath = getWorking(&a, BRIDGETYPE::LookupExtractor(*node, *pak, a));
|
||||
std::string ret;
|
||||
for (size_t i = 0; i < aPath.levelCount(); ++i)
|
||||
ret += "../";
|
||||
hecl::ProjectPath bPath = getWorking(be, BRIDGETYPE::LookupExtractor(*node, *pak, *be));
|
||||
ret += bPath.getRelativePath();
|
||||