mirror of https://github.com/AxioDL/metaforce.git
Merge branch 'main' into patch-4
This commit is contained in:
commit
8d81106681
|
@ -2,8 +2,8 @@ name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
# branches-ignore:
|
||||||
- main
|
# - main
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '*.json'
|
- '*.json'
|
||||||
- '*.md'
|
- '*.md'
|
||||||
|
@ -18,7 +18,7 @@ env:
|
||||||
jobs:
|
jobs:
|
||||||
build-linux:
|
build-linux:
|
||||||
name: Build Linux (${{matrix.name}} x86_64)
|
name: Build Linux (${{matrix.name}} x86_64)
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILDCACHE_DIR: ${{github.workspace}}/.buildcache
|
BUILDCACHE_DIR: ${{github.workspace}}/.buildcache
|
||||||
|
@ -46,10 +46,8 @@ jobs:
|
||||||
sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main"
|
sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main"
|
||||||
sudo apt-get -y install ninja-build clang lld libcurl4-openssl-dev intel-oneapi-ipp-devel \
|
sudo apt-get -y install ninja-build clang lld libcurl4-openssl-dev intel-oneapi-ipp-devel \
|
||||||
zlib1g-dev libglu1-mesa-dev libdbus-1-dev libvulkan-dev libxi-dev libxrandr-dev libasound2-dev \
|
zlib1g-dev libglu1-mesa-dev libdbus-1-dev libvulkan-dev libxi-dev libxrandr-dev libasound2-dev \
|
||||||
libpulse-dev libudev-dev libpng-dev libncurses5-dev libx11-xcb-dev qt5-default
|
libpulse-dev libudev-dev libpng-dev libncurses5-dev libx11-xcb-dev libfreetype-dev \
|
||||||
|
libxinerama-dev libxcursor-dev python3-markupsafe libgtk-3-dev
|
||||||
yarn global add @sentry/cli
|
|
||||||
echo "$(yarn global bin)" >> $GITHUB_PATH
|
|
||||||
|
|
||||||
# setup buildcache
|
# setup buildcache
|
||||||
curl -LSfs https://github.com/encounter/buildcache/releases/download/$BUILDCACHE_VERSION/buildcache-linux.tar.gz | tar xz -C "$RUNNER_WORKSPACE"
|
curl -LSfs https://github.com/encounter/buildcache/releases/download/$BUILDCACHE_VERSION/buildcache-linux.tar.gz | tar xz -C "$RUNNER_WORKSPACE"
|
||||||
|
@ -84,12 +82,22 @@ jobs:
|
||||||
- name: Print buildcache stats
|
- name: Print buildcache stats
|
||||||
run: buildcache -s
|
run: buildcache -s
|
||||||
|
|
||||||
|
- name: Generate AppImage
|
||||||
|
run: ci/build-appimage.sh
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: metaforce-${{env.METAFORCE_VERSION}}-linux-${{matrix.preset}}-x86_64
|
||||||
|
path: |
|
||||||
|
build/install/Metaforce-*.AppImage
|
||||||
|
build/install/debug.tar.*
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
name: Build macOS (AppleClang universal)
|
name: Build macOS (AppleClang universal)
|
||||||
runs-on: macos-11
|
runs-on: macos-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
Qt_VERSION: 5.15.2
|
|
||||||
IPP_VERSION: 2021.2.0.192
|
IPP_VERSION: 2021.2.0.192
|
||||||
BUILDCACHE_DIR: ${{github.workspace}}/.buildcache
|
BUILDCACHE_DIR: ${{github.workspace}}/.buildcache
|
||||||
|
|
||||||
|
@ -105,11 +113,7 @@ jobs:
|
||||||
brew upgrade --formula
|
brew upgrade --formula
|
||||||
brew install ninja graphicsmagick imagemagick
|
brew install ninja graphicsmagick imagemagick
|
||||||
yarn global add create-dmg
|
yarn global add create-dmg
|
||||||
|
pip3 install markupsafe
|
||||||
# universal qt5 from macports
|
|
||||||
curl -LSfs https://axiodl.com/files/qt-$Qt_VERSION.mpkg -o /tmp/qt-$Qt_VERSION.mpkg
|
|
||||||
sudo installer -pkg /tmp/qt-$Qt_VERSION.mpkg -target /
|
|
||||||
echo /opt/local/libexec/qt5/bin >> $GITHUB_PATH
|
|
||||||
|
|
||||||
# setup buildcache
|
# setup buildcache
|
||||||
curl -LSfs https://github.com/encounter/buildcache/releases/download/$BUILDCACHE_VERSION/buildcache-macos.zip -o /tmp/buildcache-macos.zip
|
curl -LSfs https://github.com/encounter/buildcache/releases/download/$BUILDCACHE_VERSION/buildcache-macos.zip -o /tmp/buildcache-macos.zip
|
||||||
|
@ -141,11 +145,103 @@ jobs:
|
||||||
run: cmake --build --preset x-macos-ci
|
run: cmake --build --preset x-macos-ci
|
||||||
|
|
||||||
- name: Print buildcache stats
|
- name: Print buildcache stats
|
||||||
|
if: 'false' # temporarily disabled
|
||||||
run: buildcache -s
|
run: buildcache -s
|
||||||
|
|
||||||
|
- name: Import signing certificate
|
||||||
|
uses: devbotsxyz/xcode-import-certificate@master
|
||||||
|
with:
|
||||||
|
certificate-data: ${{secrets.MACOS_CERTIFICATE_DATA}}
|
||||||
|
certificate-passphrase: ${{secrets.MACOS_CERTIFICATE_PASSWORD}}
|
||||||
|
keychain-password: ${{secrets.MACOS_KEYCHAIN_PASSWORD}}
|
||||||
|
|
||||||
|
- name: Deploy & codesign application
|
||||||
|
env:
|
||||||
|
ASC_USERNAME: ${{secrets.MACOS_ASC_USERNAME}}
|
||||||
|
ASC_PASSWORD: ${{secrets.MACOS_ASC_PASSWORD}}
|
||||||
|
ASC_TEAM_ID: ${{secrets.MACOS_ASC_TEAM_ID}}
|
||||||
|
CODESIGN_IDENT: ${{secrets.MACOS_CODESIGN_IDENT}}
|
||||||
|
run: ci/build-dmg.sh
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: metaforce-${{env.METAFORCE_VERSION}}-macos-appleclang-universal
|
||||||
|
path: |
|
||||||
|
build/install/Metaforce *.dmg
|
||||||
|
build/install/debug.tar.*
|
||||||
|
|
||||||
|
build-ios:
|
||||||
|
name: Build iOS (AppleClang arm64)
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
brew update
|
||||||
|
brew upgrade --formula
|
||||||
|
brew install ninja
|
||||||
|
pip3 install markupsafe
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: cmake --preset ios-default
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build --preset ios-default --target install
|
||||||
|
|
||||||
|
- name: Generate IPA
|
||||||
|
run: ci/build-ipa.sh
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: metaforce-${{env.METAFORCE_VERSION}}-ios-appleclang-arm64
|
||||||
|
path: |
|
||||||
|
build/install/Metaforce.ipa
|
||||||
|
build/install/debug.tar.*
|
||||||
|
|
||||||
|
build-tvos:
|
||||||
|
name: Build tvOS (AppleClang arm64)
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
brew update
|
||||||
|
brew upgrade --formula
|
||||||
|
brew install ninja
|
||||||
|
pip3 install markupsafe
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: cmake --preset tvos-default
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build --preset tvos-default --target install
|
||||||
|
|
||||||
|
- name: Generate IPA
|
||||||
|
run: ci/build-ipa.sh
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: metaforce-${{env.METAFORCE_VERSION}}-tvos-appleclang-arm64
|
||||||
|
path: |
|
||||||
|
build/install/Metaforce.ipa
|
||||||
|
build/install/debug.tar.*
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
name: Build Windows (${{matrix.name}} x86_64)
|
name: Build Windows (${{matrix.name}} x86_64)
|
||||||
runs-on: windows-2019
|
runs-on: windows-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
Qt_VERSION: 5.15.2
|
Qt_VERSION: 5.15.2
|
||||||
|
@ -158,11 +254,10 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# Disabled due to memory constraints
|
- name: MSVC
|
||||||
# - name: MSVC
|
preset: msvc
|
||||||
# preset: msvc
|
#- name: Clang
|
||||||
- name: Clang
|
# preset: clang
|
||||||
preset: clang
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -191,6 +286,7 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
choco install ninja vulkan-sdk
|
choco install ninja vulkan-sdk
|
||||||
|
pip install markupsafe
|
||||||
|
|
||||||
# set up buildcache
|
# set up buildcache
|
||||||
$TempDir = "$env:RUNNER_WORKSPACE\temp"
|
$TempDir = "$env:RUNNER_WORKSPACE\temp"
|
||||||
|
@ -221,3 +317,11 @@ jobs:
|
||||||
|
|
||||||
- name: Print buildcache stats
|
- name: Print buildcache stats
|
||||||
run: buildcache -s
|
run: buildcache -s
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: metaforce-${{env.METAFORCE_VERSION}}-win32-${{matrix.preset}}-x86_64
|
||||||
|
path: |
|
||||||
|
${{env.BUILD_DIR}}/install/*.exe
|
||||||
|
${{env.BUILD_DIR}}/install/debug.7z
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on: workflow_dispatch
|
||||||
push:
|
#on:
|
||||||
branches:
|
# push:
|
||||||
- main
|
# branches:
|
||||||
paths-ignore:
|
# - main
|
||||||
- '*.json'
|
# paths-ignore:
|
||||||
- '*.md'
|
# - '*.json'
|
||||||
- '*LICENSE'
|
# - '*.md'
|
||||||
|
# - '*LICENSE'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILDCACHE_VERSION: v0.27.3
|
BUILDCACHE_VERSION: v0.27.3
|
||||||
|
@ -46,7 +47,7 @@ jobs:
|
||||||
sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main"
|
sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main"
|
||||||
sudo apt-get -y install ninja-build clang lld libcurl4-openssl-dev intel-oneapi-ipp-devel \
|
sudo apt-get -y install ninja-build clang lld libcurl4-openssl-dev intel-oneapi-ipp-devel \
|
||||||
zlib1g-dev libglu1-mesa-dev libdbus-1-dev libvulkan-dev libxi-dev libxrandr-dev libasound2-dev \
|
zlib1g-dev libglu1-mesa-dev libdbus-1-dev libvulkan-dev libxi-dev libxrandr-dev libasound2-dev \
|
||||||
libpulse-dev libudev-dev libpng-dev libncurses5-dev libx11-xcb-dev qt5-default
|
libpulse-dev libudev-dev libpng-dev libncurses5-dev libx11-xcb-dev qt5-default libfreetype-dev
|
||||||
|
|
||||||
yarn global add @sentry/cli
|
yarn global add @sentry/cli
|
||||||
echo "$(yarn global bin)" >> $GITHUB_PATH
|
echo "$(yarn global bin)" >> $GITHUB_PATH
|
||||||
|
@ -121,7 +122,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
brew update
|
brew update
|
||||||
brew upgrade --formula
|
brew upgrade --formula
|
||||||
brew install ninja graphicsmagick imagemagick getsentry/tools/sentry-cli
|
brew install ninja graphicsmagick imagemagick getsentry/tools/sentry-cli freetype
|
||||||
yarn global add create-dmg
|
yarn global add create-dmg
|
||||||
|
|
||||||
# universal qt5 from macports
|
# universal qt5 from macports
|
||||||
|
|
|
@ -1,59 +1,48 @@
|
||||||
[submodule "nod"]
|
[submodule "extern/nod"]
|
||||||
path = extern/nod
|
path = extern/nod
|
||||||
url = ../nod.git
|
url = ../nod.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "amuse"]
|
[submodule "extern/kabufuda"]
|
||||||
path = extern/amuse
|
|
||||||
url = ../amuse.git
|
|
||||||
branch = master
|
|
||||||
[submodule "kabufuda"]
|
|
||||||
path = extern/kabufuda
|
path = extern/kabufuda
|
||||||
url = ../kabufuda.git
|
url = ../kabufuda.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "jbus"]
|
[submodule "extern/jbus"]
|
||||||
path = extern/jbus
|
path = extern/jbus
|
||||||
url = ../jbus.git
|
url = ../jbus.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "assetnameparser/tinyxml2"]
|
[submodule "extern/discord-rpc"]
|
||||||
path = extern/tinyxml2
|
|
||||||
url = ../tinyxml2.git
|
|
||||||
branch = master
|
|
||||||
[submodule "sanitizers-cmake"]
|
|
||||||
path = extern/sanitizers-cmake
|
|
||||||
url = https://github.com/arsenm/sanitizers-cmake.git
|
|
||||||
branch = master
|
|
||||||
[submodule "discord-rpc"]
|
|
||||||
path = extern/discord-rpc
|
path = extern/discord-rpc
|
||||||
url = https://github.com/discordapp/discord-rpc.git
|
url = https://github.com/discordapp/discord-rpc.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "rapidjson"]
|
[submodule "extern/rapidjson"]
|
||||||
path = extern/rapidjson
|
path = extern/rapidjson
|
||||||
url = https://github.com/Tencent/rapidjson.git
|
url = https://github.com/Tencent/rapidjson.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "NESEmulator/fixNES"]
|
[submodule "extern/fixNES"]
|
||||||
path = extern/fixNES
|
path = extern/fixNES
|
||||||
url = https://github.com/FIX94/fixNES.git
|
url = https://github.com/FIX94/fixNES.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "extern/libSquish"]
|
|
||||||
path = extern/libSquish
|
|
||||||
url = ../libSquish.git
|
|
||||||
branch = master
|
|
||||||
[submodule "extern/athena"]
|
[submodule "extern/athena"]
|
||||||
path = extern/athena
|
path = extern/athena
|
||||||
url = ../../libAthena/athena.git
|
url = ../../libAthena/athena.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "extern/boo"]
|
|
||||||
path = extern/boo
|
|
||||||
url = ../boo.git
|
|
||||||
branch = master
|
|
||||||
[submodule "extern/libjpeg-turbo"]
|
[submodule "extern/libjpeg-turbo"]
|
||||||
path = extern/libjpeg-turbo
|
path = extern/libjpeg-turbo
|
||||||
url = ../libjpeg-turbo.git
|
url = ../libjpeg-turbo.git
|
||||||
branch = thp
|
branch = thp
|
||||||
[submodule "zeus"]
|
[submodule "extern/zeus"]
|
||||||
path = extern/zeus
|
path = extern/zeus
|
||||||
url = ../zeus.git
|
url = ../zeus.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "extern/imgui"]
|
[submodule "extern/nativefiledialog-extended"]
|
||||||
path = extern/imgui
|
path = extern/nativefiledialog-extended
|
||||||
url = https://github.com/ocornut/imgui.git
|
url = https://github.com/encounter/nativefiledialog-extended.git
|
||||||
|
branch = nfd-install-option
|
||||||
|
[submodule "extern/optick"]
|
||||||
|
path = extern/optick
|
||||||
|
url = https://github.com/AxioDL/optick.git
|
||||||
|
branch = master
|
||||||
|
[submodule "extern/aurora"]
|
||||||
|
path = extern/aurora
|
||||||
|
url = https://github.com/encounter/aurora.git
|
||||||
|
branch = main
|
||||||
|
|
328
CMakeLists.txt
328
CMakeLists.txt
|
@ -50,7 +50,7 @@ endif ()
|
||||||
|
|
||||||
if (METAFORCE_WC_DESCRIBE)
|
if (METAFORCE_WC_DESCRIBE)
|
||||||
string(REGEX REPLACE "v([0-9]+)\.([0-9]+)\.([0-9]+)\-([0-9]+).*" "\\1.\\2.\\3.\\4" METAFORCE_VERSION_STRING "${METAFORCE_WC_DESCRIBE}")
|
string(REGEX REPLACE "v([0-9]+)\.([0-9]+)\.([0-9]+)\-([0-9]+).*" "\\1.\\2.\\3.\\4" METAFORCE_VERSION_STRING "${METAFORCE_WC_DESCRIBE}")
|
||||||
string(REGEX REPLACE "v([0-9]+)\.([0-9]+)\.([0-9]+).*" "\\1.\\2.\\3" METAFORCE_VERSION_STRING "${METAFORCE_VERSION_STRING}")
|
string(REGEX REPLACE "v([0-9]+)\.([0-9]+)\.([0-9]+).*" "\\1.\\2.\\3" METAFORCE_SHORT_VERSION_STRING "${METAFORCE_WC_DESCRIBE}")
|
||||||
else ()
|
else ()
|
||||||
set(METAFORCE_WC_DESCRIBE "UNKNOWN-VERSION")
|
set(METAFORCE_WC_DESCRIBE "UNKNOWN-VERSION")
|
||||||
set(METAFORCE_VERSION_STRING "0.0.0")
|
set(METAFORCE_VERSION_STRING "0.0.0")
|
||||||
|
@ -64,20 +64,36 @@ if(DEFINED ENV{GITHUB_ENV})
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Metaforce version set to ${METAFORCE_WC_DESCRIBE}")
|
message(STATUS "Metaforce version set to ${METAFORCE_WC_DESCRIBE}")
|
||||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
project(metaforce LANGUAGES C CXX ASM VERSION ${METAFORCE_VERSION_STRING})
|
if(APPLE)
|
||||||
|
set(EXTRA_LANGUAGES OBJC)
|
||||||
|
endif()
|
||||||
|
project(metaforce LANGUAGES C CXX ASM ${EXTRA_LANGUAGES} VERSION ${METAFORCE_VERSION_STRING})
|
||||||
|
if (APPLE AND NOT TVOS AND CMAKE_SYSTEM_NAME STREQUAL tvOS)
|
||||||
|
# ios.toolchain.cmake hack for SDL
|
||||||
|
set(TVOS ON)
|
||||||
|
set(IOS OFF)
|
||||||
|
endif ()
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
set(CMAKE_EXECUTABLE_SUFFIX .html)
|
||||||
|
endif ()
|
||||||
|
|
||||||
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" ATHENA_ARCH)
|
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" ATHENA_ARCH)
|
||||||
string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" ATHENA_HOST_ARCH)
|
string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" ATHENA_HOST_ARCH)
|
||||||
|
string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" HOST_PLATFORM_NAME)
|
||||||
|
string(TOLOWER "${CMAKE_SYSTEM_NAME}" PLATFORM_NAME)
|
||||||
set(ATHENA_EXTENSION "tar.gz")
|
set(ATHENA_EXTENSION "tar.gz")
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL Darwin)
|
if (CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)
|
||||||
set(PLATFORM_NAME macos)
|
set(HOST_PLATFORM_NAME macos)
|
||||||
set(ATHENA_ARCH universal)
|
set(ATHENA_ARCH universal)
|
||||||
set(ATHENA_HOST_ARCH universal)
|
set(ATHENA_HOST_ARCH universal)
|
||||||
|
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
|
||||||
|
set(HOST_PLATFORM_NAME win32)
|
||||||
|
set(ATHENA_EXTENSION ".7z")
|
||||||
|
endif ()
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL Darwin)
|
||||||
|
set(PLATFORM_NAME macos)
|
||||||
elseif (CMAKE_SYSTEM_NAME STREQUAL Windows)
|
elseif (CMAKE_SYSTEM_NAME STREQUAL Windows)
|
||||||
set(PLATFORM_NAME win32)
|
set(PLATFORM_NAME win32)
|
||||||
set(ATHENA_EXTENSION ".7z")
|
|
||||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
||||||
set(PLATFORM_NAME linux)
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Binaries)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Binaries)
|
||||||
|
@ -89,15 +105,6 @@ if(APPLE AND NOT CMAKE_OSX_SYSROOT)
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
endif()
|
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
|
# MSVC has a "latest" flag, which always uses the newest standard
|
||||||
# when available. GCC and Clang posess no such flag, and must be
|
# when available. GCC and Clang posess no such flag, and must be
|
||||||
# manually enforced. CMake, curiously, also doesn't have a "latest"
|
# manually enforced. CMake, curiously, also doesn't have a "latest"
|
||||||
|
@ -110,9 +117,6 @@ endif()
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Force shared libs off" FORCE)
|
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Force shared libs off" FORCE)
|
||||||
set(BUILD_STATIC_LIBS ON CACHE BOOL "Force static libs on" FORCE)
|
set(BUILD_STATIC_LIBS ON CACHE BOOL "Force static libs on" FORCE)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/extern/sanitizers-cmake/cmake")
|
|
||||||
find_package(Sanitizers)
|
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64)
|
if (CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64)
|
||||||
set(METAFORCE_VECTOR_ISA "sse41" CACHE STRING "Vector ISA to build for (sse2, sse3, sse41, avx, avx2)")
|
set(METAFORCE_VECTOR_ISA "sse41" CACHE STRING "Vector ISA to build for (sse2, sse3, sse41, avx, avx2)")
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -240,15 +244,20 @@ else()
|
||||||
add_compile_options(-fno-asynchronous-unwind-tables)
|
add_compile_options(-fno-asynchronous-unwind-tables)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(METAFORCE_MSAN)
|
if (METAFORCE_ASAN)
|
||||||
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-stdlib=libc++> -fsanitize=address
|
||||||
|
-fsanitize-address-use-after-scope)
|
||||||
|
add_link_options($<$<COMPILE_LANGUAGE:CXX>:-stdlib=libc++> -fsanitize=address
|
||||||
|
-fsanitize-address-use-after-scope)
|
||||||
|
elseif(METAFORCE_MSAN)
|
||||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-stdlib=libc++> -fsanitize=memory
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-stdlib=libc++> -fsanitize=memory
|
||||||
-fsanitize-memory-track-origins -fsanitize-recover=all)
|
-fsanitize-memory-track-origins -fsanitize-recover=all)
|
||||||
endif()
|
endif()
|
||||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
|
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
|
||||||
-Wall -Wno-multichar -Werror=implicit-fallthrough
|
-Wall -Wno-multichar
|
||||||
-Wno-unused-variable -Wno-unused-result -Wno-unused-but-set-variable
|
-Wno-unused-variable -Wno-unused-result -Wno-unused-but-set-variable
|
||||||
-Wno-unused-function -Wno-sign-compare -Wno-unknown-pragmas -Werror)
|
-Wno-unused-function -Wno-sign-compare -Wno-unknown-pragmas)
|
||||||
# doesn't work with generator expression in add_compile_options?
|
# doesn't work with generator expression in add_compile_options?
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
|
||||||
add_compile_options(-Wno-unknown-warning-option -Wno-unused-private-field)
|
add_compile_options(-Wno-unknown-warning-option -Wno-unused-private-field)
|
||||||
|
@ -259,10 +268,6 @@ else()
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_compile_options(-Wno-error=deprecated-declarations
|
add_compile_options(-Wno-error=deprecated-declarations
|
||||||
$<$<CONFIG:Release>:-flto=thin>)
|
$<$<CONFIG:Release>:-flto=thin>)
|
||||||
if (METAFORCE_ASAN)
|
|
||||||
add_compile_options(-fsanitize=address -fsanitize-address-use-after-scope)
|
|
||||||
add_link_options(-fsanitize=address -fsanitize-address-use-after-scope)
|
|
||||||
endif ()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
@ -341,161 +346,18 @@ if(USE_LD_GOLD)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Add discord-rpc here
|
find_package(ZLIB REQUIRED)
|
||||||
if(NOT GEKKO AND NOT NX)
|
set(ZLIB_LIBRARIES ZLIB::ZLIB CACHE STRING "zlib libraries" FORCE)
|
||||||
set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/discord-rpc)
|
|
||||||
if (NOT CMAKE_INSTALL_LIBDIR)
|
|
||||||
set(CMAKE_INSTALL_LIBDIR ${CMAKE_BINARY_DIR}/fake-prefix)
|
|
||||||
endif()
|
|
||||||
add_subdirectory(extern/discord-rpc/src EXCLUDE_FROM_ALL)
|
|
||||||
target_include_directories(discord-rpc PRIVATE extern/rapidjson/include PUBLIC extern/discord-rpc/include)
|
|
||||||
if (APPLE)
|
|
||||||
# remove their nasty hack
|
|
||||||
get_target_property(DISCORD_LINK_LIBRARIES discord-rpc INTERFACE_LINK_LIBRARIES)
|
|
||||||
list(REMOVE_ITEM DISCORD_LINK_LIBRARIES "-framework AppKit, -mmacosx-version-min=10.10")
|
|
||||||
set_target_properties(discord-rpc PROPERTIES INTERFACE_LINK_LIBRARIES "${DISCORD_LINK_LIBRARIES}")
|
|
||||||
endif ()
|
|
||||||
if (UNIX)
|
|
||||||
# remove another nasty hack
|
|
||||||
get_target_property(DISCORD_COMPILE_OPTIONS discord-rpc COMPILE_OPTIONS)
|
|
||||||
list(REMOVE_ITEM DISCORD_COMPILE_OPTIONS "-g")
|
|
||||||
set_target_properties(discord-rpc PROPERTIES COMPILE_OPTIONS "${DISCORD_COMPILE_OPTIONS}")
|
|
||||||
endif ()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT WIN32)
|
|
||||||
find_package(ZLIB REQUIRED)
|
|
||||||
set(ZLIB_LIBRARIES ZLIB::ZLIB CACHE STRING "zlib libraries" FORCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(BUILD_ATHENA "Build Athena libraries from source" ON)
|
|
||||||
if (WIN32 OR APPLE)
|
|
||||||
# Default to binary atdna on Windows & macOS
|
|
||||||
option(BUILD_ATDNA "Build atdna utility from source (requires LLVM)" OFF)
|
|
||||||
else ()
|
|
||||||
option(BUILD_ATDNA "Build atdna utility from source (requires LLVM)" ON)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (NOT BUILD_ATDNA)
|
|
||||||
set(ATHENA_BASE_URL "https://github.com/libAthena/athena/releases/download/latest")
|
|
||||||
if (WIN32)
|
|
||||||
set(ATHENA_EXT 7z)
|
|
||||||
else()
|
|
||||||
set(ATHENA_EXT tar.gz)
|
|
||||||
endif()
|
|
||||||
include(FetchContent)
|
|
||||||
FetchContent_Declare(athena-host
|
|
||||||
URL "${ATHENA_BASE_URL}/athena-${PLATFORM_NAME}-${ATHENA_HOST_ARCH}.${ATHENA_EXT}")
|
|
||||||
message(STATUS "Fetching atdna host binary")
|
|
||||||
FetchContent_Populate(athena-host)
|
|
||||||
include(${athena-host_SOURCE_DIR}/lib/cmake/atdna/atdnaConfig.cmake)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (BUILD_ATHENA OR BUILD_ATDNA)
|
|
||||||
add_subdirectory(extern/athena EXCLUDE_FROM_ALL)
|
|
||||||
else()
|
|
||||||
if (ATHENA_ARCH STREQUAL ATHENA_HOST_ARCH)
|
|
||||||
set(athena_SOURCE_DIR "${athena-host_SOURCE_DIR}")
|
|
||||||
else()
|
|
||||||
FetchContent_Declare(athena
|
|
||||||
URL "${ATHENA_BASE_URL}/athena-${PLATFORM_NAME}-${ATHENA_ARCH}.tar.gz")
|
|
||||||
FetchContent_Populate(athena)
|
|
||||||
endif()
|
|
||||||
include(${athena_SOURCE_DIR}/lib/cmake/athena/AthenaConfig.cmake)
|
|
||||||
include(${athena_SOURCE_DIR}/lib/cmake/lzokay/lzokayConfig.cmake)
|
|
||||||
add_library(lzokay ALIAS lzokay::lzokay)
|
|
||||||
include(extern/athena/atdna/atdnaHelpers.cmake)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
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(extern/nod EXCLUDE_FROM_ALL)
|
|
||||||
add_subdirectory(extern/boo EXCLUDE_FROM_ALL)
|
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
ExternalProject_Add(shaderc
|
ExternalProject_Add(bintoc
|
||||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/hecl/shaderc"
|
SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/bintoc"
|
||||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||||
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --config Release --target install)
|
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --config Release --target install)
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/bintoc/bintocHelpers.cmake)
|
||||||
|
|
||||||
function(shaderc out)
|
add_subdirectory(extern)
|
||||||
if(IS_ABSOLUTE ${out})
|
|
||||||
set(theOut ${out})
|
|
||||||
else()
|
|
||||||
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
|
|
||||||
endif()
|
|
||||||
unset(theInsList)
|
|
||||||
foreach(in ${ARGN})
|
|
||||||
if(IS_ABSOLUTE ${in})
|
|
||||||
list(APPEND theInsList ${in})
|
|
||||||
else()
|
|
||||||
list(APPEND theInsList ${CMAKE_CURRENT_SOURCE_DIR}/${in})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
get_filename_component(outDir ${theOut} DIRECTORY)
|
|
||||||
file(MAKE_DIRECTORY ${outDir})
|
|
||||||
file(RELATIVE_PATH outRel ${CMAKE_BINARY_DIR} ${theOut})
|
|
||||||
ExternalProject_Get_Property(shaderc INSTALL_DIR)
|
|
||||||
add_custom_command(OUTPUT ${theOut}.cpp ${theOut}.hpp
|
|
||||||
COMMAND "${INSTALL_DIR}/bin/shaderc" ARGS -o ${theOut} ${theInsList}
|
|
||||||
DEPENDS ${theInsList} shaderc
|
|
||||||
COMMENT "Compiling shader ${outRel}.shader")
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
include(hecl/ApplicationTools.cmake)
|
|
||||||
add_subdirectory(Shaders)
|
|
||||||
add_subdirectory(imgui)
|
add_subdirectory(imgui)
|
||||||
add_subdirectory(extern/libSquish EXCLUDE_FROM_ALL)
|
|
||||||
add_subdirectory(extern/libpng EXCLUDE_FROM_ALL)
|
|
||||||
add_subdirectory(extern/libjpeg-turbo EXCLUDE_FROM_ALL)
|
|
||||||
add_subdirectory(hecl EXCLUDE_FROM_ALL)
|
|
||||||
add_subdirectory(extern/zeus 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)
|
|
||||||
|
|
||||||
bintoc(CModelShaders.common.glsl.cpp Shaders/CModelShaders.common.glsl CMODELSHADERS_COMMON_GLSL)
|
|
||||||
bintoc(CModelShaders.vert.glsl.cpp Shaders/CModelShaders.vert.glsl CMODELSHADERS_VERT_GLSL)
|
|
||||||
bintoc(CModelShaders.frag.glsl.cpp Shaders/CModelShaders.frag.glsl CMODELSHADERS_FRAG_GLSL)
|
|
||||||
bintoc(CModelShaders.common.hlsl.cpp Shaders/CModelShaders.common.hlsl CMODELSHADERS_COMMON_HLSL)
|
|
||||||
bintoc(CModelShaders.vert.hlsl.cpp Shaders/CModelShaders.vert.hlsl CMODELSHADERS_VERT_HLSL)
|
|
||||||
bintoc(CModelShaders.frag.hlsl.cpp Shaders/CModelShaders.frag.hlsl CMODELSHADERS_FRAG_HLSL)
|
|
||||||
bintoc(CModelShaders.common.metal.cpp Shaders/CModelShaders.common.metal CMODELSHADERS_COMMON_METAL)
|
|
||||||
bintoc(CModelShaders.vert.metal.cpp Shaders/CModelShaders.vert.metal CMODELSHADERS_VERT_METAL)
|
|
||||||
bintoc(CModelShaders.frag.metal.cpp Shaders/CModelShaders.frag.metal CMODELSHADERS_FRAG_METAL)
|
|
||||||
add_library(CModelShaders
|
|
||||||
CModelShaders.common.glsl.cpp CModelShaders.vert.glsl.cpp CModelShaders.frag.glsl.cpp
|
|
||||||
CModelShaders.common.hlsl.cpp CModelShaders.vert.hlsl.cpp CModelShaders.frag.hlsl.cpp
|
|
||||||
CModelShaders.common.metal.cpp CModelShaders.vert.metal.cpp CModelShaders.frag.metal.cpp)
|
|
||||||
target_link_libraries(CModelShaders PUBLIC zeus)
|
|
||||||
target_link_libraries(shader_CModelShaders PUBLIC CModelShaders)
|
|
||||||
|
|
||||||
if(NOT TARGET atdna)
|
if(NOT TARGET atdna)
|
||||||
# Import native atdna if cross-compiling
|
# Import native atdna if cross-compiling
|
||||||
|
@ -505,86 +367,64 @@ if(NOT TARGET atdna)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(extern/amuse EXCLUDE_FROM_ALL)
|
|
||||||
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)
|
|
||||||
add_subdirectory(extern/kabufuda EXCLUDE_FROM_ALL)
|
|
||||||
|
|
||||||
add_subdirectory(extern/jbus EXCLUDE_FROM_ALL)
|
|
||||||
set(JBUS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/jbus/include)
|
|
||||||
|
|
||||||
add_subdirectory(NESEmulator EXCLUDE_FROM_ALL)
|
add_subdirectory(NESEmulator EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(Runtime)
|
add_subdirectory(Runtime)
|
||||||
add_subdirectory(mpcksum EXCLUDE_FROM_ALL)
|
|
||||||
add_subdirectory(gbalink EXCLUDE_FROM_ALL)
|
add_subdirectory(gbalink EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(visigen)
|
|
||||||
|
|
||||||
add_dependencies(hecl visigen)
|
|
||||||
|
|
||||||
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)
|
|
||||||
elseif(Qt5Widgets_FOUND)
|
|
||||||
message(STATUS "Qt5 found, metaforce-gui will be built")
|
|
||||||
add_subdirectory(metaforce-gui)
|
|
||||||
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)
|
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_BINARY_DIR}/version.h)
|
||||||
|
|
||||||
# Packaging logic
|
# Packaging logic
|
||||||
list(APPEND BINARY_TARGETS metaforce hecl visigen)
|
function(get_target_output_name target result_var)
|
||||||
set(DSYM_ONLY_TARGETS "")
|
get_target_property(output_name ${target} OUTPUT_NAME)
|
||||||
|
if (output_name STREQUAL "output_name-NOTFOUND")
|
||||||
|
set(${result_var} "${target}" PARENT_SCOPE)
|
||||||
|
else ()
|
||||||
|
set(${result_var} "${output_name}" PARENT_SCOPE)
|
||||||
|
endif ()
|
||||||
|
endfunction()
|
||||||
|
function(get_target_prefix target result_var)
|
||||||
|
set(${result_var} "" PARENT_SCOPE)
|
||||||
|
if (APPLE)
|
||||||
|
# Have to recreate some bundle logic here, since CMake can't tell us
|
||||||
|
get_target_property(is_bundle ${target} MACOSX_BUNDLE)
|
||||||
|
if (is_bundle)
|
||||||
|
get_target_output_name(${target} output_name)
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL Darwin)
|
||||||
|
set(${result_var} "${output_name}.app/Contents/MacOS/" PARENT_SCOPE)
|
||||||
|
else ()
|
||||||
|
set(${result_var} "${output_name}.app/" PARENT_SCOPE)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
endfunction()
|
||||||
|
list(APPEND BINARY_TARGETS metaforce)
|
||||||
|
set(EXTRA_TARGETS "")
|
||||||
if (TARGET crashpad_handler)
|
if (TARGET crashpad_handler)
|
||||||
list(APPEND BINARY_TARGETS crashpad_handler)
|
list(APPEND EXTRA_TARGETS crashpad_handler)
|
||||||
endif ()
|
endif ()
|
||||||
set(BIN_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
set(BIN_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||||
if (TARGET metaforce-gui)
|
install(TARGETS ${BINARY_TARGETS} ${EXTRA_TARGETS} DESTINATION ${BIN_PREFIX})
|
||||||
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)
|
if (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
|
||||||
foreach (target IN LISTS BINARY_TARGETS DSYM_ONLY_TARGETS)
|
set(DEBUG_FILES_LIST "")
|
||||||
|
foreach (target IN LISTS BINARY_TARGETS EXTRA_TARGETS)
|
||||||
|
get_target_output_name(${target} output_name)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
install(FILES $<TARGET_PDB_FILE:${target}> DESTINATION ${BIN_PREFIX} OPTIONAL)
|
install(FILES $<TARGET_PDB_FILE:${target}> DESTINATION ${BIN_PREFIX} OPTIONAL)
|
||||||
elseif (APPLE)
|
elseif (APPLE)
|
||||||
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND dsymutil ${target})")
|
get_target_prefix(${target} target_prefix)
|
||||||
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND strip -S ${target})")
|
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND rm -fr \"$<TARGET_FILE_NAME:${target}>.dSYM\")")
|
||||||
elseif (UNIX)
|
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND dsymutil \"${target_prefix}$<TARGET_FILE_NAME:${target}>\")")
|
||||||
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND objcopy --only-keep-debug ${target} ${target}.dbg)")
|
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND strip -S \"${target_prefix}$<TARGET_FILE_NAME:${target}>\")")
|
||||||
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND objcopy --strip-debug --add-gnu-debuglink=${target}.dbg ${target})")
|
if (NOT target_prefix STREQUAL "")
|
||||||
|
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND mv \"${target_prefix}$<TARGET_FILE_NAME:${target}>.dSYM\" .)")
|
||||||
endif ()
|
endif ()
|
||||||
|
elseif (UNIX)
|
||||||
|
get_target_prefix(${target} target_prefix)
|
||||||
|
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND objcopy --only-keep-debug \"${target_prefix}$<TARGET_FILE_NAME:${target}>\" \"${target_prefix}$<TARGET_FILE_NAME:${target}>.dbg\")")
|
||||||
|
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND objcopy --strip-debug --add-gnu-debuglink=$<TARGET_FILE_NAME:${target}>.dbg \"${target_prefix}$<TARGET_FILE_NAME:${target}>\")")
|
||||||
|
endif ()
|
||||||
|
list(APPEND DEBUG_FILES_LIST "${output_name}")
|
||||||
endforeach ()
|
endforeach ()
|
||||||
set(DEBUG_FILES_LIST ${BINARY_TARGETS} ${DSYM_ONLY_TARGETS})
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
list(TRANSFORM DEBUG_FILES_LIST APPEND ".pdb")
|
list(TRANSFORM DEBUG_FILES_LIST APPEND ".pdb")
|
||||||
list(JOIN DEBUG_FILES_LIST " " DEBUG_FILES)
|
list(JOIN DEBUG_FILES_LIST " " DEBUG_FILES)
|
||||||
|
@ -599,3 +439,13 @@ if (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
|
||||||
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND tar -I \"xz -9 -T0\" -cvf \"${CMAKE_INSTALL_PREFIX}/debug.tar.xz\" ${DEBUG_FILES})")
|
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND tar -I \"xz -9 -T0\" -cvf \"${CMAKE_INSTALL_PREFIX}/debug.tar.xz\" ${DEBUG_FILES})")
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
foreach (target IN LISTS BINARY_TARGETS)
|
||||||
|
get_target_prefix(${target} target_prefix)
|
||||||
|
foreach (extra_target IN LISTS EXTRA_TARGETS)
|
||||||
|
get_target_prefix(${extra_target} extra_prefix)
|
||||||
|
if (NOT "${target_prefix}" STREQUAL "${extra_prefix}")
|
||||||
|
# Copy extra target to target prefix
|
||||||
|
install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND cp \"${extra_prefix}$<TARGET_FILE_NAME:${extra_target}>\" \"${target_prefix}$<TARGET_FILE_NAME:${extra_target}>\")")
|
||||||
|
endif ()
|
||||||
|
endforeach ()
|
||||||
|
endforeach ()
|
||||||
|
|
|
@ -29,7 +29,11 @@
|
||||||
"generator": "Ninja",
|
"generator": "Ninja",
|
||||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install"
|
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install",
|
||||||
|
"USE_LTO": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"vendor": {
|
"vendor": {
|
||||||
"microsoft.com/VisualStudioSettings/CMake/1.0": {
|
"microsoft.com/VisualStudioSettings/CMake/1.0": {
|
||||||
|
@ -83,7 +87,39 @@
|
||||||
"inherits": [
|
"inherits": [
|
||||||
"relwithdebinfo",
|
"relwithdebinfo",
|
||||||
"linux-clang"
|
"linux-clang"
|
||||||
]
|
],
|
||||||
|
"cacheVariables": {
|
||||||
|
"USE_LTO": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-debug-asan",
|
||||||
|
"displayName": "Linux (Clang) Debug w/ ASAN",
|
||||||
|
"inherits": [
|
||||||
|
"linux-clang-debug"
|
||||||
|
],
|
||||||
|
"cacheVariables": {
|
||||||
|
"METAFORCE_ASAN": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-relwithdebinfo-asan",
|
||||||
|
"displayName": "Linux (Clang) RelWithDebInfo w/ ASAN",
|
||||||
|
"inherits": [
|
||||||
|
"linux-clang-relwithdebinfo"
|
||||||
|
],
|
||||||
|
"cacheVariables": {
|
||||||
|
"METAFORCE_ASAN": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "windows-msvc",
|
"name": "windows-msvc",
|
||||||
|
@ -102,7 +138,11 @@
|
||||||
"type": "FILEPATH",
|
"type": "FILEPATH",
|
||||||
"value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
"value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
||||||
},
|
},
|
||||||
"VCPKG_TARGET_TRIPLET": "x64-windows-static"
|
"VCPKG_TARGET_TRIPLET": "x64-windows-static",
|
||||||
|
"VCPKG_SETUP_CMAKE_PROGRAM_PATH": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"vendor": {
|
"vendor": {
|
||||||
"microsoft.com/VisualStudioSettings/CMake/1.0": {
|
"microsoft.com/VisualStudioSettings/CMake/1.0": {
|
||||||
|
@ -193,6 +233,52 @@
|
||||||
"macos-default"
|
"macos-default"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ios-default",
|
||||||
|
"displayName": "iOS",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "ios.toolchain.cmake",
|
||||||
|
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install",
|
||||||
|
"PLATFORM": "OS64",
|
||||||
|
"DEPLOYMENT_TARGET": "13.0",
|
||||||
|
"ENABLE_BITCODE": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vendor": {
|
||||||
|
"microsoft.com/VisualStudioSettings/CMake/1.0": {
|
||||||
|
"hostOS": [
|
||||||
|
"macOS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tvos-default",
|
||||||
|
"displayName": "tvOS",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "ios.toolchain.cmake",
|
||||||
|
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install",
|
||||||
|
"PLATFORM": "TVOS",
|
||||||
|
"DEPLOYMENT_TARGET": "14.5",
|
||||||
|
"ENABLE_BITCODE": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vendor": {
|
||||||
|
"microsoft.com/VisualStudioSettings/CMake/1.0": {
|
||||||
|
"hostOS": [
|
||||||
|
"macOS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "x-linux-ci",
|
"name": "x-linux-ci",
|
||||||
"hidden": true,
|
"hidden": true,
|
||||||
|
@ -232,9 +318,11 @@
|
||||||
"macos-default-relwithdebinfo"
|
"macos-default-relwithdebinfo"
|
||||||
],
|
],
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_C_COMPILER_LAUNCHER": "buildcache",
|
"CMAKE_OSX_ARCHITECTURES": "arm64;x86_64",
|
||||||
"CMAKE_CXX_COMPILER_LAUNCHER": "buildcache",
|
"IMGUI_USE_FREETYPE": {
|
||||||
"CMAKE_OSX_ARCHITECTURES": "arm64;x86_64"
|
"type": "BOOL",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -295,6 +383,18 @@
|
||||||
"description": "Linux (Clang) release build with debug info",
|
"description": "Linux (Clang) release build with debug info",
|
||||||
"displayName": "Linux (Clang) RelWithDebInfo"
|
"displayName": "Linux (Clang) RelWithDebInfo"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-debug-asan",
|
||||||
|
"configurePreset": "linux-clang-debug-asan",
|
||||||
|
"description": "Linux (Clang) debug build w/ ASAN",
|
||||||
|
"displayName": "Linux (Clang) Debug w/ ASAN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-relwithdebinfo-asan",
|
||||||
|
"configurePreset": "linux-clang-relwithdebinfo-asan",
|
||||||
|
"description": "Linux (Clang) release build with debug info w/ ASAN",
|
||||||
|
"displayName": "Linux (Clang) RelWithDebInfo w/ ASAN"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "macos-default-debug",
|
"name": "macos-default-debug",
|
||||||
"configurePreset": "macos-default-debug",
|
"configurePreset": "macos-default-debug",
|
||||||
|
@ -307,6 +407,24 @@
|
||||||
"description": "macOS release build with debug info",
|
"description": "macOS release build with debug info",
|
||||||
"displayName": "macOS RelWithDebInfo"
|
"displayName": "macOS RelWithDebInfo"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ios-default",
|
||||||
|
"configurePreset": "ios-default",
|
||||||
|
"description": "iOS release build with debug info",
|
||||||
|
"displayName": "iOS RelWithDebInfo",
|
||||||
|
"targets": [
|
||||||
|
"metaforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tvos-default",
|
||||||
|
"configurePreset": "tvos-default",
|
||||||
|
"description": "tvOS release build with debug info",
|
||||||
|
"displayName": "tvOS RelWithDebInfo",
|
||||||
|
"targets": [
|
||||||
|
"metaforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "windows-msvc-debug",
|
"name": "windows-msvc-debug",
|
||||||
"configurePreset": "windows-msvc-debug",
|
"configurePreset": "windows-msvc-debug",
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
message(STATUS "32-bit asset name map not found; downloading to '${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin'")
|
|
||||||
file(DOWNLOAD "https://axiodl.com/files/AssetNameMap32.dat"
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin SHOW_PROGRESS EXPECTED_HASH SHA1=90b4e941c192eef41c81e60314f348bc787d1336)
|
|
|
@ -1,3 +0,0 @@
|
||||||
message(STATUS "64-bit asset name map not found; downloading to '${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin'")
|
|
||||||
file(DOWNLOAD "https://axiodl.com/files/AssetNameMap64.dat"
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin SHOW_PROGRESS EXPECTED_HASH SHA1=e49c03c9fff66adccec7af8120dda091636513e2)
|
|
|
@ -1,95 +0,0 @@
|
||||||
#include "AssetNameMap.hpp"
|
|
||||||
#include "athena/Compression.hpp"
|
|
||||||
#include "athena/MemoryReader.hpp"
|
|
||||||
|
|
||||||
extern "C" const uint8_t ASSET_NAME_MP32[];
|
|
||||||
extern "C" const size_t ASSET_NAME_MP32_SZ;
|
|
||||||
extern "C" const size_t ASSET_NAME_MP32_DECOMPRESSED_SZ;
|
|
||||||
extern "C" const uint8_t ASSET_NAME_MP64[];
|
|
||||||
extern "C" const size_t ASSET_NAME_MP64_SZ;
|
|
||||||
extern "C" const size_t ASSET_NAME_MP64_DECOMPRESSED_SZ;
|
|
||||||
|
|
||||||
namespace DataSpec::AssetNameMap {
|
|
||||||
logvisor::Module Log("AssetNameMap");
|
|
||||||
|
|
||||||
struct SAsset {
|
|
||||||
std::string name;
|
|
||||||
std::string directory;
|
|
||||||
hecl::FourCC type;
|
|
||||||
SAsset() = default;
|
|
||||||
SAsset(const hecl::FourCC& typeIn, athena::io::IStreamReader& in) : type(typeIn) {
|
|
||||||
uint32_t nameLen = in.readUint32Big();
|
|
||||||
name = in.readString(nameLen);
|
|
||||||
uint32_t dirLen = in.readUint32Big();
|
|
||||||
directory = in.readString(dirLen);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::unordered_map<uint64_t, SAsset> g_AssetNameMap;
|
|
||||||
static bool g_AssetNameMapInit = false;
|
|
||||||
|
|
||||||
void LoadAssetMap(athena::io::MemoryReader& ar) {
|
|
||||||
if (!ar.hasError()) {
|
|
||||||
hecl::FourCC magic;
|
|
||||||
if (ar.length() >= 4)
|
|
||||||
ar.readBytesToBuf(&magic, 4);
|
|
||||||
if (magic != FOURCC('AIDM'))
|
|
||||||
Log.report(
|
|
||||||
logvisor::Warning,
|
|
||||||
FMT_STRING("Unable to load asset map; Assets will not have proper filenames for most files."));
|
|
||||||
else {
|
|
||||||
uint32_t assetCount = ar.readUint32Big();
|
|
||||||
g_AssetNameMap.reserve(assetCount);
|
|
||||||
for (uint32_t i = 0; i < assetCount; ++i) {
|
|
||||||
hecl::FourCC type;
|
|
||||||
ar.readBytesToBuf(&type, 4);
|
|
||||||
uint64_t id = ar.readUint64Big();
|
|
||||||
g_AssetNameMap[id] = SAsset(type, ar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitAssetNameMap() {
|
|
||||||
if (g_AssetNameMapInit)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Log.report(logvisor::Info, FMT_STRING("Initializing asset name database..."));
|
|
||||||
|
|
||||||
/* First load the 32bit map for MP1/2 */
|
|
||||||
if (ASSET_NAME_MP32_DECOMPRESSED_SZ != 0u) {
|
|
||||||
auto* decompressed = new uint8_t[ASSET_NAME_MP32_DECOMPRESSED_SZ];
|
|
||||||
athena::io::Compression::decompressZlib(ASSET_NAME_MP32, ASSET_NAME_MP32_SZ, decompressed,
|
|
||||||
ASSET_NAME_MP32_DECOMPRESSED_SZ);
|
|
||||||
athena::io::MemoryReader ar(decompressed, ASSET_NAME_MP32_DECOMPRESSED_SZ);
|
|
||||||
LoadAssetMap(ar);
|
|
||||||
delete[](decompressed);
|
|
||||||
} else {
|
|
||||||
Log.report(
|
|
||||||
logvisor::Warning,
|
|
||||||
FMT_STRING("AssetNameMap32 unavailable; Assets will not have proper filenames for most files."));
|
|
||||||
}
|
|
||||||
/* Now load the 64bit map for MP3 */
|
|
||||||
if (ASSET_NAME_MP64_DECOMPRESSED_SZ != 0u) {
|
|
||||||
auto* decompressed = new uint8_t[ASSET_NAME_MP64_DECOMPRESSED_SZ];
|
|
||||||
athena::io::Compression::decompressZlib(ASSET_NAME_MP64, ASSET_NAME_MP64_SZ, decompressed,
|
|
||||||
ASSET_NAME_MP64_DECOMPRESSED_SZ);
|
|
||||||
athena::io::MemoryReader ar(decompressed, ASSET_NAME_MP64_DECOMPRESSED_SZ);
|
|
||||||
LoadAssetMap(ar);
|
|
||||||
delete[](decompressed);
|
|
||||||
} else {
|
|
||||||
Log.report(
|
|
||||||
logvisor::Warning,
|
|
||||||
FMT_STRING("AssetNameMap64 unavailable; Assets will not have proper filenames for most files."));
|
|
||||||
}
|
|
||||||
g_AssetNameMapInit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string* TranslateIdToName(const UniqueID32& id) {
|
|
||||||
if (g_AssetNameMap.find(id.toUint64()) == g_AssetNameMap.cend())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return &g_AssetNameMap[id.toUint64()].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::AssetNameMap
|
|
|
@ -1,11 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <string>
|
|
||||||
#include "DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::AssetNameMap {
|
|
||||||
void InitAssetNameMap();
|
|
||||||
const std::string* TranslateIdToName(const UniqueID32&);
|
|
||||||
const std::string* TranslateIdToName(const UniqueID64&);
|
|
||||||
} // namespace DataSpec::AssetNameMap
|
|
|
@ -1,9 +0,0 @@
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
extern "C" const uint8_t ASSET_NAME_MP32[] = {0};
|
|
||||||
extern "C" const size_t ASSET_NAME_MP32_SZ = 0;
|
|
||||||
extern "C" const size_t ASSET_NAME_MP32_DECOMPRESSED_SZ = 0;
|
|
||||||
extern "C" const uint8_t ASSET_NAME_MP64[] = {0};
|
|
||||||
extern "C" const size_t ASSET_NAME_MP64_SZ = 0;
|
|
||||||
extern "C" const size_t ASSET_NAME_MP64_DECOMPRESSED_SZ = 0;
|
|
|
@ -1,22 +0,0 @@
|
||||||
#include <cstdint>
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
#include "BlenderSupport.hpp"
|
|
||||||
|
|
||||||
extern "C" uint8_t RETRO_MASTER_SHADER[];
|
|
||||||
extern "C" size_t RETRO_MASTER_SHADER_SZ;
|
|
||||||
|
|
||||||
namespace DataSpec::Blender {
|
|
||||||
|
|
||||||
bool BuildMasterShader(const hecl::ProjectPath& path) {
|
|
||||||
hecl::blender::Connection& conn = hecl::blender::Connection::SharedConnection();
|
|
||||||
if (!conn.createBlend(path, hecl::blender::BlendType::None))
|
|
||||||
return false;
|
|
||||||
{
|
|
||||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
|
||||||
os << std::string_view((char*)RETRO_MASTER_SHADER, RETRO_MASTER_SHADER_SZ);
|
|
||||||
os << "make_master_shader_library()\n"sv;
|
|
||||||
}
|
|
||||||
return conn.saveBlend();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::Blender
|
|
|
@ -1,9 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <hecl/hecl.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::Blender {
|
|
||||||
|
|
||||||
bool BuildMasterShader(const hecl::ProjectPath& path);
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,91 +0,0 @@
|
||||||
# Assembles a source/header pair list for use in a DNA library
|
|
||||||
macro(make_dnalist)
|
|
||||||
file(RELATIVE_PATH subdir "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_LIST_DIR}")
|
|
||||||
set(CMAKE_CURRENT_LIST_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_LIST_BINARY_DIR}")
|
|
||||||
foreach (type ${ARGN})
|
|
||||||
get_filename_component(dir ${type} DIRECTORY)
|
|
||||||
if (dir)
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_LIST_BINARY_DIR}/${dir}")
|
|
||||||
set(dir "${dir}/")
|
|
||||||
endif ()
|
|
||||||
get_filename_component(name ${type} NAME)
|
|
||||||
list(APPEND DNA_SOURCES "${subdir}/${dir}atdna_${name}.cpp")
|
|
||||||
list(APPEND DNA_HEADERS "${subdir}/${dir}${name}.hpp")
|
|
||||||
endforeach ()
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
# Assembles source files together for the main DataSpecCommon library
|
|
||||||
macro(dataspec_add_list rel_path a_list)
|
|
||||||
unset(tmp_list)
|
|
||||||
foreach (path IN LISTS ${a_list})
|
|
||||||
if (IS_ABSOLUTE ${path})
|
|
||||||
list(APPEND tmp_list "${path}")
|
|
||||||
else ()
|
|
||||||
list(APPEND tmp_list "${rel_path}/${path}")
|
|
||||||
endif ()
|
|
||||||
endforeach (path)
|
|
||||||
set(${a_list} "${tmp_list}")
|
|
||||||
endmacro(dataspec_add_list)
|
|
||||||
|
|
||||||
# Each game's DNA library
|
|
||||||
unset(DNA_SOURCES)
|
|
||||||
unset(DNA_HEADERS)
|
|
||||||
include(DNACommon/CMakeLists.txt)
|
|
||||||
include(DNAMP1/CMakeLists.txt)
|
|
||||||
include(DNAMP2/CMakeLists.txt)
|
|
||||||
include(DNAMP3/CMakeLists.txt)
|
|
||||||
|
|
||||||
# Embed master shader script
|
|
||||||
bintoc(RetroMasterShader.cpp Blender/RetroMasterShader.py RETRO_MASTER_SHADER)
|
|
||||||
|
|
||||||
# Download asset name databases
|
|
||||||
add_custom_command(OUTPUT AssetNameMap32.bin COMMAND ${CMAKE_COMMAND} ARGS -P
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/AssetMap32Download.cmake)
|
|
||||||
bintoc_compress(AssetNameMap32.cpp ${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin ASSET_NAME_MP32)
|
|
||||||
|
|
||||||
add_custom_command(OUTPUT AssetNameMap64.bin COMMAND ${CMAKE_COMMAND} ARGS -P
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/AssetMap64Download.cmake)
|
|
||||||
bintoc_compress(AssetNameMap64.cpp ${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin ASSET_NAME_MP64)
|
|
||||||
|
|
||||||
# Each game's DataSpec implementation
|
|
||||||
add_library(RetroDataSpec
|
|
||||||
SpecBase.cpp
|
|
||||||
${DNACOMMON_SOURCES}
|
|
||||||
SpecMP1.cpp
|
|
||||||
${DNAMP1_SOURCES}
|
|
||||||
${ScriptObjectsMP1_SOURCES}
|
|
||||||
${DNAMP1_SFX_SOURCES}
|
|
||||||
SpecMP2.cpp
|
|
||||||
${DNAMP2_SOURCES}
|
|
||||||
SpecMP3.cpp
|
|
||||||
${DNAMP3_SOURCES}
|
|
||||||
Blender/BlenderSupport.hpp
|
|
||||||
Blender/BlenderSupport.cpp
|
|
||||||
Blender/RetroMasterShader.py
|
|
||||||
AssetNameMap.hpp
|
|
||||||
AssetNameMap.cpp
|
|
||||||
RetroMasterShader.cpp)
|
|
||||||
add_library(AssetNameMap
|
|
||||||
AssetNameMap32.bin AssetNameMap32.cpp
|
|
||||||
AssetNameMap64.bin AssetNameMap64.cpp)
|
|
||||||
add_library(AssetNameMapNull
|
|
||||||
AssetNameMapNull.cpp)
|
|
||||||
|
|
||||||
get_target_property(HECL_INCLUDES hecl-full INCLUDE_DIRECTORIES)
|
|
||||||
target_include_directories(RetroDataSpec PUBLIC ${HECL_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR})
|
|
||||||
target_link_libraries(RetroDataSpec PUBLIC amuse zeus nod squish ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} lzokay logvisor)
|
|
||||||
if (COMMAND add_sanitizers)
|
|
||||||
add_sanitizers(RetroDataSpec)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Resolve all DNA sources into target
|
|
||||||
list(LENGTH DNA_SOURCES count)
|
|
||||||
math(EXPR count "${count}-1")
|
|
||||||
foreach (i RANGE ${count})
|
|
||||||
list(GET DNA_SOURCES ${i} src)
|
|
||||||
list(GET DNA_HEADERS ${i} header)
|
|
||||||
target_atdna(RetroDataSpec ${src} ${header})
|
|
||||||
endforeach ()
|
|
||||||
|
|
||||||
add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_PROPERTY:RetroDataSpec,INCLUDE_DIRECTORIES>")
|
|
|
@ -1,282 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/ANCS.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/CMDL.hpp"
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/RigInverter.hpp"
|
|
||||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
|
||||||
#include "DataSpec/DNAMP1/ANCS.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/ANCS.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/CHAR.hpp"
|
|
||||||
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAANCS {
|
|
||||||
|
|
||||||
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
|
|
||||||
bool ReadANCSToBlender(hecl::blender::Token& btok, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
|
|
||||||
std::function<void(const char*)> fileChanged, bool force) {
|
|
||||||
auto& conn = btok.getBlenderConnection();
|
|
||||||
/* Extract character CMDL/CSKR/CINF first */
|
|
||||||
std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo;
|
|
||||||
ancs.getCharacterResInfo(chResInfo);
|
|
||||||
for (const auto& info : chResInfo) {
|
|
||||||
const nod::Node* node;
|
|
||||||
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, &node, true, false)) {
|
|
||||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
|
||||||
if (force || cmdlPath.isNone()) {
|
|
||||||
cmdlPath.makeDirChain(false);
|
|
||||||
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
|
|
||||||
fileChanged(bestName.c_str());
|
|
||||||
|
|
||||||
typename ANCSDNA::CSKRType cskr;
|
|
||||||
pakRouter.lookupAndReadDNA(info.cskr, cskr);
|
|
||||||
typename ANCSDNA::CINFType cinf;
|
|
||||||
pakRouter.lookupAndReadDNA(info.cinf, cinf);
|
|
||||||
using RigPair = std::pair<std::pair<typename PAKRouter::IDType, typename ANCSDNA::CSKRType*>,
|
|
||||||
std::pair<typename PAKRouter::IDType, typename ANCSDNA::CINFType*>>;
|
|
||||||
RigPair rigPair({info.cskr, &cskr}, {info.cinf, &cinf});
|
|
||||||
|
|
||||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
|
||||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>(
|
|
||||||
conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
|
|
||||||
|
|
||||||
conn.saveBlend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf, &node, true, false)) {
|
|
||||||
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
|
|
||||||
if (cinfPath.getPathType() == hecl::ProjectPath::Type::None) {
|
|
||||||
PAKEntryReadStream rs = cinfE->beginReadStream(*node);
|
|
||||||
ANCSDNA::CINFType::Extract(dataspec, rs, cinfPath, pakRouter, *cinfE, false, btok, fileChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract attachment CMDL/CSKR/CINFs first */
|
|
||||||
auto attRange = pakRouter.lookupCharacterAttachmentRigs(entry.id);
|
|
||||||
for (auto it = attRange.first; it != attRange.second; ++it) {
|
|
||||||
auto cinfid = it->second.first.cinf;
|
|
||||||
auto cmdlid = it->second.first.cmdl;
|
|
||||||
|
|
||||||
const nod::Node* node;
|
|
||||||
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(cmdlid, &node, true, false)) {
|
|
||||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
|
||||||
if (force || cmdlPath.isNone()) {
|
|
||||||
cmdlPath.makeDirChain(false);
|
|
||||||
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
|
|
||||||
fileChanged(bestName.c_str());
|
|
||||||
|
|
||||||
const auto* rp = pakRouter.lookupCMDLRigPair(cmdlid);
|
|
||||||
typename ANCSDNA::CSKRType cskr;
|
|
||||||
pakRouter.lookupAndReadDNA(rp->cskr, cskr);
|
|
||||||
typename ANCSDNA::CINFType cinf;
|
|
||||||
pakRouter.lookupAndReadDNA(rp->cinf, cinf);
|
|
||||||
using RigPair = std::pair<std::pair<typename PAKRouter::IDType, typename ANCSDNA::CSKRType*>,
|
|
||||||
std::pair<typename PAKRouter::IDType, typename ANCSDNA::CINFType*>>;
|
|
||||||
RigPair rigPair({rp->cskr, &cskr}, {rp->cinf, &cinf});
|
|
||||||
|
|
||||||
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
|
|
||||||
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>(
|
|
||||||
conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
|
|
||||||
|
|
||||||
conn.saveBlend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cinfid.isValid()) {
|
|
||||||
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(cinfid, &node, true, false)) {
|
|
||||||
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
|
|
||||||
if (cinfPath.getPathType() == hecl::ProjectPath::Type::None) {
|
|
||||||
PAKEntryReadStream rs = cinfE->beginReadStream(*node);
|
|
||||||
ANCSDNA::CINFType::Extract(dataspec, rs, cinfPath, pakRouter, *cinfE, false, btok, fileChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string bestName = pakRouter.getBestEntryName(entry);
|
|
||||||
fileChanged(bestName.c_str());
|
|
||||||
|
|
||||||
/* Establish ANCS blend */
|
|
||||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::Actor))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string firstName;
|
|
||||||
typename ANCSDNA::CINFType firstCinf;
|
|
||||||
{
|
|
||||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
|
||||||
|
|
||||||
os.format(FMT_STRING("import bpy\n"
|
|
||||||
"from mathutils import Vector\n"
|
|
||||||
"bpy.context.scene.name = '{}'\n"
|
|
||||||
"bpy.context.scene.hecl_mesh_obj = bpy.context.scene.name\n"
|
|
||||||
"\n"
|
|
||||||
"# Clear Scene\n"
|
|
||||||
"if len(bpy.data.collections):\n"
|
|
||||||
" bpy.data.collections.remove(bpy.data.collections[0])\n"
|
|
||||||
"\n"
|
|
||||||
"actor_data = bpy.context.scene.hecl_sact_data\n"
|
|
||||||
"arm_obj = None\n"),
|
|
||||||
pakRouter.getBestEntryName(entry));
|
|
||||||
|
|
||||||
std::unordered_set<typename PAKRouter::IDType> cinfsDone;
|
|
||||||
for (const auto& info : chResInfo) {
|
|
||||||
/* Provide data to add-on */
|
|
||||||
os.format(FMT_STRING("actor_subtype = actor_data.subtypes.add()\n"
|
|
||||||
"actor_subtype.name = '{}'\n\n"),
|
|
||||||
info.name);
|
|
||||||
|
|
||||||
/* Build CINF if needed */
|
|
||||||
if (cinfsDone.find(info.cinf) == cinfsDone.end()) {
|
|
||||||
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf, nullptr, true, false)) {
|
|
||||||
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
|
|
||||||
os.linkArmature(cinfPath.getAbsolutePath(), fmt::format(FMT_STRING("CINF_{}"), info.cinf));
|
|
||||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
|
||||||
" bpy.context.scene.collection.objects.link(obj)\n";
|
|
||||||
}
|
|
||||||
if (cinfsDone.empty()) {
|
|
||||||
firstName = ANCSDNA::CINFType::GetCINFArmatureName(info.cinf);
|
|
||||||
pakRouter.lookupAndReadDNA(info.cinf, firstCinf);
|
|
||||||
}
|
|
||||||
cinfsDone.insert(info.cinf);
|
|
||||||
}
|
|
||||||
os.format(FMT_STRING("arm_obj = bpy.data.objects['CINF_{}']\n"), info.cinf);
|
|
||||||
os << "actor_subtype.linked_armature = arm_obj.name\n";
|
|
||||||
|
|
||||||
/* Link CMDL */
|
|
||||||
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, nullptr, true, false)) {
|
|
||||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
|
||||||
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
|
|
||||||
|
|
||||||
/* Attach CMDL to CINF */
|
|
||||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
|
||||||
" bpy.context.scene.collection.objects.link(obj)\n"
|
|
||||||
"obj.parent = arm_obj\n"
|
|
||||||
"obj.parent_type = 'ARMATURE'\n"
|
|
||||||
"actor_subtype.linked_mesh = obj.name\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Link overlays */
|
|
||||||
for (const auto& overlay : info.overlays) {
|
|
||||||
os << "overlay = actor_subtype.overlays.add()\n";
|
|
||||||
os.format(FMT_STRING("overlay.name = '{}'\n"), overlay.first);
|
|
||||||
|
|
||||||
/* Link CMDL */
|
|
||||||
if (const typename PAKRouter::EntryType* cmdlE =
|
|
||||||
pakRouter.lookupEntry(overlay.second.first, nullptr, true, false)) {
|
|
||||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
|
||||||
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
|
|
||||||
|
|
||||||
/* Attach CMDL to CINF */
|
|
||||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
|
||||||
" bpy.context.scene.collection.objects.link(obj)\n"
|
|
||||||
"obj.parent = arm_obj\n"
|
|
||||||
"obj.parent_type = 'ARMATURE'\n"
|
|
||||||
"overlay.linked_mesh = obj.name\n\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Link attachments */
|
|
||||||
for (auto it = attRange.first; it != attRange.second; ++it) {
|
|
||||||
os << "attachment = actor_data.attachments.add()\n";
|
|
||||||
os.format(FMT_STRING("attachment.name = '{}'\n"), it->second.second);
|
|
||||||
|
|
||||||
auto cinfid = it->second.first.cinf;
|
|
||||||
auto cmdlid = it->second.first.cmdl;
|
|
||||||
|
|
||||||
if (cinfid.isValid()) {
|
|
||||||
/* Build CINF if needed */
|
|
||||||
if (cinfsDone.find(cinfid) == cinfsDone.end()) {
|
|
||||||
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(cinfid, nullptr, true, false)) {
|
|
||||||
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
|
|
||||||
os.linkArmature(cinfPath.getAbsolutePath(), fmt::format(FMT_STRING("CINF_{}"), cinfid));
|
|
||||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
|
||||||
" bpy.context.scene.collection.objects.link(obj)\n";
|
|
||||||
}
|
|
||||||
if (cinfsDone.empty()) {
|
|
||||||
firstName = ANCSDNA::CINFType::GetCINFArmatureName(cinfid);
|
|
||||||
pakRouter.lookupAndReadDNA(cinfid, firstCinf);
|
|
||||||
}
|
|
||||||
cinfsDone.insert(cinfid);
|
|
||||||
}
|
|
||||||
os.format(FMT_STRING("arm_obj = bpy.data.objects['CINF_{}']\n"), cinfid);
|
|
||||||
os << "attachment.linked_armature = arm_obj.name\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Link CMDL */
|
|
||||||
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(cmdlid, nullptr, true, false)) {
|
|
||||||
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
|
|
||||||
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
|
|
||||||
|
|
||||||
/* Attach CMDL to CINF */
|
|
||||||
os << "if obj.name not in bpy.context.scene.objects:\n"
|
|
||||||
" bpy.context.scene.collection.objects.link(obj)\n"
|
|
||||||
"obj.parent = arm_obj\n"
|
|
||||||
"obj.parent_type = 'ARMATURE'\n"
|
|
||||||
"attachment.linked_mesh = obj.name\n\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
hecl::blender::DataStream ds = conn.beginData();
|
|
||||||
std::unordered_map<std::string, hecl::blender::Matrix3f> matrices = ds.getBoneMatrices(firstName);
|
|
||||||
ds.close();
|
|
||||||
DNAANIM::RigInverter<typename ANCSDNA::CINFType> inverter(firstCinf, matrices);
|
|
||||||
|
|
||||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
|
||||||
os << "import bpy\n"
|
|
||||||
"actor_data = bpy.context.scene.hecl_sact_data\n";
|
|
||||||
|
|
||||||
/* Get animation primitives */
|
|
||||||
std::map<atUint32, AnimationResInfo<typename PAKRouter::IDType>> animResInfo;
|
|
||||||
ancs.getAnimationResInfo(&pakRouter, animResInfo);
|
|
||||||
for (const auto& id : animResInfo) {
|
|
||||||
typename ANCSDNA::ANIMType anim;
|
|
||||||
if (pakRouter.lookupAndReadDNA(id.second.animId, anim, true)) {
|
|
||||||
os.format(FMT_STRING("act = bpy.data.actions.new('{}')\n"
|
|
||||||
"act.use_fake_user = True\n"
|
|
||||||
"act.anim_id = '{}'\n"),
|
|
||||||
id.second.name, id.second.animId);
|
|
||||||
anim.sendANIMToBlender(os, inverter, id.second.additive);
|
|
||||||
}
|
|
||||||
|
|
||||||
os.format(FMT_STRING("actor_action = actor_data.actions.add()\n"
|
|
||||||
"actor_action.name = '{}'\n"),
|
|
||||||
id.second.name);
|
|
||||||
|
|
||||||
/* Extract EVNT if present */
|
|
||||||
anim.extractEVNT(id.second, outPath, pakRouter, force);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.saveBlend();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template bool
|
|
||||||
ReadANCSToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::ANCS, DNAMP1::MaterialSet, DNACMDL::SurfaceHeader_1, 2>(
|
|
||||||
hecl::blender::Token& btok, const DNAMP1::ANCS& ancs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
|
|
||||||
const SpecBase& dataspec, std::function<void(const char*)> fileChanged, bool force);
|
|
||||||
template bool
|
|
||||||
ReadANCSToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::ANCS, DNAMP2::MaterialSet, DNACMDL::SurfaceHeader_2, 4>(
|
|
||||||
hecl::blender::Token& btok, const DNAMP2::ANCS& ancs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
|
|
||||||
const SpecBase& dataspec, std::function<void(const char*)> fileChanged, bool force);
|
|
||||||
template bool
|
|
||||||
ReadANCSToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::CHAR, DNAMP3::MaterialSet, DNACMDL::SurfaceHeader_3, 4>(
|
|
||||||
hecl::blender::Token& btok, const DNAMP3::CHAR& ancs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP3::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP3::PAKBridge>::EntryType& entry,
|
|
||||||
const SpecBase& dataspec, std::function<void(const char*)> fileChanged, bool force);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAANCS
|
|
|
@ -1,47 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "athena/Types.hpp"
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
struct SpecBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAANCS {
|
|
||||||
|
|
||||||
using Actor = hecl::blender::Actor;
|
|
||||||
using Armature = Actor::ActorArmature;
|
|
||||||
using Action = hecl::blender::Action;
|
|
||||||
|
|
||||||
template <typename IDTYPE>
|
|
||||||
struct CharacterResInfo {
|
|
||||||
std::string name;
|
|
||||||
IDTYPE cmdl;
|
|
||||||
IDTYPE cskr;
|
|
||||||
IDTYPE cinf;
|
|
||||||
std::vector<std::pair<std::string, std::pair<IDTYPE, IDTYPE>>> overlays;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename IDTYPE>
|
|
||||||
struct AnimationResInfo {
|
|
||||||
std::string name;
|
|
||||||
IDTYPE animId;
|
|
||||||
IDTYPE evntId;
|
|
||||||
bool additive;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
|
|
||||||
bool ReadANCSToBlender(hecl::blender::Token& btok, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
|
|
||||||
std::function<void(const char*)> fileChanged, bool force = false);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAANCS
|
|
|
@ -1,462 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/ANIM.hpp"
|
|
||||||
|
|
||||||
#include <cfloat>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <hecl/hecl.hpp>
|
|
||||||
#include <zeus/Global.hpp>
|
|
||||||
#include <zeus/Math.hpp>
|
|
||||||
|
|
||||||
#define DUMP_KEYS 0
|
|
||||||
|
|
||||||
#if DUMP_KEYS
|
|
||||||
#include <cstdio>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace DataSpec::DNAANIM {
|
|
||||||
|
|
||||||
size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector<Channel>& channels) {
|
|
||||||
size_t bitsPerKeyFrame = 0;
|
|
||||||
for (const Channel& chan : channels) {
|
|
||||||
switch (chan.type) {
|
|
||||||
case Channel::Type::Rotation:
|
|
||||||
bitsPerKeyFrame += 1;
|
|
||||||
[[fallthrough]];
|
|
||||||
case Channel::Type::Translation:
|
|
||||||
case Channel::Type::Scale:
|
|
||||||
bitsPerKeyFrame += chan.q[0];
|
|
||||||
bitsPerKeyFrame += chan.q[1];
|
|
||||||
bitsPerKeyFrame += chan.q[2];
|
|
||||||
break;
|
|
||||||
case Channel::Type::KfHead:
|
|
||||||
bitsPerKeyFrame += 1;
|
|
||||||
break;
|
|
||||||
case Channel::Type::RotationMP3:
|
|
||||||
bitsPerKeyFrame += chan.q[0];
|
|
||||||
bitsPerKeyFrame += chan.q[1];
|
|
||||||
bitsPerKeyFrame += chan.q[2];
|
|
||||||
bitsPerKeyFrame += chan.q[3];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (bitsPerKeyFrame * keyFrameCount + 31) / 32 * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QuantizedRot QuantizeRotation(const Value& quat, atUint32 div) {
|
|
||||||
float q = float(div) / (M_PIF / 2.0f);
|
|
||||||
zeus::simd_floats f(quat.simd);
|
|
||||||
assert(std::abs(f[1]) <= 1.f && "Out of range quat X component");
|
|
||||||
assert(std::abs(f[2]) <= 1.f && "Out of range quat Y component");
|
|
||||||
assert(std::abs(f[3]) <= 1.f && "Out of range quat Z component");
|
|
||||||
return {{
|
|
||||||
atInt32(std::asin(f[1]) * q),
|
|
||||||
atInt32(std::asin(f[2]) * q),
|
|
||||||
atInt32(std::asin(f[3]) * q),
|
|
||||||
},
|
|
||||||
(f[0] < 0.f)};
|
|
||||||
}
|
|
||||||
|
|
||||||
static Value DequantizeRotation(const QuantizedRot& v, atUint32 div) {
|
|
||||||
float q = (M_PIF / 2.0f) / float(div);
|
|
||||||
athena::simd_floats f = {
|
|
||||||
0.0f,
|
|
||||||
std::sin(v.v[0] * q),
|
|
||||||
std::sin(v.v[1] * q),
|
|
||||||
std::sin(v.v[2] * q),
|
|
||||||
};
|
|
||||||
f[0] = std::sqrt(std::max((1.0f - (f[1] * f[1] + f[2] * f[2] + f[3] * f[3])), 0.0f));
|
|
||||||
f[0] = v.w ? -f[0] : f[0];
|
|
||||||
Value retval;
|
|
||||||
retval.simd.copy_from(f);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Value DequantizeRotation_3(const QuantizedRot& v, atUint32 div) {
|
|
||||||
float q = 1.0f / float(div);
|
|
||||||
athena::simd_floats f = {
|
|
||||||
0.0f,
|
|
||||||
v.v[0] * q,
|
|
||||||
v.v[1] * q,
|
|
||||||
v.v[2] * q,
|
|
||||||
};
|
|
||||||
f[0] = std::sqrt(std::max((1.0f - (f[1] * f[1] + f[2] * f[2] + f[3] * f[3])), 0.0f));
|
|
||||||
f[0] = v.w ? -f[0] : f[0];
|
|
||||||
Value retval;
|
|
||||||
retval.simd.copy_from(f);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BitstreamReader::dequantizeBit(const atUint8* data) {
|
|
||||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
|
||||||
atUint32 bitRem = m_bitCur % 32;
|
|
||||||
|
|
||||||
/* Fill 32 bit buffer with region containing bits */
|
|
||||||
/* Make them least significant */
|
|
||||||
atUint32 tempBuf = hecl::SBig(*reinterpret_cast<const atUint32*>(data + byteCur)) >> bitRem;
|
|
||||||
|
|
||||||
/* That's it */
|
|
||||||
m_bitCur += 1;
|
|
||||||
return tempBuf & 0x1;
|
|
||||||
}
|
|
||||||
|
|
||||||
atInt32 BitstreamReader::dequantize(const atUint8* data, atUint8 q) {
|
|
||||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
|
||||||
atUint32 bitRem = m_bitCur % 32;
|
|
||||||
|
|
||||||
/* Fill 32 bit buffer with region containing bits */
|
|
||||||
/* Make them least significant */
|
|
||||||
atUint32 tempBuf = hecl::SBig(*reinterpret_cast<const atUint32*>(data + byteCur)) >> bitRem;
|
|
||||||
|
|
||||||
/* If this shift underflows the value, buffer the next 32 bits */
|
|
||||||
/* And tack onto shifted buffer */
|
|
||||||
if ((bitRem + q) > 32) {
|
|
||||||
atUint32 tempBuf2 = hecl::SBig(*reinterpret_cast<const atUint32*>(data + byteCur + 4));
|
|
||||||
tempBuf |= (tempBuf2 << (32 - bitRem));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mask it */
|
|
||||||
atUint32 mask = (1 << q) - 1;
|
|
||||||
tempBuf &= mask;
|
|
||||||
|
|
||||||
/* Sign extend */
|
|
||||||
atUint32 sign = (tempBuf >> (q - 1)) & 0x1;
|
|
||||||
if (sign)
|
|
||||||
tempBuf |= ~0u << q;
|
|
||||||
|
|
||||||
/* Return delta value */
|
|
||||||
m_bitCur += q;
|
|
||||||
return atInt32(tempBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::vector<Value>> BitstreamReader::read(const atUint8* data, size_t keyFrameCount,
|
|
||||||
const std::vector<Channel>& channels, atUint32 rotDiv,
|
|
||||||
float transMult, float scaleMult) {
|
|
||||||
m_bitCur = 0;
|
|
||||||
std::vector<std::vector<Value>> chanKeys;
|
|
||||||
std::vector<QuantizedValue> chanAccum;
|
|
||||||
chanKeys.reserve(channels.size());
|
|
||||||
chanAccum.reserve(channels.size());
|
|
||||||
for (const Channel& chan : channels) {
|
|
||||||
chanAccum.push_back(chan.i);
|
|
||||||
|
|
||||||
chanKeys.emplace_back();
|
|
||||||
std::vector<Value>& keys = chanKeys.back();
|
|
||||||
keys.reserve(keyFrameCount);
|
|
||||||
switch (chan.type) {
|
|
||||||
case Channel::Type::Rotation: {
|
|
||||||
QuantizedRot qr = {{chan.i[0], chan.i[1], chan.i[2]}, false};
|
|
||||||
keys.emplace_back(DequantizeRotation(qr, rotDiv));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Translation: {
|
|
||||||
keys.push_back({chan.i[0] * transMult, chan.i[1] * transMult, chan.i[2] * transMult});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Scale: {
|
|
||||||
keys.push_back({chan.i[0] * scaleMult, chan.i[1] * scaleMult, chan.i[2] * scaleMult});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::KfHead: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::RotationMP3: {
|
|
||||||
QuantizedRot qr = {{chan.i[1], chan.i[2], chan.i[3]}, bool(chan.i[0] & 0x1)};
|
|
||||||
keys.emplace_back(DequantizeRotation_3(qr, rotDiv));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t f = 0; f < keyFrameCount; ++f) {
|
|
||||||
#if DUMP_KEYS
|
|
||||||
fmt::print(stderr, FMT_STRING("\nFRAME {} {} {}\n"), f, (m_bitCur / 32) * 4, m_bitCur % 32);
|
|
||||||
int lastId = -1;
|
|
||||||
#endif
|
|
||||||
auto kit = chanKeys.begin();
|
|
||||||
auto ait = chanAccum.begin();
|
|
||||||
for (const Channel& chan : channels) {
|
|
||||||
#if DUMP_KEYS
|
|
||||||
if (chan.id != lastId) {
|
|
||||||
lastId = chan.id;
|
|
||||||
std::fputc('\n', stderr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
QuantizedValue& p = *ait;
|
|
||||||
switch (chan.type) {
|
|
||||||
case Channel::Type::Rotation: {
|
|
||||||
bool wBit = dequantizeBit(data);
|
|
||||||
p[0] += dequantize(data, chan.q[0]);
|
|
||||||
p[1] += dequantize(data, chan.q[1]);
|
|
||||||
p[2] += dequantize(data, chan.q[2]);
|
|
||||||
QuantizedRot qr = {{p[0], p[1], p[2]}, wBit};
|
|
||||||
kit->emplace_back(DequantizeRotation(qr, rotDiv));
|
|
||||||
#if DUMP_KEYS
|
|
||||||
fmt::print(stderr, FMT_STRING("{} R: {} {} {} {}\t"), chan.id, wBit, p[0], p[1], p[2]);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Translation: {
|
|
||||||
atInt32 val1 = dequantize(data, chan.q[0]);
|
|
||||||
p[0] += val1;
|
|
||||||
atInt32 val2 = dequantize(data, chan.q[1]);
|
|
||||||
p[1] += val2;
|
|
||||||
atInt32 val3 = dequantize(data, chan.q[2]);
|
|
||||||
p[2] += val3;
|
|
||||||
kit->push_back({p[0] * transMult, p[1] * transMult, p[2] * transMult});
|
|
||||||
#if DUMP_KEYS
|
|
||||||
fmt::print(stderr, FMT_STRING("{} T: {} {} {}\t"), chan.id, p[0], p[1], p[2]);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Scale: {
|
|
||||||
p[0] += dequantize(data, chan.q[0]);
|
|
||||||
p[1] += dequantize(data, chan.q[1]);
|
|
||||||
p[2] += dequantize(data, chan.q[2]);
|
|
||||||
kit->push_back({p[0] * scaleMult, p[1] * scaleMult, p[2] * scaleMult});
|
|
||||||
#if DUMP_KEYS
|
|
||||||
fmt::print(stderr, FMT_STRING("{} S: {} {} {}\t"), chan.id, p[0], p[1], p[2]);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::KfHead: {
|
|
||||||
dequantizeBit(data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::RotationMP3: {
|
|
||||||
atInt32 val1 = dequantize(data, chan.q[0]);
|
|
||||||
p[0] += val1;
|
|
||||||
atInt32 val2 = dequantize(data, chan.q[1]);
|
|
||||||
p[1] += val2;
|
|
||||||
atInt32 val3 = dequantize(data, chan.q[2]);
|
|
||||||
p[2] += val3;
|
|
||||||
atInt32 val4 = dequantize(data, chan.q[3]);
|
|
||||||
p[3] += val4;
|
|
||||||
QuantizedRot qr = {{p[1], p[2], p[3]}, bool(p[0] & 0x1)};
|
|
||||||
kit->emplace_back(DequantizeRotation_3(qr, rotDiv));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++kit;
|
|
||||||
++ait;
|
|
||||||
}
|
|
||||||
#if DUMP_KEYS
|
|
||||||
std::fputc('\n', stderr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return chanKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BitstreamWriter::quantizeBit(atUint8* data, bool val) {
|
|
||||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
|
||||||
atUint32 bitRem = m_bitCur % 32;
|
|
||||||
|
|
||||||
/* Fill 32 bit buffer with region containing bits */
|
|
||||||
/* Make them least significant */
|
|
||||||
*(atUint32*)(data + byteCur) = hecl::SBig(hecl::SBig(*(atUint32*)(data + byteCur)) | (val << bitRem));
|
|
||||||
|
|
||||||
m_bitCur += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BitstreamWriter::quantize(atUint8* data, atUint8 q, atInt32 val) {
|
|
||||||
atUint32 byteCur = (m_bitCur / 32) * 4;
|
|
||||||
atUint32 bitRem = m_bitCur % 32;
|
|
||||||
|
|
||||||
atUint32 masked = val & ((1 << q) - 1);
|
|
||||||
assert(((((val >> 31) & 0x1) == 0x1) || (((masked >> (q - 1)) & 0x1) == 0)) && "Twos compliment fail");
|
|
||||||
|
|
||||||
/* Fill 32 bit buffer with region containing bits */
|
|
||||||
/* Make them least significant */
|
|
||||||
*(atUint32*)(data + byteCur) = hecl::SBig(hecl::SBig(*(atUint32*)(data + byteCur)) | (masked << bitRem));
|
|
||||||
|
|
||||||
/* If this shift underflows the value, buffer the next 32 bits */
|
|
||||||
/* And tack onto shifted buffer */
|
|
||||||
if ((bitRem + q) > 32) {
|
|
||||||
*(atUint32*)(data + byteCur + 4) =
|
|
||||||
hecl::SBig(hecl::SBig(*(atUint32*)(data + byteCur + 4)) | (masked >> (32 - bitRem)));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_bitCur += q;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<atUint8[]> BitstreamWriter::write(const std::vector<std::vector<Value>>& chanKeys, size_t keyFrameCount,
|
|
||||||
std::vector<Channel>& channels, atUint32 quantRange,
|
|
||||||
atUint32& rotDivOut, float& transMultOut, float& scaleMultOut,
|
|
||||||
size_t& sizeOut) {
|
|
||||||
m_bitCur = 0;
|
|
||||||
rotDivOut = quantRange; /* Normalized range of values */
|
|
||||||
float quantRangeF = float(quantRange);
|
|
||||||
|
|
||||||
/* Pre-pass to calculate translation multiplier */
|
|
||||||
float maxTransDelta = 0.0f;
|
|
||||||
float maxScaleDelta = 0.0f;
|
|
||||||
auto kit = chanKeys.begin();
|
|
||||||
for (Channel& chan : channels) {
|
|
||||||
switch (chan.type) {
|
|
||||||
case Channel::Type::Translation: {
|
|
||||||
zeus::simd<float> lastVal = {};
|
|
||||||
for (auto it = kit->begin(); it != kit->end(); ++it) {
|
|
||||||
const Value* key = &*it;
|
|
||||||
zeus::simd_floats f(key->simd - lastVal);
|
|
||||||
lastVal = key->simd;
|
|
||||||
maxTransDelta = std::max(maxTransDelta, std::fabs(f[0]));
|
|
||||||
maxTransDelta = std::max(maxTransDelta, std::fabs(f[1]));
|
|
||||||
maxTransDelta = std::max(maxTransDelta, std::fabs(f[2]));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Scale: {
|
|
||||||
zeus::simd<float> lastVal = {};
|
|
||||||
for (auto it = kit->begin(); it != kit->end(); ++it) {
|
|
||||||
const Value* key = &*it;
|
|
||||||
zeus::simd_floats f(key->simd - lastVal);
|
|
||||||
lastVal = key->simd;
|
|
||||||
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[0]));
|
|
||||||
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[1]));
|
|
||||||
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[2]));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++kit;
|
|
||||||
}
|
|
||||||
transMultOut = maxTransDelta / quantRangeF + FLT_EPSILON;
|
|
||||||
scaleMultOut = maxScaleDelta / quantRangeF + FLT_EPSILON;
|
|
||||||
|
|
||||||
/* Output channel inits */
|
|
||||||
std::vector<QuantizedValue> initVals;
|
|
||||||
initVals.reserve(channels.size());
|
|
||||||
kit = chanKeys.begin();
|
|
||||||
for (Channel& chan : channels) {
|
|
||||||
chan.q[0] = 1;
|
|
||||||
chan.q[1] = 1;
|
|
||||||
chan.q[2] = 1;
|
|
||||||
switch (chan.type) {
|
|
||||||
case Channel::Type::Rotation: {
|
|
||||||
QuantizedRot qr = QuantizeRotation((*kit)[0], rotDivOut);
|
|
||||||
chan.i = qr.v;
|
|
||||||
initVals.push_back(chan.i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Translation: {
|
|
||||||
zeus::simd_floats f((*kit)[0].simd);
|
|
||||||
chan.i = {atInt32(f[0] / transMultOut), atInt32(f[1] / transMultOut), atInt32(f[2] / transMultOut)};
|
|
||||||
initVals.push_back(chan.i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Scale: {
|
|
||||||
zeus::simd_floats f((*kit)[0].simd);
|
|
||||||
chan.i = {atInt32(f[0] / scaleMultOut), atInt32(f[1] / scaleMultOut), atInt32(f[2] / scaleMultOut)};
|
|
||||||
initVals.push_back(chan.i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++kit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pre-pass to analyze quantization factors for channels */
|
|
||||||
std::vector<QuantizedValue> lastVals = initVals;
|
|
||||||
kit = chanKeys.begin();
|
|
||||||
auto vit = lastVals.begin();
|
|
||||||
for (Channel& chan : channels) {
|
|
||||||
QuantizedValue& last = *vit++;
|
|
||||||
switch (chan.type) {
|
|
||||||
case Channel::Type::Rotation: {
|
|
||||||
for (auto it = kit->begin() + 1; it != kit->end(); ++it) {
|
|
||||||
QuantizedRot qrCur = QuantizeRotation(*it, rotDivOut);
|
|
||||||
chan.q[0] = std::max(chan.q[0], atUint8(qrCur.v.qFrom(last, 0)));
|
|
||||||
chan.q[1] = std::max(chan.q[1], atUint8(qrCur.v.qFrom(last, 1)));
|
|
||||||
chan.q[2] = std::max(chan.q[2], atUint8(qrCur.v.qFrom(last, 2)));
|
|
||||||
last = qrCur.v;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Translation: {
|
|
||||||
for (auto it = kit->begin() + 1; it != kit->end(); ++it) {
|
|
||||||
zeus::simd_floats f(it->simd);
|
|
||||||
QuantizedValue cur = {atInt32(f[0] / transMultOut), atInt32(f[1] / transMultOut), atInt32(f[2] / transMultOut)};
|
|
||||||
chan.q[0] = std::max(chan.q[0], atUint8(cur.qFrom(last, 0)));
|
|
||||||
chan.q[1] = std::max(chan.q[1], atUint8(cur.qFrom(last, 1)));
|
|
||||||
chan.q[2] = std::max(chan.q[2], atUint8(cur.qFrom(last, 2)));
|
|
||||||
last = cur;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Scale: {
|
|
||||||
for (auto it = kit->begin() + 1; it != kit->end(); ++it) {
|
|
||||||
zeus::simd_floats f(it->simd);
|
|
||||||
QuantizedValue cur = {atInt32(f[0] / scaleMultOut), atInt32(f[1] / scaleMultOut), atInt32(f[2] / scaleMultOut)};
|
|
||||||
chan.q[0] = std::max(chan.q[0], atUint8(cur.qFrom(last, 0)));
|
|
||||||
chan.q[1] = std::max(chan.q[1], atUint8(cur.qFrom(last, 1)));
|
|
||||||
chan.q[2] = std::max(chan.q[2], atUint8(cur.qFrom(last, 2)));
|
|
||||||
last = cur;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++kit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generate Bitstream */
|
|
||||||
sizeOut = ComputeBitstreamSize(keyFrameCount, channels);
|
|
||||||
std::unique_ptr<atUint8[]> newData(new atUint8[sizeOut]);
|
|
||||||
memset(newData.get(), 0, sizeOut);
|
|
||||||
|
|
||||||
lastVals = initVals;
|
|
||||||
for (size_t frame = 0; frame < keyFrameCount; ++frame) {
|
|
||||||
kit = chanKeys.begin();
|
|
||||||
vit = lastVals.begin();
|
|
||||||
for (const Channel& chan : channels) {
|
|
||||||
const Value& val = (*kit++)[frame + 1];
|
|
||||||
QuantizedValue& last = *vit++;
|
|
||||||
switch (chan.type) {
|
|
||||||
case Channel::Type::Rotation: {
|
|
||||||
QuantizedRot qrCur = QuantizeRotation(val, rotDivOut);
|
|
||||||
quantizeBit(newData.get(), qrCur.w);
|
|
||||||
quantize(newData.get(), chan.q[0], qrCur.v[0] - last.v[0]);
|
|
||||||
quantize(newData.get(), chan.q[1], qrCur.v[1] - last.v[1]);
|
|
||||||
quantize(newData.get(), chan.q[2], qrCur.v[2] - last.v[2]);
|
|
||||||
last = qrCur.v;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Translation: {
|
|
||||||
zeus::simd_floats f(val.simd);
|
|
||||||
QuantizedValue cur = {atInt32(f[0] / transMultOut), atInt32(f[1] / transMultOut), atInt32(f[2] / transMultOut)};
|
|
||||||
quantize(newData.get(), chan.q[0], cur[0] - last[0]);
|
|
||||||
quantize(newData.get(), chan.q[1], cur[1] - last[1]);
|
|
||||||
quantize(newData.get(), chan.q[2], cur[2] - last[2]);
|
|
||||||
last = cur;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Channel::Type::Scale: {
|
|
||||||
zeus::simd_floats f(val.simd);
|
|
||||||
QuantizedValue cur = {atInt32(f[0] / scaleMultOut), atInt32(f[1] / scaleMultOut), atInt32(f[2] / scaleMultOut)};
|
|
||||||
quantize(newData.get(), chan.q[0], cur[0] - last[0]);
|
|
||||||
quantize(newData.get(), chan.q[1], cur[1] - last[1]);
|
|
||||||
quantize(newData.get(), chan.q[2], cur[2] - last[2]);
|
|
||||||
last = cur;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newData;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAANIM
|
|
|
@ -1,73 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <athena/Types.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAANIM {
|
|
||||||
|
|
||||||
struct Value {
|
|
||||||
athena::simd<float> simd;
|
|
||||||
Value() = default;
|
|
||||||
Value(const athena::simd<float>& s) : simd(s) {}
|
|
||||||
Value(const atVec3f& v) : simd(v.simd) {}
|
|
||||||
Value(const atVec4f& v) : simd(v.simd) {}
|
|
||||||
Value(float x, float y, float z) : simd(x, y, z, 0.f) {}
|
|
||||||
Value(float w, float x, float y, float z) : simd(w, x, y, z) {}
|
|
||||||
};
|
|
||||||
struct QuantizedValue {
|
|
||||||
atInt32 v[4];
|
|
||||||
atInt32& operator[](size_t idx) { return v[idx]; }
|
|
||||||
atInt32 operator[](size_t idx) const { return v[idx]; }
|
|
||||||
|
|
||||||
int qFrom(const QuantizedValue& other, size_t idx) const {
|
|
||||||
atInt32 delta = v[idx] - other.v[idx];
|
|
||||||
atInt32 absDelta = std::abs(delta);
|
|
||||||
if (absDelta == 0)
|
|
||||||
return 1;
|
|
||||||
int ret = int(std::ceil(std::log2(absDelta))) + 1;
|
|
||||||
if (delta > 0 && (delta >> (ret - 1)))
|
|
||||||
++ret;
|
|
||||||
assert(ret <= 24 && "Bad q value");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct QuantizedRot {
|
|
||||||
QuantizedValue v;
|
|
||||||
bool w;
|
|
||||||
};
|
|
||||||
struct Channel {
|
|
||||||
enum class Type { Rotation, Translation, Scale, KfHead, RotationMP3 } type;
|
|
||||||
atInt32 id = -1;
|
|
||||||
QuantizedValue i = {};
|
|
||||||
atUint8 q[4] = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector<Channel>& channels);
|
|
||||||
|
|
||||||
class BitstreamReader {
|
|
||||||
size_t m_bitCur;
|
|
||||||
atInt32 dequantize(const atUint8* data, atUint8 q);
|
|
||||||
bool dequantizeBit(const atUint8* data);
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::vector<std::vector<Value>> read(const atUint8* data, size_t keyFrameCount, const std::vector<Channel>& channels,
|
|
||||||
atUint32 rotDiv, float transMult, float scaleMult);
|
|
||||||
};
|
|
||||||
|
|
||||||
class BitstreamWriter {
|
|
||||||
size_t m_bitCur;
|
|
||||||
void quantize(atUint8* data, atUint8 q, atInt32 val);
|
|
||||||
void quantizeBit(atUint8* data, bool val);
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::unique_ptr<atUint8[]> write(const std::vector<std::vector<Value>>& chanKeys, size_t keyFrameCount,
|
|
||||||
std::vector<Channel>& channels, atUint32 quantRange, atUint32& rotDivOut,
|
|
||||||
float& transMultOut, float& scaleMultOut, size_t& sizeOut);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAANIM
|
|
|
@ -1,440 +0,0 @@
|
||||||
#include "AROTBuilder.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
#include "PATH.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
logvisor::Module Log("AROTBuilder");
|
|
||||||
|
|
||||||
constexpr s32 AROT_MAX_LEVEL = 10;
|
|
||||||
constexpr s32 AROT_MIN_MODELS = 8;
|
|
||||||
constexpr s32 COLLISION_MIN_NODE_TRIANGLES = 8;
|
|
||||||
constexpr s32 PATH_MIN_NODE_REGIONS = 16;
|
|
||||||
constexpr float AROT_MIN_SUBDIV = 8.f;
|
|
||||||
|
|
||||||
static zeus::CAABox SplitAABB(const zeus::CAABox& aabb, int i) {
|
|
||||||
zeus::CAABox pos, neg;
|
|
||||||
aabb.splitZ(neg, pos);
|
|
||||||
if (i & 4) {
|
|
||||||
zeus::CAABox(pos).splitY(neg, pos);
|
|
||||||
if (i & 2) {
|
|
||||||
zeus::CAABox(pos).splitX(neg, pos);
|
|
||||||
if (i & 1)
|
|
||||||
return pos;
|
|
||||||
else
|
|
||||||
return neg;
|
|
||||||
} else {
|
|
||||||
zeus::CAABox(neg).splitX(neg, pos);
|
|
||||||
if (i & 1)
|
|
||||||
return pos;
|
|
||||||
else
|
|
||||||
return neg;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
zeus::CAABox(neg).splitY(neg, pos);
|
|
||||||
if (i & 2) {
|
|
||||||
zeus::CAABox(pos).splitX(neg, pos);
|
|
||||||
if (i & 1)
|
|
||||||
return pos;
|
|
||||||
else
|
|
||||||
return neg;
|
|
||||||
} else {
|
|
||||||
zeus::CAABox(neg).splitX(neg, pos);
|
|
||||||
if (i & 1)
|
|
||||||
return pos;
|
|
||||||
else
|
|
||||||
return neg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AROTBuilder::Node::mergeSets(int a, int b) {
|
|
||||||
childNodes[a].childIndices.insert(childNodes[b].childIndices.cbegin(), childNodes[b].childIndices.cend());
|
|
||||||
childNodes[b].childIndices = childNodes[a].childIndices;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AROTBuilder::Node::compareSets(int a, int b) const {
|
|
||||||
return childNodes[a].childIndices != childNodes[b].childIndices;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AROTBuilder::Node::addChild(int level, int minChildren, const std::vector<zeus::CAABox>& triBoxes,
|
|
||||||
const zeus::CAABox& curAABB, BspNodeType& typeOut) {
|
|
||||||
/* Gather intersecting faces */
|
|
||||||
for (size_t i = 0; i < triBoxes.size(); ++i)
|
|
||||||
if (triBoxes[i].intersects(curAABB))
|
|
||||||
childIndices.insert(i);
|
|
||||||
|
|
||||||
zeus::CVector3f extents = curAABB.extents();
|
|
||||||
|
|
||||||
/* Return early if empty, triangle intersection below performance threshold, or at max level */
|
|
||||||
if (childIndices.empty()) {
|
|
||||||
typeOut = BspNodeType::Invalid;
|
|
||||||
return;
|
|
||||||
} else if (childIndices.size() < minChildren || level == AROT_MAX_LEVEL ||
|
|
||||||
std::max(extents.x(), std::max(extents.y(), extents.z())) < AROT_MIN_SUBDIV) {
|
|
||||||
typeOut = BspNodeType::Leaf;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Subdivide */
|
|
||||||
typeOut = BspNodeType::Branch;
|
|
||||||
childNodes.resize(8);
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
BspNodeType chType;
|
|
||||||
childNodes[i].addChild(level + 1, minChildren, triBoxes, SplitAABB(curAABB, i), chType);
|
|
||||||
flags |= int(chType) << (i * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unsubdivide minimum axis dimensions */
|
|
||||||
if (extents.x() < AROT_MIN_SUBDIV) {
|
|
||||||
mergeSets(0, 1);
|
|
||||||
mergeSets(4, 5);
|
|
||||||
mergeSets(2, 3);
|
|
||||||
mergeSets(6, 7);
|
|
||||||
}
|
|
||||||
if (extents.y() < AROT_MIN_SUBDIV) {
|
|
||||||
mergeSets(0, 2);
|
|
||||||
mergeSets(1, 3);
|
|
||||||
mergeSets(4, 6);
|
|
||||||
mergeSets(5, 7);
|
|
||||||
}
|
|
||||||
if (extents.z() < AROT_MIN_SUBDIV) {
|
|
||||||
mergeSets(0, 4);
|
|
||||||
mergeSets(1, 5);
|
|
||||||
mergeSets(2, 6);
|
|
||||||
mergeSets(3, 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unsubdivide */
|
|
||||||
compSubdivs = 0;
|
|
||||||
if (compareSets(0, 1) || compareSets(4, 5) || compareSets(2, 3) || compareSets(6, 7))
|
|
||||||
compSubdivs |= 0x1;
|
|
||||||
if (compareSets(0, 2) || compareSets(1, 3) || compareSets(4, 6) || compareSets(5, 7))
|
|
||||||
compSubdivs |= 0x2;
|
|
||||||
if (compareSets(0, 4) || compareSets(1, 5) || compareSets(2, 6) || compareSets(3, 7))
|
|
||||||
compSubdivs |= 0x4;
|
|
||||||
|
|
||||||
if (!compSubdivs) {
|
|
||||||
typeOut = BspNodeType::Leaf;
|
|
||||||
childNodes = std::vector<Node>();
|
|
||||||
flags = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AROTBuilder::BitmapPool::addIndices(const std::set<int>& indices) {
|
|
||||||
for (size_t i = 0; i < m_pool.size(); ++i)
|
|
||||||
if (m_pool[i] == indices)
|
|
||||||
return i;
|
|
||||||
m_pool.push_back(indices);
|
|
||||||
return m_pool.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::array<uint32_t, 8> AROTChildCounts{
|
|
||||||
0, 2, 2, 4, 2, 4, 4, 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
void AROTBuilder::Node::nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff) {
|
|
||||||
sz += 1;
|
|
||||||
poolIdx = bmpPool.addIndices(childIndices);
|
|
||||||
if (poolIdx > 65535)
|
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("AROT bitmap exceeds 16-bit node addressing; area too complex"));
|
|
||||||
|
|
||||||
uint32_t childCount = AROTChildCounts[compSubdivs];
|
|
||||||
nodeOff = curOff;
|
|
||||||
nodeSz = childCount * 2 + 4;
|
|
||||||
curOff += nodeSz;
|
|
||||||
if (childNodes.size()) {
|
|
||||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
|
||||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
|
||||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
|
||||||
int idx = k * 4 + j * 2 + i;
|
|
||||||
childNodes[idx].nodeCount(sz, idxRefs, bmpPool, curOff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idxRefs += childCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AROTBuilder::Node::writeIndirectionTable(athena::io::MemoryWriter& w) {
|
|
||||||
w.writeUint32Big(nodeOff);
|
|
||||||
if (childNodes.size()) {
|
|
||||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
|
||||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
|
||||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
|
||||||
int idx = k * 4 + j * 2 + i;
|
|
||||||
childNodes[idx].writeIndirectionTable(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx) {
|
|
||||||
w.writeUint16Big(poolIdx);
|
|
||||||
w.writeUint16Big(compSubdivs);
|
|
||||||
|
|
||||||
if (childNodes.size()) {
|
|
||||||
int curIdx = nodeIdx + 1;
|
|
||||||
if (curIdx > 65535)
|
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("AROT node exceeds 16-bit node addressing; area too complex"));
|
|
||||||
|
|
||||||
std::array<int, 8> childIndices;
|
|
||||||
|
|
||||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
|
||||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
|
||||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
|
||||||
int idx = k * 4 + j * 2 + i;
|
|
||||||
w.writeUint16Big(curIdx);
|
|
||||||
childIndices[idx] = curIdx;
|
|
||||||
childNodes[idx].advanceIndex(curIdx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
|
||||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
|
||||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
|
||||||
int idx = k * 4 + j * 2 + i;
|
|
||||||
childNodes[idx].writeNodes(w, childIndices[idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AROTBuilder::Node::advanceIndex(int& nodeIdx) {
|
|
||||||
++nodeIdx;
|
|
||||||
if (childNodes.size()) {
|
|
||||||
for (int k = 0; k < 1 + ((compSubdivs & 0x4) != 0); ++k) {
|
|
||||||
for (int j = 0; j < 1 + ((compSubdivs & 0x2) != 0); ++j) {
|
|
||||||
for (int i = 0; i < 1 + ((compSubdivs & 0x1) != 0); ++i) {
|
|
||||||
int idx = k * 4 + j * 2 + i;
|
|
||||||
childNodes[idx].advanceIndex(nodeIdx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AROTBuilder::Node::colSize(size_t& totalSz) {
|
|
||||||
if (childIndices.size()) {
|
|
||||||
nodeOff = totalSz;
|
|
||||||
if (childNodes.empty()) {
|
|
||||||
totalSz += 26 + childIndices.size() * 2;
|
|
||||||
} else {
|
|
||||||
totalSz += 36;
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
childNodes[i].colSize(totalSz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AROTBuilder::Node::writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB) {
|
|
||||||
if (childIndices.size()) {
|
|
||||||
if (childNodes.empty()) {
|
|
||||||
float* aabbOut = reinterpret_cast<float*>(ptr);
|
|
||||||
aabbOut[0] = hecl::SBig(curAABB.min[0]);
|
|
||||||
aabbOut[1] = hecl::SBig(curAABB.min[1]);
|
|
||||||
aabbOut[2] = hecl::SBig(curAABB.min[2]);
|
|
||||||
aabbOut[3] = hecl::SBig(curAABB.max[0]);
|
|
||||||
aabbOut[4] = hecl::SBig(curAABB.max[1]);
|
|
||||||
aabbOut[5] = hecl::SBig(curAABB.max[2]);
|
|
||||||
athena::io::MemoryWriter w(ptr + 24, INT32_MAX);
|
|
||||||
w.writeUint16Big(childIndices.size());
|
|
||||||
for (int idx : childIndices)
|
|
||||||
w.writeUint16Big(idx);
|
|
||||||
ptr += 26 + childIndices.size() * 2;
|
|
||||||
} else {
|
|
||||||
uint16_t* pflags = reinterpret_cast<uint16_t*>(ptr);
|
|
||||||
uint32_t* offsets = reinterpret_cast<uint32_t*>(ptr + 4);
|
|
||||||
memset(pflags, 0, sizeof(uint32_t) * 9);
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
const Node& chNode = childNodes[i];
|
|
||||||
BspNodeType type = BspNodeType((flags >> (i * 2)) & 0x3);
|
|
||||||
if (type != BspNodeType::Invalid)
|
|
||||||
offsets[i] = hecl::SBig(uint32_t(chNode.nodeOff - nodeOff - 36));
|
|
||||||
}
|
|
||||||
|
|
||||||
*pflags = hecl::SBig(flags);
|
|
||||||
ptr += 36;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
childNodes[i].writeColNodes(ptr, SplitAABB(curAABB, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AROTBuilder::Node::pathCountNodesAndLookups(size_t& nodeCount, size_t& lookupCount) {
|
|
||||||
++nodeCount;
|
|
||||||
if (childNodes.empty()) {
|
|
||||||
lookupCount += childIndices.size();
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
childNodes[i].pathCountNodesAndLookups(nodeCount, lookupCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class PAKBridge>
|
|
||||||
void AROTBuilder::Node::pathWrite(DNAPATH::PATH<PAKBridge>& path, const zeus::CAABox& curAABB) {
|
|
||||||
if (childNodes.empty()) {
|
|
||||||
auto& n = path.octree.emplace_back();
|
|
||||||
n.isLeaf = 1;
|
|
||||||
n.aabb[0] = curAABB.min;
|
|
||||||
n.aabb[1] = curAABB.max;
|
|
||||||
n.centroid = curAABB.center();
|
|
||||||
std::fill(std::begin(n.children), std::end(n.children), 0xFFFFFFFF);
|
|
||||||
n.regionCount = childIndices.size();
|
|
||||||
n.regionStart = path.octreeRegionLookup.size();
|
|
||||||
for (int r : childIndices)
|
|
||||||
path.octreeRegionLookup.push_back(r);
|
|
||||||
} else {
|
|
||||||
std::array<atUint32, 8> children;
|
|
||||||
for (size_t i = 0; i < children.size(); ++i) {
|
|
||||||
/* Head recursion (first node will be a leaf) */
|
|
||||||
childNodes[i].pathWrite(path, SplitAABB(curAABB, static_cast<int>(i)));
|
|
||||||
children[i] = path.octree.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& n = path.octree.emplace_back();
|
|
||||||
n.isLeaf = 0;
|
|
||||||
n.aabb[0] = curAABB.min;
|
|
||||||
n.aabb[1] = curAABB.max;
|
|
||||||
n.centroid = curAABB.center();
|
|
||||||
std::copy(children.cbegin(), children.cend(), std::begin(n.children));
|
|
||||||
n.regionCount = 0;
|
|
||||||
n.regionStart = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP1::PAKBridge>& path, const zeus::CAABox& curAABB);
|
|
||||||
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP2::PAKBridge>& path, const zeus::CAABox& curAABB);
|
|
||||||
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP3::PAKBridge>& path, const zeus::CAABox& curAABB);
|
|
||||||
|
|
||||||
void AROTBuilder::build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb,
|
|
||||||
const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes) {
|
|
||||||
/* Recursively split */
|
|
||||||
BspNodeType rootType;
|
|
||||||
rootNode.addChild(0, AROT_MIN_MODELS, meshAabbs, fullAabb, rootType);
|
|
||||||
|
|
||||||
/* Calculate indexing metrics */
|
|
||||||
size_t totalNodeCount = 0;
|
|
||||||
size_t idxRefCount = 0;
|
|
||||||
size_t curOff = 0;
|
|
||||||
rootNode.nodeCount(totalNodeCount, idxRefCount, bmpPool, curOff);
|
|
||||||
size_t bmpWordCount = ROUND_UP_32(meshes.size()) / 32;
|
|
||||||
size_t arotSz = 64 + bmpWordCount * bmpPool.m_pool.size() * 4 + totalNodeCount * 8 + idxRefCount * 2;
|
|
||||||
|
|
||||||
/* Write header */
|
|
||||||
secs.emplace_back(arotSz, 0);
|
|
||||||
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
|
|
||||||
w.writeUint32Big('AROT');
|
|
||||||
w.writeUint32Big(1);
|
|
||||||
w.writeUint32Big(bmpPool.m_pool.size());
|
|
||||||
w.writeUint32Big(meshes.size());
|
|
||||||
w.writeUint32Big(totalNodeCount);
|
|
||||||
w.writeVec3fBig(fullAabb.min);
|
|
||||||
w.writeVec3fBig(fullAabb.max);
|
|
||||||
w.seekAlign32();
|
|
||||||
|
|
||||||
/* Write bitmap */
|
|
||||||
std::vector<uint32_t> bmpWords;
|
|
||||||
bmpWords.reserve(bmpWordCount);
|
|
||||||
for (const std::set<int>& bmp : bmpPool.m_pool) {
|
|
||||||
bmpWords.clear();
|
|
||||||
bmpWords.resize(bmpWordCount);
|
|
||||||
|
|
||||||
auto bmpIt = bmp.cbegin();
|
|
||||||
if (bmpIt != bmp.cend()) {
|
|
||||||
int curIdx = 0;
|
|
||||||
for (size_t word = 0; word < bmpWordCount; ++word) {
|
|
||||||
for (u32 b = 0; b < 32; ++b) {
|
|
||||||
if (*bmpIt == curIdx) {
|
|
||||||
bmpWords[word] |= 1U << b;
|
|
||||||
++bmpIt;
|
|
||||||
if (bmpIt == bmp.cend()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++curIdx;
|
|
||||||
}
|
|
||||||
if (bmpIt == bmp.cend()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t word : bmpWords)
|
|
||||||
w.writeUint32Big(word);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write the rest */
|
|
||||||
rootNode.writeIndirectionTable(w);
|
|
||||||
rootNode.writeNodes(w, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::unique_ptr<uint8_t[]>, uint32_t> AROTBuilder::buildCol(const ColMesh& mesh, BspNodeType& rootOut) {
|
|
||||||
/* Accumulate total AABB */
|
|
||||||
zeus::CAABox fullAABB;
|
|
||||||
for (const auto& vert : mesh.verts)
|
|
||||||
fullAABB.accumulateBounds(zeus::CVector3f(vert));
|
|
||||||
|
|
||||||
/* Predetermine triangle AABBs */
|
|
||||||
std::vector<zeus::CAABox> triBoxes;
|
|
||||||
triBoxes.reserve(mesh.trianges.size());
|
|
||||||
for (const ColMesh::Triangle& tri : mesh.trianges) {
|
|
||||||
zeus::CAABox& aabb = triBoxes.emplace_back();
|
|
||||||
for (const u32 edgeIdx : tri.edges) {
|
|
||||||
const ColMesh::Edge& edge = mesh.edges[edgeIdx];
|
|
||||||
for (const u32 vertIdx : edge.verts) {
|
|
||||||
const auto& vert = mesh.verts[vertIdx];
|
|
||||||
aabb.accumulateBounds(zeus::CVector3f(vert));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively split */
|
|
||||||
rootNode.addChild(0, COLLISION_MIN_NODE_TRIANGLES, triBoxes, fullAABB, rootOut);
|
|
||||||
|
|
||||||
/* Calculate offsets and write out */
|
|
||||||
size_t totalSize = 0;
|
|
||||||
rootNode.colSize(totalSize);
|
|
||||||
std::unique_ptr<uint8_t[]> ret(new uint8_t[totalSize]);
|
|
||||||
uint8_t* ptr = ret.get();
|
|
||||||
rootNode.writeColNodes(ptr, fullAABB);
|
|
||||||
|
|
||||||
return {std::move(ret), totalSize};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class PAKBridge>
|
|
||||||
void AROTBuilder::buildPath(DNAPATH::PATH<PAKBridge>& path) {
|
|
||||||
/* Accumulate total AABB and gather region boxes */
|
|
||||||
std::vector<zeus::CAABox> regionBoxes;
|
|
||||||
regionBoxes.reserve(path.regions.size());
|
|
||||||
zeus::CAABox fullAABB;
|
|
||||||
for (const auto& r : path.regions)
|
|
||||||
fullAABB.accumulateBounds(regionBoxes.emplace_back(r.aabb[0], r.aabb[1]));
|
|
||||||
|
|
||||||
/* Recursively split */
|
|
||||||
BspNodeType dontCare;
|
|
||||||
rootNode.addChild(0, PATH_MIN_NODE_REGIONS, regionBoxes, fullAABB, dontCare);
|
|
||||||
|
|
||||||
/* Write out */
|
|
||||||
size_t nodeCount = 0;
|
|
||||||
size_t lookupCount = 0;
|
|
||||||
rootNode.pathCountNodesAndLookups(nodeCount, lookupCount);
|
|
||||||
path.octreeNodeCount = nodeCount;
|
|
||||||
path.octree.reserve(nodeCount);
|
|
||||||
path.octreeRegionLookupCount = lookupCount;
|
|
||||||
path.octreeRegionLookup.reserve(lookupCount);
|
|
||||||
rootNode.pathWrite(path, fullAABB);
|
|
||||||
}
|
|
||||||
|
|
||||||
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP1::PAKBridge>& path);
|
|
||||||
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP2::PAKBridge>& path);
|
|
||||||
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP3::PAKBridge>& path);
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,57 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DNACommon.hpp"
|
|
||||||
#include "DeafBabe.hpp"
|
|
||||||
#include "zeus/CAABox.hpp"
|
|
||||||
#include "CMDL.hpp"
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
namespace DNAPATH {
|
|
||||||
template <class PAKBridge>
|
|
||||||
struct PATH;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AROTBuilder {
|
|
||||||
using ColMesh = hecl::blender::ColMesh;
|
|
||||||
|
|
||||||
struct BitmapPool {
|
|
||||||
std::vector<std::set<int>> m_pool;
|
|
||||||
size_t addIndices(const std::set<int>& indices);
|
|
||||||
} bmpPool;
|
|
||||||
|
|
||||||
struct Node {
|
|
||||||
std::vector<Node> childNodes;
|
|
||||||
std::set<int> childIndices;
|
|
||||||
size_t poolIdx = 0;
|
|
||||||
uint16_t flags = 0;
|
|
||||||
uint16_t compSubdivs = 0;
|
|
||||||
|
|
||||||
size_t nodeOff = 0;
|
|
||||||
size_t nodeSz = 4;
|
|
||||||
|
|
||||||
void addChild(int level, int minChildren, const std::vector<zeus::CAABox>& triBoxes, const zeus::CAABox& curAABB,
|
|
||||||
BspNodeType& typeOut);
|
|
||||||
void mergeSets(int a, int b);
|
|
||||||
bool compareSets(int a, int b) const;
|
|
||||||
void nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPool, size_t& curOff);
|
|
||||||
void writeIndirectionTable(athena::io::MemoryWriter& w);
|
|
||||||
void writeNodes(athena::io::MemoryWriter& w, int nodeIdx);
|
|
||||||
void advanceIndex(int& nodeIdx);
|
|
||||||
|
|
||||||
void colSize(size_t& totalSz);
|
|
||||||
void writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB);
|
|
||||||
|
|
||||||
void pathCountNodesAndLookups(size_t& nodeCount, size_t& lookupCount);
|
|
||||||
template <class PAKBridge>
|
|
||||||
void pathWrite(DNAPATH::PATH<PAKBridge>& path, const zeus::CAABox& curAABB);
|
|
||||||
} rootNode;
|
|
||||||
|
|
||||||
void build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb,
|
|
||||||
const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes);
|
|
||||||
std::pair<std::unique_ptr<uint8_t[]>, uint32_t> buildCol(const ColMesh& mesh, BspNodeType& rootOut);
|
|
||||||
template <class PAKBridge>
|
|
||||||
void buildPath(DNAPATH::PATH<PAKBridge>& path);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,66 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/ATBL.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
#include <athena/FileReader.hpp>
|
|
||||||
#include <athena/FileWriter.hpp>
|
|
||||||
#include <athena/YAMLDocReader.hpp>
|
|
||||||
#include <athena/YAMLDocWriter.hpp>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAAudio {
|
|
||||||
|
|
||||||
bool ATBL::Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
uint32_t idxCount = rs.readUint32Big();
|
|
||||||
athena::io::YAMLDocWriter w("ATBL");
|
|
||||||
for (uint32_t i = 0; i < idxCount; ++i) {
|
|
||||||
uint16_t idx = rs.readUint16Big();
|
|
||||||
if (idx == 0xffff)
|
|
||||||
continue;
|
|
||||||
w.writeUint16(fmt::format(FMT_STRING("0x{:04X}"), i), idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
athena::io::FileWriter fw(outPath.getAbsolutePath());
|
|
||||||
w.finish(&fw);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ATBL::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileReader r(inPath.getAbsolutePath());
|
|
||||||
if (r.hasError())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
athena::io::YAMLDocReader dr;
|
|
||||||
if (!dr.parse(&r))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
unsigned long maxI = 0;
|
|
||||||
for (const auto& pair : dr.getRootNode()->m_mapChildren) {
|
|
||||||
unsigned long i = strtoul(pair.first.c_str(), nullptr, 0);
|
|
||||||
maxI = std::max(maxI, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint16_t> vecOut(maxI + 1, 0xffff);
|
|
||||||
for (const auto& pair : dr.getRootNode()->m_mapChildren) {
|
|
||||||
unsigned long i = strtoul(pair.first.c_str(), nullptr, 0);
|
|
||||||
vecOut[i] = hecl::SBig(uint16_t(strtoul(pair.second->m_scalarString.c_str(), nullptr, 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath());
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
w.writeUint32Big(uint32_t(vecOut.size()));
|
|
||||||
w.writeBytes(vecOut.data(), vecOut.size() * 2);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAAudio
|
|
|
@ -1,19 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAAudio {
|
|
||||||
|
|
||||||
class ATBL {
|
|
||||||
public:
|
|
||||||
static bool Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
static bool Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAAudio
|
|
|
@ -1,161 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/BabeDead.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNAMP1/MREA.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/MREA.hpp"
|
|
||||||
|
|
||||||
#include <cfloat>
|
|
||||||
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
#include <zeus/CTransform.hpp>
|
|
||||||
#include <zeus/Math.hpp>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
template <class BabeDeadLight>
|
|
||||||
void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLight& light, unsigned s, unsigned l) {
|
|
||||||
switch (light.lightType) {
|
|
||||||
case BabeDeadLight::LightType::LocalAmbient:
|
|
||||||
case BabeDeadLight::LightType::LocalAmbient2:
|
|
||||||
os.format(FMT_STRING("bg_node.inputs[0].default_value = ({},{},{},1.0)\n"
|
|
||||||
"bg_node.inputs[1].default_value = {}\n"),
|
|
||||||
light.color.simd[0], light.color.simd[1], light.color.simd[2], light.q / 8.f);
|
|
||||||
return;
|
|
||||||
case BabeDeadLight::LightType::Directional:
|
|
||||||
os.format(FMT_STRING("lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'SUN')\n"
|
|
||||||
"lamp.color = ({},{},{})\n"
|
|
||||||
"lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n"
|
|
||||||
"lamp_obj.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"lamp_obj.rotation_quaternion = Vector((0,0,-1)).rotation_difference(Vector(({},{},{})))\n"
|
|
||||||
"lamp.use_shadow = {}\n"
|
|
||||||
"\n"),
|
|
||||||
s, l, light.color.simd[0], light.color.simd[1], light.color.simd[2], light.direction.simd[0],
|
|
||||||
light.direction.simd[1], light.direction.simd[2], light.castShadows ? "True" : "False");
|
|
||||||
return;
|
|
||||||
case BabeDeadLight::LightType::Custom:
|
|
||||||
os.format(FMT_STRING("lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'POINT')\n"
|
|
||||||
"lamp.color = ({},{},{})\n"
|
|
||||||
"lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n"
|
|
||||||
"lamp.shadow_soft_size = 1.0\n"
|
|
||||||
"lamp.use_shadow = {}\n"
|
|
||||||
"\n"),
|
|
||||||
s, l, light.color.simd[0], light.color.simd[1], light.color.simd[2],
|
|
||||||
light.castShadows ? "True" : "False");
|
|
||||||
break;
|
|
||||||
case BabeDeadLight::LightType::Spot:
|
|
||||||
case BabeDeadLight::LightType::Spot2:
|
|
||||||
os.format(FMT_STRING("lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'SPOT')\n"
|
|
||||||
"lamp.color = ({},{},{})\n"
|
|
||||||
"lamp.spot_size = {:.6g}\n"
|
|
||||||
"lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n"
|
|
||||||
"lamp_obj.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"lamp_obj.rotation_quaternion = Vector((0,0,-1)).rotation_difference(Vector(({},{},{})))\n"
|
|
||||||
"lamp.shadow_soft_size = 0.5\n"
|
|
||||||
"lamp.use_shadow = {}\n"
|
|
||||||
"\n"),
|
|
||||||
s, l, light.color.simd[0], light.color.simd[1], light.color.simd[2], zeus::degToRad(light.spotCutoff),
|
|
||||||
light.direction.simd[0], light.direction.simd[1], light.direction.simd[2],
|
|
||||||
light.castShadows ? "True" : "False");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
os.format(FMT_STRING("lamp.retro_layer = {}\n"
|
|
||||||
"lamp.retro_origtype = {}\n"
|
|
||||||
"lamp.falloff_type = 'INVERSE_COEFFICIENTS'\n"
|
|
||||||
"lamp.constant_coefficient = 0\n"
|
|
||||||
"lamp.use_nodes = True\n"
|
|
||||||
"falloff_node = lamp.node_tree.nodes.new('ShaderNodeLightFalloff')\n"
|
|
||||||
"lamp.energy = 0.0\n"
|
|
||||||
"falloff_node.inputs[0].default_value = {}\n"
|
|
||||||
"hue_sat_node = lamp.node_tree.nodes.new('ShaderNodeHueSaturation')\n"
|
|
||||||
"hue_sat_node.inputs[1].default_value = 1.25\n"
|
|
||||||
"hue_sat_node.inputs[4].default_value = ({},{},{},1.0)\n"
|
|
||||||
"lamp.node_tree.links.new(hue_sat_node.outputs[0], lamp.node_tree.nodes['Emission'].inputs[0])\n"
|
|
||||||
"lamp_obj.location = ({},{},{})\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(lamp_obj)\n"
|
|
||||||
"\n"),
|
|
||||||
s, unsigned(light.lightType), light.q / 8.f, light.color.simd[0], light.color.simd[1], light.color.simd[2],
|
|
||||||
light.position.simd[0], light.position.simd[1], light.position.simd[2]);
|
|
||||||
|
|
||||||
switch (light.falloff) {
|
|
||||||
case BabeDeadLight::Falloff::Constant:
|
|
||||||
os << "falloff_node.inputs[0].default_value *= 150.0\n"
|
|
||||||
"lamp.node_tree.links.new(falloff_node.outputs[2], lamp.node_tree.nodes['Emission'].inputs[1])\n";
|
|
||||||
if (light.q > FLT_EPSILON)
|
|
||||||
os.format(FMT_STRING("lamp.constant_coefficient = 2.0 / {}\n"), light.q);
|
|
||||||
break;
|
|
||||||
case BabeDeadLight::Falloff::Linear:
|
|
||||||
os << "lamp.node_tree.links.new(falloff_node.outputs[1], lamp.node_tree.nodes['Emission'].inputs[1])\n";
|
|
||||||
if (light.q > FLT_EPSILON)
|
|
||||||
os.format(FMT_STRING("lamp.linear_coefficient = 250 / {}\n"), light.q);
|
|
||||||
break;
|
|
||||||
case BabeDeadLight::Falloff::Quadratic:
|
|
||||||
os << "lamp.node_tree.links.new(falloff_node.outputs[0], lamp.node_tree.nodes['Emission'].inputs[1])\n";
|
|
||||||
if (light.q > FLT_EPSILON)
|
|
||||||
os.format(FMT_STRING("lamp.quadratic_coefficient = 25000 / {}\n"), light.q);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template void ReadBabeDeadLightToBlender<DNAMP1::MREA::BabeDeadLight>(hecl::blender::PyOutStream& os,
|
|
||||||
const DNAMP1::MREA::BabeDeadLight& light,
|
|
||||||
unsigned s, unsigned l);
|
|
||||||
template void ReadBabeDeadLightToBlender<DNAMP3::MREA::BabeDeadLight>(hecl::blender::PyOutStream& os,
|
|
||||||
const DNAMP3::MREA::BabeDeadLight& light,
|
|
||||||
unsigned s, unsigned l);
|
|
||||||
|
|
||||||
template <class BabeDeadLight>
|
|
||||||
void WriteBabeDeadLightFromBlender(BabeDeadLight& lightOut, const hecl::blender::Light& lightIn) {
|
|
||||||
using InterType = hecl::blender::Light::Type;
|
|
||||||
switch (lightIn.type) {
|
|
||||||
case InterType::Ambient:
|
|
||||||
lightOut.lightType = BabeDeadLight::LightType::LocalAmbient;
|
|
||||||
break;
|
|
||||||
case InterType::Directional:
|
|
||||||
lightOut.lightType = BabeDeadLight::LightType::Directional;
|
|
||||||
break;
|
|
||||||
case InterType::Custom:
|
|
||||||
default:
|
|
||||||
lightOut.lightType = BabeDeadLight::LightType::Custom;
|
|
||||||
break;
|
|
||||||
case InterType::Spot:
|
|
||||||
lightOut.lightType = BabeDeadLight::LightType::Spot;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lightIn.type == InterType::Ambient) {
|
|
||||||
lightOut.falloff = BabeDeadLight::Falloff::Constant;
|
|
||||||
lightOut.q = lightIn.energy * 8.f;
|
|
||||||
} else if (lightIn.linear > lightIn.constant && lightIn.linear > lightIn.quadratic) {
|
|
||||||
lightOut.falloff = BabeDeadLight::Falloff::Linear;
|
|
||||||
lightOut.q = 250.f / lightIn.linear;
|
|
||||||
} else if (lightIn.quadratic > lightIn.constant && lightIn.quadratic > lightIn.linear) {
|
|
||||||
lightOut.falloff = BabeDeadLight::Falloff::Quadratic;
|
|
||||||
lightOut.q = 25000.f / lightIn.quadratic;
|
|
||||||
} else {
|
|
||||||
lightOut.falloff = BabeDeadLight::Falloff::Constant;
|
|
||||||
lightOut.q = 2.f / lightIn.constant;
|
|
||||||
}
|
|
||||||
|
|
||||||
lightOut.color = lightIn.color;
|
|
||||||
lightOut.spotCutoff = zeus::radToDeg(lightIn.spotCutoff);
|
|
||||||
lightOut.castShadows = lightIn.shadow;
|
|
||||||
lightOut.position.simd[0] = lightIn.sceneXf[0].simd[3];
|
|
||||||
lightOut.position.simd[1] = lightIn.sceneXf[1].simd[3];
|
|
||||||
lightOut.position.simd[2] = lightIn.sceneXf[2].simd[3];
|
|
||||||
|
|
||||||
zeus::CTransform lightXf(&lightIn.sceneXf[0]);
|
|
||||||
lightOut.direction = (lightXf.basis.transposed() * zeus::CVector3f(0.f, 0.f, -1.f)).normalized();
|
|
||||||
}
|
|
||||||
|
|
||||||
template void WriteBabeDeadLightFromBlender<DNAMP1::MREA::BabeDeadLight>(DNAMP1::MREA::BabeDeadLight& lightOut,
|
|
||||||
const hecl::blender::Light& lightIn);
|
|
||||||
template void WriteBabeDeadLightFromBlender<DNAMP3::MREA::BabeDeadLight>(DNAMP3::MREA::BabeDeadLight& lightOut,
|
|
||||||
const hecl::blender::Light& lightIn);
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,16 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace hecl::blender {
|
|
||||||
struct Light;
|
|
||||||
class PyOutStream;
|
|
||||||
} // namespace hecl::blender
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
template <class BabeDeadLight>
|
|
||||||
void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLight& light, unsigned s, unsigned l);
|
|
||||||
|
|
||||||
template <class BabeDeadLight>
|
|
||||||
void WriteBabeDeadLightFromBlender(BabeDeadLight& lightOut, const hecl::blender::Light& lightIn);
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,168 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/GX.hpp"
|
|
||||||
#include "DataSpec/DNACommon/TXTR.hpp"
|
|
||||||
|
|
||||||
#include <athena/DNA.hpp>
|
|
||||||
#include <athena/Types.hpp>
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace zeus {
|
|
||||||
class CAABox;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNACMDL {
|
|
||||||
|
|
||||||
using Mesh = hecl::blender::Mesh;
|
|
||||||
using Material = hecl::blender::Material;
|
|
||||||
|
|
||||||
struct Header : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> magic;
|
|
||||||
Value<atUint32> version;
|
|
||||||
struct Flags : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> flags = 0;
|
|
||||||
bool skinned() const { return (flags & 0x1) != 0; }
|
|
||||||
void setSkinned(bool val) {
|
|
||||||
flags &= ~0x1;
|
|
||||||
flags |= val;
|
|
||||||
}
|
|
||||||
bool shortNormals() const { return (flags & 0x2) != 0; }
|
|
||||||
void setShortNormals(bool val) {
|
|
||||||
flags &= ~0x2;
|
|
||||||
flags |= val << 1;
|
|
||||||
}
|
|
||||||
bool shortUVs() const { return (flags & 0x4) != 0; }
|
|
||||||
void setShortUVs(bool val) {
|
|
||||||
flags &= ~0x4;
|
|
||||||
flags |= val << 2;
|
|
||||||
}
|
|
||||||
} flags;
|
|
||||||
Value<atVec3f> aabbMin;
|
|
||||||
Value<atVec3f> aabbMax;
|
|
||||||
Value<atUint32> secCount;
|
|
||||||
Value<atUint32> matSetCount;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(secCount)> secSizes;
|
|
||||||
Align<32> align;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SurfaceHeader_1 : BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA
|
|
||||||
Value<atVec3f> centroid;
|
|
||||||
Value<atUint32> matIdx = 0;
|
|
||||||
Value<atUint32> dlSize = 0;
|
|
||||||
Value<atUint32> idxStart = 0; /* Actually used by game to stash CCubeModel pointer */
|
|
||||||
Value<atUint32> idxCount = 0; /* Actually used by game to stash next CCubeSurface pointer */
|
|
||||||
Value<atUint32> aabbSz = 0;
|
|
||||||
Value<atVec3f> reflectionNormal;
|
|
||||||
Value<atVec3f> aabb[2];
|
|
||||||
Align<32> align;
|
|
||||||
|
|
||||||
static constexpr bool UseMatrixSkinning() { return false; }
|
|
||||||
static constexpr atInt16 skinMatrixBankIdx() { return -1; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SurfaceHeader_2 : BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA
|
|
||||||
Value<atVec3f> centroid;
|
|
||||||
Value<atUint32> matIdx = 0;
|
|
||||||
Value<atUint32> dlSize = 0;
|
|
||||||
Value<atUint32> idxStart = 0; /* Actually used by game to stash CCubeModel pointer */
|
|
||||||
Value<atUint32> idxCount = 0; /* Actually used by game to stash next CCubeSurface pointer */
|
|
||||||
Value<atUint32> aabbSz = 0;
|
|
||||||
Value<atVec3f> reflectionNormal;
|
|
||||||
Value<atInt16> skinMtxBankIdx;
|
|
||||||
Value<atUint16> surfaceGroup;
|
|
||||||
Value<atVec3f> aabb[2];
|
|
||||||
Align<32> align;
|
|
||||||
|
|
||||||
static constexpr bool UseMatrixSkinning() { return false; }
|
|
||||||
atInt16 skinMatrixBankIdx() const { return skinMtxBankIdx; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SurfaceHeader_3 : BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA
|
|
||||||
Value<atVec3f> centroid;
|
|
||||||
Value<atUint32> matIdx = 0;
|
|
||||||
Value<atUint32> dlSize = 0;
|
|
||||||
Value<atUint32> idxStart = 0; /* Actually used by game to stash CCubeModel pointer */
|
|
||||||
Value<atUint32> idxCount = 0; /* Actually used by game to stash next CCubeSurface pointer */
|
|
||||||
Value<atUint32> aabbSz = 0;
|
|
||||||
Value<atVec3f> reflectionNormal;
|
|
||||||
Value<atInt16> skinMtxBankIdx;
|
|
||||||
Value<atUint16> surfaceGroup;
|
|
||||||
Value<atVec3f> aabb[2];
|
|
||||||
Value<atUint8> unk3;
|
|
||||||
Align<32> align;
|
|
||||||
|
|
||||||
static constexpr bool UseMatrixSkinning() { return true; }
|
|
||||||
atInt16 skinMatrixBankIdx() const { return skinMtxBankIdx; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexAttributes {
|
|
||||||
GX::AttrType pos = GX::NONE;
|
|
||||||
GX::AttrType norm = GX::NONE;
|
|
||||||
GX::AttrType color0 = GX::NONE;
|
|
||||||
GX::AttrType color1 = GX::NONE;
|
|
||||||
unsigned uvCount = 0;
|
|
||||||
GX::AttrType uvs[7] = {GX::NONE};
|
|
||||||
GX::AttrType pnMtxIdx = GX::NONE;
|
|
||||||
unsigned texMtxIdxCount = 0;
|
|
||||||
GX::AttrType texMtxIdx[7] = {GX::NONE};
|
|
||||||
bool shortUVs;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class MaterialSet>
|
|
||||||
void GetVertexAttributes(const MaterialSet& matSet, std::vector<VertexAttributes>& attributesOut);
|
|
||||||
|
|
||||||
template <class PAKRouter, class MaterialSet>
|
|
||||||
void ReadMaterialSetToBlender_1_2(hecl::blender::PyOutStream& os, const MaterialSet& matSet, const PAKRouter& pakRouter,
|
|
||||||
const typename PAKRouter::EntryType& entry, unsigned setIdx);
|
|
||||||
|
|
||||||
template <class PAKRouter, class MaterialSet>
|
|
||||||
void ReadMaterialSetToBlender_3(hecl::blender::PyOutStream& os, const MaterialSet& matSet, const PAKRouter& pakRouter,
|
|
||||||
const typename PAKRouter::EntryType& entry, unsigned setIdx);
|
|
||||||
|
|
||||||
void InitGeomBlenderContext(hecl::blender::PyOutStream& os, const hecl::ProjectPath& masterShaderPath);
|
|
||||||
void FinishBlenderMesh(hecl::blender::PyOutStream& os, unsigned matSetCount, int meshIdx);
|
|
||||||
|
|
||||||
template <class PAKRouter, class MaterialSet, class RigPair, class SurfaceHeader>
|
|
||||||
atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::IStreamReader& reader,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const RigPair& rp,
|
|
||||||
bool shortNormals, bool shortUVs, std::vector<VertexAttributes>& vertAttribs,
|
|
||||||
int meshIdx, atUint32 secCount, atUint32 matSetCount, const atUint32* secSizes,
|
|
||||||
atUint32 surfaceCount = 0);
|
|
||||||
|
|
||||||
template <class PAKRouter, class MaterialSet, class RigPair, class SurfaceHeader, atUint32 Version>
|
|
||||||
bool ReadCMDLToBlender(hecl::blender::Connection& conn, athena::io::IStreamReader& reader, PAKRouter& pakRouter,
|
|
||||||
const typename PAKRouter::EntryType& entry, const SpecBase& dataspec, const RigPair& rp);
|
|
||||||
|
|
||||||
template <class PAKRouter, class MaterialSet>
|
|
||||||
void NameCMDL(athena::io::IStreamReader& reader, PAKRouter& pakRouter, typename PAKRouter::EntryType& entry,
|
|
||||||
const SpecBase& dataspec);
|
|
||||||
|
|
||||||
template <class MaterialSet, class SurfaceHeader, atUint32 Version>
|
|
||||||
bool WriteCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const Mesh& mesh);
|
|
||||||
|
|
||||||
template <class MaterialSet, class SurfaceHeader, atUint32 Version>
|
|
||||||
bool WriteHMDLCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const Mesh& mesh,
|
|
||||||
hecl::blender::PoolSkinIndex& poolSkinIndex);
|
|
||||||
|
|
||||||
template <class MaterialSet, class SurfaceHeader, class MeshHeader>
|
|
||||||
bool WriteMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
|
|
||||||
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs);
|
|
||||||
|
|
||||||
template <class MaterialSet, class SurfaceHeader, class MeshHeader>
|
|
||||||
bool WriteHMDLMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::ProjectPath& inPath,
|
|
||||||
const std::vector<Mesh>& meshes, zeus::CAABox& fullAABB, std::vector<zeus::CAABox>& meshAABBs);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNACMDL
|
|
|
@ -1,60 +0,0 @@
|
||||||
make_dnalist(CMDL
|
|
||||||
FONT
|
|
||||||
DGRP
|
|
||||||
FSM2
|
|
||||||
MAPA
|
|
||||||
MAPU
|
|
||||||
PATH
|
|
||||||
MayaSpline
|
|
||||||
EGMC
|
|
||||||
SAVWCommon
|
|
||||||
ParticleCommon
|
|
||||||
MetaforceVersionInfo
|
|
||||||
Tweaks/ITweakPlayerGun)
|
|
||||||
|
|
||||||
set(DNACOMMON_SOURCES
|
|
||||||
DNACommon.hpp DNACommon.cpp
|
|
||||||
PAK.hpp PAK.cpp
|
|
||||||
GX.hpp GX.cpp
|
|
||||||
FSM2.hpp FSM2.cpp
|
|
||||||
MLVL.hpp MLVL.cpp
|
|
||||||
CMDL.cpp
|
|
||||||
MAPA.cpp
|
|
||||||
MAPU.cpp
|
|
||||||
PATH.hpp PATH.cpp
|
|
||||||
STRG.hpp STRG.cpp
|
|
||||||
TXTR.hpp TXTR.cpp
|
|
||||||
ANCS.hpp ANCS.cpp
|
|
||||||
ANIM.hpp ANIM.cpp
|
|
||||||
PART.hpp PART.cpp
|
|
||||||
SWHC.hpp SWHC.cpp
|
|
||||||
CRSC.hpp CRSC.cpp
|
|
||||||
ELSC.hpp ELSC.cpp
|
|
||||||
WPSC.hpp WPSC.cpp
|
|
||||||
DPSC.hpp DPSC.cpp
|
|
||||||
ParticleCommon.cpp
|
|
||||||
FONT.cpp
|
|
||||||
DGRP.cpp
|
|
||||||
ATBL.hpp ATBL.cpp
|
|
||||||
DeafBabe.hpp DeafBabe.cpp
|
|
||||||
BabeDead.hpp BabeDead.cpp
|
|
||||||
RigInverter.hpp RigInverter.cpp
|
|
||||||
AROTBuilder.hpp AROTBuilder.cpp
|
|
||||||
OBBTreeBuilder.hpp OBBTreeBuilder.cpp
|
|
||||||
MetaforceVersionInfo.hpp
|
|
||||||
Tweaks/ITweak.hpp
|
|
||||||
Tweaks/TweakWriter.hpp
|
|
||||||
Tweaks/ITweakGame.hpp
|
|
||||||
Tweaks/ITweakParticle.hpp
|
|
||||||
Tweaks/ITweakPlayer.hpp
|
|
||||||
Tweaks/ITweakPlayerControl.hpp
|
|
||||||
Tweaks/ITweakGunRes.hpp
|
|
||||||
Tweaks/ITweakPlayerRes.hpp
|
|
||||||
Tweaks/ITweakGui.hpp
|
|
||||||
Tweaks/ITweakSlideShow.hpp
|
|
||||||
Tweaks/ITweakTargeting.hpp
|
|
||||||
Tweaks/ITweakAutoMapper.hpp
|
|
||||||
Tweaks/ITweakBall.hpp
|
|
||||||
Tweaks/ITweakGuiColors.hpp)
|
|
||||||
|
|
||||||
dataspec_add_list(DNACommon DNACOMMON_SOURCES)
|
|
|
@ -1,50 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/CRSC.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template struct PPImpl<_CRSM<UniqueID32>>;
|
|
||||||
template struct PPImpl<_CRSM<UniqueID64>>;
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_CRSM<UniqueID32>>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_CRSM<UniqueID64>>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view PPImpl<_CRSM<UniqueID32>>::DNAType() {
|
|
||||||
return "CRSM<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view PPImpl<_CRSM<UniqueID64>>::DNAType() {
|
|
||||||
return "CRSM<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractCRSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
CRSM<IDType> crsm;
|
|
||||||
crsm.read(rs);
|
|
||||||
athena::io::ToYAMLStream(crsm, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template bool ExtractCRSM<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractCRSM<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteCRSM(const CRSM<IDType>& crsm, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
crsm.write(w);
|
|
||||||
int64_t rem = w.position() % 32;
|
|
||||||
if (rem)
|
|
||||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
|
||||||
w.writeUByte(0xff);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template bool WriteCRSM<UniqueID32>(const CRSM<UniqueID32>& crsm, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteCRSM<UniqueID64>(const CRSM<UniqueID64>& crsm, const hecl::ProjectPath& outPath);
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,222 +0,0 @@
|
||||||
#ifndef ENTRY
|
|
||||||
#define ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef RES_ENTRY
|
|
||||||
#define RES_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef U32_ENTRY
|
|
||||||
#define U32_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FLOAT_ENTRY
|
|
||||||
#define FLOAT_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
RES_ENTRY('NODP', NODP)
|
|
||||||
RES_ENTRY('DEFS', DEFS)
|
|
||||||
RES_ENTRY('CRTS', CRTS)
|
|
||||||
RES_ENTRY('MTLS', MTLS)
|
|
||||||
RES_ENTRY('GRAS', GRAS)
|
|
||||||
RES_ENTRY('ICEE', ICEE)
|
|
||||||
RES_ENTRY('GOOO', GOOO)
|
|
||||||
RES_ENTRY('WODS', WODS)
|
|
||||||
RES_ENTRY('WATR', WATR)
|
|
||||||
RES_ENTRY('1MUD', _1MUD)
|
|
||||||
RES_ENTRY('1LAV', _1LAV)
|
|
||||||
RES_ENTRY('1SAN', _1SAN)
|
|
||||||
RES_ENTRY('1PRJ', _1PRJ)
|
|
||||||
RES_ENTRY('DCHR', DCHR)
|
|
||||||
RES_ENTRY('DCHS', DCHS)
|
|
||||||
RES_ENTRY('DCSH', DCSH)
|
|
||||||
RES_ENTRY('DENM', DENM)
|
|
||||||
RES_ENTRY('DESP', DESP)
|
|
||||||
RES_ENTRY('DESH', DESH)
|
|
||||||
RES_ENTRY('BTLE', BTLE)
|
|
||||||
RES_ENTRY('WASP', WASP)
|
|
||||||
RES_ENTRY('TALP', TALP)
|
|
||||||
RES_ENTRY('PTGM', PTGM)
|
|
||||||
RES_ENTRY('SPIR', SPIR)
|
|
||||||
RES_ENTRY('FPIR', FPIR)
|
|
||||||
RES_ENTRY('FFLE', FFLE)
|
|
||||||
RES_ENTRY('PARA', PARA)
|
|
||||||
RES_ENTRY('BMON', BMON)
|
|
||||||
RES_ENTRY('BFLR', BFLR)
|
|
||||||
RES_ENTRY('PBOS', PBOS)
|
|
||||||
RES_ENTRY('IBOS', IBOS)
|
|
||||||
RES_ENTRY('1SVA', _1SVA)
|
|
||||||
RES_ENTRY('1RPR', _1RPR)
|
|
||||||
RES_ENTRY('1MTR', _1MTR)
|
|
||||||
RES_ENTRY('1PDS', _1PDS)
|
|
||||||
RES_ENTRY('1FLB', _1FLB)
|
|
||||||
RES_ENTRY('1DRN', _1DRN)
|
|
||||||
RES_ENTRY('1MRE', _1MRE)
|
|
||||||
RES_ENTRY('CHOZ', CHOZ)
|
|
||||||
RES_ENTRY('JZAP', JZAP)
|
|
||||||
RES_ENTRY('1ISE', _1ISE)
|
|
||||||
RES_ENTRY('1BSE', _1BSE)
|
|
||||||
RES_ENTRY('1ATB', _1ATB)
|
|
||||||
RES_ENTRY('1ATA', _1ATA)
|
|
||||||
RES_ENTRY('BTSP', BTSP)
|
|
||||||
RES_ENTRY('WWSP', WWSP)
|
|
||||||
RES_ENTRY('TASP', TASP)
|
|
||||||
RES_ENTRY('TGSP', TGSP)
|
|
||||||
RES_ENTRY('SPSP', SPSP)
|
|
||||||
RES_ENTRY('FPSP', FPSP)
|
|
||||||
RES_ENTRY('FFSP', FFSP)
|
|
||||||
RES_ENTRY('PSSP', PSSP)
|
|
||||||
RES_ENTRY('BMSP', BMSP)
|
|
||||||
RES_ENTRY('BFSP', BFSP)
|
|
||||||
RES_ENTRY('PBSP', PBSP)
|
|
||||||
RES_ENTRY('IBSP', IBSP)
|
|
||||||
RES_ENTRY('2SVA', _2SVA)
|
|
||||||
RES_ENTRY('2RPR', _2RPR)
|
|
||||||
RES_ENTRY('2MTR', _2MTR)
|
|
||||||
RES_ENTRY('2PDS', _2PDS)
|
|
||||||
RES_ENTRY('2FLB', _2FLB)
|
|
||||||
RES_ENTRY('2DRN', _2DRN)
|
|
||||||
RES_ENTRY('2MRE', _2MRE)
|
|
||||||
RES_ENTRY('CHSP', CHSP)
|
|
||||||
RES_ENTRY('JZSP', JZSP)
|
|
||||||
RES_ENTRY('3ISE', _3ISE)
|
|
||||||
RES_ENTRY('3BSE', _3BSE)
|
|
||||||
RES_ENTRY('3ATB', _3ATB)
|
|
||||||
RES_ENTRY('3ATA', _3ATA)
|
|
||||||
RES_ENTRY('BTSH', BTSH)
|
|
||||||
RES_ENTRY('WWSH', WWSH)
|
|
||||||
RES_ENTRY('TASH', TASH)
|
|
||||||
RES_ENTRY('TGSH', TGSH)
|
|
||||||
RES_ENTRY('SPSH', SPSH)
|
|
||||||
RES_ENTRY('FPSH', FPSH)
|
|
||||||
RES_ENTRY('FFSH', FFSH)
|
|
||||||
RES_ENTRY('PSSH', PSSH)
|
|
||||||
RES_ENTRY('BMSH', BMSH)
|
|
||||||
RES_ENTRY('BFSH', BFSH)
|
|
||||||
RES_ENTRY('PBSH', PBSH)
|
|
||||||
RES_ENTRY('IBSH', IBSH)
|
|
||||||
RES_ENTRY('3SVA', _3SVA)
|
|
||||||
RES_ENTRY('3RPR', _3RPR)
|
|
||||||
RES_ENTRY('3MTR', _3MTR)
|
|
||||||
RES_ENTRY('3PDS', _3PDS)
|
|
||||||
RES_ENTRY('3FLB', _3FLB)
|
|
||||||
RES_ENTRY('3DRN', _3DRN)
|
|
||||||
RES_ENTRY('3MRE', _3MRE)
|
|
||||||
RES_ENTRY('CHSH', CHSH)
|
|
||||||
RES_ENTRY('JZSH', JZSH)
|
|
||||||
RES_ENTRY('5ISE', _5ISE)
|
|
||||||
RES_ENTRY('5BSE', _5BSE)
|
|
||||||
RES_ENTRY('5ATB', _5ATB)
|
|
||||||
RES_ENTRY('5ATA', _5ATA)
|
|
||||||
RES_ENTRY('NCDL', NCDL)
|
|
||||||
RES_ENTRY('DDCL', DDCL)
|
|
||||||
RES_ENTRY('CODL', CODL)
|
|
||||||
RES_ENTRY('MEDL', MEDL)
|
|
||||||
RES_ENTRY('GRDL', GRDL)
|
|
||||||
RES_ENTRY('ICDL', ICDL)
|
|
||||||
RES_ENTRY('GODL', GODL)
|
|
||||||
RES_ENTRY('WODL', WODL)
|
|
||||||
RES_ENTRY('WTDL', WTDL)
|
|
||||||
RES_ENTRY('3MUD', _3MUD)
|
|
||||||
RES_ENTRY('3LAV', _3LAV)
|
|
||||||
RES_ENTRY('3SAN', _3SAN)
|
|
||||||
RES_ENTRY('CHDL', CHDL)
|
|
||||||
RES_ENTRY('ENDL', ENDL)
|
|
||||||
|
|
||||||
U32_ENTRY('NSFX', NSFX)
|
|
||||||
U32_ENTRY('DSFX', DSFX)
|
|
||||||
U32_ENTRY('CSFX', CSFX)
|
|
||||||
U32_ENTRY('MSFX', MSFX)
|
|
||||||
U32_ENTRY('GRFX', GRFX)
|
|
||||||
U32_ENTRY('ICFX', ICFX)
|
|
||||||
U32_ENTRY('GOFX', GOFX)
|
|
||||||
U32_ENTRY('WSFX', WSFX)
|
|
||||||
U32_ENTRY('WTFX', WTFX)
|
|
||||||
U32_ENTRY('2MUD', _2MUD)
|
|
||||||
U32_ENTRY('2LAV', _2LAV)
|
|
||||||
U32_ENTRY('2SAN', _2SAN)
|
|
||||||
U32_ENTRY('2PRJ', _2PRJ)
|
|
||||||
U32_ENTRY('DCFX', DCFX)
|
|
||||||
U32_ENTRY('DSHX', DSHX)
|
|
||||||
U32_ENTRY('DEFX', DEFX)
|
|
||||||
U32_ENTRY('ESFX', ESFX)
|
|
||||||
U32_ENTRY('SHFX', SHFX)
|
|
||||||
U32_ENTRY('BEFX', BEFX)
|
|
||||||
U32_ENTRY('WWFX', WWFX)
|
|
||||||
U32_ENTRY('TAFX', TAFX)
|
|
||||||
U32_ENTRY('GTFX', GTFX)
|
|
||||||
U32_ENTRY('SPFX', SPFX)
|
|
||||||
U32_ENTRY('FPFX', FPFX)
|
|
||||||
U32_ENTRY('FFFX', FFFX)
|
|
||||||
U32_ENTRY('PAFX', PAFX)
|
|
||||||
U32_ENTRY('BMFX', BMFX)
|
|
||||||
U32_ENTRY('BFFX', BFFX)
|
|
||||||
U32_ENTRY('PBFX', PBFX)
|
|
||||||
U32_ENTRY('IBFX', IBFX)
|
|
||||||
U32_ENTRY('4SVA', _4SVA)
|
|
||||||
U32_ENTRY('4RPR', _4RPR)
|
|
||||||
U32_ENTRY('4MTR', _4MTR)
|
|
||||||
U32_ENTRY('4PDS', _4PDS)
|
|
||||||
U32_ENTRY('4FLB', _4FLB)
|
|
||||||
U32_ENTRY('4DRN', _4DRN)
|
|
||||||
U32_ENTRY('4MRE', _4MRE)
|
|
||||||
U32_ENTRY('CZFX', CZFX)
|
|
||||||
U32_ENTRY('JZAS', JZAS)
|
|
||||||
U32_ENTRY('2ISE', _2ISE)
|
|
||||||
U32_ENTRY('2BSE', _2BSE)
|
|
||||||
U32_ENTRY('2ATB', _2ATB)
|
|
||||||
U32_ENTRY('2ATA', _2ATA)
|
|
||||||
U32_ENTRY('BSFX', BSFX)
|
|
||||||
U32_ENTRY('TSFX', TSFX)
|
|
||||||
U32_ENTRY('GSFX', GSFX)
|
|
||||||
U32_ENTRY('SSFX', SSFX)
|
|
||||||
U32_ENTRY('FSFX', FSFX)
|
|
||||||
U32_ENTRY('SFFX', SFFX)
|
|
||||||
U32_ENTRY('PSFX', PSFX)
|
|
||||||
U32_ENTRY('SBFX', SBFX)
|
|
||||||
U32_ENTRY('PBSX', PBSX)
|
|
||||||
U32_ENTRY('IBSX', IBSX)
|
|
||||||
U32_ENTRY('5SVA', _5SVA)
|
|
||||||
U32_ENTRY('5RPR', _5RPR)
|
|
||||||
U32_ENTRY('5MTR', _5MTR)
|
|
||||||
U32_ENTRY('5PDS', _5PDS)
|
|
||||||
U32_ENTRY('5FLB', _5FLB)
|
|
||||||
U32_ENTRY('5DRN', _5DRN)
|
|
||||||
U32_ENTRY('5MRE', _5MRE)
|
|
||||||
U32_ENTRY('JZPS', JZPS)
|
|
||||||
U32_ENTRY('4ISE', _4ISE)
|
|
||||||
U32_ENTRY('4BSE', _4BSE)
|
|
||||||
U32_ENTRY('4ATB', _4ATB)
|
|
||||||
U32_ENTRY('4ATA', _4ATA)
|
|
||||||
U32_ENTRY('BHFX', BHFX)
|
|
||||||
U32_ENTRY('WHFX', WHFX)
|
|
||||||
U32_ENTRY('THFX', THFX)
|
|
||||||
U32_ENTRY('GHFX', GHFX)
|
|
||||||
U32_ENTRY('FHFX', FHFX)
|
|
||||||
U32_ENTRY('HFFX', HFFX)
|
|
||||||
U32_ENTRY('PHFX', PHFX)
|
|
||||||
U32_ENTRY('MHFX', MHFX)
|
|
||||||
U32_ENTRY('HBFX', HBFX)
|
|
||||||
U32_ENTRY('PBHX', PBHX)
|
|
||||||
U32_ENTRY('IBHX', IBHX)
|
|
||||||
U32_ENTRY('6SVA', _6SVA)
|
|
||||||
U32_ENTRY('6RPR', _6RPR)
|
|
||||||
U32_ENTRY('6MTR', _6MTR)
|
|
||||||
U32_ENTRY('6PDS', _6PDS)
|
|
||||||
U32_ENTRY('6FLB', _6FLB)
|
|
||||||
U32_ENTRY('6DRN', _6DRN)
|
|
||||||
U32_ENTRY('6MRE', _6MRE)
|
|
||||||
U32_ENTRY('CHFX', CHFX)
|
|
||||||
U32_ENTRY('JZHS', JZHS)
|
|
||||||
U32_ENTRY('6ISE', _6ISE)
|
|
||||||
U32_ENTRY('6BSE', _6BSE)
|
|
||||||
U32_ENTRY('6ATB', _6ATB)
|
|
||||||
U32_ENTRY('6ATA', _6ATA)
|
|
||||||
|
|
||||||
FLOAT_ENTRY('RNGE', x30_RNGE)
|
|
||||||
FLOAT_ENTRY('FOFF', x34_FOFF)
|
|
||||||
|
|
||||||
#undef ENTRY
|
|
||||||
#undef RES_ENTRY
|
|
||||||
#undef U32_ENTRY
|
|
||||||
#undef FLOAT_ENTRY
|
|
|
@ -1,57 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
|
||||||
|
|
||||||
#include <athena/DNA.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct _CRSM {
|
|
||||||
static constexpr ParticleType Type = ParticleType::CRSM;
|
|
||||||
#define RES_ENTRY(name, identifier) ChildResourceFactory<IDType> identifier;
|
|
||||||
#define U32_ENTRY(name, identifier) uint32_t identifier = ~0;
|
|
||||||
#define FLOAT_ENTRY(name, identifier) float identifier = 0.f;
|
|
||||||
#include "CRSC.def"
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
void constexpr Enumerate(_Func f) {
|
|
||||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
|
||||||
#include "CRSC.def"
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
bool constexpr Lookup(FourCC fcc, _Func f) {
|
|
||||||
switch (fcc.toUint32()) {
|
|
||||||
#define ENTRY(name, identifier) \
|
|
||||||
case SBIG(name): \
|
|
||||||
f(identifier); \
|
|
||||||
return true;
|
|
||||||
#include "CRSC.def"
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <class IDType>
|
|
||||||
using CRSM = PPImpl<_CRSM<IDType>>;
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractCRSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteCRSM(const CRSM<IDType>& crsm, const hecl::ProjectPath& outPath);
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,40 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/DGRP.hpp"
|
|
||||||
|
|
||||||
#include <athena/DNAYaml.hpp>
|
|
||||||
#include <athena/FileWriter.hpp>
|
|
||||||
#include <athena/IStreamWriter.hpp>
|
|
||||||
|
|
||||||
#include <hecl/hecl.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNADGRP {
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractDGRP(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
DGRP<IDType> dgrp;
|
|
||||||
dgrp.read(rs);
|
|
||||||
athena::io::ToYAMLStream(dgrp, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template bool ExtractDGRP<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractDGRP<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteDGRP(const DGRP<IDType>& dgrp, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
dgrp.write(w);
|
|
||||||
int64_t rem = w.position() % 32;
|
|
||||||
if (rem)
|
|
||||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
|
||||||
w.writeUByte(0xff);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template bool WriteDGRP<UniqueID32>(const DGRP<UniqueID32>& dgrp, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteDGRP<UniqueID64>(const DGRP<UniqueID64>& dgrp, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNADGRP
|
|
|
@ -1,45 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNADGRP {
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct AT_SPECIALIZE_PARMS(DataSpec::UniqueID32, DataSpec::UniqueID64) DGRP : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> dependCount;
|
|
||||||
struct ObjectTag : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
DNAFourCC type;
|
|
||||||
Value<IDType> id;
|
|
||||||
|
|
||||||
bool validate() const {
|
|
||||||
if (!id.isValid())
|
|
||||||
return false;
|
|
||||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(id);
|
|
||||||
return path && !path.isNone();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<ObjectTag, AT_DNA_COUNT(dependCount)> depends;
|
|
||||||
|
|
||||||
void validateDeps() {
|
|
||||||
std::vector<ObjectTag> newDeps;
|
|
||||||
newDeps.reserve(depends.size());
|
|
||||||
for (const ObjectTag& tag : depends)
|
|
||||||
if (tag.validate())
|
|
||||||
newDeps.push_back(tag);
|
|
||||||
depends = std::move(newDeps);
|
|
||||||
dependCount = atUint32(depends.size());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractDGRP(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteDGRP(const DGRP<IDType>& dgrp, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNADGRP
|
|
|
@ -1,199 +0,0 @@
|
||||||
#include "DNACommon.hpp"
|
|
||||||
#include "PAK.hpp"
|
|
||||||
#include "boo/ThreadLocalPtr.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
logvisor::Module LogDNACommon("DataSpec::DNACommon");
|
|
||||||
ThreadLocalPtr<SpecBase> g_curSpec;
|
|
||||||
ThreadLocalPtr<PAKRouterBase> g_PakRouter;
|
|
||||||
ThreadLocalPtr<hecl::blender::Token> g_ThreadBlenderToken;
|
|
||||||
ThreadLocalPtr<hecl::Database::Project> UniqueIDBridge::s_Project;
|
|
||||||
UniqueID32 UniqueID32::kInvalidId;
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const IDType& id, bool silenceWarnings) {
|
|
||||||
/* Try PAKRouter first (only available at extract) */
|
|
||||||
PAKRouterBase* pakRouter = g_PakRouter.get();
|
|
||||||
if (pakRouter) {
|
|
||||||
hecl::ProjectPath path = pakRouter->getWorking(id, silenceWarnings);
|
|
||||||
if (path)
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try project cache second (populated with paths read from YAML resources) */
|
|
||||||
hecl::Database::Project* project = s_Project.get();
|
|
||||||
if (!project) {
|
|
||||||
if (pakRouter) {
|
|
||||||
if (hecl::VerbosityLevel >= 1 && !silenceWarnings && id.isValid())
|
|
||||||
LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to translate {} to path"), id);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("g_PakRouter or s_Project must be set to non-null before "
|
|
||||||
"calling UniqueIDBridge::TranslatePakIdToPath"));
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const hecl::ProjectPath* search = project->lookupBridgePath(id.toUint64());
|
|
||||||
if (!search) {
|
|
||||||
if (hecl::VerbosityLevel >= 1 && !silenceWarnings && id.isValid())
|
|
||||||
LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to translate {} to path"), id);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return *search;
|
|
||||||
}
|
|
||||||
template hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const UniqueID32& id, bool silenceWarnings);
|
|
||||||
template hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const UniqueID64& id, bool silenceWarnings);
|
|
||||||
template hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const UniqueID128& id, bool silenceWarnings);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
hecl::ProjectPath UniqueIDBridge::MakePathFromString(std::string_view str) {
|
|
||||||
if (str.empty())
|
|
||||||
return {};
|
|
||||||
hecl::Database::Project* project = s_Project.get();
|
|
||||||
if (!project)
|
|
||||||
LogDNACommon.report(logvisor::Fatal,
|
|
||||||
FMT_STRING("UniqueIDBridge::setGlobalProject must be called before MakePathFromString"));
|
|
||||||
hecl::ProjectPath path = hecl::ProjectPath(*project, str);
|
|
||||||
project->addBridgePathToCache(IDType(path).toUint64(), path);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
template hecl::ProjectPath UniqueIDBridge::MakePathFromString<UniqueID32>(std::string_view str);
|
|
||||||
template hecl::ProjectPath UniqueIDBridge::MakePathFromString<UniqueID64>(std::string_view str);
|
|
||||||
|
|
||||||
void UniqueIDBridge::SetThreadProject(hecl::Database::Project& project) { s_Project.reset(&project); }
|
|
||||||
|
|
||||||
/** PAK 32-bit Unique ID */
|
|
||||||
template <>
|
|
||||||
void UniqueID32::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
|
||||||
assign(reader.readUint32Big());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID32::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
|
||||||
writer.writeUint32Big(m_id);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID32::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
|
|
||||||
*this = UniqueIDBridge::MakePathFromString<UniqueID32>(reader.readString());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID32::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& writer) {
|
|
||||||
if (!isValid())
|
|
||||||
return;
|
|
||||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(*this);
|
|
||||||
if (!path)
|
|
||||||
return;
|
|
||||||
writer.writeString(path.getEncodableString());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID32::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string UniqueID32::toString() const { return fmt::format(FMT_STRING("{}"), *this); }
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void UniqueID32Zero::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
|
||||||
UniqueID32::Enumerate<BigDNA::Read>(reader);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID32Zero::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
|
||||||
writer.writeUint32Big(isValid() ? m_id : 0);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID32Zero::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
|
|
||||||
UniqueID32::Enumerate<BigDNA::ReadYaml>(reader);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID32Zero::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& writer) {
|
|
||||||
UniqueID32::Enumerate<BigDNA::WriteYaml>(writer);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID32Zero::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
UniqueID32::Enumerate<BigDNA::BinarySize>(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** PAK 64-bit Unique ID */
|
|
||||||
template <>
|
|
||||||
void UniqueID64::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
|
||||||
assign(reader.readUint64Big());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID64::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
|
||||||
writer.writeUint64Big(m_id);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID64::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
|
|
||||||
*this = UniqueIDBridge::MakePathFromString<UniqueID64>(reader.readString());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID64::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& writer) {
|
|
||||||
if (!isValid())
|
|
||||||
return;
|
|
||||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(*this);
|
|
||||||
if (!path)
|
|
||||||
return;
|
|
||||||
writer.writeString(path.getEncodableString());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID64::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string UniqueID64::toString() const { return fmt::format(FMT_STRING("{}"), *this); }
|
|
||||||
|
|
||||||
/** PAK 128-bit Unique ID */
|
|
||||||
template <>
|
|
||||||
void UniqueID128::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
|
||||||
m_id.id[0] = reader.readUint64Big();
|
|
||||||
m_id.id[1] = reader.readUint64Big();
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID128::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
|
||||||
writer.writeUint64Big(m_id.id[0]);
|
|
||||||
writer.writeUint64Big(m_id.id[1]);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID128::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
|
|
||||||
*this = UniqueIDBridge::MakePathFromString<UniqueID128>(reader.readString());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID128::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& writer) {
|
|
||||||
if (!isValid())
|
|
||||||
return;
|
|
||||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(*this);
|
|
||||||
if (!path)
|
|
||||||
return;
|
|
||||||
writer.writeString(path.getEncodableString());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void UniqueID128::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string UniqueID128::toString() const { return fmt::format(FMT_STRING("{}"), *this); }
|
|
||||||
|
|
||||||
/** Word Bitmap reader/writer */
|
|
||||||
void WordBitmap::read(athena::io::IStreamReader& reader, size_t bitCount) {
|
|
||||||
m_bitCount = bitCount;
|
|
||||||
size_t wordCount = (bitCount + 31) / 32;
|
|
||||||
m_words.clear();
|
|
||||||
m_words.reserve(wordCount);
|
|
||||||
for (size_t w = 0; w < wordCount; ++w)
|
|
||||||
m_words.push_back(reader.readUint32Big());
|
|
||||||
}
|
|
||||||
void WordBitmap::write(athena::io::IStreamWriter& writer) const {
|
|
||||||
for (atUint32 word : m_words)
|
|
||||||
writer.writeUint32Big(word);
|
|
||||||
}
|
|
||||||
void WordBitmap::binarySize(size_t& __isz) const { __isz += m_words.size() * 4; }
|
|
||||||
|
|
||||||
hecl::ProjectPath GetPathBeginsWith(const hecl::DirectoryEnumerator& dEnum, const hecl::ProjectPath& parentPath,
|
|
||||||
std::string_view test) {
|
|
||||||
for (const auto& ent : dEnum)
|
|
||||||
if (hecl::StringUtils::BeginsWith(ent.m_name, test))
|
|
||||||
return hecl::ProjectPath(parentPath, ent.m_name);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,394 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include "logvisor/logvisor.hpp"
|
|
||||||
#include "athena/DNAYaml.hpp"
|
|
||||||
#include "hecl/Database.hpp"
|
|
||||||
#include "../SpecBase.hpp"
|
|
||||||
#include "boo/ThreadLocalPtr.hpp"
|
|
||||||
#include "zeus/CColor.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
struct SpecBase;
|
|
||||||
|
|
||||||
extern logvisor::Module LogDNACommon;
|
|
||||||
extern ThreadLocalPtr<SpecBase> g_curSpec;
|
|
||||||
extern ThreadLocalPtr<class PAKRouterBase> g_PakRouter;
|
|
||||||
extern ThreadLocalPtr<hecl::blender::Token> g_ThreadBlenderToken;
|
|
||||||
|
|
||||||
/* This comes up a great deal */
|
|
||||||
using BigDNA = athena::io::DNA<athena::Endian::Big>;
|
|
||||||
using BigDNAV = athena::io::DNAV<athena::Endian::Big>;
|
|
||||||
using BigDNAVYaml = athena::io::DNAVYaml<athena::Endian::Big>;
|
|
||||||
|
|
||||||
/** FourCC with DNA read/write */
|
|
||||||
using DNAFourCC = hecl::DNAFourCC;
|
|
||||||
|
|
||||||
class DNAColor final : public BigDNA, public zeus::CColor {
|
|
||||||
public:
|
|
||||||
DNAColor() = default;
|
|
||||||
DNAColor(const zeus::CColor& color) : zeus::CColor(color) {}
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
};
|
|
||||||
template <>
|
|
||||||
inline void DNAColor::Enumerate<BigDNA::Read>(typename Read::StreamT& _r) {
|
|
||||||
zeus::CColor::readRGBABig(_r);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline void DNAColor::Enumerate<BigDNA::Write>(typename Write::StreamT& _w) {
|
|
||||||
zeus::CColor::writeRGBABig(_w);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline void DNAColor::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& _r) {
|
|
||||||
size_t count;
|
|
||||||
if (auto v = _r.enterSubVector(count)) {
|
|
||||||
zeus::simd_floats f;
|
|
||||||
f[0] = (count >= 1) ? _r.readFloat() : 0.f;
|
|
||||||
f[1] = (count >= 2) ? _r.readFloat() : 0.f;
|
|
||||||
f[2] = (count >= 3) ? _r.readFloat() : 0.f;
|
|
||||||
f[3] = (count >= 4) ? _r.readFloat() : 0.f;
|
|
||||||
mSimd.copy_from(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline void DNAColor::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& _w) {
|
|
||||||
if (auto v = _w.enterSubVector()) {
|
|
||||||
zeus::simd_floats f(mSimd);
|
|
||||||
_w.writeFloat(f[0]);
|
|
||||||
_w.writeFloat(f[1]);
|
|
||||||
_w.writeFloat(f[2]);
|
|
||||||
_w.writeFloat(f[3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline void DNAColor::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& _s) {
|
|
||||||
_s += 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
using FourCC = hecl::FourCC;
|
|
||||||
class UniqueID32;
|
|
||||||
class UniqueID64;
|
|
||||||
class UniqueID128;
|
|
||||||
|
|
||||||
/** Common virtual interface for runtime ambiguity resolution */
|
|
||||||
class PAKRouterBase {
|
|
||||||
protected:
|
|
||||||
const SpecBase& m_dataSpec;
|
|
||||||
|
|
||||||
public:
|
|
||||||
PAKRouterBase(const SpecBase& dataSpec) : m_dataSpec(dataSpec) {}
|
|
||||||
hecl::Database::Project& getProject() const { return m_dataSpec.getProject(); }
|
|
||||||
virtual hecl::ProjectPath getWorking(const UniqueID32&, bool silenceWarnings = false) const {
|
|
||||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID32 specialization"));
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
virtual hecl::ProjectPath getWorking(const UniqueID64&, bool silenceWarnings = false) const {
|
|
||||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID64 specialization"));
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
virtual hecl::ProjectPath getWorking(const UniqueID128&, bool silenceWarnings = false) const {
|
|
||||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID128 specialization"));
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Globally-accessed manager allowing UniqueID* classes to directly
|
|
||||||
* lookup destination paths of resources */
|
|
||||||
class UniqueIDBridge {
|
|
||||||
friend class UniqueID32;
|
|
||||||
friend class UniqueID64;
|
|
||||||
|
|
||||||
static ThreadLocalPtr<hecl::Database::Project> s_Project;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <class IDType>
|
|
||||||
static hecl::ProjectPath TranslatePakIdToPath(const IDType& id, bool silenceWarnings = false);
|
|
||||||
template <class IDType>
|
|
||||||
static hecl::ProjectPath MakePathFromString(std::string_view str);
|
|
||||||
|
|
||||||
static void SetThreadProject(hecl::Database::Project& project);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** PAK 32-bit Unique ID */
|
|
||||||
class UniqueID32 : public BigDNA {
|
|
||||||
protected:
|
|
||||||
uint32_t m_id = 0xffffffff;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using value_type = uint32_t;
|
|
||||||
static UniqueID32 kInvalidId;
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
bool isValid() const noexcept { return m_id != 0xffffffff && m_id != 0; }
|
|
||||||
void assign(uint32_t id) noexcept { m_id = id ? id : 0xffffffff; }
|
|
||||||
|
|
||||||
UniqueID32& operator=(const hecl::ProjectPath& path) noexcept {
|
|
||||||
assign(path.parsedHash32());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const UniqueID32& other) const noexcept { return m_id != other.m_id; }
|
|
||||||
bool operator==(const UniqueID32& other) const noexcept { return m_id == other.m_id; }
|
|
||||||
bool operator<(const UniqueID32& other) const noexcept { return m_id < other.m_id; }
|
|
||||||
uint32_t toUint32() const noexcept { return m_id; }
|
|
||||||
uint64_t toUint64() const noexcept { return m_id; }
|
|
||||||
std::string toString() const;
|
|
||||||
void clear() noexcept { m_id = 0xffffffff; }
|
|
||||||
|
|
||||||
UniqueID32() noexcept = default;
|
|
||||||
UniqueID32(uint32_t idin) noexcept { assign(idin); }
|
|
||||||
UniqueID32(athena::io::IStreamReader& reader) { read(reader); }
|
|
||||||
UniqueID32(const hecl::ProjectPath& path) noexcept { *this = path; }
|
|
||||||
UniqueID32(const char* hexStr) noexcept {
|
|
||||||
char copy[9];
|
|
||||||
strncpy(copy, hexStr, 8);
|
|
||||||
copy[8] = '\0';
|
|
||||||
assign(strtoul(copy, nullptr, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr size_t BinarySize() noexcept { return 4; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/** PAK 32-bit Unique ID - writes zero when invalid */
|
|
||||||
class UniqueID32Zero : public UniqueID32 {
|
|
||||||
public:
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Delete __d2;
|
|
||||||
using UniqueID32::UniqueID32;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** PAK 64-bit Unique ID */
|
|
||||||
class UniqueID64 : public BigDNA {
|
|
||||||
uint64_t m_id = 0xffffffffffffffff;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using value_type = uint64_t;
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
bool isValid() const noexcept { return m_id != 0xffffffffffffffff && m_id != 0; }
|
|
||||||
void assign(uint64_t id) noexcept { m_id = id ? id : 0xffffffffffffffff; }
|
|
||||||
|
|
||||||
UniqueID64& operator=(const hecl::ProjectPath& path) noexcept {
|
|
||||||
assign(path.hash().val64());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const UniqueID64& other) const noexcept { return m_id != other.m_id; }
|
|
||||||
bool operator==(const UniqueID64& other) const noexcept { return m_id == other.m_id; }
|
|
||||||
bool operator<(const UniqueID64& other) const noexcept { return m_id < other.m_id; }
|
|
||||||
uint64_t toUint64() const noexcept { return m_id; }
|
|
||||||
std::string toString() const;
|
|
||||||
void clear() noexcept { m_id = 0xffffffffffffffff; }
|
|
||||||
|
|
||||||
UniqueID64() noexcept = default;
|
|
||||||
UniqueID64(uint64_t idin) noexcept { assign(idin); }
|
|
||||||
UniqueID64(athena::io::IStreamReader& reader) { read(reader); }
|
|
||||||
UniqueID64(const hecl::ProjectPath& path) noexcept { *this = path; }
|
|
||||||
UniqueID64(const char* hexStr) noexcept {
|
|
||||||
char copy[17];
|
|
||||||
std::strncpy(copy, hexStr, 16);
|
|
||||||
copy[16] = '\0';
|
|
||||||
assign(std::strtoull(copy, nullptr, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr size_t BinarySize() noexcept { return 8; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/** PAK 128-bit Unique ID */
|
|
||||||
class UniqueID128 : public BigDNA {
|
|
||||||
public:
|
|
||||||
union Value {
|
|
||||||
uint64_t id[2];
|
|
||||||
#if __SSE__
|
|
||||||
__m128i id128;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
Value m_id;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using value_type = uint64_t;
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
UniqueID128() noexcept {
|
|
||||||
m_id.id[0] = 0xffffffffffffffff;
|
|
||||||
m_id.id[1] = 0xffffffffffffffff;
|
|
||||||
}
|
|
||||||
UniqueID128(uint64_t idin) noexcept {
|
|
||||||
m_id.id[0] = idin;
|
|
||||||
m_id.id[1] = 0;
|
|
||||||
}
|
|
||||||
bool isValid() const noexcept {
|
|
||||||
return m_id.id[0] != 0xffffffffffffffff && m_id.id[0] != 0 && m_id.id[1] != 0xffffffffffffffff && m_id.id[1] != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
UniqueID128& operator=(const hecl::ProjectPath& path) noexcept {
|
|
||||||
m_id.id[0] = path.hash().val64();
|
|
||||||
m_id.id[1] = 0;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
UniqueID128(const hecl::ProjectPath& path) noexcept { *this = path; }
|
|
||||||
|
|
||||||
bool operator!=(const UniqueID128& other) const noexcept {
|
|
||||||
#if __SSE__
|
|
||||||
__m128i vcmp = _mm_cmpeq_epi32(m_id.id128, other.m_id.id128);
|
|
||||||
int vmask = _mm_movemask_epi8(vcmp);
|
|
||||||
return vmask != 0xffff;
|
|
||||||
#else
|
|
||||||
return (m_id.id[0] != other.m_id.id[0]) || (m_id.id[1] != other.m_id.id[1]);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
bool operator==(const UniqueID128& other) const noexcept {
|
|
||||||
#if __SSE__
|
|
||||||
__m128i vcmp = _mm_cmpeq_epi32(m_id.id128, other.m_id.id128);
|
|
||||||
int vmask = _mm_movemask_epi8(vcmp);
|
|
||||||
return vmask == 0xffff;
|
|
||||||
#else
|
|
||||||
return (m_id.id[0] == other.m_id.id[0]) && (m_id.id[1] == other.m_id.id[1]);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
bool operator<(const UniqueID128& other) const noexcept {
|
|
||||||
return m_id.id[0] < other.m_id.id[0] || (m_id.id[0] == other.m_id.id[0] && m_id.id[1] < other.m_id.id[1]);
|
|
||||||
}
|
|
||||||
void clear() noexcept {
|
|
||||||
m_id.id[0] = 0xffffffffffffffff;
|
|
||||||
m_id.id[1] = 0xffffffffffffffff;
|
|
||||||
}
|
|
||||||
uint64_t toUint64() const noexcept { return m_id.id[0]; }
|
|
||||||
uint64_t toHighUint64() const noexcept { return m_id.id[0]; }
|
|
||||||
uint64_t toLowUint64() const noexcept { return m_id.id[1]; }
|
|
||||||
std::string toString() const;
|
|
||||||
|
|
||||||
static constexpr size_t BinarySize() noexcept { return 16; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Casts ID type to its null-zero equivalent */
|
|
||||||
template <class T>
|
|
||||||
using CastIDToZero = typename std::conditional_t<std::is_same_v<T, UniqueID32>, UniqueID32Zero, T>;
|
|
||||||
|
|
||||||
/** Word Bitmap reader/writer */
|
|
||||||
class WordBitmap {
|
|
||||||
std::vector<atUint32> m_words;
|
|
||||||
size_t m_bitCount = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void read(athena::io::IStreamReader& reader, size_t bitCount);
|
|
||||||
void write(athena::io::IStreamWriter& writer) const;
|
|
||||||
void reserve(size_t bitCount) { m_words.reserve((bitCount + 31) / 32); }
|
|
||||||
void binarySize(size_t& __isz) const;
|
|
||||||
size_t getBitCount() const { return m_bitCount; }
|
|
||||||
bool getBit(size_t idx) const {
|
|
||||||
size_t wordIdx = idx / 32;
|
|
||||||
if (wordIdx >= m_words.size())
|
|
||||||
return false;
|
|
||||||
size_t wordCur = idx % 32;
|
|
||||||
return (m_words[wordIdx] >> wordCur) & 0x1;
|
|
||||||
}
|
|
||||||
void setBit(size_t idx) {
|
|
||||||
size_t wordIdx = idx / 32;
|
|
||||||
while (wordIdx >= m_words.size())
|
|
||||||
m_words.push_back(0);
|
|
||||||
size_t wordCur = idx % 32;
|
|
||||||
m_words[wordIdx] |= (1 << wordCur);
|
|
||||||
m_bitCount = std::max(m_bitCount, idx + 1);
|
|
||||||
}
|
|
||||||
void unsetBit(size_t idx) {
|
|
||||||
size_t wordIdx = idx / 32;
|
|
||||||
while (wordIdx >= m_words.size())
|
|
||||||
m_words.push_back(0);
|
|
||||||
size_t wordCur = idx % 32;
|
|
||||||
m_words[wordIdx] &= ~(1 << wordCur);
|
|
||||||
m_bitCount = std::max(m_bitCount, idx + 1);
|
|
||||||
}
|
|
||||||
void clear() {
|
|
||||||
m_words.clear();
|
|
||||||
m_bitCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Iterator {
|
|
||||||
friend class WordBitmap;
|
|
||||||
const WordBitmap& m_bmp;
|
|
||||||
size_t m_idx = 0;
|
|
||||||
Iterator(const WordBitmap& bmp, size_t idx) : m_bmp(bmp), m_idx(idx) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
using iterator_category = std::forward_iterator_tag;
|
|
||||||
using value_type = bool;
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
using pointer = bool*;
|
|
||||||
using reference = bool&;
|
|
||||||
|
|
||||||
Iterator& operator++() {
|
|
||||||
++m_idx;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
bool operator*() const { return m_bmp.getBit(m_idx); }
|
|
||||||
bool operator!=(const Iterator& other) const { return m_idx != other.m_idx; }
|
|
||||||
};
|
|
||||||
Iterator begin() const { return Iterator(*this, 0); }
|
|
||||||
Iterator end() const { return Iterator(*this, m_bitCount); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Resource cooker function */
|
|
||||||
using ResCooker = std::function<bool(const hecl::ProjectPath&, const hecl::ProjectPath&)>;
|
|
||||||
|
|
||||||
/** Mappings of resources involved in extracting characters */
|
|
||||||
template <class IDType>
|
|
||||||
struct CharacterAssociations {
|
|
||||||
struct RigPair {
|
|
||||||
IDType cskr, cinf;
|
|
||||||
};
|
|
||||||
struct ModelRigPair {
|
|
||||||
IDType cinf, cmdl;
|
|
||||||
};
|
|
||||||
/* CMDL -> (CSKR, CINF) */
|
|
||||||
std::unordered_map<IDType, RigPair> m_cmdlRigs;
|
|
||||||
/* CSKR -> ANCS */
|
|
||||||
std::unordered_map<IDType, std::pair<IDType, std::string>> m_cskrToCharacter;
|
|
||||||
/* ANCS -> (CINF, CMDL) */
|
|
||||||
std::unordered_multimap<IDType, std::pair<ModelRigPair, std::string>> m_characterToAttachmentRigs;
|
|
||||||
using MultimapIteratorPair =
|
|
||||||
std::pair<typename std::unordered_multimap<IDType, std::pair<ModelRigPair, std::string>>::const_iterator,
|
|
||||||
typename std::unordered_multimap<IDType, std::pair<ModelRigPair, std::string>>::const_iterator>;
|
|
||||||
void addAttachmentRig(IDType character, IDType cinf, IDType cmdl, const char* name) {
|
|
||||||
auto range = m_characterToAttachmentRigs.equal_range(character);
|
|
||||||
for (auto it = range.first; it != range.second; ++it)
|
|
||||||
if (it->second.second == name)
|
|
||||||
return;
|
|
||||||
m_characterToAttachmentRigs.insert(std::make_pair(character, std::make_pair(ModelRigPair{cinf, cmdl}, name)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
hecl::ProjectPath GetPathBeginsWith(const hecl::DirectoryEnumerator& dEnum, const hecl::ProjectPath& parentPath,
|
|
||||||
std::string_view test);
|
|
||||||
inline hecl::ProjectPath GetPathBeginsWith(const hecl::ProjectPath& parentPath, std::string_view test) {
|
|
||||||
return GetPathBeginsWith(hecl::DirectoryEnumerator(parentPath.getAbsolutePath()), parentPath, test);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
||||||
|
|
||||||
/* Hash template-specializations for UniqueID types */
|
|
||||||
namespace std {
|
|
||||||
template <>
|
|
||||||
struct hash<DataSpec::DNAFourCC> {
|
|
||||||
size_t operator()(const DataSpec::DNAFourCC& fcc) const noexcept { return fcc.toUint32(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<DataSpec::UniqueID32> {
|
|
||||||
size_t operator()(const DataSpec::UniqueID32& id) const noexcept { return id.toUint32(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<DataSpec::UniqueID64> {
|
|
||||||
size_t operator()(const DataSpec::UniqueID64& id) const noexcept { return id.toUint64(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<DataSpec::UniqueID128> {
|
|
||||||
size_t operator()(const DataSpec::UniqueID128& id) const noexcept { return id.toHighUint64() ^ id.toLowUint64(); }
|
|
||||||
};
|
|
||||||
} // namespace std
|
|
||||||
|
|
||||||
FMT_CUSTOM_FORMATTER(DataSpec::UniqueID32, "{:08X}", obj.toUint32())
|
|
||||||
FMT_CUSTOM_FORMATTER(DataSpec::UniqueID32Zero, "{:08X}", obj.toUint32())
|
|
||||||
FMT_CUSTOM_FORMATTER(DataSpec::UniqueID64, "{:016X}", obj.toUint64())
|
|
||||||
FMT_CUSTOM_FORMATTER(DataSpec::UniqueID128, "{:016X}{:016X}", obj.toHighUint64(), obj.toLowUint64())
|
|
|
@ -1,51 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/DPSC.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template struct PPImpl<_DPSM<UniqueID32>>;
|
|
||||||
template struct PPImpl<_DPSM<UniqueID64>>;
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_DPSM<UniqueID32>>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_DPSM<UniqueID64>>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view PPImpl<_DPSM<UniqueID32>>::DNAType() {
|
|
||||||
return "DPSM<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view PPImpl<_DPSM<UniqueID64>>::DNAType() {
|
|
||||||
return "DPSM<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractDPSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
DPSM<IDType> dpsm;
|
|
||||||
dpsm.read(rs);
|
|
||||||
athena::io::ToYAMLStream(dpsm, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template bool ExtractDPSM<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractDPSM<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteDPSM(const DPSM<IDType>& dpsm, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
dpsm.write(w);
|
|
||||||
int64_t rem = w.position() % 32;
|
|
||||||
if (rem)
|
|
||||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
|
||||||
w.writeUByte(0xff);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template bool WriteDPSM<UniqueID32>(const DPSM<UniqueID32>& dpsm, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteDPSM<UniqueID64>(const DPSM<UniqueID64>& dpsm, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,28 +0,0 @@
|
||||||
#ifndef ENTRY
|
|
||||||
#define ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ENTRY('1LFT', x0_quad.x0_LFT)
|
|
||||||
ENTRY('1SZE', x0_quad.x4_SZE)
|
|
||||||
ENTRY('1ROT', x0_quad.x8_ROT)
|
|
||||||
ENTRY('1OFF', x0_quad.xc_OFF)
|
|
||||||
ENTRY('1CLR', x0_quad.x10_CLR)
|
|
||||||
ENTRY('1TEX', x0_quad.x14_TEX)
|
|
||||||
ENTRY('1ADD', x0_quad.x18_ADD)
|
|
||||||
ENTRY('2LFT', x1c_quad.x0_LFT)
|
|
||||||
ENTRY('2SZE', x1c_quad.x4_SZE)
|
|
||||||
ENTRY('2ROT', x1c_quad.x8_ROT)
|
|
||||||
ENTRY('2OFF', x1c_quad.xc_OFF)
|
|
||||||
ENTRY('2CLR', x1c_quad.x10_CLR)
|
|
||||||
ENTRY('2TEX', x1c_quad.x14_TEX)
|
|
||||||
ENTRY('2ADD', x1c_quad.x18_ADD)
|
|
||||||
ENTRY('DMDL', x38_DMDL)
|
|
||||||
ENTRY('DLFT', x48_DLFT)
|
|
||||||
ENTRY('DMOP', x4c_DMOP)
|
|
||||||
ENTRY('DMRT', x50_DMRT)
|
|
||||||
ENTRY('DMSC', x54_DMSC)
|
|
||||||
ENTRY('DMCL', x58_DMCL)
|
|
||||||
ENTRY('DMAB', x5c_24_DMAB)
|
|
||||||
ENTRY('DMOO', x5c_25_DMOO)
|
|
||||||
|
|
||||||
#undef ENTRY
|
|
|
@ -1,76 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
|
||||||
|
|
||||||
#include <athena/FileWriter.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct _DPSM {
|
|
||||||
static constexpr ParticleType Type = ParticleType::DPSM;
|
|
||||||
|
|
||||||
struct SQuadDescr {
|
|
||||||
IntElementFactory x0_LFT;
|
|
||||||
RealElementFactory x4_SZE;
|
|
||||||
RealElementFactory x8_ROT;
|
|
||||||
VectorElementFactory xc_OFF;
|
|
||||||
ColorElementFactory x10_CLR;
|
|
||||||
UVElementFactory<IDType> x14_TEX;
|
|
||||||
bool x18_ADD = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
SQuadDescr x0_quad;
|
|
||||||
SQuadDescr x1c_quad;
|
|
||||||
ChildResourceFactory<IDType> x38_DMDL;
|
|
||||||
IntElementFactory x48_DLFT;
|
|
||||||
VectorElementFactory x4c_DMOP;
|
|
||||||
VectorElementFactory x50_DMRT;
|
|
||||||
VectorElementFactory x54_DMSC;
|
|
||||||
ColorElementFactory x58_DMCL;
|
|
||||||
|
|
||||||
bool x5c_24_DMAB = false;
|
|
||||||
bool x5c_25_DMOO = false;
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
void constexpr Enumerate(_Func f) {
|
|
||||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
|
||||||
#include "DPSC.def"
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
bool constexpr Lookup(FourCC fcc, _Func f) {
|
|
||||||
switch (fcc.toUint32()) {
|
|
||||||
#define ENTRY(name, identifier) \
|
|
||||||
case SBIG(name): \
|
|
||||||
f(identifier); \
|
|
||||||
return true;
|
|
||||||
#include "DPSC.def"
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <class IDType>
|
|
||||||
using DPSM = PPImpl<_DPSM<IDType>>;
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractDPSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteDPSM(const DPSM<IDType>& dpsm, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,258 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/DeafBabe.hpp"
|
|
||||||
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/AROTBuilder.hpp"
|
|
||||||
#include "DataSpec/DNAMP1/DeafBabe.hpp"
|
|
||||||
#include "DataSpec/DNAMP1/DCLN.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/DeafBabe.hpp"
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
#include <zeus/Global.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
template <class DEAFBABE>
|
|
||||||
void DeafBabeSendToBlender(hecl::blender::PyOutStream& os, const DEAFBABE& db, bool isDcln, atInt32 idx) {
|
|
||||||
os << "material_index = []\n"
|
|
||||||
"col_bm = bmesh.new()\n";
|
|
||||||
for (const atVec3f& vert : db.verts) {
|
|
||||||
zeus::simd_floats f(vert.simd);
|
|
||||||
os.format(FMT_STRING("col_bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "col_bm.verts.ensure_lookup_table()\n";
|
|
||||||
|
|
||||||
int triIdx = 0;
|
|
||||||
for (const typename DEAFBABE::Triangle& tri : db.triangleEdgeConnections) {
|
|
||||||
const typename DEAFBABE::Material& triMat = db.materials[db.triMats[triIdx++]];
|
|
||||||
const typename DEAFBABE::Edge& edge0 = db.edgeVertConnections[tri.edges[0]];
|
|
||||||
const typename DEAFBABE::Edge& edge1 = db.edgeVertConnections[tri.edges[1]];
|
|
||||||
const typename DEAFBABE::Edge& edge2 = db.edgeVertConnections[tri.edges[2]];
|
|
||||||
if (!edge0.verts[0] && !edge1.verts[0] && !edge2.verts[0])
|
|
||||||
break;
|
|
||||||
|
|
||||||
int vindices[3];
|
|
||||||
vindices[2] =
|
|
||||||
(edge1.verts[0] != edge0.verts[0] && edge1.verts[0] != edge0.verts[1]) ? edge1.verts[0] : edge1.verts[1];
|
|
||||||
|
|
||||||
if (triMat.flipFace()) {
|
|
||||||
vindices[0] = edge0.verts[1];
|
|
||||||
vindices[1] = edge0.verts[0];
|
|
||||||
} else {
|
|
||||||
vindices[0] = edge0.verts[0];
|
|
||||||
vindices[1] = edge0.verts[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "tri_verts = []\n";
|
|
||||||
os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[0]);
|
|
||||||
os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[1]);
|
|
||||||
os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[2]);
|
|
||||||
|
|
||||||
os.format(FMT_STRING("face = col_bm.faces.get(tri_verts)\n"
|
|
||||||
"if face is None:\n"
|
|
||||||
" face = col_bm.faces.new(tri_verts)\n"
|
|
||||||
"else:\n"
|
|
||||||
" face = face.copy()\n"
|
|
||||||
" for i in range(3):\n"
|
|
||||||
" face.verts[i].co = tri_verts[i].co\n"
|
|
||||||
" col_bm.verts.ensure_lookup_table()\n"
|
|
||||||
"face.material_index = select_material(0x{:016X}"
|
|
||||||
")\n"
|
|
||||||
"face.smooth = False\n"
|
|
||||||
"\n"),
|
|
||||||
atUint64(triMat.material));
|
|
||||||
}
|
|
||||||
|
|
||||||
db.insertNoClimb(os);
|
|
||||||
|
|
||||||
if (isDcln)
|
|
||||||
os.format(FMT_STRING("col_mesh = bpy.data.meshes.new('CMESH_{}')\n"), idx);
|
|
||||||
else
|
|
||||||
os << "col_mesh = bpy.data.meshes.new('CMESH')\n";
|
|
||||||
|
|
||||||
os << "col_bm.to_mesh(col_mesh)\n"
|
|
||||||
"col_mesh_obj = bpy.data.objects.new(col_mesh.name, col_mesh)\n"
|
|
||||||
"\n"
|
|
||||||
"for mat_name in material_index:\n"
|
|
||||||
" mat = material_dict[mat_name]\n"
|
|
||||||
" col_mesh.materials.append(mat)\n"
|
|
||||||
"\n"
|
|
||||||
"if 'Collision' not in bpy.data.collections:\n"
|
|
||||||
" coll = bpy.data.collections.new('Collision')\n"
|
|
||||||
" bpy.context.scene.collection.children.link(coll)\n"
|
|
||||||
"else:\n"
|
|
||||||
" coll = bpy.data.collections['Collision']\n"
|
|
||||||
"coll.objects.link(col_mesh_obj)\n"
|
|
||||||
"bpy.context.view_layer.objects.active = col_mesh_obj\n"
|
|
||||||
"bpy.ops.object.mode_set(mode='EDIT')\n"
|
|
||||||
"bpy.ops.mesh.tris_convert_to_quads()\n"
|
|
||||||
"bpy.ops.object.mode_set(mode='OBJECT')\n"
|
|
||||||
"bpy.context.view_layer.objects.active = None\n"
|
|
||||||
"col_mesh_obj.display_type = 'SOLID'\n"
|
|
||||||
"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
template void DeafBabeSendToBlender<DNAMP1::DeafBabe>(hecl::blender::PyOutStream& os, const DNAMP1::DeafBabe& db,
|
|
||||||
bool isDcln, atInt32 idx);
|
|
||||||
template void DeafBabeSendToBlender<DNAMP2::DeafBabe>(hecl::blender::PyOutStream& os, const DNAMP2::DeafBabe& db,
|
|
||||||
bool isDcln, atInt32 idx);
|
|
||||||
template void DeafBabeSendToBlender<DNAMP1::DCLN::Collision>(hecl::blender::PyOutStream& os,
|
|
||||||
const DNAMP1::DCLN::Collision& db, bool isDcln,
|
|
||||||
atInt32 idx);
|
|
||||||
|
|
||||||
template <class DEAFBABE>
|
|
||||||
static void PopulateAreaFields(
|
|
||||||
DEAFBABE& db, const hecl::blender::ColMesh& colMesh, const zeus::CAABox& fullAABB,
|
|
||||||
std::enable_if_t<std::is_same<DEAFBABE, DNAMP1::DeafBabe>::value || std::is_same<DEAFBABE, DNAMP2::DeafBabe>::value,
|
|
||||||
int>* = 0) {
|
|
||||||
AROTBuilder builder;
|
|
||||||
auto octree = builder.buildCol(colMesh, db.rootNodeType);
|
|
||||||
static_cast<std::unique_ptr<atUint8[]>&>(db.bspTree) = std::move(octree.first);
|
|
||||||
db.bspSize = octree.second;
|
|
||||||
|
|
||||||
db.unk1 = 0x1000000;
|
|
||||||
size_t dbSize = 0;
|
|
||||||
db.binarySize(dbSize);
|
|
||||||
db.length = dbSize - 8;
|
|
||||||
db.magic = 0xDEAFBABE;
|
|
||||||
db.version = 3;
|
|
||||||
db.aabb[0] = fullAABB.min;
|
|
||||||
db.aabb[1] = fullAABB.max;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class DEAFBABE>
|
|
||||||
static void PopulateAreaFields(DEAFBABE& db, const hecl::blender::ColMesh& colMesh, const zeus::CAABox& fullAABB,
|
|
||||||
std::enable_if_t<std::is_same<DEAFBABE, DNAMP1::DCLN::Collision>::value, int>* = 0) {
|
|
||||||
db.magic = 0xDEAFBABE;
|
|
||||||
db.version = 2;
|
|
||||||
db.memSize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MaterialPool {
|
|
||||||
std::unordered_map<u64, int> m_materials;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <class M, class V>
|
|
||||||
int AddOrLookup(const M& mat, V& vec) {
|
|
||||||
auto search = m_materials.find(mat.material);
|
|
||||||
if (search != m_materials.end())
|
|
||||||
return search->second;
|
|
||||||
auto idx = int(vec.size());
|
|
||||||
vec.push_back(mat);
|
|
||||||
m_materials[mat.material] = idx;
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class DEAFBABE>
|
|
||||||
void DeafBabeBuildFromBlender(DEAFBABE& db, const hecl::blender::ColMesh& colMesh) {
|
|
||||||
using BlendMat = hecl::blender::ColMesh::Material;
|
|
||||||
|
|
||||||
auto MakeMat = [](const BlendMat& mat, bool flipFace) -> typename DEAFBABE::Material {
|
|
||||||
typename DEAFBABE::Material dbMat = {};
|
|
||||||
dbMat.setUnknown(mat.unknown);
|
|
||||||
dbMat.setSurfaceStone(mat.surfaceStone);
|
|
||||||
dbMat.setSurfaceMetal(mat.surfaceMetal);
|
|
||||||
dbMat.setSurfaceGrass(mat.surfaceGrass);
|
|
||||||
dbMat.setSurfaceIce(mat.surfaceIce);
|
|
||||||
dbMat.setPillar(mat.pillar);
|
|
||||||
dbMat.setSurfaceMetalGrating(mat.surfaceMetalGrating);
|
|
||||||
dbMat.setSurfacePhazon(mat.surfacePhazon);
|
|
||||||
dbMat.setSurfaceDirt(mat.surfaceDirt);
|
|
||||||
dbMat.setSurfaceLava(mat.surfaceLava);
|
|
||||||
dbMat.setSurfaceSPMetal(mat.surfaceSPMetal);
|
|
||||||
dbMat.setSurfaceLavaStone(mat.surfaceLavaStone);
|
|
||||||
dbMat.setSurfaceSnow(mat.surfaceSnow);
|
|
||||||
dbMat.setSurfaceMudSlow(mat.surfaceMudSlow);
|
|
||||||
dbMat.setSurfaceFabric(mat.surfaceFabric);
|
|
||||||
dbMat.setHalfPipe(mat.halfPipe);
|
|
||||||
dbMat.setSurfaceMud(mat.surfaceMud);
|
|
||||||
dbMat.setSurfaceGlass(mat.surfaceGlass);
|
|
||||||
dbMat.setUnused3(mat.unused3);
|
|
||||||
dbMat.setUnused4(mat.unused4);
|
|
||||||
dbMat.setSurfaceShield(mat.surfaceShield);
|
|
||||||
dbMat.setSurfaceSand(mat.surfaceSand);
|
|
||||||
dbMat.setSurfaceMothOrSeedOrganics(mat.surfaceMothOrSeedOrganics);
|
|
||||||
dbMat.setSurfaceWeb(mat.surfaceWeb);
|
|
||||||
dbMat.setProjectilePassthrough(mat.projPassthrough);
|
|
||||||
dbMat.setSolid(mat.solid);
|
|
||||||
dbMat.setNoPlatformCollision(mat.noPlatformCollision);
|
|
||||||
dbMat.setCameraPassthrough(mat.camPassthrough);
|
|
||||||
dbMat.setSurfaceWood(mat.surfaceWood);
|
|
||||||
dbMat.setSurfaceOrganic(mat.surfaceOrganic);
|
|
||||||
dbMat.setNoEdgeCollision(mat.noEdgeCollision);
|
|
||||||
dbMat.setSurfaceRubber(mat.surfaceRubber);
|
|
||||||
dbMat.setSeeThrough(mat.seeThrough);
|
|
||||||
dbMat.setScanPassthrough(mat.scanPassthrough);
|
|
||||||
dbMat.setAiPassthrough(mat.aiPassthrough);
|
|
||||||
dbMat.setCeiling(mat.ceiling);
|
|
||||||
dbMat.setWall(mat.wall);
|
|
||||||
dbMat.setFloor(mat.floor);
|
|
||||||
dbMat.setAiBlock(mat.aiBlock);
|
|
||||||
dbMat.setJumpNotAllowed(mat.jumpNotAllowed);
|
|
||||||
dbMat.setSpiderBall(mat.spiderBall);
|
|
||||||
dbMat.setScrewAttackWallJump(mat.screwAttackWallJump);
|
|
||||||
dbMat.setFlipFace(flipFace);
|
|
||||||
return dbMat;
|
|
||||||
};
|
|
||||||
|
|
||||||
MaterialPool matPool;
|
|
||||||
db.materials.reserve(colMesh.materials.size() * 2);
|
|
||||||
|
|
||||||
zeus::CAABox fullAABB;
|
|
||||||
|
|
||||||
db.verts.reserve(colMesh.verts.size());
|
|
||||||
db.vertMats.resize(colMesh.verts.size());
|
|
||||||
for (const auto& vert : colMesh.verts) {
|
|
||||||
fullAABB.accumulateBounds(zeus::CVector3f(vert));
|
|
||||||
db.verts.push_back(vert);
|
|
||||||
}
|
|
||||||
db.vertMatsCount = colMesh.verts.size();
|
|
||||||
db.vertCount = colMesh.verts.size();
|
|
||||||
|
|
||||||
db.edgeVertConnections.reserve(colMesh.edges.size());
|
|
||||||
db.edgeMats.resize(colMesh.edges.size());
|
|
||||||
for (const auto& edge : colMesh.edges) {
|
|
||||||
db.edgeVertConnections.emplace_back();
|
|
||||||
db.edgeVertConnections.back().verts[0] = edge.verts[0];
|
|
||||||
db.edgeVertConnections.back().verts[1] = edge.verts[1];
|
|
||||||
}
|
|
||||||
db.edgeMatsCount = colMesh.edges.size();
|
|
||||||
db.edgeVertsCount = colMesh.edges.size();
|
|
||||||
|
|
||||||
db.triMats.reserve(colMesh.trianges.size());
|
|
||||||
db.triangleEdgeConnections.reserve(colMesh.trianges.size());
|
|
||||||
for (const auto& tri : colMesh.trianges) {
|
|
||||||
int triMatIdx = matPool.AddOrLookup(MakeMat(colMesh.materials[tri.matIdx], tri.flip), db.materials);
|
|
||||||
db.triMats.push_back(triMatIdx);
|
|
||||||
|
|
||||||
db.triangleEdgeConnections.emplace_back();
|
|
||||||
db.triangleEdgeConnections.back().edges[0] = tri.edges[0];
|
|
||||||
db.triangleEdgeConnections.back().edges[1] = tri.edges[1];
|
|
||||||
db.triangleEdgeConnections.back().edges[2] = tri.edges[2];
|
|
||||||
|
|
||||||
for (int e = 0; e < 3; ++e) {
|
|
||||||
db.edgeMats[tri.edges[e]] = triMatIdx;
|
|
||||||
for (int v = 0; v < 2; ++v)
|
|
||||||
db.vertMats[colMesh.edges[e].verts[v]] = triMatIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.triMatsCount = colMesh.trianges.size();
|
|
||||||
db.triangleEdgesCount = colMesh.trianges.size() * 3;
|
|
||||||
|
|
||||||
db.materialCount = db.materials.size();
|
|
||||||
|
|
||||||
PopulateAreaFields(db, colMesh, fullAABB);
|
|
||||||
}
|
|
||||||
|
|
||||||
template void DeafBabeBuildFromBlender<DNAMP1::DeafBabe>(DNAMP1::DeafBabe& db, const hecl::blender::ColMesh& colMesh);
|
|
||||||
template void DeafBabeBuildFromBlender<DNAMP2::DeafBabe>(DNAMP2::DeafBabe& db, const hecl::blender::ColMesh& colMesh);
|
|
||||||
template void DeafBabeBuildFromBlender<DNAMP1::DCLN::Collision>(DNAMP1::DCLN::Collision& db,
|
|
||||||
const hecl::blender::ColMesh& colMesh);
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,20 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <athena/Types.hpp>
|
|
||||||
|
|
||||||
namespace hecl::blender {
|
|
||||||
class PyOutStream;
|
|
||||||
struct ColMesh;
|
|
||||||
} // namespace hecl::blender
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
enum class BspNodeType : atUint32 { Invalid, Branch, Leaf };
|
|
||||||
|
|
||||||
template <class DEAFBABE>
|
|
||||||
void DeafBabeSendToBlender(hecl::blender::PyOutStream& os, const DEAFBABE& db, bool isDcln = false, atInt32 idx = -1);
|
|
||||||
|
|
||||||
template <class DEAFBABE>
|
|
||||||
void DeafBabeBuildFromBlender(DEAFBABE& db, const hecl::blender::ColMesh& colMesh);
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,18 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNACommon {
|
|
||||||
struct EGMC : public BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> count;
|
|
||||||
|
|
||||||
struct Object : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> mesh;
|
|
||||||
Value<atUint32> instanceId;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<Object, AT_DNA_COUNT(count)> objects;
|
|
||||||
};
|
|
||||||
} // namespace DataSpec::DNACommon
|
|
|
@ -1,51 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/ELSC.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template struct PPImpl<_ELSM<UniqueID32>>;
|
|
||||||
template struct PPImpl<_ELSM<UniqueID64>>;
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_ELSM<UniqueID32>>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_ELSM<UniqueID64>>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view ELSM<UniqueID32>::DNAType() {
|
|
||||||
return "ELSM<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view ELSM<UniqueID64>::DNAType() {
|
|
||||||
return "ELSM<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractELSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
ELSM<IDType> elsm;
|
|
||||||
elsm.read(rs);
|
|
||||||
athena::io::ToYAMLStream(elsm, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template bool ExtractELSM<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractELSM<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteELSM(const ELSM<IDType>& elsm, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
elsm.write(w);
|
|
||||||
int64_t rem = w.position() % 32;
|
|
||||||
if (rem)
|
|
||||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
|
||||||
w.writeUByte(0xff);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template bool WriteELSM<UniqueID32>(const ELSM<UniqueID32>& gpsm, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteELSM<UniqueID64>(const ELSM<UniqueID64>& gpsm, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,56 +0,0 @@
|
||||||
#ifndef ENTRY
|
|
||||||
#define ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef INT_ENTRY
|
|
||||||
#define INT_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef REAL_ENTRY
|
|
||||||
#define REAL_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef COLOR_ENTRY
|
|
||||||
#define COLOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef EMITTER_ENTRY
|
|
||||||
#define EMITTER_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef RES_ENTRY
|
|
||||||
#define RES_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BOOL_ENTRY
|
|
||||||
#define BOOL_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
INT_ENTRY('LIFE', x0_LIFE)
|
|
||||||
INT_ENTRY('SLIF', x4_SLIF)
|
|
||||||
REAL_ENTRY('GRAT', x8_GRAT)
|
|
||||||
INT_ENTRY('SCNT', xc_SCNT)
|
|
||||||
INT_ENTRY('SSEG', x10_SSEG)
|
|
||||||
COLOR_ENTRY('COLR', x14_COLR)
|
|
||||||
EMITTER_ENTRY('IEMT', x18_IEMT)
|
|
||||||
EMITTER_ENTRY('FEMT', x1c_FEMT)
|
|
||||||
REAL_ENTRY('AMPL', x20_AMPL)
|
|
||||||
REAL_ENTRY('AMPD', x24_AMPD)
|
|
||||||
REAL_ENTRY('LWD1', x28_LWD1)
|
|
||||||
REAL_ENTRY('LWD2', x2c_LWD2)
|
|
||||||
REAL_ENTRY('LWD3', x30_LWD3)
|
|
||||||
COLOR_ENTRY('LCL1', x34_LCL1)
|
|
||||||
COLOR_ENTRY('LCL2', x38_LCL2)
|
|
||||||
COLOR_ENTRY('LCL3', x3c_LCL3)
|
|
||||||
RES_ENTRY('SSWH', x40_SSWH)
|
|
||||||
RES_ENTRY('GPSM', x50_GPSM)
|
|
||||||
RES_ENTRY('EPSM', x60_EPSM)
|
|
||||||
BOOL_ENTRY('ZERY', x70_ZERY)
|
|
||||||
|
|
||||||
#undef ENTRY
|
|
||||||
#undef INT_ENTRY
|
|
||||||
#undef REAL_ENTRY
|
|
||||||
#undef COLOR_ENTRY
|
|
||||||
#undef EMITTER_ENTRY
|
|
||||||
#undef RES_ENTRY
|
|
||||||
#undef BOOL_ENTRY
|
|
|
@ -1,56 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
#include <athena/FileWriter.hpp>
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct _ELSM {
|
|
||||||
static constexpr ParticleType Type = ParticleType::ELSM;
|
|
||||||
|
|
||||||
#define INT_ENTRY(name, identifier) IntElementFactory identifier;
|
|
||||||
#define REAL_ENTRY(name, identifier) RealElementFactory identifier;
|
|
||||||
#define COLOR_ENTRY(name, identifier) ColorElementFactory identifier;
|
|
||||||
#define EMITTER_ENTRY(name, identifier) EmitterElementFactory identifier;
|
|
||||||
#define RES_ENTRY(name, identifier) ChildResourceFactory<IDType> identifier;
|
|
||||||
#define BOOL_ENTRY(name, identifier) bool identifier = false;
|
|
||||||
#include "ELSC.def"
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
void constexpr Enumerate(_Func f) {
|
|
||||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
|
||||||
#include "ELSC.def"
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
bool constexpr Lookup(FourCC fcc, _Func f) {
|
|
||||||
switch (fcc.toUint32()) {
|
|
||||||
#define ENTRY(name, identifier) \
|
|
||||||
case SBIG(name): \
|
|
||||||
f(identifier); \
|
|
||||||
return true;
|
|
||||||
#include "ELSC.def"
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <class IDType>
|
|
||||||
using ELSM = PPImpl<_ELSM<IDType>>;
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractELSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteELSM(const ELSM<IDType>& elsm, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,235 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/FONT.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
#include <logvisor/logvisor.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAFont {
|
|
||||||
logvisor::Module LogModule("DataSpec::DNAFont");
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void FONT<IDType>::_read(athena::io::IStreamReader& __dna_reader) {
|
|
||||||
/* magic */
|
|
||||||
DNAFourCC magic;
|
|
||||||
magic.read(__dna_reader);
|
|
||||||
if (magic != SBIG('FONT')) {
|
|
||||||
LogModule.report(logvisor::Fatal, FMT_STRING("Invalid FONT magic '{}'"), magic);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* version */
|
|
||||||
version = __dna_reader.readUint32Big();
|
|
||||||
/* unknown1 */
|
|
||||||
unknown1 = __dna_reader.readUint32Big();
|
|
||||||
/* lineHeight */
|
|
||||||
lineHeight = __dna_reader.readInt32Big();
|
|
||||||
/* verticalOffset */
|
|
||||||
verticalOffset = __dna_reader.readInt32Big();
|
|
||||||
/* lineMargin */
|
|
||||||
lineMargin = __dna_reader.readInt32Big();
|
|
||||||
/* unknown2 */
|
|
||||||
unknown2 = __dna_reader.readBool();
|
|
||||||
/* unknown3 */
|
|
||||||
unknown3 = __dna_reader.readBool();
|
|
||||||
/* unknown4 */
|
|
||||||
unknown4 = __dna_reader.readUint32Big();
|
|
||||||
/* fontSize */
|
|
||||||
fontSize = __dna_reader.readUint32Big();
|
|
||||||
/* name */
|
|
||||||
name = __dna_reader.readString(-1);
|
|
||||||
/* textureId */
|
|
||||||
textureId.read(__dna_reader);
|
|
||||||
/* textureFormat */
|
|
||||||
textureFormat = __dna_reader.readUint32Big();
|
|
||||||
/* glyphCount */
|
|
||||||
glyphCount = __dna_reader.readUint32Big();
|
|
||||||
/* glyphs */
|
|
||||||
for (atUint32 i = 0; i < glyphCount; i++) {
|
|
||||||
if (version < 4)
|
|
||||||
glyphs.emplace_back(new GlyphMP1);
|
|
||||||
else
|
|
||||||
glyphs.emplace_back(new GlyphMP2);
|
|
||||||
glyphs.back()->read(__dna_reader);
|
|
||||||
}
|
|
||||||
/* kerningInfoCount */
|
|
||||||
kerningInfoCount = __dna_reader.readUint32Big();
|
|
||||||
/* kerningInfo */
|
|
||||||
__dna_reader.enumerate(kerningInfo, kerningInfoCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void FONT<IDType>::_write(athena::io::IStreamWriter& __dna_writer) const {
|
|
||||||
/* magic */
|
|
||||||
__dna_writer.writeBytes((atInt8*)"FONT", 4);
|
|
||||||
/* version */
|
|
||||||
__dna_writer.writeUint32Big(version);
|
|
||||||
/* unknown1 */
|
|
||||||
__dna_writer.writeUint32Big(unknown1);
|
|
||||||
/* lineHeight */
|
|
||||||
__dna_writer.writeInt32Big(lineHeight);
|
|
||||||
/* verticalOffset */
|
|
||||||
__dna_writer.writeInt32Big(verticalOffset);
|
|
||||||
/* lineMargin */
|
|
||||||
__dna_writer.writeInt32Big(lineMargin);
|
|
||||||
/* unknown2 */
|
|
||||||
__dna_writer.writeBool(unknown2);
|
|
||||||
/* unknown3 */
|
|
||||||
__dna_writer.writeBool(unknown3);
|
|
||||||
/* unknown4 */
|
|
||||||
__dna_writer.writeUint32Big(unknown4);
|
|
||||||
/* fontSize */
|
|
||||||
__dna_writer.writeUint32Big(fontSize);
|
|
||||||
/* name */
|
|
||||||
__dna_writer.writeString(name, -1);
|
|
||||||
/* textureId */
|
|
||||||
textureId.write(__dna_writer);
|
|
||||||
/* textureFormat */
|
|
||||||
__dna_writer.writeUint32Big(textureFormat);
|
|
||||||
/* glyphCount */
|
|
||||||
__dna_writer.writeUint32Big(glyphCount);
|
|
||||||
/* glyphs */
|
|
||||||
for (const std::unique_ptr<IGlyph>& glyph : glyphs)
|
|
||||||
glyph->write(__dna_writer);
|
|
||||||
/* kerningInfoCount */
|
|
||||||
__dna_writer.writeUint32Big(kerningInfoCount);
|
|
||||||
/* kerningInfo */
|
|
||||||
__dna_writer.enumerate(kerningInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void FONT<IDType>::_read(athena::io::YAMLDocReader& __dna_docin) {
|
|
||||||
/* version */
|
|
||||||
version = __dna_docin.readUint32("version");
|
|
||||||
/* unknown1 */
|
|
||||||
unknown1 = __dna_docin.readUint32("unknown1");
|
|
||||||
/* lineHeight */
|
|
||||||
lineHeight = __dna_docin.readInt32("lineHeight");
|
|
||||||
/* verticalOffset */
|
|
||||||
verticalOffset = __dna_docin.readInt32("verticalOffset");
|
|
||||||
/* lineMargin */
|
|
||||||
lineMargin = __dna_docin.readInt32("lineMargin");
|
|
||||||
/* unknown2 */
|
|
||||||
unknown2 = __dna_docin.readBool("unknown2");
|
|
||||||
/* unknown3 */
|
|
||||||
unknown3 = __dna_docin.readBool("unknown3");
|
|
||||||
/* unknown4 */
|
|
||||||
unknown4 = __dna_docin.readUint32("unknown4");
|
|
||||||
/* fontSize */
|
|
||||||
fontSize = __dna_docin.readUint32("fontSize");
|
|
||||||
/* name */
|
|
||||||
name = __dna_docin.readString("name");
|
|
||||||
/* textureId */
|
|
||||||
__dna_docin.enumerate("textureId", textureId);
|
|
||||||
/* textureFormat */
|
|
||||||
textureFormat = __dna_docin.readUint32("textureFormat");
|
|
||||||
/* glyphCount */
|
|
||||||
/* glyphs */
|
|
||||||
size_t count;
|
|
||||||
if (auto v = __dna_docin.enterSubVector("glyphs", count)) {
|
|
||||||
glyphCount = count;
|
|
||||||
for (atUint32 i = 0; i < glyphCount; i++) {
|
|
||||||
if (version < 4)
|
|
||||||
glyphs.emplace_back(new GlyphMP1);
|
|
||||||
else
|
|
||||||
glyphs.emplace_back(new GlyphMP2);
|
|
||||||
|
|
||||||
if (auto rec = __dna_docin.enterSubRecord())
|
|
||||||
glyphs.back()->read(__dna_docin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* kerningInfoCount squelched */
|
|
||||||
/* kerningInfo */
|
|
||||||
kerningInfoCount = __dna_docin.enumerate("kerningInfo", kerningInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void FONT<IDType>::_write(athena::io::YAMLDocWriter& __dna_docout) const {
|
|
||||||
/* version */
|
|
||||||
__dna_docout.writeUint32("version", version);
|
|
||||||
/* unknown1 */
|
|
||||||
__dna_docout.writeUint32("unknown1", unknown1);
|
|
||||||
/* lineHeight */
|
|
||||||
__dna_docout.writeInt32("lineHeight", lineHeight);
|
|
||||||
/* verticalOffset */
|
|
||||||
__dna_docout.writeInt32("verticalOffset", verticalOffset);
|
|
||||||
/* lineMargin */
|
|
||||||
__dna_docout.writeInt32("lineMargin", lineMargin);
|
|
||||||
/* unknown2 */
|
|
||||||
__dna_docout.writeBool("unknown2", unknown2);
|
|
||||||
/* unknown3 */
|
|
||||||
__dna_docout.writeBool("unknown3", unknown3);
|
|
||||||
/* unknown4 */
|
|
||||||
__dna_docout.writeUint32("unknown4", unknown4);
|
|
||||||
/* fontSize */
|
|
||||||
__dna_docout.writeUint32("fontSize", fontSize);
|
|
||||||
/* name */
|
|
||||||
__dna_docout.writeString("name", name);
|
|
||||||
/* textureId */
|
|
||||||
__dna_docout.enumerate("textureId", textureId);
|
|
||||||
/* textureFormat */
|
|
||||||
__dna_docout.writeUint32("textureFormat", textureFormat);
|
|
||||||
/* glyphCount squelched */
|
|
||||||
/* glyphs */
|
|
||||||
if (auto v = __dna_docout.enterSubVector("glyphs"))
|
|
||||||
for (const std::unique_ptr<IGlyph>& glyph : glyphs)
|
|
||||||
if (auto rec = __dna_docout.enterSubRecord())
|
|
||||||
glyph->write(__dna_docout);
|
|
||||||
/* kerningInfoCount squelched */
|
|
||||||
/* kerningInfo */
|
|
||||||
__dna_docout.enumerate("kerningInfo", kerningInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view FONT<UniqueID32>::DNAType() {
|
|
||||||
return "FONT<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view FONT<UniqueID64>::DNAType() {
|
|
||||||
return "FONT<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void FONT<IDType>::_binarySize(size_t& __isz) const {
|
|
||||||
__isz += name.size() + 1;
|
|
||||||
textureId.binarySize(__isz);
|
|
||||||
for (const std::unique_ptr<IGlyph>& glyph : glyphs)
|
|
||||||
glyph->binarySize(__isz);
|
|
||||||
for (const KerningInfo& k : kerningInfo)
|
|
||||||
k.binarySize(__isz);
|
|
||||||
__isz += 46;
|
|
||||||
}
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(FONT<UniqueID32>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(FONT<UniqueID64>)
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractFONT(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
FONT<IDType> font;
|
|
||||||
font.read(rs);
|
|
||||||
athena::io::ToYAMLStream(font, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template bool ExtractFONT<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractFONT<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteFONT(const FONT<IDType>& font, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
font.write(w);
|
|
||||||
int64_t rem = w.position() % 32;
|
|
||||||
if (rem)
|
|
||||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
|
||||||
w.writeUByte(0xff);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template bool WriteFONT<UniqueID32>(const FONT<UniqueID32>& font, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteFONT<UniqueID64>(const FONT<UniqueID64>& font, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAFont
|
|
|
@ -1,127 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
#include <athena/FileWriter.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAFont {
|
|
||||||
struct GlyphRect : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<float> left;
|
|
||||||
Value<float> top;
|
|
||||||
Value<float> right;
|
|
||||||
Value<float> bottom;
|
|
||||||
};
|
|
||||||
struct IGlyph : BigDNAVYaml {
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<atUint16> m_character;
|
|
||||||
GlyphRect m_glyphRect;
|
|
||||||
|
|
||||||
atUint16 character() const { return m_character; }
|
|
||||||
float left() const { return m_glyphRect.left; }
|
|
||||||
float top() const { return m_glyphRect.top; }
|
|
||||||
float right() const { return m_glyphRect.right; }
|
|
||||||
float bottom() const { return m_glyphRect.bottom; }
|
|
||||||
GlyphRect rect() const { return m_glyphRect; }
|
|
||||||
|
|
||||||
virtual atInt32 layer() const { return 0; }
|
|
||||||
virtual atInt32 leftPadding() const = 0;
|
|
||||||
virtual atInt32 advance() const = 0;
|
|
||||||
virtual atInt32 rightPadding() const = 0;
|
|
||||||
virtual atInt32 width() const = 0;
|
|
||||||
virtual atInt32 height() const = 0;
|
|
||||||
virtual atInt32 baseline() const = 0;
|
|
||||||
virtual atInt32 kerningIndex() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GlyphMP1 : IGlyph {
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<atInt32> m_leftPadding;
|
|
||||||
Value<atInt32> m_advance;
|
|
||||||
Value<atInt32> m_rightPadding;
|
|
||||||
Value<atInt32> m_width;
|
|
||||||
Value<atInt32> m_height;
|
|
||||||
Value<atInt32> m_baseline;
|
|
||||||
Value<atInt32> m_kerningIndex;
|
|
||||||
|
|
||||||
atInt32 leftPadding() const override { return m_leftPadding; }
|
|
||||||
atInt32 advance() const override { return m_advance; }
|
|
||||||
atInt32 rightPadding() const override { return m_rightPadding; }
|
|
||||||
atInt32 width() const override { return m_width; }
|
|
||||||
atInt32 height() const override { return m_height; }
|
|
||||||
atInt32 baseline() const override { return m_baseline; }
|
|
||||||
atInt32 kerningIndex() const override { return m_kerningIndex; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GlyphMP2 : IGlyph {
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<atInt8> m_layer;
|
|
||||||
Value<atInt8> m_leftPadding;
|
|
||||||
Value<atInt8> m_advance;
|
|
||||||
Value<atInt8> m_rightPadding;
|
|
||||||
Value<atInt8> m_width;
|
|
||||||
Value<atInt8> m_height;
|
|
||||||
Value<atInt8> m_baseline;
|
|
||||||
Value<atInt16> m_kerningIndex;
|
|
||||||
|
|
||||||
atInt32 layer() const override { return m_layer; }
|
|
||||||
atInt32 leftPadding() const override { return m_leftPadding; }
|
|
||||||
atInt32 advance() const override { return m_advance; }
|
|
||||||
atInt32 rightPadding() const override { return m_rightPadding; }
|
|
||||||
atInt32 width() const override { return m_width; }
|
|
||||||
atInt32 height() const override { return m_height; }
|
|
||||||
atInt32 baseline() const override { return m_baseline; }
|
|
||||||
atInt32 kerningIndex() const override { return m_kerningIndex; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct KerningInfo : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint16> thisChar;
|
|
||||||
Value<atUint16> nextChar;
|
|
||||||
Value<atInt32> adjust;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct AT_SPECIALIZE_PARMS(DataSpec::UniqueID32, DataSpec::UniqueID64) FONT : BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
AT_SUBDECL_DNA
|
|
||||||
Value<atUint32> version;
|
|
||||||
Value<atUint32> unknown1;
|
|
||||||
Value<atInt32> lineHeight;
|
|
||||||
Value<atInt32> verticalOffset;
|
|
||||||
Value<atInt32> lineMargin;
|
|
||||||
Value<bool> unknown2;
|
|
||||||
Value<bool> unknown3;
|
|
||||||
Value<atUint32> unknown4;
|
|
||||||
Value<atUint32> fontSize; // in points
|
|
||||||
String<-1> name;
|
|
||||||
Value<IDType> textureId;
|
|
||||||
Value<atUint32> textureFormat;
|
|
||||||
Value<atUint32> glyphCount;
|
|
||||||
std::vector<std::unique_ptr<IGlyph>> glyphs;
|
|
||||||
Value<atUint32> kerningInfoCount;
|
|
||||||
Vector<KerningInfo, AT_DNA_COUNT(kerningInfoCount)> kerningInfo;
|
|
||||||
|
|
||||||
void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {
|
|
||||||
g_curSpec->flattenDependencies(textureId, pathsOut);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractFONT(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteFONT(const FONT<IDType>& font, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAFont
|
|
|
@ -1,82 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/FSM2.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
#include <athena/FileWriter.hpp>
|
|
||||||
#include <athena/Global.hpp>
|
|
||||||
#include <athena/IStreamWriter.hpp>
|
|
||||||
|
|
||||||
#include <logvisor/logvisor.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAFSM2 {
|
|
||||||
logvisor::Module LogDNAFSM2("DataSpec::DNAFSM2");
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
template <class Op>
|
|
||||||
void FSM2<IDType>::Enumerate(typename Op::StreamT& s) {
|
|
||||||
Do<Op>(athena::io::PropId{"header"}, header, s);
|
|
||||||
if (header.magic != SBIG('FSM2')) {
|
|
||||||
LogDNAFSM2.report(logvisor::Fatal, FMT_STRING("Invalid FSM2 magic '{}' expected 'FSM2'"), header.magic);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header.version == 1) {
|
|
||||||
if (!detail)
|
|
||||||
detail.reset(new FSMV1);
|
|
||||||
Do<Op>(athena::io::PropId{"detail"}, static_cast<FSMV1&>(*detail), s);
|
|
||||||
} else if (header.version == 2) {
|
|
||||||
if (!detail)
|
|
||||||
detail.reset(new FSMV2);
|
|
||||||
Do<Op>(athena::io::PropId{"detail"}, static_cast<FSMV2&>(*detail), s);
|
|
||||||
} else {
|
|
||||||
LogDNAFSM2.report(logvisor::Fatal, FMT_STRING("Invalid FSM2 version '{}'"), header.version);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AT_SPECIALIZE_DNA(FSM2<UniqueID32>)
|
|
||||||
AT_SPECIALIZE_DNA(FSM2<UniqueID64>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view FSM2<UniqueID32>::DNAType() {
|
|
||||||
return "FSM2<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view FSM2<UniqueID64>::DNAType() {
|
|
||||||
return "FSM2<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template struct FSM2<UniqueID32>;
|
|
||||||
template struct FSM2<UniqueID64>;
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractFSM2(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
FSM2<IDType> fsm2;
|
|
||||||
fsm2.read(rs);
|
|
||||||
athena::io::ToYAMLStream(fsm2, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template bool ExtractFSM2<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractFSM2<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteFSM2(const FSM2<IDType>& fsm2, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
fsm2.write(w);
|
|
||||||
int64_t rem = w.position() % 32;
|
|
||||||
if (rem)
|
|
||||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
|
||||||
w.writeUByte(0xff);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template bool WriteFSM2<UniqueID32>(const FSM2<UniqueID32>& fsm2, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteFSM2<UniqueID64>(const FSM2<UniqueID64>& fsm2, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAFSM2
|
|
|
@ -1,147 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
#include <athena/DNA.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAFSM2 {
|
|
||||||
struct IFSM : BigDNAVYaml {
|
|
||||||
Delete _d;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct AT_SPECIALIZE_PARMS(DataSpec::UniqueID32, DataSpec::UniqueID64) FSM2 : BigDNA {
|
|
||||||
struct Header : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
DNAFourCC magic = FOURCC('FSM2');
|
|
||||||
Value<atUint32> version;
|
|
||||||
} header;
|
|
||||||
|
|
||||||
struct CommonStruct : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FSMV1 : IFSM {
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<atUint32> stateCount;
|
|
||||||
Value<atUint32> unknown1Count;
|
|
||||||
Value<atUint32> unknown2Count;
|
|
||||||
Value<atUint32> unknown3Count;
|
|
||||||
struct State : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unknownCount;
|
|
||||||
Vector<CommonStruct, AT_DNA_COUNT(unknownCount)> unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Unknown1 : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<float> unknown1;
|
|
||||||
Value<atUint32> unknown2Count;
|
|
||||||
Vector<CommonStruct, AT_DNA_COUNT(unknown2Count)> unknown2;
|
|
||||||
Value<atUint8> unknown3;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Unknown2 : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unknownCount;
|
|
||||||
Vector<CommonStruct, AT_DNA_COUNT(unknownCount)> unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Unknown3 : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unknownCount;
|
|
||||||
Vector<CommonStruct, AT_DNA_COUNT(unknownCount)> unknown;
|
|
||||||
Value<IDType> fsmId;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<State, AT_DNA_COUNT(stateCount)> states;
|
|
||||||
Vector<Unknown1, AT_DNA_COUNT(unknown1Count)> unknown1;
|
|
||||||
Vector<Unknown2, AT_DNA_COUNT(unknown2Count)> unknown2;
|
|
||||||
Vector<Unknown3, AT_DNA_COUNT(unknown3Count)> unknown3;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FSMV2 : IFSM {
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<atUint32> stateCount;
|
|
||||||
Value<atUint32> unknown1Count;
|
|
||||||
Value<atUint32> unknown2Count;
|
|
||||||
Value<atUint32> unknown3Count;
|
|
||||||
struct State : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unknown1;
|
|
||||||
Value<atUint32> unknown2;
|
|
||||||
Value<atUint32> unknown3;
|
|
||||||
Value<atUint32> unknown4;
|
|
||||||
Value<atUint32> unknown5Count;
|
|
||||||
Vector<CommonStruct, AT_DNA_COUNT(unknown5Count)> unknown5;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Unknown1 : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unknown1;
|
|
||||||
Value<atUint32> unknown2;
|
|
||||||
Value<atUint32> unknown3;
|
|
||||||
Value<atUint32> unknown4;
|
|
||||||
Value<float> unknown5;
|
|
||||||
Value<atUint32> unknown6Count;
|
|
||||||
Vector<CommonStruct, AT_DNA_COUNT(unknown6Count)> unknown6;
|
|
||||||
Value<atUint8> unknown7;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Unknown2 : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unknown1;
|
|
||||||
Value<atUint32> unknown2;
|
|
||||||
Value<atUint32> unknown3;
|
|
||||||
Value<atUint32> unknown4;
|
|
||||||
Value<atUint32> unknown5Count;
|
|
||||||
Vector<CommonStruct, AT_DNA_COUNT(unknown5Count)> unknown5;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Unknown3 : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unknown1;
|
|
||||||
Value<atUint32> unknown2;
|
|
||||||
Value<atUint32> unknown3;
|
|
||||||
Value<atUint32> unknown4;
|
|
||||||
Value<atUint32> unknown5Count;
|
|
||||||
Vector<CommonStruct, AT_DNA_COUNT(unknown5Count)> unknown5;
|
|
||||||
Value<IDType> fsmId;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<State, AT_DNA_COUNT(stateCount)> states;
|
|
||||||
Vector<Unknown1, AT_DNA_COUNT(unknown1Count)> unknown1;
|
|
||||||
Vector<Unknown2, AT_DNA_COUNT(unknown2Count)> unknown2;
|
|
||||||
Vector<Unknown3, AT_DNA_COUNT(unknown3Count)> unknown3;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<IFSM> detail;
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractFSM2(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteFSM2(const FSM2<IDType>& fsm2, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAFSM2
|
|
|
@ -1,18 +0,0 @@
|
||||||
#include "GX.hpp"
|
|
||||||
|
|
||||||
namespace GX {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Color::Enumerate<athena::io::DNA<athena::Endian::Big>::Read>(Read::StreamT& reader) {
|
|
||||||
reader.readUBytesToBuf(&num, 4);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void Color::Enumerate<athena::io::DNA<athena::Endian::Big>::Write>(Write::StreamT& writer) {
|
|
||||||
writer.writeUBytes(reinterpret_cast<const atUint8*>(&num), 4);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void Color::Enumerate<athena::io::DNA<athena::Endian::Big>::BinarySize>(BinarySize::StreamT& s) {
|
|
||||||
s += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace GX
|
|
|
@ -1,298 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <athena/DNA.hpp>
|
|
||||||
|
|
||||||
namespace GX {
|
|
||||||
enum AttrType { NONE, DIRECT, INDEX8, INDEX16 };
|
|
||||||
|
|
||||||
enum TevColorArg {
|
|
||||||
CC_CPREV = 0,
|
|
||||||
CC_APREV = 1,
|
|
||||||
CC_C0 = 2,
|
|
||||||
CC_A0 = 3,
|
|
||||||
CC_C1 = 4,
|
|
||||||
CC_A1 = 5,
|
|
||||||
CC_C2 = 6,
|
|
||||||
CC_A2 = 7,
|
|
||||||
CC_TEXC = 8,
|
|
||||||
CC_TEXA = 9,
|
|
||||||
CC_RASC = 10,
|
|
||||||
CC_RASA = 11,
|
|
||||||
CC_ONE = 12,
|
|
||||||
CC_HALF = 13,
|
|
||||||
CC_KONST = 14,
|
|
||||||
CC_ZERO = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TevAlphaArg {
|
|
||||||
CA_APREV = 0,
|
|
||||||
CA_A0 = 1,
|
|
||||||
CA_A1 = 2,
|
|
||||||
CA_A2 = 3,
|
|
||||||
CA_TEXA = 4,
|
|
||||||
CA_RASA = 5,
|
|
||||||
CA_KONST = 6,
|
|
||||||
CA_ZERO = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TevKColorSel {
|
|
||||||
TEV_KCSEL_8_8 = 0x00,
|
|
||||||
TEV_KCSEL_7_8 = 0x01,
|
|
||||||
TEV_KCSEL_6_8 = 0x02,
|
|
||||||
TEV_KCSEL_5_8 = 0x03,
|
|
||||||
TEV_KCSEL_4_8 = 0x04,
|
|
||||||
TEV_KCSEL_3_8 = 0x05,
|
|
||||||
TEV_KCSEL_2_8 = 0x06,
|
|
||||||
TEV_KCSEL_1_8 = 0x07,
|
|
||||||
|
|
||||||
TEV_KCSEL_1 = TEV_KCSEL_8_8,
|
|
||||||
TEV_KCSEL_3_4 = TEV_KCSEL_6_8,
|
|
||||||
TEV_KCSEL_1_2 = TEV_KCSEL_4_8,
|
|
||||||
TEV_KCSEL_1_4 = TEV_KCSEL_2_8,
|
|
||||||
|
|
||||||
TEV_KCSEL_K0 = 0x0C,
|
|
||||||
TEV_KCSEL_K1 = 0x0D,
|
|
||||||
TEV_KCSEL_K2 = 0x0E,
|
|
||||||
TEV_KCSEL_K3 = 0x0F,
|
|
||||||
TEV_KCSEL_K0_R = 0x10,
|
|
||||||
TEV_KCSEL_K1_R = 0x11,
|
|
||||||
TEV_KCSEL_K2_R = 0x12,
|
|
||||||
TEV_KCSEL_K3_R = 0x13,
|
|
||||||
TEV_KCSEL_K0_G = 0x14,
|
|
||||||
TEV_KCSEL_K1_G = 0x15,
|
|
||||||
TEV_KCSEL_K2_G = 0x16,
|
|
||||||
TEV_KCSEL_K3_G = 0x17,
|
|
||||||
TEV_KCSEL_K0_B = 0x18,
|
|
||||||
TEV_KCSEL_K1_B = 0x19,
|
|
||||||
TEV_KCSEL_K2_B = 0x1A,
|
|
||||||
TEV_KCSEL_K3_B = 0x1B,
|
|
||||||
TEV_KCSEL_K0_A = 0x1C,
|
|
||||||
TEV_KCSEL_K1_A = 0x1D,
|
|
||||||
TEV_KCSEL_K2_A = 0x1E,
|
|
||||||
TEV_KCSEL_K3_A = 0x1F
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TevKAlphaSel {
|
|
||||||
TEV_KASEL_8_8 = 0x00,
|
|
||||||
TEV_KASEL_7_8 = 0x01,
|
|
||||||
TEV_KASEL_6_8 = 0x02,
|
|
||||||
TEV_KASEL_5_8 = 0x03,
|
|
||||||
TEV_KASEL_4_8 = 0x04,
|
|
||||||
TEV_KASEL_3_8 = 0x05,
|
|
||||||
TEV_KASEL_2_8 = 0x06,
|
|
||||||
TEV_KASEL_1_8 = 0x07,
|
|
||||||
|
|
||||||
TEV_KASEL_1 = TEV_KASEL_8_8,
|
|
||||||
TEV_KASEL_3_4 = TEV_KASEL_6_8,
|
|
||||||
TEV_KASEL_1_2 = TEV_KASEL_4_8,
|
|
||||||
TEV_KASEL_1_4 = TEV_KASEL_2_8,
|
|
||||||
|
|
||||||
TEV_KASEL_K0_R = 0x10,
|
|
||||||
TEV_KASEL_K1_R = 0x11,
|
|
||||||
TEV_KASEL_K2_R = 0x12,
|
|
||||||
TEV_KASEL_K3_R = 0x13,
|
|
||||||
TEV_KASEL_K0_G = 0x14,
|
|
||||||
TEV_KASEL_K1_G = 0x15,
|
|
||||||
TEV_KASEL_K2_G = 0x16,
|
|
||||||
TEV_KASEL_K3_G = 0x17,
|
|
||||||
TEV_KASEL_K0_B = 0x18,
|
|
||||||
TEV_KASEL_K1_B = 0x19,
|
|
||||||
TEV_KASEL_K2_B = 0x1A,
|
|
||||||
TEV_KASEL_K3_B = 0x1B,
|
|
||||||
TEV_KASEL_K0_A = 0x1C,
|
|
||||||
TEV_KASEL_K1_A = 0x1D,
|
|
||||||
TEV_KASEL_K2_A = 0x1E,
|
|
||||||
TEV_KASEL_K3_A = 0x1F
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TevOp {
|
|
||||||
TEV_ADD = 0,
|
|
||||||
TEV_SUB = 1,
|
|
||||||
TEV_COMP_R8_GT = 8,
|
|
||||||
TEV_COMP_R8_EQ = 9,
|
|
||||||
TEV_COMP_GR16_GT = 10,
|
|
||||||
TEV_COMP_GR16_EQ = 11,
|
|
||||||
TEV_COMP_BGR24_GT = 12,
|
|
||||||
TEV_COMP_BGR24_EQ = 13,
|
|
||||||
TEV_COMP_RGB8_GT = 14,
|
|
||||||
TEV_COMP_RGB8_EQ = 15,
|
|
||||||
TEV_COMP_A8_GT = TEV_COMP_RGB8_GT,
|
|
||||||
TEV_COMP_A8_EQ = TEV_COMP_RGB8_EQ
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TevBias {
|
|
||||||
TB_ZERO = 0,
|
|
||||||
TB_ADDHALF = 1,
|
|
||||||
TB_SUBHALF = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TevScale { CS_SCALE_1 = 0, CS_SCALE_2 = 1, CS_SCALE_4 = 2, CS_DIVIDE_2 = 3 };
|
|
||||||
|
|
||||||
enum TexGenType {
|
|
||||||
TG_MTX3x4 = 0,
|
|
||||||
TG_MTX2x4,
|
|
||||||
TG_BUMP0,
|
|
||||||
TG_BUMP1,
|
|
||||||
TG_BUMP2,
|
|
||||||
TG_BUMP3,
|
|
||||||
TG_BUMP4,
|
|
||||||
TG_BUMP5,
|
|
||||||
TG_BUMP6,
|
|
||||||
TG_BUMP7,
|
|
||||||
TG_SRTG
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TexGenSrc {
|
|
||||||
TG_POS = 0,
|
|
||||||
TG_NRM,
|
|
||||||
TG_BINRM,
|
|
||||||
TG_TANGENT,
|
|
||||||
TG_TEX0,
|
|
||||||
TG_TEX1,
|
|
||||||
TG_TEX2,
|
|
||||||
TG_TEX3,
|
|
||||||
TG_TEX4,
|
|
||||||
TG_TEX5,
|
|
||||||
TG_TEX6,
|
|
||||||
TG_TEX7,
|
|
||||||
TG_TEXCOORD0,
|
|
||||||
TG_TEXCOORD1,
|
|
||||||
TG_TEXCOORD2,
|
|
||||||
TG_TEXCOORD3,
|
|
||||||
TG_TEXCOORD4,
|
|
||||||
TG_TEXCOORD5,
|
|
||||||
TG_TEXCOORD6,
|
|
||||||
TG_COLOR0,
|
|
||||||
TG_COLOR1
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TexMtx {
|
|
||||||
TEXMTX0 = 30,
|
|
||||||
TEXMTX1 = 33,
|
|
||||||
TEXMTX2 = 36,
|
|
||||||
TEXMTX3 = 39,
|
|
||||||
TEXMTX4 = 42,
|
|
||||||
TEXMTX5 = 45,
|
|
||||||
TEXMTX6 = 48,
|
|
||||||
TEXMTX7 = 51,
|
|
||||||
TEXMTX8 = 54,
|
|
||||||
TEXMTX9 = 57,
|
|
||||||
IDENTITY = 60
|
|
||||||
};
|
|
||||||
|
|
||||||
enum PTTexMtx {
|
|
||||||
PTTEXMTX0 = 64,
|
|
||||||
PTTEXMTX1 = 67,
|
|
||||||
PTTEXMTX2 = 70,
|
|
||||||
PTTEXMTX3 = 73,
|
|
||||||
PTTEXMTX4 = 76,
|
|
||||||
PTTEXMTX5 = 79,
|
|
||||||
PTTEXMTX6 = 82,
|
|
||||||
PTTEXMTX7 = 85,
|
|
||||||
PTTEXMTX8 = 88,
|
|
||||||
PTTEXMTX9 = 91,
|
|
||||||
PTTEXMTX10 = 94,
|
|
||||||
PTTEXMTX11 = 97,
|
|
||||||
PTTEXMTX12 = 100,
|
|
||||||
PTTEXMTX13 = 103,
|
|
||||||
PTTEXMTX14 = 106,
|
|
||||||
PTTEXMTX15 = 109,
|
|
||||||
PTTEXMTX16 = 112,
|
|
||||||
PTTEXMTX17 = 115,
|
|
||||||
PTTEXMTX18 = 118,
|
|
||||||
PTTEXMTX19 = 121,
|
|
||||||
PTIDENTITY = 125
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TevRegID { TEVPREV = 0, TEVREG0 = 1, TEVREG1 = 2, TEVREG2 = 3, TEVLAZY = 5 };
|
|
||||||
|
|
||||||
enum DiffuseFn { DF_NONE = 0, DF_SIGN, DF_CLAMP };
|
|
||||||
|
|
||||||
enum AttnFn { AF_SPEC = 0, AF_SPOT = 1, AF_NONE };
|
|
||||||
|
|
||||||
enum Primitive {
|
|
||||||
POINTS = 0xb8,
|
|
||||||
LINES = 0xa8,
|
|
||||||
LINESTRIP = 0xb0,
|
|
||||||
TRIANGLES = 0x90,
|
|
||||||
TRIANGLESTRIP = 0x98,
|
|
||||||
TRIANGLEFAN = 0xa0,
|
|
||||||
QUADS = 0x80
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ChannelID {
|
|
||||||
GX_COLOR0,
|
|
||||||
GX_COLOR1,
|
|
||||||
GX_ALPHA0,
|
|
||||||
GX_ALPHA1,
|
|
||||||
GX_COLOR0A0,
|
|
||||||
GX_COLOR1A1,
|
|
||||||
GX_COLOR_ZERO,
|
|
||||||
GX_ALPHA_BUMP,
|
|
||||||
GX_ALPHA_BUMPN,
|
|
||||||
GX_COLOR_NULL = 0xff
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BlendFactor : uint16_t {
|
|
||||||
BL_ZERO,
|
|
||||||
BL_ONE,
|
|
||||||
BL_SRCCLR,
|
|
||||||
BL_INVSRCCLR,
|
|
||||||
BL_SRCALPHA,
|
|
||||||
BL_INVSRCALPHA,
|
|
||||||
BL_DSTALPHA,
|
|
||||||
BL_INVDSTALPHA
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Color : athena::io::DNA<athena::Endian::Big> {
|
|
||||||
union {
|
|
||||||
uint8_t color[4];
|
|
||||||
uint32_t num = 0;
|
|
||||||
};
|
|
||||||
Color() = default;
|
|
||||||
Color& operator=(const atVec4f& vec) {
|
|
||||||
athena::simd_floats f(vec.simd);
|
|
||||||
color[0] = uint8_t(std::min(std::max(f[0] * 255.f, 0.f), 255.f));
|
|
||||||
color[1] = uint8_t(std::min(std::max(f[1] * 255.f, 0.f), 255.f));
|
|
||||||
color[2] = uint8_t(std::min(std::max(f[2] * 255.f, 0.f), 255.f));
|
|
||||||
color[3] = uint8_t(std::min(std::max(f[3] * 255.f, 0.f), 255.f));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Color& operator=(const atVec3f& vec) {
|
|
||||||
athena::simd_floats f(vec.simd);
|
|
||||||
color[0] = uint8_t(std::min(std::max(f[0] * 255.f, 0.f), 255.f));
|
|
||||||
color[1] = uint8_t(std::min(std::max(f[1] * 255.f, 0.f), 255.f));
|
|
||||||
color[2] = uint8_t(std::min(std::max(f[2] * 255.f, 0.f), 255.f));
|
|
||||||
color[3] = 0xff;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Color& operator=(uint8_t val) {
|
|
||||||
color[0] = val;
|
|
||||||
color[1] = val;
|
|
||||||
color[2] = val;
|
|
||||||
color[3] = val;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
atVec4f toVec4f() const {
|
|
||||||
atVec4f out;
|
|
||||||
athena::simd_floats f;
|
|
||||||
f[0] = color[0] / 255.f;
|
|
||||||
f[1] = color[1] / 255.f;
|
|
||||||
f[2] = color[2] / 255.f;
|
|
||||||
f[3] = color[3] / 255.f;
|
|
||||||
out.simd.copy_from(f);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
Color(const atVec4f& vec) { *this = vec; }
|
|
||||||
Color(const atVec3f& vec) { *this = vec; }
|
|
||||||
Color(uint8_t val) { *this = val; }
|
|
||||||
bool operator==(const Color& other) const { return num == other.num; }
|
|
||||||
bool operator!=(const Color& other) const { return num != other.num; }
|
|
||||||
uint8_t operator[](size_t idx) const { return color[idx]; }
|
|
||||||
uint8_t& operator[](size_t idx) { return color[idx]; }
|
|
||||||
AT_DECL_EXPLICIT_DNA
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace GX
|
|
|
@ -1,426 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/MAPA.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/GX.hpp"
|
|
||||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
|
||||||
#include "DataSpec/DNAMP1/MAPA.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/MAPA.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/MAPA.hpp"
|
|
||||||
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
#include <logvisor/logvisor.hpp>
|
|
||||||
#include <zeus/CAABox.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMAPA {
|
|
||||||
|
|
||||||
static logvisor::Module Log("DNAMAPA");
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void MAPA::Enumerate<BigDNA::Read>(typename Read::StreamT& __dna_reader) {
|
|
||||||
/* magic */
|
|
||||||
magic = __dna_reader.readUint32Big();
|
|
||||||
if (magic != 0xDEADD00D) {
|
|
||||||
LogDNACommon.report(logvisor::Error, FMT_STRING("invalid MAPA magic"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* version */
|
|
||||||
version = __dna_reader.readUint32Big();
|
|
||||||
if (version == 2)
|
|
||||||
header = std::make_unique<HeaderMP1>();
|
|
||||||
else if (version == 3)
|
|
||||||
header = std::make_unique<HeaderMP2>();
|
|
||||||
else if (version == 5)
|
|
||||||
header = std::make_unique<HeaderMP3>();
|
|
||||||
else {
|
|
||||||
LogDNACommon.report(logvisor::Error, FMT_STRING("invalid MAPA version"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
header->read(__dna_reader);
|
|
||||||
|
|
||||||
for (atUint32 i = 0; i < header->mappableObjectCount(); i++) {
|
|
||||||
std::unique_ptr<IMappableObject> mo = nullptr;
|
|
||||||
if (version != 5) {
|
|
||||||
mo = std::make_unique<MappableObjectMP1_2>();
|
|
||||||
} else {
|
|
||||||
mo = std::make_unique<MappableObjectMP3>();
|
|
||||||
}
|
|
||||||
mo->read(__dna_reader);
|
|
||||||
mappableObjects.push_back(std::move(mo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vertices */
|
|
||||||
__dna_reader.enumerateBig(vertices, header->vertexCount());
|
|
||||||
/* surfaceHeaders */
|
|
||||||
__dna_reader.enumerate(surfaceHeaders, header->surfaceCount());
|
|
||||||
/* surfaces */
|
|
||||||
__dna_reader.enumerate(surfaces, header->surfaceCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void MAPA::Enumerate<BigDNA::Write>(typename Write::StreamT& __dna_writer) {
|
|
||||||
/* magic */
|
|
||||||
__dna_writer.writeUint32Big(magic);
|
|
||||||
/* version */
|
|
||||||
__dna_writer.writeUint32Big(version);
|
|
||||||
header->write(__dna_writer);
|
|
||||||
|
|
||||||
/* mappableObjects */
|
|
||||||
for (const std::unique_ptr<IMappableObject>& mo : mappableObjects)
|
|
||||||
mo->write(__dna_writer);
|
|
||||||
/* vertices */
|
|
||||||
__dna_writer.enumerateBig(vertices);
|
|
||||||
/* surfaceHeaders */
|
|
||||||
__dna_writer.enumerate(surfaceHeaders);
|
|
||||||
/* surfaces */
|
|
||||||
__dna_writer.enumerate(surfaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void MAPA::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
header->binarySize(s);
|
|
||||||
|
|
||||||
for (const std::unique_ptr<IMappableObject>& mo : mappableObjects)
|
|
||||||
mo->binarySize(s);
|
|
||||||
|
|
||||||
s += vertices.size() * 12;
|
|
||||||
for (const SurfaceHeader& sh : surfaceHeaders)
|
|
||||||
sh.binarySize(s);
|
|
||||||
for (const Surface& su : surfaces)
|
|
||||||
su.binarySize(s);
|
|
||||||
s += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* RetroMapVisModes[] = {"ALWAYS", "MAPSTATIONORVISIT", "VISIT", "NEVER"};
|
|
||||||
|
|
||||||
static const char* RetroMapObjVisModes[] = {"ALWAYS", "MAPSTATIONORVISIT", "VISIT", "NEVER", "MAPSTATIONORVISIT2"};
|
|
||||||
|
|
||||||
template <typename PAKRouter>
|
|
||||||
bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force) {
|
|
||||||
if (!force && outPath.isFile())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::MapArea))
|
|
||||||
return false;
|
|
||||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
|
||||||
|
|
||||||
os << "import bpy, bmesh\n"
|
|
||||||
"from mathutils import Matrix\n"
|
|
||||||
"\n"
|
|
||||||
"bpy.types.Object.retro_mappable_type = bpy.props.IntProperty(name='Retro: MAPA object type', default=-1)\n"
|
|
||||||
"bpy.types.Object.retro_mappable_sclyid = bpy.props.StringProperty(name='Retro: MAPA object SCLY ID')\n"
|
|
||||||
"bpy.types.Scene.retro_map_vis_mode = bpy.props.EnumProperty(items=[('ALWAYS', 'Always', 'Always Visible', 0),"
|
|
||||||
"('MAPSTATIONORVISIT', 'Map Station or Visit', 'Visible after Map Station or Visit', 1),"
|
|
||||||
"('VISIT', 'Visit', 'Visible after Visit', 2),"
|
|
||||||
"('NEVER', 'Never', 'Never Visible', 3)],"
|
|
||||||
"name='Retro: Map Visibility Mode')\n"
|
|
||||||
"bpy.types.Object.retro_mapobj_vis_mode = bpy.props.EnumProperty(items=[('ALWAYS', 'Always', 'Always Visible', "
|
|
||||||
"0),"
|
|
||||||
"('MAPSTATIONORVISIT', 'Map Station or Visit', 'Visible after Map Station or Visit', 1),"
|
|
||||||
"('VISIT', 'Visit', 'Visible after Door Visit', 2),"
|
|
||||||
"('NEVER', 'Never', 'Never Visible', 3),"
|
|
||||||
"('MAPSTATIONORVISIT2', 'Map Station or Visit 2', 'Visible after Map Station or Visit', 4)],"
|
|
||||||
"name='Retro: Map Object Visibility Mode')\n"
|
|
||||||
"\n"
|
|
||||||
"# Clear Scene\n"
|
|
||||||
"if len(bpy.data.collections):\n"
|
|
||||||
" bpy.data.collections.remove(bpy.data.collections[0])\n"
|
|
||||||
"\n"
|
|
||||||
"def add_triangle(bm, verts):\n"
|
|
||||||
" verts = [bm.verts[vi] for vi in verts]\n"
|
|
||||||
" face = bm.faces.get(verts)\n"
|
|
||||||
" if face:\n"
|
|
||||||
" face = face.copy()\n"
|
|
||||||
" bm.verts.ensure_lookup_table()\n"
|
|
||||||
" face.normal_flip()\n"
|
|
||||||
" else:\n"
|
|
||||||
" bm.faces.new(verts)\n"
|
|
||||||
"\n"
|
|
||||||
"def add_border(bm, verts):\n"
|
|
||||||
" verts = [bm.verts[vi] for vi in verts]\n"
|
|
||||||
" edge = bm.edges.get(verts)\n"
|
|
||||||
" if not edge:\n"
|
|
||||||
" edge = bm.edges.new(verts)\n"
|
|
||||||
" edge.seam = True\n"
|
|
||||||
"\n";
|
|
||||||
|
|
||||||
os.format(FMT_STRING("bpy.context.scene.name = 'MAPA_{}'\n"
|
|
||||||
"bpy.context.scene.retro_map_vis_mode = '{}'\n"),
|
|
||||||
entry.id, RetroMapVisModes[mapa.header->visMode()]);
|
|
||||||
|
|
||||||
/* Add empties representing MappableObjects */
|
|
||||||
int moIdx = 0;
|
|
||||||
for (const std::unique_ptr<MAPA::IMappableObject>& mo : mapa.mappableObjects) {
|
|
||||||
if (mapa.version < 5) {
|
|
||||||
const MAPA::MappableObjectMP1_2* moMP12 = static_cast<const MAPA::MappableObjectMP1_2*>(mo.get());
|
|
||||||
zeus::simd_floats mtxF[3];
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
moMP12->transformMtx[i].simd.copy_to(mtxF[i]);
|
|
||||||
os.format(FMT_STRING("obj = bpy.data.objects.new('MAPOBJ_{:02d}', None)\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
|
||||||
"obj.retro_mappable_type = {}\n"
|
|
||||||
"obj.retro_mapobj_vis_mode = '{}'\n"
|
|
||||||
"obj.retro_mappable_sclyid = '0x{:08X}'\n"
|
|
||||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
|
||||||
"mtxd = mtx.decompose()\n"
|
|
||||||
"obj.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"obj.location = mtxd[0]\n"
|
|
||||||
"obj.rotation_quaternion = mtxd[1]\n"
|
|
||||||
"obj.scale = mtxd[2]\n"),
|
|
||||||
moIdx, int(moMP12->type), RetroMapObjVisModes[moMP12->visMode], moMP12->sclyId, mtxF[0][0], mtxF[0][1],
|
|
||||||
mtxF[0][2], mtxF[0][3], mtxF[1][0], mtxF[1][1], mtxF[1][2], mtxF[1][3], mtxF[2][0], mtxF[2][1],
|
|
||||||
mtxF[2][2], mtxF[2][3]);
|
|
||||||
++moIdx;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
const MAPA::MappableObjectMP3* moMP3 = static_cast<const MAPA::MappableObjectMP3*>(mo.get());
|
|
||||||
zeus::simd_floats mtxF[3];
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
moMP3->transformMtx[i].simd.copy_to(mtxF[i]);
|
|
||||||
os.format(FMT_STRING("obj = bpy.data.objects.new('MAPOBJ_{:02d}', None)\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
|
||||||
"obj.retro_mappable_type = {}\n"
|
|
||||||
"obj.retro_mapobj_vis_mode = '{}'\n"
|
|
||||||
"obj.retro_mappable_sclyid = '0x{:08X}'\n"
|
|
||||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
|
||||||
"mtxd = mtx.decompose()\n"
|
|
||||||
"obj.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"obj.location = mtxd[0]\n"
|
|
||||||
"obj.rotation_quaternion = mtxd[1]\n"
|
|
||||||
"obj.scale = mtxd[2]\n"),
|
|
||||||
moIdx, int(moMP3->type), RetroMapObjVisModes[moMP3->visMode], moMP3->sclyId, mtxF[0][0], mtxF[0][1],
|
|
||||||
mtxF[0][2], mtxF[0][3], mtxF[1][0], mtxF[1][1], mtxF[1][2], mtxF[1][3], mtxF[2][0], mtxF[2][1],
|
|
||||||
mtxF[2][2], mtxF[2][3]);
|
|
||||||
++moIdx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "# Begin bmesh\n"
|
|
||||||
"bm = bmesh.new()\n"
|
|
||||||
"\n";
|
|
||||||
|
|
||||||
/* Read in verts */
|
|
||||||
for (const atVec3f& vert : mapa.vertices) {
|
|
||||||
zeus::simd_floats f(vert.simd);
|
|
||||||
os.format(FMT_STRING("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
|
|
||||||
}
|
|
||||||
os << "bm.verts.ensure_lookup_table()\n";
|
|
||||||
|
|
||||||
/* Read in surfaces */
|
|
||||||
for (const typename MAPA::Surface& surf : mapa.surfaces) {
|
|
||||||
for (const typename MAPA::Surface::Primitive& prim : surf.primitives) {
|
|
||||||
auto iit = prim.indices.cbegin();
|
|
||||||
|
|
||||||
/* 3 Prim Verts to start */
|
|
||||||
int c = 0;
|
|
||||||
unsigned int primVerts[3] = {*iit++, *iit++, *iit++};
|
|
||||||
|
|
||||||
if (GX::Primitive(prim.type) == GX::TRIANGLESTRIP) {
|
|
||||||
atUint8 flip = 0;
|
|
||||||
for (size_t v = 0; v < prim.indexCount - 2; ++v) {
|
|
||||||
if (flip) {
|
|
||||||
os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[c % 3], primVerts[(c + 2) % 3],
|
|
||||||
primVerts[(c + 1) % 3]);
|
|
||||||
} else {
|
|
||||||
os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[c % 3], primVerts[(c + 1) % 3],
|
|
||||||
primVerts[(c + 2) % 3]);
|
|
||||||
}
|
|
||||||
flip ^= 1;
|
|
||||||
|
|
||||||
/* Break if done */
|
|
||||||
if (iit == prim.indices.cend())
|
|
||||||
break;
|
|
||||||
|
|
||||||
bool peek = (v >= prim.indexCount - 3);
|
|
||||||
|
|
||||||
/* Advance one prim vert */
|
|
||||||
if (peek)
|
|
||||||
primVerts[c % 3] = *iit;
|
|
||||||
else
|
|
||||||
primVerts[c % 3] = *iit++;
|
|
||||||
++c;
|
|
||||||
}
|
|
||||||
} else if (GX::Primitive(prim.type) == GX::TRIANGLES) {
|
|
||||||
for (size_t v = 0; v < prim.indexCount; v += 3) {
|
|
||||||
os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[0], primVerts[1], primVerts[2]);
|
|
||||||
|
|
||||||
/* Break if done */
|
|
||||||
if (v + 3 >= prim.indexCount)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Advance 3 Prim Verts */
|
|
||||||
for (int pv = 0; pv < 3; ++pv)
|
|
||||||
primVerts[pv] = *iit++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const typename MAPA::Surface::Border& border : surf.borders) {
|
|
||||||
auto iit = border.indices.cbegin();
|
|
||||||
for (size_t i = 0; i < border.indexCount - 1; ++i) {
|
|
||||||
os.format(FMT_STRING("add_border(bm, ({},{}))\n"), *iit, *(iit + 1));
|
|
||||||
++iit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "mesh = bpy.data.meshes.new('MAP')\n"
|
|
||||||
"obj = bpy.data.objects.new(mesh.name, mesh)\n"
|
|
||||||
"bm.to_mesh(mesh)\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
|
||||||
"bm.free()\n";
|
|
||||||
|
|
||||||
const zeus::CMatrix4f* tmpMtx = pakRouter.lookupMAPATransform(entry.id);
|
|
||||||
const zeus::CMatrix4f& mtx = tmpMtx ? *tmpMtx : zeus::skIdentityMatrix4f;
|
|
||||||
os.format(FMT_STRING("mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
|
||||||
"mtxd = mtx.decompose()\n"
|
|
||||||
"obj.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"obj.location = mtxd[0]\n"
|
|
||||||
"obj.rotation_quaternion = mtxd[1]\n"
|
|
||||||
"obj.scale = mtxd[2]\n"),
|
|
||||||
mtx[0][0], mtx[1][0], mtx[2][0], mtx[3][0], mtx[0][1], mtx[1][1], mtx[2][1], mtx[3][1], mtx[0][2],
|
|
||||||
mtx[1][2], mtx[2][2], mtx[3][2]);
|
|
||||||
|
|
||||||
/* World background */
|
|
||||||
hecl::ProjectPath worldDir = outPath.getParentPath().getParentPath();
|
|
||||||
for (const auto& ent : hecl::DirectoryEnumerator(worldDir.getAbsolutePath())) {
|
|
||||||
if (hecl::StringUtils::BeginsWith(ent.m_name, "!world") &&
|
|
||||||
hecl::StringUtils::EndsWith(ent.m_name, ".blend")) {
|
|
||||||
os.linkBackground(fmt::format(FMT_STRING("//../{}"), ent.m_name), "World"sv);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
os.centerView();
|
|
||||||
os.close();
|
|
||||||
conn.saveBlend();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template bool ReadMAPAToBlender<PAKRouter<DNAMP1::PAKBridge>>(hecl::blender::Connection& conn, const MAPA& mapa,
|
|
||||||
const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter,
|
|
||||||
const PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
|
|
||||||
bool force);
|
|
||||||
|
|
||||||
template bool ReadMAPAToBlender<PAKRouter<DNAMP2::PAKBridge>>(hecl::blender::Connection& conn, const MAPA& mapa,
|
|
||||||
const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter,
|
|
||||||
const PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
|
|
||||||
bool force);
|
|
||||||
|
|
||||||
template bool ReadMAPAToBlender<PAKRouter<DNAMP3::PAKBridge>>(hecl::blender::Connection& conn, const MAPA& mapa,
|
|
||||||
const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP3::PAKBridge>& pakRouter,
|
|
||||||
const PAKRouter<DNAMP3::PAKBridge>::EntryType& entry,
|
|
||||||
bool force);
|
|
||||||
|
|
||||||
template <typename MAPAType>
|
|
||||||
bool Cook(const hecl::blender::MapArea& mapaIn, const hecl::ProjectPath& out) {
|
|
||||||
if (mapaIn.verts.size() >= 256) {
|
|
||||||
Log.report(logvisor::Error, FMT_STRING("MAPA {} vertex range exceeded [{}/{}]"), out.getRelativePath(),
|
|
||||||
mapaIn.verts.size(), 255);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MAPAType mapa;
|
|
||||||
mapa.magic = 0xDEADD00D;
|
|
||||||
mapa.version = MAPAType::Version();
|
|
||||||
|
|
||||||
zeus::CAABox aabb;
|
|
||||||
for (const hecl::blender::Vector3f& vert : mapaIn.verts)
|
|
||||||
aabb.accumulateBounds(vert.val);
|
|
||||||
|
|
||||||
mapa.header = std::make_unique<typename MAPAType::Header>();
|
|
||||||
typename MAPAType::Header& header = static_cast<typename MAPAType::Header&>(*mapa.header);
|
|
||||||
header.unknown1 = 0;
|
|
||||||
header.mapVisMode = mapaIn.visType;
|
|
||||||
header.boundingBox[0] = aabb.min;
|
|
||||||
header.boundingBox[1] = aabb.max;
|
|
||||||
header.moCount = mapaIn.pois.size();
|
|
||||||
header.vtxCount = mapaIn.verts.size();
|
|
||||||
header.surfCount = mapaIn.surfaces.size();
|
|
||||||
|
|
||||||
mapa.mappableObjects.reserve(mapaIn.pois.size());
|
|
||||||
for (const hecl::blender::MapArea::POI& poi : mapaIn.pois) {
|
|
||||||
mapa.mappableObjects.push_back(std::make_unique<typename MAPAType::MappableObject>());
|
|
||||||
typename MAPAType::MappableObject& mobj =
|
|
||||||
static_cast<typename MAPAType::MappableObject&>(*mapa.mappableObjects.back());
|
|
||||||
mobj.type = MAPA::IMappableObject::Type(poi.type);
|
|
||||||
mobj.visMode = poi.visMode;
|
|
||||||
mobj.sclyId = poi.objid;
|
|
||||||
mobj.transformMtx[0] = poi.xf.val[0];
|
|
||||||
mobj.transformMtx[1] = poi.xf.val[1];
|
|
||||||
mobj.transformMtx[2] = poi.xf.val[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
mapa.vertices.reserve(mapaIn.verts.size());
|
|
||||||
for (const hecl::blender::Vector3f& vert : mapaIn.verts)
|
|
||||||
mapa.vertices.push_back(vert.val);
|
|
||||||
|
|
||||||
size_t offsetCur = 0;
|
|
||||||
for (const auto& mo : mapa.mappableObjects)
|
|
||||||
mo->binarySize(offsetCur);
|
|
||||||
offsetCur += mapa.vertices.size() * 12;
|
|
||||||
offsetCur += mapaIn.surfaces.size() * 32;
|
|
||||||
|
|
||||||
mapa.surfaceHeaders.reserve(mapaIn.surfaces.size());
|
|
||||||
mapa.surfaces.reserve(mapaIn.surfaces.size());
|
|
||||||
for (const hecl::blender::MapArea::Surface& surfIn : mapaIn.surfaces) {
|
|
||||||
mapa.surfaceHeaders.emplace_back();
|
|
||||||
DNAMAPA::MAPA::SurfaceHeader& surfHead = mapa.surfaceHeaders.back();
|
|
||||||
mapa.surfaces.emplace_back();
|
|
||||||
DNAMAPA::MAPA::Surface& surf = mapa.surfaces.back();
|
|
||||||
|
|
||||||
surf.primitiveCount = 1;
|
|
||||||
surf.primitives.emplace_back();
|
|
||||||
DNAMAPA::MAPA::Surface::Primitive& prim = surf.primitives.back();
|
|
||||||
prim.type = GX::TRIANGLESTRIP;
|
|
||||||
prim.indexCount = surfIn.count;
|
|
||||||
prim.indices.reserve(surfIn.count);
|
|
||||||
auto itBegin = mapaIn.indices.begin() + surfIn.start;
|
|
||||||
auto itEnd = itBegin + surfIn.count;
|
|
||||||
for (auto it = itBegin; it != itEnd; ++it)
|
|
||||||
prim.indices.push_back(*it);
|
|
||||||
|
|
||||||
surf.borderCount = surfIn.borders.size();
|
|
||||||
surf.borders.reserve(surfIn.borders.size());
|
|
||||||
for (const auto& borderIn : surfIn.borders) {
|
|
||||||
surf.borders.emplace_back();
|
|
||||||
DNAMAPA::MAPA::Surface::Border& border = surf.borders.back();
|
|
||||||
border.indexCount = borderIn.second;
|
|
||||||
border.indices.reserve(borderIn.second);
|
|
||||||
auto it2Begin = mapaIn.indices.begin() + borderIn.first;
|
|
||||||
auto it2End = it2Begin + borderIn.second;
|
|
||||||
for (auto it = it2Begin; it != it2End; ++it)
|
|
||||||
border.indices.push_back(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
surfHead.normal = surfIn.normal.val;
|
|
||||||
surfHead.centroid = surfIn.centerOfMass;
|
|
||||||
surfHead.polyOff = offsetCur;
|
|
||||||
offsetCur += 4;
|
|
||||||
prim.binarySize(offsetCur);
|
|
||||||
surfHead.edgeOff = offsetCur;
|
|
||||||
offsetCur += 4;
|
|
||||||
for (const auto& border : surf.borders)
|
|
||||||
border.binarySize(offsetCur);
|
|
||||||
}
|
|
||||||
|
|
||||||
athena::io::FileWriter f(out.getAbsolutePath());
|
|
||||||
mapa.write(f);
|
|
||||||
int64_t rem = f.position() % 32;
|
|
||||||
if (rem)
|
|
||||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
|
||||||
f.writeBytes((atInt8*)"\xff", 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template bool Cook<DNAMP1::MAPA>(const hecl::blender::MapArea& mapa, const hecl::ProjectPath& out);
|
|
||||||
template bool Cook<DNAMP2::MAPA>(const hecl::blender::MapArea& mapa, const hecl::ProjectPath& out);
|
|
||||||
template bool Cook<DNAMP3::MAPA>(const hecl::blender::MapArea& mapa, const hecl::ProjectPath& out);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMAPA
|
|
|
@ -1,177 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl::blender {
|
|
||||||
class Connection;
|
|
||||||
struct MapArea;
|
|
||||||
} // namespace hecl::blender
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMAPA {
|
|
||||||
struct MAPA : BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA
|
|
||||||
Value<atUint32> magic;
|
|
||||||
Value<atUint32> version;
|
|
||||||
struct IMAPAHeader : BigDNAV {
|
|
||||||
Delete _d;
|
|
||||||
virtual atUint32 visMode() const = 0;
|
|
||||||
virtual atUint32 mappableObjectCount() const = 0;
|
|
||||||
virtual atUint32 vertexCount() const = 0;
|
|
||||||
virtual atUint32 surfaceCount() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HeaderMP1 : IMAPAHeader {
|
|
||||||
AT_DECL_DNAV
|
|
||||||
Value<atUint32> unknown1 = 0;
|
|
||||||
Value<atUint32> mapVisMode = 0;
|
|
||||||
Value<atVec3f> boundingBox[2] = {};
|
|
||||||
Value<atUint32> moCount = 0;
|
|
||||||
Value<atUint32> vtxCount = 0;
|
|
||||||
Value<atUint32> surfCount = 0;
|
|
||||||
atUint32 visMode() const override { return mapVisMode; }
|
|
||||||
atUint32 mappableObjectCount() const override { return moCount; }
|
|
||||||
atUint32 vertexCount() const override { return vtxCount; }
|
|
||||||
atUint32 surfaceCount() const override { return surfCount; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HeaderMP2 : IMAPAHeader {
|
|
||||||
AT_DECL_DNAV
|
|
||||||
Value<atUint32> unknown1 = 0;
|
|
||||||
Value<atUint32> mapVisMode = 0;
|
|
||||||
Value<atVec3f> boundingBox[2] = {};
|
|
||||||
Value<atUint32> unknown3 = 0;
|
|
||||||
Value<atUint32> unknown4 = 0;
|
|
||||||
Value<atUint32> unknown5 = 0;
|
|
||||||
Value<atUint32> moCount = 0;
|
|
||||||
Value<atUint32> vtxCount = 0;
|
|
||||||
Value<atUint32> surfCount = 0;
|
|
||||||
atUint32 visMode() const override { return mapVisMode; }
|
|
||||||
atUint32 mappableObjectCount() const override { return moCount; }
|
|
||||||
atUint32 vertexCount() const override { return vtxCount; }
|
|
||||||
atUint32 surfaceCount() const override { return surfCount; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HeaderMP3 : IMAPAHeader {
|
|
||||||
AT_DECL_DNAV
|
|
||||||
Value<atUint32> unknown1 = 0;
|
|
||||||
Value<atUint32> mapVisMode = 0;
|
|
||||||
Value<atVec3f> boundingBox[2] = {};
|
|
||||||
Value<atUint32> unknown3 = 0;
|
|
||||||
Value<atUint32> unknown4 = 0;
|
|
||||||
Value<atUint32> unknown5 = 0;
|
|
||||||
Value<atUint32> unknown6 = 0;
|
|
||||||
Value<atUint32> moCount = 0;
|
|
||||||
Value<atUint32> vtxCount = 0;
|
|
||||||
Value<atUint32> surfCount = 0;
|
|
||||||
Value<atUint32> internalNameLength = 0;
|
|
||||||
Value<atUint32> unknown7 = 0;
|
|
||||||
String<AT_DNA_COUNT(internalNameLength)> internalName;
|
|
||||||
atUint32 visMode() const override { return mapVisMode; }
|
|
||||||
atUint32 mappableObjectCount() const override { return moCount; }
|
|
||||||
atUint32 vertexCount() const override { return vtxCount; }
|
|
||||||
atUint32 surfaceCount() const override { return surfCount; }
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<IMAPAHeader> header;
|
|
||||||
|
|
||||||
struct IMappableObject : BigDNAV {
|
|
||||||
Delete _d;
|
|
||||||
enum class Type : atUint32 {
|
|
||||||
BlueDoor = 0,
|
|
||||||
ShieldDoor = 1,
|
|
||||||
IceDoor = 2,
|
|
||||||
WaveDoor = 3,
|
|
||||||
PlasmaDoor = 4,
|
|
||||||
BigDoor1 = 5,
|
|
||||||
BigDoor2 = 6,
|
|
||||||
IceDoorCeiling = 7,
|
|
||||||
IceDoorFloor = 8,
|
|
||||||
WaveDoorCeiling = 9,
|
|
||||||
WaveDoorFloor = 10,
|
|
||||||
IceDoorFloor2 = 13,
|
|
||||||
WaveDoorFloor2 = 14,
|
|
||||||
DownArrowYellow = 27, /* Maintenance Tunnel */
|
|
||||||
UpArrowYellow = 28, /* Phazon Processing Center */
|
|
||||||
DownArrowGreen = 29, /* Elevator A */
|
|
||||||
UpArrowGreen = 30, /* Elite Control Access */
|
|
||||||
DownArrowRed = 31, /* Elevator B */
|
|
||||||
UpArrowRed = 32, /* Fungal Hall Access */
|
|
||||||
TransportLift = 33,
|
|
||||||
SaveStation = 34,
|
|
||||||
MissileStation = 37
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MappableObjectMP1_2 : IMappableObject {
|
|
||||||
AT_DECL_DNAV
|
|
||||||
Value<Type> type;
|
|
||||||
Value<atUint32> visMode;
|
|
||||||
Value<atUint32> sclyId;
|
|
||||||
Value<atInt32> seek1 = -1;
|
|
||||||
Value<atVec4f> transformMtx[3];
|
|
||||||
Value<atInt32> seek2[4] = {-1, -1, -1, -1};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MappableObjectMP3 : IMappableObject {
|
|
||||||
AT_DECL_DNAV
|
|
||||||
Value<Type> type;
|
|
||||||
Value<atUint32> visMode;
|
|
||||||
Value<atUint32> sclyId;
|
|
||||||
Buffer<AT_DNA_COUNT(0x10)> unknownHash;
|
|
||||||
Value<atInt32> seek1 = -1;
|
|
||||||
Value<atVec4f> transformMtx[3];
|
|
||||||
Value<atInt32> seek2[4] = {-1, -1, -1, -1};
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<IMappableObject>> mappableObjects;
|
|
||||||
Vector<atVec3f, AT_DNA_COUNT(header->vertexCount())> vertices;
|
|
||||||
|
|
||||||
struct SurfaceHeader : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atVec3f> normal;
|
|
||||||
Value<atVec3f> centroid;
|
|
||||||
Value<atUint32> polyOff;
|
|
||||||
Value<atUint32> edgeOff;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<SurfaceHeader, AT_DNA_COUNT(header->surfaceCount())> surfaceHeaders;
|
|
||||||
|
|
||||||
struct Surface : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> primitiveCount;
|
|
||||||
struct Primitive : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> type;
|
|
||||||
Value<atUint32> indexCount;
|
|
||||||
Vector<atUint8, AT_DNA_COUNT(indexCount)> indices;
|
|
||||||
Align<4> align;
|
|
||||||
};
|
|
||||||
Vector<Primitive, AT_DNA_COUNT(primitiveCount)> primitives;
|
|
||||||
Value<atUint32> borderCount;
|
|
||||||
struct Border : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> indexCount;
|
|
||||||
Vector<atUint8, AT_DNA_COUNT(indexCount)> indices;
|
|
||||||
Align<4> align;
|
|
||||||
};
|
|
||||||
Vector<Border, AT_DNA_COUNT(borderCount)> borders;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<Surface, AT_DNA_COUNT(header->surfaceCount())> surfaces;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename PAKRouter>
|
|
||||||
bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force);
|
|
||||||
|
|
||||||
template <typename MAPAType>
|
|
||||||
bool Cook(const hecl::blender::MapArea& mapa, const hecl::ProjectPath& out);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMAPA
|
|
|
@ -1,146 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/MAPU.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
|
||||||
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
#include <zeus/Global.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMAPU {
|
|
||||||
|
|
||||||
template <typename PAKRouter>
|
|
||||||
bool ReadMAPUToBlender(hecl::blender::Connection& conn, const MAPU& mapu, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force) {
|
|
||||||
if (!force && outPath.isFile())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::MapUniverse))
|
|
||||||
return false;
|
|
||||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
|
||||||
|
|
||||||
os << "import bpy\n"
|
|
||||||
"from mathutils import Matrix\n"
|
|
||||||
"\n"
|
|
||||||
"# Clear Scene\n"
|
|
||||||
"if len(bpy.data.collections):\n"
|
|
||||||
" bpy.data.collections.remove(bpy.data.collections[0])\n"
|
|
||||||
"\n"
|
|
||||||
"bpy.types.Object.retro_mapworld_color = bpy.props.FloatVectorProperty(name='Retro: MapWorld Color',"
|
|
||||||
" description='Sets map world color', subtype='COLOR', size=4, min=0.0, max=1.0)\n"
|
|
||||||
"bpy.types.Object.retro_mapworld_path = bpy.props.StringProperty(name='Retro: MapWorld Path',"
|
|
||||||
" description='Sets path to World root')\n"
|
|
||||||
"\n";
|
|
||||||
|
|
||||||
hecl::ProjectPath hexPath = pakRouter.getWorking(mapu.hexMapa);
|
|
||||||
os.linkMesh(hexPath.getAbsolutePath(), "MAP");
|
|
||||||
os << "hexMesh = bpy.data.objects['MAP'].data\n";
|
|
||||||
|
|
||||||
for (const MAPU::World& wld : mapu.worlds) {
|
|
||||||
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(wld.mlvl);
|
|
||||||
const MAPU::Transform& wldXf = wld.transform;
|
|
||||||
zeus::simd_floats wldXfF[3];
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
wldXf.xf[i].simd.copy_to(wldXfF[i]);
|
|
||||||
zeus::simd_floats hexColorF(wld.hexColor.mSimd);
|
|
||||||
os.format(FMT_STRING("wldObj = bpy.data.objects.new('{}', None)\n"
|
|
||||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
|
||||||
"mtxd = mtx.decompose()\n"
|
|
||||||
"wldObj.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"wldObj.location = mtxd[0]\n"
|
|
||||||
"wldObj.rotation_quaternion = mtxd[1]\n"
|
|
||||||
"wldObj.scale = mtxd[2]\n"
|
|
||||||
"wldObj.retro_mapworld_color = ({}, {}, {}, {})\n"
|
|
||||||
"wldObj.retro_mapworld_path = '''{}'''\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(wldObj)\n"),
|
|
||||||
wld.name, wldXfF[0][0], wldXfF[0][1], wldXfF[0][2], wldXfF[0][3], wldXfF[1][0], wldXfF[1][1],
|
|
||||||
wldXfF[1][2], wldXfF[1][3], wldXfF[2][0], wldXfF[2][1], wldXfF[2][2], wldXfF[2][3], hexColorF[0],
|
|
||||||
hexColorF[1], hexColorF[2], hexColorF[3], path.getParentPath().getRelativePath());
|
|
||||||
int idx = 0;
|
|
||||||
for (const MAPU::Transform& hexXf : wld.hexTransforms) {
|
|
||||||
zeus::simd_floats hexXfF[3];
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
hexXf.xf[i].simd.copy_to(hexXfF[i]);
|
|
||||||
os.format(FMT_STRING("obj = bpy.data.objects.new('{}_{}', hexMesh)\n"
|
|
||||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
|
||||||
"mtxd = mtx.decompose()\n"
|
|
||||||
"obj.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"obj.location = mtxd[0]\n"
|
|
||||||
"obj.rotation_quaternion = mtxd[1]\n"
|
|
||||||
"obj.scale = mtxd[2]\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
|
||||||
"obj.parent = wldObj\n"),
|
|
||||||
wld.name, idx++, hexXfF[0][0], hexXfF[0][1], hexXfF[0][2], hexXfF[0][3], hexXfF[1][0], hexXfF[1][1],
|
|
||||||
hexXfF[1][2], hexXfF[1][3], hexXfF[2][0], hexXfF[2][1], hexXfF[2][2], hexXfF[2][3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "for screen in bpy.data.screens:\n"
|
|
||||||
" for area in screen.areas:\n"
|
|
||||||
" for space in area.spaces:\n"
|
|
||||||
" if space.type == 'VIEW_3D':\n"
|
|
||||||
" space.clip_end = 8000.0\n";
|
|
||||||
|
|
||||||
os.centerView();
|
|
||||||
os.close();
|
|
||||||
conn.saveBlend();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template bool ReadMAPUToBlender<PAKRouter<DNAMP1::PAKBridge>>(hecl::blender::Connection& conn, const MAPU& mapu,
|
|
||||||
const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter,
|
|
||||||
const PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
|
|
||||||
bool force);
|
|
||||||
|
|
||||||
template bool ReadMAPUToBlender<PAKRouter<DNAMP2::PAKBridge>>(hecl::blender::Connection& conn, const MAPU& mapu,
|
|
||||||
const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter,
|
|
||||||
const PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
|
|
||||||
bool force);
|
|
||||||
|
|
||||||
bool MAPU::Cook(const hecl::blender::MapUniverse& mapuIn, const hecl::ProjectPath& out) {
|
|
||||||
MAPU mapu;
|
|
||||||
|
|
||||||
mapu.magic = 0xABCDEF01;
|
|
||||||
mapu.version = 1;
|
|
||||||
mapu.hexMapa = mapuIn.hexagonPath;
|
|
||||||
|
|
||||||
mapu.worldCount = mapuIn.worlds.size();
|
|
||||||
mapu.worlds.reserve(mapuIn.worlds.size());
|
|
||||||
for (const hecl::blender::MapUniverse::World& wld : mapuIn.worlds) {
|
|
||||||
mapu.worlds.emplace_back();
|
|
||||||
MAPU::World& wldOut = mapu.worlds.back();
|
|
||||||
wldOut.name = wld.name;
|
|
||||||
for (const auto& ent : wld.worldPath.enumerateDir()) {
|
|
||||||
if (hecl::StringUtils::BeginsWith(ent.m_name, "!world") &&
|
|
||||||
hecl::StringUtils::EndsWith(ent.m_name, ".blend")) {
|
|
||||||
wldOut.mlvl = hecl::ProjectPath(wld.worldPath, ent.m_name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wldOut.transform.xf[0] = wld.xf.val[0];
|
|
||||||
wldOut.transform.xf[1] = wld.xf.val[1];
|
|
||||||
wldOut.transform.xf[2] = wld.xf.val[2];
|
|
||||||
wldOut.hexCount = wld.hexagons.size();
|
|
||||||
wldOut.hexTransforms.reserve(wld.hexagons.size());
|
|
||||||
for (const hecl::blender::Matrix4f& mtx : wld.hexagons) {
|
|
||||||
wldOut.hexTransforms.emplace_back();
|
|
||||||
MAPU::Transform& xf = wldOut.hexTransforms.back();
|
|
||||||
xf.xf[0] = mtx.val[0];
|
|
||||||
xf.xf[1] = mtx.val[1];
|
|
||||||
xf.xf[2] = mtx.val[2];
|
|
||||||
}
|
|
||||||
wldOut.hexColor = zeus::CColor(wld.color.val);
|
|
||||||
}
|
|
||||||
|
|
||||||
athena::io::FileWriter f(out.getAbsolutePath());
|
|
||||||
mapu.write(f);
|
|
||||||
int64_t rem = f.position() % 32;
|
|
||||||
if (rem)
|
|
||||||
for (int64_t i = 0; i < 32 - rem; ++i)
|
|
||||||
f.writeBytes((atInt8*)"\xff", 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMAPU
|
|
|
@ -1,45 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl::blender {
|
|
||||||
class Connection;
|
|
||||||
struct MapUniverse;
|
|
||||||
} // namespace hecl::blender
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMAPU {
|
|
||||||
struct MAPU : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<uint32_t> magic;
|
|
||||||
Value<uint32_t> version;
|
|
||||||
UniqueID32 hexMapa;
|
|
||||||
Value<uint32_t> worldCount;
|
|
||||||
struct Transform : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atVec4f> xf[3];
|
|
||||||
};
|
|
||||||
struct World : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
String<-1> name;
|
|
||||||
UniqueID32 mlvl;
|
|
||||||
Transform transform;
|
|
||||||
Value<uint32_t> hexCount;
|
|
||||||
Vector<Transform, AT_DNA_COUNT(hexCount)> hexTransforms;
|
|
||||||
DNAColor hexColor;
|
|
||||||
};
|
|
||||||
Vector<World, AT_DNA_COUNT(worldCount)> worlds;
|
|
||||||
|
|
||||||
static bool Cook(const hecl::blender::MapUniverse& mapu, const hecl::ProjectPath& out);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename PAKRouter>
|
|
||||||
bool ReadMAPUToBlender(hecl::blender::Connection& conn, const MAPU& mapu, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMAPU
|
|
|
@ -1,109 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/MLVL.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNAMP1/MLVL.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/MLVL.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/MLVL.hpp"
|
|
||||||
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
#include <zeus/Global.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMLVL {
|
|
||||||
|
|
||||||
template <class PAKRouter, typename MLVL>
|
|
||||||
bool ReadMLVLToBlender(hecl::blender::Connection& conn, const MLVL& mlvl, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force,
|
|
||||||
std::function<void(const char*)> fileChanged) {
|
|
||||||
hecl::ProjectPath blendPath = outPath.getWithExtension(".blend", true);
|
|
||||||
if (!force && blendPath.isFile())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* Create World Blend */
|
|
||||||
if (!conn.createBlend(blendPath, hecl::blender::BlendType::World))
|
|
||||||
return false;
|
|
||||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
|
||||||
os << "import bpy\n"
|
|
||||||
"import bmesh\n"
|
|
||||||
"from mathutils import Matrix\n"
|
|
||||||
"\n"
|
|
||||||
"bpy.context.scene.name = 'World'\n"
|
|
||||||
"\n"
|
|
||||||
"# Clear Scene\n"
|
|
||||||
"if len(bpy.data.collections):\n"
|
|
||||||
" bpy.data.collections.remove(bpy.data.collections[0])\n";
|
|
||||||
|
|
||||||
/* Insert area empties */
|
|
||||||
int areaIdx = 0;
|
|
||||||
for (const auto& area : mlvl.areas) {
|
|
||||||
const typename PAKRouter::EntryType* mreaEntry = pakRouter.lookupEntry(area.areaMREAId);
|
|
||||||
|
|
||||||
os.AABBToBMesh(area.aabb[0], area.aabb[1]);
|
|
||||||
zeus::simd_floats xfMtxF[3];
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
area.transformMtx[i].simd.copy_to(xfMtxF[i]);
|
|
||||||
os.format(FMT_STRING("box_mesh = bpy.data.meshes.new('''{}''')\n"
|
|
||||||
"bm.to_mesh(box_mesh)\n"
|
|
||||||
"bm.free()\n"
|
|
||||||
"box = bpy.data.objects.new(box_mesh.name, box_mesh)\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(box)\n"
|
|
||||||
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
|
||||||
"mtxd = mtx.decompose()\n"
|
|
||||||
"box.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"box.location = mtxd[0]\n"
|
|
||||||
"box.rotation_quaternion = mtxd[1]\n"
|
|
||||||
"box.scale = mtxd[2]\n"),
|
|
||||||
*mreaEntry->unique.m_areaName, xfMtxF[0][0], xfMtxF[0][1], xfMtxF[0][2], xfMtxF[0][3], xfMtxF[1][0], xfMtxF[1][1],
|
|
||||||
xfMtxF[1][2], xfMtxF[1][3], xfMtxF[2][0], xfMtxF[2][1], xfMtxF[2][2], xfMtxF[2][3]);
|
|
||||||
|
|
||||||
/* Insert dock planes */
|
|
||||||
int dockIdx = 0;
|
|
||||||
for (const auto& dock : area.docks) {
|
|
||||||
os << "bm = bmesh.new()\n";
|
|
||||||
zeus::CVector3f pvAvg;
|
|
||||||
for (const atVec3f& pv : dock.planeVerts)
|
|
||||||
pvAvg += pv;
|
|
||||||
pvAvg /= zeus::CVector3f(dock.planeVerts.size());
|
|
||||||
int idx = 0;
|
|
||||||
for (const atVec3f& pv : dock.planeVerts) {
|
|
||||||
const zeus::CVector3f pvRel = zeus::CVector3f(pv) - pvAvg;
|
|
||||||
os.format(FMT_STRING("bm.verts.new(({},{},{}))\n"
|
|
||||||
"bm.verts.ensure_lookup_table()\n"),
|
|
||||||
pvRel[0], pvRel[1], pvRel[2]);
|
|
||||||
if (idx)
|
|
||||||
os << "bm.edges.new((bm.verts[-2], bm.verts[-1]))\n";
|
|
||||||
++idx;
|
|
||||||
}
|
|
||||||
os << "bm.edges.new((bm.verts[-1], bm.verts[0]))\n";
|
|
||||||
os.format(FMT_STRING("dockMesh = bpy.data.meshes.new('DOCK_{:02d}_{:02d}')\n"), areaIdx, dockIdx);
|
|
||||||
os << "dockObj = bpy.data.objects.new(dockMesh.name, dockMesh)\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(dockObj)\n"
|
|
||||||
"bm.to_mesh(dockMesh)\n"
|
|
||||||
"bm.free()\n"
|
|
||||||
"dockObj.parent = box\n";
|
|
||||||
os.format(FMT_STRING("dockObj.location = ({},{},{})\n"), float(pvAvg[0]), float(pvAvg[1]), float(pvAvg[2]));
|
|
||||||
++dockIdx;
|
|
||||||
}
|
|
||||||
++areaIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
os.centerView();
|
|
||||||
os.close();
|
|
||||||
conn.saveBlend();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template bool ReadMLVLToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::MLVL>(
|
|
||||||
hecl::blender::Connection& conn, const DNAMP1::MLVL& mlvl, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP1::PAKBridge>& pakRouter, const PAKRouter<DNAMP1::PAKBridge>::EntryType& entry, bool force,
|
|
||||||
std::function<void(const char*)> fileChanged);
|
|
||||||
|
|
||||||
template bool ReadMLVLToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::MLVL>(
|
|
||||||
hecl::blender::Connection& conn, const DNAMP2::MLVL& mlvl, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP2::PAKBridge>& pakRouter, const PAKRouter<DNAMP2::PAKBridge>::EntryType& entry, bool force,
|
|
||||||
std::function<void(const char*)> fileChanged);
|
|
||||||
|
|
||||||
template bool ReadMLVLToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::MLVL>(
|
|
||||||
hecl::blender::Connection& conn, const DNAMP3::MLVL& mlvl, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<DNAMP3::PAKBridge>& pakRouter, const PAKRouter<DNAMP3::PAKBridge>::EntryType& entry, bool force,
|
|
||||||
std::function<void(const char*)> fileChanged);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMLVL
|
|
|
@ -1,22 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl::blender {
|
|
||||||
class Connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMLVL {
|
|
||||||
|
|
||||||
template <class PAKRouter, typename MLVL>
|
|
||||||
bool ReadMLVLToBlender(hecl::blender::Connection& conn, const MLVL& mlvl, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, bool force,
|
|
||||||
std::function<void(const char*)> fileChanged);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
struct MayaSpline : public BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint8> preInf;
|
|
||||||
Value<atUint8> postInf;
|
|
||||||
Value<atUint32> knotCount;
|
|
||||||
struct Knot : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<float> time;
|
|
||||||
Value<float> amplitude;
|
|
||||||
Value<atUint8> unk1;
|
|
||||||
Value<atUint8> unk2;
|
|
||||||
Vector<atVec2f, AT_DNA_COUNT(unk1 == 5)> unk1Floats;
|
|
||||||
Vector<atVec2f, AT_DNA_COUNT(unk2 == 5)> unk2Floats;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<Knot, AT_DNA_COUNT(knotCount)> knots;
|
|
||||||
Value<atUint8> clampMode;
|
|
||||||
Value<float> minAmp;
|
|
||||||
Value<float> maxAmp;
|
|
||||||
};
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,24 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
enum class ERegion { Invalid = -1, NTSC_U = 'E', PAL = 'P', NTSC_J = 'J' };
|
|
||||||
enum class EGame {
|
|
||||||
Invalid = 0,
|
|
||||||
MetroidPrime1,
|
|
||||||
MetroidPrime2,
|
|
||||||
MetroidPrime3,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MetaforceVersionInfo : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
|
|
||||||
String<-1> version;
|
|
||||||
Value<ERegion> region;
|
|
||||||
Value<EGame> game;
|
|
||||||
Value<bool> isTrilogy;
|
|
||||||
};
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,258 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/OBBTreeBuilder.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNAMP1/DCLN.hpp"
|
|
||||||
|
|
||||||
#include <athena/Types.hpp>
|
|
||||||
#include <gmm/gmm.h>
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
#include <zeus/CTransform.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
using ColMesh = hecl::blender::ColMesh;
|
|
||||||
|
|
||||||
struct FittedOBB {
|
|
||||||
zeus::CTransform xf;
|
|
||||||
zeus::CVector3f he;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::vector<int> MakeRootTriangleIndex(const ColMesh& mesh) {
|
|
||||||
std::vector<int> ret;
|
|
||||||
ret.reserve(mesh.trianges.size());
|
|
||||||
for (size_t i = 0; i < mesh.trianges.size(); ++i)
|
|
||||||
ret.push_back(i);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::unordered_set<uint32_t> GetTriangleVerts(const ColMesh& mesh, int triIdx) {
|
|
||||||
const ColMesh::Triangle& T = mesh.trianges[triIdx];
|
|
||||||
std::unordered_set<uint32_t> verts;
|
|
||||||
verts.insert(mesh.edges[T.edges[0]].verts[0]);
|
|
||||||
verts.insert(mesh.edges[T.edges[0]].verts[1]);
|
|
||||||
verts.insert(mesh.edges[T.edges[1]].verts[0]);
|
|
||||||
verts.insert(mesh.edges[T.edges[1]].verts[1]);
|
|
||||||
verts.insert(mesh.edges[T.edges[2]].verts[0]);
|
|
||||||
verts.insert(mesh.edges[T.edges[2]].verts[1]);
|
|
||||||
return verts;
|
|
||||||
}
|
|
||||||
|
|
||||||
// method to set the OBB parameters which produce a box oriented according to
|
|
||||||
// the covariance matrix C, which just containts the points pnts
|
|
||||||
static FittedOBB BuildFromCovarianceMatrix(gmm::dense_matrix<float>& C, const ColMesh& mesh,
|
|
||||||
const std::vector<int>& index) {
|
|
||||||
FittedOBB ret;
|
|
||||||
|
|
||||||
// extract the eigenvalues and eigenvectors from C
|
|
||||||
gmm::dense_matrix<float> eigvec(3, 3);
|
|
||||||
std::vector<float> eigval(3);
|
|
||||||
using namespace gmm;
|
|
||||||
using MAT1 = gmm::dense_matrix<float>;
|
|
||||||
gmm::symmetric_qr_algorithm(C, eigval, eigvec, default_tol_for_qr);
|
|
||||||
|
|
||||||
// find the right, up and forward vectors from the eigenvectors
|
|
||||||
zeus::CVector3f r(eigvec(0, 0), eigvec(1, 0), eigvec(2, 0));
|
|
||||||
zeus::CVector3f f(eigvec(0, 1), eigvec(1, 1), eigvec(2, 1));
|
|
||||||
zeus::CVector3f u(eigvec(0, 2), eigvec(1, 2), eigvec(2, 2));
|
|
||||||
r.normalize();
|
|
||||||
f.normalize();
|
|
||||||
u.normalize();
|
|
||||||
|
|
||||||
// set the rotation matrix using the eigvenvectors
|
|
||||||
ret.xf.basis[0] = r;
|
|
||||||
ret.xf.basis[1] = f;
|
|
||||||
ret.xf.basis[2] = u;
|
|
||||||
ret.xf.orthonormalize();
|
|
||||||
|
|
||||||
// now build the bounding box extents in the rotated frame
|
|
||||||
zeus::CVector3f minim(1e10f, 1e10f, 1e10f), maxim(-1e10f, -1e10f, -1e10f);
|
|
||||||
for (int triIdx : index) {
|
|
||||||
std::unordered_set<uint32_t> verts = GetTriangleVerts(mesh, triIdx);
|
|
||||||
for (uint32_t v : verts) {
|
|
||||||
const zeus::CVector3f& p = mesh.verts[v].val;
|
|
||||||
zeus::CVector3f p_prime(ret.xf.basis[0].dot(p), ret.xf.basis[1].dot(p), ret.xf.basis[2].dot(p));
|
|
||||||
minim = zeus::min(minim, p_prime);
|
|
||||||
maxim = zeus::max(maxim, p_prime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the center of the OBB to be the average of the
|
|
||||||
// minimum and maximum, and the extents be half of the
|
|
||||||
// difference between the minimum and maximum
|
|
||||||
zeus::CVector3f center = (maxim + minim) * 0.5f;
|
|
||||||
ret.xf.origin = ret.xf.basis * center;
|
|
||||||
ret.he = (maxim - minim) * 0.5f;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// builds an OBB from triangles specified as an array of
|
|
||||||
// points with integer indices into the point array. Forms
|
|
||||||
// the covariance matrix for the triangles, then uses the
|
|
||||||
// method build_from_covariance_matrix() method to fit
|
|
||||||
// the box. ALL points will be fit in the box, regardless
|
|
||||||
// of whether they are indexed by a triangle or not.
|
|
||||||
static FittedOBB FitOBB(const ColMesh& mesh, const std::vector<int>& index) {
|
|
||||||
float Ai, Am = 0.0;
|
|
||||||
zeus::CVector3f mu, mui;
|
|
||||||
gmm::dense_matrix<float> C(3, 3);
|
|
||||||
float cxx = 0.0, cxy = 0.0, cxz = 0.0, cyy = 0.0, cyz = 0.0, czz = 0.0;
|
|
||||||
|
|
||||||
// loop over the triangles this time to find the
|
|
||||||
// mean location
|
|
||||||
for (int i : index) {
|
|
||||||
std::unordered_set<uint32_t> verts = GetTriangleVerts(mesh, i);
|
|
||||||
auto it = verts.begin();
|
|
||||||
zeus::CVector3f p = mesh.verts[*it++].val;
|
|
||||||
zeus::CVector3f q = mesh.verts[*it++].val;
|
|
||||||
zeus::CVector3f r = mesh.verts[*it++].val;
|
|
||||||
mui = (p + q + r) / 3.f;
|
|
||||||
Ai = (q - p).cross(r - p).magnitude() / 2.f;
|
|
||||||
mu += mui * Ai;
|
|
||||||
Am += Ai;
|
|
||||||
|
|
||||||
// these bits set the c terms to Am*E[xx], Am*E[xy], Am*E[xz]....
|
|
||||||
cxx += (9.0 * mui.x() * mui.x() + p.x() * p.x() + q.x() * q.x() + r.x() * r.x()) * (Ai / 12.0);
|
|
||||||
cxy += (9.0 * mui.x() * mui.y() + p.x() * p.y() + q.x() * q.y() + r.x() * r.y()) * (Ai / 12.0);
|
|
||||||
cxz += (9.0 * mui.x() * mui.z() + p.x() * p.z() + q.x() * q.z() + r.x() * r.z()) * (Ai / 12.0);
|
|
||||||
cyy += (9.0 * mui.y() * mui.y() + p.y() * p.y() + q.y() * q.y() + r.y() * r.y()) * (Ai / 12.0);
|
|
||||||
cyz += (9.0 * mui.y() * mui.z() + p.y() * p.z() + q.y() * q.z() + r.y() * r.z()) * (Ai / 12.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zeus::close_enough(Am, 0.f))
|
|
||||||
return {};
|
|
||||||
|
|
||||||
// divide out the Am fraction from the average position and
|
|
||||||
// covariance terms
|
|
||||||
mu = mu / Am;
|
|
||||||
cxx /= Am;
|
|
||||||
cxy /= Am;
|
|
||||||
cxz /= Am;
|
|
||||||
cyy /= Am;
|
|
||||||
cyz /= Am;
|
|
||||||
czz /= Am;
|
|
||||||
|
|
||||||
// now subtract off the E[x]*E[x], E[x]*E[y], ... terms
|
|
||||||
cxx -= mu.x() * mu.x();
|
|
||||||
cxy -= mu.x() * mu.y();
|
|
||||||
cxz -= mu.x() * mu.z();
|
|
||||||
cyy -= mu.y() * mu.y();
|
|
||||||
cyz -= mu.y() * mu.z();
|
|
||||||
czz -= mu.z() * mu.z();
|
|
||||||
|
|
||||||
// now build the covariance matrix
|
|
||||||
C(0, 0) = cxx;
|
|
||||||
C(0, 1) = cxy;
|
|
||||||
C(0, 2) = cxz;
|
|
||||||
C(1, 0) = cxy;
|
|
||||||
C(1, 1) = cyy;
|
|
||||||
C(1, 2) = cyz;
|
|
||||||
C(2, 0) = cxz;
|
|
||||||
C(2, 1) = cyz;
|
|
||||||
C(2, 2) = czz;
|
|
||||||
|
|
||||||
// set the obb parameters from the covariance matrix
|
|
||||||
return BuildFromCovarianceMatrix(C, mesh, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Node>
|
|
||||||
static void MakeLeaf(const ColMesh& mesh, const std::vector<int>& index, Node& n) {
|
|
||||||
n.left.reset();
|
|
||||||
n.right.reset();
|
|
||||||
n.isLeaf = true;
|
|
||||||
n.leafData = std::make_unique<typename Node::LeafData>();
|
|
||||||
n.leafData->triangleIndexCount = atUint32(index.size());
|
|
||||||
n.leafData->triangleIndices.reserve(n.leafData->triangleIndexCount);
|
|
||||||
for (int i : index)
|
|
||||||
n.leafData->triangleIndices.push_back(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Node>
|
|
||||||
static std::unique_ptr<Node> RecursiveMakeNode(const ColMesh& mesh, const std::vector<int>& index) {
|
|
||||||
// calculate root OBB
|
|
||||||
FittedOBB obb = FitOBB(mesh, index);
|
|
||||||
|
|
||||||
// make results row-major and also invert the rotation basis
|
|
||||||
obb.xf.basis.transpose();
|
|
||||||
|
|
||||||
std::unique_ptr<Node> n = std::make_unique<Node>();
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
n->xf[i] = zeus::CVector4f{obb.xf.basis[i]};
|
|
||||||
n->xf[i].simd[3] = float(obb.xf.origin[i]);
|
|
||||||
}
|
|
||||||
n->halfExtent = obb.he;
|
|
||||||
|
|
||||||
// terminate branch when volume < 1.0
|
|
||||||
if (obb.he[0] * obb.he[1] * obb.he[2] < 1.f) {
|
|
||||||
MakeLeaf(mesh, index, *n);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->isLeaf = false;
|
|
||||||
|
|
||||||
std::vector<int> indexNeg[3];
|
|
||||||
std::vector<int> indexPos[3];
|
|
||||||
for (int c = 0; c < 3; ++c) {
|
|
||||||
// subdivide negative side
|
|
||||||
indexNeg[c].reserve(index.size());
|
|
||||||
for (int i : index) {
|
|
||||||
std::unordered_set<uint32_t> verts = GetTriangleVerts(mesh, i);
|
|
||||||
for (uint32_t vtx : verts) {
|
|
||||||
zeus::CVector3f v = mesh.verts[vtx].val;
|
|
||||||
v = obb.xf.basis * (v - obb.xf.origin);
|
|
||||||
if (v[c] < 0.f) {
|
|
||||||
indexNeg[c].push_back(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// subdivide positive side
|
|
||||||
indexPos[c].reserve(index.size());
|
|
||||||
for (int i : index) {
|
|
||||||
std::unordered_set<uint32_t> verts = GetTriangleVerts(mesh, i);
|
|
||||||
for (uint32_t vtx : verts) {
|
|
||||||
zeus::CVector3f v = mesh.verts[vtx].val;
|
|
||||||
v = obb.xf.basis * (v - obb.xf.origin);
|
|
||||||
if (v[c] >= 0.f) {
|
|
||||||
indexPos[c].push_back(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t idxMin = index.size();
|
|
||||||
int minComp = -1;
|
|
||||||
for (int c = 0; c < 3; ++c) {
|
|
||||||
size_t test = std::max(indexNeg[c].size(), indexPos[c].size());
|
|
||||||
if (test < idxMin && test < index.size() * 3 / 4) {
|
|
||||||
minComp = c;
|
|
||||||
idxMin = test;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minComp == -1) {
|
|
||||||
MakeLeaf(mesh, index, *n);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->left = RecursiveMakeNode<Node>(mesh, indexNeg[minComp]);
|
|
||||||
n->right = RecursiveMakeNode<Node>(mesh, indexPos[minComp]);
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Node>
|
|
||||||
std::unique_ptr<Node> OBBTreeBuilder::buildCol(const ColMesh& mesh) {
|
|
||||||
std::vector<int> root = MakeRootTriangleIndex(mesh);
|
|
||||||
return RecursiveMakeNode<Node>(mesh, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
template std::unique_ptr<DNAMP1::DCLN::Collision::Node>
|
|
||||||
OBBTreeBuilder::buildCol<DNAMP1::DCLN::Collision::Node>(const ColMesh& mesh);
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,15 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
struct OBBTreeBuilder {
|
|
||||||
using ColMesh = hecl::blender::ColMesh;
|
|
||||||
template <typename Node>
|
|
||||||
static std::unique_ptr<Node> buildCol(const ColMesh& mesh);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,679 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
template <class PAKBRIDGE>
|
|
||||||
void UniqueResult::checkEntry(const PAKBRIDGE& pakBridge, const typename PAKBRIDGE::PAKType::Entry& entry) {
|
|
||||||
UniqueResult::Type resultType = UniqueResult::Type::NotFound;
|
|
||||||
bool foundOneLayer = false;
|
|
||||||
const std::string* levelName = nullptr;
|
|
||||||
typename PAKBRIDGE::PAKType::IDType useLevelId;
|
|
||||||
typename PAKBRIDGE::PAKType::IDType useAreaId;
|
|
||||||
unsigned layerIdx = 0;
|
|
||||||
for (const auto& [levelId, level] : pakBridge.m_levelDeps) {
|
|
||||||
if (entry.id == levelId || level.resources.find(entry.id) != level.resources.end()) {
|
|
||||||
levelName = &level.name;
|
|
||||||
resultType = UniqueResult::Type::Level;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& [areaId, area] : level.areas) {
|
|
||||||
unsigned l = 0;
|
|
||||||
for (const auto& layer : area.layers) {
|
|
||||||
if (layer.resources.find(entry.id) != layer.resources.end()) {
|
|
||||||
if (foundOneLayer) {
|
|
||||||
if (useAreaId == areaId) {
|
|
||||||
resultType = UniqueResult::Type::Area;
|
|
||||||
} else if (useLevelId == levelId) {
|
|
||||||
resultType = UniqueResult::Type::Level;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
m_type = UniqueResult::Type::Pak;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
resultType = UniqueResult::Type::Layer;
|
|
||||||
levelName = &level.name;
|
|
||||||
useLevelId = levelId;
|
|
||||||
useAreaId = areaId;
|
|
||||||
layerIdx = l;
|
|
||||||
foundOneLayer = true;
|
|
||||||
}
|
|
||||||
++l;
|
|
||||||
}
|
|
||||||
if (area.resources.find(entry.id) != area.resources.end()) {
|
|
||||||
if (foundOneLayer) {
|
|
||||||
if (useAreaId == areaId) {
|
|
||||||
resultType = UniqueResult::Type::Area;
|
|
||||||
} else if (useLevelId == levelId) {
|
|
||||||
resultType = UniqueResult::Type::Level;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
m_type = UniqueResult::Type::Pak;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
resultType = UniqueResult::Type::Area;
|
|
||||||
levelName = &level.name;
|
|
||||||
useLevelId = levelId;
|
|
||||||
useAreaId = areaId;
|
|
||||||
foundOneLayer = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_type = resultType;
|
|
||||||
m_levelName = levelName;
|
|
||||||
if (resultType == UniqueResult::Type::Layer || resultType == UniqueResult::Type::Area) {
|
|
||||||
const typename PAKBRIDGE::Level::Area& area = pakBridge.m_levelDeps.at(useLevelId).areas.at(useAreaId);
|
|
||||||
m_areaName = &area.name;
|
|
||||||
if (resultType == UniqueResult::Type::Layer) {
|
|
||||||
const typename PAKBRIDGE::Level::Area::Layer& layer = area.layers[layerIdx];
|
|
||||||
m_layerName = &layer.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template void UniqueResult::checkEntry(const DNAMP1::PAKBridge& pakBridge,
|
|
||||||
const DNAMP1::PAKBridge::PAKType::Entry& entry);
|
|
||||||
template void UniqueResult::checkEntry(const DNAMP2::PAKBridge& pakBridge,
|
|
||||||
const DNAMP2::PAKBridge::PAKType::Entry& entry);
|
|
||||||
template void UniqueResult::checkEntry(const DNAMP3::PAKBridge& pakBridge,
|
|
||||||
const DNAMP3::PAKBridge::PAKType::Entry& entry);
|
|
||||||
|
|
||||||
hecl::ProjectPath UniqueResult::uniquePath(const hecl::ProjectPath& pakPath) const {
|
|
||||||
if (m_type == Type::Pak)
|
|
||||||
return pakPath;
|
|
||||||
|
|
||||||
hecl::ProjectPath levelDir;
|
|
||||||
if (m_levelName)
|
|
||||||
levelDir.assign(pakPath, *m_levelName);
|
|
||||||
else
|
|
||||||
levelDir = pakPath;
|
|
||||||
|
|
||||||
if (m_type == Type::Area) {
|
|
||||||
hecl::ProjectPath areaDir(levelDir, *m_areaName);
|
|
||||||
return areaDir;
|
|
||||||
} else if (m_type == Type::Layer) {
|
|
||||||
hecl::ProjectPath areaDir(levelDir, *m_areaName);
|
|
||||||
hecl::ProjectPath layerDir(areaDir, *m_layerName);
|
|
||||||
return layerDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
return levelDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
void PAKRouter<BRIDGETYPE>::build(std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress) {
|
|
||||||
m_bridges = &bridges;
|
|
||||||
m_bridgePaths.clear();
|
|
||||||
|
|
||||||
m_uniqueEntries.clear();
|
|
||||||
m_sharedEntries.clear();
|
|
||||||
m_charAssoc.m_cmdlRigs.clear();
|
|
||||||
size_t count = 0;
|
|
||||||
float bridgesSz = bridges.size();
|
|
||||||
|
|
||||||
/* Route entries unique/shared per-pak */
|
|
||||||
size_t bridgeIdx = 0;
|
|
||||||
for (BRIDGETYPE& bridge : bridges) {
|
|
||||||
const auto& name = bridge.getName();
|
|
||||||
|
|
||||||
std::string_view::const_iterator extit = name.end() - 4;
|
|
||||||
std::string baseName(name.begin(), extit);
|
|
||||||
|
|
||||||
m_bridgePaths.emplace_back(
|
|
||||||
std::make_pair(hecl::ProjectPath(m_gameWorking, baseName), hecl::ProjectPath(m_gameCooked, baseName)));
|
|
||||||
|
|
||||||
/* Index this PAK */
|
|
||||||
bridge.build();
|
|
||||||
|
|
||||||
/* Add to global entry lookup */
|
|
||||||
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
|
|
||||||
for (const auto& entry : pak.m_entries) {
|
|
||||||
if (!pak.m_noShare) {
|
|
||||||
auto sSearch = m_sharedEntries.find(entry.first);
|
|
||||||
if (sSearch != m_sharedEntries.end())
|
|
||||||
continue;
|
|
||||||
auto uSearch = m_uniqueEntries.find(entry.first);
|
|
||||||
if (uSearch != m_uniqueEntries.end()) {
|
|
||||||
m_uniqueEntries.erase(uSearch);
|
|
||||||
m_sharedEntries[entry.first] = std::make_pair(bridgeIdx, &entry.second);
|
|
||||||
} else
|
|
||||||
m_uniqueEntries[entry.first] = std::make_pair(bridgeIdx, &entry.second);
|
|
||||||
} else
|
|
||||||
m_uniqueEntries[entry.first] = std::make_pair(bridgeIdx, &entry.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add RigPairs to global map */
|
|
||||||
bridge.addCMDLRigPairs(*this, m_charAssoc);
|
|
||||||
|
|
||||||
progress(++count / bridgesSz);
|
|
||||||
++bridgeIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add named resources to catalog YAML files */
|
|
||||||
for (BRIDGETYPE& bridge : bridges) {
|
|
||||||
athena::io::YAMLDocWriter catalogWriter;
|
|
||||||
|
|
||||||
enterPAKBridge(bridge);
|
|
||||||
|
|
||||||
/* Add MAPA transforms to global map */
|
|
||||||
bridge.addMAPATransforms(*this, m_mapaTransforms, m_overrideEntries);
|
|
||||||
|
|
||||||
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
|
|
||||||
for (const auto& namedEntry : pak.m_nameEntries) {
|
|
||||||
if (namedEntry.name == "holo_cinf")
|
|
||||||
continue; /* Problematic corner case */
|
|
||||||
if (auto rec = catalogWriter.enterSubRecord(namedEntry.name)) {
|
|
||||||
hecl::ProjectPath working = getWorking(namedEntry.id);
|
|
||||||
if (working.getAuxInfo().size()) {
|
|
||||||
if (auto v = catalogWriter.enterSubVector()) {
|
|
||||||
catalogWriter.writeString(working.getRelativePath());
|
|
||||||
catalogWriter.writeString(working.getAuxInfo());
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
catalogWriter.writeString(working.getRelativePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write catalog */
|
|
||||||
intptr_t curBridgeIdx = reinterpret_cast<intptr_t>(m_curBridgeIdx.get());
|
|
||||||
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].first;
|
|
||||||
pakPath.makeDirChain(true);
|
|
||||||
athena::io::FileWriter writer(hecl::ProjectPath(pakPath, "!catalog.yaml").getAbsolutePath());
|
|
||||||
catalogWriter.finish(&writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
void PAKRouter<BRIDGETYPE>::enterPAKBridge(const BRIDGETYPE& pakBridge) {
|
|
||||||
g_PakRouter.reset(this);
|
|
||||||
auto pit = m_bridgePaths.begin();
|
|
||||||
size_t bridgeIdx = 0;
|
|
||||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
|
||||||
if (&bridge == &pakBridge) {
|
|
||||||
m_pak.reset(&pakBridge.getPAK());
|
|
||||||
m_node.reset(&pakBridge.getNode());
|
|
||||||
m_curBridgeIdx.reset(reinterpret_cast<void*>(bridgeIdx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
++pit;
|
|
||||||
++bridgeIdx;
|
|
||||||
}
|
|
||||||
LogDNACommon.report(logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKBridge provided to PAKRouter::enterPAKBridge() was not part of build()"));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCharacterWorking(const EntryType* entry) const {
|
|
||||||
auto characterSearch = m_charAssoc.m_cskrToCharacter.find(entry->id);
|
|
||||||
if (characterSearch != m_charAssoc.m_cskrToCharacter.cend()) {
|
|
||||||
hecl::ProjectPath characterPath = getWorking(characterSearch->second.first);
|
|
||||||
if (entry->type == FOURCC('EVNT')) {
|
|
||||||
std::string extension(characterSearch->second.second);
|
|
||||||
return characterPath.getWithExtension((std::string(".") + extension.c_str()).c_str(), true);
|
|
||||||
}
|
|
||||||
return characterPath.ensureAuxInfo(characterSearch->second.second);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const EntryType* entry,
|
|
||||||
const ResExtractor<BRIDGETYPE>& extractor) const {
|
|
||||||
if (!entry)
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
|
|
||||||
auto overrideSearch = m_overrideEntries.find(entry->id);
|
|
||||||
if (overrideSearch != m_overrideEntries.end())
|
|
||||||
return overrideSearch->second;
|
|
||||||
|
|
||||||
const PAKType* pak = m_pak.get();
|
|
||||||
intptr_t curBridgeIdx = reinterpret_cast<intptr_t>(m_curBridgeIdx.get());
|
|
||||||
if (pak && pak->m_noShare) {
|
|
||||||
const EntryType* singleSearch = pak->lookupEntry(entry->id);
|
|
||||||
if (singleSearch) {
|
|
||||||
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].first;
|
|
||||||
std::string entName = getBestEntryName(*entry);
|
|
||||||
std::string auxInfo;
|
|
||||||
if (extractor.fileExts[0] && !extractor.fileExts[1])
|
|
||||||
entName += extractor.fileExts[0];
|
|
||||||
else if (extractor.fileExts[0])
|
|
||||||
entName += ".*";
|
|
||||||
else if (hecl::ProjectPath chWork = getCharacterWorking(entry))
|
|
||||||
return chWork;
|
|
||||||
return hecl::ProjectPath(pakPath, entName).ensureAuxInfo(auxInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto uniqueSearch = m_uniqueEntries.find(entry->id);
|
|
||||||
if (uniqueSearch != m_uniqueEntries.end()) {
|
|
||||||
const BRIDGETYPE& bridge = m_bridges->at(uniqueSearch->second.first);
|
|
||||||
const hecl::ProjectPath& pakPath = m_bridgePaths[uniqueSearch->second.first].first;
|
|
||||||
std::string entName = getBestEntryName(*entry);
|
|
||||||
std::string auxInfo;
|
|
||||||
if (extractor.fileExts[0] && !extractor.fileExts[1])
|
|
||||||
entName += extractor.fileExts[0];
|
|
||||||
else if (extractor.fileExts[0])
|
|
||||||
entName += ".*";
|
|
||||||
else if (hecl::ProjectPath chWork = getCharacterWorking(entry))
|
|
||||||
return chWork;
|
|
||||||
if (bridge.getPAK().m_noShare) {
|
|
||||||
return hecl::ProjectPath(pakPath, entName).ensureAuxInfo(auxInfo);
|
|
||||||
} else {
|
|
||||||
hecl::ProjectPath uniquePath = entry->unique.uniquePath(pakPath);
|
|
||||||
return hecl::ProjectPath(uniquePath, entName).ensureAuxInfo(auxInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sharedSearch = m_sharedEntries.find(entry->id);
|
|
||||||
if (sharedSearch != m_sharedEntries.end()) {
|
|
||||||
std::string entBase = getBestEntryName(*entry);
|
|
||||||
std::string auxInfo;
|
|
||||||
std::string entName = entBase;
|
|
||||||
if (extractor.fileExts[0] && !extractor.fileExts[1])
|
|
||||||
entName += extractor.fileExts[0];
|
|
||||||
else if (extractor.fileExts[0])
|
|
||||||
entName += ".*";
|
|
||||||
else if (hecl::ProjectPath chWork = getCharacterWorking(entry))
|
|
||||||
return chWork;
|
|
||||||
hecl::ProjectPath sharedPath(m_sharedWorking, entName);
|
|
||||||
return sharedPath.ensureAuxInfo(auxInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("Unable to find entry {}"), entry->id);
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const EntryType* entry) const {
|
|
||||||
if (!entry)
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
return getWorking(entry, BRIDGETYPE::LookupExtractor(*m_node.get(), *m_pak.get(), *entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const IDType& id, bool silenceWarnings) const {
|
|
||||||
return getWorking(lookupEntry(id, nullptr, silenceWarnings, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCooked(const EntryType* entry) const {
|
|
||||||
if (!entry)
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
|
|
||||||
auto overrideSearch = m_overrideEntries.find(entry->id);
|
|
||||||
if (overrideSearch != m_overrideEntries.end()) {
|
|
||||||
return overrideSearch->second.getCookedPath(
|
|
||||||
*m_dataSpec.overrideDataSpec(overrideSearch->second, m_dataSpec.getDataSpecEntry()));
|
|
||||||
}
|
|
||||||
|
|
||||||
const PAKType* pak = m_pak.get();
|
|
||||||
intptr_t curBridgeIdx = reinterpret_cast<intptr_t>(m_curBridgeIdx.get());
|
|
||||||
if (pak && pak->m_noShare) {
|
|
||||||
const EntryType* singleSearch = pak->lookupEntry(entry->id);
|
|
||||||
if (singleSearch) {
|
|
||||||
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].second;
|
|
||||||
return hecl::ProjectPath(pakPath, getBestEntryName(*entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto uniqueSearch = m_uniqueEntries.find(entry->id);
|
|
||||||
if (uniqueSearch != m_uniqueEntries.end()) {
|
|
||||||
const BRIDGETYPE& bridge = m_bridges->at(uniqueSearch->second.first);
|
|
||||||
const hecl::ProjectPath& pakPath = m_bridgePaths[uniqueSearch->second.first].second;
|
|
||||||
if (bridge.getPAK().m_noShare) {
|
|
||||||
return hecl::ProjectPath(pakPath, getBestEntryName(*entry));
|
|
||||||
} else {
|
|
||||||
hecl::ProjectPath uniquePath = entry->unique.uniquePath(pakPath);
|
|
||||||
return hecl::ProjectPath(uniquePath, getBestEntryName(*entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto sharedSearch = m_sharedEntries.find(entry->id);
|
|
||||||
if (sharedSearch != m_sharedEntries.end()) {
|
|
||||||
return hecl::ProjectPath(m_sharedCooked, getBestEntryName(*entry));
|
|
||||||
}
|
|
||||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("Unable to find entry {}"), entry->id);
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCooked(const IDType& id, bool silenceWarnings) const {
|
|
||||||
return getCooked(lookupEntry(id, nullptr, silenceWarnings, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
std::string PAKRouter<BRIDGETYPE>::getResourceRelativePath(const EntryType& a, const IDType& b) const {
|
|
||||||
const nod::Node* node = m_node.get();
|
|
||||||
const PAKType* pak = m_pak.get();
|
|
||||||
if (!pak)
|
|
||||||
LogDNACommon.report(
|
|
||||||
logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKRouter::enterPAKBridge() must be called before PAKRouter::getResourceRelativePath()"));
|
|
||||||
const typename BRIDGETYPE::PAKType::Entry* be = lookupEntry(b);
|
|
||||||
if (!be)
|
|
||||||
return std::string();
|
|
||||||
hecl::ProjectPath aPath = getWorking(&a, BRIDGETYPE::LookupExtractor(*node, *pak, a));
|
|
||||||
std::string ret;
|
|
||||||
for (size_t i = 0; i < aPath.levelCount(); ++i)
|
|
||||||
ret += "../";
|
|
||||||
hecl::ProjectPath bPath = getWorking(be, BRIDGETYPE::LookupExtractor(*node, *pak, *be));
|
|
||||||
ret += bPath.getRelativePath();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
std::string PAKRouter<BRIDGETYPE>::getBestEntryName(const EntryType& entry, bool stdOverride) const {
|
|
||||||
std::string name;
|
|
||||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
|
||||||
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
|
|
||||||
const typename BRIDGETYPE::PAKType::Entry* e = pak.lookupEntry(entry.id);
|
|
||||||
if (!e)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (stdOverride && !pak.m_noShare) {
|
|
||||||
if (entry.type == FOURCC('MLVL'))
|
|
||||||
return fmt::format(FMT_STRING("!world_{}"), entry.id);
|
|
||||||
else if (entry.type == FOURCC('MREA'))
|
|
||||||
return fmt::format(FMT_STRING("!area_{}"), entry.id);
|
|
||||||
else if (entry.type == FOURCC('MAPA'))
|
|
||||||
return fmt::format(FMT_STRING("!map_{}"), entry.id);
|
|
||||||
else if (entry.type == FOURCC('PATH'))
|
|
||||||
return fmt::format(FMT_STRING("!path_{}"), entry.id);
|
|
||||||
else if (entry.type == FOURCC('MAPW'))
|
|
||||||
return fmt::format(FMT_STRING("!mapw_{}"), entry.id);
|
|
||||||
else if (entry.type == FOURCC('SAVW'))
|
|
||||||
return fmt::format(FMT_STRING("!savw_{}"), entry.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string catalogueName;
|
|
||||||
name = pak.bestEntryName(bridge.getNode(), entry, catalogueName);
|
|
||||||
if (!catalogueName.empty())
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
std::string PAKRouter<BRIDGETYPE>::getBestEntryName(const IDType& entry, bool stdOverride) const {
|
|
||||||
std::string name;
|
|
||||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
|
||||||
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
|
|
||||||
const typename BRIDGETYPE::PAKType::Entry* e = pak.lookupEntry(entry);
|
|
||||||
if (!e)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (stdOverride && !pak.m_noShare) {
|
|
||||||
if (e->type == FOURCC('MLVL'))
|
|
||||||
return fmt::format(FMT_STRING("!world_{}"), e->id);
|
|
||||||
else if (e->type == FOURCC('MREA'))
|
|
||||||
return fmt::format(FMT_STRING("!area_{}"), e->id);
|
|
||||||
else if (e->type == FOURCC('MAPA'))
|
|
||||||
return fmt::format(FMT_STRING("!map_{}"), e->id);
|
|
||||||
else if (e->type == FOURCC('PATH'))
|
|
||||||
return fmt::format(FMT_STRING("!path_{}"), e->id);
|
|
||||||
else if (e->type == FOURCC('MAPW'))
|
|
||||||
return fmt::format(FMT_STRING("!mapw_{}"), e->id);
|
|
||||||
else if (e->type == FOURCC('SAVW'))
|
|
||||||
return fmt::format(FMT_STRING("!savw_{}"), e->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string catalogueName;
|
|
||||||
name = pak.bestEntryName(bridge.getNode(), *e, catalogueName);
|
|
||||||
if (!catalogueName.empty())
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
bool PAKRouter<BRIDGETYPE>::extractResources(const BRIDGETYPE& pakBridge, bool force, hecl::blender::Token& btok,
|
|
||||||
std::function<void(const char*, float)> progress) {
|
|
||||||
enterPAKBridge(pakBridge);
|
|
||||||
size_t count = 0;
|
|
||||||
size_t sz = m_pak->m_entries.size();
|
|
||||||
float fsz = sz;
|
|
||||||
for (unsigned w = 0; count < sz; ++w) {
|
|
||||||
for (const auto& item : m_pak->m_firstEntries) {
|
|
||||||
const auto* entryPtr = m_pak->lookupEntry(item);
|
|
||||||
ResExtractor<BRIDGETYPE> extractor = BRIDGETYPE::LookupExtractor(*m_node.get(), *m_pak.get(), *entryPtr);
|
|
||||||
if (extractor.weight != w)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::string bestName = getBestEntryName(*entryPtr, false);
|
|
||||||
float thisFac = ++count / fsz;
|
|
||||||
progress(bestName.c_str(), thisFac);
|
|
||||||
|
|
||||||
const nod::Node* node = m_node.get();
|
|
||||||
|
|
||||||
hecl::ProjectPath working = getWorking(entryPtr, extractor);
|
|
||||||
working.makeDirChain(false);
|
|
||||||
hecl::ResourceLock resLk(working);
|
|
||||||
if (!resLk)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Extract to unmodified directory */
|
|
||||||
hecl::ProjectPath cooked = working.getCookedPath(m_dataSpec.getUnmodifiedSpec());
|
|
||||||
if (force || cooked.isNone()) {
|
|
||||||
cooked.makeDirChain(false);
|
|
||||||
PAKEntryReadStream s = entryPtr->beginReadStream(*node);
|
|
||||||
const auto fout = hecl::FopenUnique(cooked.getAbsolutePath().data(), "wb");
|
|
||||||
std::fwrite(s.data(), 1, s.length(), fout.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extractor.func_a) /* Doesn't need PAKRouter access */
|
|
||||||
{
|
|
||||||
if (force || !extractor.IsFullyExtracted(working)) {
|
|
||||||
PAKEntryReadStream s = entryPtr->beginReadStream(*node);
|
|
||||||
extractor.func_a(s, working);
|
|
||||||
}
|
|
||||||
} else if (extractor.func_b) /* Needs PAKRouter access */
|
|
||||||
{
|
|
||||||
if (force || !extractor.IsFullyExtracted(working)) {
|
|
||||||
PAKEntryReadStream s = entryPtr->beginReadStream(*node);
|
|
||||||
extractor.func_b(m_dataSpec, s, working, *this, *entryPtr, force, btok,
|
|
||||||
[&progress, thisFac](const char* update) { progress(update, thisFac); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
const typename BRIDGETYPE::PAKType::Entry*
|
|
||||||
PAKRouter<BRIDGETYPE>::lookupEntry(const IDType& entry, const nod::Node** nodeOut, bool silenceWarnings,
|
|
||||||
bool currentPAK) const {
|
|
||||||
if (!entry.isValid())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (!m_bridges)
|
|
||||||
LogDNACommon.report(logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKRouter::build() must be called before PAKRouter::lookupEntry()"));
|
|
||||||
|
|
||||||
const PAKType* pak = m_pak.get();
|
|
||||||
const nod::Node* node = m_node.get();
|
|
||||||
if (pak) {
|
|
||||||
const EntryType* ent = pak->lookupEntry(entry);
|
|
||||||
if (ent) {
|
|
||||||
if (nodeOut)
|
|
||||||
*nodeOut = node;
|
|
||||||
return ent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPAK) {
|
|
||||||
#ifndef NDEBUG
|
|
||||||
if (!silenceWarnings)
|
|
||||||
LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to find PAK entry {} in current PAK"), entry);
|
|
||||||
#endif
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
|
||||||
const PAKType& pak = bridge.getPAK();
|
|
||||||
const EntryType* ent = pak.lookupEntry(entry);
|
|
||||||
if (ent) {
|
|
||||||
if (nodeOut)
|
|
||||||
*nodeOut = &bridge.getNode();
|
|
||||||
return ent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
if (!silenceWarnings)
|
|
||||||
LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to find PAK entry {}"), entry);
|
|
||||||
#endif
|
|
||||||
if (nodeOut)
|
|
||||||
*nodeOut = nullptr;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
const typename CharacterAssociations<typename PAKRouter<BRIDGETYPE>::IDType>::RigPair*
|
|
||||||
PAKRouter<BRIDGETYPE>::lookupCMDLRigPair(const IDType& id) const {
|
|
||||||
auto search = m_charAssoc.m_cmdlRigs.find(id);
|
|
||||||
if (search == m_charAssoc.m_cmdlRigs.end())
|
|
||||||
return nullptr;
|
|
||||||
return &search->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
const typename CharacterAssociations<typename PAKRouter<BRIDGETYPE>::IDType>::MultimapIteratorPair
|
|
||||||
PAKRouter<BRIDGETYPE>::lookupCharacterAttachmentRigs(const IDType& id) const {
|
|
||||||
return m_charAssoc.m_characterToAttachmentRigs.equal_range(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
const zeus::CMatrix4f* PAKRouter<BRIDGETYPE>::lookupMAPATransform(const IDType& id) const {
|
|
||||||
auto search = m_mapaTransforms.find(id);
|
|
||||||
if (search == m_mapaTransforms.end())
|
|
||||||
return nullptr;
|
|
||||||
return &search->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerWorking(const IDType& areaId, int layerIdx) const {
|
|
||||||
if (!m_bridges)
|
|
||||||
LogDNACommon.report(logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKRouter::build() must be called before PAKRouter::getAreaLayerWorking()"));
|
|
||||||
auto bridgePathIt = m_bridgePaths.cbegin();
|
|
||||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
|
||||||
for (const auto& level : bridge.m_levelDeps)
|
|
||||||
for (const auto& area : level.second.areas)
|
|
||||||
if (area.first == areaId) {
|
|
||||||
hecl::ProjectPath levelPath(bridgePathIt->first, level.second.name);
|
|
||||||
hecl::ProjectPath areaPath(levelPath, area.second.name);
|
|
||||||
if (layerIdx < 0)
|
|
||||||
return areaPath;
|
|
||||||
return hecl::ProjectPath(areaPath, area.second.layers.at(layerIdx).name);
|
|
||||||
}
|
|
||||||
++bridgePathIt;
|
|
||||||
}
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerWorking(const IDType& areaId, int layerIdx,
|
|
||||||
bool& activeOut) const {
|
|
||||||
activeOut = false;
|
|
||||||
if (!m_bridges)
|
|
||||||
LogDNACommon.report(logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKRouter::build() must be called before PAKRouter::getAreaLayerWorking()"));
|
|
||||||
auto bridgePathIt = m_bridgePaths.cbegin();
|
|
||||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
|
||||||
for (const auto& level : bridge.m_levelDeps)
|
|
||||||
for (const auto& area : level.second.areas)
|
|
||||||
if (area.first == areaId) {
|
|
||||||
hecl::ProjectPath levelPath(bridgePathIt->first, level.second.name);
|
|
||||||
hecl::ProjectPath areaPath(levelPath, area.second.name);
|
|
||||||
if (layerIdx < 0)
|
|
||||||
return areaPath;
|
|
||||||
const typename Level<IDType>::Area::Layer& layer = area.second.layers.at(layerIdx);
|
|
||||||
activeOut = layer.active;
|
|
||||||
return hecl::ProjectPath(areaPath, layer.name);
|
|
||||||
}
|
|
||||||
++bridgePathIt;
|
|
||||||
}
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerCooked(const IDType& areaId, int layerIdx) const {
|
|
||||||
if (!m_bridges)
|
|
||||||
LogDNACommon.report(logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKRouter::build() must be called before PAKRouter::getAreaLayerCooked()"));
|
|
||||||
auto bridgePathIt = m_bridgePaths.cbegin();
|
|
||||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
|
||||||
for (const auto& level : bridge.m_levelDeps)
|
|
||||||
for (const auto& area : level.second.areas)
|
|
||||||
if (area.first == areaId) {
|
|
||||||
hecl::ProjectPath levelPath(bridgePathIt->second, level.second.name);
|
|
||||||
hecl::ProjectPath areaPath(levelPath, area.second.name);
|
|
||||||
if (layerIdx < 0)
|
|
||||||
return areaPath;
|
|
||||||
return hecl::ProjectPath(areaPath, area.second.layers.at(layerIdx).name);
|
|
||||||
}
|
|
||||||
++bridgePathIt;
|
|
||||||
}
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerCooked(const IDType& areaId, int layerIdx, bool& activeOut) const {
|
|
||||||
activeOut = false;
|
|
||||||
if (!m_bridges)
|
|
||||||
LogDNACommon.report(logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKRouter::build() must be called before PAKRouter::getAreaLayerCooked()"));
|
|
||||||
auto bridgePathIt = m_bridgePaths.cbegin();
|
|
||||||
for (const BRIDGETYPE& bridge : *m_bridges) {
|
|
||||||
for (const auto& level : bridge.m_levelDeps)
|
|
||||||
for (const auto& area : level.second.areas)
|
|
||||||
if (area.first == areaId) {
|
|
||||||
hecl::ProjectPath levelPath(bridgePathIt->second, level.second.name);
|
|
||||||
hecl::ProjectPath areaPath(levelPath, area.second.name);
|
|
||||||
if (layerIdx < 0)
|
|
||||||
return areaPath;
|
|
||||||
const typename Level<IDType>::Area::Layer& layer = area.second.layers.at(layerIdx);
|
|
||||||
activeOut = layer.active;
|
|
||||||
return hecl::ProjectPath(areaPath, layer.name);
|
|
||||||
}
|
|
||||||
++bridgePathIt;
|
|
||||||
}
|
|
||||||
return hecl::ProjectPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
void PAKRouter<BRIDGETYPE>::enumerateResources(const std::function<bool(const EntryType*)>& func) {
|
|
||||||
if (!m_bridges)
|
|
||||||
LogDNACommon.report(logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKRouter::build() must be called before PAKRouter::enumerateResources()"));
|
|
||||||
for (const auto& entryPair : m_uniqueEntries)
|
|
||||||
if (!func(entryPair.second.second))
|
|
||||||
return;
|
|
||||||
for (const auto& entryPair : m_sharedEntries)
|
|
||||||
if (!func(entryPair.second.second))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
bool PAKRouter<BRIDGETYPE>::mreaHasDupeResources(const IDType& id) const {
|
|
||||||
const PAKType* pak = m_pak.get();
|
|
||||||
if (!pak)
|
|
||||||
LogDNACommon.report(
|
|
||||||
logvisor::Fatal,
|
|
||||||
FMT_STRING("PAKRouter::enterPAKBridge() must be called before PAKRouter::mreaHasDupeResources()"));
|
|
||||||
return pak->mreaHasDupeResources(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
template class PAKRouter<DNAMP1::PAKBridge>;
|
|
||||||
template class PAKRouter<DNAMP2::PAKBridge>;
|
|
||||||
template class PAKRouter<DNAMP3::PAKBridge>;
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,234 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstring>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
#include <boo/ThreadLocalPtr.hpp>
|
|
||||||
#include <zeus/CMatrix4f.hpp>
|
|
||||||
#include <zeus/Global.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
/** PAK entry stream reader */
|
|
||||||
class PAKEntryReadStream : public athena::io::IStreamReader {
|
|
||||||
std::unique_ptr<atUint8[]> m_buf;
|
|
||||||
atUint64 m_sz = 0;
|
|
||||||
atUint64 m_pos = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
PAKEntryReadStream() = default;
|
|
||||||
explicit operator bool() const { return m_buf.operator bool(); }
|
|
||||||
PAKEntryReadStream(const PAKEntryReadStream& other) = delete;
|
|
||||||
PAKEntryReadStream(PAKEntryReadStream&& other) = default;
|
|
||||||
PAKEntryReadStream& operator=(const PAKEntryReadStream& other) = delete;
|
|
||||||
PAKEntryReadStream& operator=(PAKEntryReadStream&& other) = default;
|
|
||||||
PAKEntryReadStream(std::unique_ptr<atUint8[]>&& buf, atUint64 sz, atUint64 pos)
|
|
||||||
: m_buf(std::move(buf)), m_sz(sz), m_pos(pos) {
|
|
||||||
if (m_pos >= m_sz)
|
|
||||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAK stream cursor overrun"));
|
|
||||||
}
|
|
||||||
void seek(atInt64 pos, athena::SeekOrigin origin) override {
|
|
||||||
if (origin == athena::SeekOrigin::Begin) {
|
|
||||||
m_pos = pos;
|
|
||||||
} else if (origin == athena::SeekOrigin::Current) {
|
|
||||||
m_pos += pos;
|
|
||||||
} else if (origin == athena::SeekOrigin::End) {
|
|
||||||
m_pos = m_sz + pos;
|
|
||||||
}
|
|
||||||
if (m_pos > m_sz) {
|
|
||||||
LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAK stream cursor overrun"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
atUint64 position() const override { return m_pos; }
|
|
||||||
atUint64 length() const override { return m_sz; }
|
|
||||||
const atUint8* data() const { return m_buf.get(); }
|
|
||||||
atUint64 readUBytesToBuf(void* buf, atUint64 len) override {
|
|
||||||
atUint64 bufEnd = m_pos + len;
|
|
||||||
if (bufEnd > m_sz)
|
|
||||||
len -= bufEnd - m_sz;
|
|
||||||
memmove(buf, m_buf.get() + m_pos, len);
|
|
||||||
m_pos += len;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UniqueResult {
|
|
||||||
enum class Type { NotFound, Pak, Level, Area, Layer } m_type = Type::NotFound;
|
|
||||||
const std::string* m_levelName = nullptr;
|
|
||||||
const std::string* m_areaName = nullptr;
|
|
||||||
const std::string* m_layerName = nullptr;
|
|
||||||
UniqueResult() = default;
|
|
||||||
UniqueResult(Type tp) : m_type(tp) {}
|
|
||||||
|
|
||||||
template <class PAKBRIDGE>
|
|
||||||
void checkEntry(const PAKBRIDGE& pakBridge, const typename PAKBRIDGE::PAKType::Entry& entry);
|
|
||||||
|
|
||||||
hecl::ProjectPath uniquePath(const hecl::ProjectPath& pakPath) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
class PAKRouter;
|
|
||||||
|
|
||||||
/** Resource extractor type */
|
|
||||||
template <class PAKBRIDGE>
|
|
||||||
struct ResExtractor {
|
|
||||||
std::function<bool(PAKEntryReadStream&, const hecl::ProjectPath&)> func_a;
|
|
||||||
std::function<bool(const SpecBase&, PAKEntryReadStream&, const hecl::ProjectPath&, PAKRouter<PAKBRIDGE>&,
|
|
||||||
const typename PAKBRIDGE::PAKType::Entry&, bool, hecl::blender::Token&,
|
|
||||||
std::function<void(const char*)>)>
|
|
||||||
func_b;
|
|
||||||
std::array<const char*, 6> fileExts = {};
|
|
||||||
unsigned weight = 0;
|
|
||||||
std::function<void(const SpecBase&, PAKEntryReadStream&, PAKRouter<PAKBRIDGE>&, typename PAKBRIDGE::PAKType::Entry&)>
|
|
||||||
func_name;
|
|
||||||
|
|
||||||
ResExtractor() = default;
|
|
||||||
|
|
||||||
ResExtractor(std::function<bool(PAKEntryReadStream&, const hecl::ProjectPath&)> func,
|
|
||||||
std::array<const char*, 6>&& fileExtsIn, unsigned weightin = 0,
|
|
||||||
std::function<void(const SpecBase&, PAKEntryReadStream&, PAKRouter<PAKBRIDGE>&,
|
|
||||||
typename PAKBRIDGE::PAKType::Entry&)>
|
|
||||||
nfunc = {})
|
|
||||||
: func_a(std::move(func)), fileExts(std::move(fileExtsIn)), weight(weightin), func_name(std::move(nfunc)) {}
|
|
||||||
|
|
||||||
ResExtractor(std::function<bool(const SpecBase&, PAKEntryReadStream&, const hecl::ProjectPath&, PAKRouter<PAKBRIDGE>&,
|
|
||||||
const typename PAKBRIDGE::PAKType::Entry&, bool, hecl::blender::Token&,
|
|
||||||
std::function<void(const char*)>)>
|
|
||||||
func,
|
|
||||||
std::array<const char*, 6>&& fileExtsIn, unsigned weightin = 0,
|
|
||||||
std::function<void(const SpecBase&, PAKEntryReadStream&, PAKRouter<PAKBRIDGE>&,
|
|
||||||
typename PAKBRIDGE::PAKType::Entry&)>
|
|
||||||
nfunc = {})
|
|
||||||
: func_b(std::move(func)), fileExts(std::move(fileExtsIn)), weight(weightin), func_name(std::move(nfunc)) {}
|
|
||||||
|
|
||||||
bool IsFullyExtracted(const hecl::ProjectPath& path) const {
|
|
||||||
hecl::ProjectPath::Type tp = path.getPathType();
|
|
||||||
if (tp == hecl::ProjectPath::Type::None)
|
|
||||||
return false;
|
|
||||||
else if (tp == hecl::ProjectPath::Type::Glob) {
|
|
||||||
for (int i = 0; i < 6; ++i) {
|
|
||||||
if (!fileExts[i])
|
|
||||||
break;
|
|
||||||
hecl::ProjectPath withExt = path.getWithExtension(fileExts[i], true);
|
|
||||||
if (withExt.isNone())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Level hierarchy representation */
|
|
||||||
template <class IDType>
|
|
||||||
struct Level {
|
|
||||||
std::string name;
|
|
||||||
struct Area {
|
|
||||||
std::string name;
|
|
||||||
struct Layer {
|
|
||||||
std::string name;
|
|
||||||
bool active;
|
|
||||||
std::unordered_set<IDType> resources;
|
|
||||||
};
|
|
||||||
std::vector<Layer> layers;
|
|
||||||
std::unordered_set<IDType> resources;
|
|
||||||
};
|
|
||||||
std::unordered_map<IDType, Area> areas;
|
|
||||||
std::unordered_set<IDType> resources;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** PAKRouter (for detecting shared entry locations) */
|
|
||||||
template <class BRIDGETYPE>
|
|
||||||
class PAKRouter : public PAKRouterBase {
|
|
||||||
public:
|
|
||||||
using PAKType = typename BRIDGETYPE::PAKType;
|
|
||||||
using IDType = typename PAKType::IDType;
|
|
||||||
using EntryType = typename PAKType::Entry;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::vector<BRIDGETYPE>* m_bridges = nullptr;
|
|
||||||
std::vector<std::pair<hecl::ProjectPath, hecl::ProjectPath>> m_bridgePaths;
|
|
||||||
ThreadLocalPtr<void> m_curBridgeIdx;
|
|
||||||
const hecl::ProjectPath& m_gameWorking;
|
|
||||||
const hecl::ProjectPath& m_gameCooked;
|
|
||||||
hecl::ProjectPath m_sharedWorking;
|
|
||||||
hecl::ProjectPath m_sharedCooked;
|
|
||||||
ThreadLocalPtr<const PAKType> m_pak;
|
|
||||||
ThreadLocalPtr<const nod::Node> m_node;
|
|
||||||
std::unordered_map<IDType, std::pair<size_t, const EntryType*>> m_uniqueEntries;
|
|
||||||
std::unordered_map<IDType, std::pair<size_t, const EntryType*>> m_sharedEntries;
|
|
||||||
std::unordered_map<IDType, hecl::ProjectPath> m_overrideEntries;
|
|
||||||
CharacterAssociations<IDType> m_charAssoc;
|
|
||||||
std::unordered_map<IDType, zeus::CMatrix4f> m_mapaTransforms;
|
|
||||||
|
|
||||||
hecl::ProjectPath getCharacterWorking(const EntryType* entry) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
PAKRouter(const SpecBase& dataSpec, const hecl::ProjectPath& working, const hecl::ProjectPath& cooked)
|
|
||||||
: PAKRouterBase(dataSpec)
|
|
||||||
, m_gameWorking(working)
|
|
||||||
, m_gameCooked(cooked)
|
|
||||||
, m_sharedWorking(working, "Shared")
|
|
||||||
, m_sharedCooked(cooked, "Shared") {}
|
|
||||||
|
|
||||||
void build(std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress);
|
|
||||||
|
|
||||||
void enterPAKBridge(const BRIDGETYPE& pakBridge);
|
|
||||||
const BRIDGETYPE& getCurrentBridge() const { return (*m_bridges)[reinterpret_cast<intptr_t>(m_curBridgeIdx.get())]; }
|
|
||||||
|
|
||||||
using PAKRouterBase::getWorking;
|
|
||||||
hecl::ProjectPath getWorking(const EntryType* entry, const ResExtractor<BRIDGETYPE>& extractor) const;
|
|
||||||
hecl::ProjectPath getWorking(const EntryType* entry) const;
|
|
||||||
hecl::ProjectPath getWorking(const IDType& id, bool silenceWarnings = false) const override;
|
|
||||||
hecl::ProjectPath getCooked(const EntryType* entry) const;
|
|
||||||
hecl::ProjectPath getCooked(const IDType& id, bool silenceWarnings = false) const;
|
|
||||||
|
|
||||||
std::string getResourceRelativePath(const EntryType& a, const IDType& b) const;
|
|
||||||
|
|
||||||
std::string getBestEntryName(const EntryType& entry, bool stdOverride = true) const;
|
|
||||||
std::string getBestEntryName(const IDType& entry, bool stdOverride = true) const;
|
|
||||||
|
|
||||||
bool extractResources(const BRIDGETYPE& pakBridge, bool force, hecl::blender::Token& btok,
|
|
||||||
std::function<void(const char*, float)> progress);
|
|
||||||
|
|
||||||
const typename BRIDGETYPE::PAKType::Entry* lookupEntry(const IDType& entry, const nod::Node** nodeOut = nullptr,
|
|
||||||
bool silenceWarnings = false, bool currentPAK = false) const;
|
|
||||||
|
|
||||||
template <typename DNA>
|
|
||||||
bool lookupAndReadDNA(const IDType& id, DNA& out, bool silenceWarnings = false) {
|
|
||||||
const nod::Node* node;
|
|
||||||
const EntryType* entry = lookupEntry(id, &node, silenceWarnings);
|
|
||||||
if (!entry)
|
|
||||||
return false;
|
|
||||||
PAKEntryReadStream rs = entry->beginReadStream(*node);
|
|
||||||
out.read(rs);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
PAKEntryReadStream beginReadStreamForId(const IDType& id, bool silenceWarnings = false) {
|
|
||||||
const nod::Node* node;
|
|
||||||
const EntryType* entry = lookupEntry(id, &node, silenceWarnings);
|
|
||||||
return entry->beginReadStream(*node);
|
|
||||||
}
|
|
||||||
|
|
||||||
const typename CharacterAssociations<IDType>::RigPair* lookupCMDLRigPair(const IDType& id) const;
|
|
||||||
const typename CharacterAssociations<IDType>::MultimapIteratorPair
|
|
||||||
lookupCharacterAttachmentRigs(const IDType& id) const;
|
|
||||||
const zeus::CMatrix4f* lookupMAPATransform(const IDType& mapaId) const;
|
|
||||||
|
|
||||||
hecl::ProjectPath getAreaLayerWorking(const IDType& areaId, int layerIdx) const;
|
|
||||||
hecl::ProjectPath getAreaLayerWorking(const IDType& areaId, int layerIdx, bool& activeOut) const;
|
|
||||||
hecl::ProjectPath getAreaLayerCooked(const IDType& areaId, int layerIdx) const;
|
|
||||||
hecl::ProjectPath getAreaLayerCooked(const IDType& areaId, int layerIdx, bool& activeOut) const;
|
|
||||||
|
|
||||||
void enumerateResources(const std::function<bool(const EntryType*)>& func);
|
|
||||||
|
|
||||||
bool mreaHasDupeResources(const IDType& id) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,51 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/PART.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template struct PPImpl<_GPSM<UniqueID32>>;
|
|
||||||
template struct PPImpl<_GPSM<UniqueID64>>;
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_GPSM<UniqueID32>>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_GPSM<UniqueID64>>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view GPSM<UniqueID32>::DNAType() {
|
|
||||||
return "GPSM<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view GPSM<UniqueID64>::DNAType() {
|
|
||||||
return "GPSM<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractGPSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
GPSM<IDType> gpsm;
|
|
||||||
gpsm.read(rs);
|
|
||||||
athena::io::ToYAMLStream(gpsm, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template bool ExtractGPSM<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractGPSM<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteGPSM(const GPSM<IDType>& gpsm, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
gpsm.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 WriteGPSM<UniqueID32>(const GPSM<UniqueID32>& gpsm, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteGPSM<UniqueID64>(const GPSM<UniqueID64>& gpsm, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,141 +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 VECTOR_ENTRY
|
|
||||||
#define VECTOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MOD_VECTOR_ENTRY
|
|
||||||
#define MOD_VECTOR_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 UV_ENTRY
|
|
||||||
#define UV_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef RES_ENTRY
|
|
||||||
#define RES_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef KSSM_ENTRY
|
|
||||||
#define KSSM_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BOOL_ENTRY
|
|
||||||
#define BOOL_ENTRY(name, identifier, def) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VECTOR_ENTRY('PSIV', x0_PSIV)
|
|
||||||
MOD_VECTOR_ENTRY('PSVM', x4_PSVM)
|
|
||||||
VECTOR_ENTRY('PSOV', x8_PSOV)
|
|
||||||
INT_ENTRY('PSLT', xc_PSLT)
|
|
||||||
INT_ENTRY('PSWT', x10_PSWT)
|
|
||||||
REAL_ENTRY('PSTS', x14_PSTS)
|
|
||||||
VECTOR_ENTRY('POFS', x18_POFS)
|
|
||||||
INT_ENTRY('SEED', x1c_SEED)
|
|
||||||
REAL_ENTRY('LENG', x20_LENG)
|
|
||||||
REAL_ENTRY('WIDT', x24_WIDT)
|
|
||||||
INT_ENTRY('MAXP', x28_MAXP)
|
|
||||||
REAL_ENTRY('GRTE', x2c_GRTE)
|
|
||||||
COLOR_ENTRY('COLR', x30_COLR)
|
|
||||||
INT_ENTRY('LTME', x34_LTME)
|
|
||||||
VECTOR_ENTRY('ILOC', x38_ILOC)
|
|
||||||
VECTOR_ENTRY('IVEC', x3c_IVEC)
|
|
||||||
EMITTER_ENTRY('EMTR', x40_EMTR)
|
|
||||||
INT_ENTRY('MBSP', x48_MBSP)
|
|
||||||
REAL_ENTRY('SIZE', x4c_SIZE)
|
|
||||||
REAL_ENTRY('ROTA', x50_ROTA)
|
|
||||||
UV_ENTRY('TEXR', x54_TEXR)
|
|
||||||
UV_ENTRY('TIND', x58_TIND)
|
|
||||||
RES_ENTRY('PMDL', x5c_PMDL)
|
|
||||||
VECTOR_ENTRY('PMOP', x6c_PMOP)
|
|
||||||
VECTOR_ENTRY('PMRT', x70_PMRT)
|
|
||||||
VECTOR_ENTRY('PMSC', x74_PMSC)
|
|
||||||
COLOR_ENTRY('PMCL', x78_PMCL)
|
|
||||||
MOD_VECTOR_ENTRY('VEL1', x7c_VEL1)
|
|
||||||
MOD_VECTOR_ENTRY('VEL2', x80_VEL2)
|
|
||||||
MOD_VECTOR_ENTRY('VEL3', x84_VEL3)
|
|
||||||
MOD_VECTOR_ENTRY('VEL4', x88_VEL4)
|
|
||||||
RES_ENTRY('ICTS', x8c_ICTS)
|
|
||||||
INT_ENTRY('NCSY', x9c_NCSY)
|
|
||||||
INT_ENTRY('CSSD', xa0_CSSD)
|
|
||||||
RES_ENTRY('IDTS', xa4_IDTS)
|
|
||||||
INT_ENTRY('NDSY', xb4_NDSY)
|
|
||||||
RES_ENTRY('IITS', xb8_IITS)
|
|
||||||
INT_ENTRY('PISY', xc8_PISY)
|
|
||||||
INT_ENTRY('SISY', xcc_SISY)
|
|
||||||
KSSM_ENTRY('KSSM', xd0_KSSM)
|
|
||||||
RES_ENTRY('SSWH', xd4_SSWH)
|
|
||||||
INT_ENTRY('SSSD', xe4_SSSD)
|
|
||||||
VECTOR_ENTRY('SSPO', xe8_SSPO)
|
|
||||||
INT_ENTRY('SESD', xf8_SESD)
|
|
||||||
VECTOR_ENTRY('SEPO', xfc_SEPO)
|
|
||||||
RES_ENTRY('PMLC', xec_PMLC)
|
|
||||||
INT_ENTRY('LTYP', x100_LTYP)
|
|
||||||
COLOR_ENTRY('LCLR', x104_LCLR)
|
|
||||||
REAL_ENTRY('LINT', x108_LINT)
|
|
||||||
VECTOR_ENTRY('LOFF', x10c_LOFF)
|
|
||||||
VECTOR_ENTRY('LDIR', x110_LDIR)
|
|
||||||
INT_ENTRY('LFOT', x114_LFOT)
|
|
||||||
REAL_ENTRY('LFOR', x118_LFOR)
|
|
||||||
REAL_ENTRY('LSLA', x11c_LSLA)
|
|
||||||
|
|
||||||
/* 0-00 additions */
|
|
||||||
RES_ENTRY('SELC', xd8_SELC)
|
|
||||||
REAL_ENTRY('ADV1', x10c_ADV1)
|
|
||||||
REAL_ENTRY('ADV2', x110_ADV2)
|
|
||||||
REAL_ENTRY('ADV3', x114_ADV3)
|
|
||||||
REAL_ENTRY('ADV4', x118_ADV4)
|
|
||||||
REAL_ENTRY('ADV5', x11c_ADV5)
|
|
||||||
REAL_ENTRY('ADV6', x120_ADV6)
|
|
||||||
REAL_ENTRY('ADV7', x124_ADV7)
|
|
||||||
REAL_ENTRY('ADV8', x128_ADV8)
|
|
||||||
|
|
||||||
BOOL_ENTRY('SORT', x44_28_SORT, false)
|
|
||||||
BOOL_ENTRY('MBLR', x44_30_MBLR, false)
|
|
||||||
BOOL_ENTRY('LINE', x44_24_LINE, false)
|
|
||||||
BOOL_ENTRY('LIT_', x44_29_LIT_, false)
|
|
||||||
BOOL_ENTRY('AAPH', x44_26_AAPH, false)
|
|
||||||
BOOL_ENTRY('ZBUF', x44_27_ZBUF, false)
|
|
||||||
BOOL_ENTRY('FXLL', x44_25_FXLL, false)
|
|
||||||
BOOL_ENTRY('PMAB', x44_31_PMAB, false)
|
|
||||||
BOOL_ENTRY('VMD4', x45_29_VMD4, false)
|
|
||||||
BOOL_ENTRY('VMD3', x45_28_VMD3, false)
|
|
||||||
BOOL_ENTRY('VMD2', x45_27_VMD2, false)
|
|
||||||
BOOL_ENTRY('VMD1', x45_26_VMD1, false)
|
|
||||||
BOOL_ENTRY('OPTS', x45_31_OPTS, false)
|
|
||||||
BOOL_ENTRY('PMUS', x45_24_PMUS, false)
|
|
||||||
BOOL_ENTRY('PMOO', x45_25_PMOO, true)
|
|
||||||
BOOL_ENTRY('CIND', x45_30_CIND, false)
|
|
||||||
|
|
||||||
BOOL_ENTRY('ORNT', x30_30_ORNT, false)
|
|
||||||
BOOL_ENTRY('RSOP', x30_31_RSOP, false)
|
|
||||||
|
|
||||||
#undef ENTRY
|
|
||||||
#undef INT_ENTRY
|
|
||||||
#undef REAL_ENTRY
|
|
||||||
#undef VECTOR_ENTRY
|
|
||||||
#undef MOD_VECTOR_ENTRY
|
|
||||||
#undef COLOR_ENTRY
|
|
||||||
#undef EMITTER_ENTRY
|
|
||||||
#undef UV_ENTRY
|
|
||||||
#undef RES_ENTRY
|
|
||||||
#undef KSSM_ENTRY
|
|
||||||
#undef BOOL_ENTRY
|
|
|
@ -1,64 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct _GPSM {
|
|
||||||
static constexpr ParticleType Type = ParticleType::GPSM;
|
|
||||||
|
|
||||||
#define INT_ENTRY(name, identifier) IntElementFactory identifier;
|
|
||||||
#define REAL_ENTRY(name, identifier) RealElementFactory identifier;
|
|
||||||
#define VECTOR_ENTRY(name, identifier) VectorElementFactory identifier;
|
|
||||||
#define MOD_VECTOR_ENTRY(name, identifier) ModVectorElementFactory identifier;
|
|
||||||
#define COLOR_ENTRY(name, identifier) ColorElementFactory identifier;
|
|
||||||
#define EMITTER_ENTRY(name, identifier) EmitterElementFactory identifier;
|
|
||||||
#define UV_ENTRY(name, identifier) UVElementFactory<IDType> identifier;
|
|
||||||
#define RES_ENTRY(name, identifier) ChildResourceFactory<IDType> identifier;
|
|
||||||
#define KSSM_ENTRY(name, identifier) SpawnSystemKeyframeData<IDType> identifier;
|
|
||||||
#define BOOL_ENTRY(name, identifier, def) bool identifier = def;
|
|
||||||
#include "PART.def"
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
void constexpr Enumerate(_Func f) {
|
|
||||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
|
||||||
#define BOOL_ENTRY(name, identifier, def) f(FOURCC(name), identifier, def);
|
|
||||||
#include "PART.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 "PART.def"
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <class IDType>
|
|
||||||
using GPSM = PPImpl<_GPSM<IDType>>;
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractGPSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteGPSM(const GPSM<IDType>& gpsm, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,248 +0,0 @@
|
||||||
#include "PATH.hpp"
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
#include "zeus/CAABox.hpp"
|
|
||||||
#include "DataSpec/DNACommon/AROTBuilder.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAPATH {
|
|
||||||
|
|
||||||
#define DUMP_OCTREE 0
|
|
||||||
|
|
||||||
#if DUMP_OCTREE
|
|
||||||
/* octree dumper */
|
|
||||||
static void OutputOctreeNode(hecl::blender::PyOutStream& os, int idx, const zeus::CAABox& aabb) {
|
|
||||||
const zeus::CVector3f pos = aabb.center();
|
|
||||||
const zeus::CVector3f extent = aabb.extents();
|
|
||||||
os.format(
|
|
||||||
"obj = bpy.data.objects.new('Leaf_%d', None)\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(obj)\n"
|
|
||||||
"obj.location = (%f,%f,%f)\n"
|
|
||||||
"obj.scale = (%f,%f,%f)\n"
|
|
||||||
"obj.empty_display_type = 'CUBE'\n"
|
|
||||||
"obj.layers[1] = True\n"
|
|
||||||
"obj.layers[0] = False\n",
|
|
||||||
idx, pos.x(), pos.y(), pos.z(), extent.x(), extent.y(), extent.z());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <class PAKBridge>
|
|
||||||
void PATH<PAKBridge>::sendToBlender(hecl::blender::Connection& conn, std::string_view entryName,
|
|
||||||
const zeus::CMatrix4f* xf, const std::string& areaPath) {
|
|
||||||
/* Open Py Stream and read sections */
|
|
||||||
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
|
|
||||||
os << "import bpy\n"
|
|
||||||
"import bmesh\n"
|
|
||||||
"from mathutils import Vector, Matrix\n"
|
|
||||||
"\n"
|
|
||||||
"bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')\n"
|
|
||||||
"bpy.types.Material.retro_path_type_mask = bpy.props.IntProperty(name='Retro: Path Type Mask')\n"
|
|
||||||
"\n"
|
|
||||||
"material_dict = {}\n"
|
|
||||||
"material_index = []\n"
|
|
||||||
"def make_ground_material(idxMask):\n"
|
|
||||||
" mat = bpy.data.materials.new('Ground %X' % idxMask)\n"
|
|
||||||
" mat.diffuse_color = (0.8, 0.460, 0.194, 1.0)\n"
|
|
||||||
" return mat\n"
|
|
||||||
"def make_flyer_material(idxMask):\n"
|
|
||||||
" mat = bpy.data.materials.new('Flyer %X' % idxMask)\n"
|
|
||||||
" mat.diffuse_color = (0.016, 0.8, 0.8, 1.0)\n"
|
|
||||||
" return mat\n"
|
|
||||||
"def make_swimmer_material(idxMask):\n"
|
|
||||||
" mat = bpy.data.materials.new('Swimmer %X' % idxMask)\n"
|
|
||||||
" mat.diffuse_color = (0.074, 0.293, 0.8, 1.0)\n"
|
|
||||||
" return mat\n"
|
|
||||||
"def select_material(meshIdxMask, meshTypeMask):\n"
|
|
||||||
" key = (meshIdxMask, meshTypeMask)\n"
|
|
||||||
" if key in material_index:\n"
|
|
||||||
" return material_index.index(key)\n"
|
|
||||||
" elif key in material_dict:\n"
|
|
||||||
" material_index.append(key)\n"
|
|
||||||
" return len(material_index)-1\n"
|
|
||||||
" else:\n"
|
|
||||||
" if meshTypeMask == 0x2:\n"
|
|
||||||
" mat = make_flyer_material(meshIdxMask)\n"
|
|
||||||
" elif meshTypeMask == 0x4:\n"
|
|
||||||
" mat = make_swimmer_material(meshIdxMask)\n"
|
|
||||||
" else:\n"
|
|
||||||
" mat = make_ground_material(meshIdxMask)\n"
|
|
||||||
" mat.retro_path_idx_mask = meshIdxMask\n"
|
|
||||||
" mat.retro_path_type_mask = meshTypeMask\n"
|
|
||||||
" material_dict[key] = mat\n"
|
|
||||||
" material_index.append(key)\n"
|
|
||||||
" return len(material_index)-1\n"
|
|
||||||
"\n";
|
|
||||||
os.format(FMT_STRING("bpy.context.scene.name = '{}'\n"), entryName);
|
|
||||||
os << "# Clear Scene\n"
|
|
||||||
"if len(bpy.data.collections):\n"
|
|
||||||
" bpy.data.collections.remove(bpy.data.collections[0])\n"
|
|
||||||
"\n"
|
|
||||||
"bm = bmesh.new()\n"
|
|
||||||
"height_lay = bm.faces.layers.float.new('Height')\n";
|
|
||||||
|
|
||||||
for (const Node& n : nodes) {
|
|
||||||
zeus::simd_floats f(n.position.simd);
|
|
||||||
os.format(FMT_STRING("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "bm.verts.ensure_lookup_table()\n";
|
|
||||||
|
|
||||||
for (const Region& r : regions) {
|
|
||||||
os << "tri_verts = []\n";
|
|
||||||
for (atUint32 i = 0; i < r.nodeCount; ++i)
|
|
||||||
os.format(FMT_STRING("tri_verts.append(bm.verts[{}])\n"), r.nodeStart + i);
|
|
||||||
|
|
||||||
os.format(FMT_STRING("face = bm.faces.get(tri_verts)\n"
|
|
||||||
"if face is None:\n"
|
|
||||||
" face = bm.faces.new(tri_verts)\n"
|
|
||||||
" face.normal_flip()\n"
|
|
||||||
"face.material_index = select_material(0x{:04X}, 0x{:04X})\n"
|
|
||||||
"face.smooth = False\n"
|
|
||||||
"face[height_lay] = {}\n"
|
|
||||||
"\n"),
|
|
||||||
r.meshIndexMask, r.meshTypeMask, r.height);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
const zeus::CVector3f center = xf->multiplyOneOverW(r.centroid);
|
|
||||||
zeus::CAABox aabb(xf->multiplyOneOverW(r.aabb[0]), xf->multiplyOneOverW(r.aabb[1]));
|
|
||||||
os.format(FMT_STRING("aabb = bpy.data.objects.new('AABB', None)\n")
|
|
||||||
"aabb.location = (%f,%f,%f)\n"
|
|
||||||
"aabb.scale = (%f,%f,%f)\n"
|
|
||||||
"aabb.empty_display_type = 'CUBE'\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(aabb)\n"
|
|
||||||
"centr = bpy.data.objects.new('Center', None)\n"
|
|
||||||
"centr.location = (%f,%f,%f)\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(centr)\n",
|
|
||||||
aabb.min[0] + (aabb.max[0] - aabb.min[0]) / 2.f,
|
|
||||||
aabb.min[1] + (aabb.max[1] - aabb.min[1]) / 2.f,
|
|
||||||
aabb.min[2] + (aabb.max[2] - aabb.min[2]) / 2.f,
|
|
||||||
(aabb.max[0] - aabb.min[0]) / 2.f,
|
|
||||||
(aabb.max[1] - aabb.min[1]) / 2.f,
|
|
||||||
(aabb.max[2] - aabb.min[2]) / 2.f,
|
|
||||||
center.x(), center.y(), center.z());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
for (const Node& n : nodes) {
|
|
||||||
zeus::simd_floats f(n.position.simd);
|
|
||||||
zeus::simd_floats no(n.position.simd + n.normal.simd);
|
|
||||||
os.format(FMT_STRING("v = bm.verts.new((%f,%f,%f))\n")
|
|
||||||
"v2 = bm.verts.new((%f,%f,%f))\n"
|
|
||||||
"bm.edges.new((v, v2))\n", f[0], f[1], f[2], no[0], no[1], no[2]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
os << "bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)\n"
|
|
||||||
"path_mesh = bpy.data.meshes.new('PATH')\n"
|
|
||||||
"bm.to_mesh(path_mesh)\n"
|
|
||||||
"path_mesh_obj = bpy.data.objects.new(path_mesh.name, path_mesh)\n"
|
|
||||||
"\n"
|
|
||||||
"for mat_name in material_index:\n"
|
|
||||||
" mat = material_dict[mat_name]\n"
|
|
||||||
" path_mesh.materials.append(mat)\n"
|
|
||||||
"\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(path_mesh_obj)\n"
|
|
||||||
"path_mesh_obj.display_type = 'SOLID'\n"
|
|
||||||
"bpy.context.scene.hecl_path_obj = path_mesh_obj.name\n"
|
|
||||||
"\n";
|
|
||||||
|
|
||||||
if (xf) {
|
|
||||||
const zeus::CMatrix4f& w = *xf;
|
|
||||||
zeus::simd_floats xfMtxF[4];
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
w.m[i].mSimd.copy_to(xfMtxF[i]);
|
|
||||||
os.format(FMT_STRING("mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
|
|
||||||
"mtxd = mtx.decompose()\n"
|
|
||||||
"path_mesh_obj.rotation_mode = 'QUATERNION'\n"
|
|
||||||
"path_mesh_obj.location = mtxd[0]\n"
|
|
||||||
"path_mesh_obj.rotation_quaternion = mtxd[1]\n"
|
|
||||||
"path_mesh_obj.scale = mtxd[2]\n"),
|
|
||||||
xfMtxF[0][0], xfMtxF[1][0], xfMtxF[2][0], xfMtxF[3][0], xfMtxF[0][1], xfMtxF[1][1], xfMtxF[2][1],
|
|
||||||
xfMtxF[3][1], xfMtxF[0][2], xfMtxF[1][2], xfMtxF[2][2], xfMtxF[3][2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DUMP_OCTREE
|
|
||||||
{
|
|
||||||
int idx = 0;
|
|
||||||
for (const auto& n : octree) {
|
|
||||||
if (n.isLeaf)
|
|
||||||
OutputOctreeNode(os, idx, zeus::CAABox(n.aabb[0], n.aabb[1]));
|
|
||||||
++idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
os.linkBackground(fmt::format(FMT_STRING("//{}"), areaPath));
|
|
||||||
os.centerView();
|
|
||||||
os.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class PAKBridge>
|
|
||||||
bool PATH<PAKBridge>::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry,
|
|
||||||
bool force, hecl::blender::Token& btok,
|
|
||||||
std::function<void(const char*)> fileChanged) {
|
|
||||||
PATH path;
|
|
||||||
path.read(rs);
|
|
||||||
hecl::blender::Connection& conn = btok.getBlenderConnection();
|
|
||||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::PathMesh))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string areaPath;
|
|
||||||
for (const auto& ent : hecl::DirectoryEnumerator(outPath.getParentPath().getAbsolutePath())) {
|
|
||||||
if (hecl::StringUtils::BeginsWith(ent.m_name, "!area_")) {
|
|
||||||
areaPath = ent.m_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const zeus::CMatrix4f* xf = pakRouter.lookupMAPATransform(entry.id);
|
|
||||||
path.sendToBlender(conn, pakRouter.getBestEntryName(entry, false), xf, areaPath);
|
|
||||||
return conn.saveBlend();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class PAKBridge>
|
|
||||||
bool PATH<PAKBridge>::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const PathMesh& mesh,
|
|
||||||
hecl::blender::Token& btok) {
|
|
||||||
athena::io::MemoryReader r(mesh.data.data(), mesh.data.size());
|
|
||||||
PATH path;
|
|
||||||
path.read(r);
|
|
||||||
if (!path.regions.empty()) {
|
|
||||||
AROTBuilder octreeBuilder;
|
|
||||||
octreeBuilder.buildPath(path);
|
|
||||||
} else {
|
|
||||||
path.octreeNodeCount = 1;
|
|
||||||
path.octree.emplace_back();
|
|
||||||
OctreeNode& n = path.octree.back();
|
|
||||||
n.isLeaf = 1;
|
|
||||||
n.aabb[0] = zeus::CVector3f{FLT_MAX, FLT_MAX, FLT_MAX};
|
|
||||||
n.aabb[1] = zeus::CVector3f{-FLT_MAX, -FLT_MAX, -FLT_MAX};
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
n.children[i] = 0xffffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DUMP_OCTREE
|
|
||||||
{
|
|
||||||
hecl::blender::Connection& conn = btok.getBlenderConnection();
|
|
||||||
if (!conn.createBlend(inPath.getWithExtension(".octree.blend", true), hecl::blender::BlendType::PathMesh))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
zeus::CMatrix4f xf;
|
|
||||||
path.sendToBlender(conn, "PATH"sv, &xf);
|
|
||||||
conn.saveBlend();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath());
|
|
||||||
path.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 struct PATH<DataSpec::DNAMP1::PAKBridge>;
|
|
||||||
template struct PATH<DataSpec::DNAMP2::PAKBridge>;
|
|
||||||
template struct PATH<DataSpec::DNAMP3::PAKBridge>;
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAPATH
|
|
|
@ -1,112 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
#include "DataSpec/DNAMP1/DNAMP1.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/DNAMP2.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/DNAMP3.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAPATH {
|
|
||||||
template <class PAKBridge>
|
|
||||||
struct RegionPointers {};
|
|
||||||
template <>
|
|
||||||
struct RegionPointers<DataSpec::DNAMP1::PAKBridge> : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> regionIdxPtr;
|
|
||||||
};
|
|
||||||
template <>
|
|
||||||
struct RegionPointers<DataSpec::DNAMP2::PAKBridge> : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> unk0;
|
|
||||||
Value<atUint32> unk1;
|
|
||||||
Value<atUint32> unk2;
|
|
||||||
Value<atUint32> regionIdxPtr;
|
|
||||||
};
|
|
||||||
template <>
|
|
||||||
struct RegionPointers<DataSpec::DNAMP3::PAKBridge> : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> unk0;
|
|
||||||
Value<atUint32> unk1;
|
|
||||||
Value<atUint32> unk2;
|
|
||||||
Value<atUint32> regionIdxPtr;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class PAKBridge>
|
|
||||||
struct AT_SPECIALIZE_PARMS(DataSpec::DNAMP1::PAKBridge, DataSpec::DNAMP2::PAKBridge, DataSpec::DNAMP3::PAKBridge) PATH
|
|
||||||
: BigDNA {
|
|
||||||
using PathMesh = hecl::blender::PathMesh;
|
|
||||||
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> version;
|
|
||||||
|
|
||||||
struct Node : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atVec3f> position;
|
|
||||||
Value<atVec3f> normal;
|
|
||||||
};
|
|
||||||
Value<atUint32> nodeCount;
|
|
||||||
Vector<Node, AT_DNA_COUNT(nodeCount)> nodes;
|
|
||||||
|
|
||||||
struct Link : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> nodeIdx;
|
|
||||||
Value<atUint32> regionIdx;
|
|
||||||
Value<float> width2d;
|
|
||||||
Value<float> oneOverWidth2d;
|
|
||||||
};
|
|
||||||
Value<atUint32> linkCount;
|
|
||||||
Vector<Link, AT_DNA_COUNT(linkCount)> links;
|
|
||||||
|
|
||||||
struct Region : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> nodeCount;
|
|
||||||
Value<atUint32> nodeStart;
|
|
||||||
Value<atUint32> linkCount;
|
|
||||||
Value<atUint32> linkStart;
|
|
||||||
Value<atUint16> meshIndexMask;
|
|
||||||
Value<atUint16> meshTypeMask;
|
|
||||||
Value<float> height;
|
|
||||||
Value<atVec3f> normal;
|
|
||||||
Value<atUint32> regionIdx;
|
|
||||||
Value<atVec3f> centroid;
|
|
||||||
Value<atVec3f> aabb[2];
|
|
||||||
Value<RegionPointers<PAKBridge>> pointers;
|
|
||||||
};
|
|
||||||
Value<atUint32> regionCount;
|
|
||||||
Vector<Region, AT_DNA_COUNT(regionCount)> regions;
|
|
||||||
|
|
||||||
Vector<atUint32, AT_DNA_COUNT((((regionCount * (regionCount - 1)) / 2) + 31) / 32)> bitmap1;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(bitmap1.size())> bitmap2;
|
|
||||||
|
|
||||||
/* Unused in all games, removed in MP3 */
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(std::is_same_v<PAKBridge, DataSpec::DNAMP3::PAKBridge>
|
|
||||||
? 0
|
|
||||||
: (((((regionCount * regionCount) + 31) / 32) - bitmap1.size()) * 2))>
|
|
||||||
bitmap3;
|
|
||||||
|
|
||||||
Value<atUint32> octreeRegionLookupCount;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(octreeRegionLookupCount)> octreeRegionLookup;
|
|
||||||
|
|
||||||
struct OctreeNode : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> isLeaf;
|
|
||||||
Value<atVec3f> aabb[2];
|
|
||||||
Value<atVec3f> centroid;
|
|
||||||
Value<atUint32> children[8];
|
|
||||||
Value<atUint32> regionCount;
|
|
||||||
Value<atUint32> regionStart;
|
|
||||||
};
|
|
||||||
Value<atUint32> octreeNodeCount;
|
|
||||||
Vector<OctreeNode, AT_DNA_COUNT(octreeNodeCount)> octree;
|
|
||||||
|
|
||||||
void sendToBlender(hecl::blender::Connection& conn, std::string_view entryName, const zeus::CMatrix4f* xf,
|
|
||||||
const std::string& areaPath);
|
|
||||||
|
|
||||||
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry, bool force,
|
|
||||||
hecl::blender::Token& btok, std::function<void(const char*)> fileChanged);
|
|
||||||
|
|
||||||
static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const PathMesh& mesh,
|
|
||||||
hecl::blender::Token& btok);
|
|
||||||
};
|
|
||||||
} // namespace DataSpec::DNAPATH
|
|
|
@ -1,586 +0,0 @@
|
||||||
#include "ParticleCommon.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
logvisor::Module LogModule("DataSpec::DNAParticle");
|
|
||||||
|
|
||||||
template struct PEImpl<_RealElementFactory>;
|
|
||||||
template struct PEImpl<_IntElementFactory>;
|
|
||||||
template struct PEImpl<_VectorElementFactory>;
|
|
||||||
template struct PEImpl<_ColorElementFactory>;
|
|
||||||
template struct PEImpl<_ModVectorElementFactory>;
|
|
||||||
template struct PEImpl<_EmitterElementFactory>;
|
|
||||||
template struct PEImpl<_UVElementFactory<UniqueID32>>;
|
|
||||||
template struct PEImpl<_UVElementFactory<UniqueID64>>;
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PEImpl<_RealElementFactory>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PEImpl<_IntElementFactory>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PEImpl<_VectorElementFactory>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PEImpl<_ColorElementFactory>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PEImpl<_ModVectorElementFactory>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PEImpl<_EmitterElementFactory>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PEImpl<_UVElementFactory<UniqueID32>>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PEImpl<_UVElementFactory<UniqueID64>>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void REConstant::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
|
|
||||||
val = r.readFloat();
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void REConstant::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
|
|
||||||
w.writeFloat(val);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void REConstant::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 4;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void REConstant::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
|
|
||||||
val = r.readFloatBig();
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void REConstant::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
|
|
||||||
w.writeFloatBig(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void IEConstant::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
|
|
||||||
val = r.readUint32();
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void IEConstant::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
|
|
||||||
w.writeUint32(val);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void IEConstant::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 4;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void IEConstant::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
|
|
||||||
val = r.readUint32Big();
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void IEConstant::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
|
|
||||||
w.writeUint32Big(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void VEConstant::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
|
|
||||||
size_t elemCount;
|
|
||||||
if (auto v = r.enterSubVector(elemCount)) {
|
|
||||||
for (size_t i = 0; i < 3 && i < elemCount; ++i) {
|
|
||||||
if (auto rec = r.enterSubRecord())
|
|
||||||
comps[i].read(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void VEConstant::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
|
|
||||||
if (auto v = w.enterSubVector())
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
if (auto rec = w.enterSubRecord())
|
|
||||||
comps[i].write(w);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void VEConstant::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
comps[0].binarySize(s);
|
|
||||||
comps[1].binarySize(s);
|
|
||||||
comps[2].binarySize(s);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void VEConstant::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
|
|
||||||
comps[0].read(r);
|
|
||||||
comps[1].read(r);
|
|
||||||
comps[2].read(r);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void VEConstant::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
|
|
||||||
comps[0].write(w);
|
|
||||||
comps[1].write(w);
|
|
||||||
comps[2].write(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void CEConstant::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
if (auto rec = r.enterSubRecord())
|
|
||||||
comps[i].read(r);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void CEConstant::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
|
|
||||||
if (auto v = w.enterSubVector())
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
if (auto rec = w.enterSubRecord())
|
|
||||||
comps[i].write(w);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void CEConstant::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
comps[0].binarySize(s);
|
|
||||||
comps[1].binarySize(s);
|
|
||||||
comps[2].binarySize(s);
|
|
||||||
comps[3].binarySize(s);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void CEConstant::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
|
|
||||||
comps[0].read(r);
|
|
||||||
comps[1].read(r);
|
|
||||||
comps[2].read(r);
|
|
||||||
comps[3].read(r);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void CEConstant::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
|
|
||||||
comps[0].write(w);
|
|
||||||
comps[1].write(w);
|
|
||||||
comps[2].write(w);
|
|
||||||
comps[3].write(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void MVEConstant::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
if (auto rec = r.enterSubRecord())
|
|
||||||
comps[i].read(r);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void MVEConstant::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
|
|
||||||
if (auto v = w.enterSubVector())
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
if (auto rec = w.enterSubRecord())
|
|
||||||
comps[i].write(w);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void MVEConstant::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
comps[0].binarySize(s);
|
|
||||||
comps[1].binarySize(s);
|
|
||||||
comps[2].binarySize(s);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void MVEConstant::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
|
|
||||||
comps[0].read(r);
|
|
||||||
comps[1].read(r);
|
|
||||||
comps[2].read(r);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void MVEConstant::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
|
|
||||||
comps[0].write(w);
|
|
||||||
comps[1].write(w);
|
|
||||||
comps[2].write(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void BoolHelper::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
|
|
||||||
value = r.readBool();
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void BoolHelper::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
|
|
||||||
w.writeBool(value);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void BoolHelper::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 5;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void BoolHelper::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
|
|
||||||
uint32_t clsId;
|
|
||||||
r.readBytesToBuf(&clsId, 4);
|
|
||||||
if (clsId == SBIG('CNST'))
|
|
||||||
value = r.readBool();
|
|
||||||
else
|
|
||||||
value = false;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void BoolHelper::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
|
|
||||||
w.writeBytes("CNST", 4);
|
|
||||||
w.writeBool(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template struct ValueHelper<uint32_t>;
|
|
||||||
template struct ValueHelper<float>;
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(ValueHelper<uint32_t>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(ValueHelper<float>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void EESimpleEmitterTR::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
|
|
||||||
position.reset();
|
|
||||||
velocity.reset();
|
|
||||||
if (auto rec = r.enterSubRecord("ILOC"))
|
|
||||||
position.read(r);
|
|
||||||
if (auto rec = r.enterSubRecord("IVEC"))
|
|
||||||
velocity.read(r);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void EESimpleEmitterTR::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
|
|
||||||
if (auto rec = w.enterSubRecord("ILOC"))
|
|
||||||
position.write(w);
|
|
||||||
if (auto rec = w.enterSubRecord("IVEC"))
|
|
||||||
velocity.write(w);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void EESimpleEmitterTR::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 8;
|
|
||||||
position.binarySize(s);
|
|
||||||
velocity.binarySize(s);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void EESimpleEmitterTR::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
|
|
||||||
position.reset();
|
|
||||||
velocity.reset();
|
|
||||||
uint32_t clsId;
|
|
||||||
r.readBytesToBuf(&clsId, 4);
|
|
||||||
if (clsId == SBIG('ILOC')) {
|
|
||||||
position.read(r);
|
|
||||||
r.readBytesToBuf(&clsId, 4);
|
|
||||||
if (clsId == SBIG('IVEC'))
|
|
||||||
velocity.read(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
void EESimpleEmitterTR::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
|
|
||||||
w.writeBytes("ILOC", 4);
|
|
||||||
position.write(w);
|
|
||||||
w.writeBytes("IVEC", 4);
|
|
||||||
velocity.write(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view UVEConstant<UniqueID32>::DNAType() {
|
|
||||||
return "UVEConstant<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
std::string_view UVEConstant<UniqueID64>::DNAType() {
|
|
||||||
return "UVEConstant<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void UVEConstant<IDType>::_read(typename ReadYaml::StreamT& r) {
|
|
||||||
tex.clear();
|
|
||||||
if (auto rec = r.enterSubRecord("tex"))
|
|
||||||
tex.read(r);
|
|
||||||
}
|
|
||||||
template <class IDType>
|
|
||||||
void UVEConstant<IDType>::_write(typename WriteYaml::StreamT& w) const {
|
|
||||||
if (auto rec = w.enterSubRecord("tex"))
|
|
||||||
tex.write(w);
|
|
||||||
}
|
|
||||||
template <class IDType>
|
|
||||||
void UVEConstant<IDType>::_binarySize(typename BinarySize::StreamT& _s) const {
|
|
||||||
_s += 4;
|
|
||||||
tex.binarySize(_s);
|
|
||||||
}
|
|
||||||
template <class IDType>
|
|
||||||
void UVEConstant<IDType>::_read(typename Read::StreamT& r) {
|
|
||||||
tex.clear();
|
|
||||||
uint32_t clsId;
|
|
||||||
r.readBytesToBuf(&clsId, 4);
|
|
||||||
if (clsId == SBIG('CNST'))
|
|
||||||
tex.read(r);
|
|
||||||
}
|
|
||||||
template <class IDType>
|
|
||||||
void UVEConstant<IDType>::_write(typename Write::StreamT& w) const {
|
|
||||||
w.writeBytes("CNST", 4);
|
|
||||||
tex.write(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(UVEConstant<UniqueID32>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(UVEConstant<UniqueID64>)
|
|
||||||
|
|
||||||
template struct UVEConstant<UniqueID32>;
|
|
||||||
template struct UVEConstant<UniqueID64>;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view UVEAnimTexture<UniqueID32>::DNAType() {
|
|
||||||
return "UVEAnimTexture<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
std::string_view UVEAnimTexture<UniqueID64>::DNAType() {
|
|
||||||
return "UVEAnimTexture<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void UVEAnimTexture<IDType>::_read(typename ReadYaml::StreamT& r) {
|
|
||||||
tex.clear();
|
|
||||||
if (auto rec = r.enterSubRecord("tex"))
|
|
||||||
tex.read(r);
|
|
||||||
if (auto rec = r.enterSubRecord("tileW"))
|
|
||||||
tileW.read(r);
|
|
||||||
if (auto rec = r.enterSubRecord("tileH"))
|
|
||||||
tileH.read(r);
|
|
||||||
if (auto rec = r.enterSubRecord("strideW"))
|
|
||||||
strideW.read(r);
|
|
||||||
if (auto rec = r.enterSubRecord("strideH"))
|
|
||||||
strideH.read(r);
|
|
||||||
if (auto rec = r.enterSubRecord("cycleFrames"))
|
|
||||||
cycleFrames.read(r);
|
|
||||||
if (auto rec = r.enterSubRecord("loop"))
|
|
||||||
loop = r.readBool();
|
|
||||||
}
|
|
||||||
template <class IDType>
|
|
||||||
void UVEAnimTexture<IDType>::_write(typename WriteYaml::StreamT& w) const {
|
|
||||||
if (auto rec = w.enterSubRecord("tex"))
|
|
||||||
tex.write(w);
|
|
||||||
if (auto rec = w.enterSubRecord("tileW"))
|
|
||||||
tileW.write(w);
|
|
||||||
if (auto rec = w.enterSubRecord("tileH"))
|
|
||||||
tileH.write(w);
|
|
||||||
if (auto rec = w.enterSubRecord("strideW"))
|
|
||||||
strideW.write(w);
|
|
||||||
if (auto rec = w.enterSubRecord("strideH"))
|
|
||||||
strideH.write(w);
|
|
||||||
if (auto rec = w.enterSubRecord("cycleFrames"))
|
|
||||||
cycleFrames.write(w);
|
|
||||||
w.writeBool("loop", loop);
|
|
||||||
}
|
|
||||||
template <class IDType>
|
|
||||||
void UVEAnimTexture<IDType>::_binarySize(typename BinarySize::StreamT& _s) const {
|
|
||||||
_s += 9;
|
|
||||||
tex.binarySize(_s);
|
|
||||||
tileW.binarySize(_s);
|
|
||||||
tileH.binarySize(_s);
|
|
||||||
strideW.binarySize(_s);
|
|
||||||
strideH.binarySize(_s);
|
|
||||||
cycleFrames.binarySize(_s);
|
|
||||||
}
|
|
||||||
template <class IDType>
|
|
||||||
void UVEAnimTexture<IDType>::_read(typename Read::StreamT& r) {
|
|
||||||
tex.clear();
|
|
||||||
uint32_t clsId;
|
|
||||||
r.readBytesToBuf(&clsId, 4);
|
|
||||||
if (clsId == SBIG('CNST'))
|
|
||||||
tex.read(r);
|
|
||||||
tileW.read(r);
|
|
||||||
tileH.read(r);
|
|
||||||
strideW.read(r);
|
|
||||||
strideH.read(r);
|
|
||||||
cycleFrames.read(r);
|
|
||||||
r.readBytesToBuf(&clsId, 4);
|
|
||||||
if (clsId == SBIG('CNST'))
|
|
||||||
loop = r.readBool();
|
|
||||||
}
|
|
||||||
template <class IDType>
|
|
||||||
void UVEAnimTexture<IDType>::_write(typename Write::StreamT& w) const {
|
|
||||||
w.writeBytes("CNST", 4);
|
|
||||||
tex.write(w);
|
|
||||||
tileW.write(w);
|
|
||||||
tileH.write(w);
|
|
||||||
strideW.write(w);
|
|
||||||
strideH.write(w);
|
|
||||||
cycleFrames.write(w);
|
|
||||||
w.writeBytes("CNST", 4);
|
|
||||||
w.writeBool(loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(UVEAnimTexture<UniqueID32>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(UVEAnimTexture<UniqueID64>)
|
|
||||||
|
|
||||||
template struct UVEAnimTexture<UniqueID32>;
|
|
||||||
template struct UVEAnimTexture<UniqueID64>;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view UVElementFactory<UniqueID32>::DNAType() {
|
|
||||||
return "UVElementFactory<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
std::string_view UVElementFactory<UniqueID64>::DNAType() {
|
|
||||||
return "UVElementFactory<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view SpawnSystemKeyframeData<UniqueID32>::SpawnSystemKeyframeInfo::DNAType() {
|
|
||||||
return "SpawnSystemKeyframeData<UniqueID32>::SpawnSystemKeyframeInfo"sv;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
std::string_view SpawnSystemKeyframeData<UniqueID64>::SpawnSystemKeyframeInfo::DNAType() {
|
|
||||||
return "SpawnSystemKeyframeData<UniqueID64>::SpawnSystemKeyframeInfo"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
template <class Op>
|
|
||||||
void SpawnSystemKeyframeData<IDType>::SpawnSystemKeyframeInfo::Enumerate(typename Op::StreamT& s) {
|
|
||||||
Do<Op>(athena::io::PropId{"id"}, id, s);
|
|
||||||
Do<Op>(athena::io::PropId{"a"}, a, s);
|
|
||||||
Do<Op>(athena::io::PropId{"b"}, b, s);
|
|
||||||
Do<Op>(athena::io::PropId{"c"}, c, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view SpawnSystemKeyframeData<UniqueID32>::DNAType() {
|
|
||||||
return "SpawnSystemKeyframeData<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
std::string_view SpawnSystemKeyframeData<UniqueID64>::DNAType() {
|
|
||||||
return "SpawnSystemKeyframeData<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void SpawnSystemKeyframeData<IDType>::_read(typename ReadYaml::StreamT& r) {
|
|
||||||
if (auto rec = r.enterSubRecord("a"))
|
|
||||||
a = r.readUint32();
|
|
||||||
if (auto rec = r.enterSubRecord("b"))
|
|
||||||
b = r.readUint32();
|
|
||||||
if (auto rec = r.enterSubRecord("endFrame"))
|
|
||||||
endFrame = r.readUint32();
|
|
||||||
if (auto rec = r.enterSubRecord("d"))
|
|
||||||
d = r.readUint32();
|
|
||||||
spawns.clear();
|
|
||||||
size_t spawnCount;
|
|
||||||
if (auto v = r.enterSubVector("spawns", spawnCount)) {
|
|
||||||
spawns.reserve(spawnCount);
|
|
||||||
for (const auto& child : r.getCurNode()->m_seqChildren) {
|
|
||||||
(void)child;
|
|
||||||
if (auto rec = r.enterSubRecord()) {
|
|
||||||
spawns.emplace_back();
|
|
||||||
spawns.back().first = r.readUint32("startFrame");
|
|
||||||
size_t systemCount;
|
|
||||||
if (auto v = r.enterSubVector("systems", systemCount)) {
|
|
||||||
spawns.back().second.reserve(systemCount);
|
|
||||||
for (const auto& in : r.getCurNode()->m_seqChildren) {
|
|
||||||
(void)in;
|
|
||||||
spawns.back().second.emplace_back();
|
|
||||||
SpawnSystemKeyframeInfo& info = spawns.back().second.back();
|
|
||||||
if (auto rec = r.enterSubRecord())
|
|
||||||
info.read(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void SpawnSystemKeyframeData<IDType>::_write(typename WriteYaml::StreamT& w) const {
|
|
||||||
if (spawns.empty())
|
|
||||||
return;
|
|
||||||
w.writeUint32("a", a);
|
|
||||||
w.writeUint32("b", b);
|
|
||||||
w.writeUint32("endFrame", endFrame);
|
|
||||||
w.writeUint32("d", d);
|
|
||||||
if (auto v = w.enterSubVector("spawns")) {
|
|
||||||
for (const auto& spawn : spawns) {
|
|
||||||
if (auto rec = w.enterSubRecord()) {
|
|
||||||
w.writeUint32("startFrame", spawn.first);
|
|
||||||
if (auto v = w.enterSubVector("systems"))
|
|
||||||
for (const auto& info : spawn.second)
|
|
||||||
if (auto rec = w.enterSubRecord())
|
|
||||||
info.write(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void SpawnSystemKeyframeData<IDType>::_binarySize(typename BinarySize::StreamT& s) const {
|
|
||||||
s += 20;
|
|
||||||
for (const auto& spawn : spawns) {
|
|
||||||
s += 8;
|
|
||||||
for (const auto& info : spawn.second)
|
|
||||||
info.binarySize(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void SpawnSystemKeyframeData<IDType>::_read(typename Read::StreamT& r) {
|
|
||||||
uint32_t clsId;
|
|
||||||
r.readBytesToBuf(&clsId, 4);
|
|
||||||
if (clsId != SBIG('CNST'))
|
|
||||||
return;
|
|
||||||
|
|
||||||
a = r.readUint32Big();
|
|
||||||
b = r.readUint32Big();
|
|
||||||
endFrame = r.readUint32Big();
|
|
||||||
d = r.readUint32Big();
|
|
||||||
uint32_t count = r.readUint32Big();
|
|
||||||
spawns.clear();
|
|
||||||
spawns.reserve(count);
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
|
||||||
spawns.emplace_back();
|
|
||||||
spawns.back().first = r.readUint32Big();
|
|
||||||
uint32_t infoCount = r.readUint32Big();
|
|
||||||
spawns.back().second.reserve(infoCount);
|
|
||||||
for (size_t j = 0; j < infoCount; ++j) {
|
|
||||||
spawns.back().second.emplace_back();
|
|
||||||
spawns.back().second.back().read(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void SpawnSystemKeyframeData<IDType>::_write(typename Write::StreamT& w) const {
|
|
||||||
if (spawns.empty()) {
|
|
||||||
w.writeBytes("NONE", 4);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
w.writeBytes("CNST", 4);
|
|
||||||
w.writeUint32Big(a);
|
|
||||||
w.writeUint32Big(b);
|
|
||||||
w.writeUint32Big(endFrame);
|
|
||||||
w.writeUint32Big(d);
|
|
||||||
w.writeUint32Big(spawns.size());
|
|
||||||
for (const auto& spawn : spawns) {
|
|
||||||
w.writeUint32Big(spawn.first);
|
|
||||||
w.writeUint32Big(spawn.second.size());
|
|
||||||
for (const auto& info : spawn.second)
|
|
||||||
info.write(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(SpawnSystemKeyframeData<UniqueID32>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(SpawnSystemKeyframeData<UniqueID64>)
|
|
||||||
|
|
||||||
template struct SpawnSystemKeyframeData<UniqueID32>;
|
|
||||||
template struct SpawnSystemKeyframeData<UniqueID64>;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view ChildResourceFactory<UniqueID32>::DNAType() {
|
|
||||||
return "ChildResourceFactory<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
std::string_view ChildResourceFactory<UniqueID64>::DNAType() {
|
|
||||||
return "ChildResourceFactory<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void ChildResourceFactory<IDType>::_read(typename ReadYaml::StreamT& r) {
|
|
||||||
id.clear();
|
|
||||||
if (auto rec = r.enterSubRecord("CNST"))
|
|
||||||
id.read(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void ChildResourceFactory<IDType>::_write(typename WriteYaml::StreamT& w) const {
|
|
||||||
if (id.isValid())
|
|
||||||
if (auto rec = w.enterSubRecord("CNST"))
|
|
||||||
id.write(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void ChildResourceFactory<IDType>::_binarySize(typename BinarySize::StreamT& s) const {
|
|
||||||
if (id.isValid())
|
|
||||||
id.binarySize(s);
|
|
||||||
s += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void ChildResourceFactory<IDType>::_read(typename Read::StreamT& r) {
|
|
||||||
id.clear();
|
|
||||||
uint32_t clsId;
|
|
||||||
r.readBytesToBuf(&clsId, 4);
|
|
||||||
if (clsId == SBIG('CNST'))
|
|
||||||
id.read(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
void ChildResourceFactory<IDType>::_write(typename Write::StreamT& w) const {
|
|
||||||
if (id.isValid()) {
|
|
||||||
w.writeBytes("CNST", 4);
|
|
||||||
id.write(w);
|
|
||||||
} else
|
|
||||||
w.writeBytes("NONE", 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(ChildResourceFactory<UniqueID32>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(ChildResourceFactory<UniqueID64>)
|
|
||||||
|
|
||||||
template struct ChildResourceFactory<UniqueID32>;
|
|
||||||
template struct ChildResourceFactory<UniqueID64>;
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,145 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/RigInverter.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNAMP1/CINF.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/CINF.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/CINF.hpp"
|
|
||||||
|
|
||||||
#include <hecl/Blender/Connection.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAANIM {
|
|
||||||
|
|
||||||
template <class CINFType>
|
|
||||||
RigInverter<CINFType>::Bone::Bone(const CINFType& cinf, const typename CINFType::Bone& origBone)
|
|
||||||
: m_origBone(origBone) {
|
|
||||||
atUint32 parentIdx = cinf.getInternalBoneIdxFromId(origBone.parentId);
|
|
||||||
zeus::CVector3f boneOrigin(origBone.origin);
|
|
||||||
zeus::CVector3f naturalTail = boneOrigin + zeus::CVector3f{0.f, 0.5f, 0.f};
|
|
||||||
if (parentIdx != UINT32_MAX) {
|
|
||||||
const typename CINFType::Bone& pBone = cinf.bones[parentIdx];
|
|
||||||
m_parentDelta = boneOrigin - zeus::CVector3f(pBone.origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t actualChildren = 0;
|
|
||||||
for (atUint32 chId : origBone.linked) {
|
|
||||||
if (chId == origBone.parentId)
|
|
||||||
continue;
|
|
||||||
atUint32 chIdx = cinf.getInternalBoneIdxFromId(chId);
|
|
||||||
if (chIdx != UINT32_MAX)
|
|
||||||
++actualChildren;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string* bName = cinf.getBoneNameFromId(origBone.id);
|
|
||||||
bool isLCTR = false;
|
|
||||||
if (bName)
|
|
||||||
isLCTR = bName->find("_LCTR") != std::string::npos;
|
|
||||||
|
|
||||||
if (parentIdx == UINT32_MAX) {
|
|
||||||
/* Root will always use +Y tail */
|
|
||||||
m_tail = naturalTail;
|
|
||||||
} else if (actualChildren) {
|
|
||||||
/* Position tail to average of children */
|
|
||||||
for (atUint32 chId : origBone.linked) {
|
|
||||||
if (chId == origBone.parentId)
|
|
||||||
continue;
|
|
||||||
atUint32 chIdx = cinf.getInternalBoneIdxFromId(chId);
|
|
||||||
if (chIdx != UINT32_MAX) {
|
|
||||||
const typename CINFType::Bone& chBone = cinf.bones[chIdx];
|
|
||||||
m_tail += chBone.origin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_tail /= zeus::CVector3f(float(actualChildren));
|
|
||||||
if ((m_tail - boneOrigin).magSquared() < 0.001f)
|
|
||||||
m_tail = naturalTail;
|
|
||||||
else if (isLCTR)
|
|
||||||
m_tail = boneOrigin + zeus::CVector3f{0.f, 1.0f, 0.f} * (m_tail - boneOrigin).magnitude();
|
|
||||||
} else {
|
|
||||||
/* Extrapolate by delta with parent */
|
|
||||||
m_tail = boneOrigin + m_parentDelta;
|
|
||||||
float deltaMag = m_parentDelta.magnitude();
|
|
||||||
if (deltaMag < 0.001f) {
|
|
||||||
deltaMag = 0.5f;
|
|
||||||
m_tail = naturalTail;
|
|
||||||
} else if (deltaMag > 0.5f) {
|
|
||||||
/* Extreme bones capped to +0.5 value */
|
|
||||||
deltaMag = 0.5f;
|
|
||||||
m_tail = boneOrigin + m_parentDelta.normalized() * 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLCTR)
|
|
||||||
m_tail = boneOrigin + zeus::CVector3f{0.f, 1.0f, 0.f} * deltaMag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class CINFType>
|
|
||||||
RigInverter<CINFType>::RigInverter(const CINFType& cinf) : m_cinf(cinf) {
|
|
||||||
m_bones.reserve(cinf.bones.size());
|
|
||||||
for (const typename CINFType::Bone& b : cinf.bones)
|
|
||||||
m_bones.emplace_back(cinf, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class CINFType>
|
|
||||||
RigInverter<CINFType>::RigInverter(const CINFType& cinf,
|
|
||||||
const std::unordered_map<std::string, hecl::blender::Matrix3f>& matrices)
|
|
||||||
: m_cinf(cinf) {
|
|
||||||
m_bones.reserve(cinf.bones.size());
|
|
||||||
for (const typename CINFType::Bone& b : cinf.bones) {
|
|
||||||
m_bones.emplace_back(cinf, b);
|
|
||||||
|
|
||||||
const std::string* name = cinf.getBoneNameFromId(b.id);
|
|
||||||
if (name) {
|
|
||||||
auto search = matrices.find(*name);
|
|
||||||
if (search != matrices.cend()) {
|
|
||||||
zeus::CMatrix3f boneMtx(search->second[0], search->second[1], search->second[2]);
|
|
||||||
m_bones.back().m_restorer = boneMtx;
|
|
||||||
m_bones.back().m_inverter = m_bones.back().m_restorer.inverse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class CINFType>
|
|
||||||
zeus::CQuaternion RigInverter<CINFType>::invertRotation(atUint32 boneId, const zeus::CQuaternion& origRot) const {
|
|
||||||
for (const Bone& b : m_bones)
|
|
||||||
if (b.m_origBone.id == boneId)
|
|
||||||
return b.m_restorer * origRot * b.m_inverter;
|
|
||||||
return origRot;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class CINFType>
|
|
||||||
zeus::CVector3f RigInverter<CINFType>::invertPosition(atUint32 boneId, const zeus::CVector3f& origPos,
|
|
||||||
bool subDelta) const {
|
|
||||||
for (const Bone& b : m_bones)
|
|
||||||
if (b.m_origBone.id == boneId) {
|
|
||||||
zeus::CVector3f localPos = origPos;
|
|
||||||
if (subDelta)
|
|
||||||
localPos -= b.m_parentDelta;
|
|
||||||
return b.m_restorer.transform(localPos);
|
|
||||||
}
|
|
||||||
return origPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class CINFType>
|
|
||||||
zeus::CQuaternion RigInverter<CINFType>::restoreRotation(atUint32 boneId, const zeus::CQuaternion& origRot) const {
|
|
||||||
for (const Bone& b : m_bones)
|
|
||||||
if (b.m_origBone.id == boneId)
|
|
||||||
return b.m_inverter * origRot * b.m_restorer;
|
|
||||||
return origRot;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class CINFType>
|
|
||||||
zeus::CVector3f RigInverter<CINFType>::restorePosition(atUint32 boneId, const zeus::CVector3f& origPos,
|
|
||||||
bool subDelta) const {
|
|
||||||
for (const Bone& b : m_bones)
|
|
||||||
if (b.m_origBone.id == boneId) {
|
|
||||||
zeus::CVector3f localPos = b.m_inverter.transform(origPos);
|
|
||||||
if (subDelta)
|
|
||||||
localPos += b.m_parentDelta;
|
|
||||||
return localPos;
|
|
||||||
}
|
|
||||||
return origPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
template class RigInverter<DNAMP1::CINF>;
|
|
||||||
template class RigInverter<DNAMP2::CINF>;
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAANIM
|
|
|
@ -1,44 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <hecl/hecl.hpp>
|
|
||||||
#include <zeus/CQuaternion.hpp>
|
|
||||||
#include <zeus/CVector3f.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAANIM {
|
|
||||||
|
|
||||||
/** One-shot process to invert CINF armature into connected rig,
|
|
||||||
* inverting rotations/translations of ANIM data to match */
|
|
||||||
template <class CINFType>
|
|
||||||
class RigInverter {
|
|
||||||
public:
|
|
||||||
struct Bone {
|
|
||||||
const typename CINFType::Bone& m_origBone;
|
|
||||||
zeus::CQuaternion m_inverter;
|
|
||||||
zeus::CQuaternion m_restorer;
|
|
||||||
zeus::CVector3f m_tail;
|
|
||||||
zeus::CVector3f m_parentDelta;
|
|
||||||
Bone(const CINFType& cinf, const typename CINFType::Bone& origBone);
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
const CINFType& m_cinf;
|
|
||||||
std::vector<Bone> m_bones;
|
|
||||||
|
|
||||||
public:
|
|
||||||
RigInverter(const CINFType& cinf);
|
|
||||||
RigInverter(const CINFType& cinf, const std::unordered_map<std::string, hecl::blender::Matrix3f>& matrices);
|
|
||||||
const CINFType& getCINF() const { return m_cinf; }
|
|
||||||
const std::vector<Bone>& getBones() const { return m_bones; }
|
|
||||||
|
|
||||||
zeus::CQuaternion invertRotation(atUint32 boneId, const zeus::CQuaternion& origRot) const;
|
|
||||||
zeus::CVector3f invertPosition(atUint32 boneId, const zeus::CVector3f& origPos, bool subDelta) const;
|
|
||||||
|
|
||||||
zeus::CQuaternion restoreRotation(atUint32 boneId, const zeus::CQuaternion& origRot) const;
|
|
||||||
zeus::CVector3f restorePosition(atUint32 boneId, const zeus::CVector3f& origPos, bool subDelta) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAANIM
|
|
|
@ -1,39 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::SAVWCommon {
|
|
||||||
enum class EScanCategory { None, Data, Lore, Creature, Research, Artifact };
|
|
||||||
|
|
||||||
struct Header : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> magic;
|
|
||||||
Value<atUint32> version;
|
|
||||||
Value<atUint32> areaCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EnvironmentVariable : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> unk1;
|
|
||||||
Value<atUint32> unk2;
|
|
||||||
Value<atUint32> unk3;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Layer : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> areaId;
|
|
||||||
Value<atUint32> layer;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class SAVW>
|
|
||||||
static bool ExtractSAVW(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
SAVW savw;
|
|
||||||
savw.read(rs);
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
athena::io::ToYAMLStream(savw, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::SAVWCommon
|
|
|
@ -1,43 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/STRG.hpp"
|
|
||||||
|
|
||||||
#include "DataSpec/DNAMP1/STRG.hpp"
|
|
||||||
#include "DataSpec/DNAMP2/STRG.hpp"
|
|
||||||
#include "DataSpec/DNAMP3/STRG.hpp"
|
|
||||||
|
|
||||||
#include <logvisor/logvisor.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
void ISTRG::gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const { /* TODO: parse out resource tokens */
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ISTRG> LoadSTRG(athena::io::IStreamReader& reader) {
|
|
||||||
uint32_t magic = reader.readUint32Big();
|
|
||||||
if (magic != 0x87654321) {
|
|
||||||
LogDNACommon.report(logvisor::Error, FMT_STRING("invalid STRG magic"));
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t version = reader.readUint32Big();
|
|
||||||
switch (version) {
|
|
||||||
case 0: {
|
|
||||||
auto* newStrg = new DNAMP1::STRG;
|
|
||||||
newStrg->_read(reader);
|
|
||||||
return std::unique_ptr<ISTRG>(newStrg);
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
auto* newStrg = new DNAMP2::STRG;
|
|
||||||
newStrg->_read(reader);
|
|
||||||
return std::unique_ptr<ISTRG>(newStrg);
|
|
||||||
}
|
|
||||||
case 3: {
|
|
||||||
auto* newStrg = new DNAMP3::STRG;
|
|
||||||
newStrg->_read(reader);
|
|
||||||
return std::unique_ptr<ISTRG>(newStrg);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,30 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
#include <hecl/hecl.hpp>
|
|
||||||
|
|
||||||
namespace athena::io {
|
|
||||||
class IStreamReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
struct ISTRG : BigDNAVYaml {
|
|
||||||
~ISTRG() override = default;
|
|
||||||
|
|
||||||
virtual size_t count() const = 0;
|
|
||||||
virtual std::string getUTF8(const FourCC& lang, size_t idx) const = 0;
|
|
||||||
virtual std::u16string getUTF16(const FourCC& lang, size_t idx) const = 0;
|
|
||||||
virtual int32_t lookupIdx(std::string_view name) const = 0;
|
|
||||||
|
|
||||||
virtual void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const;
|
|
||||||
};
|
|
||||||
std::unique_ptr<ISTRG> LoadSTRG(athena::io::IStreamReader& reader);
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,51 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/SWHC.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template struct PPImpl<_SWSH<UniqueID32>>;
|
|
||||||
template struct PPImpl<_SWSH<UniqueID64>>;
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_SWSH<UniqueID32>>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_SWSH<UniqueID64>>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view SWSH<UniqueID32>::DNAType() {
|
|
||||||
return "SWSH<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view SWSH<UniqueID64>::DNAType() {
|
|
||||||
return "SWSH<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractSWSH(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
SWSH<IDType> swsh;
|
|
||||||
swsh.read(rs);
|
|
||||||
athena::io::ToYAMLStream(swsh, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template bool ExtractSWSH<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractSWSH<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteSWSH(const SWSH<IDType>& swsh, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
swsh.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 WriteSWSH<UniqueID32>(const SWSH<UniqueID32>& swsh, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteSWSH<UniqueID64>(const SWSH<UniqueID64>& swsh, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,69 +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 VECTOR_ENTRY
|
|
||||||
#define VECTOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MOD_VECTOR_ENTRY
|
|
||||||
#define MOD_VECTOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef COLOR_ENTRY
|
|
||||||
#define COLOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UV_ENTRY
|
|
||||||
#define UV_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BOOL_ENTRY
|
|
||||||
#define BOOL_ENTRY(name, identifier, def) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
INT_ENTRY('PSLT', x0_PSLT)
|
|
||||||
REAL_ENTRY('TIME', x4_TIME)
|
|
||||||
REAL_ENTRY('LRAD', x8_LRAD)
|
|
||||||
REAL_ENTRY('RRAD', xc_RRAD)
|
|
||||||
INT_ENTRY('LENG', x10_LENG)
|
|
||||||
COLOR_ENTRY('COLR', x14_COLR)
|
|
||||||
INT_ENTRY('SIDE', x18_SIDE)
|
|
||||||
REAL_ENTRY('IROT', x1c_IROT)
|
|
||||||
REAL_ENTRY('ROTM', x20_ROTM)
|
|
||||||
VECTOR_ENTRY('POFS', x24_POFS)
|
|
||||||
VECTOR_ENTRY('IVEL', x28_IVEL)
|
|
||||||
VECTOR_ENTRY('NPOS', x2c_NPOS)
|
|
||||||
MOD_VECTOR_ENTRY('VELM', x30_VELM)
|
|
||||||
MOD_VECTOR_ENTRY('VLM2', x34_VLM2)
|
|
||||||
INT_ENTRY('SPLN', x38_SPLN)
|
|
||||||
UV_ENTRY('TEXR', x3c_TEXR)
|
|
||||||
INT_ENTRY('TSPN', x40_TSPN)
|
|
||||||
BOOL_ENTRY('LLRD', x44_24_LLRD, false)
|
|
||||||
BOOL_ENTRY('CROS', x44_25_CROS, true)
|
|
||||||
BOOL_ENTRY('VLS1', x44_26_VLS1, false)
|
|
||||||
BOOL_ENTRY('VLS2', x44_27_VLS2, false)
|
|
||||||
BOOL_ENTRY('SROT', x44_28_SROT, false)
|
|
||||||
BOOL_ENTRY('WIRE', x44_29_WIRE, false)
|
|
||||||
BOOL_ENTRY('TEXW', x44_30_TEXW, false)
|
|
||||||
BOOL_ENTRY('AALP', x44_31_AALP, false)
|
|
||||||
BOOL_ENTRY('ZBUF', x45_24_ZBUF, false)
|
|
||||||
BOOL_ENTRY('ORNT', x45_25_ORNT, false)
|
|
||||||
BOOL_ENTRY('CRND', x45_26_CRND, false)
|
|
||||||
|
|
||||||
#undef ENTRY
|
|
||||||
#undef INT_ENTRY
|
|
||||||
#undef REAL_ENTRY
|
|
||||||
#undef VECTOR_ENTRY
|
|
||||||
#undef MOD_VECTOR_ENTRY
|
|
||||||
#undef COLOR_ENTRY
|
|
||||||
#undef UV_ENTRY
|
|
||||||
#undef BOOL_ENTRY
|
|
|
@ -1,59 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct _SWSH {
|
|
||||||
static constexpr ParticleType Type = ParticleType::SWSH;
|
|
||||||
|
|
||||||
#define INT_ENTRY(name, identifier) IntElementFactory identifier;
|
|
||||||
#define REAL_ENTRY(name, identifier) RealElementFactory identifier;
|
|
||||||
#define VECTOR_ENTRY(name, identifier) VectorElementFactory identifier;
|
|
||||||
#define MOD_VECTOR_ENTRY(name, identifier) ModVectorElementFactory identifier;
|
|
||||||
#define COLOR_ENTRY(name, identifier) ColorElementFactory identifier;
|
|
||||||
#define UV_ENTRY(name, identifier) UVElementFactory<IDType> identifier;
|
|
||||||
#define BOOL_ENTRY(name, identifier, def) bool identifier = def;
|
|
||||||
#include "SWHC.def"
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
void constexpr Enumerate(_Func f) {
|
|
||||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
|
||||||
#define BOOL_ENTRY(name, identifier, def) f(FOURCC(name), identifier, def);
|
|
||||||
#include "SWHC.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 "SWHC.def"
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <class IDType>
|
|
||||||
using SWSH = PPImpl<_SWSH<IDType>>;
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractSWSH(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteSWSH(const SWSH<IDType>& gpsm, const hecl::ProjectPath& outPath);
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,38 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <climits>
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
|
|
||||||
struct TXTR {
|
|
||||||
struct PaletteMeta : BigDNAVYaml {
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAMLV
|
|
||||||
Value<atUint32> format = UINT_MAX;
|
|
||||||
Value<atUint32> elementCount = 0;
|
|
||||||
Value<atUint64> dolphinHash = 0;
|
|
||||||
};
|
|
||||||
struct Meta : BigDNAVYaml {
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAMLV
|
|
||||||
Value<atUint32> format = UINT_MAX;
|
|
||||||
Value<atUint32> mips = 0;
|
|
||||||
Value<atUint16> width = 0;
|
|
||||||
Value<atUint16> height = 0;
|
|
||||||
Value<atUint64> dolphinHash = 0;
|
|
||||||
Value<bool> hasPalette = false;
|
|
||||||
PaletteMeta palette;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
static bool Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath);
|
|
||||||
static bool CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath);
|
|
||||||
static TXTR::Meta GetMetaData(PAKEntryReadStream& rs);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,13 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class CVarManager;
|
|
||||||
}
|
|
||||||
namespace DataSpec {
|
|
||||||
struct ITweak : BigDNA {
|
|
||||||
|
|
||||||
virtual void initCVars(hecl::CVarManager*) {}
|
|
||||||
};
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,11 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ITweak.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
struct ITweakPlayerControl : ITweak {
|
|
||||||
virtual atUint32 GetMapping(atUint32) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,32 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
bool WriteTweak(const T& tweak, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
tweak.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 <class T>
|
|
||||||
bool ExtractTweak(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
T tweak;
|
|
||||||
tweak.read(rs);
|
|
||||||
athena::io::ToYAMLStream(tweak, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec
|
|
|
@ -1,51 +0,0 @@
|
||||||
#include "DataSpec/DNACommon/WPSC.hpp"
|
|
||||||
#include "DataSpec/DNACommon/PAK.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template struct PPImpl<_WPSM<UniqueID32>>;
|
|
||||||
template struct PPImpl<_WPSM<UniqueID64>>;
|
|
||||||
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_WPSM<UniqueID32>>)
|
|
||||||
AT_SUBSPECIALIZE_DNA_YAML(PPImpl<_WPSM<UniqueID64>>)
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view WPSM<UniqueID32>::DNAType() {
|
|
||||||
return "WPSM<UniqueID32>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string_view WPSM<UniqueID64>::DNAType() {
|
|
||||||
return "WPSM<UniqueID64>"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractWPSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
if (writer.isOpen()) {
|
|
||||||
WPSM<IDType> wpsm;
|
|
||||||
wpsm.read(rs);
|
|
||||||
athena::io::ToYAMLStream(wpsm, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template bool ExtractWPSM<UniqueID32>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
template bool ExtractWPSM<UniqueID64>(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteWPSM(const WPSM<IDType>& wpsm, const hecl::ProjectPath& outPath) {
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath(), true, false);
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
wpsm.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 WriteWPSM<UniqueID32>(const WPSM<UniqueID32>& wpsm, const hecl::ProjectPath& outPath);
|
|
||||||
template bool WriteWPSM<UniqueID64>(const WPSM<UniqueID64>& wpsm, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,87 +0,0 @@
|
||||||
#ifndef ENTRY
|
|
||||||
#define ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef INT_ENTRY
|
|
||||||
#define INT_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef U32_ENTRY
|
|
||||||
#define U32_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef REAL_ENTRY
|
|
||||||
#define REAL_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef VECTOR_ENTRY
|
|
||||||
#define VECTOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MOD_VECTOR_ENTRY
|
|
||||||
#define MOD_VECTOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef COLOR_ENTRY
|
|
||||||
#define COLOR_ENTRY(name, identifier) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UV_ENTRY
|
|
||||||
#define UV_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, def) ENTRY(name, identifier)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VECTOR_ENTRY('IORN', x0_IORN)
|
|
||||||
VECTOR_ENTRY('IVEC', x4_IVEC)
|
|
||||||
VECTOR_ENTRY('PSOV', x8_PSOV)
|
|
||||||
MOD_VECTOR_ENTRY('PSVM', xc_PSVM)
|
|
||||||
INT_ENTRY('PSLT', x14_PSLT)
|
|
||||||
VECTOR_ENTRY('PSCL', x18_PSCL)
|
|
||||||
COLOR_ENTRY('PCOL', x1c_PCOL)
|
|
||||||
VECTOR_ENTRY('POFS', x20_POFS)
|
|
||||||
VECTOR_ENTRY('OFST', x24_OFST)
|
|
||||||
|
|
||||||
REAL_ENTRY('TRAT', x30_TRAT)
|
|
||||||
RES_ENTRY('APSM', x34_APSM)
|
|
||||||
RES_ENTRY('APS2', x44_APS2)
|
|
||||||
RES_ENTRY('ASW1', x54_ASW1)
|
|
||||||
RES_ENTRY('ASW2', x64_ASW2)
|
|
||||||
RES_ENTRY('ASW3', x74_ASW3)
|
|
||||||
RES_ENTRY('OHEF', x84_OHEF)
|
|
||||||
RES_ENTRY('COLR', x94_COLR)
|
|
||||||
U32_ENTRY('PJFX', xa8_PJFX)
|
|
||||||
REAL_ENTRY('RNGE', xac_RNGE)
|
|
||||||
REAL_ENTRY('FOFF', xb0_FOFF)
|
|
||||||
|
|
||||||
BOOL_ENTRY('VMD2', x10_VMD2, false)
|
|
||||||
BOOL_ENTRY('APSO', x28_APSO, false)
|
|
||||||
BOOL_ENTRY('HOMG', x29_HOMG, false)
|
|
||||||
BOOL_ENTRY('AP11', x2a_AP11, false)
|
|
||||||
BOOL_ENTRY('AP21', x2b_AP21, false)
|
|
||||||
BOOL_ENTRY('AS11', x2c_AS11, false)
|
|
||||||
BOOL_ENTRY('AS12', x2d_AS12, false)
|
|
||||||
BOOL_ENTRY('AS13', x2e_AS13, false)
|
|
||||||
BOOL_ENTRY('EWTR', xa4_EWTR, true)
|
|
||||||
BOOL_ENTRY('LWTR', xa5_LWTR, true)
|
|
||||||
BOOL_ENTRY('SWTR', xa6_SWTR, true)
|
|
||||||
BOOL_ENTRY('FC60', xunk_FC60, false)
|
|
||||||
BOOL_ENTRY('SPS1', xunk_SPS1, false)
|
|
||||||
BOOL_ENTRY('SPS2', xunk_SPS2, false)
|
|
||||||
|
|
||||||
#undef ENTRY
|
|
||||||
#undef INT_ENTRY
|
|
||||||
#undef U32_ENTRY
|
|
||||||
#undef REAL_ENTRY
|
|
||||||
#undef VECTOR_ENTRY
|
|
||||||
#undef MOD_VECTOR_ENTRY
|
|
||||||
#undef COLOR_ENTRY
|
|
||||||
#undef UV_ENTRY
|
|
||||||
#undef RES_ENTRY
|
|
||||||
#undef BOOL_ENTRY
|
|
|
@ -1,60 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/ParticleCommon.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec {
|
|
||||||
class PAKEntryReadStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace hecl {
|
|
||||||
class ProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DataSpec::DNAParticle {
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
struct _WPSM {
|
|
||||||
static constexpr ParticleType Type = ParticleType::WPSM;
|
|
||||||
|
|
||||||
#define INT_ENTRY(name, identifier) IntElementFactory identifier;
|
|
||||||
#define U32_ENTRY(name, identifier) uint32_t identifier = ~0;
|
|
||||||
#define REAL_ENTRY(name, identifier) RealElementFactory identifier;
|
|
||||||
#define VECTOR_ENTRY(name, identifier) VectorElementFactory identifier;
|
|
||||||
#define MOD_VECTOR_ENTRY(name, identifier) ModVectorElementFactory identifier;
|
|
||||||
#define COLOR_ENTRY(name, identifier) ColorElementFactory identifier;
|
|
||||||
#define UV_ENTRY(name, identifier) UVElementFactory<IDType> identifier;
|
|
||||||
#define RES_ENTRY(name, identifier) ChildResourceFactory<IDType> identifier;
|
|
||||||
#define BOOL_ENTRY(name, identifier, def) bool identifier = def;
|
|
||||||
#include "WPSC.def"
|
|
||||||
|
|
||||||
template <typename _Func>
|
|
||||||
void constexpr Enumerate(_Func f) {
|
|
||||||
#define ENTRY(name, identifier) f(FOURCC(name), identifier);
|
|
||||||
#define BOOL_ENTRY(name, identifier, def) f(FOURCC(name), identifier, def);
|
|
||||||
#include "WPSC.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 "WPSC.def"
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <class IDType>
|
|
||||||
using WPSM = PPImpl<_WPSM<IDType>>;
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool ExtractWPSM(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
template <class IDType>
|
|
||||||
bool WriteWPSM(const WPSM<IDType>& wpsm, const hecl::ProjectPath& outPath);
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAParticle
|
|
|
@ -1,104 +0,0 @@
|
||||||
#include "AFSM.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
|
|
||||||
triggerCount = r.readUint32Big();
|
|
||||||
int i = 0;
|
|
||||||
r.enumerate<Trigger>(triggers, triggerCount, [&](athena::io::IStreamReader& in, Trigger& tr) {
|
|
||||||
tr.first = i == 0;
|
|
||||||
tr.read(in);
|
|
||||||
i++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
|
|
||||||
w.writeInt32Big(triggerCount);
|
|
||||||
w.enumerate(triggers);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
|
|
||||||
int i = 0;
|
|
||||||
/* triggers */
|
|
||||||
triggerCount = r.enumerate<Trigger>("triggers", triggers, [&](athena::io::YAMLDocReader& in, Trigger& tr) {
|
|
||||||
tr.first = i == 0;
|
|
||||||
tr.read(in);
|
|
||||||
i++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
|
|
||||||
/* triggers */
|
|
||||||
w.enumerate("triggers", triggers);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 4;
|
|
||||||
for (const Trigger& trig : triggers)
|
|
||||||
trig.binarySize(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view AFSM::State::Transition::DNAType() { return "DNAMP1::AFSM::Transition"sv; }
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Trigger::Enumerate<BigDNA::Read>(athena::io::IStreamReader& __dna_reader) {
|
|
||||||
/* name */
|
|
||||||
name = __dna_reader.readString(-1);
|
|
||||||
/* parameter */
|
|
||||||
parameter = __dna_reader.readFloatBig();
|
|
||||||
if (first) {
|
|
||||||
/* targetState */
|
|
||||||
targetState = __dna_reader.readUint32Big();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Trigger::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& __dna_writer) {
|
|
||||||
/* name */
|
|
||||||
__dna_writer.writeString(name, -1);
|
|
||||||
/* parameter */
|
|
||||||
__dna_writer.writeFloatBig(parameter);
|
|
||||||
if (first) {
|
|
||||||
/* targetState */
|
|
||||||
__dna_writer.writeUint32Big(targetState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Trigger::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& __dna_docin) {
|
|
||||||
/* name */
|
|
||||||
name = __dna_docin.readString("name");
|
|
||||||
/* parameter */
|
|
||||||
parameter = __dna_docin.readFloat("parameter");
|
|
||||||
if (first) {
|
|
||||||
/* targetState */
|
|
||||||
targetState = __dna_docin.readUint32("targetState");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Trigger::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& __dna_docout) {
|
|
||||||
/* name */
|
|
||||||
__dna_docout.writeString("name", name);
|
|
||||||
/* parameter */
|
|
||||||
__dna_docout.writeFloat("parameter", parameter);
|
|
||||||
if (first) {
|
|
||||||
/* targetState */
|
|
||||||
__dna_docout.writeUint32("targetState", targetState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AFSM::State::Transition::Trigger::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
|
|
||||||
__isz += name.size() + 1;
|
|
||||||
__isz += (first ? 8 : 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view AFSM::State::Transition::Trigger::DNAType() { return "DNAMP1::AFSM::State::Transition::Trigger"sv; }
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,51 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <athena/FileWriter.hpp>
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DNAMP1.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
struct AFSM : public BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> stateCount;
|
|
||||||
Vector<String<-1>, AT_DNA_COUNT(stateCount)> stateNames;
|
|
||||||
Value<atUint32> triggerCount;
|
|
||||||
|
|
||||||
struct State : public BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> transitionCount;
|
|
||||||
struct Transition : public BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
Value<atUint32> triggerCount;
|
|
||||||
|
|
||||||
struct Trigger : public BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
bool first = false;
|
|
||||||
String<-1> name;
|
|
||||||
Value<float> parameter;
|
|
||||||
Value<atUint32> targetState;
|
|
||||||
};
|
|
||||||
Vector<Trigger, AT_DNA_COUNT(triggerCount)> triggers;
|
|
||||||
};
|
|
||||||
Vector<Transition, AT_DNA_COUNT(transitionCount)> transitions;
|
|
||||||
};
|
|
||||||
Vector<State, AT_DNA_COUNT(stateCount)> states;
|
|
||||||
|
|
||||||
static bool Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
|
|
||||||
AFSM afsm;
|
|
||||||
afsm.read(rs);
|
|
||||||
athena::io::FileWriter writer(outPath.getAbsolutePath());
|
|
||||||
athena::io::ToYAMLStream(afsm, writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) {
|
|
||||||
AFSM afsm;
|
|
||||||
athena::io::FileReader reader(inPath.getAbsolutePath());
|
|
||||||
athena::io::FromYAMLStream(afsm, reader);
|
|
||||||
athena::io::FileWriter ws(outPath.getAbsolutePath());
|
|
||||||
afsm.write(ws);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,266 +0,0 @@
|
||||||
#include "AGSC.hpp"
|
|
||||||
#include "amuse/AudioGroup.hpp"
|
|
||||||
#include "amuse/AudioGroupData.hpp"
|
|
||||||
|
|
||||||
extern "C" const uint8_t Atomic_H[];
|
|
||||||
extern "C" const uint8_t BetaBeetle_H[];
|
|
||||||
extern "C" const uint8_t Bird_H[];
|
|
||||||
extern "C" const uint8_t BloodFlower_H[];
|
|
||||||
extern "C" const uint8_t Burrower_H[];
|
|
||||||
extern "C" const uint8_t ChozoGhost_H[];
|
|
||||||
extern "C" const uint8_t ChubbWeed_H[];
|
|
||||||
extern "C" const uint8_t CineBoots_H[];
|
|
||||||
extern "C" const uint8_t CineGeneral_H[];
|
|
||||||
extern "C" const uint8_t CineGun_H[];
|
|
||||||
extern "C" const uint8_t CineMorphball_H[];
|
|
||||||
extern "C" const uint8_t CineSuit_H[];
|
|
||||||
extern "C" const uint8_t CineVisor_H[];
|
|
||||||
extern "C" const uint8_t Crater_H[];
|
|
||||||
extern "C" const uint8_t Crystallite_H[];
|
|
||||||
extern "C" const uint8_t Drones_H[];
|
|
||||||
extern "C" const uint8_t EliteSpacePirate_H[];
|
|
||||||
extern "C" const uint8_t FireFlea_H[];
|
|
||||||
extern "C" const uint8_t Flaaghra_H[];
|
|
||||||
extern "C" const uint8_t FlickerBat_H[];
|
|
||||||
extern "C" const uint8_t FlyingPirate_H[];
|
|
||||||
extern "C" const uint8_t FrontEnd_H[];
|
|
||||||
extern "C" const uint8_t GagantuanBeatle_H[];
|
|
||||||
extern "C" const uint8_t Gnats_H[];
|
|
||||||
extern "C" const uint8_t Gryzbee_H[];
|
|
||||||
extern "C" const uint8_t IceCrack_H[];
|
|
||||||
extern "C" const uint8_t IceWorld_H[];
|
|
||||||
extern "C" const uint8_t InjuredPirates_H[];
|
|
||||||
extern "C" const uint8_t IntroBoss_H[];
|
|
||||||
extern "C" const uint8_t IntroWorld_H[];
|
|
||||||
extern "C" const uint8_t JellyZap_H[];
|
|
||||||
extern "C" const uint8_t LavaWorld_H[];
|
|
||||||
extern "C" const uint8_t Magdolite_H[];
|
|
||||||
extern "C" const uint8_t Metaree_H[];
|
|
||||||
extern "C" const uint8_t MetroidPrime_H[];
|
|
||||||
extern "C" const uint8_t Metroid_H[];
|
|
||||||
extern "C" const uint8_t MinesWorld_H[];
|
|
||||||
extern "C" const uint8_t MiscSamus_H[];
|
|
||||||
extern "C" const uint8_t Misc_H[];
|
|
||||||
extern "C" const uint8_t OmegaPirate_H[];
|
|
||||||
extern "C" const uint8_t OverWorld_H[];
|
|
||||||
extern "C" const uint8_t Parasite_H[];
|
|
||||||
extern "C" const uint8_t PhazonGun_H[];
|
|
||||||
extern "C" const uint8_t Phazon_H[];
|
|
||||||
extern "C" const uint8_t PuddleSpore_H[];
|
|
||||||
extern "C" const uint8_t PuddleToad_H[];
|
|
||||||
extern "C" const uint8_t Puffer_H[];
|
|
||||||
extern "C" const uint8_t ReactorDoor_H[];
|
|
||||||
extern "C" const uint8_t Ridley_H[];
|
|
||||||
extern "C" const uint8_t Ripper_H[];
|
|
||||||
extern "C" const uint8_t RuinsWorld_H[];
|
|
||||||
extern "C" const uint8_t SamusShip_H[];
|
|
||||||
extern "C" const uint8_t Scarab_H[];
|
|
||||||
extern "C" const uint8_t Seedling_H[];
|
|
||||||
extern "C" const uint8_t SheeGoth_H[];
|
|
||||||
extern "C" const uint8_t SnakeWeed_H[];
|
|
||||||
extern "C" const uint8_t Sova_H[];
|
|
||||||
extern "C" const uint8_t SpacePirate_H[];
|
|
||||||
extern "C" const uint8_t SpankWeed_H[];
|
|
||||||
extern "C" const uint8_t Thardus_H[];
|
|
||||||
extern "C" const uint8_t TheEnd_H[];
|
|
||||||
extern "C" const uint8_t Torobyte_H[];
|
|
||||||
extern "C" const uint8_t Triclops_H[];
|
|
||||||
extern "C" const uint8_t Turret_H[];
|
|
||||||
extern "C" const uint8_t UI_H[];
|
|
||||||
extern "C" const uint8_t WarWasp_H[];
|
|
||||||
extern "C" const uint8_t Weapons_H[];
|
|
||||||
extern "C" const uint8_t ZZZ_H[];
|
|
||||||
extern "C" const uint8_t Zoomer_H[];
|
|
||||||
extern "C" const uint8_t lumigek_H[];
|
|
||||||
extern "C" const uint8_t test_H[];
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
using namespace std::literals;
|
|
||||||
|
|
||||||
static const std::pair<std::string_view, const uint8_t*> Headers[] = {{"Atomic"sv, Atomic_H},
|
|
||||||
{"BetaBeetle"sv, BetaBeetle_H},
|
|
||||||
{"Bird"sv, Bird_H},
|
|
||||||
{"BloodFlower"sv, BloodFlower_H},
|
|
||||||
{"Burrower"sv, Burrower_H},
|
|
||||||
{"ChozoGhost"sv, ChozoGhost_H},
|
|
||||||
{"ChubbWeed"sv, ChubbWeed_H},
|
|
||||||
{"CineBoots"sv, CineBoots_H},
|
|
||||||
{"CineGeneral"sv, CineGeneral_H},
|
|
||||||
{"CineGun"sv, CineGun_H},
|
|
||||||
{"CineMorphball"sv, CineMorphball_H},
|
|
||||||
{"CineSuit"sv, CineSuit_H},
|
|
||||||
{"CineVisor"sv, CineVisor_H},
|
|
||||||
{"Crater"sv, Crater_H},
|
|
||||||
{"Crystallite"sv, Crystallite_H},
|
|
||||||
{"Drones"sv, Drones_H},
|
|
||||||
{"EliteSpacePirate"sv, EliteSpacePirate_H},
|
|
||||||
{"FireFlea"sv, FireFlea_H},
|
|
||||||
{"Flaaghra"sv, Flaaghra_H},
|
|
||||||
{"FlickerBat"sv, FlickerBat_H},
|
|
||||||
{"FlyingPirate"sv, FlyingPirate_H},
|
|
||||||
{"FrontEnd"sv, FrontEnd_H},
|
|
||||||
{"GagantuanBeatle"sv, GagantuanBeatle_H},
|
|
||||||
{"Gnats"sv, Gnats_H},
|
|
||||||
{"Gryzbee"sv, Gryzbee_H},
|
|
||||||
{"IceCrack"sv, IceCrack_H},
|
|
||||||
{"IceWorld"sv, IceWorld_H},
|
|
||||||
{"InjuredPirates"sv, InjuredPirates_H},
|
|
||||||
{"IntroBoss"sv, IntroBoss_H},
|
|
||||||
{"IntroWorld"sv, IntroWorld_H},
|
|
||||||
{"JellyZap"sv, JellyZap_H},
|
|
||||||
{"LavaWorld"sv, LavaWorld_H},
|
|
||||||
{"Magdolite"sv, Magdolite_H},
|
|
||||||
{"Metaree"sv, Metaree_H},
|
|
||||||
{"MetroidPrime"sv, MetroidPrime_H},
|
|
||||||
{"Metroid"sv, Metroid_H},
|
|
||||||
{"MinesWorld"sv, MinesWorld_H},
|
|
||||||
{"MiscSamus"sv, MiscSamus_H},
|
|
||||||
{"Misc"sv, Misc_H},
|
|
||||||
{"OmegaPirate"sv, OmegaPirate_H},
|
|
||||||
{"OverWorld"sv, OverWorld_H},
|
|
||||||
{"Parasite"sv, Parasite_H},
|
|
||||||
{"Phazon"sv, Phazon_H},
|
|
||||||
{"PhazonGun"sv, PhazonGun_H},
|
|
||||||
{"PuddleSpore"sv, PuddleSpore_H},
|
|
||||||
{"PuddleToad"sv, PuddleToad_H},
|
|
||||||
{"Puffer"sv, Puffer_H},
|
|
||||||
{"ReactorDoor"sv, ReactorDoor_H},
|
|
||||||
{"Ridley"sv, Ridley_H},
|
|
||||||
{"Ripper"sv, Ripper_H},
|
|
||||||
{"RuinsWorld"sv, RuinsWorld_H},
|
|
||||||
{"SamusShip"sv, SamusShip_H},
|
|
||||||
{"Scarab"sv, Scarab_H},
|
|
||||||
{"Seedling"sv, Seedling_H},
|
|
||||||
{"SheeGoth"sv, SheeGoth_H},
|
|
||||||
{"SnakeWeed"sv, SnakeWeed_H},
|
|
||||||
{"Sova"sv, Sova_H},
|
|
||||||
{"SpacePirate"sv, SpacePirate_H},
|
|
||||||
{"SpankWeed"sv, SpankWeed_H},
|
|
||||||
{"Thardus"sv, Thardus_H},
|
|
||||||
{"TheEnd"sv, TheEnd_H},
|
|
||||||
{"Torobyte"sv, Torobyte_H},
|
|
||||||
{"Triclops"sv, Triclops_H},
|
|
||||||
{"Turret"sv, Turret_H},
|
|
||||||
{"UI"sv, UI_H},
|
|
||||||
{"WarWasp"sv, WarWasp_H},
|
|
||||||
{"Weapons"sv, Weapons_H},
|
|
||||||
{"ZZZ"sv, ZZZ_H},
|
|
||||||
{"Zoomer"sv, Zoomer_H},
|
|
||||||
{"lumigek"sv, lumigek_H},
|
|
||||||
{"test"sv, test_H}};
|
|
||||||
|
|
||||||
bool AGSC::Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& dir) {
|
|
||||||
dir.makeDirChain(true);
|
|
||||||
|
|
||||||
Header head;
|
|
||||||
head.read(rs);
|
|
||||||
|
|
||||||
uint32_t poolLen = rs.readUint32Big();
|
|
||||||
auto pool = rs.readUBytes(poolLen);
|
|
||||||
|
|
||||||
uint32_t projLen = rs.readUint32Big();
|
|
||||||
auto proj = rs.readUBytes(projLen);
|
|
||||||
|
|
||||||
uint32_t sampLen = rs.readUint32Big();
|
|
||||||
auto samp = rs.readUBytes(sampLen);
|
|
||||||
|
|
||||||
uint32_t sdirLen = rs.readUint32Big();
|
|
||||||
auto sdir = rs.readUBytes(sdirLen);
|
|
||||||
|
|
||||||
amuse::AudioGroupData data(proj.get(), projLen, pool.get(), poolLen, sdir.get(), sdirLen, samp.get(), sampLen,
|
|
||||||
amuse::GCNDataTag{});
|
|
||||||
|
|
||||||
/* Load into amuse representation */
|
|
||||||
amuse::ProjectDatabase projDb;
|
|
||||||
projDb.setIdDatabases();
|
|
||||||
amuse::AudioGroupDatabase group(data);
|
|
||||||
group.setGroupPath(dir.getAbsolutePath());
|
|
||||||
|
|
||||||
/* Extract samples */
|
|
||||||
group.getSdir().extractAllCompressed(dir.getAbsolutePath(), data.getSamp());
|
|
||||||
|
|
||||||
/* Import C headers */
|
|
||||||
auto search = std::lower_bound(std::cbegin(Headers), std::cend(Headers), head.groupName,
|
|
||||||
[](const auto& a, const auto& b) { return a.first < b; });
|
|
||||||
if (search != std::cend(Headers) && search->first == head.groupName)
|
|
||||||
group.importCHeader((char*)search->second);
|
|
||||||
|
|
||||||
/* Write out project/pool */
|
|
||||||
{
|
|
||||||
auto projd = group.getProj().toYAML();
|
|
||||||
athena::io::FileWriter fo(hecl::ProjectPath(dir, "!project.yaml").getAbsolutePath());
|
|
||||||
if (fo.hasError())
|
|
||||||
return false;
|
|
||||||
fo.writeUBytes(projd.data(), projd.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto poold = group.getPool().toYAML();
|
|
||||||
athena::io::FileWriter fo(hecl::ProjectPath(dir, "!pool.yaml").getAbsolutePath());
|
|
||||||
if (fo.hasError())
|
|
||||||
return false;
|
|
||||||
fo.writeUBytes(poold.data(), poold.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::atomic_bool DidCook = {false};
|
|
||||||
|
|
||||||
bool AGSC::Cook(const hecl::ProjectPath& dir, const hecl::ProjectPath& refOutPath) {
|
|
||||||
/* This will cook all AGSCs in the local directory, ensuring unique ObjectIDs
|
|
||||||
* across all Amuse subprojects */
|
|
||||||
if (DidCook.exchange(true))
|
|
||||||
return true; /* We've already cooked all AGSCs */
|
|
||||||
|
|
||||||
amuse::ProjectDatabase projDb;
|
|
||||||
projDb.setIdDatabases();
|
|
||||||
|
|
||||||
auto parentDir = dir.getParentPath();
|
|
||||||
auto outParentDir = refOutPath.getParentPath();
|
|
||||||
for (const auto& ent : dir.getParentPath().enumerateDir()) {
|
|
||||||
if (!ent.m_isDir)
|
|
||||||
continue;
|
|
||||||
hecl::ProjectPath path(parentDir, ent.m_name);
|
|
||||||
if (IsPathAudioGroup(path)) {
|
|
||||||
hecl::ProjectPath outPath(outParentDir, ent.m_name);
|
|
||||||
athena::io::FileWriter w(outPath.getAbsolutePath());
|
|
||||||
if (w.hasError())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Header head;
|
|
||||||
head.audioDir = "Audio/"sv;
|
|
||||||
auto lastComp = path.getLastComponent();
|
|
||||||
auto str = fmt::format(FMT_STRING("_{:8X}"), path.parsedHash32());
|
|
||||||
auto it = lastComp.rfind(str);
|
|
||||||
if (it != std::string_view::npos) {
|
|
||||||
lastComp = lastComp.substr(0, it);
|
|
||||||
}
|
|
||||||
head.groupName = lastComp;
|
|
||||||
head.write(w);
|
|
||||||
|
|
||||||
amuse::AudioGroupDatabase group(path.getAbsolutePath());
|
|
||||||
|
|
||||||
auto proj = group.getProj().toGCNData(group.getPool(), group.getSdir());
|
|
||||||
auto pool = group.getPool().toData<athena::Endian::Big>();
|
|
||||||
auto sdirSamp = group.getSdir().toGCNData(group);
|
|
||||||
|
|
||||||
w.writeUint32Big(pool.size());
|
|
||||||
w.writeUBytes(pool.data(), pool.size());
|
|
||||||
|
|
||||||
w.writeUint32Big(proj.size());
|
|
||||||
w.writeUBytes(proj.data(), proj.size());
|
|
||||||
|
|
||||||
w.writeUint32Big(sdirSamp.second.size());
|
|
||||||
w.writeUBytes(sdirSamp.second.data(), sdirSamp.second.size());
|
|
||||||
|
|
||||||
w.writeUint32Big(sdirSamp.first.size());
|
|
||||||
w.writeUBytes(sdirSamp.first.data(), sdirSamp.first.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,19 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DNAMP1.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
class AGSC {
|
|
||||||
public:
|
|
||||||
struct Header : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
String<-1> audioDir;
|
|
||||||
String<-1> groupName;
|
|
||||||
};
|
|
||||||
static bool Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath);
|
|
||||||
static bool Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,420 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/ANCS.hpp"
|
|
||||||
#include "CMDLMaterials.hpp"
|
|
||||||
#include "CINF.hpp"
|
|
||||||
#include "CSKR.hpp"
|
|
||||||
#include "ANIM.hpp"
|
|
||||||
#include "EVNT.hpp"
|
|
||||||
#include "athena/FileReader.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
struct ANCS : BigDNA {
|
|
||||||
using CINFType = CINF;
|
|
||||||
using CSKRType = CSKR;
|
|
||||||
using ANIMType = ANIM;
|
|
||||||
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint16> version;
|
|
||||||
|
|
||||||
struct CharacterSet : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint16> version;
|
|
||||||
Value<atUint32> characterCount;
|
|
||||||
struct CharacterInfo : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Delete expl;
|
|
||||||
|
|
||||||
atUint32 idx;
|
|
||||||
std::string name;
|
|
||||||
UniqueID32 cmdl;
|
|
||||||
UniqueID32 cskr;
|
|
||||||
UniqueID32 cinf;
|
|
||||||
|
|
||||||
struct Animation : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> animIdx;
|
|
||||||
String<-1> strA;
|
|
||||||
String<-1> strB;
|
|
||||||
};
|
|
||||||
std::vector<Animation> animations;
|
|
||||||
|
|
||||||
struct PASDatabase : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> magic;
|
|
||||||
Value<atUint32> animStateCount;
|
|
||||||
Value<atUint32> defaultState;
|
|
||||||
struct AnimState : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Delete expl;
|
|
||||||
atUint32 id;
|
|
||||||
|
|
||||||
struct ParmInfo : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Delete expl;
|
|
||||||
enum class DataType { Int32 = 0, UInt32 = 1, Float = 2, Bool = 3, Enum = 4 };
|
|
||||||
union Parm {
|
|
||||||
atInt32 int32;
|
|
||||||
atUint32 uint32;
|
|
||||||
float float32;
|
|
||||||
bool bool1;
|
|
||||||
Parm() : int32(0) {}
|
|
||||||
Parm(atInt32 val) : int32(val) {}
|
|
||||||
Parm(atUint32 val) : uint32(val) {}
|
|
||||||
Parm(float val) : float32(val) {}
|
|
||||||
Parm(bool val) : bool1(val) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
atUint32 parmType;
|
|
||||||
atUint32 weightFunction;
|
|
||||||
float weight;
|
|
||||||
Parm range[2];
|
|
||||||
};
|
|
||||||
std::vector<ParmInfo> parmInfos;
|
|
||||||
|
|
||||||
struct AnimInfo {
|
|
||||||
atUint32 id;
|
|
||||||
std::vector<ParmInfo::Parm> parmVals;
|
|
||||||
};
|
|
||||||
std::vector<AnimInfo> animInfos;
|
|
||||||
};
|
|
||||||
Vector<AnimState, AT_DNA_COUNT(animStateCount)> animStates;
|
|
||||||
} pasDatabase;
|
|
||||||
|
|
||||||
struct ParticleResData {
|
|
||||||
std::vector<UniqueID32> part;
|
|
||||||
std::vector<UniqueID32> swhc;
|
|
||||||
std::vector<UniqueID32> unk;
|
|
||||||
std::vector<UniqueID32> elsc;
|
|
||||||
} partResData;
|
|
||||||
|
|
||||||
atUint32 unk1 = 0;
|
|
||||||
|
|
||||||
struct ActionAABB : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atVec3f> aabb[2];
|
|
||||||
};
|
|
||||||
std::vector<ActionAABB> animAABBs;
|
|
||||||
|
|
||||||
struct Effect : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> compCount;
|
|
||||||
struct EffectComponent : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
DNAFourCC type;
|
|
||||||
UniqueID32 id;
|
|
||||||
String<-1> locator;
|
|
||||||
Value<float> scale;
|
|
||||||
Value<atUint32> parentMode;
|
|
||||||
Value<atUint32> flags;
|
|
||||||
};
|
|
||||||
Vector<EffectComponent, AT_DNA_COUNT(compCount)> comps;
|
|
||||||
};
|
|
||||||
std::vector<Effect> effects;
|
|
||||||
|
|
||||||
UniqueID32Zero cmdlIce;
|
|
||||||
UniqueID32Zero cskrIce;
|
|
||||||
|
|
||||||
std::vector<atUint32> animIdxs;
|
|
||||||
};
|
|
||||||
Vector<CharacterInfo, AT_DNA_COUNT(characterCount)> characters;
|
|
||||||
} characterSet;
|
|
||||||
|
|
||||||
struct AnimationSet : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Delete expl;
|
|
||||||
|
|
||||||
struct MetaAnimPrimitive;
|
|
||||||
struct IMetaAnim : BigDNAVYaml {
|
|
||||||
Delete expl;
|
|
||||||
enum class Type { Primitive = 0, Blend = 1, PhaseBlend = 2, Random = 3, Sequence = 4 } m_type;
|
|
||||||
const char* m_typeStr;
|
|
||||||
IMetaAnim(Type type, const char* typeStr) : m_type(type), m_typeStr(typeStr) {}
|
|
||||||
virtual void gatherPrimitives(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) = 0;
|
|
||||||
virtual bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) = 0;
|
|
||||||
};
|
|
||||||
struct MetaAnimFactory : BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
|
||||||
std::unique_ptr<IMetaAnim> m_anim;
|
|
||||||
};
|
|
||||||
struct MetaAnimPrimitive : IMetaAnim {
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
MetaAnimPrimitive() : IMetaAnim(Type::Primitive, "Primitive") {}
|
|
||||||
|
|
||||||
UniqueID32 animId;
|
|
||||||
Value<atUint32> animIdx;
|
|
||||||
String<-1> animName;
|
|
||||||
Value<float> unk1;
|
|
||||||
Value<atUint32> unk2;
|
|
||||||
|
|
||||||
void gatherPrimitives(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) override;
|
|
||||||
|
|
||||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) override {
|
|
||||||
return func(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct MetaAnimBlend : IMetaAnim {
|
|
||||||
MetaAnimBlend() : IMetaAnim(Type::Blend, "Blend") {}
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
MetaAnimFactory animA;
|
|
||||||
MetaAnimFactory animB;
|
|
||||||
Value<float> unkFloat;
|
|
||||||
Value<atUint8> unk;
|
|
||||||
|
|
||||||
void gatherPrimitives(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) override {
|
|
||||||
animA.m_anim->gatherPrimitives(pakRouter, out);
|
|
||||||
animB.m_anim->gatherPrimitives(pakRouter, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) override {
|
|
||||||
if (!animA.m_anim->enumeratePrimitives(func))
|
|
||||||
return false;
|
|
||||||
if (!animB.m_anim->enumeratePrimitives(func))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct MetaAnimPhaseBlend : IMetaAnim {
|
|
||||||
MetaAnimPhaseBlend() : IMetaAnim(Type::PhaseBlend, "PhaseBlend") {}
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
MetaAnimFactory animA;
|
|
||||||
MetaAnimFactory animB;
|
|
||||||
Value<float> unkFloat;
|
|
||||||
Value<atUint8> unk;
|
|
||||||
|
|
||||||
void gatherPrimitives(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) override {
|
|
||||||
animA.m_anim->gatherPrimitives(pakRouter, out);
|
|
||||||
animB.m_anim->gatherPrimitives(pakRouter, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) override {
|
|
||||||
if (!animA.m_anim->enumeratePrimitives(func))
|
|
||||||
return false;
|
|
||||||
if (!animB.m_anim->enumeratePrimitives(func))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct MetaAnimRandom : IMetaAnim {
|
|
||||||
MetaAnimRandom() : IMetaAnim(Type::Random, "Random") {}
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<atUint32> animCount;
|
|
||||||
struct Child : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
MetaAnimFactory anim;
|
|
||||||
Value<atUint32> probability;
|
|
||||||
};
|
|
||||||
Vector<Child, AT_DNA_COUNT(animCount)> children;
|
|
||||||
|
|
||||||
void gatherPrimitives(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) override {
|
|
||||||
for (const auto& child : children)
|
|
||||||
child.anim.m_anim->gatherPrimitives(pakRouter, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) override {
|
|
||||||
for (auto& child : children)
|
|
||||||
if (!child.anim.m_anim->enumeratePrimitives(func))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct MetaAnimSequence : IMetaAnim {
|
|
||||||
MetaAnimSequence() : IMetaAnim(Type::Sequence, "Sequence") {}
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<atUint32> animCount;
|
|
||||||
Vector<MetaAnimFactory, AT_DNA_COUNT(animCount)> children;
|
|
||||||
|
|
||||||
void gatherPrimitives(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) override {
|
|
||||||
for (const auto& child : children)
|
|
||||||
child.m_anim->gatherPrimitives(pakRouter, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) override {
|
|
||||||
for (auto& child : children)
|
|
||||||
if (!child.m_anim->enumeratePrimitives(func))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Animation : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
String<-1> name;
|
|
||||||
MetaAnimFactory metaAnim;
|
|
||||||
};
|
|
||||||
std::vector<Animation> animations;
|
|
||||||
|
|
||||||
struct IMetaTrans : BigDNAVYaml {
|
|
||||||
Delete expl;
|
|
||||||
enum class Type {
|
|
||||||
MetaAnim = 0,
|
|
||||||
Trans = 1,
|
|
||||||
PhaseTrans = 2,
|
|
||||||
NoTrans = 3,
|
|
||||||
} m_type;
|
|
||||||
const char* m_typeStr;
|
|
||||||
IMetaTrans(Type type, const char* typeStr) : m_type(type), m_typeStr(typeStr) {}
|
|
||||||
virtual void gatherPrimitives(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) {}
|
|
||||||
virtual bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) { return true; }
|
|
||||||
};
|
|
||||||
struct MetaTransFactory : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Delete expl;
|
|
||||||
std::unique_ptr<IMetaTrans> m_trans;
|
|
||||||
};
|
|
||||||
struct MetaTransMetaAnim : IMetaTrans {
|
|
||||||
MetaTransMetaAnim() : IMetaTrans(Type::MetaAnim, "MetaAnim") {}
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
MetaAnimFactory anim;
|
|
||||||
|
|
||||||
void gatherPrimitives(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) override {
|
|
||||||
anim.m_anim->gatherPrimitives(pakRouter, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool enumeratePrimitives(const std::function<bool(MetaAnimPrimitive& prim)>& func) override {
|
|
||||||
return anim.m_anim->enumeratePrimitives(func);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct MetaTransTrans : IMetaTrans {
|
|
||||||
MetaTransTrans() : IMetaTrans(Type::Trans, "Trans") {}
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<float> transDurTime;
|
|
||||||
Value<atUint32> transDurTimeMode;
|
|
||||||
Value<bool> unk2;
|
|
||||||
Value<bool> runA;
|
|
||||||
Value<atUint32> flags;
|
|
||||||
};
|
|
||||||
struct MetaTransPhaseTrans : IMetaTrans {
|
|
||||||
MetaTransPhaseTrans() : IMetaTrans(Type::PhaseTrans, "PhaseTrans") {}
|
|
||||||
AT_DECL_DNA_YAMLV
|
|
||||||
Value<float> transDurTime;
|
|
||||||
Value<atUint32> transDurTimeMode;
|
|
||||||
Value<bool> unk2;
|
|
||||||
Value<bool> runA;
|
|
||||||
Value<atUint32> flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Transition : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> unk;
|
|
||||||
Value<atUint32> animIdxA;
|
|
||||||
Value<atUint32> animIdxB;
|
|
||||||
MetaTransFactory metaTrans;
|
|
||||||
};
|
|
||||||
std::vector<Transition> transitions;
|
|
||||||
MetaTransFactory defaultTransition;
|
|
||||||
|
|
||||||
struct AdditiveAnimationInfo : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> animIdx;
|
|
||||||
Value<float> fadeInDur;
|
|
||||||
Value<float> fadeOutDur;
|
|
||||||
};
|
|
||||||
std::vector<AdditiveAnimationInfo> additiveAnims;
|
|
||||||
|
|
||||||
float additiveDefaultFadeInDur = 0.0;
|
|
||||||
float additiveDefaultFadeOutDur = 0.0;
|
|
||||||
|
|
||||||
struct HalfTransition : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint32> animIdx;
|
|
||||||
MetaTransFactory metaTrans;
|
|
||||||
};
|
|
||||||
std::vector<HalfTransition> halfTransitions;
|
|
||||||
|
|
||||||
struct AnimationResources : BigDNA {
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
UniqueID32 animId;
|
|
||||||
UniqueID32 evntId;
|
|
||||||
};
|
|
||||||
std::vector<AnimationResources> animResources;
|
|
||||||
} animationSet;
|
|
||||||
|
|
||||||
void getCharacterResInfo(std::vector<DNAANCS::CharacterResInfo<UniqueID32>>& out) const {
|
|
||||||
out.clear();
|
|
||||||
out.reserve(characterSet.characters.size());
|
|
||||||
for (const CharacterSet::CharacterInfo& ci : characterSet.characters) {
|
|
||||||
out.emplace_back();
|
|
||||||
DNAANCS::CharacterResInfo<UniqueID32>& chOut = out.back();
|
|
||||||
chOut.name = ci.name;
|
|
||||||
chOut.cmdl = ci.cmdl;
|
|
||||||
chOut.cskr = ci.cskr;
|
|
||||||
chOut.cinf = ci.cinf;
|
|
||||||
|
|
||||||
if (ci.cmdlIce.isValid())
|
|
||||||
chOut.overlays.emplace_back("ICE", std::make_pair(ci.cmdlIce, ci.cskrIce));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void getAnimationResInfo(PAKRouter<PAKBridge>* pakRouter,
|
|
||||||
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>>& out) const {
|
|
||||||
out.clear();
|
|
||||||
for (const AnimationSet::Animation& ai : animationSet.animations)
|
|
||||||
if (AnimationSet::IMetaAnim* anim = ai.metaAnim.m_anim.get())
|
|
||||||
anim->gatherPrimitives(pakRouter, out);
|
|
||||||
for (const AnimationSet::Transition& ti : animationSet.transitions)
|
|
||||||
if (AnimationSet::IMetaTrans* trans = ti.metaTrans.m_trans.get())
|
|
||||||
trans->gatherPrimitives(pakRouter, out);
|
|
||||||
if (AnimationSet::IMetaTrans* trans = animationSet.defaultTransition.m_trans.get())
|
|
||||||
trans->gatherPrimitives(pakRouter, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void enumeratePrimitives(const std::function<bool(AnimationSet::MetaAnimPrimitive& prim)>& func) {
|
|
||||||
for (const AnimationSet::Animation& ai : animationSet.animations)
|
|
||||||
if (AnimationSet::IMetaAnim* anim = ai.metaAnim.m_anim.get())
|
|
||||||
anim->enumeratePrimitives(func);
|
|
||||||
for (const AnimationSet::Transition& ti : animationSet.transitions)
|
|
||||||
if (AnimationSet::IMetaTrans* trans = ti.metaTrans.m_trans.get())
|
|
||||||
trans->enumeratePrimitives(func);
|
|
||||||
if (AnimationSet::IMetaTrans* trans = animationSet.defaultTransition.m_trans.get())
|
|
||||||
trans->enumeratePrimitives(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut, int charIdx) const {
|
|
||||||
auto doCi = [&](const CharacterSet::CharacterInfo& ci) {
|
|
||||||
for (const auto& id : ci.partResData.part)
|
|
||||||
g_curSpec->flattenDependencies(id, pathsOut);
|
|
||||||
for (const auto& id : ci.partResData.swhc)
|
|
||||||
g_curSpec->flattenDependencies(id, pathsOut);
|
|
||||||
for (const auto& id : ci.partResData.unk)
|
|
||||||
g_curSpec->flattenDependencies(id, pathsOut);
|
|
||||||
for (const auto& id : ci.partResData.elsc)
|
|
||||||
g_curSpec->flattenDependencies(id, pathsOut);
|
|
||||||
};
|
|
||||||
if (charIdx < 0)
|
|
||||||
for (const CharacterSet::CharacterInfo& ci : characterSet.characters)
|
|
||||||
doCi(ci);
|
|
||||||
else if (charIdx < characterSet.characters.size())
|
|
||||||
doCi(characterSet.characters[charIdx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok,
|
|
||||||
std::function<void(const char*)> fileChanged);
|
|
||||||
|
|
||||||
static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNAANCS::Actor& actor);
|
|
||||||
|
|
||||||
static bool CookCSKR(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNAANCS::Actor& actor,
|
|
||||||
const std::function<bool(const hecl::ProjectPath& modelPath)>& modelCookFunc);
|
|
||||||
static bool CookCSKRPC(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNAANCS::Actor& actor,
|
|
||||||
const std::function<bool(const hecl::ProjectPath& modelPath)>& modelCookFunc);
|
|
||||||
|
|
||||||
static bool CookANIM(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNAANCS::Actor& actor,
|
|
||||||
hecl::blender::DataStream& ds, bool pc);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,624 +0,0 @@
|
||||||
#include "ANIM.hpp"
|
|
||||||
#include "zeus/CVector3f.hpp"
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
using ANIMOutStream = hecl::blender::ANIMOutStream;
|
|
||||||
|
|
||||||
void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig) const {
|
|
||||||
os.format(FMT_STRING("act.hecl_fps = round({})\n"
|
|
||||||
"act.hecl_looping = {}\n"),
|
|
||||||
(1.0f / mainInterval), looping ? "True" : "False");
|
|
||||||
|
|
||||||
auto kit = chanKeys.begin();
|
|
||||||
|
|
||||||
std::vector<zeus::CQuaternion> fixedRotKeys;
|
|
||||||
std::vector<zeus::CVector3f> fixedTransKeys;
|
|
||||||
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
const std::string* bName = rig.getCINF().getBoneNameFromId(bone.first);
|
|
||||||
if (!bName) {
|
|
||||||
++kit;
|
|
||||||
if (bone.second)
|
|
||||||
++kit;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
os.format(FMT_STRING("bone_string = '{}'\n"), *bName);
|
|
||||||
os << "action_group = act.groups.new(bone_string)\n"
|
|
||||||
"\n"
|
|
||||||
"rotCurves = []\n"
|
|
||||||
"rotCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=0, "
|
|
||||||
"action_group=bone_string))\n"
|
|
||||||
"rotCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=1, "
|
|
||||||
"action_group=bone_string))\n"
|
|
||||||
"rotCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=2, "
|
|
||||||
"action_group=bone_string))\n"
|
|
||||||
"rotCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=3, "
|
|
||||||
"action_group=bone_string))\n"
|
|
||||||
"\n";
|
|
||||||
|
|
||||||
if (bone.second)
|
|
||||||
os << "transCurves = []\n"
|
|
||||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=0, "
|
|
||||||
"action_group=bone_string))\n"
|
|
||||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=1, "
|
|
||||||
"action_group=bone_string))\n"
|
|
||||||
"transCurves.append(act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=2, "
|
|
||||||
"action_group=bone_string))\n"
|
|
||||||
"\n";
|
|
||||||
|
|
||||||
ANIMOutStream ao = os.beginANIMCurve();
|
|
||||||
|
|
||||||
{
|
|
||||||
const std::vector<DNAANIM::Value>& rotKeys = *kit++;
|
|
||||||
fixedRotKeys.clear();
|
|
||||||
fixedRotKeys.resize(rotKeys.size());
|
|
||||||
|
|
||||||
for (int c = 0; c < 4; ++c) {
|
|
||||||
size_t idx = 0;
|
|
||||||
for (const DNAANIM::Value& val : rotKeys)
|
|
||||||
fixedRotKeys[idx++][c] = val.simd[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (zeus::CQuaternion& rot : fixedRotKeys)
|
|
||||||
rot = rig.invertRotation(bone.first, rot);
|
|
||||||
|
|
||||||
for (int c = 0; c < 4; ++c) {
|
|
||||||
auto frameit = frames.begin();
|
|
||||||
ao.changeCurve(ANIMOutStream::CurveType::Rotate, c, rotKeys.size());
|
|
||||||
for (const zeus::CQuaternion& val : fixedRotKeys)
|
|
||||||
ao.write(*frameit++, val[c]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bone.second) {
|
|
||||||
const std::vector<DNAANIM::Value>& transKeys = *kit++;
|
|
||||||
fixedTransKeys.clear();
|
|
||||||
fixedTransKeys.resize(transKeys.size());
|
|
||||||
|
|
||||||
for (int c = 0; c < 3; ++c) {
|
|
||||||
size_t idx = 0;
|
|
||||||
for (const DNAANIM::Value& val : transKeys)
|
|
||||||
fixedTransKeys[idx++][c] = val.simd[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (zeus::CVector3f& t : fixedTransKeys)
|
|
||||||
t = rig.invertPosition(bone.first, t, true);
|
|
||||||
|
|
||||||
for (int c = 0; c < 3; ++c) {
|
|
||||||
auto frameit = frames.begin();
|
|
||||||
ao.changeCurve(ANIMOutStream::CurveType::Translate, c, fixedTransKeys.size());
|
|
||||||
for (const zeus::CVector3f& val : fixedTransKeys)
|
|
||||||
ao.write(*frameit++, val[c]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UniqueID32 ANIM::GetEVNTId(athena::io::IStreamReader& reader) {
|
|
||||||
atUint32 version = reader.readUint32Big();
|
|
||||||
switch (version) {
|
|
||||||
case 0: {
|
|
||||||
ANIM0 anim0;
|
|
||||||
anim0.read(reader);
|
|
||||||
return anim0.evnt;
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
reader.seek(4);
|
|
||||||
return reader.readUint32Big();
|
|
||||||
default:
|
|
||||||
Log.report(logvisor::Error, FMT_STRING("unrecognized ANIM version"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
|
|
||||||
atUint32 version = reader.readUint32Big();
|
|
||||||
switch (version) {
|
|
||||||
case 0:
|
|
||||||
m_anim = std::make_unique<ANIM0>();
|
|
||||||
m_anim->read(reader);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
m_anim = std::make_unique<ANIM2>(false);
|
|
||||||
m_anim->read(reader);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
m_anim = std::make_unique<ANIM2>(true);
|
|
||||||
m_anim->read(reader);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.report(logvisor::Error, FMT_STRING("unrecognized ANIM version"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
|
|
||||||
writer.writeUint32Big(m_anim->m_version);
|
|
||||||
m_anim->write(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
|
|
||||||
s += 4;
|
|
||||||
m_anim->binarySize(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view ANIM::ANIM0::DNAType() { return "ANIM0"sv; }
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::ANIM0::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
|
|
||||||
Header head;
|
|
||||||
head.read(reader);
|
|
||||||
mainInterval = head.interval;
|
|
||||||
|
|
||||||
frames.clear();
|
|
||||||
frames.reserve(head.keyCount);
|
|
||||||
for (size_t k = 0; k < head.keyCount; ++k)
|
|
||||||
frames.push_back(k);
|
|
||||||
|
|
||||||
std::map<atUint8, atUint32> boneMap;
|
|
||||||
for (size_t b = 0; b < head.boneSlotCount; ++b) {
|
|
||||||
atUint8 idx = reader.readUByte();
|
|
||||||
if (idx == 0xff)
|
|
||||||
continue;
|
|
||||||
boneMap[idx] = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
atUint32 boneCount = reader.readUint32Big();
|
|
||||||
bones.clear();
|
|
||||||
bones.reserve(boneCount);
|
|
||||||
channels.clear();
|
|
||||||
for (size_t b = 0; b < boneCount; ++b) {
|
|
||||||
bones.emplace_back(boneMap[b], false);
|
|
||||||
atUint8 idx = reader.readUByte();
|
|
||||||
channels.emplace_back();
|
|
||||||
DNAANIM::Channel& chan = channels.back();
|
|
||||||
chan.type = DNAANIM::Channel::Type::Rotation;
|
|
||||||
if (idx != 0xff) {
|
|
||||||
bones.back().second = true;
|
|
||||||
channels.emplace_back();
|
|
||||||
DNAANIM::Channel& chan = channels.back();
|
|
||||||
chan.type = DNAANIM::Channel::Type::Translation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.readUint32Big();
|
|
||||||
chanKeys.clear();
|
|
||||||
chanKeys.reserve(channels.size());
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
chanKeys.emplace_back();
|
|
||||||
std::vector<DNAANIM::Value>& keys = chanKeys.back();
|
|
||||||
for (size_t k = 0; k < head.keyCount; ++k)
|
|
||||||
keys.emplace_back(reader.readVec4fBig());
|
|
||||||
|
|
||||||
if (bone.second)
|
|
||||||
chanKeys.emplace_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.readUint32Big();
|
|
||||||
auto kit = chanKeys.begin();
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
++kit;
|
|
||||||
if (bone.second) {
|
|
||||||
std::vector<DNAANIM::Value>& keys = *kit++;
|
|
||||||
for (size_t k = 0; k < head.keyCount; ++k)
|
|
||||||
keys.emplace_back(reader.readVec3fBig());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
evnt.read(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::ANIM0::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
|
|
||||||
Header head;
|
|
||||||
head.unk0 = 0;
|
|
||||||
head.unk1 = 0;
|
|
||||||
head.unk2 = 0;
|
|
||||||
head.keyCount = frames.size();
|
|
||||||
head.duration = head.keyCount * mainInterval;
|
|
||||||
head.interval = mainInterval;
|
|
||||||
|
|
||||||
atUint32 maxId = 0;
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones)
|
|
||||||
maxId = std::max(maxId, bone.first);
|
|
||||||
head.boneSlotCount = maxId + 1;
|
|
||||||
head.write(writer);
|
|
||||||
|
|
||||||
for (size_t s = 0; s < head.boneSlotCount; ++s) {
|
|
||||||
size_t boneIdx = 0;
|
|
||||||
bool found = false;
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
if (s == bone.first) {
|
|
||||||
writer.writeUByte(boneIdx);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++boneIdx;
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
writer.writeUByte(0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writeUint32Big(bones.size());
|
|
||||||
size_t boneIdx = 0;
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
if (bone.second)
|
|
||||||
writer.writeUByte(boneIdx);
|
|
||||||
else
|
|
||||||
writer.writeUByte(0xff);
|
|
||||||
++boneIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writeUint32Big(bones.size() * head.keyCount);
|
|
||||||
auto cit = chanKeys.begin();
|
|
||||||
atUint32 transKeyCount = 0;
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
const std::vector<DNAANIM::Value>& keys = *cit++;
|
|
||||||
auto kit = keys.begin();
|
|
||||||
for (size_t k = 0; k < head.keyCount; ++k)
|
|
||||||
writer.writeVec4fBig(atVec4f{(*kit++).simd});
|
|
||||||
if (bone.second) {
|
|
||||||
transKeyCount += head.keyCount;
|
|
||||||
++cit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writeUint32Big(transKeyCount);
|
|
||||||
cit = chanKeys.begin();
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
++cit;
|
|
||||||
if (bone.second) {
|
|
||||||
const std::vector<DNAANIM::Value>& keys = *cit++;
|
|
||||||
auto kit = keys.begin();
|
|
||||||
for (size_t k = 0; k < head.keyCount; ++k)
|
|
||||||
writer.writeVec3fBig(atVec3f{(*kit++).simd});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
evnt.write(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::ANIM0::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
|
|
||||||
Header head;
|
|
||||||
|
|
||||||
atUint32 maxId = 0;
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones)
|
|
||||||
maxId = std::max(maxId, bone.first);
|
|
||||||
|
|
||||||
head.binarySize(__isz);
|
|
||||||
__isz += maxId + 1;
|
|
||||||
__isz += bones.size() + 4;
|
|
||||||
|
|
||||||
__isz += 8;
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
__isz += head.keyCount * 16;
|
|
||||||
if (bone.second)
|
|
||||||
__isz += head.keyCount * 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
__isz += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view ANIM::ANIM2::DNAType() { return "ANIM2"sv; }
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::ANIM2::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
|
|
||||||
Header head;
|
|
||||||
head.read(reader);
|
|
||||||
evnt = head.evnt;
|
|
||||||
mainInterval = head.interval;
|
|
||||||
looping = bool(head.looping);
|
|
||||||
|
|
||||||
WordBitmap keyBmp;
|
|
||||||
keyBmp.read(reader, head.keyBitmapBitCount);
|
|
||||||
frames.clear();
|
|
||||||
atUint32 frameAccum = 0;
|
|
||||||
for (bool bit : keyBmp) {
|
|
||||||
if (bit)
|
|
||||||
frames.push_back(frameAccum);
|
|
||||||
++frameAccum;
|
|
||||||
}
|
|
||||||
reader.seek(8);
|
|
||||||
|
|
||||||
bones.clear();
|
|
||||||
bones.reserve(head.boneChannelCount);
|
|
||||||
channels.clear();
|
|
||||||
channels.reserve(head.boneChannelCount);
|
|
||||||
atUint32 keyframeCount = 0;
|
|
||||||
|
|
||||||
if (m_version == 3) {
|
|
||||||
for (size_t b = 0; b < head.boneChannelCount; ++b) {
|
|
||||||
ChannelDescPC desc;
|
|
||||||
desc.read(reader);
|
|
||||||
bones.emplace_back(desc.id, desc.keyCount2 != 0);
|
|
||||||
|
|
||||||
if (desc.keyCount1) {
|
|
||||||
channels.emplace_back();
|
|
||||||
DNAANIM::Channel& chan = channels.back();
|
|
||||||
chan.type = DNAANIM::Channel::Type::Rotation;
|
|
||||||
chan.id = desc.id;
|
|
||||||
chan.i[0] = atInt32(desc.QinitRX) >> 8;
|
|
||||||
chan.q[0] = desc.QinitRX & 0xff;
|
|
||||||
chan.i[1] = atInt32(desc.QinitRY) >> 8;
|
|
||||||
chan.q[1] = desc.QinitRY & 0xff;
|
|
||||||
chan.i[2] = atInt32(desc.QinitRZ) >> 8;
|
|
||||||
chan.q[2] = desc.QinitRZ & 0xff;
|
|
||||||
}
|
|
||||||
keyframeCount = std::max(keyframeCount, desc.keyCount1);
|
|
||||||
|
|
||||||
if (desc.keyCount2) {
|
|
||||||
channels.emplace_back();
|
|
||||||
DNAANIM::Channel& chan = channels.back();
|
|
||||||
chan.type = DNAANIM::Channel::Type::Translation;
|
|
||||||
chan.id = desc.id;
|
|
||||||
chan.i[0] = atInt32(desc.QinitTX) >> 8;
|
|
||||||
chan.q[0] = desc.QinitTX & 0xff;
|
|
||||||
chan.i[1] = atInt32(desc.QinitTY) >> 8;
|
|
||||||
chan.q[1] = desc.QinitTY & 0xff;
|
|
||||||
chan.i[2] = atInt32(desc.QinitTZ) >> 8;
|
|
||||||
chan.q[2] = desc.QinitTZ & 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (size_t b = 0; b < head.boneChannelCount; ++b) {
|
|
||||||
ChannelDesc desc;
|
|
||||||
desc.read(reader);
|
|
||||||
bones.emplace_back(desc.id, desc.keyCount2 != 0);
|
|
||||||
|
|
||||||
if (desc.keyCount1) {
|
|
||||||
channels.emplace_back();
|
|
||||||
DNAANIM::Channel& chan = channels.back();
|
|
||||||
chan.type = DNAANIM::Channel::Type::Rotation;
|
|
||||||
chan.id = desc.id;
|
|
||||||
chan.i[0] = desc.initRX;
|
|
||||||
chan.q[0] = desc.qRX;
|
|
||||||
chan.i[1] = desc.initRY;
|
|
||||||
chan.q[1] = desc.qRY;
|
|
||||||
chan.i[2] = desc.initRZ;
|
|
||||||
chan.q[2] = desc.qRZ;
|
|
||||||
}
|
|
||||||
keyframeCount = std::max(keyframeCount, atUint32(desc.keyCount1));
|
|
||||||
|
|
||||||
if (desc.keyCount2) {
|
|
||||||
channels.emplace_back();
|
|
||||||
DNAANIM::Channel& chan = channels.back();
|
|
||||||
chan.type = DNAANIM::Channel::Type::Translation;
|
|
||||||
chan.id = desc.id;
|
|
||||||
chan.i[0] = desc.initTX;
|
|
||||||
chan.q[0] = desc.qTX;
|
|
||||||
chan.i[1] = desc.initTY;
|
|
||||||
chan.q[1] = desc.qTY;
|
|
||||||
chan.i[2] = desc.initTZ;
|
|
||||||
chan.q[2] = desc.qTZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t bsSize = DNAANIM::ComputeBitstreamSize(keyframeCount, channels);
|
|
||||||
std::unique_ptr<atUint8[]> bsData = reader.readUBytes(bsSize);
|
|
||||||
DNAANIM::BitstreamReader bsReader;
|
|
||||||
chanKeys = bsReader.read(bsData.get(), keyframeCount, channels, head.rotDiv, head.translationMult, 0.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::ANIM2::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
|
|
||||||
Header head;
|
|
||||||
head.evnt = evnt;
|
|
||||||
head.unk0 = 1;
|
|
||||||
head.interval = mainInterval;
|
|
||||||
head.rootBoneId = 3;
|
|
||||||
head.looping = looping;
|
|
||||||
head.unk3 = 1;
|
|
||||||
|
|
||||||
WordBitmap keyBmp;
|
|
||||||
size_t frameCount = 0;
|
|
||||||
for (atUint32 frame : frames) {
|
|
||||||
if (!keyBmp.getBit(frame)) {
|
|
||||||
keyBmp.setBit(frame);
|
|
||||||
frameCount += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
head.keyBitmapBitCount = keyBmp.getBitCount();
|
|
||||||
head.duration = frames.back() * mainInterval;
|
|
||||||
head.boneChannelCount = bones.size();
|
|
||||||
|
|
||||||
size_t keyframeCount = frameCount - 1;
|
|
||||||
std::vector<DNAANIM::Channel> qChannels = channels;
|
|
||||||
DNAANIM::BitstreamWriter bsWriter;
|
|
||||||
size_t bsSize;
|
|
||||||
float scaleMult;
|
|
||||||
std::unique_ptr<atUint8[]> bsData =
|
|
||||||
bsWriter.write(chanKeys, keyframeCount, qChannels, m_version == 3 ? 0x7fffff : 0x7fff, head.rotDiv,
|
|
||||||
head.translationMult, scaleMult, bsSize);
|
|
||||||
|
|
||||||
/* Tally up buffer size */
|
|
||||||
size_t scratchSize = 0;
|
|
||||||
head.binarySize(scratchSize);
|
|
||||||
keyBmp.binarySize(scratchSize);
|
|
||||||
scratchSize += bsSize;
|
|
||||||
if (m_version == 3) {
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
ChannelDescPC desc;
|
|
||||||
desc.keyCount1 = keyframeCount;
|
|
||||||
if (bone.second)
|
|
||||||
desc.keyCount2 = keyframeCount;
|
|
||||||
desc.binarySize(scratchSize);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
ChannelDesc desc;
|
|
||||||
desc.keyCount1 = keyframeCount;
|
|
||||||
if (bone.second)
|
|
||||||
desc.keyCount2 = keyframeCount;
|
|
||||||
desc.binarySize(scratchSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
head.scratchSize = scratchSize;
|
|
||||||
|
|
||||||
head.write(writer);
|
|
||||||
keyBmp.write(writer);
|
|
||||||
writer.writeUint32Big(head.boneChannelCount);
|
|
||||||
writer.writeUint32Big(head.boneChannelCount);
|
|
||||||
auto cit = qChannels.begin();
|
|
||||||
|
|
||||||
if (m_version == 3) {
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
ChannelDescPC desc;
|
|
||||||
desc.id = bone.first;
|
|
||||||
DNAANIM::Channel& chan = *cit++;
|
|
||||||
desc.keyCount1 = keyframeCount;
|
|
||||||
desc.QinitRX = (chan.i[0] << 8) | chan.q[0];
|
|
||||||
desc.QinitRY = (chan.i[1] << 8) | chan.q[1];
|
|
||||||
desc.QinitRZ = (chan.i[2] << 8) | chan.q[2];
|
|
||||||
if (bone.second) {
|
|
||||||
DNAANIM::Channel& chan = *cit++;
|
|
||||||
desc.keyCount2 = keyframeCount;
|
|
||||||
desc.QinitTX = (chan.i[0] << 8) | chan.q[0];
|
|
||||||
desc.QinitTY = (chan.i[1] << 8) | chan.q[1];
|
|
||||||
desc.QinitTZ = (chan.i[2] << 8) | chan.q[2];
|
|
||||||
}
|
|
||||||
desc.write(writer);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
ChannelDesc desc;
|
|
||||||
desc.id = bone.first;
|
|
||||||
DNAANIM::Channel& chan = *cit++;
|
|
||||||
desc.keyCount1 = keyframeCount;
|
|
||||||
desc.initRX = chan.i[0];
|
|
||||||
desc.qRX = chan.q[0];
|
|
||||||
desc.initRY = chan.i[1];
|
|
||||||
desc.qRY = chan.q[1];
|
|
||||||
desc.initRZ = chan.i[2];
|
|
||||||
desc.qRZ = chan.q[2];
|
|
||||||
if (bone.second) {
|
|
||||||
DNAANIM::Channel& chan = *cit++;
|
|
||||||
desc.keyCount2 = keyframeCount;
|
|
||||||
desc.initTX = chan.i[0];
|
|
||||||
desc.qTX = chan.q[0];
|
|
||||||
desc.initTY = chan.i[1];
|
|
||||||
desc.qTY = chan.q[1];
|
|
||||||
desc.initTZ = chan.i[2];
|
|
||||||
desc.qTZ = chan.q[2];
|
|
||||||
}
|
|
||||||
desc.write(writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writeUBytes(bsData.get(), bsSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void ANIM::ANIM2::Enumerate<BigDNA::BinarySize>(size_t& __isz) {
|
|
||||||
Header head;
|
|
||||||
|
|
||||||
WordBitmap keyBmp;
|
|
||||||
for (atUint32 frame : frames)
|
|
||||||
keyBmp.setBit(frame);
|
|
||||||
|
|
||||||
head.binarySize(__isz);
|
|
||||||
keyBmp.binarySize(__isz);
|
|
||||||
__isz += 8;
|
|
||||||
if (m_version == 3) {
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
__isz += 24;
|
|
||||||
if (bone.second)
|
|
||||||
__isz += 12;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const std::pair<atUint32, bool>& bone : bones) {
|
|
||||||
__isz += 17;
|
|
||||||
if (bone.second)
|
|
||||||
__isz += 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__isz += DNAANIM::ComputeBitstreamSize(frames.size(), channels);
|
|
||||||
}
|
|
||||||
|
|
||||||
ANIM::ANIM(const BlenderAction& act, const std::unordered_map<std::string, atInt32>& idMap,
|
|
||||||
const DNAANIM::RigInverter<CINF>& rig, bool pc) {
|
|
||||||
m_anim = std::make_unique<ANIM2>(pc);
|
|
||||||
IANIM& newAnim = *m_anim;
|
|
||||||
newAnim.looping = act.looping;
|
|
||||||
|
|
||||||
newAnim.bones.reserve(act.channels.size());
|
|
||||||
size_t extChanCount = 0;
|
|
||||||
std::unordered_set<atInt32> addedBones;
|
|
||||||
addedBones.reserve(act.channels.size());
|
|
||||||
for (const BlenderAction::Channel& chan : act.channels) {
|
|
||||||
auto search = idMap.find(chan.boneName);
|
|
||||||
if (search == idMap.cend()) {
|
|
||||||
Log.report(logvisor::Warning, FMT_STRING("unable to find id for bone '{}'"), chan.boneName);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (addedBones.find(search->second) != addedBones.cend())
|
|
||||||
continue;
|
|
||||||
addedBones.insert(search->second);
|
|
||||||
|
|
||||||
extChanCount += std::max(zeus::PopCount(chan.attrMask), 2);
|
|
||||||
newAnim.bones.emplace_back(search->second, (chan.attrMask & 0x2) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
newAnim.frames.reserve(act.frames.size());
|
|
||||||
for (int32_t frame : act.frames)
|
|
||||||
newAnim.frames.push_back(frame);
|
|
||||||
|
|
||||||
newAnim.channels.reserve(extChanCount);
|
|
||||||
newAnim.chanKeys.reserve(extChanCount);
|
|
||||||
|
|
||||||
for (const BlenderAction::Channel& chan : act.channels) {
|
|
||||||
auto search = idMap.find(chan.boneName);
|
|
||||||
if (search == idMap.cend())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
newAnim.channels.emplace_back();
|
|
||||||
DNAANIM::Channel& newChan = newAnim.channels.back();
|
|
||||||
newChan.type = DNAANIM::Channel::Type::Rotation;
|
|
||||||
newChan.id = search->second;
|
|
||||||
|
|
||||||
newAnim.chanKeys.emplace_back();
|
|
||||||
std::vector<DNAANIM::Value>& rotVals = newAnim.chanKeys.back();
|
|
||||||
rotVals.reserve(chan.keys.size());
|
|
||||||
float sign = 0.f;
|
|
||||||
for (const BlenderAction::Channel::Key& key : chan.keys) {
|
|
||||||
zeus::CQuaternion q(key.rotation.val);
|
|
||||||
q = rig.restoreRotation(newChan.id, q);
|
|
||||||
if (sign == 0.f)
|
|
||||||
sign = q.w() < 0.f ? -1.f : 1.f;
|
|
||||||
q *= sign;
|
|
||||||
q.normalize();
|
|
||||||
rotVals.emplace_back(q.mSimd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chan.attrMask & 0x2) {
|
|
||||||
newAnim.channels.emplace_back();
|
|
||||||
DNAANIM::Channel& newChan = newAnim.channels.back();
|
|
||||||
newChan.type = DNAANIM::Channel::Type::Translation;
|
|
||||||
newChan.id = search->second;
|
|
||||||
|
|
||||||
newAnim.chanKeys.emplace_back();
|
|
||||||
std::vector<DNAANIM::Value>& transVals = newAnim.chanKeys.back();
|
|
||||||
transVals.reserve(chan.keys.size());
|
|
||||||
for (const BlenderAction::Channel::Key& key : chan.keys) {
|
|
||||||
zeus::CVector3f pos(key.position.val);
|
|
||||||
pos = rig.restorePosition(newChan.id, pos, true);
|
|
||||||
transVals.emplace_back(pos.mSimd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Retro's original data uses microsecond precision */
|
|
||||||
newAnim.mainInterval = std::trunc(act.interval * 1000000.0) / 1000000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,214 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DNAMP1.hpp"
|
|
||||||
#include "DataSpec/DNACommon/ANIM.hpp"
|
|
||||||
#include "DataSpec/DNACommon/RigInverter.hpp"
|
|
||||||
#include "CINF.hpp"
|
|
||||||
#include "EVNT.hpp"
|
|
||||||
#include "DataSpec/DNACommon/ANCS.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
struct ANIM : BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA
|
|
||||||
|
|
||||||
static UniqueID32 GetEVNTId(athena::io::IStreamReader& r);
|
|
||||||
|
|
||||||
struct IANIM : BigDNAV {
|
|
||||||
Delete expl;
|
|
||||||
atUint32 m_version;
|
|
||||||
IANIM(atUint32 version) : m_version(version) {}
|
|
||||||
|
|
||||||
std::vector<std::pair<atUint32, bool>> bones;
|
|
||||||
std::vector<atUint32> frames;
|
|
||||||
std::vector<DNAANIM::Channel> channels;
|
|
||||||
std::vector<std::vector<DNAANIM::Value>> chanKeys;
|
|
||||||
float mainInterval = 0.0;
|
|
||||||
UniqueID32Zero evnt;
|
|
||||||
bool looping = false;
|
|
||||||
|
|
||||||
void sendANIMToBlender(hecl::blender::PyOutStream&, const DNAANIM::RigInverter<CINF>& rig) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ANIM0 : IANIM {
|
|
||||||
AT_DECL_EXPLICIT_DNAV
|
|
||||||
ANIM0() : IANIM(0) {}
|
|
||||||
|
|
||||||
struct Header : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<float> duration;
|
|
||||||
Value<atUint32> unk0;
|
|
||||||
Value<float> interval;
|
|
||||||
Value<atUint32> unk1;
|
|
||||||
Value<atUint32> keyCount;
|
|
||||||
Value<atUint32> unk2;
|
|
||||||
Value<atUint32> boneSlotCount;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ANIM2 : IANIM {
|
|
||||||
AT_DECL_EXPLICIT_DNAV
|
|
||||||
ANIM2(bool pc) : IANIM(pc ? 3 : 2) {}
|
|
||||||
|
|
||||||
struct Header : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> scratchSize;
|
|
||||||
UniqueID32Zero evnt;
|
|
||||||
Value<atUint32> unk0 = 1;
|
|
||||||
Value<float> duration;
|
|
||||||
Value<float> interval;
|
|
||||||
Value<atUint32> rootBoneId = 3;
|
|
||||||
Value<atUint32> looping = 0;
|
|
||||||
Value<atUint32> rotDiv;
|
|
||||||
Value<float> translationMult;
|
|
||||||
Value<atUint32> boneChannelCount;
|
|
||||||
Value<atUint32> unk3;
|
|
||||||
Value<atUint32> keyBitmapBitCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ChannelDesc : BigDNA {
|
|
||||||
Delete expl;
|
|
||||||
Value<atUint32> id = 0;
|
|
||||||
Value<atUint16> keyCount1 = 0;
|
|
||||||
Value<atInt16> initRX = 0;
|
|
||||||
Value<atUint8> qRX = 0;
|
|
||||||
Value<atInt16> initRY = 0;
|
|
||||||
Value<atUint8> qRY = 0;
|
|
||||||
Value<atInt16> initRZ = 0;
|
|
||||||
Value<atUint8> qRZ = 0;
|
|
||||||
Value<atUint16> keyCount2 = 0;
|
|
||||||
Value<atInt16> initTX = 0;
|
|
||||||
Value<atUint8> qTX = 0;
|
|
||||||
Value<atInt16> initTY = 0;
|
|
||||||
Value<atUint8> qTY = 0;
|
|
||||||
Value<atInt16> initTZ = 0;
|
|
||||||
Value<atUint8> qTZ = 0;
|
|
||||||
|
|
||||||
void read(athena::io::IStreamReader& reader) {
|
|
||||||
id = reader.readUint32Big();
|
|
||||||
keyCount1 = reader.readUint16Big();
|
|
||||||
initRX = reader.readInt16Big();
|
|
||||||
qRX = reader.readUByte();
|
|
||||||
initRY = reader.readInt16Big();
|
|
||||||
qRY = reader.readUByte();
|
|
||||||
initRZ = reader.readInt16Big();
|
|
||||||
qRZ = reader.readUByte();
|
|
||||||
keyCount2 = reader.readUint16Big();
|
|
||||||
if (keyCount2) {
|
|
||||||
initTX = reader.readInt16Big();
|
|
||||||
qTX = reader.readUByte();
|
|
||||||
initTY = reader.readInt16Big();
|
|
||||||
qTY = reader.readUByte();
|
|
||||||
initTZ = reader.readInt16Big();
|
|
||||||
qTZ = reader.readUByte();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void write(athena::io::IStreamWriter& writer) const {
|
|
||||||
writer.writeUint32Big(id);
|
|
||||||
writer.writeUint16Big(keyCount1);
|
|
||||||
writer.writeInt16Big(initRX);
|
|
||||||
writer.writeUByte(qRX);
|
|
||||||
writer.writeInt16Big(initRY);
|
|
||||||
writer.writeUByte(qRY);
|
|
||||||
writer.writeInt16Big(initRZ);
|
|
||||||
writer.writeUByte(qRZ);
|
|
||||||
writer.writeUint16Big(keyCount2);
|
|
||||||
if (keyCount2) {
|
|
||||||
writer.writeInt16Big(initTX);
|
|
||||||
writer.writeUByte(qTX);
|
|
||||||
writer.writeInt16Big(initTY);
|
|
||||||
writer.writeUByte(qTY);
|
|
||||||
writer.writeInt16Big(initTZ);
|
|
||||||
writer.writeUByte(qTZ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void binarySize(size_t& __isz) const {
|
|
||||||
__isz += 17;
|
|
||||||
if (keyCount2)
|
|
||||||
__isz += 9;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ChannelDescPC : BigDNA {
|
|
||||||
Delete expl;
|
|
||||||
Value<atUint32> id = 0;
|
|
||||||
Value<atUint32> keyCount1 = 0;
|
|
||||||
Value<atUint32> QinitRX = 0;
|
|
||||||
Value<atUint32> QinitRY = 0;
|
|
||||||
Value<atUint32> QinitRZ = 0;
|
|
||||||
Value<atUint32> keyCount2 = 0;
|
|
||||||
Value<atUint32> QinitTX = 0;
|
|
||||||
Value<atUint32> QinitTY = 0;
|
|
||||||
Value<atUint32> QinitTZ = 0;
|
|
||||||
|
|
||||||
void read(athena::io::IStreamReader& reader) {
|
|
||||||
id = reader.readUint32Big();
|
|
||||||
keyCount1 = reader.readUint32Big();
|
|
||||||
QinitRX = reader.readUint32Big();
|
|
||||||
QinitRY = reader.readUint32Big();
|
|
||||||
QinitRZ = reader.readUint32Big();
|
|
||||||
keyCount2 = reader.readUint32Big();
|
|
||||||
if (keyCount2) {
|
|
||||||
QinitTX = reader.readUint32Big();
|
|
||||||
QinitTY = reader.readUint32Big();
|
|
||||||
QinitTZ = reader.readUint32Big();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void write(athena::io::IStreamWriter& writer) const {
|
|
||||||
writer.writeUint32Big(id);
|
|
||||||
writer.writeUint32Big(keyCount1);
|
|
||||||
writer.writeUint32Big(QinitRX);
|
|
||||||
writer.writeUint32Big(QinitRY);
|
|
||||||
writer.writeUint32Big(QinitRZ);
|
|
||||||
writer.writeUint32Big(keyCount2);
|
|
||||||
if (keyCount2) {
|
|
||||||
writer.writeUint32Big(QinitTX);
|
|
||||||
writer.writeUint32Big(QinitTY);
|
|
||||||
writer.writeUint32Big(QinitTZ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void binarySize(size_t& __isz) const {
|
|
||||||
__isz += 24;
|
|
||||||
if (keyCount2)
|
|
||||||
__isz += 12;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<IANIM> m_anim;
|
|
||||||
|
|
||||||
void sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig, bool) const {
|
|
||||||
m_anim->sendANIMToBlender(os, rig);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isLooping() const {
|
|
||||||
if (!m_anim)
|
|
||||||
return false;
|
|
||||||
return m_anim->looping;
|
|
||||||
}
|
|
||||||
|
|
||||||
void extractEVNT(const DNAANCS::AnimationResInfo<UniqueID32>& animInfo, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<PAKBridge>& pakRouter, bool force) const {
|
|
||||||
if (m_anim->evnt.isValid()) {
|
|
||||||
hecl::ProjectPath evntYamlPath = outPath.getWithExtension(
|
|
||||||
fmt::format(FMT_STRING(".{}_{}.evnt.yaml"), animInfo.name, m_anim->evnt).c_str(), true);
|
|
||||||
hecl::ProjectPath::Type evntYamlType = evntYamlPath.getPathType();
|
|
||||||
|
|
||||||
if (force || evntYamlType == hecl::ProjectPath::Type::None) {
|
|
||||||
EVNT evnt;
|
|
||||||
if (pakRouter.lookupAndReadDNA(m_anim->evnt, evnt, true)) {
|
|
||||||
athena::io::FileWriter writer(evntYamlPath.getAbsolutePath());
|
|
||||||
athena::io::ToYAMLStream(evnt, writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using BlenderAction = hecl::blender::Action;
|
|
||||||
|
|
||||||
ANIM() = default;
|
|
||||||
ANIM(const BlenderAction& act, const std::unordered_map<std::string, atInt32>& idMap,
|
|
||||||
const DNAANIM::RigInverter<CINF>& rig, bool pc);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,198 +0,0 @@
|
||||||
#include "CINF.hpp"
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
atUint32 CINF::getInternalBoneIdxFromId(atUint32 id) const {
|
|
||||||
atUint32 idx = 0;
|
|
||||||
for (const Bone& b : bones) {
|
|
||||||
if (b.id == id)
|
|
||||||
return idx;
|
|
||||||
++idx;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
atUint32 CINF::getBoneIdxFromId(atUint32 id) const {
|
|
||||||
atUint32 idx = 0;
|
|
||||||
for (atUint32 bid : boneIds) {
|
|
||||||
if (bid == id)
|
|
||||||
return idx;
|
|
||||||
++idx;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string* CINF::getBoneNameFromId(atUint32 id) const {
|
|
||||||
for (const Name& name : names)
|
|
||||||
if (id == name.boneId)
|
|
||||||
return &name.name;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CINF::sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const {
|
|
||||||
for (atUint32 bid : boneIds) {
|
|
||||||
for (const Name& name : names) {
|
|
||||||
if (name.boneId == bid) {
|
|
||||||
os.format(FMT_STRING("obj.vertex_groups.new(name='{}')\n"), name.name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const {
|
|
||||||
DNAANIM::RigInverter<CINF> inverter(*this);
|
|
||||||
|
|
||||||
os.format(FMT_STRING("arm = bpy.data.armatures.new('CINF_{}')\n"
|
|
||||||
"arm_obj = bpy.data.objects.new(arm.name, arm)\n"
|
|
||||||
"bpy.context.scene.collection.objects.link(arm_obj)\n"
|
|
||||||
"bpy.context.view_layer.objects.active = arm_obj\n"
|
|
||||||
"bpy.ops.object.mode_set(mode='EDIT')\n"
|
|
||||||
"arm_bone_table = {{}}\n"),
|
|
||||||
cinfId);
|
|
||||||
|
|
||||||
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) {
|
|
||||||
zeus::simd_floats originF(bone.m_origBone.origin.simd);
|
|
||||||
zeus::simd_floats tailF(bone.m_tail.mSimd);
|
|
||||||
os.format(FMT_STRING("bone = arm.edit_bones.new('{}')\n"
|
|
||||||
"bone.head = ({},{},{})\n"
|
|
||||||
"bone.tail = ({},{},{})\n"
|
|
||||||
"bone.use_inherit_scale = False\n"
|
|
||||||
"arm_bone_table[{}] = bone\n"),
|
|
||||||
*getBoneNameFromId(bone.m_origBone.id), originF[0], originF[1], originF[2], tailF[0], tailF[1], tailF[2],
|
|
||||||
bone.m_origBone.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const Bone& bone : bones)
|
|
||||||
if (bone.parentId != 2)
|
|
||||||
os.format(FMT_STRING("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
|
|
||||||
|
|
||||||
os << "bpy.ops.object.mode_set(mode='OBJECT')\n";
|
|
||||||
|
|
||||||
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones())
|
|
||||||
os.format(FMT_STRING("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"),
|
|
||||||
*getBoneNameFromId(bone.m_origBone.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CINF::GetCINFArmatureName(const UniqueID32& cinfId) { return fmt::format(FMT_STRING("CINF_{}"), cinfId); }
|
|
||||||
|
|
||||||
int CINF::RecursiveAddArmatureBone(const Armature& armature, const BlenderBone* bone, int parent, int& curId,
|
|
||||||
std::unordered_map<std::string, atInt32>& idMap,
|
|
||||||
std::map<std::string, int>& nameMap) {
|
|
||||||
int selId;
|
|
||||||
auto search = idMap.find(bone->name);
|
|
||||||
if (search == idMap.end()) {
|
|
||||||
selId = curId++;
|
|
||||||
idMap.emplace(std::make_pair(bone->name, selId));
|
|
||||||
} else
|
|
||||||
selId = search->second;
|
|
||||||
|
|
||||||
bones.emplace_back();
|
|
||||||
Bone& boneOut = bones.back();
|
|
||||||
nameMap[bone->name] = selId;
|
|
||||||
boneOut.id = selId;
|
|
||||||
boneOut.parentId = parent;
|
|
||||||
boneOut.origin = bone->origin;
|
|
||||||
boneOut.linkedCount = bone->children.size() + 1;
|
|
||||||
boneOut.linked.reserve(boneOut.linkedCount);
|
|
||||||
|
|
||||||
const BlenderBone* child;
|
|
||||||
boneOut.linked.push_back(parent);
|
|
||||||
for (size_t i = 0; (child = armature.getChild(bone, i)); ++i)
|
|
||||||
boneOut.linked.push_back(RecursiveAddArmatureBone(armature, child, boneOut.id, curId, idMap, nameMap));
|
|
||||||
|
|
||||||
return boneOut.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
CINF::CINF(const Armature& armature, std::unordered_map<std::string, atInt32>& idMap) {
|
|
||||||
idMap.reserve(armature.bones.size());
|
|
||||||
bones.reserve(armature.bones.size());
|
|
||||||
|
|
||||||
std::map<std::string, int> nameMap;
|
|
||||||
|
|
||||||
const BlenderBone* bone = armature.getRoot();
|
|
||||||
if (bone) {
|
|
||||||
if (bone->children.size()) {
|
|
||||||
int curId = 4;
|
|
||||||
const BlenderBone* child;
|
|
||||||
for (size_t i = 0; (child = armature.getChild(bone, i)); ++i)
|
|
||||||
RecursiveAddArmatureBone(armature, child, 3, curId, idMap, nameMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
bones.emplace_back();
|
|
||||||
Bone& boneOut = bones.back();
|
|
||||||
nameMap[bone->name] = 3;
|
|
||||||
boneOut.id = 3;
|
|
||||||
boneOut.parentId = 2;
|
|
||||||
boneOut.origin = bone->origin;
|
|
||||||
idMap.emplace(std::make_pair(bone->name, 3));
|
|
||||||
|
|
||||||
if (bone->children.size()) {
|
|
||||||
boneOut.linkedCount = 2;
|
|
||||||
boneOut.linked = {2, 4};
|
|
||||||
} else {
|
|
||||||
boneOut.linkedCount = 1;
|
|
||||||
boneOut.linked = {2};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boneCount = bones.size();
|
|
||||||
|
|
||||||
names.reserve(nameMap.size());
|
|
||||||
nameCount = nameMap.size();
|
|
||||||
for (const auto& name : nameMap) {
|
|
||||||
names.emplace_back();
|
|
||||||
Name& nameOut = names.back();
|
|
||||||
nameOut.name = name.first;
|
|
||||||
nameOut.boneId = name.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
boneIdCount = boneCount;
|
|
||||||
boneIds.reserve(boneIdCount);
|
|
||||||
for (auto it = bones.crbegin(); it != bones.crend(); ++it)
|
|
||||||
boneIds.push_back(it->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok,
|
|
||||||
std::function<void(const char*)> fileChanged) {
|
|
||||||
if (!force && outPath.isFile())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
auto& conn = btok.getBlenderConnection();
|
|
||||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::Armature))
|
|
||||||
return false;
|
|
||||||
auto os = conn.beginPythonOut(true);
|
|
||||||
|
|
||||||
os.format(FMT_STRING("import bpy\n"
|
|
||||||
"from mathutils import Vector\n"
|
|
||||||
"bpy.context.scene.name = 'CINF_{}'\n"
|
|
||||||
"bpy.context.scene.hecl_arm_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"),
|
|
||||||
entry.id);
|
|
||||||
|
|
||||||
CINF cinf;
|
|
||||||
cinf.read(rs);
|
|
||||||
cinf.sendCINFToBlender(os, entry.id);
|
|
||||||
os.centerView();
|
|
||||||
os.close();
|
|
||||||
return conn.saveBlend();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CINF::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath,
|
|
||||||
const hecl::blender::Armature& armature) {
|
|
||||||
std::unordered_map<std::string, atInt32> boneIdMap;
|
|
||||||
CINF cinf(armature, boneIdMap);
|
|
||||||
|
|
||||||
/* Write out CINF resource */
|
|
||||||
athena::io::TransactionalFileWriter w(outPath.getAbsolutePath());
|
|
||||||
cinf.write(w);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,57 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/RigInverter.hpp"
|
|
||||||
#include "DNAMP1.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
struct CINF : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> boneCount;
|
|
||||||
struct Bone : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> id;
|
|
||||||
Value<atUint32> parentId;
|
|
||||||
Value<atVec3f> origin;
|
|
||||||
Value<atUint32> linkedCount;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(linkedCount)> linked;
|
|
||||||
};
|
|
||||||
Vector<Bone, AT_DNA_COUNT(boneCount)> bones;
|
|
||||||
|
|
||||||
Value<atUint32> boneIdCount;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(boneIdCount)> boneIds;
|
|
||||||
|
|
||||||
Value<atUint32> nameCount;
|
|
||||||
struct Name : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
String<-1> name;
|
|
||||||
Value<atUint32> boneId;
|
|
||||||
};
|
|
||||||
Vector<Name, AT_DNA_COUNT(nameCount)> names;
|
|
||||||
|
|
||||||
atUint32 getInternalBoneIdxFromId(atUint32 id) const;
|
|
||||||
atUint32 getBoneIdxFromId(atUint32 id) const;
|
|
||||||
const std::string* getBoneNameFromId(atUint32 id) const;
|
|
||||||
void sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const;
|
|
||||||
void sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const;
|
|
||||||
static std::string GetCINFArmatureName(const UniqueID32& cinfId);
|
|
||||||
|
|
||||||
CINF() = default;
|
|
||||||
using Armature = hecl::blender::Armature;
|
|
||||||
using BlenderBone = hecl::blender::Bone;
|
|
||||||
|
|
||||||
int RecursiveAddArmatureBone(const Armature& armature, const BlenderBone* bone, int parent, int& curId,
|
|
||||||
std::unordered_map<std::string, atInt32>& idMap, std::map<std::string, int>& nameMap);
|
|
||||||
|
|
||||||
CINF(const Armature& armature, std::unordered_map<std::string, atInt32>& idMap);
|
|
||||||
|
|
||||||
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok,
|
|
||||||
std::function<void(const char*)> fileChanged);
|
|
||||||
|
|
||||||
static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath,
|
|
||||||
const hecl::blender::Armature& armature);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,125 +0,0 @@
|
||||||
#include "CMDL.hpp"
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
bool CMDL::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok,
|
|
||||||
std::function<void(const char*)> fileChanged) {
|
|
||||||
/* Check for RigPair */
|
|
||||||
CINF cinf;
|
|
||||||
CSKR cskr;
|
|
||||||
using RigPair = std::pair<std::pair<UniqueID32, CSKR*>, std::pair<UniqueID32, CINF*>>;
|
|
||||||
RigPair loadRp = {};
|
|
||||||
if (const typename CharacterAssociations<UniqueID32>::RigPair* rp = pakRouter.lookupCMDLRigPair(entry.id)) {
|
|
||||||
pakRouter.lookupAndReadDNA(rp->cskr, cskr);
|
|
||||||
pakRouter.lookupAndReadDNA(rp->cinf, cinf);
|
|
||||||
loadRp.first = {rp->cskr, &cskr};
|
|
||||||
loadRp.second = {rp->cinf, &cinf};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do extract */
|
|
||||||
hecl::blender::Connection& conn = btok.getBlenderConnection();
|
|
||||||
if (!conn.createBlend(outPath, hecl::blender::BlendType::Mesh))
|
|
||||||
return false;
|
|
||||||
DNACMDL::ReadCMDLToBlender<PAKRouter<PAKBridge>, MaterialSet, RigPair, DNACMDL::SurfaceHeader_1, 2>(
|
|
||||||
conn, rs, pakRouter, entry, dataSpec, loadRp);
|
|
||||||
conn.saveBlend();
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* Cook and re-extract test */
|
|
||||||
hecl::ProjectPath tempOut = outPath.getWithExtension(".recook", true);
|
|
||||||
hecl::blender::Connection::DataStream ds = conn.beginData();
|
|
||||||
DNACMDL::Mesh mesh = ds.compileMesh(hecl::TopologyTriStrips, -1);
|
|
||||||
ds.close();
|
|
||||||
DNACMDL::WriteCMDL<MaterialSet, DNACMDL::SurfaceHeader_1_2, 2>(tempOut, outPath, mesh);
|
|
||||||
|
|
||||||
athena::io::FileReader reader(tempOut.getAbsolutePath());
|
|
||||||
hecl::ProjectPath tempBlend = outPath.getWithExtension(".recook.blend", true);
|
|
||||||
if (!conn.createBlend(tempBlend, hecl::blender::Connection::TypeMesh))
|
|
||||||
return false;
|
|
||||||
DNACMDL::ReadCMDLToBlender<PAKRouter<PAKBridge>, MaterialSet, std::pair<CSKR*,CINF*>, DNACMDL::SurfaceHeader_1_2, 2>
|
|
||||||
(conn, reader, pakRouter, entry, dataSpec, loadRp);
|
|
||||||
return conn.saveBlend();
|
|
||||||
#elif 0
|
|
||||||
/* HMDL cook test */
|
|
||||||
hecl::ProjectPath tempOut = outPath.getWithExtension(".recook", true);
|
|
||||||
hecl::blender::Connection::DataStream ds = conn.beginData();
|
|
||||||
DNACMDL::Mesh mesh = ds.compileMesh(hecl::HMDLTopology::TriStrips, 16);
|
|
||||||
ds.close();
|
|
||||||
DNACMDL::WriteHMDLCMDL<HMDLMaterialSet, DNACMDL::SurfaceHeader_1, 2>(tempOut, outPath, mesh);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CMDL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNACMDL::Mesh& mesh) {
|
|
||||||
if (!mesh.skins.empty()) {
|
|
||||||
DNACMDL::Mesh skinMesh = mesh.getContiguousSkinningVersion();
|
|
||||||
if (!DNACMDL::WriteCMDL<MaterialSet, DNACMDL::SurfaceHeader_1, 2>(outPath, inPath, skinMesh))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Output skinning intermediate */
|
|
||||||
auto vertCountIt = skinMesh.contiguousSkinVertCounts.cbegin();
|
|
||||||
athena::io::FileWriter writer(outPath.getWithExtension(".skinint").getAbsolutePath());
|
|
||||||
writer.writeUint32Big(skinMesh.boneNames.size());
|
|
||||||
for (const std::string& boneName : skinMesh.boneNames)
|
|
||||||
writer.writeString(boneName);
|
|
||||||
|
|
||||||
writer.writeUint32Big(skinMesh.skins.size());
|
|
||||||
for (const auto& skin : skinMesh.skins) {
|
|
||||||
size_t numBinds = skinMesh.countSkinBinds(skin);
|
|
||||||
writer.writeUint32Big(numBinds);
|
|
||||||
for (size_t i = 0; i < numBinds; ++i) {
|
|
||||||
writer.writeUint32Big(skin[i].vg_idx);
|
|
||||||
writer.writeFloatBig(skin[i].weight);
|
|
||||||
}
|
|
||||||
writer.writeUint32Big(*vertCountIt++);
|
|
||||||
}
|
|
||||||
writer.writeUint32Big(skinMesh.pos.size());
|
|
||||||
writer.writeUint32Big(skinMesh.norm.size());
|
|
||||||
} else if (!DNACMDL::WriteCMDL<MaterialSet, DNACMDL::SurfaceHeader_1, 2>(outPath, inPath, mesh))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CMDL::HMDLCook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNACMDL::Mesh& mesh) {
|
|
||||||
hecl::blender::PoolSkinIndex poolSkinIndex;
|
|
||||||
if (mesh.skins.size()) {
|
|
||||||
if (!DNACMDL::WriteHMDLCMDL<HMDLMaterialSet, DNACMDL::SurfaceHeader_2, 2>(outPath, inPath, mesh, poolSkinIndex))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Output skinning intermediate */
|
|
||||||
athena::io::FileWriter writer(outPath.getWithExtension(".skinint").getAbsolutePath());
|
|
||||||
writer.writeUint32Big(mesh.skinBanks.banks.size());
|
|
||||||
for (const DNACMDL::Mesh::SkinBanks::Bank& sb : mesh.skinBanks.banks) {
|
|
||||||
writer.writeUint32Big(sb.m_boneIdxs.size());
|
|
||||||
for (uint32_t bind : sb.m_boneIdxs)
|
|
||||||
writer.writeUint32Big(bind);
|
|
||||||
}
|
|
||||||
writer.writeUint32Big(mesh.boneNames.size());
|
|
||||||
for (const std::string& boneName : mesh.boneNames)
|
|
||||||
writer.writeString(boneName);
|
|
||||||
|
|
||||||
/* CVirtualBone structure just like original (for CPU skinning) */
|
|
||||||
writer.writeUint32Big(mesh.skins.size());
|
|
||||||
for (auto& s : mesh.skins) {
|
|
||||||
size_t numBinds = mesh.countSkinBinds(s);
|
|
||||||
writer.writeUint32Big(numBinds);
|
|
||||||
for (size_t i = 0; i < numBinds; ++i) {
|
|
||||||
writer.writeUint32Big(s[i].vg_idx);
|
|
||||||
writer.writeFloatBig(s[i].weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write indirection table mapping pool verts to CVirtualBones */
|
|
||||||
writer.writeUint32Big(poolSkinIndex.m_poolSz);
|
|
||||||
for (uint32_t i = 0; i < poolSkinIndex.m_poolSz; ++i)
|
|
||||||
writer.writeUint32Big(poolSkinIndex.m_poolToSkinIndex[i]);
|
|
||||||
} else if (!DNACMDL::WriteHMDLCMDL<HMDLMaterialSet, DNACMDL::SurfaceHeader_2, 2>(outPath, inPath, mesh,
|
|
||||||
poolSkinIndex))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/CMDL.hpp"
|
|
||||||
#include "CMDLMaterials.hpp"
|
|
||||||
#include "DNAMP1.hpp"
|
|
||||||
#include "CINF.hpp"
|
|
||||||
#include "CSKR.hpp"
|
|
||||||
|
|
||||||
#include <athena/FileReader.hpp>
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
struct CMDL {
|
|
||||||
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
|
|
||||||
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok,
|
|
||||||
std::function<void(const char*)> fileChanged);
|
|
||||||
|
|
||||||
static void Name(const SpecBase& dataSpec, PAKEntryReadStream& rs, PAKRouter<PAKBridge>& pakRouter,
|
|
||||||
PAK::Entry& entry) {
|
|
||||||
DNACMDL::NameCMDL<PAKRouter<PAKBridge>, MaterialSet>(rs, pakRouter, entry, dataSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNACMDL::Mesh& mesh);
|
|
||||||
|
|
||||||
static bool HMDLCook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const DNACMDL::Mesh& mesh);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,640 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "DataSpec/DNACommon/DNACommon.hpp"
|
|
||||||
#include "DataSpec/DNACommon/GX.hpp"
|
|
||||||
#include "DataSpec/DNACommon/CMDL.hpp"
|
|
||||||
#include "DNAMP1.hpp"
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
struct MaterialSet : BigDNA {
|
|
||||||
static constexpr bool OneSection() { return false; }
|
|
||||||
|
|
||||||
AT_DECL_DNA
|
|
||||||
struct MaterialSetHead : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> textureCount = 0;
|
|
||||||
Vector<UniqueID32, AT_DNA_COUNT(textureCount)> textureIDs;
|
|
||||||
Value<atUint32> materialCount = 0;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(materialCount)> materialEndOffs;
|
|
||||||
|
|
||||||
void addTexture(const UniqueID32& id) {
|
|
||||||
textureIDs.push_back(id);
|
|
||||||
++textureCount;
|
|
||||||
}
|
|
||||||
void addMaterialEndOff(atUint32 off) {
|
|
||||||
materialEndOffs.push_back(off);
|
|
||||||
++materialCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class PAKBRIDGE>
|
|
||||||
void ensureTexturesExtracted(PAKRouter<PAKBRIDGE>& pakRouter) const {
|
|
||||||
for (const auto& id : textureIDs) {
|
|
||||||
const nod::Node* node;
|
|
||||||
const PAK::Entry* texEntry = pakRouter.lookupEntry(id, &node);
|
|
||||||
if (!texEntry)
|
|
||||||
continue;
|
|
||||||
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry);
|
|
||||||
if (txtrPath.isNone()) {
|
|
||||||
txtrPath.makeDirChain(false);
|
|
||||||
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
|
|
||||||
TXTR::Extract(rs, txtrPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} head;
|
|
||||||
|
|
||||||
struct Material : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
struct Flags : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> flags = 0;
|
|
||||||
bool konstValuesEnabled() const { return (flags & 0x8) != 0; }
|
|
||||||
void setKonstValuesEnabled(bool enabled) {
|
|
||||||
flags &= ~0x8;
|
|
||||||
flags |= atUint32(enabled) << 3;
|
|
||||||
}
|
|
||||||
bool depthSorting() const { return (flags & 0x10) != 0; }
|
|
||||||
void setDepthSorting(bool enabled) {
|
|
||||||
flags &= ~0x10;
|
|
||||||
flags |= atUint32(enabled) << 4;
|
|
||||||
}
|
|
||||||
bool alphaTest() const { return (flags & 0x20) != 0; }
|
|
||||||
void setAlphaTest(bool enabled) {
|
|
||||||
flags &= ~0x20;
|
|
||||||
flags |= atUint32(enabled) << 5;
|
|
||||||
}
|
|
||||||
bool samusReflection() const { return (flags & 0x40) != 0; }
|
|
||||||
void setSamusReflection(bool enabled) {
|
|
||||||
flags &= ~0x40;
|
|
||||||
flags |= atUint32(enabled) << 6;
|
|
||||||
}
|
|
||||||
bool depthWrite() const { return (flags & 0x80) != 0; }
|
|
||||||
void setDepthWrite(bool enabled) {
|
|
||||||
flags &= ~0x80;
|
|
||||||
flags |= atUint32(enabled) << 7;
|
|
||||||
}
|
|
||||||
bool samusReflectionSurfaceEye() const { return (flags & 0x100) != 0; }
|
|
||||||
void setSamusReflectionSurfaceEye(bool enabled) {
|
|
||||||
flags &= ~0x100;
|
|
||||||
flags |= atUint32(enabled) << 8;
|
|
||||||
}
|
|
||||||
bool shadowOccluderMesh() const { return (flags & 0x200) != 0; }
|
|
||||||
void setShadowOccluderMesh(bool enabled) {
|
|
||||||
flags &= ~0x200;
|
|
||||||
flags |= atUint32(enabled) << 9;
|
|
||||||
}
|
|
||||||
bool samusReflectionIndirectTexture() const { return (flags & 0x400) != 0; }
|
|
||||||
void setSamusReflectionIndirectTexture(bool enabled) {
|
|
||||||
flags &= ~0x400;
|
|
||||||
flags |= atUint32(enabled) << 10;
|
|
||||||
}
|
|
||||||
bool lightmap() const { return (flags & 0x800) != 0; }
|
|
||||||
void setLightmap(bool enabled) {
|
|
||||||
flags &= ~0x800;
|
|
||||||
flags |= atUint32(enabled) << 11;
|
|
||||||
}
|
|
||||||
bool lightmapUVArray() const { return (flags & 0x2000) != 0; }
|
|
||||||
void setLightmapUVArray(bool enabled) {
|
|
||||||
flags &= ~0x2000;
|
|
||||||
flags |= atUint32(enabled) << 13;
|
|
||||||
}
|
|
||||||
atUint16 textureSlots() const { return (flags >> 16) != 0; }
|
|
||||||
void setTextureSlots(atUint16 texslots) {
|
|
||||||
flags &= ~0xffff0000;
|
|
||||||
flags |= atUint32(texslots) << 16;
|
|
||||||
}
|
|
||||||
} flags;
|
|
||||||
const Flags& getFlags() const { return flags; }
|
|
||||||
|
|
||||||
Value<atUint32> textureCount = 0;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(textureCount)> textureIdxs;
|
|
||||||
struct VAFlags : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> vaFlags = 0;
|
|
||||||
GX::AttrType position() const { return GX::AttrType(vaFlags & 0x3); }
|
|
||||||
void setPosition(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x3;
|
|
||||||
vaFlags |= atUint32(val);
|
|
||||||
}
|
|
||||||
GX::AttrType normal() const { return GX::AttrType(vaFlags >> 2 & 0x3); }
|
|
||||||
void setNormal(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0xC;
|
|
||||||
vaFlags |= atUint32(val) << 2;
|
|
||||||
}
|
|
||||||
GX::AttrType color0() const { return GX::AttrType(vaFlags >> 4 & 0x3); }
|
|
||||||
void setColor0(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x30;
|
|
||||||
vaFlags |= atUint32(val) << 4;
|
|
||||||
}
|
|
||||||
GX::AttrType color1() const { return GX::AttrType(vaFlags >> 6 & 0x3); }
|
|
||||||
void setColor1(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0xC0;
|
|
||||||
vaFlags |= atUint32(val) << 6;
|
|
||||||
}
|
|
||||||
GX::AttrType tex0() const { return GX::AttrType(vaFlags >> 8 & 0x3); }
|
|
||||||
void setTex0(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x300;
|
|
||||||
vaFlags |= atUint32(val) << 8;
|
|
||||||
}
|
|
||||||
GX::AttrType tex1() const { return GX::AttrType(vaFlags >> 10 & 0x3); }
|
|
||||||
void setTex1(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0xC00;
|
|
||||||
vaFlags |= atUint32(val) << 10;
|
|
||||||
}
|
|
||||||
GX::AttrType tex2() const { return GX::AttrType(vaFlags >> 12 & 0x3); }
|
|
||||||
void setTex2(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x3000;
|
|
||||||
vaFlags |= atUint32(val) << 12;
|
|
||||||
}
|
|
||||||
GX::AttrType tex3() const { return GX::AttrType(vaFlags >> 14 & 0x3); }
|
|
||||||
void setTex3(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0xC000;
|
|
||||||
vaFlags |= atUint32(val) << 14;
|
|
||||||
}
|
|
||||||
GX::AttrType tex4() const { return GX::AttrType(vaFlags >> 16 & 0x3); }
|
|
||||||
void setTex4(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x30000;
|
|
||||||
vaFlags |= atUint32(val) << 16;
|
|
||||||
}
|
|
||||||
GX::AttrType tex5() const { return GX::AttrType(vaFlags >> 18 & 0x3); }
|
|
||||||
void setTex5(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0xC0000;
|
|
||||||
vaFlags |= atUint32(val) << 18;
|
|
||||||
}
|
|
||||||
GX::AttrType tex6() const { return GX::AttrType(vaFlags >> 20 & 0x3); }
|
|
||||||
void setTex6(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x300000;
|
|
||||||
vaFlags |= atUint32(val) << 20;
|
|
||||||
}
|
|
||||||
GX::AttrType pnMatIdx() const { return GX::AttrType(vaFlags >> 24 & 0x1); }
|
|
||||||
void setPnMatIdx(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x1000000;
|
|
||||||
vaFlags |= atUint32(val & 0x1) << 24;
|
|
||||||
}
|
|
||||||
GX::AttrType tex0MatIdx() const { return GX::AttrType(vaFlags >> 25 & 0x1); }
|
|
||||||
void setTex0MatIdx(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x2000000;
|
|
||||||
vaFlags |= atUint32(val & 0x1) << 25;
|
|
||||||
}
|
|
||||||
GX::AttrType tex1MatIdx() const { return GX::AttrType(vaFlags >> 26 & 0x1); }
|
|
||||||
void setTex1MatIdx(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x4000000;
|
|
||||||
vaFlags |= atUint32(val & 0x1) << 26;
|
|
||||||
}
|
|
||||||
GX::AttrType tex2MatIdx() const { return GX::AttrType(vaFlags >> 27 & 0x1); }
|
|
||||||
void setTex2MatIdx(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x8000000;
|
|
||||||
vaFlags |= atUint32(val & 0x1) << 27;
|
|
||||||
}
|
|
||||||
GX::AttrType tex3MatIdx() const { return GX::AttrType(vaFlags >> 28 & 0x1); }
|
|
||||||
void setTex3MatIdx(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x10000000;
|
|
||||||
vaFlags |= atUint32(val & 0x1) << 28;
|
|
||||||
}
|
|
||||||
GX::AttrType tex4MatIdx() const { return GX::AttrType(vaFlags >> 29 & 0x1); }
|
|
||||||
void setTex4MatIdx(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x20000000;
|
|
||||||
vaFlags |= atUint32(val & 0x1) << 29;
|
|
||||||
}
|
|
||||||
GX::AttrType tex5MatIdx() const { return GX::AttrType(vaFlags >> 30 & 0x1); }
|
|
||||||
void setTex5MatIdx(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x40000000;
|
|
||||||
vaFlags |= atUint32(val & 0x1) << 30;
|
|
||||||
}
|
|
||||||
GX::AttrType tex6MatIdx() const { return GX::AttrType(vaFlags >> 31 & 0x1); }
|
|
||||||
void setTex6MatIdx(GX::AttrType val) {
|
|
||||||
vaFlags &= ~0x80000000;
|
|
||||||
vaFlags |= atUint32(val & 0x1) << 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t vertDLSize() const {
|
|
||||||
static size_t ATTR_SZ[] = {0, 1, 1, 2};
|
|
||||||
size_t ret = 0;
|
|
||||||
ret += ATTR_SZ[position()];
|
|
||||||
ret += ATTR_SZ[normal()];
|
|
||||||
ret += ATTR_SZ[color0()];
|
|
||||||
ret += ATTR_SZ[color1()];
|
|
||||||
ret += ATTR_SZ[tex0()];
|
|
||||||
ret += ATTR_SZ[tex1()];
|
|
||||||
ret += ATTR_SZ[tex2()];
|
|
||||||
ret += ATTR_SZ[tex3()];
|
|
||||||
ret += ATTR_SZ[tex4()];
|
|
||||||
ret += ATTR_SZ[tex5()];
|
|
||||||
ret += ATTR_SZ[tex6()];
|
|
||||||
ret += ATTR_SZ[pnMatIdx()];
|
|
||||||
ret += ATTR_SZ[tex0MatIdx()];
|
|
||||||
ret += ATTR_SZ[tex1MatIdx()];
|
|
||||||
ret += ATTR_SZ[tex2MatIdx()];
|
|
||||||
ret += ATTR_SZ[tex3MatIdx()];
|
|
||||||
ret += ATTR_SZ[tex4MatIdx()];
|
|
||||||
ret += ATTR_SZ[tex5MatIdx()];
|
|
||||||
ret += ATTR_SZ[tex6MatIdx()];
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
} vaFlags;
|
|
||||||
const VAFlags& getVAFlags() const { return vaFlags; }
|
|
||||||
Value<atUint32> uniqueIdx;
|
|
||||||
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(flags.konstValuesEnabled())> konstCount;
|
|
||||||
Vector<GX::Color, AT_DNA_COUNT(flags.konstValuesEnabled() ? konstCount[0] : 0)> konstColors;
|
|
||||||
|
|
||||||
using BlendFactor = GX::BlendFactor;
|
|
||||||
Value<BlendFactor> blendDstFac;
|
|
||||||
Value<BlendFactor> blendSrcFac;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(flags.samusReflectionIndirectTexture())> indTexSlot;
|
|
||||||
|
|
||||||
Value<atUint32> colorChannelCount = 0;
|
|
||||||
struct ColorChannel : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> flags = 0;
|
|
||||||
bool lighting() const { return (flags & 0x1) != 0; }
|
|
||||||
void setLighting(bool enabled) {
|
|
||||||
flags &= ~0x1;
|
|
||||||
flags |= atUint32(enabled);
|
|
||||||
}
|
|
||||||
bool useAmbient() const { return (flags & 0x2) != 0; }
|
|
||||||
void setUseAmbient(bool enabled) {
|
|
||||||
flags &= ~0x2;
|
|
||||||
flags |= atUint32(enabled) << 1;
|
|
||||||
}
|
|
||||||
bool useMaterial() const { return (flags & 0x4) != 0; }
|
|
||||||
void setUseMaterial(bool enabled) {
|
|
||||||
flags &= ~0x4;
|
|
||||||
flags |= atUint32(enabled) << 2;
|
|
||||||
}
|
|
||||||
atUint8 lightmask() const { return atUint8(flags >> 3 & 0xff); }
|
|
||||||
void setLightmask(atUint8 mask) {
|
|
||||||
flags &= ~0x7f8;
|
|
||||||
flags |= atUint32(mask) << 3;
|
|
||||||
}
|
|
||||||
GX::DiffuseFn diffuseFn() const { return GX::DiffuseFn(flags >> 11 & 0x3); }
|
|
||||||
void setDiffuseFn(GX::DiffuseFn fn) {
|
|
||||||
flags &= ~0x1800;
|
|
||||||
flags |= atUint32(fn) << 11;
|
|
||||||
}
|
|
||||||
GX::AttnFn attenuationFn() const { return GX::AttnFn(flags >> 13 & 0x3); }
|
|
||||||
void setAttenuationFn(GX::AttnFn fn) {
|
|
||||||
flags &= ~0x6000;
|
|
||||||
flags |= atUint32(fn) << 13;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Vector<ColorChannel, AT_DNA_COUNT(colorChannelCount)> colorChannels;
|
|
||||||
|
|
||||||
Value<atUint32> tevStageCount = 0;
|
|
||||||
struct TEVStage : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> ciFlags = 0;
|
|
||||||
Value<atUint32> aiFlags = 0;
|
|
||||||
Value<atUint32> ccFlags = 0;
|
|
||||||
Value<atUint32> acFlags = 0;
|
|
||||||
Value<atUint8> pad = 0;
|
|
||||||
Value<atUint8> kaInput = 0;
|
|
||||||
Value<atUint8> kcInput = 0;
|
|
||||||
Value<atUint8> rascInput = 0;
|
|
||||||
|
|
||||||
GX::TevColorArg colorInA() const { return GX::TevColorArg(ciFlags & 0xf); }
|
|
||||||
void setColorInA(GX::TevColorArg val) {
|
|
||||||
ciFlags &= ~0x1f;
|
|
||||||
ciFlags |= atUint32(val);
|
|
||||||
}
|
|
||||||
GX::TevColorArg colorInB() const { return GX::TevColorArg(ciFlags >> 5 & 0xf); }
|
|
||||||
void setColorInB(GX::TevColorArg val) {
|
|
||||||
ciFlags &= ~0x3e0;
|
|
||||||
ciFlags |= atUint32(val) << 5;
|
|
||||||
}
|
|
||||||
GX::TevColorArg colorInC() const { return GX::TevColorArg(ciFlags >> 10 & 0xf); }
|
|
||||||
void setColorInC(GX::TevColorArg val) {
|
|
||||||
ciFlags &= ~0x7c00;
|
|
||||||
ciFlags |= atUint32(val) << 10;
|
|
||||||
}
|
|
||||||
GX::TevColorArg colorInD() const { return GX::TevColorArg(ciFlags >> 15 & 0xf); }
|
|
||||||
void setColorInD(GX::TevColorArg val) {
|
|
||||||
ciFlags &= ~0xf8000;
|
|
||||||
ciFlags |= atUint32(val) << 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
GX::TevAlphaArg alphaInA() const { return GX::TevAlphaArg(aiFlags & 0x7); }
|
|
||||||
void setAlphaInA(GX::TevAlphaArg val) {
|
|
||||||
aiFlags &= ~0x1f;
|
|
||||||
aiFlags |= atUint32(val);
|
|
||||||
}
|
|
||||||
GX::TevAlphaArg alphaInB() const { return GX::TevAlphaArg(aiFlags >> 5 & 0x7); }
|
|
||||||
void setAlphaInB(GX::TevAlphaArg val) {
|
|
||||||
aiFlags &= ~0x3e0;
|
|
||||||
aiFlags |= atUint32(val) << 5;
|
|
||||||
}
|
|
||||||
GX::TevAlphaArg alphaInC() const { return GX::TevAlphaArg(aiFlags >> 10 & 0x7); }
|
|
||||||
void setAlphaInC(GX::TevAlphaArg val) {
|
|
||||||
aiFlags &= ~0x7c00;
|
|
||||||
aiFlags |= atUint32(val) << 10;
|
|
||||||
}
|
|
||||||
GX::TevAlphaArg alphaInD() const { return GX::TevAlphaArg(aiFlags >> 15 & 0x7); }
|
|
||||||
void setAlphaInD(GX::TevAlphaArg val) {
|
|
||||||
aiFlags &= ~0xf8000;
|
|
||||||
aiFlags |= atUint32(val) << 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
GX::TevOp colorOp() const { return GX::TevOp(ccFlags & 0xf); }
|
|
||||||
void setColorOp(GX::TevOp val) {
|
|
||||||
ccFlags &= ~0x1;
|
|
||||||
ccFlags |= atUint32(val);
|
|
||||||
}
|
|
||||||
GX::TevBias colorOpBias() const { return GX::TevBias(ccFlags >> 4 & 0x3); }
|
|
||||||
void setColorOpBias(GX::TevBias val) {
|
|
||||||
ccFlags &= ~0x30;
|
|
||||||
ccFlags |= atUint32(val) << 4;
|
|
||||||
}
|
|
||||||
GX::TevScale colorOpScale() const { return GX::TevScale(ccFlags >> 6 & 0x3); }
|
|
||||||
void setColorOpScale(GX::TevScale val) {
|
|
||||||
ccFlags &= ~0xc0;
|
|
||||||
ccFlags |= atUint32(val) << 6;
|
|
||||||
}
|
|
||||||
bool colorOpClamp() const { return ccFlags >> 8 & 0x1; }
|
|
||||||
void setColorOpClamp(bool val) {
|
|
||||||
ccFlags &= ~0x100;
|
|
||||||
ccFlags |= atUint32(val) << 8;
|
|
||||||
}
|
|
||||||
GX::TevRegID colorOpOutReg() const { return GX::TevRegID(ccFlags >> 9 & 0x3); }
|
|
||||||
void setColorOpOutReg(GX::TevRegID val) {
|
|
||||||
ccFlags &= ~0x600;
|
|
||||||
ccFlags |= atUint32(val) << 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
GX::TevOp alphaOp() const { return GX::TevOp(acFlags & 0xf); }
|
|
||||||
void setAlphaOp(GX::TevOp val) {
|
|
||||||
acFlags &= ~0x1;
|
|
||||||
acFlags |= atUint32(val);
|
|
||||||
}
|
|
||||||
GX::TevBias alphaOpBias() const { return GX::TevBias(acFlags >> 4 & 0x3); }
|
|
||||||
void setAlphaOpBias(GX::TevBias val) {
|
|
||||||
acFlags &= ~0x30;
|
|
||||||
acFlags |= atUint32(val) << 4;
|
|
||||||
}
|
|
||||||
GX::TevScale alphaOpScale() const { return GX::TevScale(acFlags >> 6 & 0x3); }
|
|
||||||
void setAlphaOpScale(GX::TevScale val) {
|
|
||||||
acFlags &= ~0xc0;
|
|
||||||
acFlags |= atUint32(val) << 6;
|
|
||||||
}
|
|
||||||
bool alphaOpClamp() const { return acFlags >> 8 & 0x1; }
|
|
||||||
void setAlphaOpClamp(bool val) {
|
|
||||||
acFlags &= ~0x100;
|
|
||||||
acFlags |= atUint32(val) << 8;
|
|
||||||
}
|
|
||||||
GX::TevRegID alphaOpOutReg() const { return GX::TevRegID(acFlags >> 9 & 0x3); }
|
|
||||||
void setAlphaOpOutReg(GX::TevRegID val) {
|
|
||||||
acFlags &= ~0x600;
|
|
||||||
acFlags |= atUint32(val) << 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
GX::TevKColorSel kColorIn() const { return GX::TevKColorSel(kcInput); }
|
|
||||||
void setKColorIn(GX::TevKColorSel val) { kcInput = val; }
|
|
||||||
GX::TevKAlphaSel kAlphaIn() const { return GX::TevKAlphaSel(kaInput); }
|
|
||||||
void setKAlphaIn(GX::TevKAlphaSel val) { kaInput = val; }
|
|
||||||
|
|
||||||
GX::ChannelID rasIn() const { return GX::ChannelID(rascInput); }
|
|
||||||
void setRASIn(GX::ChannelID id) { rascInput = id; }
|
|
||||||
};
|
|
||||||
Vector<TEVStage, AT_DNA_COUNT(tevStageCount)> tevStages;
|
|
||||||
struct TEVStageTexInfo : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint16> pad = 0;
|
|
||||||
Value<atUint8> texSlot = 0xff;
|
|
||||||
Value<atUint8> tcgSlot = 0xff;
|
|
||||||
};
|
|
||||||
Vector<TEVStageTexInfo, AT_DNA_COUNT(tevStageCount)> tevStageTexInfo;
|
|
||||||
|
|
||||||
Value<atUint32> tcgCount = 0;
|
|
||||||
struct TexCoordGen : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> flags = 0;
|
|
||||||
|
|
||||||
GX::TexGenType type() const { return GX::TexGenType(flags & 0xf); }
|
|
||||||
void setType(GX::TexGenType val) {
|
|
||||||
flags &= ~0xf;
|
|
||||||
flags |= atUint32(val);
|
|
||||||
}
|
|
||||||
GX::TexGenSrc source() const { return GX::TexGenSrc(flags >> 4 & 0x1f); }
|
|
||||||
void setSource(GX::TexGenSrc val) {
|
|
||||||
flags &= ~0x1f0;
|
|
||||||
flags |= atUint32(val) << 4;
|
|
||||||
}
|
|
||||||
GX::TexMtx mtx() const { return GX::TexMtx((flags >> 9 & 0x1f) + 30); }
|
|
||||||
void setMtx(GX::TexMtx val) {
|
|
||||||
flags &= ~0x3e00;
|
|
||||||
flags |= (atUint32(val) - 30) << 9;
|
|
||||||
}
|
|
||||||
bool normalize() const { return flags >> 14 & 0x1; }
|
|
||||||
void setNormalize(bool val) {
|
|
||||||
flags &= ~0x4000;
|
|
||||||
flags |= atUint32(val) << 14;
|
|
||||||
}
|
|
||||||
GX::PTTexMtx postMtx() const { return GX::PTTexMtx((flags >> 15 & 0x3f) + 64); }
|
|
||||||
void setPostMtx(GX::PTTexMtx val) {
|
|
||||||
flags &= ~0x1f8000;
|
|
||||||
flags |= (atUint32(val) - 64) << 15;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Vector<TexCoordGen, AT_DNA_COUNT(tcgCount)> tcgs;
|
|
||||||
|
|
||||||
Value<atUint32> uvAnimsSize = 4;
|
|
||||||
Value<atUint32> uvAnimsCount = 0;
|
|
||||||
struct UVAnimation : BigDNA {
|
|
||||||
AT_DECL_EXPLICIT_DNA
|
|
||||||
enum class Mode {
|
|
||||||
MvInvNoTranslation,
|
|
||||||
MvInv,
|
|
||||||
Scroll,
|
|
||||||
Rotation,
|
|
||||||
HStrip,
|
|
||||||
VStrip,
|
|
||||||
Model,
|
|
||||||
CylinderEnvironment,
|
|
||||||
Eight
|
|
||||||
} mode;
|
|
||||||
float vals[9];
|
|
||||||
|
|
||||||
UVAnimation() = default;
|
|
||||||
UVAnimation(const std::string& gameFunction, const std::vector<atVec4f>& gameArgs);
|
|
||||||
};
|
|
||||||
Vector<UVAnimation, AT_DNA_COUNT(uvAnimsCount)> uvAnims;
|
|
||||||
|
|
||||||
static void AddTexture(hecl::blender::PyOutStream& out, GX::TexGenSrc type, int mtxIdx, uint32_t texIdx,
|
|
||||||
bool diffuse);
|
|
||||||
static void AddTextureAnim(hecl::blender::PyOutStream& out, MaterialSet::Material::UVAnimation::Mode type,
|
|
||||||
unsigned idx, const float* vals);
|
|
||||||
static void AddKcolor(hecl::blender::PyOutStream& out, const GX::Color& col, unsigned idx);
|
|
||||||
static void AddDynamicColor(hecl::blender::PyOutStream& out, unsigned idx);
|
|
||||||
static void AddDynamicAlpha(hecl::blender::PyOutStream& out, unsigned idx);
|
|
||||||
|
|
||||||
Material() = default;
|
|
||||||
Material(const hecl::blender::Material& material, std::vector<hecl::ProjectPath>& texPathsOut, int colorCount,
|
|
||||||
bool lightmapUVs, bool matrixSkinning);
|
|
||||||
};
|
|
||||||
Vector<Material, AT_DNA_COUNT(head.materialCount)> materials;
|
|
||||||
|
|
||||||
static void RegisterMaterialProps(hecl::blender::PyOutStream& out);
|
|
||||||
static void ConstructMaterial(hecl::blender::PyOutStream& out, const MaterialSet::Material& material,
|
|
||||||
unsigned groupIdx, unsigned matIdx);
|
|
||||||
|
|
||||||
void readToBlender(hecl::blender::PyOutStream& os, const PAKRouter<PAKBridge>& pakRouter,
|
|
||||||
const PAKRouter<PAKBridge>::EntryType& entry, unsigned setIdx) {
|
|
||||||
DNACMDL::ReadMaterialSetToBlender_1_2(os, *this, pakRouter, entry, setIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class PAKRouter>
|
|
||||||
void nameTextures(PAKRouter& pakRouter, const char* prefix, int setIdx) const {
|
|
||||||
int matIdx = 0;
|
|
||||||
for (const Material& mat : materials) {
|
|
||||||
int stageIdx = 0;
|
|
||||||
for (const Material::TEVStage& stage : mat.tevStages) {
|
|
||||||
(void)stage;
|
|
||||||
const Material::TEVStageTexInfo& texInfo = mat.tevStageTexInfo[stageIdx];
|
|
||||||
if (texInfo.texSlot == 0xff) {
|
|
||||||
++stageIdx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const nod::Node* node;
|
|
||||||
typename PAKRouter::EntryType* texEntry = (typename PAKRouter::EntryType*)pakRouter.lookupEntry(
|
|
||||||
head.textureIDs[mat.textureIdxs[texInfo.texSlot]], &node);
|
|
||||||
if (texEntry->name.size()) {
|
|
||||||
if (texEntry->name.size() < 5 || texEntry->name.compare(0, 5, "mult_"))
|
|
||||||
texEntry->name = "mult_" + texEntry->name;
|
|
||||||
++stageIdx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (setIdx < 0)
|
|
||||||
texEntry->name = fmt::format(FMT_STRING("{}_{}_{}"), prefix, matIdx, stageIdx);
|
|
||||||
else
|
|
||||||
texEntry->name = fmt::format(FMT_STRING("{}_{}_{}_{}"), prefix, setIdx, matIdx, stageIdx);
|
|
||||||
|
|
||||||
if (mat.flags.lightmap() && stageIdx == 0) {
|
|
||||||
texEntry->name += "light";
|
|
||||||
++stageIdx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
++stageIdx;
|
|
||||||
}
|
|
||||||
++matIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ensureTexturesExtracted(PAKRouter<PAKBridge>& pakRouter) const { head.ensureTexturesExtracted(pakRouter); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HMDLMaterialSet : BigDNA {
|
|
||||||
static constexpr bool OneSection() { return false; }
|
|
||||||
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<atUint32> materialCount = 0;
|
|
||||||
Vector<atUint32, AT_DNA_COUNT(materialCount)> materialEndOffs;
|
|
||||||
|
|
||||||
struct Material : BigDNA {
|
|
||||||
AT_DECL_DNA
|
|
||||||
MaterialSet::Material::Flags flags;
|
|
||||||
|
|
||||||
using BlendMaterial = hecl::blender::Material;
|
|
||||||
|
|
||||||
struct PASS : hecl::TypedRecordBigDNA<BlendMaterial::ChunkType::TexturePass> {
|
|
||||||
AT_DECL_EXPLICIT_DNA
|
|
||||||
Value<BlendMaterial::PassType> type;
|
|
||||||
UniqueID32 texId;
|
|
||||||
Value<BlendMaterial::TexCoordSource> source;
|
|
||||||
Value<BlendMaterial::UVAnimType> uvAnimType;
|
|
||||||
Value<float> uvAnimParms[9] = {};
|
|
||||||
Value<bool> alpha;
|
|
||||||
PASS() = default;
|
|
||||||
explicit PASS(const BlendMaterial::PASS& pass)
|
|
||||||
: type(pass.type), texId(pass.tex), source(pass.source), uvAnimType(pass.uvAnimType), alpha(pass.alpha) {
|
|
||||||
std::copy(pass.uvAnimParms.begin(), pass.uvAnimParms.end(), std::begin(uvAnimParms));
|
|
||||||
}
|
|
||||||
bool shouldNormalizeUv() const {
|
|
||||||
switch (uvAnimType) {
|
|
||||||
case BlendMaterial::UVAnimType::MvInvNoTranslation:
|
|
||||||
case BlendMaterial::UVAnimType::MvInv:
|
|
||||||
case BlendMaterial::UVAnimType::Model:
|
|
||||||
case BlendMaterial::UVAnimType::CylinderEnvironment:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size_t uvAnimParamsCount() const {
|
|
||||||
switch (uvAnimType) {
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
case BlendMaterial::UVAnimType::Scroll:
|
|
||||||
case BlendMaterial::UVAnimType::HStrip:
|
|
||||||
case BlendMaterial::UVAnimType::VStrip:
|
|
||||||
return 4;
|
|
||||||
case BlendMaterial::UVAnimType::Rotation:
|
|
||||||
case BlendMaterial::UVAnimType::CylinderEnvironment:
|
|
||||||
return 2;
|
|
||||||
case BlendMaterial::UVAnimType::Eight:
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct CLR : hecl::TypedRecordBigDNA<BlendMaterial::ChunkType::ColorPass> {
|
|
||||||
AT_DECL_DNA
|
|
||||||
Value<BlendMaterial::PassType> type;
|
|
||||||
Value<atVec4f> color;
|
|
||||||
CLR() = default;
|
|
||||||
explicit CLR(const BlendMaterial::CLR& clr) : type(clr.type), color(clr.color.val) {}
|
|
||||||
};
|
|
||||||
using Chunk = hecl::TypedVariantBigDNA<PASS, CLR>;
|
|
||||||
|
|
||||||
static unsigned TexMapIdx(BlendMaterial::PassType type) {
|
|
||||||
switch (type) {
|
|
||||||
case BlendMaterial::PassType::Lightmap:
|
|
||||||
return 0;
|
|
||||||
case BlendMaterial::PassType::Diffuse:
|
|
||||||
return 1;
|
|
||||||
case BlendMaterial::PassType::Emissive:
|
|
||||||
return 2;
|
|
||||||
case BlendMaterial::PassType::Specular:
|
|
||||||
return 3;
|
|
||||||
case BlendMaterial::PassType::ExtendedSpecular:
|
|
||||||
return 4;
|
|
||||||
case BlendMaterial::PassType::Reflection:
|
|
||||||
return 5;
|
|
||||||
case BlendMaterial::PassType::Alpha:
|
|
||||||
return 6;
|
|
||||||
case BlendMaterial::PassType::IndirectTex:
|
|
||||||
return 7;
|
|
||||||
default:
|
|
||||||
assert(false && "Unknown pass type");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<atUint64> hash;
|
|
||||||
Value<BlendMaterial::ShaderType> shaderType;
|
|
||||||
Value<atUint32> chunkCount;
|
|
||||||
Vector<Chunk, AT_DNA_COUNT(chunkCount)> chunks;
|
|
||||||
Value<BlendMaterial::BlendMode> blendMode = BlendMaterial::BlendMode::Opaque;
|
|
||||||
|
|
||||||
std::pair<hecl::Backend::BlendFactor, hecl::Backend::BlendFactor> blendFactors() const {
|
|
||||||
switch (blendMode) {
|
|
||||||
case BlendMaterial::BlendMode::Opaque:
|
|
||||||
default:
|
|
||||||
return {hecl::Backend::BlendFactor::One, hecl::Backend::BlendFactor::Zero};
|
|
||||||
case BlendMaterial::BlendMode::Alpha:
|
|
||||||
return {hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha};
|
|
||||||
case BlendMaterial::BlendMode::Additive:
|
|
||||||
return {hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::One};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Material() = default;
|
|
||||||
Material(const hecl::blender::Material& mat);
|
|
||||||
};
|
|
||||||
Vector<Material, AT_DNA_COUNT(materialCount)> materials;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
||||||
|
|
||||||
AT_SPECIALIZE_TYPED_VARIANT_BIGDNA(DataSpec::DNAMP1::HMDLMaterialSet::Material::PASS,
|
|
||||||
DataSpec::DNAMP1::HMDLMaterialSet::Material::CLR)
|
|
|
@ -1,69 +0,0 @@
|
||||||
include(DNAMP1/ScriptObjects/CMakeLists.txt)
|
|
||||||
include(DNAMP1/SFX/CMakeLists.txt)
|
|
||||||
|
|
||||||
make_dnalist(PAK
|
|
||||||
MLVL
|
|
||||||
AGSC
|
|
||||||
CSNG
|
|
||||||
AFSM
|
|
||||||
ANCS
|
|
||||||
ANIM
|
|
||||||
CINF
|
|
||||||
CSKR
|
|
||||||
EVNT
|
|
||||||
CMDLMaterials
|
|
||||||
MREA
|
|
||||||
DeafBabe
|
|
||||||
SCAN
|
|
||||||
FRME
|
|
||||||
SAVW
|
|
||||||
HINT
|
|
||||||
MazeSeeds
|
|
||||||
SnowForces
|
|
||||||
DCLN
|
|
||||||
Tweaks/CTweakGame
|
|
||||||
Tweaks/CTweakParticle
|
|
||||||
Tweaks/CTweakPlayer
|
|
||||||
Tweaks/CTweakPlayerControl
|
|
||||||
Tweaks/CTweakPlayerGun
|
|
||||||
Tweaks/CTweakGunRes
|
|
||||||
Tweaks/CTweakPlayerRes
|
|
||||||
Tweaks/CTweakGui
|
|
||||||
Tweaks/CTweakSlideShow
|
|
||||||
Tweaks/CTweakCameraBob
|
|
||||||
Tweaks/CTweakTargeting
|
|
||||||
Tweaks/CTweakAutoMapper
|
|
||||||
Tweaks/CTweakBall
|
|
||||||
Tweaks/CTweakGuiColors)
|
|
||||||
|
|
||||||
set(DNAMP1_SOURCES
|
|
||||||
DNAMP1.hpp DNAMP1.cpp
|
|
||||||
AFSM.cpp
|
|
||||||
PAK.cpp
|
|
||||||
MLVL.cpp
|
|
||||||
STRG.hpp STRG.cpp
|
|
||||||
AGSC.cpp
|
|
||||||
CSNG.cpp
|
|
||||||
CSKR.cpp
|
|
||||||
ANCS.cpp
|
|
||||||
ANIM.cpp
|
|
||||||
CINF.cpp
|
|
||||||
EVNT.cpp
|
|
||||||
PATH.hpp
|
|
||||||
CMDL.hpp CMDL.cpp
|
|
||||||
CMDLMaterials.cpp
|
|
||||||
DCLN.cpp
|
|
||||||
MAPA.hpp
|
|
||||||
MAPU.hpp
|
|
||||||
MREA.cpp
|
|
||||||
SCLY.hpp SCLY.cpp
|
|
||||||
FRME.cpp
|
|
||||||
SCAN.cpp
|
|
||||||
DeafBabe.cpp
|
|
||||||
Tweaks/CTweakAutoMapper.cpp
|
|
||||||
Tweaks/CTweakPlayer.cpp
|
|
||||||
Tweaks/CTweakTargeting.cpp
|
|
||||||
Tweaks/CTweakBall.cpp
|
|
||||||
Tweaks/CTweakGame.cpp)
|
|
||||||
|
|
||||||
dataspec_add_list(DNAMP1 DNAMP1_SOURCES)
|
|
|
@ -1,16 +0,0 @@
|
||||||
#include "CSKR.hpp"
|
|
||||||
#include "hecl/Blender/Connection.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
|
||||||
|
|
||||||
void CSKR::weightVertex(hecl::blender::PyOutStream& os, const CINF& cinf, atUint32 idx) const {
|
|
||||||
atUint32 accum = 0;
|
|
||||||
for (const SkinningRule& rule : skinningRules) {
|
|
||||||
if (idx >= accum && idx < accum + rule.vertCount)
|
|
||||||
for (const SkinningRule::Weight& weight : rule.weights)
|
|
||||||
os.format(FMT_STRING("vert[dvert_lay][{}] = {}\n"), cinf.getBoneIdxFromId(weight.boneId), weight.weight);
|
|
||||||
accum += rule.vertCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DataSpec::DNAMP1
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue