mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-23 18:39:40 +00:00
Merge branch 'master' of https://gitlab.com/madsbuvi/openmw into multiview_test_branch
This commit is contained in:
commit
70e8e818b6
67 changed files with 1905 additions and 684 deletions
|
@ -1,33 +1,67 @@
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
|
||||||
Debian:
|
.Debian:
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- linux
|
- linux
|
||||||
image: debian:bullseye
|
image: debian:bullseye
|
||||||
cache:
|
cache:
|
||||||
key: cache.002
|
|
||||||
paths:
|
paths:
|
||||||
- apt-cache/
|
- apt-cache/
|
||||||
- ccache/
|
- ccache/
|
||||||
before_script:
|
before_script:
|
||||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||||
- apt-get update -yq
|
- apt-get update -yq
|
||||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential 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 libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev ccache
|
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential 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 libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev ccache git clang
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
- export CCACHE_BASEDIR="`pwd`"
|
- export CCACHE_BASEDIR="`pwd`"
|
||||||
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
|
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
|
||||||
- ccache -z -M 250M
|
- ccache -z -M 1G
|
||||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
- CI/before_script.linux.sh
|
||||||
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
- cd build
|
||||||
- make -j$cores_to_use
|
- cmake --build . -- -j $(nproc)
|
||||||
- DESTDIR=artifacts make install
|
- cmake --install .
|
||||||
|
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_test_suite; fi
|
||||||
- ccache -s
|
- ccache -s
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/artifacts/
|
- build/install/
|
||||||
|
|
||||||
|
Debian_GCC:
|
||||||
|
extends: .Debian
|
||||||
|
cache:
|
||||||
|
key: Debian_GCC.v1
|
||||||
|
variables:
|
||||||
|
CC: gcc
|
||||||
|
CXX: g++
|
||||||
|
|
||||||
|
Debian_GCC_tests:
|
||||||
|
extends: .Debian
|
||||||
|
cache:
|
||||||
|
key: Debian_GCC_tests.v1
|
||||||
|
variables:
|
||||||
|
CC: gcc
|
||||||
|
CXX: g++
|
||||||
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
||||||
|
Debian_Clang:
|
||||||
|
extends: .Debian
|
||||||
|
cache:
|
||||||
|
key: Debian_Clang.v1
|
||||||
|
variables:
|
||||||
|
CC: clang
|
||||||
|
CXX: clang++
|
||||||
|
|
||||||
|
Debian_Clang_tests:
|
||||||
|
extends: .Debian
|
||||||
|
cache:
|
||||||
|
key: Debian_Clang_tests.v1
|
||||||
|
variables:
|
||||||
|
CC: clang
|
||||||
|
CXX: clang++
|
||||||
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
||||||
MacOS:
|
MacOS:
|
||||||
tags:
|
tags:
|
||||||
|
|
|
@ -45,6 +45,12 @@ matrix:
|
||||||
os: linux
|
os: linux
|
||||||
dist: focal
|
dist: focal
|
||||||
if: branch != coverity_scan
|
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
|
- name: OpenMW (openmw) on Ubuntu Focal with Clang's Static Analysis
|
||||||
os: linux
|
os: linux
|
||||||
dist: focal
|
dist: focal
|
||||||
|
@ -73,7 +79,7 @@ script:
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi
|
- 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 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}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; 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
|
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||||
- cd "${TRAVIS_BUILD_DIR}"
|
- cd "${TRAVIS_BUILD_DIR}"
|
||||||
- ccache -s
|
- ccache -s
|
||||||
|
|
|
@ -3,14 +3,17 @@
|
||||||
|
|
||||||
Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path
|
Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path
|
||||||
Bug #1952: Incorrect particle lighting
|
Bug #1952: Incorrect particle lighting
|
||||||
|
Bug #2069: Fireflies in Fireflies invade Morrowind look wrong
|
||||||
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
|
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
|
||||||
Bug #3676: NiParticleColorModifier isn't applied properly
|
Bug #3676: NiParticleColorModifier isn't applied properly
|
||||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
||||||
Bug #4021: Attributes and skills are not stored as floats
|
Bug #4021: Attributes and skills are not stored as floats
|
||||||
Bug #4055: Local scripts don't inherit variables from their base record
|
Bug #4055: Local scripts don't inherit variables from their base record
|
||||||
Bug #4623: Corprus implementation is incorrect
|
Bug #4623: Corprus implementation is incorrect
|
||||||
|
Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level
|
||||||
Bug #4764: Data race in osg ParticleSystem
|
Bug #4764: Data race in osg ParticleSystem
|
||||||
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
|
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
|
||||||
|
Bug #5101: Hostile followers travel with the player
|
||||||
Bug #5108: Savegame bloating due to inefficient fog textures format
|
Bug #5108: Savegame bloating due to inefficient fog textures format
|
||||||
Bug #5165: Active spells should use real time intead of timestamps
|
Bug #5165: Active spells should use real time intead of timestamps
|
||||||
Bug #5358: ForceGreeting always resets the dialogue window completely
|
Bug #5358: ForceGreeting always resets the dialogue window completely
|
||||||
|
@ -45,8 +48,12 @@
|
||||||
Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution
|
Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution
|
||||||
Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue
|
Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue
|
||||||
Bug #5557: Diagonal movement is noticeably slower with analogue stick
|
Bug #5557: Diagonal movement is noticeably slower with analogue stick
|
||||||
|
Bug #5588: Randomly clicking on the journal's right-side page when it's empty shows random topics
|
||||||
|
Bug #5603: Setting constant effect cast style doesn't correct effects view
|
||||||
|
Bug #5611: Usable items with "0 Uses" should be used only once
|
||||||
Feature #390: 3rd person look "over the shoulder"
|
Feature #390: 3rd person look "over the shoulder"
|
||||||
Feature #2386: Distant Statics in the form of Object Paging
|
Feature #2386: Distant Statics in the form of Object Paging
|
||||||
|
Feature #4894: Consider actors as obstacles for pathfinding
|
||||||
Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher
|
Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher
|
||||||
Feature #5362: Show the soul gems' trapped soul in count dialog
|
Feature #5362: Show the soul gems' trapped soul in count dialog
|
||||||
Feature #5445: Handle NiLines
|
Feature #5445: Handle NiLines
|
||||||
|
@ -56,6 +63,8 @@
|
||||||
Feature #5524: Resume failed script execution after reload
|
Feature #5524: Resume failed script execution after reload
|
||||||
Feature #5525: Search fields tweaks (utf-8)
|
Feature #5525: Search fields tweaks (utf-8)
|
||||||
Feature #5545: Option to allow stealing from an unconscious NPC during combat
|
Feature #5545: Option to allow stealing from an unconscious NPC during combat
|
||||||
|
Feature #5579: MCP SetAngle enhancement
|
||||||
|
Feature #5610: Actors movement should be smoother
|
||||||
Task #5480: Drop Qt4 support
|
Task #5480: Drop Qt4 support
|
||||||
Task #5520: Improve cell name autocompleter implementation
|
Task #5520: Improve cell name autocompleter implementation
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,43 @@
|
||||||
|
|
||||||
free -m
|
free -m
|
||||||
|
|
||||||
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
|
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||||
GOOGLETEST_DIR="$(pwd)/googletest/build"
|
export GOOGLETEST_DIR="$(pwd)/googletest/build/install"
|
||||||
|
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
${ANALYZE} cmake \
|
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||||
-DCMAKE_C_COMPILER="${CC}" \
|
${ANALYZE} cmake \
|
||||||
-DCMAKE_CXX_COMPILER="${CXX}" \
|
-D CMAKE_C_COMPILER="${CC}" \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-D CMAKE_CXX_COMPILER="${CXX}" \
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
-D CMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
-DBUILD_UNITTESTS=TRUE \
|
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
-DUSE_SYSTEM_TINYXML=TRUE \
|
-D CMAKE_INSTALL_PREFIX=install \
|
||||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DBINDIR="/usr/games" \
|
-D USE_SYSTEM_TINYXML=TRUE \
|
||||||
-DCMAKE_BUILD_TYPE="DEBUG" \
|
-D BUILD_OPENMW=OFF \
|
||||||
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
|
-D BUILD_BSATOOL=OFF \
|
||||||
-DGMOCK_ROOT="${GOOGLETEST_DIR}" \
|
-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}" \
|
||||||
|
..
|
||||||
|
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 \
|
||||||
|
..
|
||||||
|
fi
|
||||||
|
|
|
@ -523,52 +523,52 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
||||||
# Boost
|
# Boost
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
download "Boost ${BOOST_VER}" \
|
download "Boost ${BOOST_VER}" \
|
||||||
"https://sourceforge.net/projects/boost/files/boost-binaries/${BOOST_VER}/boost_${BOOST_VER_URL}-msvc-${MSVC_VER}-${BITS}.exe" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/boost_${BOOST_VER_URL}-msvc-${MSVC_VER}-${BITS}.exe" \
|
||||||
"boost-${BOOST_VER}-msvc${MSVC_VER}-win${BITS}.exe"
|
"boost-${BOOST_VER}-msvc${MSVC_VER}-win${BITS}.exe"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Bullet
|
# Bullet
|
||||||
download "Bullet 2.89 (${BULLET_DBL_DISPLAY})" \
|
download "Bullet 2.89 (${BULLET_DBL_DISPLAY})" \
|
||||||
"https://rgw.ctrl-c.liu.se/openmw/Deps/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \
|
||||||
"Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z"
|
"Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z"
|
||||||
|
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
download "FFmpeg 4.2.2" \
|
download "FFmpeg 4.2.2" \
|
||||||
"https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-4.2.2-win${BITS}-shared.zip" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-win${BITS}.zip" \
|
||||||
"ffmpeg-4.2.2-win${BITS}.zip" \
|
"ffmpeg-4.2.2-win${BITS}.zip" \
|
||||||
"https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-4.2.2-win${BITS}-dev.zip" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-dev-win${BITS}.zip" \
|
||||||
"ffmpeg-4.2.2-dev-win${BITS}.zip"
|
"ffmpeg-4.2.2-dev-win${BITS}.zip"
|
||||||
|
|
||||||
# MyGUI
|
# MyGUI
|
||||||
download "MyGUI 3.4.0" \
|
download "MyGUI 3.4.0" \
|
||||||
"https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
||||||
"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
||||||
|
|
||||||
if [ -n "$PDBS" ]; then
|
if [ -n "$PDBS" ]; then
|
||||||
download "MyGUI symbols" \
|
download "MyGUI symbols" \
|
||||||
"https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
||||||
"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# OpenAL
|
# OpenAL
|
||||||
download "OpenAL-Soft 1.20.1" \
|
download "OpenAL-Soft 1.20.1" \
|
||||||
"http://openal-soft.org/openal-binaries/openal-soft-1.20.1-bin.zip" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OpenAL-Soft-1.20.1.zip" \
|
||||||
"OpenAL-Soft-1.20.1.zip"
|
"OpenAL-Soft-1.20.1.zip"
|
||||||
|
|
||||||
# OSG
|
# OSG
|
||||||
download "OpenSceneGraph 3.6.5" \
|
download "OpenSceneGraph 3.6.5" \
|
||||||
"https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
||||||
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
||||||
|
|
||||||
if [ -n "$PDBS" ]; then
|
if [ -n "$PDBS" ]; then
|
||||||
download "OpenSceneGraph symbols" \
|
download "OpenSceneGraph symbols" \
|
||||||
"https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
||||||
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SDL2
|
# SDL2
|
||||||
download "SDL 2.0.12" \
|
download "SDL 2.0.12" \
|
||||||
"https://www.libsdl.org/release/SDL2-devel-2.0.12-VC.zip" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-2.0.12.zip" \
|
||||||
"SDL2-2.0.12.zip"
|
"SDL2-2.0.12.zip"
|
||||||
|
|
||||||
# Google test and mock
|
# Google test and mock
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -ex
|
||||||
|
|
||||||
git clone -b release-1.10.0 https://github.com/google/googletest.git
|
git clone -b release-1.10.0 https://github.com/google/googletest.git
|
||||||
cd googletest
|
cd googletest
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake \
|
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_BUILD_TYPE="${CONFIGURATION}" \
|
-D CMAKE_BUILD_TYPE="${CONFIGURATION}" \
|
||||||
-D CMAKE_INSTALL_PREFIX=. \
|
-D CMAKE_INSTALL_PREFIX="${GOOGLETEST_DIR}" \
|
||||||
-G "${GENERATOR}" \
|
-G "${GENERATOR}" \
|
||||||
..
|
..
|
||||||
cmake --build . --config "${CONFIGURATION}"
|
cmake --build . --config "${CONFIGURATION}" -- -j $(nproc)
|
||||||
cmake --build . --target install --config "${CONFIGURATION}"
|
cmake --install . --config "${CONFIGURATION}"
|
||||||
|
|
301
CMakeLists.txt
301
CMakeLists.txt
|
@ -435,152 +435,6 @@ elseif (MSVC)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
|
||||||
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||||
|
|
||||||
IF(NOT WIN32 AND NOT APPLE)
|
|
||||||
# Linux installation
|
|
||||||
|
|
||||||
# Install binaries
|
|
||||||
IF(BUILD_OPENMW)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_OPENMW)
|
|
||||||
IF(BUILD_LAUNCHER)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_LAUNCHER)
|
|
||||||
IF(BUILD_BSATOOL)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_BSATOOL)
|
|
||||||
IF(BUILD_ESMTOOL)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_ESMTOOL)
|
|
||||||
IF(BUILD_NIFTEST)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_NIFTEST)
|
|
||||||
IF(BUILD_MWINIIMPORTER)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_MWINIIMPORTER)
|
|
||||||
IF(BUILD_ESSIMPORTER)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-essimporter" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_ESSIMPORTER)
|
|
||||||
IF(BUILD_OPENCS)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-cs" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_OPENCS)
|
|
||||||
IF(BUILD_WIZARD)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-wizard" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_WIZARD)
|
|
||||||
|
|
||||||
# Install licenses
|
|
||||||
INSTALL(FILES "files/mygui/DejaVuFontLicense.txt" DESTINATION "${LICDIR}" )
|
|
||||||
|
|
||||||
# Install icon and desktop file
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/org.openmw.launcher.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
|
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/metainfo" COMPONENT "openmw")
|
|
||||||
IF(BUILD_OPENCS)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/org.openmw.cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs")
|
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
|
|
||||||
ENDIF(BUILD_OPENCS)
|
|
||||||
|
|
||||||
# Install global configuration files
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
|
||||||
|
|
||||||
IF(BUILD_OPENCS)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
|
|
||||||
ENDIF(BUILD_OPENCS)
|
|
||||||
|
|
||||||
# Install resources
|
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" COMPONENT "Resources")
|
|
||||||
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
|
|
||||||
ENDIF(NOT WIN32 AND NOT APPLE)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
FILE(GLOB dll_files_debug "${OpenMW_BINARY_DIR}/Debug/*.dll")
|
|
||||||
FILE(GLOB dll_files_release "${OpenMW_BINARY_DIR}/Release/*.dll")
|
|
||||||
INSTALL(FILES ${dll_files_debug} DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES ${dll_files_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg" CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg" CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
|
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
|
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/LICENSE" DESTINATION "." RENAME "LICENSE.txt")
|
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/mygui/DejaVuFontLicense.txt" DESTINATION ".")
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/settings-default.cfg" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/settings-default.cfg" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
|
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
|
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/resources" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
|
|
||||||
FILE(GLOB plugin_dir_debug "${OpenMW_BINARY_DIR}/Debug/osgPlugins-*")
|
|
||||||
FILE(GLOB plugin_dir_release "${OpenMW_BINARY_DIR}/Release/osgPlugins-*")
|
|
||||||
INSTALL(DIRECTORY ${plugin_dir_debug} DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(DIRECTORY ${plugin_dir_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
|
|
||||||
SET(CPACK_GENERATOR "NSIS")
|
|
||||||
SET(CPACK_PACKAGE_NAME "OpenMW")
|
|
||||||
SET(CPACK_PACKAGE_VENDOR "OpenMW.org")
|
|
||||||
SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
|
||||||
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
|
|
||||||
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
|
|
||||||
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW")
|
|
||||||
IF(BUILD_LAUNCHER)
|
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher")
|
|
||||||
ENDIF(BUILD_LAUNCHER)
|
|
||||||
IF(BUILD_OPENCS)
|
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set")
|
|
||||||
ENDIF(BUILD_OPENCS)
|
|
||||||
IF(BUILD_WIZARD)
|
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard")
|
|
||||||
ENDIF(BUILD_WIZARD)
|
|
||||||
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\README.txt'")
|
|
||||||
SET(CPACK_NSIS_DELETE_ICONS_EXTRA "
|
|
||||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
|
||||||
Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Readme.lnk\\\"
|
|
||||||
")
|
|
||||||
SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/README.md")
|
|
||||||
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/README.md")
|
|
||||||
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
|
||||||
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
|
|
||||||
SET(CPACK_NSIS_HELP_LINK "https:\\\\\\\\www.openmw.org")
|
|
||||||
SET(CPACK_NSIS_URL_INFO_ABOUT "https:\\\\\\\\www.openmw.org")
|
|
||||||
SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
|
|
||||||
SET(CPACK_NSIS_MUI_FINISHPAGE_RUN "openmw-launcher.exe")
|
|
||||||
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
|
|
||||||
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
|
|
||||||
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
|
|
||||||
|
|
||||||
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
|
|
||||||
if(EXISTS ${VCREDIST32})
|
|
||||||
INSTALL(FILES ${VCREDIST32} DESTINATION "redist")
|
|
||||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q'" )
|
|
||||||
endif(EXISTS ${VCREDIST32})
|
|
||||||
|
|
||||||
SET(VCREDIST64 "${OpenMW_BINARY_DIR}/vcredist_x64.exe")
|
|
||||||
if(EXISTS ${VCREDIST64})
|
|
||||||
INSTALL(FILES ${VCREDIST64} DESTINATION "redist")
|
|
||||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q'" )
|
|
||||||
endif(EXISTS ${VCREDIST64})
|
|
||||||
|
|
||||||
SET(OALREDIST "${OpenMW_BINARY_DIR}/oalinst.exe")
|
|
||||||
if(EXISTS ${OALREDIST})
|
|
||||||
INSTALL(FILES ${OALREDIST} DESTINATION "redist")
|
|
||||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}
|
|
||||||
ExecWait '\\\"$INSTDIR\\\\redist\\\\oalinst.exe\\\" /s'" )
|
|
||||||
endif(EXISTS ${OALREDIST})
|
|
||||||
|
|
||||||
if(CMAKE_CL_64)
|
|
||||||
SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(CPack)
|
|
||||||
endif(WIN32)
|
|
||||||
|
|
||||||
# Extern
|
# Extern
|
||||||
set(RECASTNAVIGATION_STATIC ON CACHE BOOL "Build recastnavigation static libraries")
|
set(RECASTNAVIGATION_STATIC ON CACHE BOOL "Build recastnavigation static libraries")
|
||||||
|
|
||||||
|
@ -875,7 +729,160 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE)
|
||||||
fixup_bundle(\"${INSTALLED_OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"\")
|
fixup_bundle(\"${INSTALLED_OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"\")
|
||||||
" COMPONENT Runtime)
|
" COMPONENT Runtime)
|
||||||
include(CPack)
|
include(CPack)
|
||||||
endif ()
|
elseif(NOT APPLE)
|
||||||
|
get_generator_is_multi_config(multi_config)
|
||||||
|
if (multi_config)
|
||||||
|
SET(INSTALL_SOURCE "${OpenMW_BINARY_DIR}/$<CONFIG>")
|
||||||
|
else ()
|
||||||
|
SET(INSTALL_SOURCE "${OpenMW_BINARY_DIR}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
INSTALL(DIRECTORY "${INSTALL_SOURCE}/" DESTINATION "." FILES_MATCHING PATTERN "*.dll"
|
||||||
|
PATTERN "deps" EXCLUDE
|
||||||
|
PATTERN "apps" EXCLUDE
|
||||||
|
PATTERN "CMakeFiles" EXCLUDE
|
||||||
|
PATTERN "components" EXCLUDE
|
||||||
|
PATTERN "docs" EXCLUDE
|
||||||
|
PATTERN "extern" EXCLUDE
|
||||||
|
PATTERN "files" EXCLUDE
|
||||||
|
PATTERN "Testing" EXCLUDE)
|
||||||
|
INSTALL(DIRECTORY "${INSTALL_SOURCE}/" DESTINATION "." CONFIGURATIONS Debug;RelWithDebInfo FILES_MATCHING PATTERN "*.pdb"
|
||||||
|
PATTERN "deps" EXCLUDE
|
||||||
|
PATTERN "apps" EXCLUDE
|
||||||
|
PATTERN "CMakeFiles" EXCLUDE
|
||||||
|
PATTERN "components" EXCLUDE
|
||||||
|
PATTERN "docs" EXCLUDE
|
||||||
|
PATTERN "extern" EXCLUDE
|
||||||
|
PATTERN "files" EXCLUDE
|
||||||
|
PATTERN "Testing" EXCLUDE)
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
|
||||||
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
|
||||||
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
|
||||||
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/LICENSE" DESTINATION "." RENAME "LICENSE.txt")
|
||||||
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/mygui/DejaVuFontLicense.txt" DESTINATION ".")
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/settings-default.cfg" DESTINATION ".")
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION ".")
|
||||||
|
|
||||||
|
INSTALL(DIRECTORY "${INSTALL_SOURCE}/resources" DESTINATION ".")
|
||||||
|
|
||||||
|
SET(CPACK_GENERATOR "NSIS")
|
||||||
|
SET(CPACK_PACKAGE_NAME "OpenMW")
|
||||||
|
SET(CPACK_PACKAGE_VENDOR "OpenMW.org")
|
||||||
|
SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
||||||
|
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
|
||||||
|
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
|
||||||
|
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||||
|
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW")
|
||||||
|
IF(BUILD_LAUNCHER)
|
||||||
|
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher")
|
||||||
|
ENDIF(BUILD_LAUNCHER)
|
||||||
|
IF(BUILD_OPENCS)
|
||||||
|
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set")
|
||||||
|
ENDIF(BUILD_OPENCS)
|
||||||
|
IF(BUILD_WIZARD)
|
||||||
|
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard")
|
||||||
|
ENDIF(BUILD_WIZARD)
|
||||||
|
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\README.txt'")
|
||||||
|
SET(CPACK_NSIS_DELETE_ICONS_EXTRA "
|
||||||
|
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||||
|
Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Readme.lnk\\\"
|
||||||
|
")
|
||||||
|
SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/README.md")
|
||||||
|
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/README.md")
|
||||||
|
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||||
|
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
|
||||||
|
SET(CPACK_NSIS_HELP_LINK "https:\\\\\\\\www.openmw.org")
|
||||||
|
SET(CPACK_NSIS_URL_INFO_ABOUT "https:\\\\\\\\www.openmw.org")
|
||||||
|
SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
|
||||||
|
SET(CPACK_NSIS_MUI_FINISHPAGE_RUN "openmw-launcher.exe")
|
||||||
|
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
|
||||||
|
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
|
||||||
|
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
|
||||||
|
|
||||||
|
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
|
||||||
|
if(EXISTS ${VCREDIST32})
|
||||||
|
INSTALL(FILES ${VCREDIST32} DESTINATION "redist")
|
||||||
|
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q'" )
|
||||||
|
endif(EXISTS ${VCREDIST32})
|
||||||
|
|
||||||
|
SET(VCREDIST64 "${OpenMW_BINARY_DIR}/vcredist_x64.exe")
|
||||||
|
if(EXISTS ${VCREDIST64})
|
||||||
|
INSTALL(FILES ${VCREDIST64} DESTINATION "redist")
|
||||||
|
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q'" )
|
||||||
|
endif(EXISTS ${VCREDIST64})
|
||||||
|
|
||||||
|
SET(OALREDIST "${OpenMW_BINARY_DIR}/oalinst.exe")
|
||||||
|
if(EXISTS ${OALREDIST})
|
||||||
|
INSTALL(FILES ${OALREDIST} DESTINATION "redist")
|
||||||
|
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}
|
||||||
|
ExecWait '\\\"$INSTDIR\\\\redist\\\\oalinst.exe\\\" /s'" )
|
||||||
|
endif(EXISTS ${OALREDIST})
|
||||||
|
|
||||||
|
if(CMAKE_CL_64)
|
||||||
|
SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
|
else(WIN32)
|
||||||
|
# Linux installation
|
||||||
|
|
||||||
|
# Install binaries
|
||||||
|
IF(BUILD_OPENMW)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_OPENMW)
|
||||||
|
IF(BUILD_LAUNCHER)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-launcher" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_LAUNCHER)
|
||||||
|
IF(BUILD_BSATOOL)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/bsatool" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_BSATOOL)
|
||||||
|
IF(BUILD_ESMTOOL)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/esmtool" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_ESMTOOL)
|
||||||
|
IF(BUILD_NIFTEST)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/niftest" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_NIFTEST)
|
||||||
|
IF(BUILD_MWINIIMPORTER)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-iniimporter" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_MWINIIMPORTER)
|
||||||
|
IF(BUILD_ESSIMPORTER)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-essimporter" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_ESSIMPORTER)
|
||||||
|
IF(BUILD_OPENCS)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-cs" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_OPENCS)
|
||||||
|
IF(BUILD_WIZARD)
|
||||||
|
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-wizard" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_WIZARD)
|
||||||
|
|
||||||
|
# Install licenses
|
||||||
|
INSTALL(FILES "files/mygui/DejaVuFontLicense.txt" DESTINATION "${LICDIR}" )
|
||||||
|
|
||||||
|
# Install icon and desktop file
|
||||||
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/org.openmw.launcher.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
|
||||||
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
|
||||||
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/metainfo" COMPONENT "openmw")
|
||||||
|
IF(BUILD_OPENCS)
|
||||||
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/org.openmw.cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs")
|
||||||
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
|
||||||
|
ENDIF(BUILD_OPENCS)
|
||||||
|
|
||||||
|
# Install global configuration files
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
|
|
||||||
|
IF(BUILD_OPENCS)
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
|
||||||
|
ENDIF(BUILD_OPENCS)
|
||||||
|
|
||||||
|
# Install resources
|
||||||
|
INSTALL(DIRECTORY "${INSTALL_SOURCE}/resources" DESTINATION "${DATADIR}" COMPONENT "Resources")
|
||||||
|
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
|
||||||
|
endif(WIN32)
|
||||||
|
endif(NOT APPLE)
|
||||||
|
|
||||||
# Doxygen Target -- simply run 'make doc' or 'make doc_pages'
|
# Doxygen Target -- simply run 'make doc' or 'make doc_pages'
|
||||||
# output directory for 'make doc' is "${OpenMW_BINARY_DIR}/docs/Doxygen"
|
# output directory for 'make doc' is "${OpenMW_BINARY_DIR}/docs/Doxygen"
|
||||||
|
|
|
@ -88,6 +88,7 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
|
loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
|
||||||
loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
||||||
loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
||||||
|
loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
|
||||||
int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game");
|
int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game");
|
||||||
if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)
|
if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)
|
||||||
unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);
|
unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);
|
||||||
|
@ -112,6 +113,7 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
|
loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
|
||||||
}
|
}
|
||||||
loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
|
loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
|
||||||
|
loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
|
||||||
|
|
||||||
const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain");
|
const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain");
|
||||||
const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain");
|
const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain");
|
||||||
|
@ -200,6 +202,7 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
|
saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
|
||||||
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
||||||
saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
||||||
|
saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
|
||||||
int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex();
|
int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex();
|
||||||
if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game"))
|
if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game"))
|
||||||
mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex);
|
mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex);
|
||||||
|
@ -220,6 +223,7 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
|
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
|
||||||
saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
|
saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
|
||||||
saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
|
saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
|
||||||
|
saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
|
||||||
|
|
||||||
const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain");
|
const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain");
|
||||||
const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain");
|
const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain");
|
||||||
|
|
|
@ -233,8 +233,15 @@ target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
|
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
|
||||||
INSTALL(TARGETS openmw-cs RUNTIME DESTINATION ".")
|
INSTALL(TARGETS openmw-cs RUNTIME DESTINATION ".")
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/openmw-cs.cfg" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/openmw-cs.cfg" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
get_generator_is_multi_config(multi_config)
|
||||||
|
if (multi_config)
|
||||||
|
SET(INSTALL_SOURCE "${OpenMW_BINARY_DIR}/$<CONFIG>")
|
||||||
|
else ()
|
||||||
|
SET(INSTALL_SOURCE "${OpenMW_BINARY_DIR}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/openmw-cs.cfg" DESTINATION ".")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/debug/gldebug.hpp>
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
|
@ -498,7 +499,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||||
bool fullscreen = settings.getBool("fullscreen", "Video");
|
bool fullscreen = settings.getBool("fullscreen", "Video");
|
||||||
bool windowBorder = settings.getBool("window border", "Video");
|
bool windowBorder = settings.getBool("window border", "Video");
|
||||||
bool vsync = settings.getBool("vsync", "Video");
|
bool vsync = settings.getBool("vsync", "Video");
|
||||||
int antialiasing = settings.getInt("antialiasing", "Video");
|
unsigned int antialiasing = std::max(0, settings.getInt("antialiasing", "Video"));
|
||||||
|
|
||||||
int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(screen),
|
int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(screen),
|
||||||
pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(screen);
|
pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(screen);
|
||||||
|
@ -524,6 +525,8 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8));
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8));
|
||||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0));
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0));
|
||||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24));
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24));
|
||||||
|
if (Debug::shouldDebugOpenGL())
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG));
|
||||||
|
|
||||||
if (antialiasing > 0)
|
if (antialiasing > 0)
|
||||||
{
|
{
|
||||||
|
@ -531,62 +534,80 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!mWindow)
|
osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> graphicsWindow;
|
||||||
|
while (!graphicsWindow || !graphicsWindow->valid())
|
||||||
{
|
{
|
||||||
mWindow = SDL_CreateWindow("OpenMW", pos_x, pos_y, width, height, flags);
|
while (!mWindow)
|
||||||
if (!mWindow)
|
|
||||||
{
|
{
|
||||||
// Try with a lower AA
|
mWindow = SDL_CreateWindow("OpenMW", pos_x, pos_y, width, height, flags);
|
||||||
if (antialiasing > 0)
|
if (!mWindow)
|
||||||
{
|
{
|
||||||
Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2;
|
// Try with a lower AA
|
||||||
antialiasing /= 2;
|
if (antialiasing > 0)
|
||||||
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
|
{
|
||||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2;
|
||||||
continue;
|
antialiasing /= 2;
|
||||||
}
|
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
|
||||||
else
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||||
{
|
continue;
|
||||||
std::stringstream error;
|
}
|
||||||
error << "Failed to create SDL window: " << SDL_GetError();
|
else
|
||||||
throw std::runtime_error(error.str());
|
{
|
||||||
|
std::stringstream error;
|
||||||
|
error << "Failed to create SDL window: " << SDL_GetError();
|
||||||
|
throw std::runtime_error(error.str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setWindowIcon();
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
|
||||||
|
SDL_GetWindowPosition(mWindow, &traits->x, &traits->y);
|
||||||
|
SDL_GetWindowSize(mWindow, &traits->width, &traits->height);
|
||||||
|
traits->windowName = SDL_GetWindowTitle(mWindow);
|
||||||
|
traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
|
||||||
|
traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);
|
||||||
|
traits->vsync = vsync;
|
||||||
|
traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);
|
||||||
|
|
||||||
|
graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits);
|
||||||
|
if (!graphicsWindow->valid()) throw std::runtime_error("Failed to create GraphicsContext");
|
||||||
|
|
||||||
|
if (traits->samples < antialiasing)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Warning: Framebuffer MSAA level is only " << traits->samples << "x instead of " << antialiasing << "x. Trying " << antialiasing / 2 << "x instead.";
|
||||||
|
graphicsWindow->closeImplementation();
|
||||||
|
SDL_DestroyWindow(mWindow);
|
||||||
|
mWindow = nullptr;
|
||||||
|
antialiasing /= 2;
|
||||||
|
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traits->red < 8)
|
||||||
|
Log(Debug::Warning) << "Warning: Framebuffer only has a " << traits->red << " bit red channel.";
|
||||||
|
if (traits->green < 8)
|
||||||
|
Log(Debug::Warning) << "Warning: Framebuffer only has a " << traits->green << " bit green channel.";
|
||||||
|
if (traits->blue < 8)
|
||||||
|
Log(Debug::Warning) << "Warning: Framebuffer only has a " << traits->blue << " bit blue channel.";
|
||||||
|
if (traits->depth < 8)
|
||||||
|
Log(Debug::Warning) << "Warning: Framebuffer only has " << traits->red << " bits of depth precision.";
|
||||||
|
|
||||||
|
traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel
|
||||||
}
|
}
|
||||||
|
|
||||||
setWindowIcon();
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
|
|
||||||
SDL_GetWindowPosition(mWindow, &traits->x, &traits->y);
|
|
||||||
SDL_GetWindowSize(mWindow, &traits->width, &traits->height);
|
|
||||||
traits->windowName = SDL_GetWindowTitle(mWindow);
|
|
||||||
traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
|
|
||||||
traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);
|
|
||||||
// We tried to get rid of the hardcoding but failed: https://github.com/OpenMW/openmw/pull/1771
|
|
||||||
// Here goes kcat's quote:
|
|
||||||
// It's ultimately a chicken and egg problem, and the reason why the code is like it was in the first place.
|
|
||||||
// It needs a context to get the current attributes, but it needs the attributes to set up the context.
|
|
||||||
// So it just specifies the same values that were given to SDL in the hopes that it's good enough to what the window eventually gets.
|
|
||||||
traits->red = 8;
|
|
||||||
traits->green = 8;
|
|
||||||
traits->blue = 8;
|
|
||||||
traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel
|
|
||||||
traits->depth = 24;
|
|
||||||
traits->stencil = 8;
|
|
||||||
traits->vsync = vsync;
|
|
||||||
traits->doubleBuffer = true;
|
|
||||||
traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);
|
|
||||||
|
|
||||||
osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits);
|
|
||||||
if(!graphicsWindow->valid()) throw std::runtime_error("Failed to create GraphicsContext");
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
|
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
|
||||||
camera->setGraphicsContext(graphicsWindow);
|
camera->setGraphicsContext(graphicsWindow);
|
||||||
camera->setViewport(0, 0, traits->width, traits->height);
|
camera->setViewport(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height);
|
||||||
|
|
||||||
|
if (Debug::shouldDebugOpenGL())
|
||||||
|
mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation());
|
||||||
|
|
||||||
mViewer->realize();
|
mViewer->realize();
|
||||||
|
|
||||||
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height);
|
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OMW::Engine::setWindowIcon()
|
void OMW::Engine::setWindowIcon()
|
||||||
|
|
|
@ -941,6 +941,9 @@ public:
|
||||||
if (!mBook)
|
if (!mBook)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (mPage >= mBook->mPages.size())
|
||||||
|
return;
|
||||||
|
|
||||||
dirtyFocusItem ();
|
dirtyFocusItem ();
|
||||||
|
|
||||||
mFocusItem = 0;
|
mFocusItem = 0;
|
||||||
|
@ -952,6 +955,9 @@ public:
|
||||||
if (!mBook)
|
if (!mBook)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (mPage >= mBook->mPages.size())
|
||||||
|
return;
|
||||||
|
|
||||||
left -= mCroppedParent->getAbsoluteLeft ();
|
left -= mCroppedParent->getAbsoluteLeft ();
|
||||||
top -= mCroppedParent->getAbsoluteTop ();
|
top -= mCroppedParent->getAbsoluteTop ();
|
||||||
|
|
||||||
|
@ -988,6 +994,9 @@ public:
|
||||||
if (!mBook)
|
if (!mBook)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (mPage >= mBook->mPages.size())
|
||||||
|
return;
|
||||||
|
|
||||||
// work around inconsistency in MyGUI where the mouse press coordinates aren't
|
// work around inconsistency in MyGUI where the mouse press coordinates aren't
|
||||||
// transformed by the current Layer (even though mouse *move* events are).
|
// transformed by the current Layer (even though mouse *move* events are).
|
||||||
MyGUI::IntPoint pos (left, top);
|
MyGUI::IntPoint pos (left, top);
|
||||||
|
@ -1013,6 +1022,9 @@ public:
|
||||||
if (!mBook)
|
if (!mBook)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (mPage >= mBook->mPages.size())
|
||||||
|
return;
|
||||||
|
|
||||||
// work around inconsistency in MyGUI where the mouse release coordinates aren't
|
// work around inconsistency in MyGUI where the mouse release coordinates aren't
|
||||||
// transformed by the current Layer (even though mouse *move* events are).
|
// transformed by the current Layer (even though mouse *move* events are).
|
||||||
MyGUI::IntPoint pos (left, top);
|
MyGUI::IntPoint pos (left, top);
|
||||||
|
|
|
@ -739,5 +739,24 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
mAddEffectDialog.setConstantEffect(constant);
|
mAddEffectDialog.setConstantEffect(constant);
|
||||||
mConstantEffect = constant;
|
mConstantEffect = constant;
|
||||||
|
|
||||||
|
if (!constant)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto it = mEffects.begin(); it != mEffects.end();)
|
||||||
|
{
|
||||||
|
if (it->mRange != ESM::RT_Self)
|
||||||
|
{
|
||||||
|
auto& store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
auto magicEffect = store.get<ESM::MagicEffect>().find(it->mEffectID);
|
||||||
|
if ((magicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) == 0)
|
||||||
|
{
|
||||||
|
it = mEffects.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it->mRange = ESM::RT_Self;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace MWGui
|
||||||
|
|
||||||
// Add price for the travelling followers
|
// Add price for the travelling followers
|
||||||
std::set<MWWorld::Ptr> followers;
|
std::set<MWWorld::Ptr> followers;
|
||||||
MWWorld::ActionTeleport::getFollowersToTeleport(player, followers);
|
MWWorld::ActionTeleport::getFollowers(player, followers);
|
||||||
|
|
||||||
// Apply followers cost, unlike vanilla the first follower doesn't travel for free
|
// Apply followers cost, unlike vanilla the first follower doesn't travel for free
|
||||||
price *= 1 + static_cast<int>(followers.size());
|
price *= 1 + static_cast<int>(followers.size());
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
#include <components/misc/mathutil.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
#include "aicombataction.hpp"
|
#include "aicombataction.hpp"
|
||||||
#include "aifollow.hpp"
|
#include "aifollow.hpp"
|
||||||
#include "aipursue.hpp"
|
#include "aipursue.hpp"
|
||||||
|
#include "aiwander.hpp"
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "summoning.hpp"
|
#include "summoning.hpp"
|
||||||
#include "combat.hpp"
|
#include "combat.hpp"
|
||||||
|
@ -424,7 +426,7 @@ namespace MWMechanics
|
||||||
const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());
|
const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());
|
||||||
float sqrDist = (actor1Pos - actor2Pos).length2();
|
float sqrDist = (actor1Pos - actor2Pos).length2();
|
||||||
|
|
||||||
if (sqrDist > maxDistance*maxDistance)
|
if (sqrDist > std::min(maxDistance * maxDistance, sqrHeadTrackDistance))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// stop tracking when target is behind the actor
|
// stop tracking when target is behind the actor
|
||||||
|
@ -432,10 +434,7 @@ namespace MWMechanics
|
||||||
osg::Vec3f targetDirection(actor2Pos - actor1Pos);
|
osg::Vec3f targetDirection(actor2Pos - actor1Pos);
|
||||||
actorDirection.z() = 0;
|
actorDirection.z() = 0;
|
||||||
targetDirection.z() = 0;
|
targetDirection.z() = 0;
|
||||||
actorDirection.normalize();
|
if (actorDirection * targetDirection > 0
|
||||||
targetDirection.normalize();
|
|
||||||
if (std::acos(actorDirection * targetDirection) < osg::DegreesToRadians(90.f)
|
|
||||||
&& sqrDist <= sqrHeadTrackDistance
|
|
||||||
&& MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function
|
&& MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function
|
||||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor))
|
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor))
|
||||||
{
|
{
|
||||||
|
@ -473,6 +472,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
|
void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
|
if (mSmoothMovement)
|
||||||
|
return;
|
||||||
|
|
||||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||||
MWMechanics::AiSequence& seq = stats.getAiSequence();
|
MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||||
|
|
||||||
|
@ -481,9 +483,10 @@ namespace MWMechanics
|
||||||
osg::Vec3f targetPos = seq.getActivePackage().getDestination();
|
osg::Vec3f targetPos = seq.getActivePackage().getDestination();
|
||||||
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||||
float distance = (targetPos - actorPos).length();
|
float distance = (targetPos - actorPos).length();
|
||||||
|
|
||||||
if (distance < DECELERATE_DISTANCE)
|
if (distance < DECELERATE_DISTANCE)
|
||||||
{
|
{
|
||||||
float speedCoef = std::max(0.7f, 0.1f * (distance/64.f + 2.f));
|
float speedCoef = std::max(0.7f, 0.2f + 0.8f * distance / DECELERATE_DISTANCE);
|
||||||
auto& movement = actor.getClass().getMovementSettings(actor);
|
auto& movement = actor.getClass().getMovementSettings(actor);
|
||||||
movement.mPosition[0] *= speedCoef;
|
movement.mPosition[0] *= speedCoef;
|
||||||
movement.mPosition[1] *= speedCoef;
|
movement.mPosition[1] *= speedCoef;
|
||||||
|
@ -587,8 +590,11 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (!actorState.isTurningToPlayer())
|
if (!actorState.isTurningToPlayer())
|
||||||
{
|
{
|
||||||
actorState.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
|
float angle = std::atan2(dir.x(), dir.y());
|
||||||
actorState.setTurningToPlayer(true);
|
actorState.setAngleToPlayer(angle);
|
||||||
|
float deltaAngle = Misc::normalizeAngle(angle - actor.getRefData().getPosition().rot[2]);
|
||||||
|
if (!mSmoothMovement || std::abs(deltaAngle) > osg::DegreesToRadians(60.f))
|
||||||
|
actorState.setTurningToPlayer(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1460,7 +1466,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Actors::Actors()
|
Actors::Actors() : mSmoothMovement(Settings::Manager::getBool("smooth movement", "Game"))
|
||||||
{
|
{
|
||||||
mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning
|
mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning
|
||||||
|
|
||||||
|
@ -1659,6 +1665,131 @@ namespace MWMechanics
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actors::predictAndAvoidCollisions()
|
||||||
|
{
|
||||||
|
const float minGap = 10.f;
|
||||||
|
const float maxDistToCheck = 100.f;
|
||||||
|
const float maxTimeToCheck = 1.f;
|
||||||
|
static const bool giveWayWhenIdle = Settings::Manager::getBool("NPCs give way", "Game");
|
||||||
|
|
||||||
|
MWWorld::Ptr player = getPlayer();
|
||||||
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||||
|
{
|
||||||
|
const MWWorld::Ptr& ptr = iter->first;
|
||||||
|
if (ptr == player)
|
||||||
|
continue; // Don't interfere with player controls.
|
||||||
|
|
||||||
|
Movement& movement = ptr.getClass().getMovementSettings(ptr);
|
||||||
|
osg::Vec2f origMovement(movement.mPosition[0], movement.mPosition[1]);
|
||||||
|
bool isMoving = origMovement.length2() > 0.01;
|
||||||
|
|
||||||
|
// Moving NPCs always should avoid collisions.
|
||||||
|
// Standing NPCs give way to moving ones if they are not in combat (or pursue) mode and either
|
||||||
|
// follow player or have a AIWander package with non-empty wander area.
|
||||||
|
bool shouldAvoidCollision = isMoving;
|
||||||
|
bool shouldTurnToApproachingActor = !isMoving;
|
||||||
|
MWWorld::Ptr currentTarget; // Combat or pursue target (NPCs should not avoid collision with their targets).
|
||||||
|
for (const auto& package : ptr.getClass().getCreatureStats(ptr).getAiSequence())
|
||||||
|
{
|
||||||
|
if (package->getTypeId() == AiPackageTypeId::Follow)
|
||||||
|
shouldAvoidCollision = true;
|
||||||
|
else if (package->getTypeId() == AiPackageTypeId::Wander && giveWayWhenIdle)
|
||||||
|
{
|
||||||
|
if (!dynamic_cast<const AiWander*>(package.get())->isStationary())
|
||||||
|
shouldAvoidCollision = true;
|
||||||
|
}
|
||||||
|
else if (package->getTypeId() == AiPackageTypeId::Combat || package->getTypeId() == AiPackageTypeId::Pursue)
|
||||||
|
{
|
||||||
|
currentTarget = package->getTarget();
|
||||||
|
shouldAvoidCollision = isMoving;
|
||||||
|
shouldTurnToApproachingActor = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!shouldAvoidCollision)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float maxSpeed = ptr.getClass().getMaxSpeed(ptr);
|
||||||
|
osg::Vec2f baseSpeed = origMovement * maxSpeed;
|
||||||
|
osg::Vec3f basePos = ptr.getRefData().getPosition().asVec3();
|
||||||
|
float baseRotZ = ptr.getRefData().getPosition().rot[2];
|
||||||
|
osg::Vec3f halfExtents = world->getHalfExtents(ptr);
|
||||||
|
|
||||||
|
float timeToCollision = maxTimeToCheck;
|
||||||
|
osg::Vec2f movementCorrection(0, 0);
|
||||||
|
float angleToApproachingActor = 0;
|
||||||
|
|
||||||
|
// Iterate through all other actors and predict collisions.
|
||||||
|
for(PtrActorMap::iterator otherIter(mActors.begin()); otherIter != mActors.end(); ++otherIter)
|
||||||
|
{
|
||||||
|
const MWWorld::Ptr& otherPtr = otherIter->first;
|
||||||
|
if (otherPtr == ptr || otherPtr == currentTarget)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
osg::Vec3f otherHalfExtents = world->getHalfExtents(otherPtr);
|
||||||
|
osg::Vec3f deltaPos = otherPtr.getRefData().getPosition().asVec3() - basePos;
|
||||||
|
osg::Vec2f relPos = Misc::rotateVec2f(osg::Vec2f(deltaPos.x(), deltaPos.y()), baseRotZ);
|
||||||
|
|
||||||
|
// Ignore actors which are not close enough or come from behind.
|
||||||
|
if (deltaPos.length2() > maxDistToCheck * maxDistToCheck || relPos.y() < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Don't check for a collision if vertical distance is greater then the actor's height.
|
||||||
|
if (deltaPos.z() > halfExtents.z() * 2 || deltaPos.z() < -otherHalfExtents.z() * 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
osg::Vec3f speed = otherPtr.getClass().getMovementSettings(otherPtr).asVec3() *
|
||||||
|
otherPtr.getClass().getMaxSpeed(otherPtr);
|
||||||
|
float rotZ = otherPtr.getRefData().getPosition().rot[2];
|
||||||
|
osg::Vec2f relSpeed = Misc::rotateVec2f(osg::Vec2f(speed.x(), speed.y()), baseRotZ - rotZ) - baseSpeed;
|
||||||
|
|
||||||
|
float collisionDist = minGap + world->getHalfExtents(ptr).x() + world->getHalfExtents(otherPtr).x();
|
||||||
|
collisionDist = std::min(collisionDist, relPos.length());
|
||||||
|
|
||||||
|
// Find the earliest `t` when |relPos + relSpeed * t| == collisionDist.
|
||||||
|
float vr = relPos.x() * relSpeed.x() + relPos.y() * relSpeed.y();
|
||||||
|
float v2 = relSpeed.length2();
|
||||||
|
float Dh = vr * vr - v2 * (relPos.length2() - collisionDist * collisionDist);
|
||||||
|
if (Dh <= 0 || v2 == 0)
|
||||||
|
continue; // No solution; distance is always >= collisionDist.
|
||||||
|
float t = (-vr - std::sqrt(Dh)) / v2;
|
||||||
|
|
||||||
|
if (t < 0 || t > timeToCollision)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check visibility and awareness last as it's expensive.
|
||||||
|
if (!MWBase::Environment::get().getWorld()->getLOS(otherPtr, ptr))
|
||||||
|
continue;
|
||||||
|
if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(otherPtr, ptr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
timeToCollision = t;
|
||||||
|
angleToApproachingActor = std::atan2(deltaPos.x(), deltaPos.y());
|
||||||
|
osg::Vec2f posAtT = relPos + relSpeed * t;
|
||||||
|
float coef = (posAtT.x() * relSpeed.x() + posAtT.y() * relSpeed.y()) / (collisionDist * maxSpeed);
|
||||||
|
movementCorrection = posAtT * coef;
|
||||||
|
// Step to the side rather than backward. Otherwise player will be able to push the NPC far away from it's original location.
|
||||||
|
movementCorrection.y() = std::max(0.f, movementCorrection.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeToCollision < maxTimeToCheck)
|
||||||
|
{
|
||||||
|
// Try to evade the nearest collision.
|
||||||
|
osg::Vec2f newMovement = origMovement + movementCorrection;
|
||||||
|
if (isMoving)
|
||||||
|
{ // Keep the original speed.
|
||||||
|
newMovement.normalize();
|
||||||
|
newMovement *= origMovement.length();
|
||||||
|
}
|
||||||
|
movement.mPosition[0] = newMovement.x();
|
||||||
|
movement.mPosition[1] = newMovement.y();
|
||||||
|
if (shouldTurnToApproachingActor)
|
||||||
|
zTurn(ptr, angleToApproachingActor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Actors::update (float duration, bool paused)
|
void Actors::update (float duration, bool paused)
|
||||||
{
|
{
|
||||||
if(!paused)
|
if(!paused)
|
||||||
|
@ -1769,14 +1900,12 @@ namespace MWMechanics
|
||||||
|
|
||||||
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||||
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
|
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
|
||||||
|
bool inCombatOrPursue = stats.getAiSequence().isInCombat() || stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue);
|
||||||
|
|
||||||
// 1. Unconsious actor can not track target
|
// 1. Unconsious actor can not track target
|
||||||
// 2. Actors in combat and pursue mode do not bother to headtrack
|
// 2. Actors in combat and pursue mode do not bother to headtrack
|
||||||
// 3. Player character does not use headtracking in the 1st-person view
|
// 3. Player character does not use headtracking in the 1st-person view
|
||||||
if (!stats.getKnockedDown() &&
|
if (!stats.getKnockedDown() && !firstPersonPlayer && !inCombatOrPursue)
|
||||||
!stats.getAiSequence().isInCombat() &&
|
|
||||||
!stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) &&
|
|
||||||
!firstPersonPlayer)
|
|
||||||
{
|
{
|
||||||
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
||||||
{
|
{
|
||||||
|
@ -1786,6 +1915,17 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!stats.getKnockedDown() && !isPlayer && inCombatOrPursue)
|
||||||
|
{
|
||||||
|
// Actors in combat and pursue mode always look at their target.
|
||||||
|
for (const auto& package : stats.getAiSequence())
|
||||||
|
{
|
||||||
|
headTrackTarget = package->getTarget();
|
||||||
|
if (!headTrackTarget.isEmpty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctrl->setHeadTrackTarget(headTrackTarget);
|
ctrl->setHeadTrackTarget(headTrackTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1824,6 +1964,10 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const bool avoidCollisions = Settings::Manager::getBool("NPCs avoid collisions", "Game");
|
||||||
|
if (avoidCollisions)
|
||||||
|
predictAndAvoidCollisions();
|
||||||
|
|
||||||
timerUpdateAITargets += duration;
|
timerUpdateAITargets += duration;
|
||||||
timerUpdateHeadTrack += duration;
|
timerUpdateHeadTrack += duration;
|
||||||
timerUpdateEquippedLight += duration;
|
timerUpdateEquippedLight += duration;
|
||||||
|
@ -2053,10 +2197,11 @@ namespace MWMechanics
|
||||||
|
|
||||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||||
{
|
{
|
||||||
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
|
|
||||||
|
|
||||||
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
|
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
|
||||||
|
{
|
||||||
|
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!sleep || iter->first == player)
|
if (!sleep || iter->first == player)
|
||||||
restoreDynamicStats(iter->first, hours, sleep);
|
restoreDynamicStats(iter->first, hours, sleep);
|
||||||
|
@ -2073,13 +2218,14 @@ namespace MWMechanics
|
||||||
if (iter->first.getClass().isNpc())
|
if (iter->first.getClass().isNpc())
|
||||||
calculateNpcStatModifiers(iter->first, duration);
|
calculateNpcStatModifiers(iter->first, duration);
|
||||||
|
|
||||||
|
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
|
||||||
|
|
||||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);
|
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);
|
||||||
if (animation)
|
if (animation)
|
||||||
{
|
{
|
||||||
animation->removeEffects();
|
animation->removeEffects();
|
||||||
MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first);
|
MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fastForwardAi();
|
fastForwardAi();
|
||||||
|
|
|
@ -63,6 +63,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
void purgeSpellEffects (int casterActorId);
|
void purgeSpellEffects (int casterActorId);
|
||||||
|
|
||||||
|
void predictAndAvoidCollisions();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Actors();
|
Actors();
|
||||||
|
@ -209,6 +211,7 @@ namespace MWMechanics
|
||||||
float mTimerDisposeSummonsCorpses;
|
float mTimerDisposeSummonsCorpses;
|
||||||
float mActorsProcessingRange;
|
float mActorsProcessingRange;
|
||||||
|
|
||||||
|
bool mSmoothMovement;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
|
||||||
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||||
|
|
||||||
actorClass.getMovementSettings(actor).mPosition[1] = 1;
|
actorClass.getMovementSettings(actor).mPosition[1] = 1;
|
||||||
smoothTurn(actor, -180, 0);
|
smoothTurn(actor, -osg::PI / 2, 0);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include <components/esm/aisequence.hpp>
|
#include <components/esm/aisequence.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/mathutil.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
|
|
||||||
#include "../mwphysics/collisiontype.hpp"
|
#include "../mwphysics/collisiontype.hpp"
|
||||||
|
@ -240,10 +242,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (storage.mReadyToAttack)
|
if (storage.mReadyToAttack)
|
||||||
{
|
{
|
||||||
storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target);
|
|
||||||
// start new attack
|
|
||||||
storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat);
|
|
||||||
|
|
||||||
if (isRangedCombat)
|
if (isRangedCombat)
|
||||||
{
|
{
|
||||||
// rotate actor taking into account target movement direction and projectile speed
|
// rotate actor taking into account target movement direction and projectile speed
|
||||||
|
@ -259,6 +257,10 @@ namespace MWMechanics
|
||||||
storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);
|
storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);
|
||||||
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
|
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target);
|
||||||
|
// start new attack
|
||||||
|
storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -372,9 +374,13 @@ namespace MWMechanics
|
||||||
void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage)
|
void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage)
|
||||||
{
|
{
|
||||||
// apply combat movement
|
// apply combat movement
|
||||||
|
float deltaAngle = storage.mMovement.mRotation[2] - actor.getRefData().getPosition().rot[2];
|
||||||
|
osg::Vec2f movement = Misc::rotateVec2f(
|
||||||
|
osg::Vec2f(storage.mMovement.mPosition[0], storage.mMovement.mPosition[1]), -deltaAngle);
|
||||||
|
|
||||||
MWMechanics::Movement& actorMovementSettings = actor.getClass().getMovementSettings(actor);
|
MWMechanics::Movement& actorMovementSettings = actor.getClass().getMovementSettings(actor);
|
||||||
actorMovementSettings.mPosition[0] = storage.mMovement.mPosition[0];
|
actorMovementSettings.mPosition[0] = movement.x();
|
||||||
actorMovementSettings.mPosition[1] = storage.mMovement.mPosition[1];
|
actorMovementSettings.mPosition[1] = movement.y();
|
||||||
actorMovementSettings.mPosition[2] = storage.mMovement.mPosition[2];
|
actorMovementSettings.mPosition[2] = storage.mMovement.mPosition[2];
|
||||||
|
|
||||||
rotateActorOnAxis(actor, 2, actorMovementSettings, storage);
|
rotateActorOnAxis(actor, 2, actorMovementSettings, storage);
|
||||||
|
@ -385,26 +391,11 @@ namespace MWMechanics
|
||||||
MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage)
|
MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage)
|
||||||
{
|
{
|
||||||
actorMovementSettings.mRotation[axis] = 0;
|
actorMovementSettings.mRotation[axis] = 0;
|
||||||
float& targetAngleRadians = storage.mMovement.mRotation[axis];
|
bool isRangedCombat = false;
|
||||||
if (targetAngleRadians != 0)
|
storage.mCurrentAction->getCombatRange(isRangedCombat);
|
||||||
{
|
float eps = isRangedCombat ? osg::DegreesToRadians(0.5) : osg::DegreesToRadians(3.f);
|
||||||
// Some attack animations contain small amount of movement.
|
float targetAngleRadians = storage.mMovement.mRotation[axis];
|
||||||
// Since we use cone shapes for melee, we can use a threshold to avoid jittering
|
smoothTurn(actor, targetAngleRadians, axis, eps);
|
||||||
std::shared_ptr<Action>& currentAction = storage.mCurrentAction;
|
|
||||||
bool isRangedCombat = false;
|
|
||||||
currentAction->getCombatRange(isRangedCombat);
|
|
||||||
// Check if the actor now facing desired direction, no need to turn any more
|
|
||||||
if (isRangedCombat)
|
|
||||||
{
|
|
||||||
if (smoothTurn(actor, targetAngleRadians, axis))
|
|
||||||
targetAngleRadians = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (smoothTurn(actor, targetAngleRadians, axis, osg::DegreesToRadians(3.f)))
|
|
||||||
targetAngleRadians = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr AiCombat::getTarget() const
|
MWWorld::Ptr AiCombat::getTarget() const
|
||||||
|
@ -489,12 +480,19 @@ namespace MWMechanics
|
||||||
// Note: do not use for ranged combat yet since in couple with back up behaviour can move actor out of cliff
|
// Note: do not use for ranged combat yet since in couple with back up behaviour can move actor out of cliff
|
||||||
else if (actor.getClass().isBipedal(actor))
|
else if (actor.getClass().isBipedal(actor))
|
||||||
{
|
{
|
||||||
// apply sideway movement (kind of dodging) with some probability
|
float moveDuration = 0;
|
||||||
// if actor is within range of target's weapon
|
float angleToTarget = Misc::normalizeAngle(mMovement.mRotation[2] - actor.getRefData().getPosition().rot[2]);
|
||||||
if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25)
|
// Apply a big side step if enemy tries to get around and come from behind.
|
||||||
|
// Otherwise apply a random side step (kind of dodging) with some probability
|
||||||
|
// if actor is within range of target's weapon.
|
||||||
|
if (std::abs(angleToTarget) > osg::PI / 4)
|
||||||
|
moveDuration = 0.2;
|
||||||
|
else if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25)
|
||||||
|
moveDuration = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
|
||||||
|
if (moveDuration > 0)
|
||||||
{
|
{
|
||||||
mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right
|
mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right
|
||||||
mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
|
mTimerCombatMove = moveDuration;
|
||||||
mCombatMove = true;
|
mCombatMove = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <components/esm/loadmgef.hpp>
|
#include <components/esm/loadmgef.hpp>
|
||||||
#include <components/detournavigator/navigator.hpp>
|
#include <components/detournavigator/navigator.hpp>
|
||||||
#include <components/misc/coordinateconverter.hpp>
|
#include <components/misc/coordinateconverter.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
@ -87,6 +88,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
//... But AI processing distance may increase in the future.
|
//... But AI processing distance may increase in the future.
|
||||||
if (isNearInactiveCell(position))
|
if (isNearInactiveCell(position))
|
||||||
{
|
{
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
||||||
return false;
|
return false;
|
||||||
|
@ -169,12 +171,34 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn to next path point by X,Z axes
|
// turn to next path point by X,Z axes
|
||||||
zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y()));
|
float zAngleToNext = mPathFinder.getZAngleToNext(position.x(), position.y());
|
||||||
|
zTurn(actor, zAngleToNext);
|
||||||
smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);
|
smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);
|
||||||
|
|
||||||
const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front();
|
const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front();
|
||||||
mObstacleCheck.update(actor, destination, duration);
|
mObstacleCheck.update(actor, destination, duration);
|
||||||
|
|
||||||
|
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
||||||
|
if (smoothMovement)
|
||||||
|
{
|
||||||
|
const float smoothTurnReservedDist = 150;
|
||||||
|
auto& movement = actor.getClass().getMovementSettings(actor);
|
||||||
|
float distToNextSqr = osg::Vec2f(destination.x() - position.x(), destination.y() - position.y()).length2();
|
||||||
|
float diffAngle = zAngleToNext - actor.getRefData().getPosition().rot[2];
|
||||||
|
if (std::cos(diffAngle) < -0.1)
|
||||||
|
movement.mPosition[0] = movement.mPosition[1] = 0;
|
||||||
|
else if (distToNextSqr > smoothTurnReservedDist * smoothTurnReservedDist)
|
||||||
|
{ // Go forward (and slowly turn towards the next path point)
|
||||||
|
movement.mPosition[0] = 0;
|
||||||
|
movement.mPosition[1] = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Next path point is near, so use diagonal movement to follow the path precisely.
|
||||||
|
movement.mPosition[0] = std::sin(diffAngle);
|
||||||
|
movement.mPosition[1] = std::max(std::cos(diffAngle), 0.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handle obstacles on the way
|
// handle obstacles on the way
|
||||||
evadeObstacles(actor);
|
evadeObstacles(actor);
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
static const int COUNT_BEFORE_RESET = 10;
|
static const int COUNT_BEFORE_RESET = 10;
|
||||||
static const float DOOR_CHECK_INTERVAL = 1.5f;
|
static const float IDLE_POSITION_CHECK_INTERVAL = 1.5f;
|
||||||
|
|
||||||
// to prevent overcrowding
|
// to prevent overcrowding
|
||||||
static const int DESTINATION_TOLERANCE = 64;
|
static const int DESTINATION_TOLERANCE = 64;
|
||||||
|
@ -96,6 +96,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
void stopMovement(const MWWorld::Ptr& actor)
|
void stopMovement(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,15 +425,14 @@ namespace MWMechanics
|
||||||
|
|
||||||
void AiWander::onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)
|
void AiWander::onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
// Check if an idle actor is too close to a door - if so start walking
|
// Check if an idle actor is too far from all allowed nodes or too close to a door - if so start walking.
|
||||||
storage.mDoorCheckDuration += duration;
|
storage.mCheckIdlePositionTimer += duration;
|
||||||
|
|
||||||
if (storage.mDoorCheckDuration >= DOOR_CHECK_INTERVAL)
|
if (storage.mCheckIdlePositionTimer >= IDLE_POSITION_CHECK_INTERVAL && !isStationary())
|
||||||
{
|
{
|
||||||
storage.mDoorCheckDuration = 0; // restart timer
|
storage.mCheckIdlePositionTimer = 0; // restart timer
|
||||||
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
|
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 1.6f;
|
||||||
if (mDistance && // actor is not intended to be stationary
|
if (proximityToDoor(actor, distance) || !isNearAllowedNode(actor, storage, distance))
|
||||||
proximityToDoor(actor, distance*1.6f))
|
|
||||||
{
|
{
|
||||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||||
storage.mTrimCurrentNode = false; // just in case
|
storage.mTrimCurrentNode = false; // just in case
|
||||||
|
@ -451,6 +451,20 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AiWander::isNearAllowedNode(const MWWorld::Ptr& actor, const AiWanderStorage& storage, float distance) const
|
||||||
|
{
|
||||||
|
const osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||||
|
auto cell = actor.getCell()->getCell();
|
||||||
|
for (const ESM::Pathgrid::Point& node : storage.mAllowedNodes)
|
||||||
|
{
|
||||||
|
osg::Vec3f point(node.mX, node.mY, node.mZ);
|
||||||
|
Misc::CoordinateConverter(cell).toWorld(point);
|
||||||
|
if ((actorPos - point).length2() < distance * distance)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)
|
void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
// Is there no destination or are we there yet?
|
// Is there no destination or are we there yet?
|
||||||
|
@ -468,6 +482,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
void AiWander::onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
void AiWander::onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
|
// Wait while fully stop before starting idle animation (important if "smooth movement" is enabled).
|
||||||
|
if (actor.getClass().getCurrentSpeed(actor) > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
unsigned short idleAnimation = getRandomIdle();
|
unsigned short idleAnimation = getRandomIdle();
|
||||||
storage.mIdleAnimation = idleAnimation;
|
storage.mIdleAnimation = idleAnimation;
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace MWMechanics
|
||||||
ESM::Pathgrid::Point mCurrentNode;
|
ESM::Pathgrid::Point mCurrentNode;
|
||||||
bool mTrimCurrentNode;
|
bool mTrimCurrentNode;
|
||||||
|
|
||||||
float mDoorCheckDuration;
|
float mCheckIdlePositionTimer;
|
||||||
int mStuckCount;
|
int mStuckCount;
|
||||||
|
|
||||||
AiWanderStorage():
|
AiWanderStorage():
|
||||||
|
@ -66,7 +66,7 @@ namespace MWMechanics
|
||||||
mPopulateAvailableNodes(true),
|
mPopulateAvailableNodes(true),
|
||||||
mAllowedNodes(),
|
mAllowedNodes(),
|
||||||
mTrimCurrentNode(false),
|
mTrimCurrentNode(false),
|
||||||
mDoorCheckDuration(0), // TODO: maybe no longer needed
|
mCheckIdlePositionTimer(0),
|
||||||
mStuckCount(0)
|
mStuckCount(0)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
@ -117,6 +117,8 @@ namespace MWMechanics
|
||||||
return mDestination;
|
return mDestination;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isStationary() const { return mDistance == 0; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void stopWalking(const MWWorld::Ptr& actor);
|
void stopWalking(const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
@ -137,6 +139,7 @@ namespace MWMechanics
|
||||||
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
|
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
|
||||||
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
|
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
|
||||||
void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);
|
void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);
|
||||||
|
bool isNearAllowedNode(const MWWorld::Ptr &actor, const AiWanderStorage& storage, float distance) const;
|
||||||
|
|
||||||
const int mDistance; // how far the actor can wander from the spawn point
|
const int mDistance; // how far the actor can wander from the spawn point
|
||||||
const int mDuration;
|
const int mDuration;
|
||||||
|
|
|
@ -1963,6 +1963,50 @@ void CharacterController::update(float duration, bool animationOnly)
|
||||||
if (isPlayer && !isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f)
|
if (isPlayer && !isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f)
|
||||||
movementSettings.mSpeedFactor *= 2.f;
|
movementSettings.mSpeedFactor *= 2.f;
|
||||||
|
|
||||||
|
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
||||||
|
if (smoothMovement && !isFirstPersonPlayer)
|
||||||
|
{
|
||||||
|
float angle = mPtr.getRefData().getPosition().rot[2];
|
||||||
|
osg::Vec2f targetSpeed = Misc::rotateVec2f(osg::Vec2f(vec.x(), vec.y()), -angle) * movementSettings.mSpeedFactor;
|
||||||
|
osg::Vec2f delta = targetSpeed - mSmoothedSpeed;
|
||||||
|
float speedDelta = movementSettings.mSpeedFactor - mSmoothedSpeed.length();
|
||||||
|
float deltaLen = delta.length();
|
||||||
|
|
||||||
|
float maxDelta;
|
||||||
|
if (std::abs(speedDelta) < deltaLen / 2)
|
||||||
|
// Turning is smooth for player and less smooth for NPCs (otherwise NPC can miss a path point).
|
||||||
|
maxDelta = duration * (isPlayer ? 3.f : 6.f);
|
||||||
|
else if (isPlayer && speedDelta < -deltaLen / 2)
|
||||||
|
// As soon as controls are released, mwinput switches player from running to walking.
|
||||||
|
// So stopping should be instant for player, otherwise it causes a small twitch.
|
||||||
|
maxDelta = 1;
|
||||||
|
else // In all other cases speeding up and stopping are smooth.
|
||||||
|
maxDelta = duration * 3.f;
|
||||||
|
|
||||||
|
if (deltaLen > maxDelta)
|
||||||
|
delta *= maxDelta / deltaLen;
|
||||||
|
mSmoothedSpeed += delta;
|
||||||
|
|
||||||
|
osg::Vec2f newSpeed = Misc::rotateVec2f(mSmoothedSpeed, angle);
|
||||||
|
movementSettings.mSpeedFactor = newSpeed.normalize();
|
||||||
|
vec.x() = newSpeed.x();
|
||||||
|
vec.y() = newSpeed.y();
|
||||||
|
|
||||||
|
const float eps = 0.001f;
|
||||||
|
if (movementSettings.mSpeedFactor < eps)
|
||||||
|
{
|
||||||
|
movementSettings.mSpeedFactor = 0;
|
||||||
|
vec.x() = 0;
|
||||||
|
vec.y() = 1;
|
||||||
|
}
|
||||||
|
else if ((vec.y() < 0) != mIsMovingBackward)
|
||||||
|
{
|
||||||
|
if (targetSpeed.length() < eps || (movementSettings.mPosition[1] < 0) == mIsMovingBackward)
|
||||||
|
vec.y() = mIsMovingBackward ? -eps : eps;
|
||||||
|
}
|
||||||
|
vec.normalize();
|
||||||
|
}
|
||||||
|
|
||||||
float effectiveRotation = rot.z();
|
float effectiveRotation = rot.z();
|
||||||
bool canMove = cls.getMaxSpeed(mPtr) > 0;
|
bool canMove = cls.getMaxSpeed(mPtr) > 0;
|
||||||
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
|
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
|
||||||
|
@ -1994,6 +2038,8 @@ void CharacterController::update(float duration, bool animationOnly)
|
||||||
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 2);
|
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 2);
|
||||||
else
|
else
|
||||||
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4);
|
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4);
|
||||||
|
if (smoothMovement && !isPlayer && !inwater)
|
||||||
|
mAnimation->setUpperBodyYawRadians(mAnimation->getUpperBodyYawRadians() + mAnimation->getHeadYaw() / 2);
|
||||||
|
|
||||||
speed = cls.getCurrentSpeed(mPtr);
|
speed = cls.getCurrentSpeed(mPtr);
|
||||||
vec.x() *= speed;
|
vec.x() *= speed;
|
||||||
|
@ -2185,13 +2231,11 @@ void CharacterController::update(float duration, bool animationOnly)
|
||||||
: (sneak ? CharState_SneakBack
|
: (sneak ? CharState_SneakBack
|
||||||
: (isrunning ? CharState_RunBack : CharState_WalkBack)));
|
: (isrunning ? CharState_RunBack : CharState_WalkBack)));
|
||||||
}
|
}
|
||||||
else if (effectiveRotation != 0.0f)
|
else
|
||||||
{
|
{
|
||||||
// Do not play turning animation for player if rotation speed is very slow.
|
// Do not play turning animation for player if rotation speed is very slow.
|
||||||
// Actual threshold should take framerate in account.
|
// Actual threshold should take framerate in account.
|
||||||
float rotationThreshold = 0.f;
|
float rotationThreshold = (isPlayer ? 0.015f : 0.001f) * 60 * duration;
|
||||||
if (isPlayer)
|
|
||||||
rotationThreshold = 0.015 * 60 * duration;
|
|
||||||
|
|
||||||
// It seems only bipedal actors use turning animations.
|
// It seems only bipedal actors use turning animations.
|
||||||
// Also do not use turning animations in the first-person view and when sneaking.
|
// Also do not use turning animations in the first-person view and when sneaking.
|
||||||
|
@ -2695,10 +2739,9 @@ void CharacterController::setVisibility(float visibility)
|
||||||
void CharacterController::setAttackTypeBasedOnMovement()
|
void CharacterController::setAttackTypeBasedOnMovement()
|
||||||
{
|
{
|
||||||
float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition;
|
float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition;
|
||||||
|
if (std::abs(move[1]) > std::abs(move[0]) + 0.2f) // forward-backward
|
||||||
if (move[1] && !move[0]) // forward-backward
|
|
||||||
mAttackType = "thrust";
|
mAttackType = "thrust";
|
||||||
else if (move[0] && !move[1]) //sideway
|
else if (std::abs(move[0]) > std::abs(move[1]) + 0.2f) // sideway
|
||||||
mAttackType = "slash";
|
mAttackType = "slash";
|
||||||
else
|
else
|
||||||
mAttackType = "chop";
|
mAttackType = "chop";
|
||||||
|
@ -2893,19 +2936,21 @@ void CharacterController::updateHeadTracking(float duration)
|
||||||
return;
|
return;
|
||||||
const osg::Vec3f actorDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
|
const osg::Vec3f actorDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
|
||||||
|
|
||||||
zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y());
|
zAngleRadians = std::atan2(actorDirection.x(), actorDirection.y()) - std::atan2(direction.x(), direction.y());
|
||||||
xAngleRadians = -std::asin(direction.z());
|
xAngleRadians = std::asin(direction.z());
|
||||||
|
|
||||||
const double xLimit = osg::DegreesToRadians(40.0);
|
|
||||||
const double zLimit = osg::DegreesToRadians(30.0);
|
|
||||||
zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians), -xLimit, xLimit);
|
|
||||||
xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -zLimit, zLimit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const double xLimit = osg::DegreesToRadians(40.0);
|
||||||
|
const double zLimit = osg::DegreesToRadians(30.0);
|
||||||
|
double zLimitOffset = mAnimation->getUpperBodyYawRadians();
|
||||||
|
xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -xLimit, xLimit);
|
||||||
|
zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians),
|
||||||
|
-zLimit + zLimitOffset, zLimit + zLimitOffset);
|
||||||
|
|
||||||
float factor = duration*5;
|
float factor = duration*5;
|
||||||
factor = std::min(factor, 1.f);
|
factor = std::min(factor, 1.f);
|
||||||
xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngleRadians);
|
xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * xAngleRadians;
|
||||||
zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngleRadians);
|
zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * zAngleRadians;
|
||||||
|
|
||||||
mAnimation->setHeadPitch(xAngleRadians);
|
mAnimation->setHeadPitch(xAngleRadians);
|
||||||
mAnimation->setHeadYaw(zAngleRadians);
|
mAnimation->setHeadYaw(zAngleRadians);
|
||||||
|
|
|
@ -196,6 +196,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
||||||
float mTimeUntilWake;
|
float mTimeUntilWake;
|
||||||
|
|
||||||
bool mIsMovingBackward;
|
bool mIsMovingBackward;
|
||||||
|
osg::Vec2f mSmoothedSpeed;
|
||||||
|
|
||||||
void setAttackTypeBasedOnMovement();
|
void setAttackTypeBasedOnMovement();
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,24 @@ namespace
|
||||||
const auto halfExtents = world->getHalfExtents(actor);
|
const auto halfExtents = world->getHalfExtents(actor);
|
||||||
return 2.0 * halfExtents.z();
|
return 2.0 * halfExtents.z();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if turn in `p2` is less than 10 degrees and all the 3 points are almost on one line.
|
||||||
|
bool isAlmostStraight(const osg::Vec3f& p1, const osg::Vec3f& p2, const osg::Vec3f& p3, float pointTolerance) {
|
||||||
|
osg::Vec3f v1 = p1 - p2;
|
||||||
|
osg::Vec3f v3 = p3 - p2;
|
||||||
|
v1.z() = v3.z() = 0;
|
||||||
|
float dotProduct = v1.x() * v3.x() + v1.y() * v3.y();
|
||||||
|
float crossProduct = v1.x() * v3.y() - v1.y() * v3.x();
|
||||||
|
|
||||||
|
// Check that the angle between v1 and v3 is less or equal than 10 degrees.
|
||||||
|
static const float cos170 = std::cos(osg::PI / 180 * 170);
|
||||||
|
bool checkAngle = dotProduct <= cos170 * v1.length() * v3.length();
|
||||||
|
|
||||||
|
// Check that distance from p2 to the line (p1, p3) is less or equal than `pointTolerance`.
|
||||||
|
bool checkDist = std::abs(crossProduct) <= pointTolerance * (p3 - p1).length() * 2;
|
||||||
|
|
||||||
|
return checkAngle && checkDist;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
|
@ -286,6 +304,11 @@ namespace MWMechanics
|
||||||
while (mPath.size() > 1 && sqrDistanceIgnoreZ(mPath.front(), position) < pointTolerance * pointTolerance)
|
while (mPath.size() > 1 && sqrDistanceIgnoreZ(mPath.front(), position) < pointTolerance * pointTolerance)
|
||||||
mPath.pop_front();
|
mPath.pop_front();
|
||||||
|
|
||||||
|
while (mPath.size() > 2 && isAlmostStraight(mPath[0], mPath[1], mPath[2], pointTolerance))
|
||||||
|
mPath.erase(mPath.begin() + 1);
|
||||||
|
if (mPath.size() > 1 && isAlmostStraight(position, mPath[0], mPath[1], pointTolerance))
|
||||||
|
mPath.pop_front();
|
||||||
|
|
||||||
if (mPath.size() == 1 && sqrDistanceIgnoreZ(mPath.front(), position) < destinationTolerance * destinationTolerance)
|
if (mPath.size() == 1 && sqrDistanceIgnoreZ(mPath.front(), position) < destinationTolerance * destinationTolerance)
|
||||||
mPath.pop_front();
|
mPath.pop_front();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
|
||||||
|
|
||||||
// reduce number of uses left
|
// reduce number of uses left
|
||||||
int uses = mTool.getClass().getItemHealth(mTool);
|
int uses = mTool.getClass().getItemHealth(mTool);
|
||||||
mTool.getCellRef().setCharge(uses-1);
|
uses -= std::min(uses, 1);
|
||||||
|
mTool.getCellRef().setCharge(uses);
|
||||||
|
|
||||||
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,10 @@ namespace MWMechanics
|
||||||
!lock.getClass().hasToolTip(lock)) //If it's unlocked or can not be unlocked back out immediately
|
!lock.getClass().hasToolTip(lock)) //If it's unlocked or can not be unlocked back out immediately
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
int uses = lockpick.getClass().getItemHealth(lockpick);
|
||||||
|
if (uses == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
int lockStrength = lock.getCellRef().getLockLevel();
|
int lockStrength = lock.getCellRef().getLockLevel();
|
||||||
|
|
||||||
float pickQuality = lockpick.get<ESM::Lockpick>()->mBase->mData.mQuality;
|
float pickQuality = lockpick.get<ESM::Lockpick>()->mBase->mData.mQuality;
|
||||||
|
@ -61,9 +65,7 @@ namespace MWMechanics
|
||||||
resultMessage = "#{sLockFail}";
|
resultMessage = "#{sLockFail}";
|
||||||
}
|
}
|
||||||
|
|
||||||
int uses = lockpick.getClass().getItemHealth(lockpick);
|
lockpick.getCellRef().setCharge(uses-1);
|
||||||
--uses;
|
|
||||||
lockpick.getCellRef().setCharge(uses);
|
|
||||||
if (!uses)
|
if (!uses)
|
||||||
lockpick.getContainerStore()->remove(lockpick, 1, mActor);
|
lockpick.getContainerStore()->remove(lockpick, 1, mActor);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +73,11 @@ namespace MWMechanics
|
||||||
void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe,
|
void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe,
|
||||||
std::string& resultMessage, std::string& resultSound)
|
std::string& resultMessage, std::string& resultSound)
|
||||||
{
|
{
|
||||||
if (trap.getCellRef().getTrap() == "")
|
if (trap.getCellRef().getTrap().empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int uses = probe.getClass().getItemHealth(probe);
|
||||||
|
if (uses == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float probeQuality = probe.get<ESM::Probe>()->mBase->mData.mQuality;
|
float probeQuality = probe.get<ESM::Probe>()->mBase->mData.mQuality;
|
||||||
|
@ -104,9 +110,7 @@ namespace MWMechanics
|
||||||
resultMessage = "#{sTrapFail}";
|
resultMessage = "#{sTrapFail}";
|
||||||
}
|
}
|
||||||
|
|
||||||
int uses = probe.getClass().getItemHealth(probe);
|
probe.getCellRef().setCharge(uses-1);
|
||||||
--uses;
|
|
||||||
probe.getCellRef().setCharge(uses);
|
|
||||||
if (!uses)
|
if (!uses)
|
||||||
probe.getContainerStore()->remove(probe, 1, mActor);
|
probe.getContainerStore()->remove(probe, 1, mActor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
#include "creaturestats.hpp"
|
#include "creaturestats.hpp"
|
||||||
|
#include "spellutil.hpp"
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
@ -43,9 +44,9 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool absorbSpell (const ESM::Spell* spell, const MWWorld::Ptr& caster, const MWWorld::Ptr& target)
|
bool absorbSpell (const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target)
|
||||||
{
|
{
|
||||||
if (!spell || caster == target || !target.getClass().isActor())
|
if (spellId.empty() || caster == target || !target.getClass().isActor())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CreatureStats& stats = target.getClass().getCreatureStats(target);
|
CreatureStats& stats = target.getClass().getCreatureStats(target);
|
||||||
|
@ -62,13 +63,27 @@ namespace MWMechanics
|
||||||
if (Misc::Rng::roll0to99() >= chance)
|
if (Misc::Rng::roll0to99() >= chance)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find("VFX_Absorb");
|
const auto& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
const ESM::Static* absorbStatic = esmStore.get<ESM::Static>().find("VFX_Absorb");
|
||||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);
|
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);
|
||||||
if (animation && !absorbStatic->mModel.empty())
|
if (animation && !absorbStatic->mModel.empty())
|
||||||
animation->addEffect( "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, std::string());
|
animation->addEffect( "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, std::string());
|
||||||
|
const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(spellId);
|
||||||
|
int spellCost = 0;
|
||||||
|
if (spell)
|
||||||
|
{
|
||||||
|
spellCost = spell->mData.mCost;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const ESM::Enchantment* enchantment = esmStore.get<ESM::Enchantment>().search(spellId);
|
||||||
|
if (enchantment)
|
||||||
|
spellCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), caster);
|
||||||
|
}
|
||||||
|
|
||||||
// Magicka is increased by the cost of the spell
|
// Magicka is increased by the cost of the spell
|
||||||
DynamicStat<float> magicka = stats.getMagicka();
|
DynamicStat<float> magicka = stats.getMagicka();
|
||||||
magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost);
|
magicka.setCurrent(magicka.getCurrent() + spellCost);
|
||||||
stats.setMagicka(magicka);
|
stats.setMagicka(magicka);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
#ifndef MWMECHANICS_SPELLABSORPTION_H
|
#ifndef MWMECHANICS_SPELLABSORPTION_H
|
||||||
#define MWMECHANICS_SPELLABSORPTION_H
|
#define MWMECHANICS_SPELLABSORPTION_H
|
||||||
|
|
||||||
namespace ESM
|
#include <string>
|
||||||
{
|
|
||||||
struct Spell;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
@ -14,7 +11,7 @@ namespace MWWorld
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
// Try to absorb a spell based on the magnitude of every Spell Absorption effect source on the target.
|
// Try to absorb a spell based on the magnitude of every Spell Absorption effect source on the target.
|
||||||
bool absorbSpell(const ESM::Spell* spell, const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
|
bool absorbSpell(const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -119,7 +119,7 @@ namespace MWMechanics
|
||||||
// effects, we display a "can't re-cast" message
|
// effects, we display a "can't re-cast" message
|
||||||
|
|
||||||
// Try absorbing the spell. Some handling must still happen for absorbed effects.
|
// Try absorbing the spell. Some handling must still happen for absorbed effects.
|
||||||
bool absorbed = absorbSpell(spell, caster, target);
|
bool absorbed = absorbSpell(mId, caster, target);
|
||||||
|
|
||||||
int currentEffectIndex = 0;
|
int currentEffectIndex = 0;
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
#include "steering.hpp"
|
#include "steering.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/mathutil.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
@ -12,19 +15,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians)
|
bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians)
|
||||||
{
|
{
|
||||||
float currentAngle (actor.getRefData().getPosition().rot[axis]);
|
MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor);
|
||||||
float diff (targetAngleRadians - currentAngle);
|
float diff = Misc::normalizeAngle(targetAngleRadians - actor.getRefData().getPosition().rot[axis]);
|
||||||
if (std::abs(diff) >= osg::DegreesToRadians(180.f))
|
|
||||||
{
|
|
||||||
if (diff >= 0)
|
|
||||||
{
|
|
||||||
diff = diff - osg::DegreesToRadians(360.f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
diff = osg::DegreesToRadians(360.f) + diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
float absDiff = std::abs(diff);
|
float absDiff = std::abs(diff);
|
||||||
|
|
||||||
// The turning animation actually moves you slightly, so the angle will be wrong again.
|
// The turning animation actually moves you slightly, so the angle will be wrong again.
|
||||||
|
@ -33,10 +25,14 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
float limit = getAngularVelocity(actor.getClass().getMaxSpeed(actor)) * MWBase::Environment::get().getFrameDuration();
|
float limit = getAngularVelocity(actor.getClass().getMaxSpeed(actor)) * MWBase::Environment::get().getFrameDuration();
|
||||||
|
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
||||||
|
if (smoothMovement)
|
||||||
|
limit *= std::min(absDiff / osg::PI + 0.1, 0.5);
|
||||||
|
|
||||||
if (absDiff > limit)
|
if (absDiff > limit)
|
||||||
diff = osg::sign(diff) * limit;
|
diff = osg::sign(diff) * limit;
|
||||||
|
|
||||||
actor.getClass().getMovementSettings(actor).mRotation[axis] = diff;
|
movement.mRotation[axis] = diff;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,6 +264,8 @@ namespace MWPhysics
|
||||||
|
|
||||||
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr> targets, int mask, int group) const
|
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr> targets, int mask, int group) const
|
||||||
{
|
{
|
||||||
|
if (from == to)
|
||||||
|
return RayCastingResult { false };
|
||||||
btVector3 btFrom = Misc::Convert::toBullet(from);
|
btVector3 btFrom = Misc::Convert::toBullet(from);
|
||||||
btVector3 btTo = Misc::Convert::toBullet(to);
|
btVector3 btTo = Misc::Convert::toBullet(to);
|
||||||
|
|
||||||
|
|
|
@ -159,12 +159,20 @@ namespace MWScript
|
||||||
float ay = ptr.getRefData().getPosition().rot[1];
|
float ay = ptr.getRefData().getPosition().rot[1];
|
||||||
float az = ptr.getRefData().getPosition().rot[2];
|
float az = ptr.getRefData().getPosition().rot[2];
|
||||||
|
|
||||||
|
// XYZ axis use the inverse (XYZ) rotation order like vanilla SetAngle.
|
||||||
|
// UWV axis use the standard (ZYX) rotation order like TESCS/OpenMW-CS and the rest of the game.
|
||||||
if (axis == "x")
|
if (axis == "x")
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az);
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az,MWBase::RotationFlag_inverseOrder);
|
||||||
else if (axis == "y")
|
else if (axis == "y")
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az);
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az,MWBase::RotationFlag_inverseOrder);
|
||||||
else if (axis == "z")
|
else if (axis == "z")
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle);
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle,MWBase::RotationFlag_inverseOrder);
|
||||||
|
else if (axis == "u")
|
||||||
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az,MWBase::RotationFlag_none);
|
||||||
|
else if (axis == "w")
|
||||||
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az,MWBase::RotationFlag_none);
|
||||||
|
else if (axis == "v")
|
||||||
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle,MWBase::RotationFlag_none);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
// Find any NPCs that are following the actor and teleport them with him
|
// Find any NPCs that are following the actor and teleport them with him
|
||||||
std::set<MWWorld::Ptr> followers;
|
std::set<MWWorld::Ptr> followers;
|
||||||
getFollowersToTeleport(actor, followers);
|
getFollowers(actor, followers, true);
|
||||||
|
|
||||||
for (std::set<MWWorld::Ptr>::iterator it = followers.begin(); it != followers.end(); ++it)
|
for (std::set<MWWorld::Ptr>::iterator it = followers.begin(); it != followers.end(); ++it)
|
||||||
teleport(*it);
|
teleport(*it);
|
||||||
|
@ -47,7 +47,9 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mCellName.empty())
|
if (actor.getClass().getCreatureStats(actor).getAiSequence().isInCombat(world->getPlayerPtr()))
|
||||||
|
actor.getClass().getCreatureStats(actor).getAiSequence().stopCombat();
|
||||||
|
else if (mCellName.empty())
|
||||||
{
|
{
|
||||||
int cellX;
|
int cellX;
|
||||||
int cellY;
|
int cellY;
|
||||||
|
@ -60,7 +62,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionTeleport::getFollowersToTeleport(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) {
|
void ActionTeleport::getFollowers(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out, bool includeHostiles) {
|
||||||
std::set<MWWorld::Ptr> followers;
|
std::set<MWWorld::Ptr> followers;
|
||||||
MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor, followers);
|
MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor, followers);
|
||||||
|
|
||||||
|
@ -69,11 +71,17 @@ namespace MWWorld
|
||||||
MWWorld::Ptr follower = *it;
|
MWWorld::Ptr follower = *it;
|
||||||
|
|
||||||
std::string script = follower.getClass().getScript(follower);
|
std::string script = follower.getClass().getScript(follower);
|
||||||
|
|
||||||
|
if (!includeHostiles && follower.getClass().getCreatureStats(follower).getAiSequence().isInCombat(actor))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1)
|
if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() <= 800*800)
|
if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() > 800 * 800)
|
||||||
out.insert(follower);
|
continue;
|
||||||
|
|
||||||
|
out.emplace(follower);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,9 @@ namespace MWWorld
|
||||||
/// @param teleportFollowers Whether to teleport any following actors of the target actor as well.
|
/// @param teleportFollowers Whether to teleport any following actors of the target actor as well.
|
||||||
ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers);
|
ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers);
|
||||||
|
|
||||||
/// Outputs every actor follower who is in teleport range and wasn't ordered to not enter interiors
|
/// @param includeHostiles If true, include hostile followers (which won't actually be teleported) in the output,
|
||||||
static void getFollowersToTeleport(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out);
|
/// e.g. so that the teleport action can calm them.
|
||||||
|
static void getFollowers(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out, bool includeHostiles = false);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -287,9 +287,9 @@ namespace MWWorld
|
||||||
///< Return a pointer to a liveCellRef with the given name.
|
///< Return a pointer to a liveCellRef with the given name.
|
||||||
/// \param activeOnly do non search inactive cells.
|
/// \param activeOnly do non search inactive cells.
|
||||||
|
|
||||||
Ptr searchPtr (const std::string& name, bool activeOnly, bool searchInContainers = true) override;
|
Ptr searchPtr (const std::string& name, bool activeOnly, bool searchInContainers = false) override;
|
||||||
///< Return a pointer to a liveCellRef with the given name.
|
///< Return a pointer to a liveCellRef with the given name.
|
||||||
/// \param activeOnly do non search inactive cells.
|
/// \param activeOnly do not search inactive cells.
|
||||||
|
|
||||||
Ptr searchPtrViaActorId (int actorId) override;
|
Ptr searchPtrViaActorId (int actorId) override;
|
||||||
///< Search is limited to the active cells.
|
///< Search is limited to the active cells.
|
||||||
|
|
|
@ -244,6 +244,7 @@ namespace
|
||||||
void init(Nif::Named& value)
|
void init(Nif::Named& value)
|
||||||
{
|
{
|
||||||
value.extra = Nif::ExtraPtr(nullptr);
|
value.extra = Nif::ExtraPtr(nullptr);
|
||||||
|
value.extralist = Nif::ExtraList();
|
||||||
value.controller = Nif::ControllerPtr(nullptr);
|
value.controller = Nif::ControllerPtr(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace
|
||||||
"\n"
|
"\n"
|
||||||
"void bar() { foo() }\n"
|
"void bar() { foo() }\n"
|
||||||
"\n"
|
"\n"
|
||||||
"#line 2 0\n"
|
"#line 1 0\n"
|
||||||
"\n"
|
"\n"
|
||||||
"void main() { bar() }\n";
|
"void main() { bar() }\n";
|
||||||
EXPECT_EQ(shader->getShaderSource(), expected);
|
EXPECT_EQ(shader->getShaderSource(), expected);
|
||||||
|
|
|
@ -90,7 +90,7 @@ add_component_dir (misc
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (debug
|
add_component_dir (debug
|
||||||
debugging debuglog
|
debugging debuglog gldebug
|
||||||
)
|
)
|
||||||
|
|
||||||
IF(NOT WIN32 AND NOT APPLE)
|
IF(NOT WIN32 AND NOT APPLE)
|
||||||
|
|
164
components/debug/gldebug.cpp
Normal file
164
components/debug/gldebug.cpp
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// This file is based heavily on code from https://github.com/ThermalPixel/osgdemos/blob/master/osgdebug/EnableGLDebugOperation.cpp
|
||||||
|
// The original licence is included below:
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Andreas Klein
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
The views and conclusions contained in the software and documentation are those
|
||||||
|
of the authors and should not be interpreted as representing official policies,
|
||||||
|
either expressed or implied, of the FreeBSD Project.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gldebug.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
// OpenGL constants not provided by OSG:
|
||||||
|
#include <SDL_opengl_glext.h>
|
||||||
|
|
||||||
|
void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
|
||||||
|
{
|
||||||
|
#ifdef GL_DEBUG_OUTPUT
|
||||||
|
std::string srcStr;
|
||||||
|
switch (source)
|
||||||
|
{
|
||||||
|
case GL_DEBUG_SOURCE_API:
|
||||||
|
srcStr = "API";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
||||||
|
srcStr = "WINDOW_SYSTEM";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
||||||
|
srcStr = "SHADER_COMPILER";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
||||||
|
srcStr = "THIRD_PARTY";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SOURCE_APPLICATION:
|
||||||
|
srcStr = "APPLICATION";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SOURCE_OTHER:
|
||||||
|
srcStr = "OTHER";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
srcStr = "UNDEFINED";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string typeStr;
|
||||||
|
|
||||||
|
Debug::Level logSeverity = Debug::Warning;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GL_DEBUG_TYPE_ERROR:
|
||||||
|
typeStr = "ERROR";
|
||||||
|
logSeverity = Debug::Error;
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||||
|
typeStr = "DEPRECATED_BEHAVIOR";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||||
|
typeStr = "UNDEFINED_BEHAVIOR";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_TYPE_PORTABILITY:
|
||||||
|
typeStr = "PORTABILITY";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||||
|
typeStr = "PERFORMANCE";
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_TYPE_OTHER:
|
||||||
|
typeStr = "OTHER";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
typeStr = "UNDEFINED";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(logSeverity) << "OpenGL " << typeStr << " [" << srcStr << "]: " << message;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void enableGLDebugExtension(unsigned int contextID)
|
||||||
|
{
|
||||||
|
#ifdef GL_DEBUG_OUTPUT
|
||||||
|
typedef void (GL_APIENTRY *DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam);
|
||||||
|
typedef void (GL_APIENTRY *GLDebugMessageControlFunction)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
|
||||||
|
typedef void (GL_APIENTRY *GLDebugMessageCallbackFunction)(DEBUGPROC, const void* userParam);
|
||||||
|
|
||||||
|
GLDebugMessageControlFunction glDebugMessageControl = nullptr;
|
||||||
|
GLDebugMessageCallbackFunction glDebugMessageCallback = nullptr;
|
||||||
|
|
||||||
|
if (osg::isGLExtensionSupported(contextID, "GL_KHR_debug"))
|
||||||
|
{
|
||||||
|
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallback");
|
||||||
|
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControl");
|
||||||
|
}
|
||||||
|
else if (osg::isGLExtensionSupported(contextID, "GL_ARB_debug_output"))
|
||||||
|
{
|
||||||
|
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackARB");
|
||||||
|
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlARB");
|
||||||
|
}
|
||||||
|
else if (osg::isGLExtensionSupported(contextID, "GL_AMD_debug_output"))
|
||||||
|
{
|
||||||
|
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackAMD");
|
||||||
|
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlAMD");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glDebugMessageCallback && glDebugMessageControl)
|
||||||
|
{
|
||||||
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
|
glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, true);
|
||||||
|
glDebugMessageCallback(debugCallback, nullptr);
|
||||||
|
|
||||||
|
Log(Debug::Info) << "OpenGL debug callback attached.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
Log(Debug::Error) << "Unable to attach OpenGL debug callback.";
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug::EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||||
|
|
||||||
|
unsigned int contextID = graphicsContext->getState()->getContextID();
|
||||||
|
enableGLDebugExtension(contextID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Debug::shouldDebugOpenGL()
|
||||||
|
{
|
||||||
|
const char* env = std::getenv("OPENMW_DEBUG_OPENGL");
|
||||||
|
if (!env)
|
||||||
|
return false;
|
||||||
|
std::string str(env);
|
||||||
|
if (str.length() == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return str.find("OFF") == std::string::npos && str.find("0") == std::string::npos && str.find("NO") == std::string::npos;
|
||||||
|
}
|
21
components/debug/gldebug.hpp
Normal file
21
components/debug/gldebug.hpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_DEBUG_GLDEBUG_H
|
||||||
|
#define OPENMW_COMPONENTS_DEBUG_GLDEBUG_H
|
||||||
|
|
||||||
|
#include <osgViewer/ViewerEventHandlers>
|
||||||
|
|
||||||
|
namespace Debug
|
||||||
|
{
|
||||||
|
class EnableGLDebugOperation : public osg::GraphicsOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EnableGLDebugOperation();
|
||||||
|
|
||||||
|
virtual void operator()(osg::GraphicsContext* graphicsContext);
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenThreads::Mutex mMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool shouldDebugOpenGL();
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -14,12 +14,18 @@ namespace Nif
|
||||||
class Extra : public Record
|
class Extra : public Record
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
std::string name;
|
||||||
ExtraPtr next; // Next extra data record in the list
|
ExtraPtr next; // Next extra data record in the list
|
||||||
|
|
||||||
void read(NIFStream *nif)
|
void read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
next.read(nif);
|
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||||
nif->getUInt(); // Size of the record
|
name = nif->getString();
|
||||||
|
else if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||||
|
{
|
||||||
|
next.read(nif);
|
||||||
|
nif->getUInt(); // Size of the record
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void post(NIFFile *nif) { next.post(nif); }
|
void post(NIFFile *nif) { next.post(nif); }
|
||||||
|
@ -44,18 +50,23 @@ class Named : public Record
|
||||||
public:
|
public:
|
||||||
std::string name;
|
std::string name;
|
||||||
ExtraPtr extra;
|
ExtraPtr extra;
|
||||||
|
ExtraList extralist;
|
||||||
ControllerPtr controller;
|
ControllerPtr controller;
|
||||||
|
|
||||||
void read(NIFStream *nif)
|
void read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
name = nif->getString();
|
name = nif->getString();
|
||||||
extra.read(nif);
|
if (nif->getVersion() < NIFStream::generateVersion(10,0,1,0))
|
||||||
|
extra.read(nif);
|
||||||
|
else
|
||||||
|
extralist.read(nif);
|
||||||
controller.read(nif);
|
controller.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void post(NIFFile *nif)
|
void post(NIFFile *nif)
|
||||||
{
|
{
|
||||||
extra.post(nif);
|
extra.post(nif);
|
||||||
|
extralist.post(nif);
|
||||||
controller.post(nif);
|
controller.post(nif);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,16 +14,31 @@ namespace Nif
|
||||||
if (external)
|
if (external)
|
||||||
filename = nif->getString();
|
filename = nif->getString();
|
||||||
else
|
else
|
||||||
internal = nif->getChar();
|
{
|
||||||
|
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,3))
|
||||||
if (!external && internal)
|
internal = nif->getChar();
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
filename = nif->getString(); // Original file path of the internal texture
|
||||||
|
}
|
||||||
|
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,3))
|
||||||
|
{
|
||||||
|
if (!external && internal)
|
||||||
|
data.read(nif);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
|
}
|
||||||
|
|
||||||
pixel = nif->getUInt();
|
pixel = nif->getUInt();
|
||||||
mipmap = nif->getUInt();
|
mipmap = nif->getUInt();
|
||||||
alpha = nif->getUInt();
|
alpha = nif->getUInt();
|
||||||
|
|
||||||
nif->getChar(); // always 1
|
nif->getChar(); // always 1
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,103))
|
||||||
|
nif->getBoolean(); // Direct rendering
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,4))
|
||||||
|
nif->getBoolean(); // NiPersistentSrcTextureRendererData is used instead of NiPixelData
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiSourceTexture::post(NIFFile *nif)
|
void NiSourceTexture::post(NIFFile *nif)
|
||||||
|
@ -79,6 +94,12 @@ namespace Nif
|
||||||
NiParticleModifier::read(nif);
|
NiParticleModifier::read(nif);
|
||||||
|
|
||||||
mBounceFactor = nif->getFloat();
|
mBounceFactor = nif->getFloat();
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(4,2,2,0))
|
||||||
|
{
|
||||||
|
// Unused in NifSkope. Need to figure out what these do.
|
||||||
|
/*bool spawnOnCollision = */nif->getBoolean();
|
||||||
|
/*bool dieOnCollision = */nif->getBoolean();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiPlanarCollider::read(NIFStream *nif)
|
void NiPlanarCollider::read(NIFStream *nif)
|
||||||
|
|
|
@ -97,7 +97,10 @@ namespace Nif
|
||||||
// 01: Diffuse
|
// 01: Diffuse
|
||||||
// 10: Specular
|
// 10: Specular
|
||||||
// 11: Emissive
|
// 11: Emissive
|
||||||
targetColor = (flags >> 4) & 3;
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
targetColor = nif->getUShort() & 3;
|
||||||
|
else
|
||||||
|
targetColor = (flags >> 4) & 3;
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +113,8 @@ namespace Nif
|
||||||
void NiLookAtController::read(NIFStream *nif)
|
void NiLookAtController::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Controller::read(nif);
|
Controller::read(nif);
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
lookAtFlags = nif->getUShort();
|
||||||
target.read(nif);
|
target.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,25 +170,13 @@ namespace Nif
|
||||||
data.post(nif);
|
data.post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiAlphaController::read(NIFStream *nif)
|
void NiFloatInterpController::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Controller::read(nif);
|
Controller::read(nif);
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiAlphaController::post(NIFFile *nif)
|
void NiFloatInterpController::post(NIFFile *nif)
|
||||||
{
|
|
||||||
Controller::post(nif);
|
|
||||||
data.post(nif);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NiRollController::read(NIFStream *nif)
|
|
||||||
{
|
|
||||||
Controller::read(nif);
|
|
||||||
data.read(nif);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NiRollController::post(NIFFile *nif)
|
|
||||||
{
|
{
|
||||||
Controller::post(nif);
|
Controller::post(nif);
|
||||||
data.post(nif);
|
data.post(nif);
|
||||||
|
@ -192,6 +185,8 @@ namespace Nif
|
||||||
void NiGeomMorpherController::read(NIFStream *nif)
|
void NiGeomMorpherController::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Controller::read(nif);
|
Controller::read(nif);
|
||||||
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_OB_OLD)
|
||||||
|
/*bool updateNormals = !!*/nif->getUShort();
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
||||||
/*bool alwaysActive = */nif->getChar(); // Always 0
|
/*bool alwaysActive = */nif->getChar(); // Always 0
|
||||||
|
@ -219,8 +214,11 @@ namespace Nif
|
||||||
{
|
{
|
||||||
Controller::read(nif);
|
Controller::read(nif);
|
||||||
mTexSlot = nif->getUInt();
|
mTexSlot = nif->getUInt();
|
||||||
/*unknown=*/nif->getUInt();/*0?*/
|
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||||
mDelta = nif->getFloat();
|
{
|
||||||
|
timeStart = nif->getFloat();
|
||||||
|
mDelta = nif->getFloat();
|
||||||
|
}
|
||||||
mSources.read(nif);
|
mSources.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,7 @@ class NiLookAtController : public Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NodePtr target;
|
NodePtr target;
|
||||||
|
unsigned short lookAtFlags{0};
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
void post(NIFFile *nif);
|
void post(NIFFile *nif);
|
||||||
|
@ -142,23 +143,16 @@ public:
|
||||||
void post(NIFFile *nif);
|
void post(NIFFile *nif);
|
||||||
};
|
};
|
||||||
|
|
||||||
class NiAlphaController : public Controller
|
struct NiFloatInterpController : public Controller
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
NiFloatDataPtr data;
|
NiFloatDataPtr data;
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
void post(NIFFile *nif);
|
void post(NIFFile *nif);
|
||||||
};
|
};
|
||||||
|
|
||||||
class NiRollController : public Controller
|
class NiAlphaController : public NiFloatInterpController { };
|
||||||
{
|
class NiRollController : public NiFloatInterpController { };
|
||||||
public:
|
|
||||||
NiFloatDataPtr data;
|
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
|
||||||
void post(NIFFile *nif);
|
|
||||||
};
|
|
||||||
|
|
||||||
class NiGeomMorpherController : public Controller
|
class NiGeomMorpherController : public Controller
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,13 +33,33 @@ void NiSkinInstance::post(NIFFile *nif)
|
||||||
|
|
||||||
void NiGeometryData::read(NIFStream *nif)
|
void NiGeometryData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,114))
|
||||||
|
nif->getInt(); // Group ID. (Almost?) always 0.
|
||||||
|
|
||||||
int verts = nif->getUShort();
|
int verts = nif->getUShort();
|
||||||
|
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
nif->skip(2); // Keep flags and compress flags
|
||||||
|
|
||||||
if (nif->getBoolean())
|
if (nif->getBoolean())
|
||||||
nif->getVector3s(vertices, verts);
|
nif->getVector3s(vertices, verts);
|
||||||
|
|
||||||
|
unsigned int dataFlags = 0;
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||||
|
dataFlags = nif->getUShort();
|
||||||
|
|
||||||
|
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
|
||||||
|
nif->getUInt(); // Material CRC
|
||||||
|
|
||||||
if (nif->getBoolean())
|
if (nif->getBoolean())
|
||||||
|
{
|
||||||
nif->getVector3s(normals, verts);
|
nif->getVector3s(normals, verts);
|
||||||
|
if (dataFlags & 0x1000)
|
||||||
|
{
|
||||||
|
nif->getVector3s(tangents, verts);
|
||||||
|
nif->getVector3s(bitangents, verts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
center = nif->getVector3();
|
center = nif->getVector3();
|
||||||
radius = nif->getFloat();
|
radius = nif->getFloat();
|
||||||
|
@ -47,14 +67,27 @@ void NiGeometryData::read(NIFStream *nif)
|
||||||
if (nif->getBoolean())
|
if (nif->getBoolean())
|
||||||
nif->getVector4s(colors, verts);
|
nif->getVector4s(colors, verts);
|
||||||
|
|
||||||
// In Morrowind this field only corresponds to the number of UV sets.
|
// Only the first 6 bits are used as a count. I think the rest are
|
||||||
// NifTools research is inaccurate.
|
// flags of some sort.
|
||||||
int uvs = nif->getUShort();
|
unsigned int numUVs = dataFlags;
|
||||||
|
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||||
if(nif->getInt())
|
|
||||||
{
|
{
|
||||||
uvlist.resize(uvs);
|
numUVs = nif->getUShort();
|
||||||
for(int i = 0;i < uvs;i++)
|
// In Morrowind this field only corresponds to the number of UV sets.
|
||||||
|
// NifTools research is inaccurate.
|
||||||
|
if (nif->getVersion() > NIFFile::NIFVersion::VER_MW)
|
||||||
|
numUVs &= 0x3f;
|
||||||
|
}
|
||||||
|
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0)
|
||||||
|
numUVs &= 0x1;
|
||||||
|
|
||||||
|
bool hasUVs = true;
|
||||||
|
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)
|
||||||
|
hasUVs = nif->getBoolean();
|
||||||
|
if (hasUVs)
|
||||||
|
{
|
||||||
|
uvlist.resize(numUVs);
|
||||||
|
for (unsigned int i = 0; i < numUVs; i++)
|
||||||
{
|
{
|
||||||
nif->getVector2s(uvlist[i], verts);
|
nif->getVector2s(uvlist[i], verts);
|
||||||
// flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin
|
// flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin
|
||||||
|
@ -64,6 +97,12 @@ void NiGeometryData::read(NIFStream *nif)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||||
|
nif->getUShort(); // Consistency flags
|
||||||
|
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(20,0,0,4))
|
||||||
|
nif->skip(4); // Additional data
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiTriShapeData::read(NIFStream *nif)
|
void NiTriShapeData::read(NIFStream *nif)
|
||||||
|
@ -75,13 +114,17 @@ void NiTriShapeData::read(NIFStream *nif)
|
||||||
// We have three times as many vertices as triangles, so this
|
// We have three times as many vertices as triangles, so this
|
||||||
// is always equal to tris*3.
|
// is always equal to tris*3.
|
||||||
int cnt = nif->getInt();
|
int cnt = nif->getInt();
|
||||||
nif->getUShorts(triangles, cnt);
|
bool hasTriangles = true;
|
||||||
|
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)
|
||||||
|
hasTriangles = nif->getBoolean();
|
||||||
|
if (hasTriangles)
|
||||||
|
nif->getUShorts(triangles, cnt);
|
||||||
|
|
||||||
// Read the match list, which lists the vertices that are equal to
|
// Read the match list, which lists the vertices that are equal to
|
||||||
// vertices. We don't actually need need this for anything, so
|
// vertices. We don't actually need need this for anything, so
|
||||||
// just skip it.
|
// just skip it.
|
||||||
int verts = nif->getUShort();
|
unsigned short verts = nif->getUShort();
|
||||||
for(int i=0;i < verts;i++)
|
for (unsigned short i=0; i < verts; i++)
|
||||||
{
|
{
|
||||||
// Number of vertices matching vertex 'i'
|
// Number of vertices matching vertex 'i'
|
||||||
int num = nif->getUShort();
|
int num = nif->getUShort();
|
||||||
|
@ -101,7 +144,11 @@ void NiTriStripsData::read(NIFStream *nif)
|
||||||
std::vector<unsigned short> lengths;
|
std::vector<unsigned short> lengths;
|
||||||
nif->getUShorts(lengths, numStrips);
|
nif->getUShorts(lengths, numStrips);
|
||||||
|
|
||||||
if (!numStrips)
|
// "Has Strips" flag. Exceptionally useful.
|
||||||
|
bool hasStrips = false;
|
||||||
|
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)
|
||||||
|
hasStrips = nif->getBoolean();
|
||||||
|
if (!hasStrips || !numStrips)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
strips.resize(numStrips);
|
strips.resize(numStrips);
|
||||||
|
@ -140,27 +187,37 @@ void NiAutoNormalParticlesData::read(NIFStream *nif)
|
||||||
NiGeometryData::read(nif);
|
NiGeometryData::read(nif);
|
||||||
|
|
||||||
// Should always match the number of vertices
|
// Should always match the number of vertices
|
||||||
numParticles = nif->getUShort();
|
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)
|
||||||
|
numParticles = nif->getUShort();
|
||||||
|
|
||||||
particleRadius = nif->getFloat();
|
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
||||||
|
std::fill(particleRadii.begin(), particleRadii.end(), nif->getFloat());
|
||||||
|
else if (nif->getBoolean())
|
||||||
|
nif->getFloats(particleRadii, vertices.size());
|
||||||
activeCount = nif->getUShort();
|
activeCount = nif->getUShort();
|
||||||
|
|
||||||
|
// Particle sizes
|
||||||
if (nif->getBoolean())
|
if (nif->getBoolean())
|
||||||
{
|
|
||||||
// Particle sizes
|
|
||||||
nif->getFloats(sizes, vertices.size());
|
nif->getFloats(sizes, vertices.size());
|
||||||
|
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0) && nif->getBoolean())
|
||||||
|
nif->getQuaternions(rotations, vertices.size());
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(20,0,0,4))
|
||||||
|
{
|
||||||
|
if (nif->getBoolean())
|
||||||
|
nif->getFloats(rotationAngles, vertices.size());
|
||||||
|
if (nif->getBoolean())
|
||||||
|
nif->getVector3s(rotationAxes, vertices.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiRotatingParticlesData::read(NIFStream *nif)
|
void NiRotatingParticlesData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
NiAutoNormalParticlesData::read(nif);
|
NiAutoNormalParticlesData::read(nif);
|
||||||
|
|
||||||
if (nif->getBoolean())
|
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0) && nif->getBoolean())
|
||||||
{
|
|
||||||
// Rotation quaternions.
|
|
||||||
nif->getQuaternions(rotations, vertices.size());
|
nif->getQuaternions(rotations, vertices.size());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiPosData::read(NIFStream *nif)
|
void NiPosData::read(NIFStream *nif)
|
||||||
|
@ -188,12 +245,27 @@ void NiPixelData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
fmt = (Format)nif->getUInt();
|
fmt = (Format)nif->getUInt();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 4; ++i)
|
if (nif->getVersion() < NIFStream::generateVersion(10,4,0,2))
|
||||||
colorMask[i] = nif->getUInt();
|
{
|
||||||
bpp = nif->getUInt();
|
for (unsigned int i = 0; i < 4; ++i)
|
||||||
|
colorMask[i] = nif->getUInt();
|
||||||
|
bpp = nif->getUInt();
|
||||||
|
nif->skip(8); // "Old Fast Compare". Whatever that means.
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
pixelTiling = nif->getUInt();
|
||||||
|
}
|
||||||
|
else // TODO: see if anything from here needs to be implemented
|
||||||
|
{
|
||||||
|
bpp = nif->getChar();
|
||||||
|
nif->skip(4); // Renderer hint
|
||||||
|
nif->skip(4); // Extra data
|
||||||
|
nif->skip(4); // Flags
|
||||||
|
pixelTiling = nif->getUInt();
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(20,3,0,4))
|
||||||
|
sRGB = nif->getBoolean();
|
||||||
|
nif->skip(4*10); // Channel data
|
||||||
|
}
|
||||||
|
|
||||||
// 8 bytes of "Old Fast Compare". Whatever that means.
|
|
||||||
nif->skip(8);
|
|
||||||
palette.read(nif);
|
palette.read(nif);
|
||||||
|
|
||||||
numberOfMipmaps = nif->getUInt();
|
numberOfMipmaps = nif->getUInt();
|
||||||
|
@ -213,8 +285,10 @@ void NiPixelData::read(NIFStream *nif)
|
||||||
|
|
||||||
// Read the data
|
// Read the data
|
||||||
unsigned int numPixels = nif->getUInt();
|
unsigned int numPixels = nif->getUInt();
|
||||||
if (numPixels)
|
bool hasFaces = nif->getVersion() >= NIFStream::generateVersion(10,4,0,2);
|
||||||
nif->getUChars(data, numPixels);
|
unsigned int numFaces = hasFaces ? nif->getUInt() : 1;
|
||||||
|
if (numPixels && numFaces)
|
||||||
|
nif->getUChars(data, numPixels * numFaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiPixelData::post(NIFFile *nif)
|
void NiPixelData::post(NIFFile *nif)
|
||||||
|
@ -249,6 +323,10 @@ void NiSkinData::read(NIFStream *nif)
|
||||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
||||||
nif->skip(4); // NiSkinPartition link
|
nif->skip(4); // NiSkinPartition link
|
||||||
|
|
||||||
|
// Has vertex weights flag
|
||||||
|
if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean())
|
||||||
|
return;
|
||||||
|
|
||||||
bones.resize(boneNum);
|
bones.resize(boneNum);
|
||||||
for (BoneInfo &bi : bones)
|
for (BoneInfo &bi : bones)
|
||||||
{
|
{
|
||||||
|
@ -272,7 +350,7 @@ void NiMorphData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
int morphCount = nif->getInt();
|
int morphCount = nif->getInt();
|
||||||
int vertCount = nif->getInt();
|
int vertCount = nif->getInt();
|
||||||
/*relative targets?*/nif->getChar();
|
nif->getChar(); // Relative targets, always 1
|
||||||
|
|
||||||
mMorphs.resize(morphCount);
|
mMorphs.resize(morphCount);
|
||||||
for(int i = 0;i < morphCount;i++)
|
for(int i = 0;i < morphCount;i++)
|
||||||
|
@ -290,7 +368,8 @@ void NiKeyframeData::read(NIFStream *nif)
|
||||||
if(mRotations->mInterpolationType == InterpolationType_XYZ)
|
if(mRotations->mInterpolationType == InterpolationType_XYZ)
|
||||||
{
|
{
|
||||||
//Chomp unused float
|
//Chomp unused float
|
||||||
nif->getFloat();
|
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
nif->getFloat();
|
||||||
mXRotations = std::make_shared<FloatKeyMap>();
|
mXRotations = std::make_shared<FloatKeyMap>();
|
||||||
mYRotations = std::make_shared<FloatKeyMap>();
|
mYRotations = std::make_shared<FloatKeyMap>();
|
||||||
mZRotations = std::make_shared<FloatKeyMap>();
|
mZRotations = std::make_shared<FloatKeyMap>();
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace Nif
|
||||||
class NiGeometryData : public Record
|
class NiGeometryData : public Record
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::vector<osg::Vec3f> vertices, normals;
|
std::vector<osg::Vec3f> vertices, normals, tangents, bitangents;
|
||||||
std::vector<osg::Vec4f> colors;
|
std::vector<osg::Vec4f> colors;
|
||||||
std::vector< std::vector<osg::Vec2f> > uvlist;
|
std::vector< std::vector<osg::Vec2f> > uvlist;
|
||||||
osg::Vec3f center;
|
osg::Vec3f center;
|
||||||
|
@ -73,13 +73,13 @@ struct NiLinesData : public NiGeometryData
|
||||||
class NiAutoNormalParticlesData : public NiGeometryData
|
class NiAutoNormalParticlesData : public NiGeometryData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int numParticles;
|
int numParticles{0};
|
||||||
|
|
||||||
float particleRadius;
|
|
||||||
|
|
||||||
int activeCount;
|
int activeCount;
|
||||||
|
|
||||||
std::vector<float> sizes;
|
std::vector<float> particleRadii, sizes, rotationAngles;
|
||||||
|
std::vector<osg::Quat> rotations;
|
||||||
|
std::vector<osg::Vec3f> rotationAxes;
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
};
|
};
|
||||||
|
@ -87,8 +87,6 @@ public:
|
||||||
class NiRotatingParticlesData : public NiAutoNormalParticlesData
|
class NiRotatingParticlesData : public NiAutoNormalParticlesData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::vector<osg::Quat> rotations;
|
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -133,7 +131,8 @@ public:
|
||||||
Format fmt;
|
Format fmt;
|
||||||
|
|
||||||
unsigned int colorMask[4];
|
unsigned int colorMask[4];
|
||||||
unsigned int bpp;
|
unsigned int bpp, pixelTiling{0};
|
||||||
|
bool sRGB{false};
|
||||||
|
|
||||||
NiPalettePtr palette;
|
NiPalettePtr palette;
|
||||||
unsigned int numberOfMipmaps;
|
unsigned int numberOfMipmaps;
|
||||||
|
|
|
@ -28,6 +28,10 @@ void NiTextureEffect::read(NIFStream *nif)
|
||||||
// Texture Filtering
|
// Texture Filtering
|
||||||
nif->skip(4);
|
nif->skip(4);
|
||||||
|
|
||||||
|
// Max anisotropy samples
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(20,5,0,4))
|
||||||
|
nif->skip(2);
|
||||||
|
|
||||||
clamp = nif->getUInt();
|
clamp = nif->getUInt();
|
||||||
|
|
||||||
textureType = (TextureType)nif->getUInt();
|
textureType = (TextureType)nif->getUInt();
|
||||||
|
@ -36,14 +40,12 @@ void NiTextureEffect::read(NIFStream *nif)
|
||||||
|
|
||||||
texture.read(nif);
|
texture.read(nif);
|
||||||
|
|
||||||
/*
|
nif->skip(1); // Use clipping plane
|
||||||
byte = 0
|
nif->skip(16); // Clipping plane dimensions vector
|
||||||
vector4 = [1,0,0,0]
|
if (nif->getVersion() <= NIFStream::generateVersion(10,2,0,0))
|
||||||
short = 0
|
nif->skip(4); // PS2-specific shorts
|
||||||
short = -75
|
if (nif->getVersion() <= NIFStream::generateVersion(4,1,0,12))
|
||||||
short = 0
|
nif->skip(2); // Unknown short
|
||||||
*/
|
|
||||||
nif->skip(23);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiTextureEffect::post(NIFFile *nif)
|
void NiTextureEffect::post(NIFFile *nif)
|
||||||
|
|
|
@ -34,6 +34,9 @@ struct NiDynamicEffect : public Node
|
||||||
void read(NIFStream *nif)
|
void read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Node::read(nif);
|
Node::read(nif);
|
||||||
|
if (nif->getVersion() >= nif->generateVersion(10,1,0,106)
|
||||||
|
&& nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
|
||||||
|
nif->getBoolean(); // Switch state
|
||||||
unsigned int numAffectedNodes = nif->getUInt();
|
unsigned int numAffectedNodes = nif->getUInt();
|
||||||
for (unsigned int i=0; i<numAffectedNodes; ++i)
|
for (unsigned int i=0; i<numAffectedNodes; ++i)
|
||||||
nif->getUInt(); // ref to another Node
|
nif->getUInt(); // ref to another Node
|
||||||
|
|
|
@ -29,6 +29,56 @@ void NiVertWeightsExtraData::read(NIFStream *nif)
|
||||||
nif->skip(nif->getUShort() * sizeof(float)); // vertex weights I guess
|
nif->skip(nif->getUShort() * sizeof(float)); // vertex weights I guess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NiIntegerExtraData::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Extra::read(nif);
|
||||||
|
|
||||||
|
data = nif->getUInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiIntegersExtraData::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Extra::read(nif);
|
||||||
|
|
||||||
|
unsigned int num = nif->getUInt();
|
||||||
|
if (num)
|
||||||
|
nif->getUInts(data, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiBinaryExtraData::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Extra::read(nif);
|
||||||
|
unsigned int size = nif->getUInt();
|
||||||
|
if (size)
|
||||||
|
nif->getChars(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiBooleanExtraData::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Extra::read(nif);
|
||||||
|
data = nif->getBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiVectorExtraData::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Extra::read(nif);
|
||||||
|
data = nif->getVector4();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiFloatExtraData::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Extra::read(nif);
|
||||||
|
|
||||||
|
data = nif->getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiFloatsExtraData::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Extra::read(nif);
|
||||||
|
unsigned int num = nif->getUInt();
|
||||||
|
if (num)
|
||||||
|
nif->getFloats(data, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,5 +60,54 @@ public:
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct NiIntegerExtraData : public Extra
|
||||||
|
{
|
||||||
|
unsigned int data;
|
||||||
|
|
||||||
|
void read(NIFStream *nif);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiIntegersExtraData : public Extra
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> data;
|
||||||
|
|
||||||
|
void read(NIFStream *nif);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiBinaryExtraData : public Extra
|
||||||
|
{
|
||||||
|
std::vector<char> data;
|
||||||
|
|
||||||
|
void read(NIFStream *nif);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiBooleanExtraData : public Extra
|
||||||
|
{
|
||||||
|
bool data;
|
||||||
|
|
||||||
|
void read(NIFStream *nif);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiVectorExtraData : public Extra
|
||||||
|
{
|
||||||
|
osg::Vec4f data;
|
||||||
|
|
||||||
|
void read(NIFStream *nif);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiFloatExtraData : public Extra
|
||||||
|
{
|
||||||
|
float data;
|
||||||
|
|
||||||
|
void read(NIFStream *nif);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiFloatsExtraData : public Extra
|
||||||
|
{
|
||||||
|
std::vector<float> data;
|
||||||
|
|
||||||
|
void read(NIFStream *nif);
|
||||||
|
};
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,108 +16,106 @@ NIFFile::NIFFile(Files::IStreamPtr stream, const std::string &name)
|
||||||
|
|
||||||
NIFFile::~NIFFile()
|
NIFFile::~NIFFile()
|
||||||
{
|
{
|
||||||
for (std::vector<Record*>::iterator it = records.begin() ; it != records.end(); ++it)
|
for (Record* record : records)
|
||||||
{
|
delete record;
|
||||||
delete *it;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename NodeType> static Record* construct() { return new NodeType; }
|
template <typename NodeType> static Record* construct() { return new NodeType; }
|
||||||
|
|
||||||
struct RecordFactoryEntry {
|
struct RecordFactoryEntry {
|
||||||
|
|
||||||
typedef Record* (*create_t) ();
|
using create_t = Record* (*)();
|
||||||
|
|
||||||
create_t mCreate;
|
create_t mCreate;
|
||||||
RecordType mType;
|
RecordType mType;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
///Helper function for adding records to the factory map
|
|
||||||
static std::pair<std::string,RecordFactoryEntry> makeEntry(std::string recName, Record* (*create_t) (), RecordType type)
|
|
||||||
{
|
|
||||||
RecordFactoryEntry anEntry = {create_t,type};
|
|
||||||
return std::make_pair(recName, anEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
///These are all the record types we know how to read.
|
///These are all the record types we know how to read.
|
||||||
static std::map<std::string,RecordFactoryEntry> makeFactory()
|
static std::map<std::string,RecordFactoryEntry> makeFactory()
|
||||||
{
|
{
|
||||||
std::map<std::string,RecordFactoryEntry> newFactory;
|
std::map<std::string,RecordFactoryEntry> factory;
|
||||||
newFactory.insert(makeEntry("NiNode", &construct <NiNode> , RC_NiNode ));
|
factory["NiNode"] = {&construct <NiNode> , RC_NiNode };
|
||||||
newFactory.insert(makeEntry("NiSwitchNode", &construct <NiSwitchNode> , RC_NiSwitchNode ));
|
factory["NiSwitchNode"] = {&construct <NiSwitchNode> , RC_NiSwitchNode };
|
||||||
newFactory.insert(makeEntry("NiLODNode", &construct <NiLODNode> , RC_NiLODNode ));
|
factory["NiLODNode"] = {&construct <NiLODNode> , RC_NiLODNode };
|
||||||
newFactory.insert(makeEntry("AvoidNode", &construct <NiNode> , RC_AvoidNode ));
|
factory["AvoidNode"] = {&construct <NiNode> , RC_AvoidNode };
|
||||||
newFactory.insert(makeEntry("NiCollisionSwitch", &construct <NiNode> , RC_NiCollisionSwitch ));
|
factory["NiCollisionSwitch"] = {&construct <NiNode> , RC_NiCollisionSwitch };
|
||||||
newFactory.insert(makeEntry("NiBSParticleNode", &construct <NiNode> , RC_NiBSParticleNode ));
|
factory["NiBSParticleNode"] = {&construct <NiNode> , RC_NiBSParticleNode };
|
||||||
newFactory.insert(makeEntry("NiBSAnimationNode", &construct <NiNode> , RC_NiBSAnimationNode ));
|
factory["NiBSAnimationNode"] = {&construct <NiNode> , RC_NiBSAnimationNode };
|
||||||
newFactory.insert(makeEntry("NiBillboardNode", &construct <NiNode> , RC_NiBillboardNode ));
|
factory["NiBillboardNode"] = {&construct <NiNode> , RC_NiBillboardNode };
|
||||||
newFactory.insert(makeEntry("NiTriShape", &construct <NiTriShape> , RC_NiTriShape ));
|
factory["NiTriShape"] = {&construct <NiTriShape> , RC_NiTriShape };
|
||||||
newFactory.insert(makeEntry("NiTriStrips", &construct <NiTriStrips> , RC_NiTriStrips ));
|
factory["NiTriStrips"] = {&construct <NiTriStrips> , RC_NiTriStrips };
|
||||||
newFactory.insert(makeEntry("NiLines", &construct <NiLines> , RC_NiLines ));
|
factory["NiLines"] = {&construct <NiLines> , RC_NiLines };
|
||||||
newFactory.insert(makeEntry("NiRotatingParticles", &construct <NiRotatingParticles> , RC_NiRotatingParticles ));
|
factory["NiRotatingParticles"] = {&construct <NiRotatingParticles> , RC_NiRotatingParticles };
|
||||||
newFactory.insert(makeEntry("NiAutoNormalParticles", &construct <NiAutoNormalParticles> , RC_NiAutoNormalParticles ));
|
factory["NiAutoNormalParticles"] = {&construct <NiAutoNormalParticles> , RC_NiAutoNormalParticles };
|
||||||
newFactory.insert(makeEntry("NiCamera", &construct <NiCamera> , RC_NiCamera ));
|
factory["NiCamera"] = {&construct <NiCamera> , RC_NiCamera };
|
||||||
newFactory.insert(makeEntry("RootCollisionNode", &construct <NiNode> , RC_RootCollisionNode ));
|
factory["RootCollisionNode"] = {&construct <NiNode> , RC_RootCollisionNode };
|
||||||
newFactory.insert(makeEntry("NiTexturingProperty", &construct <NiTexturingProperty> , RC_NiTexturingProperty ));
|
factory["NiTexturingProperty"] = {&construct <NiTexturingProperty> , RC_NiTexturingProperty };
|
||||||
newFactory.insert(makeEntry("NiFogProperty", &construct <NiFogProperty> , RC_NiFogProperty ));
|
factory["NiFogProperty"] = {&construct <NiFogProperty> , RC_NiFogProperty };
|
||||||
newFactory.insert(makeEntry("NiMaterialProperty", &construct <NiMaterialProperty> , RC_NiMaterialProperty ));
|
factory["NiMaterialProperty"] = {&construct <NiMaterialProperty> , RC_NiMaterialProperty };
|
||||||
newFactory.insert(makeEntry("NiZBufferProperty", &construct <NiZBufferProperty> , RC_NiZBufferProperty ));
|
factory["NiZBufferProperty"] = {&construct <NiZBufferProperty> , RC_NiZBufferProperty };
|
||||||
newFactory.insert(makeEntry("NiAlphaProperty", &construct <NiAlphaProperty> , RC_NiAlphaProperty ));
|
factory["NiAlphaProperty"] = {&construct <NiAlphaProperty> , RC_NiAlphaProperty };
|
||||||
newFactory.insert(makeEntry("NiVertexColorProperty", &construct <NiVertexColorProperty> , RC_NiVertexColorProperty ));
|
factory["NiVertexColorProperty"] = {&construct <NiVertexColorProperty> , RC_NiVertexColorProperty };
|
||||||
newFactory.insert(makeEntry("NiShadeProperty", &construct <NiShadeProperty> , RC_NiShadeProperty ));
|
factory["NiShadeProperty"] = {&construct <NiShadeProperty> , RC_NiShadeProperty };
|
||||||
newFactory.insert(makeEntry("NiDitherProperty", &construct <NiDitherProperty> , RC_NiDitherProperty ));
|
factory["NiDitherProperty"] = {&construct <NiDitherProperty> , RC_NiDitherProperty };
|
||||||
newFactory.insert(makeEntry("NiWireframeProperty", &construct <NiWireframeProperty> , RC_NiWireframeProperty ));
|
factory["NiWireframeProperty"] = {&construct <NiWireframeProperty> , RC_NiWireframeProperty };
|
||||||
newFactory.insert(makeEntry("NiSpecularProperty", &construct <NiSpecularProperty> , RC_NiSpecularProperty ));
|
factory["NiSpecularProperty"] = {&construct <NiSpecularProperty> , RC_NiSpecularProperty };
|
||||||
newFactory.insert(makeEntry("NiStencilProperty", &construct <NiStencilProperty> , RC_NiStencilProperty ));
|
factory["NiStencilProperty"] = {&construct <NiStencilProperty> , RC_NiStencilProperty };
|
||||||
newFactory.insert(makeEntry("NiVisController", &construct <NiVisController> , RC_NiVisController ));
|
factory["NiVisController"] = {&construct <NiVisController> , RC_NiVisController };
|
||||||
newFactory.insert(makeEntry("NiGeomMorpherController", &construct <NiGeomMorpherController> , RC_NiGeomMorpherController ));
|
factory["NiGeomMorpherController"] = {&construct <NiGeomMorpherController> , RC_NiGeomMorpherController };
|
||||||
newFactory.insert(makeEntry("NiKeyframeController", &construct <NiKeyframeController> , RC_NiKeyframeController ));
|
factory["NiKeyframeController"] = {&construct <NiKeyframeController> , RC_NiKeyframeController };
|
||||||
newFactory.insert(makeEntry("NiAlphaController", &construct <NiAlphaController> , RC_NiAlphaController ));
|
factory["NiAlphaController"] = {&construct <NiAlphaController> , RC_NiAlphaController };
|
||||||
newFactory.insert(makeEntry("NiRollController", &construct <NiRollController> , RC_NiRollController ));
|
factory["NiRollController"] = {&construct <NiRollController> , RC_NiRollController };
|
||||||
newFactory.insert(makeEntry("NiUVController", &construct <NiUVController> , RC_NiUVController ));
|
factory["NiUVController"] = {&construct <NiUVController> , RC_NiUVController };
|
||||||
newFactory.insert(makeEntry("NiPathController", &construct <NiPathController> , RC_NiPathController ));
|
factory["NiPathController"] = {&construct <NiPathController> , RC_NiPathController };
|
||||||
newFactory.insert(makeEntry("NiMaterialColorController", &construct <NiMaterialColorController> , RC_NiMaterialColorController ));
|
factory["NiMaterialColorController"] = {&construct <NiMaterialColorController> , RC_NiMaterialColorController };
|
||||||
newFactory.insert(makeEntry("NiBSPArrayController", &construct <NiBSPArrayController> , RC_NiBSPArrayController ));
|
factory["NiBSPArrayController"] = {&construct <NiBSPArrayController> , RC_NiBSPArrayController };
|
||||||
newFactory.insert(makeEntry("NiParticleSystemController", &construct <NiParticleSystemController> , RC_NiParticleSystemController ));
|
factory["NiParticleSystemController"] = {&construct <NiParticleSystemController> , RC_NiParticleSystemController };
|
||||||
newFactory.insert(makeEntry("NiFlipController", &construct <NiFlipController> , RC_NiFlipController ));
|
factory["NiFlipController"] = {&construct <NiFlipController> , RC_NiFlipController };
|
||||||
newFactory.insert(makeEntry("NiAmbientLight", &construct <NiLight> , RC_NiLight ));
|
factory["NiAmbientLight"] = {&construct <NiLight> , RC_NiLight };
|
||||||
newFactory.insert(makeEntry("NiDirectionalLight", &construct <NiLight> , RC_NiLight ));
|
factory["NiDirectionalLight"] = {&construct <NiLight> , RC_NiLight };
|
||||||
newFactory.insert(makeEntry("NiPointLight", &construct <NiPointLight> , RC_NiLight ));
|
factory["NiPointLight"] = {&construct <NiPointLight> , RC_NiLight };
|
||||||
newFactory.insert(makeEntry("NiSpotLight", &construct <NiSpotLight> , RC_NiLight ));
|
factory["NiSpotLight"] = {&construct <NiSpotLight> , RC_NiLight };
|
||||||
newFactory.insert(makeEntry("NiTextureEffect", &construct <NiTextureEffect> , RC_NiTextureEffect ));
|
factory["NiTextureEffect"] = {&construct <NiTextureEffect> , RC_NiTextureEffect };
|
||||||
newFactory.insert(makeEntry("NiVertWeightsExtraData", &construct <NiVertWeightsExtraData> , RC_NiVertWeightsExtraData ));
|
factory["NiVertWeightsExtraData"] = {&construct <NiVertWeightsExtraData> , RC_NiVertWeightsExtraData };
|
||||||
newFactory.insert(makeEntry("NiTextKeyExtraData", &construct <NiTextKeyExtraData> , RC_NiTextKeyExtraData ));
|
factory["NiTextKeyExtraData"] = {&construct <NiTextKeyExtraData> , RC_NiTextKeyExtraData };
|
||||||
newFactory.insert(makeEntry("NiStringExtraData", &construct <NiStringExtraData> , RC_NiStringExtraData ));
|
factory["NiStringExtraData"] = {&construct <NiStringExtraData> , RC_NiStringExtraData };
|
||||||
newFactory.insert(makeEntry("NiGravity", &construct <NiGravity> , RC_NiGravity ));
|
factory["NiGravity"] = {&construct <NiGravity> , RC_NiGravity };
|
||||||
newFactory.insert(makeEntry("NiPlanarCollider", &construct <NiPlanarCollider> , RC_NiPlanarCollider ));
|
factory["NiPlanarCollider"] = {&construct <NiPlanarCollider> , RC_NiPlanarCollider };
|
||||||
newFactory.insert(makeEntry("NiSphericalCollider", &construct <NiSphericalCollider> , RC_NiSphericalCollider ));
|
factory["NiSphericalCollider"] = {&construct <NiSphericalCollider> , RC_NiSphericalCollider };
|
||||||
newFactory.insert(makeEntry("NiParticleGrowFade", &construct <NiParticleGrowFade> , RC_NiParticleGrowFade ));
|
factory["NiParticleGrowFade"] = {&construct <NiParticleGrowFade> , RC_NiParticleGrowFade };
|
||||||
newFactory.insert(makeEntry("NiParticleColorModifier", &construct <NiParticleColorModifier> , RC_NiParticleColorModifier ));
|
factory["NiParticleColorModifier"] = {&construct <NiParticleColorModifier> , RC_NiParticleColorModifier };
|
||||||
newFactory.insert(makeEntry("NiParticleRotation", &construct <NiParticleRotation> , RC_NiParticleRotation ));
|
factory["NiParticleRotation"] = {&construct <NiParticleRotation> , RC_NiParticleRotation };
|
||||||
newFactory.insert(makeEntry("NiFloatData", &construct <NiFloatData> , RC_NiFloatData ));
|
factory["NiFloatData"] = {&construct <NiFloatData> , RC_NiFloatData };
|
||||||
newFactory.insert(makeEntry("NiTriShapeData", &construct <NiTriShapeData> , RC_NiTriShapeData ));
|
factory["NiTriShapeData"] = {&construct <NiTriShapeData> , RC_NiTriShapeData };
|
||||||
newFactory.insert(makeEntry("NiTriStripsData", &construct <NiTriStripsData> , RC_NiTriStripsData ));
|
factory["NiTriStripsData"] = {&construct <NiTriStripsData> , RC_NiTriStripsData };
|
||||||
newFactory.insert(makeEntry("NiLinesData", &construct <NiLinesData> , RC_NiLinesData ));
|
factory["NiLinesData"] = {&construct <NiLinesData> , RC_NiLinesData };
|
||||||
newFactory.insert(makeEntry("NiVisData", &construct <NiVisData> , RC_NiVisData ));
|
factory["NiVisData"] = {&construct <NiVisData> , RC_NiVisData };
|
||||||
newFactory.insert(makeEntry("NiColorData", &construct <NiColorData> , RC_NiColorData ));
|
factory["NiColorData"] = {&construct <NiColorData> , RC_NiColorData };
|
||||||
newFactory.insert(makeEntry("NiPixelData", &construct <NiPixelData> , RC_NiPixelData ));
|
factory["NiPixelData"] = {&construct <NiPixelData> , RC_NiPixelData };
|
||||||
newFactory.insert(makeEntry("NiMorphData", &construct <NiMorphData> , RC_NiMorphData ));
|
factory["NiMorphData"] = {&construct <NiMorphData> , RC_NiMorphData };
|
||||||
newFactory.insert(makeEntry("NiKeyframeData", &construct <NiKeyframeData> , RC_NiKeyframeData ));
|
factory["NiKeyframeData"] = {&construct <NiKeyframeData> , RC_NiKeyframeData };
|
||||||
newFactory.insert(makeEntry("NiSkinData", &construct <NiSkinData> , RC_NiSkinData ));
|
factory["NiSkinData"] = {&construct <NiSkinData> , RC_NiSkinData };
|
||||||
newFactory.insert(makeEntry("NiUVData", &construct <NiUVData> , RC_NiUVData ));
|
factory["NiUVData"] = {&construct <NiUVData> , RC_NiUVData };
|
||||||
newFactory.insert(makeEntry("NiPosData", &construct <NiPosData> , RC_NiPosData ));
|
factory["NiPosData"] = {&construct <NiPosData> , RC_NiPosData };
|
||||||
newFactory.insert(makeEntry("NiRotatingParticlesData", &construct <NiRotatingParticlesData> , RC_NiRotatingParticlesData ));
|
factory["NiRotatingParticlesData"] = {&construct <NiRotatingParticlesData> , RC_NiRotatingParticlesData };
|
||||||
newFactory.insert(makeEntry("NiAutoNormalParticlesData", &construct <NiAutoNormalParticlesData> , RC_NiAutoNormalParticlesData ));
|
factory["NiAutoNormalParticlesData"] = {&construct <NiAutoNormalParticlesData> , RC_NiAutoNormalParticlesData };
|
||||||
newFactory.insert(makeEntry("NiSequenceStreamHelper", &construct <NiSequenceStreamHelper> , RC_NiSequenceStreamHelper ));
|
factory["NiSequenceStreamHelper"] = {&construct <NiSequenceStreamHelper> , RC_NiSequenceStreamHelper };
|
||||||
newFactory.insert(makeEntry("NiSourceTexture", &construct <NiSourceTexture> , RC_NiSourceTexture ));
|
factory["NiSourceTexture"] = {&construct <NiSourceTexture> , RC_NiSourceTexture };
|
||||||
newFactory.insert(makeEntry("NiSkinInstance", &construct <NiSkinInstance> , RC_NiSkinInstance ));
|
factory["NiSkinInstance"] = {&construct <NiSkinInstance> , RC_NiSkinInstance };
|
||||||
newFactory.insert(makeEntry("NiLookAtController", &construct <NiLookAtController> , RC_NiLookAtController ));
|
factory["NiLookAtController"] = {&construct <NiLookAtController> , RC_NiLookAtController };
|
||||||
newFactory.insert(makeEntry("NiPalette", &construct <NiPalette> , RC_NiPalette ));
|
factory["NiPalette"] = {&construct <NiPalette> , RC_NiPalette };
|
||||||
return newFactory;
|
factory["NiIntegerExtraData"] = {&construct <NiIntegerExtraData> , RC_NiIntegerExtraData };
|
||||||
|
factory["NiIntegersExtraData"] = {&construct <NiIntegersExtraData> , RC_NiIntegersExtraData };
|
||||||
|
factory["NiBinaryExtraData"] = {&construct <NiBinaryExtraData> , RC_NiBinaryExtraData };
|
||||||
|
factory["NiBooleanExtraData"] = {&construct <NiBooleanExtraData> , RC_NiBooleanExtraData };
|
||||||
|
factory["NiVectorExtraData"] = {&construct <NiVectorExtraData> , RC_NiVectorExtraData };
|
||||||
|
factory["NiColorExtraData"] = {&construct <NiVectorExtraData> , RC_NiColorExtraData };
|
||||||
|
factory["NiFloatExtraData"] = {&construct <NiFloatExtraData> , RC_NiFloatExtraData };
|
||||||
|
factory["NiFloatsExtraData"] = {&construct <NiFloatsExtraData> , RC_NiFloatsExtraData };
|
||||||
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///Make the factory map used for parsing the file
|
///Make the factory map used for parsing the file
|
||||||
static const std::map<std::string,RecordFactoryEntry> factories = makeFactory();
|
static const std::map<std::string,RecordFactoryEntry> factories = makeFactory();
|
||||||
|
|
||||||
|
@ -148,15 +146,77 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
||||||
// It's not used by Morrowind assets but Morrowind supports it.
|
// It's not used by Morrowind assets but Morrowind supports it.
|
||||||
if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW)
|
if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW)
|
||||||
fail("Unsupported NIF version: " + printVersion(ver));
|
fail("Unsupported NIF version: " + printVersion(ver));
|
||||||
|
|
||||||
|
// NIF data endianness
|
||||||
|
if (ver >= NIFStream::generateVersion(20,0,0,4))
|
||||||
|
{
|
||||||
|
unsigned char endianness = nif.getChar();
|
||||||
|
if (endianness == 0)
|
||||||
|
fail("Big endian NIF files are unsupported");
|
||||||
|
}
|
||||||
|
|
||||||
|
// User version
|
||||||
|
if (ver > NIFStream::generateVersion(10,0,1,8))
|
||||||
|
userVer = nif.getUInt();
|
||||||
|
|
||||||
// Number of records
|
// Number of records
|
||||||
size_t recNum = nif.getInt();
|
size_t recNum = nif.getUInt();
|
||||||
records.resize(recNum);
|
records.resize(recNum);
|
||||||
|
|
||||||
|
// Bethesda stream header
|
||||||
|
// It contains Bethesda format version and (useless) export information
|
||||||
|
if (ver == VER_OB_OLD ||
|
||||||
|
(userVer >= 3 && ((ver == VER_OB || ver == VER_BGS)
|
||||||
|
|| (ver >= NIFStream::generateVersion(10,1,0,0) && ver <= NIFStream::generateVersion(20,0,0,4) && userVer <= 11))))
|
||||||
|
{
|
||||||
|
bethVer = nif.getUInt();
|
||||||
|
nif.getExportString(); // Author
|
||||||
|
if (bethVer > BETHVER_FO4)
|
||||||
|
nif.getUInt(); // Unknown
|
||||||
|
nif.getExportString(); // Process script
|
||||||
|
nif.getExportString(); // Export script
|
||||||
|
if (bethVer == BETHVER_FO4)
|
||||||
|
nif.getExportString(); // Max file path
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> recTypes;
|
||||||
|
std::vector<unsigned short> recTypeIndices;
|
||||||
|
|
||||||
|
const bool hasRecTypeListings = ver >= NIFStream::generateVersion(5,0,0,1);
|
||||||
|
if (hasRecTypeListings)
|
||||||
|
{
|
||||||
|
unsigned short recTypeNum = nif.getUShort();
|
||||||
|
if (recTypeNum) // Record type list
|
||||||
|
nif.getSizedStrings(recTypes, recTypeNum);
|
||||||
|
if (recNum) // Record type mapping for each record
|
||||||
|
nif.getUShorts(recTypeIndices, recNum);
|
||||||
|
if (ver >= NIFStream::generateVersion(5,0,0,6)) // Groups
|
||||||
|
{
|
||||||
|
if (ver >= NIFStream::generateVersion(20,1,0,1)) // String table
|
||||||
|
{
|
||||||
|
if (ver >= NIFStream::generateVersion(20,2,0,5) && recNum) // Record sizes
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> recSizes; // Currently unused
|
||||||
|
nif.getUInts(recSizes, recNum);
|
||||||
|
}
|
||||||
|
unsigned int stringNum = nif.getUInt();
|
||||||
|
nif.getUInt(); // Max string length
|
||||||
|
if (stringNum)
|
||||||
|
nif.getSizedStrings(strings, stringNum);
|
||||||
|
}
|
||||||
|
std::vector<unsigned int> groups; // Currently unused
|
||||||
|
unsigned int groupNum = nif.getUInt();
|
||||||
|
if (groupNum)
|
||||||
|
nif.getUInts(groups, groupNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool hasRecordSeparators = ver >= NIFStream::generateVersion(10,0,0,0) && ver < NIFStream::generateVersion(10,2,0,0);
|
||||||
for(size_t i = 0;i < recNum;i++)
|
for(size_t i = 0;i < recNum;i++)
|
||||||
{
|
{
|
||||||
Record *r = nullptr;
|
Record *r = nullptr;
|
||||||
|
|
||||||
std::string rec = nif.getString();
|
std::string rec = hasRecTypeListings ? recTypes[recTypeIndices[i]] : nif.getString();
|
||||||
if(rec.empty())
|
if(rec.empty())
|
||||||
{
|
{
|
||||||
std::stringstream error;
|
std::stringstream error;
|
||||||
|
@ -164,6 +224,17 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
||||||
fail(error.str());
|
fail(error.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record separator. Some Havok records in Oblivion do not have it.
|
||||||
|
if (hasRecordSeparators && rec.compare(0, 3, "bhk"))
|
||||||
|
{
|
||||||
|
if (nif.getInt())
|
||||||
|
{
|
||||||
|
std::stringstream warning;
|
||||||
|
warning << "Record number " << i << " out of " << recNum << " is preceded by a non-zero separator.";
|
||||||
|
warn(warning.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string,RecordFactoryEntry>::const_iterator entry = factories.find(rec);
|
std::map<std::string,RecordFactoryEntry>::const_iterator entry = factories.find(rec);
|
||||||
|
|
||||||
if (entry != factories.end())
|
if (entry != factories.end())
|
||||||
|
@ -201,8 +272,8 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once parsing is done, do post-processing.
|
// Once parsing is done, do post-processing.
|
||||||
for(size_t i=0; i<recNum; i++)
|
for (Record* record : records)
|
||||||
records[i]->post(this);
|
record->post(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NIFFile::setUseSkinning(bool skinning)
|
void NIFFile::setUseSkinning(bool skinning)
|
||||||
|
|
|
@ -25,18 +25,19 @@ namespace Nif
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
///Currently specific for 4.0.0.2 and earlier
|
///Booleans in 4.0.0.2 (Morrowind format) and earlier are 4 byte, while in 4.1.0.0+ they're 1 byte.
|
||||||
bool NIFStream::getBoolean()
|
bool NIFStream::getBoolean()
|
||||||
{
|
{
|
||||||
return getInt() != 0;
|
return getVersion() < generateVersion(4,1,0,0) ? getInt() != 0 : getChar() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
///Read in a string, either from the string table using the index (currently absent) or from the stream using the specified length
|
///Read in a string, either from the string table using the index or from the stream using the specified length
|
||||||
std::string NIFStream::getString()
|
std::string NIFStream::getString()
|
||||||
{
|
{
|
||||||
return getSizedString();
|
return getVersion() < generateVersion(20,1,0,1) ? getSizedString() : file->getString(getUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Convenience utility functions: get the versions of the currently read file
|
// Convenience utility functions: get the versions of the currently read file
|
||||||
unsigned int NIFStream::getVersion() const { return file->getVersion(); }
|
unsigned int NIFStream::getVersion() const { return file->getVersion(); }
|
||||||
unsigned int NIFStream::getUserVersion() const { return file->getBethVersion(); }
|
unsigned int NIFStream::getUserVersion() const { return file->getBethVersion(); }
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
PropertyList props;
|
PropertyList props;
|
||||||
|
|
||||||
// Bounding box info
|
// Bounding box info
|
||||||
bool hasBounds;
|
bool hasBounds{false};
|
||||||
osg::Vec3f boundPos;
|
osg::Vec3f boundPos;
|
||||||
Matrix3 boundRot;
|
Matrix3 boundRot;
|
||||||
osg::Vec3f boundXYZ; // Box size
|
osg::Vec3f boundXYZ; // Box size
|
||||||
|
@ -39,12 +39,15 @@ public:
|
||||||
{
|
{
|
||||||
Named::read(nif);
|
Named::read(nif);
|
||||||
|
|
||||||
flags = nif->getUShort();
|
flags = nif->getBethVersion() <= 26 ? nif->getUShort() : nif->getUInt();
|
||||||
trafo = nif->getTrafo();
|
trafo = nif->getTrafo();
|
||||||
velocity = nif->getVector3();
|
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||||
props.read(nif);
|
velocity = nif->getVector3();
|
||||||
|
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
|
||||||
|
props.read(nif);
|
||||||
|
|
||||||
hasBounds = nif->getBoolean();
|
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||||
|
hasBounds = nif->getBoolean();
|
||||||
if(hasBounds)
|
if(hasBounds)
|
||||||
{
|
{
|
||||||
nif->getInt(); // always 1
|
nif->getInt(); // always 1
|
||||||
|
@ -52,6 +55,9 @@ public:
|
||||||
boundRot = nif->getMatrix3();
|
boundRot = nif->getMatrix3();
|
||||||
boundXYZ = nif->getVector3();
|
boundXYZ = nif->getVector3();
|
||||||
}
|
}
|
||||||
|
// Reference to the collision object in Gamebryo files.
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||||
|
nif->skip(4);
|
||||||
|
|
||||||
parent = nullptr;
|
parent = nullptr;
|
||||||
|
|
||||||
|
@ -102,7 +108,8 @@ struct NiNode : Node
|
||||||
{
|
{
|
||||||
Node::read(nif);
|
Node::read(nif);
|
||||||
children.read(nif);
|
children.read(nif);
|
||||||
effects.read(nif);
|
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
|
||||||
|
effects.read(nif);
|
||||||
|
|
||||||
// Discard transformations for the root node, otherwise some meshes
|
// Discard transformations for the root node, otherwise some meshes
|
||||||
// occasionally get wrong orientation. Only for NiNode-s for now, but
|
// occasionally get wrong orientation. Only for NiNode-s for now, but
|
||||||
|
@ -130,7 +137,39 @@ struct NiNode : Node
|
||||||
|
|
||||||
struct NiGeometry : Node
|
struct NiGeometry : Node
|
||||||
{
|
{
|
||||||
|
struct MaterialData
|
||||||
|
{
|
||||||
|
std::vector<std::string> materialNames;
|
||||||
|
std::vector<int> materialExtraData;
|
||||||
|
unsigned int activeMaterial{0};
|
||||||
|
bool materialNeedsUpdate{false};
|
||||||
|
void read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
||||||
|
return;
|
||||||
|
unsigned int numMaterials = 0;
|
||||||
|
if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,3))
|
||||||
|
numMaterials = nif->getBoolean(); // Has Shader
|
||||||
|
else if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))
|
||||||
|
numMaterials = nif->getUInt();
|
||||||
|
if (numMaterials)
|
||||||
|
{
|
||||||
|
nif->getStrings(materialNames, numMaterials);
|
||||||
|
nif->getInts(materialExtraData, numMaterials);
|
||||||
|
}
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))
|
||||||
|
activeMaterial = nif->getUInt();
|
||||||
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
|
||||||
|
{
|
||||||
|
materialNeedsUpdate = nif->getBoolean();
|
||||||
|
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
|
||||||
|
nif->skip(8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
NiSkinInstancePtr skin;
|
NiSkinInstancePtr skin;
|
||||||
|
MaterialData materialData;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NiTriShape : NiGeometry
|
struct NiTriShape : NiGeometry
|
||||||
|
@ -149,6 +188,7 @@ struct NiTriShape : NiGeometry
|
||||||
Node::read(nif);
|
Node::read(nif);
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
skin.read(nif);
|
skin.read(nif);
|
||||||
|
materialData.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void post(NIFFile *nif)
|
void post(NIFFile *nif)
|
||||||
|
@ -170,6 +210,7 @@ struct NiTriStrips : NiGeometry
|
||||||
Node::read(nif);
|
Node::read(nif);
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
skin.read(nif);
|
skin.read(nif);
|
||||||
|
materialData.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void post(NIFFile *nif)
|
void post(NIFFile *nif)
|
||||||
|
@ -207,6 +248,8 @@ struct NiCamera : Node
|
||||||
{
|
{
|
||||||
struct Camera
|
struct Camera
|
||||||
{
|
{
|
||||||
|
unsigned short cameraFlags{0};
|
||||||
|
|
||||||
// Camera frustrum
|
// Camera frustrum
|
||||||
float left, right, top, bottom, nearDist, farDist;
|
float left, right, top, bottom, nearDist, farDist;
|
||||||
|
|
||||||
|
@ -216,15 +259,21 @@ struct NiCamera : Node
|
||||||
// Level of detail modifier
|
// Level of detail modifier
|
||||||
float LOD;
|
float LOD;
|
||||||
|
|
||||||
|
// Orthographic projection usage flag
|
||||||
|
bool orthographic{false};
|
||||||
|
|
||||||
void read(NIFStream *nif)
|
void read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
cameraFlags = nif->getUShort();
|
||||||
left = nif->getFloat();
|
left = nif->getFloat();
|
||||||
right = nif->getFloat();
|
right = nif->getFloat();
|
||||||
top = nif->getFloat();
|
top = nif->getFloat();
|
||||||
bottom = nif->getFloat();
|
bottom = nif->getFloat();
|
||||||
nearDist = nif->getFloat();
|
nearDist = nif->getFloat();
|
||||||
farDist = nif->getFloat();
|
farDist = nif->getFloat();
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
orthographic = nif->getBoolean();
|
||||||
vleft = nif->getFloat();
|
vleft = nif->getFloat();
|
||||||
vright = nif->getFloat();
|
vright = nif->getFloat();
|
||||||
vtop = nif->getFloat();
|
vtop = nif->getFloat();
|
||||||
|
@ -243,6 +292,8 @@ struct NiCamera : Node
|
||||||
|
|
||||||
nif->getInt(); // -1
|
nif->getInt(); // -1
|
||||||
nif->getInt(); // 0
|
nif->getInt(); // 0
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(4,2,1,0))
|
||||||
|
nif->getInt(); // 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -285,11 +336,14 @@ struct NiRotatingParticles : Node
|
||||||
// A node used as the base to switch between child nodes, such as for LOD levels.
|
// A node used as the base to switch between child nodes, such as for LOD levels.
|
||||||
struct NiSwitchNode : public NiNode
|
struct NiSwitchNode : public NiNode
|
||||||
{
|
{
|
||||||
|
unsigned int switchFlags{0};
|
||||||
unsigned int initialIndex;
|
unsigned int initialIndex;
|
||||||
|
|
||||||
void read(NIFStream *nif)
|
void read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
NiNode::read(nif);
|
NiNode::read(nif);
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
switchFlags = nif->getUShort();
|
||||||
initialIndex = nif->getUInt();
|
initialIndex = nif->getUInt();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -310,6 +364,12 @@ struct NiLODNode : public NiSwitchNode
|
||||||
NiSwitchNode::read(nif);
|
NiSwitchNode::read(nif);
|
||||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
||||||
lodCenter = nif->getVector3();
|
lodCenter = nif->getVector3();
|
||||||
|
else if (nif->getVersion() > NIFStream::generateVersion(10,0,1,0))
|
||||||
|
{
|
||||||
|
nif->skip(4); // NiLODData, unsupported at the moment
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int numLodLevels = nif->getUInt();
|
unsigned int numLodLevels = nif->getUInt();
|
||||||
for (unsigned int i=0; i<numLodLevels; ++i)
|
for (unsigned int i=0; i<numLodLevels; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,25 +6,44 @@
|
||||||
namespace Nif
|
namespace Nif
|
||||||
{
|
{
|
||||||
|
|
||||||
void Property::read(NIFStream *nif)
|
|
||||||
{
|
|
||||||
Named::read(nif);
|
|
||||||
flags = nif->getUShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NiTexturingProperty::Texture::read(NIFStream *nif)
|
void NiTexturingProperty::Texture::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
inUse = nif->getBoolean();
|
inUse = nif->getBoolean();
|
||||||
if(!inUse) return;
|
if(!inUse) return;
|
||||||
|
|
||||||
texture.read(nif);
|
texture.read(nif);
|
||||||
clamp = nif->getUInt();
|
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||||
nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible
|
{
|
||||||
uvSet = nif->getUInt();
|
clamp = nif->getInt();
|
||||||
|
nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clamp = nif->getUShort() & 0xF;
|
||||||
|
}
|
||||||
|
// Max anisotropy. I assume we'll always only use the global anisotropy setting.
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(20,5,0,4))
|
||||||
|
nif->getUShort();
|
||||||
|
|
||||||
|
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||||
|
uvSet = nif->getUInt();
|
||||||
|
|
||||||
// Two PS2-specific shorts.
|
// Two PS2-specific shorts.
|
||||||
nif->skip(4);
|
if (nif->getVersion() < NIFStream::generateVersion(10,4,0,2))
|
||||||
nif->skip(2); // Unknown short
|
nif->skip(4);
|
||||||
|
if (nif->getVersion() <= NIFStream::generateVersion(4,1,0,18))
|
||||||
|
nif->skip(2); // Unknown short
|
||||||
|
else if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||||
|
{
|
||||||
|
if (nif->getBoolean()) // Has texture transform
|
||||||
|
{
|
||||||
|
nif->getVector2(); // UV translation
|
||||||
|
nif->getVector2(); // UV scale
|
||||||
|
nif->getFloat(); // W axis rotation
|
||||||
|
nif->getUInt(); // Transform method
|
||||||
|
nif->getVector2(); // Texture rotation origin
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiTexturingProperty::Texture::post(NIFFile *nif)
|
void NiTexturingProperty::Texture::post(NIFFile *nif)
|
||||||
|
@ -35,7 +54,10 @@ void NiTexturingProperty::Texture::post(NIFFile *nif)
|
||||||
void NiTexturingProperty::read(NIFStream *nif)
|
void NiTexturingProperty::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Property::read(nif);
|
Property::read(nif);
|
||||||
apply = nif->getUInt();
|
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD || nif->getVersion() >= NIFStream::generateVersion(20,1,0,2))
|
||||||
|
flags = nif->getUShort();
|
||||||
|
if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,1))
|
||||||
|
apply = nif->getUInt();
|
||||||
|
|
||||||
unsigned int numTextures = nif->getUInt();
|
unsigned int numTextures = nif->getUInt();
|
||||||
|
|
||||||
|
@ -51,32 +73,53 @@ void NiTexturingProperty::read(NIFStream *nif)
|
||||||
envMapLumaBias = nif->getVector2();
|
envMapLumaBias = nif->getVector2();
|
||||||
bumpMapMatrix = nif->getVector4();
|
bumpMapMatrix = nif->getVector4();
|
||||||
}
|
}
|
||||||
|
else if (i == 7 && textures[7].inUse && nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))
|
||||||
|
/*float parallaxOffset = */nif->getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||||
|
{
|
||||||
|
unsigned int numShaderTextures = nif->getUInt();
|
||||||
|
shaderTextures.resize(numShaderTextures);
|
||||||
|
for (unsigned int i = 0; i < numShaderTextures; i++)
|
||||||
|
{
|
||||||
|
shaderTextures[i].read(nif);
|
||||||
|
if (shaderTextures[i].inUse)
|
||||||
|
nif->getUInt(); // Unique identifier
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiTexturingProperty::post(NIFFile *nif)
|
void NiTexturingProperty::post(NIFFile *nif)
|
||||||
{
|
{
|
||||||
Property::post(nif);
|
Property::post(nif);
|
||||||
for(int i = 0;i < 7;i++)
|
for (size_t i = 0; i < textures.size(); i++)
|
||||||
textures[i].post(nif);
|
textures[i].post(nif);
|
||||||
|
for (size_t i = 0; i < shaderTextures.size(); i++)
|
||||||
|
shaderTextures[i].post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiFogProperty::read(NIFStream *nif)
|
void NiFogProperty::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Property::read(nif);
|
Property::read(nif);
|
||||||
|
mFlags = nif->getUShort();
|
||||||
mFogDepth = nif->getFloat();
|
mFogDepth = nif->getFloat();
|
||||||
mColour = nif->getVector3();
|
mColour = nif->getVector3();
|
||||||
}
|
}
|
||||||
|
|
||||||
void S_MaterialProperty::read(NIFStream *nif)
|
void S_MaterialProperty::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
ambient = nif->getVector3();
|
if (nif->getBethVersion() < 26)
|
||||||
diffuse = nif->getVector3();
|
{
|
||||||
|
ambient = nif->getVector3();
|
||||||
|
diffuse = nif->getVector3();
|
||||||
|
}
|
||||||
specular = nif->getVector3();
|
specular = nif->getVector3();
|
||||||
emissive = nif->getVector3();
|
emissive = nif->getVector3();
|
||||||
glossiness = nif->getFloat();
|
glossiness = nif->getFloat();
|
||||||
alpha = nif->getFloat();
|
alpha = nif->getFloat();
|
||||||
|
if (nif->getBethVersion() > 21)
|
||||||
|
emissive *= nif->getFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
void S_VertexColorProperty::read(NIFStream *nif)
|
void S_VertexColorProperty::read(NIFStream *nif)
|
||||||
|
@ -92,14 +135,29 @@ void S_AlphaProperty::read(NIFStream *nif)
|
||||||
|
|
||||||
void S_StencilProperty::read(NIFStream *nif)
|
void S_StencilProperty::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
enabled = nif->getChar();
|
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||||
compareFunc = nif->getInt();
|
{
|
||||||
stencilRef = nif->getUInt();
|
enabled = nif->getChar();
|
||||||
stencilMask = nif->getUInt();
|
compareFunc = nif->getInt();
|
||||||
failAction = nif->getInt();
|
stencilRef = nif->getUInt();
|
||||||
zFailAction = nif->getInt();
|
stencilMask = nif->getUInt();
|
||||||
zPassAction = nif->getInt();
|
failAction = nif->getInt();
|
||||||
drawMode = nif->getInt();
|
zFailAction = nif->getInt();
|
||||||
|
zPassAction = nif->getInt();
|
||||||
|
drawMode = nif->getInt();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned short flags = nif->getUShort();
|
||||||
|
enabled = flags & 0x1;
|
||||||
|
failAction = (flags >> 1) & 0x7;
|
||||||
|
zFailAction = (flags >> 4) & 0x7;
|
||||||
|
zPassAction = (flags >> 7) & 0x7;
|
||||||
|
drawMode = (flags >> 10) & 0x3;
|
||||||
|
compareFunc = (flags >> 12) & 0x7;
|
||||||
|
stencilRef = nif->getUInt();
|
||||||
|
stencilMask = nif->getUInt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,18 +29,13 @@
|
||||||
namespace Nif
|
namespace Nif
|
||||||
{
|
{
|
||||||
|
|
||||||
class Property : public Named
|
class Property : public Named { };
|
||||||
{
|
|
||||||
public:
|
|
||||||
// The meaning of these depends on the actual property type.
|
|
||||||
unsigned int flags;
|
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
|
||||||
};
|
|
||||||
|
|
||||||
class NiTexturingProperty : public Property
|
class NiTexturingProperty : public Property
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
unsigned short flags{0u};
|
||||||
|
|
||||||
// A sub-texture
|
// A sub-texture
|
||||||
struct Texture
|
struct Texture
|
||||||
{
|
{
|
||||||
|
@ -92,6 +87,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Texture> textures;
|
std::vector<Texture> textures;
|
||||||
|
std::vector<Texture> shaderTextures;
|
||||||
|
|
||||||
osg::Vec2f envMapLumaBias;
|
osg::Vec2f envMapLumaBias;
|
||||||
osg::Vec4f bumpMapMatrix;
|
osg::Vec4f bumpMapMatrix;
|
||||||
|
@ -103,28 +99,80 @@ public:
|
||||||
class NiFogProperty : public Property
|
class NiFogProperty : public Property
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
unsigned short mFlags;
|
||||||
float mFogDepth;
|
float mFogDepth;
|
||||||
osg::Vec3f mColour;
|
osg::Vec3f mColour;
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
};
|
};
|
||||||
|
|
||||||
// These contain no other data than the 'flags' field in Property
|
// These contain no other data than the 'flags' field
|
||||||
class NiShadeProperty : public Property { };
|
struct NiShadeProperty : public Property
|
||||||
class NiDitherProperty : public Property { };
|
{
|
||||||
class NiZBufferProperty : public Property { };
|
unsigned short flags{0u};
|
||||||
class NiSpecularProperty : public Property { };
|
void read(NIFStream *nif)
|
||||||
class NiWireframeProperty : public Property { };
|
{
|
||||||
|
Property::read(nif);
|
||||||
|
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
|
||||||
|
flags = nif->getUShort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiDitherProperty : public Property
|
||||||
|
{
|
||||||
|
unsigned short flags;
|
||||||
|
void read(NIFStream* nif)
|
||||||
|
{
|
||||||
|
Property::read(nif);
|
||||||
|
flags = nif->getUShort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiZBufferProperty : public Property
|
||||||
|
{
|
||||||
|
unsigned short flags;
|
||||||
|
unsigned int testFunction;
|
||||||
|
void read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Property::read(nif);
|
||||||
|
flags = nif->getUShort();
|
||||||
|
testFunction = (flags >> 2) & 0x7;
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(4,1,0,12) && nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||||
|
testFunction = nif->getUInt();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiSpecularProperty : public Property
|
||||||
|
{
|
||||||
|
unsigned short flags;
|
||||||
|
void read(NIFStream* nif)
|
||||||
|
{
|
||||||
|
Property::read(nif);
|
||||||
|
flags = nif->getUShort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiWireframeProperty : public Property
|
||||||
|
{
|
||||||
|
unsigned short flags;
|
||||||
|
void read(NIFStream* nif)
|
||||||
|
{
|
||||||
|
Property::read(nif);
|
||||||
|
flags = nif->getUShort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// The rest are all struct-based
|
// The rest are all struct-based
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct StructPropT : Property
|
struct StructPropT : Property
|
||||||
{
|
{
|
||||||
T data;
|
T data;
|
||||||
|
unsigned short flags;
|
||||||
|
|
||||||
void read(NIFStream *nif)
|
void read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Property::read(nif);
|
Property::read(nif);
|
||||||
|
flags = nif->getUShort();
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -132,7 +180,8 @@ struct StructPropT : Property
|
||||||
struct S_MaterialProperty
|
struct S_MaterialProperty
|
||||||
{
|
{
|
||||||
// The vector components are R,G,B
|
// The vector components are R,G,B
|
||||||
osg::Vec3f ambient, diffuse, specular, emissive;
|
osg::Vec3f ambient{1.f,1.f,1.f}, diffuse{1.f,1.f,1.f};
|
||||||
|
osg::Vec3f specular, emissive;
|
||||||
float glossiness, alpha;
|
float glossiness, alpha;
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
|
@ -246,9 +295,35 @@ struct S_StencilProperty
|
||||||
};
|
};
|
||||||
|
|
||||||
class NiAlphaProperty : public StructPropT<S_AlphaProperty> { };
|
class NiAlphaProperty : public StructPropT<S_AlphaProperty> { };
|
||||||
class NiMaterialProperty : public StructPropT<S_MaterialProperty> { };
|
|
||||||
class NiVertexColorProperty : public StructPropT<S_VertexColorProperty> { };
|
class NiVertexColorProperty : public StructPropT<S_VertexColorProperty> { };
|
||||||
class NiStencilProperty : public StructPropT<S_StencilProperty> { };
|
struct NiStencilProperty : public Property
|
||||||
|
{
|
||||||
|
S_StencilProperty data;
|
||||||
|
unsigned short flags{0u};
|
||||||
|
|
||||||
|
void read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Property::read(nif);
|
||||||
|
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
|
||||||
|
flags = nif->getUShort();
|
||||||
|
data.read(nif);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiMaterialProperty : public Property
|
||||||
|
{
|
||||||
|
S_MaterialProperty data;
|
||||||
|
unsigned short flags{0u};
|
||||||
|
|
||||||
|
void read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Property::read(nif);
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(3,0,0,0)
|
||||||
|
&& nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
|
||||||
|
flags = nif->getUShort();
|
||||||
|
data.read(nif);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -101,7 +101,15 @@ enum RecordType
|
||||||
RC_RootCollisionNode,
|
RC_RootCollisionNode,
|
||||||
RC_NiSphericalCollider,
|
RC_NiSphericalCollider,
|
||||||
RC_NiLookAtController,
|
RC_NiLookAtController,
|
||||||
RC_NiPalette
|
RC_NiPalette,
|
||||||
|
RC_NiIntegerExtraData,
|
||||||
|
RC_NiIntegersExtraData,
|
||||||
|
RC_NiBinaryExtraData,
|
||||||
|
RC_NiBooleanExtraData,
|
||||||
|
RC_NiVectorExtraData,
|
||||||
|
RC_NiColorExtraData,
|
||||||
|
RC_NiFloatExtraData,
|
||||||
|
RC_NiFloatsExtraData
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for all records
|
/// Base class for all records
|
||||||
|
|
|
@ -310,11 +310,6 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
|
||||||
RollController::RollController(const Nif::NiFloatData *data)
|
RollController::RollController(const Nif::NiFloatData *data)
|
||||||
: mData(data->mKeyList, 1.f)
|
: mData(data->mKeyList, 1.f)
|
||||||
, mStartingTime(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RollController::RollController() : mStartingTime(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +317,7 @@ RollController::RollController(const RollController ©, const osg::CopyOp &co
|
||||||
: osg::NodeCallback(copy, copyop)
|
: osg::NodeCallback(copy, copyop)
|
||||||
, Controller(copy)
|
, Controller(copy)
|
||||||
, mData(copy.mData)
|
, mData(copy.mData)
|
||||||
, mStartingTime(0)
|
, mStartingTime(copy.mStartingTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,7 +444,7 @@ void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *n
|
||||||
}
|
}
|
||||||
|
|
||||||
FlipController::FlipController(const Nif::NiFlipController *ctrl, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)
|
FlipController::FlipController(const Nif::NiFlipController *ctrl, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)
|
||||||
: mTexSlot(ctrl->mTexSlot)
|
: mTexSlot(0) // always affects diffuse
|
||||||
, mDelta(ctrl->mDelta)
|
, mDelta(ctrl->mDelta)
|
||||||
, mTextures(textures)
|
, mTextures(textures)
|
||||||
{
|
{
|
||||||
|
@ -462,12 +457,6 @@ FlipController::FlipController(int texSlot, float delta, const std::vector<osg::
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
FlipController::FlipController()
|
|
||||||
: mTexSlot(0)
|
|
||||||
, mDelta(0.f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FlipController::FlipController(const FlipController ©, const osg::CopyOp ©op)
|
FlipController::FlipController(const FlipController ©, const osg::CopyOp ©op)
|
||||||
: StateSetUpdater(copy, copyop)
|
: StateSetUpdater(copy, copyop)
|
||||||
, Controller(copy)
|
, Controller(copy)
|
||||||
|
|
|
@ -268,11 +268,11 @@ namespace NifOsg
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
FloatInterpolator mData;
|
FloatInterpolator mData;
|
||||||
double mStartingTime;
|
double mStartingTime{0};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RollController(const Nif::NiFloatData *data);
|
RollController(const Nif::NiFloatData *data);
|
||||||
RollController();
|
RollController() = default;
|
||||||
RollController(const RollController& copy, const osg::CopyOp& copyop);
|
RollController(const RollController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
|
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
|
||||||
|
@ -326,14 +326,14 @@ namespace NifOsg
|
||||||
class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
|
class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int mTexSlot;
|
int mTexSlot{0};
|
||||||
float mDelta;
|
float mDelta{0.f};
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;
|
std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FlipController(const Nif::NiFlipController* ctrl, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures);
|
FlipController(const Nif::NiFlipController* ctrl, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures);
|
||||||
FlipController(int texSlot, float delta, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures);
|
FlipController(int texSlot, float delta, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures);
|
||||||
FlipController();
|
FlipController() = default;
|
||||||
FlipController(const FlipController& copy, const osg::CopyOp& copyop);
|
FlipController(const FlipController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
META_Object(NifOsg, FlipController)
|
META_Object(NifOsg, FlipController)
|
||||||
|
|
|
@ -799,22 +799,23 @@ namespace NifOsg
|
||||||
{
|
{
|
||||||
const Nif::NiFlipController* flipctrl = static_cast<const Nif::NiFlipController*>(ctrl.getPtr());
|
const Nif::NiFlipController* flipctrl = static_cast<const Nif::NiFlipController*>(ctrl.getPtr());
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > textures;
|
std::vector<osg::ref_ptr<osg::Texture2D> > textures;
|
||||||
|
|
||||||
|
// inherit wrap settings from the target slot
|
||||||
|
osg::Texture2D* inherit = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
|
||||||
|
osg::Texture2D::WrapMode wrapS = osg::Texture2D::REPEAT;
|
||||||
|
osg::Texture2D::WrapMode wrapT = osg::Texture2D::REPEAT;
|
||||||
|
if (inherit)
|
||||||
|
{
|
||||||
|
wrapS = inherit->getWrap(osg::Texture2D::WRAP_S);
|
||||||
|
wrapT = inherit->getWrap(osg::Texture2D::WRAP_T);
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned int i=0; i<flipctrl->mSources.length(); ++i)
|
for (unsigned int i=0; i<flipctrl->mSources.length(); ++i)
|
||||||
{
|
{
|
||||||
Nif::NiSourceTexturePtr st = flipctrl->mSources[i];
|
Nif::NiSourceTexturePtr st = flipctrl->mSources[i];
|
||||||
if (st.empty())
|
if (st.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// inherit wrap settings from the target slot
|
|
||||||
osg::Texture2D* inherit = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(flipctrl->mTexSlot, osg::StateAttribute::TEXTURE));
|
|
||||||
osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP_TO_EDGE;
|
|
||||||
osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP_TO_EDGE;
|
|
||||||
if (inherit)
|
|
||||||
{
|
|
||||||
wrapS = inherit->getWrap(osg::Texture2D::WRAP_S);
|
|
||||||
wrapT = inherit->getWrap(osg::Texture2D::WRAP_T);
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Image> image (handleSourceTexture(st.getPtr(), imageManager));
|
osg::ref_ptr<osg::Image> image (handleSourceTexture(st.getPtr(), imageManager));
|
||||||
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D(image));
|
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D(image));
|
||||||
if (image)
|
if (image)
|
||||||
|
@ -1238,15 +1239,13 @@ namespace NifOsg
|
||||||
std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->name);
|
std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->name);
|
||||||
|
|
||||||
SceneUtil::RigGeometry::BoneInfluence influence;
|
SceneUtil::RigGeometry::BoneInfluence influence;
|
||||||
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[i].weights;
|
const auto& weights = data->bones[i].weights;
|
||||||
for(size_t j = 0;j < weights.size();j++)
|
for(size_t j = 0;j < weights.size();j++)
|
||||||
{
|
influence.mWeights.push_back({weights[j].vertex, weights[j].weight});
|
||||||
influence.mWeights.emplace_back(weights[j].vertex, weights[j].weight);
|
|
||||||
}
|
|
||||||
influence.mInvBindMatrix = data->bones[i].trafo.toMatrix();
|
influence.mInvBindMatrix = data->bones[i].trafo.toMatrix();
|
||||||
influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius);
|
influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius);
|
||||||
|
|
||||||
map->mData.emplace_back(boneName, influence);
|
map->mData.push_back({boneName, influence});
|
||||||
}
|
}
|
||||||
rig->setInfluenceMap(map);
|
rig->setInfluenceMap(map);
|
||||||
|
|
||||||
|
@ -1451,7 +1450,7 @@ namespace NifOsg
|
||||||
// If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the shadow casting shader will need to be updated accordingly.
|
// If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the shadow casting shader will need to be updated accordingly.
|
||||||
for (size_t i=0; i<texprop->textures.size(); ++i)
|
for (size_t i=0; i<texprop->textures.size(); ++i)
|
||||||
{
|
{
|
||||||
if (texprop->textures[i].inUse)
|
if (texprop->textures[i].inUse || (i == Nif::NiTexturingProperty::BaseTexture && !texprop->controller.empty()))
|
||||||
{
|
{
|
||||||
switch(i)
|
switch(i)
|
||||||
{
|
{
|
||||||
|
@ -1477,32 +1476,46 @@ namespace NifOsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i];
|
unsigned int uvSet = 0;
|
||||||
if(tex.texture.empty() && texprop->controller.empty())
|
|
||||||
{
|
|
||||||
if (i == 0)
|
|
||||||
Log(Debug::Warning) << "Base texture is in use but empty on shape \"" << nodeName << "\" in " << mFilename;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new texture, will later attempt to share using the SharedStateManager
|
// create a new texture, will later attempt to share using the SharedStateManager
|
||||||
osg::ref_ptr<osg::Texture2D> texture2d;
|
osg::ref_ptr<osg::Texture2D> texture2d;
|
||||||
if (!tex.texture.empty())
|
if (texprop->textures[i].inUse)
|
||||||
{
|
{
|
||||||
const Nif::NiSourceTexture *st = tex.texture.getPtr();
|
const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i];
|
||||||
osg::ref_ptr<osg::Image> image = handleSourceTexture(st, imageManager);
|
if(tex.texture.empty() && texprop->controller.empty())
|
||||||
texture2d = new osg::Texture2D(image);
|
{
|
||||||
if (image)
|
if (i == 0)
|
||||||
texture2d->setTextureSize(image->s(), image->t());
|
Log(Debug::Warning) << "Base texture is in use but empty on shape \"" << nodeName << "\" in " << mFilename;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tex.texture.empty())
|
||||||
|
{
|
||||||
|
const Nif::NiSourceTexture *st = tex.texture.getPtr();
|
||||||
|
osg::ref_ptr<osg::Image> image = handleSourceTexture(st, imageManager);
|
||||||
|
texture2d = new osg::Texture2D(image);
|
||||||
|
if (image)
|
||||||
|
texture2d->setTextureSize(image->s(), image->t());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
texture2d = new osg::Texture2D;
|
||||||
|
|
||||||
|
bool wrapT = tex.clamp & 0x1;
|
||||||
|
bool wrapS = (tex.clamp >> 1) & 0x1;
|
||||||
|
|
||||||
|
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
uvSet = tex.uvSet;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// Texture only comes from NiFlipController, so tex is ignored, set defaults
|
||||||
texture2d = new osg::Texture2D;
|
texture2d = new osg::Texture2D;
|
||||||
|
texture2d->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||||
bool wrapT = tex.clamp & 0x1;
|
texture2d->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||||
bool wrapS = (tex.clamp >> 1) & 0x1;
|
uvSet = 0;
|
||||||
|
}
|
||||||
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
unsigned int texUnit = boundTextures.size();
|
unsigned int texUnit = boundTextures.size();
|
||||||
|
|
||||||
|
@ -1590,7 +1603,7 @@ namespace NifOsg
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
boundTextures.push_back(tex.uvSet);
|
boundTextures.push_back(uvSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
|
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
|
||||||
|
@ -1717,7 +1730,7 @@ namespace NifOsg
|
||||||
osg::StateSet* stateset = node->getOrCreateStateSet();
|
osg::StateSet* stateset = node->getOrCreateStateSet();
|
||||||
|
|
||||||
// Specular lighting is enabled by default, but there's a quirk...
|
// Specular lighting is enabled by default, but there's a quirk...
|
||||||
int specFlags = 1;
|
bool specEnabled = true;
|
||||||
osg::ref_ptr<osg::Material> mat (new osg::Material);
|
osg::ref_ptr<osg::Material> mat (new osg::Material);
|
||||||
mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF);
|
mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF);
|
||||||
|
|
||||||
|
@ -1736,7 +1749,8 @@ namespace NifOsg
|
||||||
case Nif::RC_NiSpecularProperty:
|
case Nif::RC_NiSpecularProperty:
|
||||||
{
|
{
|
||||||
// Specular property can turn specular lighting off.
|
// Specular property can turn specular lighting off.
|
||||||
specFlags = property->flags;
|
auto specprop = static_cast<const Nif::NiSpecularProperty*>(property);
|
||||||
|
specEnabled = specprop->flags & 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Nif::RC_NiMaterialProperty:
|
case Nif::RC_NiMaterialProperty:
|
||||||
|
@ -1820,7 +1834,7 @@ namespace NifOsg
|
||||||
}
|
}
|
||||||
|
|
||||||
// While NetImmerse and Gamebryo support specular lighting, Morrowind has its support disabled.
|
// While NetImmerse and Gamebryo support specular lighting, Morrowind has its support disabled.
|
||||||
if (mVersion <= Nif::NIFFile::NIFVersion::VER_MW || specFlags == 0)
|
if (mVersion <= Nif::NIFFile::NIFVersion::VER_MW || !specEnabled)
|
||||||
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f));
|
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f));
|
||||||
|
|
||||||
if (lightmode == 0)
|
if (lightmode == 0)
|
||||||
|
|
|
@ -137,14 +137,13 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
mBoneNodesVector.clear();
|
mBoneNodesVector.clear();
|
||||||
for (auto& bonePair : mBoneSphereVector->mData)
|
for (auto& boundPair : mBoneSphereVector->mData)
|
||||||
{
|
{
|
||||||
const std::string& boneName = bonePair.first;
|
Bone* bone = mSkeleton->getBone(boundPair.name);
|
||||||
Bone* bone = mSkeleton->getBone(boneName);
|
|
||||||
if (!bone)
|
if (!bone)
|
||||||
{
|
{
|
||||||
mBoneNodesVector.push_back(nullptr);
|
mBoneNodesVector.push_back(nullptr);
|
||||||
Log(Debug::Error) << "Error: RigGeometry did not find bone " << boneName;
|
Log(Debug::Error) << "Error: RigGeometry did not find bone " << boundPair.name;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,12 +154,11 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv)
|
||||||
{
|
{
|
||||||
for (auto &weight : pair.first)
|
for (auto &weight : pair.first)
|
||||||
{
|
{
|
||||||
const std::string& boneName = weight.first.first;
|
Bone* bone = mSkeleton->getBone(weight.boneName);
|
||||||
Bone* bone = mSkeleton->getBone(boneName);
|
|
||||||
if (!bone)
|
if (!bone)
|
||||||
{
|
{
|
||||||
mBoneNodesVector.push_back(nullptr);
|
mBoneNodesVector.push_back(nullptr);
|
||||||
Log(Debug::Error) << "Error: RigGeometry did not find bone " << boneName;
|
Log(Debug::Error) << "Error: RigGeometry did not find bone " << weight.boneName;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +216,7 @@ void RigGeometry::cull(osg::NodeVisitor* nv)
|
||||||
if (bone == nullptr)
|
if (bone == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
accumulateMatrix(weight.first.second, bone->mMatrixInSkeletonSpace, weight.second, resultMat);
|
accumulateMatrix(weight.bindMatrix, bone->mMatrixInSkeletonSpace, weight.value, resultMat);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +279,7 @@ void RigGeometry::updateBounds(osg::NodeVisitor *nv)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
osg::BoundingSpheref bs = boundPair.second;
|
osg::BoundingSpheref bs = boundPair.sphere;
|
||||||
if (mGeomToSkelMatrix)
|
if (mGeomToSkelMatrix)
|
||||||
transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs);
|
transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs);
|
||||||
else
|
else
|
||||||
|
@ -337,30 +335,21 @@ void RigGeometry::setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap)
|
||||||
{
|
{
|
||||||
mInfluenceMap = influenceMap;
|
mInfluenceMap = influenceMap;
|
||||||
|
|
||||||
typedef std::map<unsigned short, std::vector<BoneWeight> > Vertex2BoneMap;
|
using Vertex2BoneMap = std::map<unsigned short, std::vector<BoneWeight>>;
|
||||||
Vertex2BoneMap vertex2BoneMap;
|
Vertex2BoneMap vertex2BoneMap;
|
||||||
mBoneSphereVector = new BoneSphereVector;
|
mBoneSphereVector = new BoneSphereVector;
|
||||||
mBoneSphereVector->mData.reserve(mInfluenceMap->mData.size());
|
mBoneSphereVector->mData.reserve(mInfluenceMap->mData.size());
|
||||||
mBone2VertexVector = new Bone2VertexVector;
|
mBone2VertexVector = new Bone2VertexVector;
|
||||||
for (auto& influencePair : mInfluenceMap->mData)
|
for (const BoneData& bone : mInfluenceMap->mData)
|
||||||
{
|
{
|
||||||
const std::string& boneName = influencePair.first;
|
mBoneSphereVector->mData.push_back({bone.name, bone.influence.mBoundSphere});
|
||||||
const BoneInfluence& bi = influencePair.second;
|
for (auto& weight : bone.influence.mWeights)
|
||||||
mBoneSphereVector->mData.emplace_back(boneName, bi.mBoundSphere);
|
vertex2BoneMap[weight.vertex].push_back({bone.name, bone.influence.mInvBindMatrix, weight.value});
|
||||||
|
|
||||||
for (auto& weightPair: bi.mWeights)
|
|
||||||
{
|
|
||||||
std::vector<BoneWeight>& vec = vertex2BoneMap[weightPair.first];
|
|
||||||
|
|
||||||
vec.emplace_back(std::make_pair(boneName, bi.mInvBindMatrix), weightPair.second);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bone2VertexMap bone2VertexMap;
|
Bone2VertexMap bone2VertexMap;
|
||||||
for (auto& vertexPair : vertex2BoneMap)
|
for (auto& vertexPair : vertex2BoneMap)
|
||||||
{
|
|
||||||
bone2VertexMap[vertexPair.second].emplace_back(vertexPair.first);
|
bone2VertexMap[vertexPair.second].emplace_back(vertexPair.first);
|
||||||
}
|
|
||||||
|
|
||||||
mBone2VertexVector->mData.reserve(bone2VertexMap.size());
|
mBone2VertexVector->mData.reserve(bone2VertexMap.size());
|
||||||
mBone2VertexVector->mData.assign(bone2VertexMap.begin(), bone2VertexMap.end());
|
mBone2VertexVector->mData.assign(bone2VertexMap.begin(), bone2VertexMap.end());
|
||||||
|
|
|
@ -25,17 +25,32 @@ namespace SceneUtil
|
||||||
// Currently empty as this is difficult to implement. Technically we would need to compile both internal geometries in separate frames but this method is only called once. Alternatively we could compile just the static parts of the model.
|
// Currently empty as this is difficult to implement. Technically we would need to compile both internal geometries in separate frames but this method is only called once. Alternatively we could compile just the static parts of the model.
|
||||||
virtual void compileGLObjects(osg::RenderInfo& renderInfo) const {}
|
virtual void compileGLObjects(osg::RenderInfo& renderInfo) const {}
|
||||||
|
|
||||||
|
struct VertexWeight
|
||||||
|
{
|
||||||
|
unsigned short vertex;
|
||||||
|
float value;
|
||||||
|
};
|
||||||
|
|
||||||
struct BoneInfluence
|
struct BoneInfluence
|
||||||
{
|
{
|
||||||
osg::Matrixf mInvBindMatrix;
|
osg::Matrixf mInvBindMatrix;
|
||||||
osg::BoundingSpheref mBoundSphere;
|
osg::BoundingSpheref mBoundSphere;
|
||||||
// <vertex index, weight>
|
std::vector<VertexWeight> mWeights;
|
||||||
std::vector<std::pair<unsigned short, float>> mWeights;
|
};
|
||||||
|
|
||||||
|
struct BoneData
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
BoneInfluence influence;
|
||||||
|
bool operator<(const BoneData& other) const
|
||||||
|
{
|
||||||
|
return name < other.name;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InfluenceMap : public osg::Referenced
|
struct InfluenceMap : public osg::Referenced
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::string, BoneInfluence>> mData;
|
std::vector<BoneData> mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
void setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap);
|
void setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap);
|
||||||
|
@ -79,23 +94,36 @@ namespace SceneUtil
|
||||||
|
|
||||||
osg::ref_ptr<InfluenceMap> mInfluenceMap;
|
osg::ref_ptr<InfluenceMap> mInfluenceMap;
|
||||||
|
|
||||||
typedef std::pair<std::string, osg::Matrixf> BoneBindMatrixPair;
|
struct BoneWeight
|
||||||
|
{
|
||||||
|
std::string boneName;
|
||||||
|
osg::Matrixf bindMatrix;
|
||||||
|
float value;
|
||||||
|
bool operator<(const BoneWeight& other) const
|
||||||
|
{
|
||||||
|
return boneName < other.boneName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
typedef std::pair<BoneBindMatrixPair, float> BoneWeight;
|
using VertexList = std::vector<unsigned short>;
|
||||||
|
using BoneWeightList = std::vector<BoneWeight>;
|
||||||
typedef std::vector<unsigned short> VertexList;
|
using Bone2VertexMap = std::map<BoneWeightList, VertexList>;
|
||||||
|
|
||||||
typedef std::map<std::vector<BoneWeight>, VertexList> Bone2VertexMap;
|
|
||||||
|
|
||||||
struct Bone2VertexVector : public osg::Referenced
|
struct Bone2VertexVector : public osg::Referenced
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::vector<BoneWeight>, VertexList>> mData;
|
std::vector<std::pair<BoneWeightList, VertexList>> mData;
|
||||||
};
|
};
|
||||||
osg::ref_ptr<Bone2VertexVector> mBone2VertexVector;
|
osg::ref_ptr<Bone2VertexVector> mBone2VertexVector;
|
||||||
|
|
||||||
|
struct BoneSphere
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
osg::BoundingSpheref sphere;
|
||||||
|
};
|
||||||
|
|
||||||
struct BoneSphereVector : public osg::Referenced
|
struct BoneSphereVector : public osg::Referenced
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::string, osg::BoundingSpheref>> mData;
|
std::vector<BoneSphere> mData;
|
||||||
};
|
};
|
||||||
osg::ref_ptr<BoneSphereVector> mBoneSphereVector;
|
osg::ref_ptr<BoneSphereVector> mBoneSphereVector;
|
||||||
std::vector<Bone*> mBoneNodesVector;
|
std::vector<Bone*> mBoneNodesVector;
|
||||||
|
|
|
@ -120,6 +120,29 @@ void GraphicsWindowSDL2::init()
|
||||||
|
|
||||||
setSwapInterval(_traits->vsync);
|
setSwapInterval(_traits->vsync);
|
||||||
|
|
||||||
|
// Update traits with what we've actually been given
|
||||||
|
// Use intermediate to avoid signed/unsigned mismatch
|
||||||
|
int intermediateLocation;
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &intermediateLocation);
|
||||||
|
_traits->red = intermediateLocation;
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &intermediateLocation);
|
||||||
|
_traits->green = intermediateLocation;
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &intermediateLocation);
|
||||||
|
_traits->blue = intermediateLocation;
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &intermediateLocation);
|
||||||
|
_traits->alpha = intermediateLocation;
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &intermediateLocation);
|
||||||
|
_traits->depth = intermediateLocation;
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &intermediateLocation);
|
||||||
|
_traits->stencil = intermediateLocation;
|
||||||
|
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &intermediateLocation);
|
||||||
|
_traits->doubleBuffer = intermediateLocation;
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &intermediateLocation);
|
||||||
|
_traits->sampleBuffers = intermediateLocation;
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &intermediateLocation);
|
||||||
|
_traits->samples = intermediateLocation;
|
||||||
|
|
||||||
SDL_GL_MakeCurrent(oldWin, oldCtx);
|
SDL_GL_MakeCurrent(oldWin, oldCtx);
|
||||||
|
|
||||||
mValid = true;
|
mValid = true;
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace Shader
|
||||||
static bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set<boost::filesystem::path> includingFiles)
|
static bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set<boost::filesystem::path> includingFiles)
|
||||||
{
|
{
|
||||||
// An include is cyclic if it is being included by itself
|
// An include is cyclic if it is being included by itself
|
||||||
if (includingFiles.insert(shaderPath / fileName).second == false)
|
if (includingFiles.insert(shaderPath/fileName).second == false)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Shader " << fileName << " error: Detected cyclic #includes";
|
Log(Debug::Error) << "Shader " << fileName << " error: Detected cyclic #includes";
|
||||||
return false;
|
return false;
|
||||||
|
@ -107,7 +107,7 @@ namespace Shader
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lineDirectivePosition = 0;
|
lineDirectivePosition = 0;
|
||||||
lineNumber = 1;
|
lineNumber = 0;
|
||||||
}
|
}
|
||||||
lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n');
|
lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n');
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,19 @@ If you are running macOS, you can also download Morrowind through Steam:
|
||||||
#. Launch the Steam client and let it download. You can then find ``Morrowind.esm`` at
|
#. Launch the Steam client and let it download. You can then find ``Morrowind.esm`` at
|
||||||
``~/Library/Application Support/Steam/steamapps/common/The Elder Scrolls III - Morrowind/Data Files/``
|
``~/Library/Application Support/Steam/steamapps/common/The Elder Scrolls III - Morrowind/Data Files/``
|
||||||
|
|
||||||
|
Linux
|
||||||
|
----
|
||||||
|
Debian/Ubuntu - using "Steam Proton" & "OpenMW launcher".
|
||||||
|
----
|
||||||
|
#. Install Steam from "Ubuntu Software" Center
|
||||||
|
#. Enable Proton (basically WINE under the hood). This is done in the Steam client menu drop down. Select, "Steam | Settings" then in the "SteamPlay" section check the box next to "enable steam play for all other titles"
|
||||||
|
#. Now Morrowind should be selectable in your game list (as long as you own it). You can install it like any other game, choose to install it and remember the directory path of the location you pick.
|
||||||
|
#. Once the game files are installed, we can now install the open OpenMW Engine. I used "OpenMW launcher" from "Ubuntu Software" Center this has a wizard to help with the basic setup of OpenMW.
|
||||||
|
#. Launch "OpenMW launcher" and follow the setup wizard, when asked, point it at the location you installed Morrowind to, we will be looking for the directory that contains the Morrowing.esm file, for example '/steam library/steamapps/common/Morrowind/Data Files/'.
|
||||||
|
#. Everything should now be in place, click that big "PLAY" button and fire up OpenMW.
|
||||||
|
|
||||||
|
Nb. Bloodmoon.esm needs to be below Tribunal.esm in your datafiles list, if you dont have the right order a red "!" will apear next to the filename in the datafiles section of the OpenMW launcher, just drag bloodmoon below tribunal to fix it.
|
||||||
|
|
||||||
Wine
|
Wine
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
@ -43,16 +43,16 @@ Savegames
|
||||||
Screenshots
|
Screenshots
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
+--------------+-----------------------------------------------------------------------------------------------+
|
+--------------+-----------------------------------------------------------------------------------------------------------+
|
||||||
| OS | Location |
|
| OS | Location |
|
||||||
+==============+===============================================================================================+
|
+==============+===========================================================================================================+
|
||||||
| Linux | ``$HOME/.local/share/openmw`` |
|
| Linux | ``$HOME/.local/share/openmw/screenshots`` |
|
||||||
+--------------+-----------------------------------------------------------------------------------------------+
|
+--------------+-----------------------------------------------------------------------------------------------------------+
|
||||||
| Mac | ``$HOME/Library/Application\ Support/openmw`` |
|
| Mac | ``$HOME/Library/Application\ Support/openmw/screenshots`` |
|
||||||
+--------------+---------------+-------------------------------------------------------------------------------+
|
+--------------+---------------+-------------------------------------------------------------------------------------------+
|
||||||
| Windows | File Explorer | ``Documents\My Games\OpenMW`` |
|
| Windows | File Explorer | ``Documents\My Games\OpenMW\screenshots`` |
|
||||||
| | | |
|
| | | |
|
||||||
| | PowerShell | ``Join-Path ([environment]::GetFolderPath("mydocuments")) "My Games\OpenMW"`` |
|
| | PowerShell | ``Join-Path ([environment]::GetFolderPath("mydocuments")) "My Games\OpenMW\screenshots"`` |
|
||||||
| | | |
|
| | | |
|
||||||
| | Example | ``C:\Users\Username\Documents\My Games\OpenMW`` |
|
| | Example | ``C:\Users\Username\Documents\My Games\OpenMW\screenshots`` |
|
||||||
+--------------+---------------+-------------------------------------------------------------------------------+
|
+--------------+---------------+-------------------------------------------------------------------------------------------+
|
||||||
|
|
|
@ -331,8 +331,43 @@ If enabled then the character turns lower body to the direction of movement. Upp
|
||||||
|
|
||||||
This setting can be controlled in Advanced tab of the launcher.
|
This setting can be controlled in Advanced tab of the launcher.
|
||||||
|
|
||||||
|
smooth movement
|
||||||
|
---------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: False
|
||||||
|
|
||||||
|
Makes NPCs and player movement more smooth.
|
||||||
|
|
||||||
|
Recommended to use with "turn to movement direction" enabled.
|
||||||
|
|
||||||
|
This setting can be controlled in Advanced tab of the launcher.
|
||||||
|
|
||||||
|
NPCs avoid collisions
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: False
|
||||||
|
|
||||||
|
If enabled NPCs apply evasion maneuver to avoid collisions with others.
|
||||||
|
|
||||||
|
This setting can be controlled in Advanced tab of the launcher.
|
||||||
|
|
||||||
|
NPCs give way
|
||||||
|
-------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: True
|
||||||
|
|
||||||
|
Standing NPCs give way to moving ones. Works only if 'NPCs avoid collisions' is enabled.
|
||||||
|
|
||||||
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
|
||||||
swim upward correction
|
swim upward correction
|
||||||
----------------
|
----------------------
|
||||||
|
|
||||||
:Type: boolean
|
:Type: boolean
|
||||||
:Range: True/False
|
:Range: True/False
|
||||||
|
|
|
@ -325,6 +325,15 @@ uncapped damage fatigue = false
|
||||||
# Turn lower body to movement direction. 'true' makes diagonal movement more realistic.
|
# Turn lower body to movement direction. 'true' makes diagonal movement more realistic.
|
||||||
turn to movement direction = false
|
turn to movement direction = false
|
||||||
|
|
||||||
|
# Makes all movements of NPCs and player more smooth.
|
||||||
|
smooth movement = false
|
||||||
|
|
||||||
|
# All actors avoid collisions with other actors.
|
||||||
|
NPCs avoid collisions = false
|
||||||
|
|
||||||
|
# Give way to moving actors when idle. Requires 'NPCs avoid collisions' to be enabled.
|
||||||
|
NPCs give way = true
|
||||||
|
|
||||||
# Makes player swim a bit upward from the line of sight.
|
# Makes player swim a bit upward from the line of sight.
|
||||||
swim upward correction = false
|
swim upward correction = false
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,16 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QCheckBox" name="avoidCollisionsCheckBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>If enabled NPCs apply evasion maneuver to avoid collisions with others.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>NPCs avoid collisions</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QCheckBox" name="enableNavigatorCheckBox">
|
<widget class="QCheckBox" name="enableNavigatorCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
|
@ -233,6 +243,16 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="smoothMovementCheckBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Makes NPCs and player movement more smooth. Recommended to use with "turn to movement direction" enabled.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Smooth movement</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QCheckBox" name="distantLandCheckBox">
|
<widget class="QCheckBox" name="distantLandCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
|
@ -283,7 +303,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QCheckBox" name="animSourcesCheckBox">
|
<widget class="QCheckBox" name="animSourcesCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html></string>
|
<string><html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html></string>
|
||||||
|
|
Loading…
Reference in a new issue