mirror of https://github.com/OpenMW/openmw.git
Merge branch 'master' into 'LTO-timing'
# Conflicts: # CI/before_script.msvc.shLTO-timing
commit
a8d4ba535f
@ -0,0 +1,8 @@
|
||||
---
|
||||
Checks: "-*,
|
||||
boost-*,
|
||||
portability-*,
|
||||
"
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
FormatStyle: none
|
@ -0,0 +1,87 @@
|
||||
name: CMake
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
|
||||
jobs:
|
||||
Ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Add OpenMW PPA Dependancies
|
||||
run: sudo add-apt-repository ppa:openmw/openmw; sudo apt-get update
|
||||
|
||||
- name: Install Building Dependancies
|
||||
run: sudo CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
||||
|
||||
- name: Prime ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
|
||||
max-size: 1000M
|
||||
|
||||
- name: Install gtest
|
||||
run: |
|
||||
export CONFIGURATION="Release"
|
||||
export GOOGLETEST_DIR="."
|
||||
export GENERATOR="Unix Makefiles"
|
||||
export CC="gcc"
|
||||
export CXX="g++"
|
||||
sudo -E CI/build_googletest.sh
|
||||
|
||||
- name: Configure
|
||||
run: cmake -S . -B . -DGTEST_ROOT="$(pwd)/googletest/build" -DGMOCK_ROOT="$(pwd)/googletest/build" -DBUILD_UNITTESTS=ON -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_FLAGS='-Werror' -DCMAKE_CXX_FLAGS="-Werror -Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=deprecated-copy"
|
||||
|
||||
- name: Build
|
||||
run: cmake --build . --config ${{env.BUILD_TYPE}} --parallel 3
|
||||
|
||||
- name: Test
|
||||
run: ./openmw_test_suite
|
||||
|
||||
# - name: Install
|
||||
# shell: bash
|
||||
# run: cmake --install .
|
||||
|
||||
# - name: Create Artifact
|
||||
# shell: bash
|
||||
# working-directory: install
|
||||
# run: |
|
||||
# ls -laR
|
||||
# 7z a ../build_artifact.7z .
|
||||
|
||||
# - name: Upload Artifact
|
||||
# uses: actions/upload-artifact@v1
|
||||
# with:
|
||||
# path: ./build_artifact.7z
|
||||
# name: build_artifact.7z
|
||||
|
||||
MacOS:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Building Dependancies
|
||||
run: CI/before_install.osx.sh
|
||||
|
||||
- name: Prime ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
|
||||
max-size: 1000M
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
rm -fr build # remove the build directory
|
||||
CI/before_script.osx.sh
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd build
|
||||
make -j $(sysctl -n hw.logicalcpu) package
|
@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
python:
|
||||
version: 3.8
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
@ -0,0 +1,8 @@
|
||||
1471
|
||||
1450
|
||||
1420
|
||||
1314
|
||||
1216
|
||||
1172
|
||||
1160
|
||||
1051
|
@ -1,105 +0,0 @@
|
||||
language: cpp
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- coverity_scan
|
||||
- /openmw-.*$/
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
|
||||
cache: ccache
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:openmw/openmw'
|
||||
# - ubuntu-toolchain-r-test # for GCC-10
|
||||
packages: [
|
||||
# Dev
|
||||
build-essential, cmake, clang-tools, ccache,
|
||||
# Boost
|
||||
libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev,
|
||||
# FFmpeg
|
||||
libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev,
|
||||
# Audio, Video and Misc. deps
|
||||
libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev
|
||||
# The other ones from OpenMW ppa
|
||||
libbullet-dev, libopenscenegraph-dev, libmygui-dev
|
||||
]
|
||||
coverity_scan: # TODO: currently takes too long, disabled openmw/openmw-cs for now.
|
||||
project:
|
||||
name: "OpenMW/openmw"
|
||||
description: "<Your project description here>"
|
||||
branch_pattern: coverity_scan
|
||||
notification_email: 1122069+psi29a@users.noreply.github.com
|
||||
build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENMW=FALSE -DBUILD_OPENCS=FALSE"
|
||||
build_command: "make VERBOSE=1 -j3"
|
||||
matrix:
|
||||
include:
|
||||
- name: OpenMW (all) on MacOS 10.15 with Xcode 11.6
|
||||
os: osx
|
||||
osx_image: xcode11.6
|
||||
if: branch != coverity_scan
|
||||
- name: OpenMW (all) on Ubuntu Focal with GCC
|
||||
os: linux
|
||||
dist: focal
|
||||
if: branch != coverity_scan
|
||||
- name: OpenMW (tests only) on Ubuntu Focal with GCC
|
||||
os: linux
|
||||
dist: focal
|
||||
if: branch != coverity_scan
|
||||
env:
|
||||
- BUILD_TESTS_ONLY: 1
|
||||
- name: OpenMW (openmw) on Ubuntu Focal with Clang's Static Analysis
|
||||
os: linux
|
||||
dist: focal
|
||||
env:
|
||||
- MATRIX_EVAL="CC=clang && CXX=clang++"
|
||||
- ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++"
|
||||
if: branch != coverity_scan
|
||||
compiler: clang
|
||||
- name: OpenMW Components Coverity Scan
|
||||
os: linux
|
||||
dist: focal
|
||||
if: branch = coverity_scan
|
||||
# allow_failures:
|
||||
# - name: OpenMW (openmw) on Ubuntu Focal with GCC-10
|
||||
# env:
|
||||
# - MATRIX_EVAL="CC=gcc-10 && CXX=g++-10"
|
||||
|
||||
before_install:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_install.${TRAVIS_OS_NAME}.sh; fi
|
||||
before_script:
|
||||
- ccache -z
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_script.${TRAVIS_OS_NAME}.sh; fi
|
||||
script:
|
||||
- cd ./build
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||
- cd "${TRAVIS_BUILD_DIR}"
|
||||
- ccache -s
|
||||
deploy:
|
||||
provider: script
|
||||
script: ./CI/deploy.osx.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
branch: master
|
||||
condition: "$TRAVIS_EVENT_TYPE = cron && $TRAVIS_OS_NAME = osx"
|
||||
repo: OpenMW/openmw
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- corrmage+travis-ci@gmail.com
|
||||
on_success: change
|
||||
on_failure: always
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#openmw"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
use_notice: true
|
@ -1,4 +1,4 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20201018.zip -o ~/openmw-android-deps.zip
|
||||
unzip -o ~/openmw-android-deps -d /usr/lib/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20211114.zip -o ~/openmw-android-deps.zip
|
||||
unzip -o ~/openmw-android-deps -d /android-ndk-r22/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null
|
||||
|
@ -1,9 +1,27 @@
|
||||
#!/bin/sh -e
|
||||
#!/bin/sh -ex
|
||||
|
||||
export HOMEBREW_NO_EMOJI=1
|
||||
brew update --quiet
|
||||
|
||||
# workaround python issue on travis
|
||||
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies python@3.8 || true
|
||||
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies python@3.9 || true
|
||||
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies qt@6 || true
|
||||
|
||||
# Some of these tools can come from places other than brew, so check before installing
|
||||
[ -z "${TRAVIS}" ] && brew reinstall xquartz
|
||||
[ -z "${TRAVIS}" ] && brew reinstall fontconfig
|
||||
command -v ccache >/dev/null 2>&1 || brew install ccache
|
||||
command -v cmake >/dev/null 2>&1 || brew install cmake
|
||||
command -v qmake >/dev/null 2>&1 || brew install qt
|
||||
command -v qmake >/dev/null 2>&1 || brew install qt@5
|
||||
brew install icu4c
|
||||
brew install yaml-cpp
|
||||
export PATH="/usr/local/opt/qt@5/bin:$PATH" # needed to use qmake in none default path as qt now points to qt6
|
||||
|
||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip
|
||||
ccache --version
|
||||
cmake --version
|
||||
qmake --version
|
||||
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20220225.zip -o ~/openmw-deps.zip
|
||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
|
||||
|
@ -1,44 +1,109 @@
|
||||
#!/bin/bash -ex
|
||||
#!/bin/bash
|
||||
|
||||
set -xeo pipefail
|
||||
|
||||
free -m
|
||||
|
||||
# Silence a git warning
|
||||
git config --global advice.detachedHead false
|
||||
|
||||
BUILD_UNITTESTS=OFF
|
||||
BUILD_BENCHMARKS=OFF
|
||||
|
||||
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||
export GOOGLETEST_DIR="$(pwd)/googletest/build/install"
|
||||
export GOOGLETEST_DIR="${PWD}/googletest/build/install"
|
||||
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
|
||||
BUILD_UNITTESTS=ON
|
||||
BUILD_BENCHMARKS=ON
|
||||
fi
|
||||
|
||||
# setup our basic cmake build options
|
||||
declare -a CMAKE_CONF_OPTS=(
|
||||
-DCMAKE_C_COMPILER="${CC:-/usr/bin/cc}"
|
||||
-DCMAKE_CXX_COMPILER="${CXX:-/usr/bin/c++}"
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_INSTALL_PREFIX=install
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
-DUSE_SYSTEM_TINYXML=ON
|
||||
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=ON
|
||||
-DOPENMW_CXX_FLAGS="-Werror -Werror=implicit-fallthrough" # flags specific to OpenMW project
|
||||
)
|
||||
|
||||
if [[ $CI_OPENMW_USE_STATIC_DEPS ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-DOPENMW_USE_SYSTEM_MYGUI=OFF
|
||||
-DOPENMW_USE_SYSTEM_OSG=OFF
|
||||
-DOPENMW_USE_SYSTEM_BULLET=OFF
|
||||
-DOPENMW_USE_SYSTEM_SQLITE3=OFF
|
||||
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=OFF
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ $CI_CLANG_TIDY ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-DCMAKE_CXX_CLANG_TIDY="clang-tidy;--warnings-as-errors=*"
|
||||
)
|
||||
fi
|
||||
|
||||
mkdir build
|
||||
|
||||
if [[ "${CMAKE_BUILD_TYPE}" ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ "${CMAKE_CXX_FLAGS_DEBUG}" ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-DCMAKE_CXX_FLAGS_DEBUG="${CMAKE_CXX_FLAGS_DEBUG}"
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ "${CMAKE_EXE_LINKER_FLAGS}" ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}"
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-DBUILD_WITH_CODE_COVERAGE="${BUILD_WITH_CODE_COVERAGE}"
|
||||
)
|
||||
fi
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||
|
||||
# flags specific to our test suite
|
||||
CXX_FLAGS="-Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=deprecated-copy"
|
||||
if [[ "${CXX}" == 'clang++' ]]; then
|
||||
CXX_FLAGS="${CXX_FLAGS} -Wno-error=unused-lambda-capture -Wno-error=gnu-zero-variadic-macro-arguments"
|
||||
fi
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-DCMAKE_CXX_FLAGS="${CXX_FLAGS}"
|
||||
)
|
||||
|
||||
${ANALYZE} cmake \
|
||||
-D CMAKE_C_COMPILER="${CC}" \
|
||||
-D CMAKE_CXX_COMPILER="${CXX}" \
|
||||
-D CMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-D CMAKE_INSTALL_PREFIX=install \
|
||||
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-D USE_SYSTEM_TINYXML=TRUE \
|
||||
-D BUILD_OPENMW=OFF \
|
||||
-D BUILD_BSATOOL=OFF \
|
||||
-D BUILD_ESMTOOL=OFF \
|
||||
-D BUILD_LAUNCHER=OFF \
|
||||
-D BUILD_MWINIIMPORTER=OFF \
|
||||
-D BUILD_ESSIMPORTER=OFF \
|
||||
-D BUILD_OPENCS=OFF \
|
||||
-D BUILD_WIZARD=OFF \
|
||||
-D BUILD_UNITTESTS=ON \
|
||||
-D GTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||
-D GMOCK_ROOT="${GOOGLETEST_DIR}" \
|
||||
"${CMAKE_CONF_OPTS[@]}" \
|
||||
-DBUILD_OPENMW=OFF \
|
||||
-DBUILD_BSATOOL=OFF \
|
||||
-DBUILD_ESMTOOL=OFF \
|
||||
-DBUILD_LAUNCHER=OFF \
|
||||
-DBUILD_MWINIIMPORTER=OFF \
|
||||
-DBUILD_ESSIMPORTER=OFF \
|
||||
-DBUILD_OPENCS=OFF \
|
||||
-DBUILD_WIZARD=OFF \
|
||||
-DBUILD_NAVMESHTOOL=OFF \
|
||||
-DBUILD_UNITTESTS=${BUILD_UNITTESTS} \
|
||||
-DBUILD_BENCHMARKS=${BUILD_BENCHMARKS} \
|
||||
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||
-DGMOCK_ROOT="${GOOGLETEST_DIR}" \
|
||||
..
|
||||
else
|
||||
${ANALYZE} cmake \
|
||||
-D CMAKE_C_COMPILER="${CC}" \
|
||||
-D CMAKE_CXX_COMPILER="${CXX}" \
|
||||
-D CMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-D USE_SYSTEM_TINYXML=TRUE \
|
||||
-D CMAKE_INSTALL_PREFIX=install \
|
||||
-D CMAKE_BUILD_TYPE=Debug \
|
||||
"${CMAKE_CONF_OPTS[@]}" \
|
||||
..
|
||||
fi
|
||||
|
@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
print_help() {
|
||||
echo "usage: $0 [group]..."
|
||||
echo
|
||||
echo " available groups: "${!GROUPED_DEPS[@]}""
|
||||
}
|
||||
|
||||
declare -rA GROUPED_DEPS=(
|
||||
[gcc]="binutils gcc build-essential cmake ccache curl unzip git pkg-config"
|
||||
[clang]="binutils clang make cmake ccache curl unzip git pkg-config"
|
||||
|
||||
# Common dependencies for building OpenMW.
|
||||
[openmw-deps]="
|
||||
libboost-filesystem-dev libboost-program-options-dev
|
||||
libboost-system-dev libboost-iostreams-dev
|
||||
|
||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||
libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev
|
||||
libbullet-dev liblz4-dev libpng-dev libjpeg-dev libluajit-5.1-dev
|
||||
librecast-dev libsqlite3-dev ca-certificates libicu-dev libyaml-cpp-dev
|
||||
"
|
||||
|
||||
# These dependencies can alternatively be built and linked statically.
|
||||
[openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev libsqlite3-dev"
|
||||
[clang-tidy]="clang-tidy"
|
||||
|
||||
# Pre-requisites for building MyGUI and OSG for static linking.
|
||||
#
|
||||
# * MyGUI and OSG: libsdl2-dev liblz4-dev libfreetype6-dev
|
||||
# * OSG: libgl-dev
|
||||
#
|
||||
# Plugins:
|
||||
# * DAE: libcollada-dom-dev libboost-system-dev libboost-filesystem-dev
|
||||
# * JPEG: libjpeg-dev
|
||||
# * PNG: libpng-dev
|
||||
[openmw-deps-static]="
|
||||
libcollada-dom-dev libfreetype6-dev libjpeg-dev libpng-dev
|
||||
libsdl2-dev libboost-system-dev libboost-filesystem-dev libgl-dev
|
||||
"
|
||||
|
||||
[openmw-coverage]="gcovr"
|
||||
|
||||
[openmw-integration-tests]="
|
||||
ca-certificates
|
||||
git
|
||||
git-lfs
|
||||
libavcodec58
|
||||
libavformat58
|
||||
libavutil56
|
||||
libboost-filesystem1.71.0
|
||||
libboost-iostreams1.71.0
|
||||
libboost-program-options1.71.0
|
||||
libboost-system1.71.0
|
||||
libbullet2.88
|
||||
libcollada-dom2.4-dp0
|
||||
libicu66
|
||||
libjpeg8
|
||||
libluajit-5.1-2
|
||||
liblz4-1
|
||||
libmyguiengine3debian1v5
|
||||
libopenal1
|
||||
libopenscenegraph161
|
||||
libpng16-16
|
||||
libqt5opengl5
|
||||
librecast1
|
||||
libsdl2-2.0-0
|
||||
libsqlite3-0
|
||||
libswresample3
|
||||
libswscale5
|
||||
libtinyxml2.6.2v5
|
||||
libyaml-cpp0.6
|
||||
python3-pip
|
||||
xvfb
|
||||
"
|
||||
)
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
>&2 print_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
deps=()
|
||||
for group in "$@"; do
|
||||
if [[ ! -v GROUPED_DEPS[$group] ]]; then
|
||||
>&2 echo "error: unknown group ${group}"
|
||||
exit 1
|
||||
fi
|
||||
deps+=(${GROUPED_DEPS[$group]})
|
||||
done
|
||||
|
||||
export APT_CACHE_DIR="${PWD}/apt-cache"
|
||||
set -x
|
||||
mkdir -pv "$APT_CACHE_DIR"
|
||||
apt-get update -yqq
|
||||
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common >/dev/null
|
||||
add-apt-repository -y ppa:openmw/openmw
|
||||
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null
|
@ -0,0 +1,10 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
git clone --depth=1 https://gitlab.com/OpenMW/example-suite.git
|
||||
|
||||
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24x60' \
|
||||
scripts/integration_tests.py --omw build/install/bin/openmw --workdir integration_tests_output example-suite/
|
||||
|
||||
ls integration_tests_output/*.osg_stats.log | while read v; do
|
||||
scripts/osg_stats.py --stats '.*' --regexp_match < "${v}"
|
||||
done
|
@ -0,0 +1,15 @@
|
||||
if(OPENMW_USE_SYSTEM_BENCHMARK)
|
||||
find_package(benchmark REQUIRED)
|
||||
endif()
|
||||
|
||||
openmw_add_executable(openmw_detournavigator_navmeshtilescache_benchmark detournavigator/navmeshtilescache.cpp)
|
||||
target_compile_features(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE cxx_std_17)
|
||||
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark benchmark::benchmark components)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
|
||||
target_precompile_headers(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE <algorithm>)
|
||||
endif()
|
@ -0,0 +1,290 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <components/detournavigator/navmeshtilescache.hpp>
|
||||
#include <components/esm3/loadland.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace DetourNavigator;
|
||||
|
||||
struct Key
|
||||
{
|
||||
AgentBounds mAgentBounds;
|
||||
TilePosition mTilePosition;
|
||||
RecastMesh mRecastMesh;
|
||||
};
|
||||
|
||||
struct Item
|
||||
{
|
||||
Key mKey;
|
||||
PreparedNavMeshData mValue;
|
||||
};
|
||||
|
||||
template <typename Random>
|
||||
osg::Vec2i generateVec2i(int max, Random& random)
|
||||
{
|
||||
std::uniform_int_distribution<int> distribution(0, max);
|
||||
return osg::Vec2i(distribution(random), distribution(random));
|
||||
}
|
||||
|
||||
template <typename Random>
|
||||
osg::Vec3f generateAgentHalfExtents(float min, float max, Random& random)
|
||||
{
|
||||
std::uniform_int_distribution<int> distribution(min, max);
|
||||
return osg::Vec3f(distribution(random), distribution(random), distribution(random));
|
||||
}
|
||||
|
||||
template <typename OutputIterator, typename Random>
|
||||
void generateVertices(OutputIterator out, std::size_t number, Random& random)
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
std::generate_n(out, 3 * (number - number % 3), [&] { return distribution(random); });
|
||||
}
|
||||
|
||||
template <typename OutputIterator, typename Random>
|
||||
void generateIndices(OutputIterator out, int max, std::size_t number, Random& random)
|
||||
{
|
||||
std::uniform_int_distribution<int> distribution(0, max);
|
||||
std::generate_n(out, number - number % 3, [&] { return distribution(random); });
|
||||
}
|
||||
|
||||
AreaType toAreaType(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return AreaType_null;
|
||||
case 1: return AreaType_water;
|
||||
case 2: return AreaType_door;
|
||||
case 3: return AreaType_pathgrid;
|
||||
case 4: return AreaType_ground;
|
||||
}
|
||||
return AreaType_null;
|
||||
}
|
||||
|
||||
template <typename Random>
|
||||
AreaType generateAreaType(Random& random)
|
||||
{
|
||||
std::uniform_int_distribution<int> distribution(0, 4);
|
||||
return toAreaType(distribution(random));
|
||||
}
|
||||
|
||||
template <typename OutputIterator, typename Random>
|
||||
void generateAreaTypes(OutputIterator out, std::size_t triangles, Random& random)
|
||||
{
|
||||
std::generate_n(out, triangles, [&] { return generateAreaType(random); });
|
||||
}
|
||||
|
||||
template <typename OutputIterator, typename Random>
|
||||
void generateWater(OutputIterator out, std::size_t count, Random& random)
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
std::generate_n(out, count, [&] {
|
||||
return CellWater {generateVec2i(1000, random), Water {ESM::Land::REAL_SIZE, distribution(random)}};
|
||||
});
|
||||
}
|
||||
|
||||
template <class Random>
|
||||
Mesh generateMesh(std::size_t triangles, Random& random)
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
std::vector<float> vertices;
|
||||
std::vector<int> indices;
|
||||
std::vector<AreaType> areaTypes;
|
||||
if (distribution(random) < 0.939)
|
||||
{
|
||||
generateVertices(std::back_inserter(vertices), triangles * 2.467, random);
|
||||
generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.279, random);
|
||||
generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);
|
||||
}
|
||||
return Mesh(std::move(indices), std::move(vertices), std::move(areaTypes));
|
||||
}
|
||||
|
||||
template <class Random>
|
||||
Heightfield generateHeightfield(Random& random)
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
Heightfield result;
|
||||
result.mCellPosition = generateVec2i(1000, random);
|
||||
result.mCellSize = ESM::Land::REAL_SIZE;
|
||||
result.mMinHeight = distribution(random);
|
||||
result.mMaxHeight = result.mMinHeight + 1.0;
|
||||
result.mLength = static_cast<std::uint8_t>(ESM::Land::LAND_SIZE);
|
||||
std::generate_n(std::back_inserter(result.mHeights), ESM::Land::LAND_NUM_VERTS, [&]
|
||||
{
|
||||
return distribution(random);
|
||||
});
|
||||
result.mOriginalSize = ESM::Land::LAND_SIZE;
|
||||
result.mMinX = 0;
|
||||
result.mMinY = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Random>
|
||||
FlatHeightfield generateFlatHeightfield(Random& random)
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
FlatHeightfield result;
|
||||
result.mCellPosition = generateVec2i(1000, random);
|
||||
result.mCellSize = ESM::Land::REAL_SIZE;
|
||||
result.mHeight = distribution(random);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Random>
|
||||
Key generateKey(std::size_t triangles, Random& random)
|
||||
{
|
||||
const CollisionShapeType agentShapeType = CollisionShapeType::Aabb;
|
||||
const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random);
|
||||
const TilePosition tilePosition = generateVec2i(10000, random);
|
||||
const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);
|
||||
const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);
|
||||
Mesh mesh = generateMesh(triangles, random);
|
||||
std::vector<CellWater> water;
|
||||
generateWater(std::back_inserter(water), 1, random);
|
||||
RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water),
|
||||
{generateHeightfield(random)}, {generateFlatHeightfield(random)}, {});
|
||||
return Key {AgentBounds {agentShapeType, agentHalfExtents}, tilePosition, std::move(recastMesh)};
|
||||
}
|
||||
|
||||
constexpr std::size_t trianglesPerTile = 239;
|
||||
|
||||
template <typename OutputIterator, typename Random>
|
||||
void generateKeys(OutputIterator out, std::size_t count, Random& random)
|
||||
{
|
||||
std::generate_n(out, count, [&] { return generateKey(trianglesPerTile, random); });
|
||||
}
|
||||
|
||||
template <typename OutputIterator, typename Random>
|
||||
void fillCache(OutputIterator out, Random& random, NavMeshTilesCache& cache)
|
||||
{
|
||||
std::size_t size = cache.getStats().mNavMeshCacheSize;
|
||||
|
||||
while (true)
|
||||
{
|
||||
Key key = generateKey(trianglesPerTile, random);
|
||||
cache.set(key.mAgentBounds, key.mTilePosition, key.mRecastMesh,
|
||||
std::make_unique<PreparedNavMeshData>());
|
||||
*out++ = std::move(key);
|
||||
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
|
||||
if (size >= newSize)
|
||||
break;
|
||||
size = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t maxCacheSize, int hitPercentage>
|
||||
void getFromFilledCache(benchmark::State& state)
|
||||
{
|
||||
NavMeshTilesCache cache(maxCacheSize);
|
||||
std::minstd_rand random;
|
||||
std::vector<Key> keys;
|
||||
fillCache(std::back_inserter(keys), random, cache);
|
||||
generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random);
|
||||
std::size_t n = 0;
|
||||
|
||||
while (state.KeepRunning())
|
||||
{
|
||||
const auto& key = keys[n++ % keys.size()];
|
||||
const auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
void getFromFilledCache_1m_100hit(benchmark::State& state)
|
||||
{
|
||||
getFromFilledCache<1 * 1024 * 1024, 100>(state);
|
||||
}
|
||||
|
||||
void getFromFilledCache_4m_100hit(benchmark::State& state)
|
||||
{
|
||||
getFromFilledCache<4 * 1024 * 1024, 100>(state);
|
||||
}
|
||||
|
||||
void getFromFilledCache_16m_100hit(benchmark::State& state)
|
||||
{
|
||||
getFromFilledCache<16 * 1024 * 1024, 100>(state);
|
||||
}
|
||||
|
||||
void getFromFilledCache_64m_100hit(benchmark::State& state)
|
||||
{
|
||||
getFromFilledCache<64 * 1024 * 1024, 100>(state);
|
||||
}
|
||||
|
||||
void getFromFilledCache_1m_70hit(benchmark::State& state)
|
||||
{
|
||||
getFromFilledCache<1 * 1024 * 1024, 70>(state);
|
||||
}
|
||||
|
||||
void getFromFilledCache_4m_70hit(benchmark::State& state)
|
||||
{
|
||||
getFromFilledCache<4 * 1024 * 1024, 70>(state);
|
||||
}
|
||||
|
||||
void getFromFilledCache_16m_70hit(benchmark::State& state)
|
||||
{
|
||||
getFromFilledCache<16 * 1024 * 1024, 70>(state);
|
||||
}
|
||||
|
||||
void getFromFilledCache_64m_70hit(benchmark::State& state)
|
||||
{
|
||||
getFromFilledCache<64 * 1024 * 1024, 70>(state);
|
||||
}
|
||||
|
||||
template <std::size_t maxCacheSize>
|
||||
void setToBoundedNonEmptyCache(benchmark::State& state)
|
||||
{
|
||||
NavMeshTilesCache cache(maxCacheSize);
|
||||
std::minstd_rand random;
|
||||
std::vector<Key> keys;
|
||||
fillCache(std::back_inserter(keys), random, cache);
|
||||
generateKeys(std::back_inserter(keys), keys.size() * 2, random);
|
||||
std::reverse(keys.begin(), keys.end());
|
||||
std::size_t n = 0;
|
||||
|
||||
while (state.KeepRunning())
|
||||
{
|
||||
const auto& key = keys[n++ % keys.size()];
|
||||
const auto result = cache.set(key.mAgentBounds, key.mTilePosition, key.mRecastMesh,
|
||||
std::make_unique<PreparedNavMeshData>());
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
void setToBoundedNonEmptyCache_1m(benchmark::State& state)
|
||||
{
|
||||
setToBoundedNonEmptyCache<1 * 1024 * 1024>(state);
|
||||
}
|
||||
|
||||
void setToBoundedNonEmptyCache_4m(benchmark::State& state)
|
||||
{
|
||||
setToBoundedNonEmptyCache<4 * 1024 * 1024>(state);
|
||||
}
|
||||
|
||||
void setToBoundedNonEmptyCache_16m(benchmark::State& state)
|
||||
{
|
||||
setToBoundedNonEmptyCache<16 * 1024 * 1024>(state);
|
||||
}
|
||||
|
||||
void setToBoundedNonEmptyCache_64m(benchmark::State& state)
|
||||
{
|
||||
setToBoundedNonEmptyCache<64 * 1024 * 1024>(state);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
BENCHMARK(getFromFilledCache_1m_100hit);
|
||||
BENCHMARK(getFromFilledCache_4m_100hit);
|
||||
BENCHMARK(getFromFilledCache_16m_100hit);
|
||||
BENCHMARK(getFromFilledCache_64m_100hit);
|
||||
BENCHMARK(getFromFilledCache_1m_70hit);
|
||||
BENCHMARK(getFromFilledCache_4m_70hit);
|
||||
BENCHMARK(getFromFilledCache_16m_70hit);
|
||||
BENCHMARK(getFromFilledCache_64m_70hit);
|
||||
BENCHMARK(setToBoundedNonEmptyCache_1m);
|
||||
BENCHMARK(setToBoundedNonEmptyCache_4m);
|
||||
BENCHMARK(setToBoundedNonEmptyCache_16m);
|
||||
BENCHMARK(setToBoundedNonEmptyCache_64m);
|
||||
|
||||
BENCHMARK_MAIN();
|
@ -0,0 +1,27 @@
|
||||
set(BULLETMESHTOOL
|
||||
main.cpp
|
||||
)
|
||||
source_group(apps\\bulletobjecttool FILES ${BULLETMESHTOOL})
|
||||
|
||||
openmw_add_executable(openmw-bulletobjecttool ${BULLETMESHTOOL})
|
||||
|
||||
target_link_libraries(openmw-bulletobjecttool
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
components
|
||||
)
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions(--coverage)
|
||||
target_link_libraries(openmw-bulletobjecttool gcov)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
install(TARGETS openmw-bulletobjecttool RUNTIME DESTINATION ".")
|
||||
endif()
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
|
||||
target_precompile_headers(openmw-bulletobjecttool PRIVATE
|
||||
<string>
|
||||
<vector>
|
||||
)
|
||||
endif()
|
@ -0,0 +1,203 @@
|
||||
#include <components/debug/debugging.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
#include <components/esmloader/esmdata.hpp>
|
||||
#include <components/esmloader/load.hpp>
|
||||
#include <components/fallback/fallback.hpp>
|
||||
#include <components/fallback/validate.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/resource/bulletshapemanager.hpp>
|
||||
#include <components/resource/foreachbulletobject.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/resource/niffilemanager.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
#include <components/platform/platform.hpp>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <charconv>
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
using StringsVector = std::vector<std::string>;
|
||||
|
||||
bpo::options_description makeOptionsDescription()
|
||||
{
|
||||
using Fallback::FallbackMap;
|
||||
|
||||
bpo::options_description result;
|
||||
|
||||
result.add_options()
|
||||
("help", "print help message")
|
||||
|
||||
("version", "print version information and quit")
|
||||
|
||||
("data", bpo::value<Files::MaybeQuotedPathContainer>()->default_value(Files::MaybeQuotedPathContainer(), "data")
|
||||
->multitoken()->composing(), "set data directories (later directories have higher priority)")
|
||||
|
||||
("data-local", bpo::value<Files::MaybeQuotedPathContainer::value_type>()->default_value(Files::MaybeQuotedPathContainer::value_type(), ""),
|
||||
"set local data directory (highest priority)")
|
||||
|
||||
("fallback-archive", bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")
|
||||
->multitoken()->composing(), "set fallback BSA archives (later archives have higher priority)")
|
||||
|
||||
("resources", bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
|
||||
"set resources directory")
|
||||
|
||||
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
|
||||
->multitoken()->composing(), "content file(s): esm/esp, or omwgame/omwaddon/omwscripts")
|
||||
|
||||
("fs-strict", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "strict file system handling (no case folding)")
|
||||
|
||||
("encoding", bpo::value<std::string>()->
|
||||
default_value("win1252"),
|
||||
"Character encoding used in OpenMW game messages:\n"
|
||||
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
|
||||
"\n\twin1252 - Western European (Latin) alphabet, used by default")
|
||||
|
||||
("fallback", bpo::value<FallbackMap>()->default_value(FallbackMap(), "")
|
||||
->multitoken()->composing(), "fallback values")
|
||||
;
|
||||
|
||||
Files::ConfigurationManager::addCommonOptions(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct WriteArray
|
||||
{
|
||||
const float (&mValue)[3];
|
||||
|
||||
friend std::ostream& operator <<(std::ostream& stream, const WriteArray& value)
|
||||
{
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
stream << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue[i] << ", ";
|
||||
return stream << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue[2];
|
||||
}
|
||||
};
|
||||
|
||||
std::string toHex(std::string_view value)
|
||||
{
|
||||
std::string buffer(value.size() * 2, '0');
|
||||
char* out = buffer.data();
|
||||
for (const char v : value)
|
||||
{
|
||||
const std::ptrdiff_t space = static_cast<std::ptrdiff_t>(static_cast<std::uint8_t>(v) <= 0xf);
|
||||
const auto [ptr, ec] = std::to_chars(out + space, out + space + 2, static_cast<std::uint8_t>(v), 16);
|
||||
if (ec != std::errc())
|
||||
throw std::system_error(std::make_error_code(ec));
|
||||
out += 2;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int runBulletObjectTool(int argc, char *argv[])
|
||||
{
|
||||
Platform::init();
|
||||
|
||||
bpo::options_description desc = makeOptionsDescription();
|
||||
|
||||
bpo::parsed_options options = bpo::command_line_parser(argc, argv)
|
||||
.options(desc).allow_unregistered().run();
|
||||
bpo::variables_map variables;
|
||||
|
||||
bpo::store(options, variables);
|
||||
bpo::notify(variables);
|
||||
|
||||
if (variables.find("help") != variables.end())
|
||||
{
|
||||
getRawStdout() << desc << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Files::ConfigurationManager config;
|
||||
|
||||
bpo::variables_map composingVariables = Files::separateComposingVariables(variables, desc);
|
||||
config.readConfiguration(variables, desc);
|
||||
Files::mergeComposingVariables(variables, composingVariables, desc);
|
||||
|
||||
const std::string encoding(variables["encoding"].as<std::string>());
|
||||
Log(Debug::Info) << ToUTF8::encodingUsingMessage(encoding);
|
||||
ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(encoding));
|
||||
|
||||
Files::PathContainer dataDirs(asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>()));
|
||||
|
||||
auto local = variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>();
|
||||
if (!local.empty())
|
||||
dataDirs.push_back(std::move(local));
|
||||
|
||||
config.filterOutNonExistingPaths(dataDirs);
|
||||
|
||||
const auto fsStrict = variables["fs-strict"].as<bool>();
|
||||
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
|
||||
Version::Version v = Version::getOpenmwVersion(resDir.string());
|
||||
Log(Debug::Info) << v.describe();
|
||||
dataDirs.insert(dataDirs.begin(), resDir / "vfs");
|
||||
const auto fileCollections = Files::Collections(dataDirs, !fsStrict);
|
||||
const auto archives = variables["fallback-archive"].as<StringsVector>();
|
||||
const auto contentFiles = variables["content"].as<StringsVector>();
|
||||
|
||||
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap);
|
||||
|
||||
VFS::Manager vfs(fsStrict);
|
||||
|
||||
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
||||
|
||||
Settings::Manager settings;
|
||||
settings.load(config);
|
||||
|
||||
ESM::ReadersCache readers;
|
||||
EsmLoader::Query query;
|
||||
query.mLoadActivators = true;
|
||||
query.mLoadCells = true;
|
||||
query.mLoadContainers = true;
|
||||
query.mLoadDoors = true;
|
||||
query.mLoadGameSettings = true;
|
||||
query.mLoadLands = true;
|
||||
query.mLoadStatics = true;
|
||||
const EsmLoader::EsmData esmData = EsmLoader::loadEsmData(query, contentFiles, fileCollections, readers, &encoder);
|
||||
|
||||
Resource::ImageManager imageManager(&vfs);
|
||||
Resource::NifFileManager nifFileManager(&vfs);
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager);
|
||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager);
|
||||
|
||||
Resource::forEachBulletObject(readers, vfs, bulletShapeManager, esmData,
|
||||
[] (const ESM::Cell& cell, const Resource::BulletObject& object)
|
||||
{
|
||||
Log(Debug::Verbose) << "Found bullet object in " << (cell.isExterior() ? "exterior" : "interior")
|
||||
<< " cell \"" << cell.getDescription() << "\":"
|
||||
<< " fileName=\"" << object.mShape->mFileName << '"'
|
||||
<< " fileHash=" << toHex(object.mShape->mFileHash)
|
||||
<< " collisionShape=" << std::boolalpha << (object.mShape->mCollisionShape == nullptr)
|
||||
<< " avoidCollisionShape=" << std::boolalpha << (object.mShape->mAvoidCollisionShape == nullptr)
|
||||
<< " position=(" << WriteArray {object.mPosition.pos} << ')'
|
||||
<< " rotation=(" << WriteArray {object.mPosition.rot} << ')'
|
||||
<< " scale=" << std::setprecision(std::numeric_limits<float>::max_exponent10) << object.mScale;
|
||||
});
|
||||
|
||||
Log(Debug::Info) << "Done";
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return wrapApplication(runBulletObjectTool, argc, argv, "BulletObjectTool");
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#ifndef OPENMW_ESMTOOL_ARGUMENTS_H
|
||||
#define OPENMW_ESMTOOL_ARGUMENTS_H
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include <components/esm/format.hpp>
|
||||
|
||||
namespace EsmTool
|
||||
{
|
||||
struct Arguments
|
||||
{
|
||||
std::optional<ESM::Format> mRawFormat;
|
||||
bool quiet_given = false;
|
||||
bool loadcells_given = false;
|
||||
bool plain_given = false;
|
||||
|
||||
std::string mode;
|
||||
std::string encoding;
|
||||
std::string filename;
|
||||
std::string outname;
|
||||
|
||||
std::vector<std::string> types;
|
||||
std::string name;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,329 @@
|
||||
#include "tes4.hpp"
|
||||
#include "arguments.hpp"
|
||||
#include "labels.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
#include <components/esm/esmcommon.hpp>
|
||||
#include <components/esm4/reader.hpp>
|
||||
#include <components/esm4/records.hpp>
|
||||
|
||||
namespace EsmTool
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct Params
|
||||
{
|
||||
const bool mQuite;
|
||||
|
||||
explicit Params(const Arguments& info)
|
||||
: mQuite(info.quiet_given || info.mode == "clone")
|
||||
{}
|
||||
};
|
||||
|
||||
std::string toString(ESM4::GroupType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ESM4::Grp_RecordType: return "RecordType";
|
||||
case ESM4::Grp_WorldChild: return "WorldChild";
|
||||
case ESM4::Grp_InteriorCell: return "InteriorCell";
|
||||
case ESM4::Grp_InteriorSubCell: return "InteriorSubCell";
|
||||
case ESM4::Grp_ExteriorCell: return "ExteriorCell";
|
||||
case ESM4::Grp_ExteriorSubCell: return "ExteriorSubCell";
|
||||
case ESM4::Grp_CellChild: return "CellChild";
|
||||
case ESM4::Grp_TopicChild: return "TopicChild";
|
||||
case ESM4::Grp_CellPersistentChild: return "CellPersistentChild";
|
||||
case ESM4::Grp_CellTemporaryChild: return "CellTemporaryChild";
|
||||
case ESM4::Grp_CellVisibleDistChild: return "CellVisibleDistChild";
|
||||
}
|
||||
|
||||
return "Unknown (" + std::to_string(type) + ")";
|
||||
}
|
||||
|
||||
template <class T, class = std::void_t<>>
|
||||
struct HasFormId : std::false_type {};
|
||||
|
||||
template <class T>
|
||||
struct HasFormId<T, std::void_t<decltype(T::mFormId)>> : std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool hasFormId = HasFormId<T>::value;
|
||||
|
||||
template <class T, class = std::void_t<>>
|
||||
struct HasFlags : std::false_type {};
|
||||
|
||||
template <class T>
|
||||
struct HasFlags<T, std::void_t<decltype(T::mFlags)>> : std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool hasFlags = HasFlags<T>::value;
|
||||
|
||||
template <class T>
|
||||
void readTypedRecord(const Params& params, ESM4::Reader& reader)
|
||||
{
|
||||
reader.getRecordData();
|
||||
|
||||
T value;
|
||||
value.load(reader);
|
||||
|
||||
if (params.mQuite)
|
||||
return;
|
||||
|
||||
std::cout << "\n Record: " << ESM::NAME(reader.hdr().record.typeId).toStringView();
|
||||
if constexpr (hasFormId<T>)
|
||||
std::cout << ' ' << value.mFormId;
|
||||
if constexpr (hasFlags<T>)
|
||||
std::cout << "\n Record flags: " << recordFlags(value.mFlags);
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
void readRecord(const Params& params, ESM4::Reader& reader)
|
||||
{
|
||||
switch (static_cast<ESM4::RecordTypes>(reader.hdr().record.typeId))
|
||||
{
|
||||
case ESM4::REC_AACT: break;
|
||||
case ESM4::REC_ACHR: return readTypedRecord<ESM4::ActorCharacter>(params, reader);
|
||||
case ESM4::REC_ACRE: return readTypedRecord<ESM4::ActorCreature>(params, reader);
|
||||
case ESM4::REC_ACTI: return readTypedRecord<ESM4::Activator>(params, reader);
|
||||
case ESM4::REC_ADDN: break;
|
||||
case ESM4::REC_ALCH: return readTypedRecord<ESM4::Potion>(params, reader);
|
||||
case ESM4::REC_ALOC: return readTypedRecord<ESM4::MediaLocationController>(params, reader);
|
||||
case ESM4::REC_AMMO: return readTypedRecord<ESM4::Ammunition>(params, reader);
|
||||
case ESM4::REC_ANIO: return readTypedRecord<ESM4::AnimObject>(params, reader);
|
||||
case ESM4::REC_APPA: return readTypedRecord<ESM4::Apparatus>(params, reader);
|
||||
case ESM4::REC_ARMA: return readTypedRecord<ESM4::ArmorAddon>(params, reader);
|
||||
case ESM4::REC_ARMO: return readTypedRecord<ESM4::Armor>(params, reader);
|
||||
case ESM4::REC_ARTO: break;
|
||||
case ESM4::REC_ASPC: return readTypedRecord<ESM4::AcousticSpace>(params, reader);
|
||||
case ESM4::REC_ASTP: break;
|
||||
case ESM4::REC_AVIF: break;
|
||||
case ESM4::REC_BOOK: return readTypedRecord<ESM4::Book>(params, reader);
|
||||
case ESM4::REC_BPTD: return readTypedRecord<ESM4::BodyPartData>(params, reader);
|
||||
case ESM4::REC_CAMS: break;
|
||||
case ESM4::REC_CCRD: break;
|
||||
case ESM4::REC_CELL: return readTypedRecord<ESM4::Cell>(params, reader);
|
||||
case ESM4::REC_CLAS: return readTypedRecord<ESM4::Class>(params, reader);
|
||||
case ESM4::REC_CLFM: return readTypedRecord<ESM4::Colour>(params, reader);
|
||||
case ESM4::REC_CLMT: break;
|
||||
case ESM4::REC_CLOT: return readTypedRecord<ESM4::Clothing>(params, reader);
|
||||
case ESM4::REC_CMNY: break;
|
||||
case ESM4::REC_COBJ: break;
|
||||
case ESM4::REC_COLL: break;
|
||||
case ESM4::REC_CONT: return readTypedRecord<ESM4::Container>(params, reader);
|
||||
case ESM4::REC_CPTH: break;
|
||||
case ESM4::REC_CREA: return readTypedRecord<ESM4::Creature>(params, reader);
|
||||
case ESM4::REC_CSTY: break;
|
||||
case ESM4::REC_DEBR: break;
|
||||
case ESM4::REC_DIAL: return readTypedRecord<ESM4::Dialogue>(params, reader);
|
||||
case ESM4::REC_DLBR: break;
|
||||
case ESM4::REC_DLVW: break;
|
||||
case ESM4::REC_DOBJ: return readTypedRecord<ESM4::DefaultObj>(params, reader);
|
||||
case ESM4::REC_DOOR: return readTypedRecord<ESM4::Door>(params, reader);
|
||||
case ESM4::REC_DUAL: break;
|
||||
case ESM4::REC_ECZN: break;
|
||||
case ESM4::REC_EFSH: break;
|
||||
case ESM4::REC_ENCH: break;
|
||||
case ESM4::REC_EQUP: break;
|
||||
case ESM4::REC_EXPL: break;
|
||||
case ESM4::REC_EYES: return readTypedRecord<ESM4::Eyes>(params, reader);
|
||||
case ESM4::REC_FACT: break;
|
||||
case ESM4::REC_FLOR: return readTypedRecord<ESM4::Flora>(params, reader);
|
||||
case ESM4::REC_FLST: return readTypedRecord<ESM4::FormIdList>(params, reader);
|
||||
case ESM4::REC_FSTP: break;
|
||||
case ESM4::REC_FSTS: break;
|
||||
case ESM4::REC_FURN: return readTypedRecord<ESM4::Furniture>(params, reader);
|
||||
case ESM4::REC_GLOB: return readTypedRecord<ESM4::GlobalVariable>(params, reader);
|
||||
case ESM4::REC_GMST: break;
|
||||
case ESM4::REC_GRAS: return readTypedRecord<ESM4::Grass>(params, reader);
|
||||
case ESM4::REC_GRUP: break;
|
||||
case ESM4::REC_HAIR: return readTypedRecord<ESM4::Hair>(params, reader);
|
||||
case ESM4::REC_HAZD: break;
|
||||
case ESM4::REC_HDPT: return readTypedRecord<ESM4::HeadPart>(params, reader);
|
||||
case ESM4::REC_IDLE:
|
||||
// FIXME: ESM4::IdleAnimation::load does not work with Oblivion.esm
|
||||
// return readTypedRecord<ESM4::IdleAnimation>(params, reader);
|
||||
break;
|
||||
case ESM4::REC_IDLM: return readTypedRecord<ESM4::IdleMarker>(params, reader);
|
||||
case ESM4::REC_IMAD: break;
|
||||
case ESM4::REC_IMGS: break;
|
||||
case ESM4::REC_IMOD: return readTypedRecord<ESM4::ItemMod>(params, reader);
|
||||
case ESM4::REC_INFO: return readTypedRecord<ESM4::DialogInfo>(params, reader);
|
||||
case ESM4::REC_INGR: return readTypedRecord<ESM4::Ingredient>(params, reader);
|
||||
case ESM4::REC_IPCT: break;
|
||||
case ESM4::REC_IPDS: break;
|
||||
case ESM4::REC_KEYM: return readTypedRecord<ESM4::Key>(params, reader);
|
||||
case ESM4::REC_KYWD: break;
|
||||
case ESM4::REC_LAND: return readTypedRecord<ESM4::Land>(params, reader);
|
||||
case ESM4::REC_LCRT: break;
|
||||
case ESM4::REC_LCTN: break;
|
||||
case ESM4::REC_LGTM: return readTypedRecord<ESM4::LightingTemplate>(params, reader);
|
||||
case ESM4::REC_LIGH: return readTypedRecord<ESM4::Light>(params, reader);
|
||||
case ESM4::REC_LSCR: break;
|
||||
case ESM4::REC_LTEX: return readTypedRecord<ESM4::LandTexture>(params, reader);
|
||||
case ESM4::REC_LVLC: return readTypedRecord<ESM4::LevelledCreature>(params, reader);
|
||||
case ESM4::REC_LVLI: return readTypedRecord<ESM4::LevelledItem>(params, reader);
|
||||
case ESM4::REC_LVLN: return readTypedRecord<ESM4::LevelledNpc>(params, reader);
|
||||
case ESM4::REC_LVSP: break;
|
||||
case ESM4::REC_MATO: return readTypedRecord<ESM4::Material>(params, reader);
|
||||
case ESM4::REC_MATT: break;
|
||||
case ESM4::REC_MESG: break;
|
||||
case ESM4::REC_MGEF: break;
|
||||
case ESM4::REC_MISC: return readTypedRecord<ESM4::MiscItem>(params, reader);
|
||||
case ESM4::REC_MOVT: break;
|
||||
case ESM4::REC_MSET: return readTypedRecord<ESM4::MediaSet>(params, reader);
|
||||
case ESM4::REC_MSTT: return readTypedRecord<ESM4::MovableStatic>(params, reader);
|
||||
case ESM4::REC_MUSC: return readTypedRecord<ESM4::Music>(params, reader);
|
||||
case ESM4::REC_MUST: break;
|
||||
case ESM4::REC_NAVI: return readTypedRecord<ESM4::Navigation>(params, reader);
|
||||
case ESM4::REC_NAVM: return readTypedRecord<ESM4::NavMesh>(params, reader);
|
||||
case ESM4::REC_NOTE: return readTypedRecord<ESM4::Note>(params, reader);
|
||||
case ESM4::REC_NPC_: return readTypedRecord<ESM4::Npc>(params, reader);
|
||||
case ESM4::REC_OTFT: return readTypedRecord<ESM4::Outfit>(params, reader);
|
||||
case ESM4::REC_PACK: return readTypedRecord<ESM4::AIPackage>(params, reader);
|
||||
case ESM4::REC_PERK: break;
|
||||
case ESM4::REC_PGRD: return readTypedRecord<ESM4::Pathgrid>(params, reader);
|
||||
case ESM4::REC_PGRE: return readTypedRecord<ESM4::PlacedGrenade>(params, reader);
|
||||
case ESM4::REC_PHZD: break;
|
||||
case ESM4::REC_PROJ: break;
|
||||
case ESM4::REC_PWAT: return readTypedRecord<ESM4::PlaceableWater>(params, reader);
|
||||
case ESM4::REC_QUST: return readTypedRecord<ESM4::Quest>(params, reader);
|
||||
case ESM4::REC_RACE: return readTypedRecord<ESM4::Race>(params, reader);
|
||||
case ESM4::REC_REFR: return readTypedRecord<ESM4::Reference>(params, reader);
|
||||
case ESM4::REC_REGN: return readTypedRecord<ESM4::Region>(params, reader);
|
||||
case ESM4::REC_RELA: break;
|
||||
case ESM4::REC_REVB: break;
|
||||
case ESM4::REC_RFCT: break;
|
||||
case ESM4::REC_ROAD: return readTypedRecord<ESM4::Road>(params, reader);
|
||||
case ESM4::REC_SBSP: return readTypedRecord<ESM4::SubSpace>(params, reader);
|
||||
case ESM4::REC_SCEN: break;
|
||||
case ESM4::REC_SCOL: return readTypedRecord<ESM4::StaticCollection>(params, reader);
|
||||
case ESM4::REC_SCPT: return readTypedRecord<ESM4::Script>(params, reader);
|
||||
case ESM4::REC_SCRL: return readTypedRecord<ESM4::Scroll>(params, reader);
|
||||
case ESM4::REC_SGST: return readTypedRecord<ESM4::SigilStone>(params, reader);
|
||||
case ESM4::REC_SHOU: break;
|
||||
case ESM4::REC_SLGM: return readTypedRecord<ESM4::SoulGem>(params, reader);
|
||||
case ESM4::REC_SMBN: break;
|
||||
case ESM4::REC_SMEN: break;
|
||||
case ESM4::REC_SMQN: break;
|
||||
case ESM4::REC_SNCT: break;
|
||||
case ESM4::REC_SNDR: return readTypedRecord<ESM4::SoundReference>(params, reader);
|
||||
case ESM4::REC_SOPM: break;
|
||||
case ESM4::REC_SOUN: return readTypedRecord<ESM4::Sound>(params, reader);
|
||||
case ESM4::REC_SPEL: break;
|
||||
case ESM4::REC_SPGD: break;
|
||||
case ESM4::REC_STAT: return readTypedRecord<ESM4::Static>(params, reader);
|
||||
case ESM4::REC_TACT: return readTypedRecord<ESM4::TalkingActivator>(params, reader);
|
||||
case ESM4::REC_TERM: return readTypedRecord<ESM4::Terminal>(params, reader);
|
||||
case ESM4::REC_TES4: return readTypedRecord<ESM4::Header>(params, reader);
|
||||
case ESM4::REC_TREE: return readTypedRecord<ESM4::Tree>(params, reader);
|
||||
case ESM4::REC_TXST: return readTypedRecord<ESM4::TextureSet>(params, reader);
|
||||
case ESM4::REC_VTYP: break;
|
||||
case ESM4::REC_WATR: break;
|
||||
case ESM4::REC_WEAP: return readTypedRecord<ESM4::Weapon>(params, reader);
|
||||
case ESM4::REC_WOOP: break;
|
||||
case ESM4::REC_WRLD: return readTypedRecord<ESM4::World>(params, reader);
|
||||
case ESM4::REC_WTHR: break;
|
||||
}
|
||||
|
||||
if (!params.mQuite)
|
||||
std::cout << "\n Unsupported record: " << ESM::NAME(reader.hdr().record.typeId).toStringView() << '\n';
|
||||
|
||||
reader.skipRecordData();
|
||||
}
|
||||
|
||||
bool readItem(const Params& params, ESM4::Reader& reader);
|
||||
|
||||
bool readGroup(const Params& params, ESM4::Reader& reader)
|
||||
{
|
||||
const ESM4::RecordHeader& header = reader.hdr();
|
||||
|
||||
if (!params.mQuite)
|
||||
std::cout << "\nGroup: " << toString(static_cast<ESM4::GroupType>(header.group.type))
|
||||
<< " " << ESM::NAME(header.group.typeId).toStringView() << '\n';
|
||||
|
||||
switch (static_cast<ESM4::GroupType>(header.group.type))
|
||||
{
|
||||
case ESM4::Grp_RecordType:
|
||||
case ESM4::Grp_InteriorCell:
|
||||
case ESM4::Grp_InteriorSubCell:
|
||||
case ESM4::Grp_ExteriorCell:
|
||||
case ESM4::Grp_ExteriorSubCell:
|
||||
reader.enterGroup();
|
||||
return readItem(params, reader);
|
||||
case ESM4::Grp_WorldChild:
|
||||
case ESM4::Grp_CellChild:
|
||||
case ESM4::Grp_TopicChild:
|
||||
case ESM4::Grp_CellPersistentChild:
|
||||
case ESM4::Grp_CellTemporaryChild:
|
||||
case ESM4::Grp_CellVisibleDistChild:
|
||||
reader.adjustGRUPFormId();
|
||||
reader.enterGroup();
|
||||
if (!reader.hasMoreRecs())
|
||||
return false;
|
||||
return readItem(params, reader);
|
||||
}
|
||||
|
||||
reader.skipGroup();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readItem(const Params& params, ESM4::Reader& reader)
|
||||
{
|
||||
if (!reader.getRecordHeader() || !reader.hasMoreRecs())
|
||||
return false;
|
||||
|
||||
const ESM4::RecordHeader& header = reader.hdr();
|
||||
|
||||
if (header.record.typeId == ESM4::REC_GRUP)
|
||||
return readGroup(params, reader);
|
||||
|
||||
readRecord(params, reader);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int loadTes4(const Arguments& info, std::unique_ptr<std::ifstream>&& stream)
|
||||
{
|
||||
std::cout << "Loading TES4 file: " << info.filename << '\n';
|
||||
|
||||
try
|
||||
{
|
||||
const ToUTF8::StatelessUtf8Encoder encoder(ToUTF8::calculateEncoding(info.encoding));
|
||||
ESM4::Reader reader(std::move(stream), info.filename);
|
||||
reader.setEncoder(&encoder);
|
||||
const Params params(info);
|
||||
|
||||
if (!params.mQuite)
|
||||
{
|
||||
std::cout << "Author: " << reader.getAuthor() << '\n'
|
||||
<< "Description: " << reader.getDesc() << '\n'
|
||||
<< "File format version: " << reader.esmVersion() << '\n';
|
||||
|
||||
if (const std::vector<ESM::MasterData>& masterData = reader.getGameFiles(); !masterData.empty())
|
||||
{
|
||||
std::cout << "Masters:" << '\n';
|
||||
for (const auto& master : masterData)
|
||||
std::cout << " " << master.name << ", " << master.size << " bytes\n";
|
||||
}
|
||||
}
|
||||
|
||||
while (reader.hasMoreRecs())
|
||||
{
|
||||
reader.exitGroupCheck();
|
||||
if (!readItem(params, reader))
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "\nERROR:\n\n " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#ifndef OPENMW_ESMTOOL_TES4_H
|
||||
#define OPENMW_ESMTOOL_TES4_H
|
||||
|
||||
#include <fstream>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
|
||||
namespace EsmTool
|
||||
{
|
||||
struct Arguments;
|
||||
|
||||
int loadTes4(const Arguments& info, std::unique_ptr<std::ifstream>&& stream);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,60 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include <apps/openmw/mwsound/alext.h>
|
||||
|
||||
#include "openalutil.hpp"
|
||||
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
||||
#endif
|
||||
|
||||
std::vector<std::string> Launcher::enumerateOpenALDevices()
|
||||
{
|
||||
std::vector<std::string> devlist;
|
||||
const ALCchar *devnames;
|
||||
|
||||
if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT"))
|
||||
{
|
||||
devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
|
||||
}
|
||||
else
|
||||
{
|
||||
devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
|
||||
}
|
||||
|
||||
while(devnames && *devnames)
|
||||
{
|
||||
devlist.emplace_back(devnames);
|
||||
devnames += strlen(devnames)+1;
|
||||
}
|
||||
return devlist;
|
||||
}
|
||||
|
||||
std::vector<std::string> Launcher::enumerateOpenALDevicesHrtf()
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
|
||||
ALCdevice *device = alcOpenDevice(nullptr);
|
||||
if(device)
|
||||
{
|
||||
if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
|
||||
{
|
||||
LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
|
||||
void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT");
|
||||
memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr));
|
||||
ALCint num_hrtf;
|
||||
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
|
||||
ret.reserve(num_hrtf);
|
||||
for(ALCint i = 0;i < num_hrtf;++i)
|
||||
{
|
||||
const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
|
||||
if(strcmp(entry, "") == 0)
|
||||
break;
|
||||
ret.emplace_back(entry);
|
||||
}
|
||||
}
|
||||
alcCloseDevice(device);
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
std::vector<std::string> enumerateOpenALDevices();
|
||||
std::vector<std::string> enumerateOpenALDevicesHrtf();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue