2
0
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:
2022-03-09 00:06:26 -05:00
parent 7186f5d4e5
commit 13b1ebb12e
752 changed files with 48 additions and 185840 deletions

15
.gitmodules vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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