mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-22 22:39:51 +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:
|
||||
- build
|
||||
|
||||
Debian:
|
||||
|
||||
.Debian:
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
image: debian:bullseye
|
||||
cache:
|
||||
key: cache.002
|
||||
paths:
|
||||
- apt-cache/
|
||||
- ccache/
|
||||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- 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
|
||||
script:
|
||||
- export CCACHE_BASEDIR="`pwd`"
|
||||
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
|
||||
- ccache -z -M 250M
|
||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
||||
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
- make -j$cores_to_use
|
||||
- DESTDIR=artifacts make install
|
||||
- ccache -z -M 1G
|
||||
- CI/before_script.linux.sh
|
||||
- cd build
|
||||
- cmake --build . -- -j $(nproc)
|
||||
- cmake --install .
|
||||
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_test_suite; fi
|
||||
- ccache -s
|
||||
artifacts:
|
||||
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:
|
||||
tags:
|
||||
|
@ -228,4 +262,4 @@ Windows_MSBuild_CS_RelWithDebInfo:
|
|||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "RelWithDebInfo"
|
||||
config: "RelWithDebInfo"
|
||||
|
|
|
@ -45,6 +45,12 @@ matrix:
|
|||
os: linux
|
||||
dist: focal
|
||||
if: branch != coverity_scan
|
||||
- name: OpenMW (tests only) on Ubuntu Focal with GCC
|
||||
os: linux
|
||||
dist: focal
|
||||
if: branch != coverity_scan
|
||||
env:
|
||||
- BUILD_TESTS_ONLY: 1
|
||||
- name: OpenMW (openmw) on Ubuntu Focal with Clang's Static Analysis
|
||||
os: linux
|
||||
dist: focal
|
||||
|
@ -73,7 +79,7 @@ script:
|
|||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; 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
|
||||
- cd "${TRAVIS_BUILD_DIR}"
|
||||
- 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 #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 #3676: NiParticleColorModifier isn't applied properly
|
||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
||||
Bug #4021: Attributes and skills are not stored as floats
|
||||
Bug #4055: Local scripts don't inherit variables from their base record
|
||||
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 #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 #5165: Active spells should use real time intead of timestamps
|
||||
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 #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 #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 #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 #5362: Show the soul gems' trapped soul in count dialog
|
||||
Feature #5445: Handle NiLines
|
||||
|
@ -56,6 +63,8 @@
|
|||
Feature #5524: Resume failed script execution after reload
|
||||
Feature #5525: Search fields tweaks (utf-8)
|
||||
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 #5520: Improve cell name autocompleter implementation
|
||||
|
||||
|
|
|
@ -2,22 +2,43 @@
|
|||
|
||||
free -m
|
||||
|
||||
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
|
||||
GOOGLETEST_DIR="$(pwd)/googletest/build"
|
||||
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||
export GOOGLETEST_DIR="$(pwd)/googletest/build/install"
|
||||
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
|
||||
fi
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
${ANALYZE} cmake \
|
||||
-DCMAKE_C_COMPILER="${CC}" \
|
||||
-DCMAKE_CXX_COMPILER="${CXX}" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DBUILD_UNITTESTS=TRUE \
|
||||
-DUSE_SYSTEM_TINYXML=TRUE \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DBINDIR="/usr/games" \
|
||||
-DCMAKE_BUILD_TYPE="DEBUG" \
|
||||
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||
-DGMOCK_ROOT="${GOOGLETEST_DIR}" \
|
||||
..
|
||||
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||
${ANALYZE} cmake \
|
||||
-D CMAKE_C_COMPILER="${CC}" \
|
||||
-D CMAKE_CXX_COMPILER="${CXX}" \
|
||||
-D CMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-D CMAKE_INSTALL_PREFIX=install \
|
||||
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-D USE_SYSTEM_TINYXML=TRUE \
|
||||
-D BUILD_OPENMW=OFF \
|
||||
-D BUILD_BSATOOL=OFF \
|
||||
-D BUILD_ESMTOOL=OFF \
|
||||
-D BUILD_LAUNCHER=OFF \
|
||||
-D BUILD_MWINIIMPORTER=OFF \
|
||||
-D BUILD_ESSIMPORTER=OFF \
|
||||
-D BUILD_OPENCS=OFF \
|
||||
-D BUILD_WIZARD=OFF \
|
||||
-D BUILD_UNITTESTS=ON \
|
||||
-D GTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||
-D GMOCK_ROOT="${GOOGLETEST_DIR}" \
|
||||
..
|
||||
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
|
||||
if [ -z $APPVEYOR ]; then
|
||||
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"
|
||||
fi
|
||||
|
||||
# Bullet
|
||||
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"
|
||||
|
||||
# FFmpeg
|
||||
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" \
|
||||
"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"
|
||||
|
||||
# MyGUI
|
||||
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"
|
||||
|
||||
if [ -n "$PDBS" ]; then
|
||||
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"
|
||||
fi
|
||||
|
||||
# OpenAL
|
||||
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"
|
||||
|
||||
# OSG
|
||||
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"
|
||||
|
||||
if [ -n "$PDBS" ]; then
|
||||
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"
|
||||
fi
|
||||
|
||||
# SDL2
|
||||
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"
|
||||
|
||||
# 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
|
||||
cd googletest
|
||||
mkdir build
|
||||
cd build
|
||||
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_INSTALL_PREFIX=. \
|
||||
-D CMAKE_INSTALL_PREFIX="${GOOGLETEST_DIR}" \
|
||||
-G "${GENERATOR}" \
|
||||
..
|
||||
cmake --build . --config "${CONFIGURATION}"
|
||||
cmake --build . --target install --config "${CONFIGURATION}"
|
||||
cmake --build . --config "${CONFIGURATION}" -- -j $(nproc)
|
||||
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")
|
||||
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
|
||||
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}\" \"\")
|
||||
" COMPONENT Runtime)
|
||||
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'
|
||||
# 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(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
||||
loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
||||
loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
|
||||
int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game");
|
||||
if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)
|
||||
unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);
|
||||
|
@ -112,6 +113,7 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
|
||||
}
|
||||
loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
|
||||
loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
|
||||
|
||||
const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain");
|
||||
const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain");
|
||||
|
@ -200,6 +202,7 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
|
||||
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
||||
saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
||||
saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
|
||||
int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex();
|
||||
if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game"))
|
||||
mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex);
|
||||
|
@ -220,6 +223,7 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
|
||||
saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
|
||||
saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
|
||||
saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
|
||||
|
||||
const bool distantTerrain = mEngineSettings.getBool("distant terrain", "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)
|
||||
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
|
||||
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()
|
||||
|
||||
if (MSVC)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <SDL.h>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/debug/gldebug.hpp>
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
|
@ -498,7 +499,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
|||
bool fullscreen = settings.getBool("fullscreen", "Video");
|
||||
bool windowBorder = settings.getBool("window border", "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),
|
||||
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_ALPHA_SIZE, 0));
|
||||
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)
|
||||
{
|
||||
|
@ -531,62 +534,80 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
|||
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);
|
||||
if (!mWindow)
|
||||
while (!mWindow)
|
||||
{
|
||||
// Try with a lower AA
|
||||
if (antialiasing > 0)
|
||||
mWindow = SDL_CreateWindow("OpenMW", pos_x, pos_y, width, height, flags);
|
||||
if (!mWindow)
|
||||
{
|
||||
Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2;
|
||||
antialiasing /= 2;
|
||||
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Failed to create SDL window: " << SDL_GetError();
|
||||
throw std::runtime_error(error.str());
|
||||
// Try with a lower AA
|
||||
if (antialiasing > 0)
|
||||
{
|
||||
Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2;
|
||||
antialiasing /= 2;
|
||||
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
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();
|
||||
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->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()
|
||||
|
|
|
@ -941,6 +941,9 @@ public:
|
|||
if (!mBook)
|
||||
return;
|
||||
|
||||
if (mPage >= mBook->mPages.size())
|
||||
return;
|
||||
|
||||
dirtyFocusItem ();
|
||||
|
||||
mFocusItem = 0;
|
||||
|
@ -952,6 +955,9 @@ public:
|
|||
if (!mBook)
|
||||
return;
|
||||
|
||||
if (mPage >= mBook->mPages.size())
|
||||
return;
|
||||
|
||||
left -= mCroppedParent->getAbsoluteLeft ();
|
||||
top -= mCroppedParent->getAbsoluteTop ();
|
||||
|
||||
|
@ -988,6 +994,9 @@ public:
|
|||
if (!mBook)
|
||||
return;
|
||||
|
||||
if (mPage >= mBook->mPages.size())
|
||||
return;
|
||||
|
||||
// work around inconsistency in MyGUI where the mouse press coordinates aren't
|
||||
// transformed by the current Layer (even though mouse *move* events are).
|
||||
MyGUI::IntPoint pos (left, top);
|
||||
|
@ -1013,6 +1022,9 @@ public:
|
|||
if (!mBook)
|
||||
return;
|
||||
|
||||
if (mPage >= mBook->mPages.size())
|
||||
return;
|
||||
|
||||
// work around inconsistency in MyGUI where the mouse release coordinates aren't
|
||||
// transformed by the current Layer (even though mouse *move* events are).
|
||||
MyGUI::IntPoint pos (left, top);
|
||||
|
|
|
@ -739,5 +739,24 @@ namespace MWGui
|
|||
{
|
||||
mAddEffectDialog.setConstantEffect(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
|
||||
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
|
||||
price *= 1 + static_cast<int>(followers.size());
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/misc/mathutil.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include "aicombataction.hpp"
|
||||
#include "aifollow.hpp"
|
||||
#include "aipursue.hpp"
|
||||
#include "aiwander.hpp"
|
||||
#include "actor.hpp"
|
||||
#include "summoning.hpp"
|
||||
#include "combat.hpp"
|
||||
|
@ -424,7 +426,7 @@ namespace MWMechanics
|
|||
const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());
|
||||
float sqrDist = (actor1Pos - actor2Pos).length2();
|
||||
|
||||
if (sqrDist > maxDistance*maxDistance)
|
||||
if (sqrDist > std::min(maxDistance * maxDistance, sqrHeadTrackDistance))
|
||||
return;
|
||||
|
||||
// stop tracking when target is behind the actor
|
||||
|
@ -432,10 +434,7 @@ namespace MWMechanics
|
|||
osg::Vec3f targetDirection(actor2Pos - actor1Pos);
|
||||
actorDirection.z() = 0;
|
||||
targetDirection.z() = 0;
|
||||
actorDirection.normalize();
|
||||
targetDirection.normalize();
|
||||
if (std::acos(actorDirection * targetDirection) < osg::DegreesToRadians(90.f)
|
||||
&& sqrDist <= sqrHeadTrackDistance
|
||||
if (actorDirection * targetDirection > 0
|
||||
&& 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))
|
||||
{
|
||||
|
@ -473,6 +472,9 @@ namespace MWMechanics
|
|||
|
||||
void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (mSmoothMovement)
|
||||
return;
|
||||
|
||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||
MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||
|
||||
|
@ -481,9 +483,10 @@ namespace MWMechanics
|
|||
osg::Vec3f targetPos = seq.getActivePackage().getDestination();
|
||||
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||
float distance = (targetPos - actorPos).length();
|
||||
|
||||
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);
|
||||
movement.mPosition[0] *= speedCoef;
|
||||
movement.mPosition[1] *= speedCoef;
|
||||
|
@ -587,8 +590,11 @@ namespace MWMechanics
|
|||
|
||||
if (!actorState.isTurningToPlayer())
|
||||
{
|
||||
actorState.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
|
||||
actorState.setTurningToPlayer(true);
|
||||
float angle = std::atan2(dir.x(), dir.y());
|
||||
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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
if(!paused)
|
||||
|
@ -1769,14 +1900,12 @@ namespace MWMechanics
|
|||
|
||||
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
|
||||
bool inCombatOrPursue = stats.getAiSequence().isInCombat() || stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue);
|
||||
|
||||
// 1. Unconsious actor can not track target
|
||||
// 2. Actors in combat and pursue mode do not bother to headtrack
|
||||
// 3. Player character does not use headtracking in the 1st-person view
|
||||
if (!stats.getKnockedDown() &&
|
||||
!stats.getAiSequence().isInCombat() &&
|
||||
!stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) &&
|
||||
!firstPersonPlayer)
|
||||
if (!stats.getKnockedDown() && !firstPersonPlayer && !inCombatOrPursue)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1824,6 +1964,10 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
static const bool avoidCollisions = Settings::Manager::getBool("NPCs avoid collisions", "Game");
|
||||
if (avoidCollisions)
|
||||
predictAndAvoidCollisions();
|
||||
|
||||
timerUpdateAITargets += duration;
|
||||
timerUpdateHeadTrack += duration;
|
||||
timerUpdateEquippedLight += duration;
|
||||
|
@ -2053,10 +2197,11 @@ namespace MWMechanics
|
|||
|
||||
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())
|
||||
{
|
||||
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sleep || iter->first == player)
|
||||
restoreDynamicStats(iter->first, hours, sleep);
|
||||
|
@ -2073,13 +2218,14 @@ namespace MWMechanics
|
|||
if (iter->first.getClass().isNpc())
|
||||
calculateNpcStatModifiers(iter->first, duration);
|
||||
|
||||
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
|
||||
|
||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);
|
||||
if (animation)
|
||||
{
|
||||
animation->removeEffects();
|
||||
MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fastForwardAi();
|
||||
|
|
|
@ -63,6 +63,8 @@ namespace MWMechanics
|
|||
|
||||
void purgeSpellEffects (int casterActorId);
|
||||
|
||||
void predictAndAvoidCollisions();
|
||||
|
||||
public:
|
||||
|
||||
Actors();
|
||||
|
@ -209,6 +211,7 @@ namespace MWMechanics
|
|||
float mTimerDisposeSummonsCorpses;
|
||||
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.getMovementSettings(actor).mPosition[1] = 1;
|
||||
smoothTurn(actor, -180, 0);
|
||||
smoothTurn(actor, -osg::PI / 2, 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include <components/esm/aisequence.hpp>
|
||||
|
||||
#include <components/misc/mathutil.hpp>
|
||||
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
|
||||
#include "../mwphysics/collisiontype.hpp"
|
||||
|
@ -240,10 +242,6 @@ namespace MWMechanics
|
|||
|
||||
if (storage.mReadyToAttack)
|
||||
{
|
||||
storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target);
|
||||
// start new attack
|
||||
storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat);
|
||||
|
||||
if (isRangedCombat)
|
||||
{
|
||||
// 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[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;
|
||||
}
|
||||
|
@ -372,9 +374,13 @@ namespace MWMechanics
|
|||
void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage)
|
||||
{
|
||||
// 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);
|
||||
actorMovementSettings.mPosition[0] = storage.mMovement.mPosition[0];
|
||||
actorMovementSettings.mPosition[1] = storage.mMovement.mPosition[1];
|
||||
actorMovementSettings.mPosition[0] = movement.x();
|
||||
actorMovementSettings.mPosition[1] = movement.y();
|
||||
actorMovementSettings.mPosition[2] = storage.mMovement.mPosition[2];
|
||||
|
||||
rotateActorOnAxis(actor, 2, actorMovementSettings, storage);
|
||||
|
@ -385,26 +391,11 @@ namespace MWMechanics
|
|||
MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage)
|
||||
{
|
||||
actorMovementSettings.mRotation[axis] = 0;
|
||||
float& targetAngleRadians = storage.mMovement.mRotation[axis];
|
||||
if (targetAngleRadians != 0)
|
||||
{
|
||||
// Some attack animations contain small amount of movement.
|
||||
// Since we use cone shapes for melee, we can use a threshold to avoid jittering
|
||||
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;
|
||||
}
|
||||
}
|
||||
bool isRangedCombat = false;
|
||||
storage.mCurrentAction->getCombatRange(isRangedCombat);
|
||||
float eps = isRangedCombat ? osg::DegreesToRadians(0.5) : osg::DegreesToRadians(3.f);
|
||||
float targetAngleRadians = storage.mMovement.mRotation[axis];
|
||||
smoothTurn(actor, targetAngleRadians, axis, eps);
|
||||
}
|
||||
|
||||
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
|
||||
else if (actor.getClass().isBipedal(actor))
|
||||
{
|
||||
// apply sideway movement (kind of dodging) with some probability
|
||||
// if actor is within range of target's weapon
|
||||
if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25)
|
||||
float moveDuration = 0;
|
||||
float angleToTarget = Misc::normalizeAngle(mMovement.mRotation[2] - actor.getRefData().getPosition().rot[2]);
|
||||
// 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
|
||||
mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
|
||||
mTimerCombatMove = moveDuration;
|
||||
mCombatMove = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <components/esm/loadmgef.hpp>
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
#include <components/misc/coordinateconverter.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/world.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.
|
||||
if (isNearInactiveCell(position))
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
||||
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
|
||||
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);
|
||||
|
||||
const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front();
|
||||
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
|
||||
evadeObstacles(actor);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
namespace MWMechanics
|
||||
{
|
||||
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
|
||||
static const int DESTINATION_TOLERANCE = 64;
|
||||
|
@ -96,6 +96,7 @@ namespace MWMechanics
|
|||
|
||||
void stopMovement(const MWWorld::Ptr& actor)
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 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)
|
||||
{
|
||||
// Check if an idle actor is too close to a door - if so start walking
|
||||
storage.mDoorCheckDuration += duration;
|
||||
// Check if an idle actor is too far from all allowed nodes or too close to a door - if so start walking.
|
||||
storage.mCheckIdlePositionTimer += duration;
|
||||
|
||||
if (storage.mDoorCheckDuration >= DOOR_CHECK_INTERVAL)
|
||||
if (storage.mCheckIdlePositionTimer >= IDLE_POSITION_CHECK_INTERVAL && !isStationary())
|
||||
{
|
||||
storage.mDoorCheckDuration = 0; // restart timer
|
||||
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
|
||||
if (mDistance && // actor is not intended to be stationary
|
||||
proximityToDoor(actor, distance*1.6f))
|
||||
storage.mCheckIdlePositionTimer = 0; // restart timer
|
||||
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 1.6f;
|
||||
if (proximityToDoor(actor, distance) || !isNearAllowedNode(actor, storage, distance))
|
||||
{
|
||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||
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)
|
||||
{
|
||||
// Is there no destination or are we there yet?
|
||||
|
@ -468,6 +482,9 @@ namespace MWMechanics
|
|||
|
||||
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();
|
||||
storage.mIdleAnimation = idleAnimation;
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace MWMechanics
|
|||
ESM::Pathgrid::Point mCurrentNode;
|
||||
bool mTrimCurrentNode;
|
||||
|
||||
float mDoorCheckDuration;
|
||||
float mCheckIdlePositionTimer;
|
||||
int mStuckCount;
|
||||
|
||||
AiWanderStorage():
|
||||
|
@ -66,7 +66,7 @@ namespace MWMechanics
|
|||
mPopulateAvailableNodes(true),
|
||||
mAllowedNodes(),
|
||||
mTrimCurrentNode(false),
|
||||
mDoorCheckDuration(0), // TODO: maybe no longer needed
|
||||
mCheckIdlePositionTimer(0),
|
||||
mStuckCount(0)
|
||||
{};
|
||||
|
||||
|
@ -117,6 +117,8 @@ namespace MWMechanics
|
|||
return mDestination;
|
||||
}
|
||||
|
||||
bool isStationary() const { return mDistance == 0; }
|
||||
|
||||
private:
|
||||
void stopWalking(const MWWorld::Ptr& actor);
|
||||
|
||||
|
@ -137,6 +139,7 @@ namespace MWMechanics
|
|||
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
|
||||
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
|
||||
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 mDuration;
|
||||
|
|
|
@ -1963,6 +1963,50 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
if (isPlayer && !isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f)
|
||||
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();
|
||||
bool canMove = cls.getMaxSpeed(mPtr) > 0;
|
||||
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);
|
||||
else
|
||||
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4);
|
||||
if (smoothMovement && !isPlayer && !inwater)
|
||||
mAnimation->setUpperBodyYawRadians(mAnimation->getUpperBodyYawRadians() + mAnimation->getHeadYaw() / 2);
|
||||
|
||||
speed = cls.getCurrentSpeed(mPtr);
|
||||
vec.x() *= speed;
|
||||
|
@ -2185,13 +2231,11 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
: (sneak ? CharState_SneakBack
|
||||
: (isrunning ? CharState_RunBack : CharState_WalkBack)));
|
||||
}
|
||||
else if (effectiveRotation != 0.0f)
|
||||
else
|
||||
{
|
||||
// Do not play turning animation for player if rotation speed is very slow.
|
||||
// Actual threshold should take framerate in account.
|
||||
float rotationThreshold = 0.f;
|
||||
if (isPlayer)
|
||||
rotationThreshold = 0.015 * 60 * duration;
|
||||
float rotationThreshold = (isPlayer ? 0.015f : 0.001f) * 60 * duration;
|
||||
|
||||
// It seems only bipedal actors use turning animations.
|
||||
// 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()
|
||||
{
|
||||
float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition;
|
||||
|
||||
if (move[1] && !move[0]) // forward-backward
|
||||
if (std::abs(move[1]) > std::abs(move[0]) + 0.2f) // forward-backward
|
||||
mAttackType = "thrust";
|
||||
else if (move[0] && !move[1]) //sideway
|
||||
else if (std::abs(move[0]) > std::abs(move[1]) + 0.2f) // sideway
|
||||
mAttackType = "slash";
|
||||
else
|
||||
mAttackType = "chop";
|
||||
|
@ -2893,19 +2936,21 @@ void CharacterController::updateHeadTracking(float duration)
|
|||
return;
|
||||
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());
|
||||
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);
|
||||
zAngleRadians = std::atan2(actorDirection.x(), actorDirection.y()) - std::atan2(direction.x(), direction.y());
|
||||
xAngleRadians = std::asin(direction.z());
|
||||
}
|
||||
|
||||
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;
|
||||
factor = std::min(factor, 1.f);
|
||||
xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngleRadians);
|
||||
zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngleRadians);
|
||||
xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * xAngleRadians;
|
||||
zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * zAngleRadians;
|
||||
|
||||
mAnimation->setHeadPitch(xAngleRadians);
|
||||
mAnimation->setHeadYaw(zAngleRadians);
|
||||
|
|
|
@ -196,6 +196,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
|||
float mTimeUntilWake;
|
||||
|
||||
bool mIsMovingBackward;
|
||||
osg::Vec2f mSmoothedSpeed;
|
||||
|
||||
void setAttackTypeBasedOnMovement();
|
||||
|
||||
|
|
|
@ -88,6 +88,24 @@ namespace
|
|||
const auto halfExtents = world->getHalfExtents(actor);
|
||||
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
|
||||
|
@ -286,6 +304,11 @@ namespace MWMechanics
|
|||
while (mPath.size() > 1 && sqrDistanceIgnoreZ(mPath.front(), position) < pointTolerance * pointTolerance)
|
||||
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)
|
||||
mPath.pop_front();
|
||||
}
|
||||
|
|
|
@ -27,7 +27,8 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
|
|||
|
||||
// reduce number of uses left
|
||||
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);
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ namespace MWMechanics
|
|||
!lock.getClass().hasToolTip(lock)) //If it's unlocked or can not be unlocked back out immediately
|
||||
return;
|
||||
|
||||
int uses = lockpick.getClass().getItemHealth(lockpick);
|
||||
if (uses == 0)
|
||||
return;
|
||||
|
||||
int lockStrength = lock.getCellRef().getLockLevel();
|
||||
|
||||
float pickQuality = lockpick.get<ESM::Lockpick>()->mBase->mData.mQuality;
|
||||
|
@ -61,9 +65,7 @@ namespace MWMechanics
|
|||
resultMessage = "#{sLockFail}";
|
||||
}
|
||||
|
||||
int uses = lockpick.getClass().getItemHealth(lockpick);
|
||||
--uses;
|
||||
lockpick.getCellRef().setCharge(uses);
|
||||
lockpick.getCellRef().setCharge(uses-1);
|
||||
if (!uses)
|
||||
lockpick.getContainerStore()->remove(lockpick, 1, mActor);
|
||||
}
|
||||
|
@ -71,7 +73,11 @@ namespace MWMechanics
|
|||
void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe,
|
||||
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;
|
||||
|
||||
float probeQuality = probe.get<ESM::Probe>()->mBase->mData.mQuality;
|
||||
|
@ -104,9 +110,7 @@ namespace MWMechanics
|
|||
resultMessage = "#{sTrapFail}";
|
||||
}
|
||||
|
||||
int uses = probe.getClass().getItemHealth(probe);
|
||||
--uses;
|
||||
probe.getCellRef().setCharge(uses);
|
||||
probe.getCellRef().setCharge(uses-1);
|
||||
if (!uses)
|
||||
probe.getContainerStore()->remove(probe, 1, mActor);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "creaturestats.hpp"
|
||||
#include "spellutil.hpp"
|
||||
|
||||
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;
|
||||
|
||||
CreatureStats& stats = target.getClass().getCreatureStats(target);
|
||||
|
@ -62,13 +63,27 @@ namespace MWMechanics
|
|||
if (Misc::Rng::roll0to99() >= chance)
|
||||
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);
|
||||
if (animation && !absorbStatic->mModel.empty())
|
||||
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
|
||||
DynamicStat<float> magicka = stats.getMagicka();
|
||||
magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost);
|
||||
magicka.setCurrent(magicka.getCurrent() + spellCost);
|
||||
stats.setMagicka(magicka);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
#ifndef MWMECHANICS_SPELLABSORPTION_H
|
||||
#define MWMECHANICS_SPELLABSORPTION_H
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Spell;
|
||||
}
|
||||
#include <string>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
@ -14,7 +11,7 @@ namespace MWWorld
|
|||
namespace MWMechanics
|
||||
{
|
||||
// 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
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace MWMechanics
|
|||
// effects, we display a "can't re-cast" message
|
||||
|
||||
// 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;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "steering.hpp"
|
||||
|
||||
#include <components/misc/mathutil.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
|
@ -12,19 +15,8 @@ namespace MWMechanics
|
|||
|
||||
bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians)
|
||||
{
|
||||
float currentAngle (actor.getRefData().getPosition().rot[axis]);
|
||||
float diff (targetAngleRadians - currentAngle);
|
||||
if (std::abs(diff) >= osg::DegreesToRadians(180.f))
|
||||
{
|
||||
if (diff >= 0)
|
||||
{
|
||||
diff = diff - osg::DegreesToRadians(360.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
diff = osg::DegreesToRadians(360.f) + diff;
|
||||
}
|
||||
}
|
||||
MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor);
|
||||
float diff = Misc::normalizeAngle(targetAngleRadians - actor.getRefData().getPosition().rot[axis]);
|
||||
float absDiff = std::abs(diff);
|
||||
|
||||
// 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;
|
||||
|
||||
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)
|
||||
diff = osg::sign(diff) * limit;
|
||||
|
||||
actor.getClass().getMovementSettings(actor).mRotation[axis] = diff;
|
||||
movement.mRotation[axis] = diff;
|
||||
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
|
||||
{
|
||||
if (from == to)
|
||||
return RayCastingResult { false };
|
||||
btVector3 btFrom = Misc::Convert::toBullet(from);
|
||||
btVector3 btTo = Misc::Convert::toBullet(to);
|
||||
|
||||
|
|
|
@ -159,12 +159,20 @@ namespace MWScript
|
|||
float ay = ptr.getRefData().getPosition().rot[1];
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
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
|
||||
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)
|
||||
teleport(*it);
|
||||
|
@ -47,7 +47,9 @@ namespace MWWorld
|
|||
}
|
||||
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 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;
|
||||
MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor, followers);
|
||||
|
||||
|
@ -69,11 +71,17 @@ namespace MWWorld
|
|||
MWWorld::Ptr follower = *it;
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() <= 800*800)
|
||||
out.insert(follower);
|
||||
if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() > 800 * 800)
|
||||
continue;
|
||||
|
||||
out.emplace(follower);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,9 @@ namespace MWWorld
|
|||
/// @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);
|
||||
|
||||
/// Outputs every actor follower who is in teleport range and wasn't ordered to not enter interiors
|
||||
static void getFollowersToTeleport(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out);
|
||||
/// @param includeHostiles If true, include hostile followers (which won't actually be teleported) in the output,
|
||||
/// 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.
|
||||
/// \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.
|
||||
/// \param activeOnly do non search inactive cells.
|
||||
/// \param activeOnly do not search inactive cells.
|
||||
|
||||
Ptr searchPtrViaActorId (int actorId) override;
|
||||
///< Search is limited to the active cells.
|
||||
|
|
|
@ -244,6 +244,7 @@ namespace
|
|||
void init(Nif::Named& value)
|
||||
{
|
||||
value.extra = Nif::ExtraPtr(nullptr);
|
||||
value.extralist = Nif::ExtraList();
|
||||
value.controller = Nif::ControllerPtr(nullptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ namespace
|
|||
"\n"
|
||||
"void bar() { foo() }\n"
|
||||
"\n"
|
||||
"#line 2 0\n"
|
||||
"#line 1 0\n"
|
||||
"\n"
|
||||
"void main() { bar() }\n";
|
||||
EXPECT_EQ(shader->getShaderSource(), expected);
|
||||
|
|
|
@ -90,7 +90,7 @@ add_component_dir (misc
|
|||
)
|
||||
|
||||
add_component_dir (debug
|
||||
debugging debuglog
|
||||
debugging debuglog gldebug
|
||||
)
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
ExtraPtr next; // Next extra data record in the list
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
next.read(nif);
|
||||
nif->getUInt(); // Size of the record
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||
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); }
|
||||
|
@ -44,18 +50,23 @@ class Named : public Record
|
|||
public:
|
||||
std::string name;
|
||||
ExtraPtr extra;
|
||||
ExtraList extralist;
|
||||
ControllerPtr controller;
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void post(NIFFile *nif)
|
||||
{
|
||||
extra.post(nif);
|
||||
extralist.post(nif);
|
||||
controller.post(nif);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,16 +14,31 @@ namespace Nif
|
|||
if (external)
|
||||
filename = nif->getString();
|
||||
else
|
||||
internal = nif->getChar();
|
||||
|
||||
if (!external && internal)
|
||||
{
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,3))
|
||||
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);
|
||||
}
|
||||
|
||||
pixel = nif->getUInt();
|
||||
mipmap = nif->getUInt();
|
||||
alpha = nif->getUInt();
|
||||
|
||||
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)
|
||||
|
@ -79,6 +94,12 @@ namespace Nif
|
|||
NiParticleModifier::read(nif);
|
||||
|
||||
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)
|
||||
|
|
|
@ -97,7 +97,10 @@ namespace Nif
|
|||
// 01: Diffuse
|
||||
// 10: Specular
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -110,6 +113,8 @@ namespace Nif
|
|||
void NiLookAtController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
lookAtFlags = nif->getUShort();
|
||||
target.read(nif);
|
||||
}
|
||||
|
||||
|
@ -165,25 +170,13 @@ namespace Nif
|
|||
data.post(nif);
|
||||
}
|
||||
|
||||
void NiAlphaController::read(NIFStream *nif)
|
||||
void NiFloatInterpController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
data.read(nif);
|
||||
}
|
||||
|
||||
void NiAlphaController::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)
|
||||
void NiFloatInterpController::post(NIFFile *nif)
|
||||
{
|
||||
Controller::post(nif);
|
||||
data.post(nif);
|
||||
|
@ -192,6 +185,8 @@ namespace Nif
|
|||
void NiGeomMorpherController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_OB_OLD)
|
||||
/*bool updateNormals = !!*/nif->getUShort();
|
||||
data.read(nif);
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
||||
/*bool alwaysActive = */nif->getChar(); // Always 0
|
||||
|
@ -219,8 +214,11 @@ namespace Nif
|
|||
{
|
||||
Controller::read(nif);
|
||||
mTexSlot = nif->getUInt();
|
||||
/*unknown=*/nif->getUInt();/*0?*/
|
||||
mDelta = nif->getFloat();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||
{
|
||||
timeStart = nif->getFloat();
|
||||
mDelta = nif->getFloat();
|
||||
}
|
||||
mSources.read(nif);
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ class NiLookAtController : public Controller
|
|||
{
|
||||
public:
|
||||
NodePtr target;
|
||||
unsigned short lookAtFlags{0};
|
||||
|
||||
void read(NIFStream *nif);
|
||||
void post(NIFFile *nif);
|
||||
|
@ -142,23 +143,16 @@ public:
|
|||
void post(NIFFile *nif);
|
||||
};
|
||||
|
||||
class NiAlphaController : public Controller
|
||||
struct NiFloatInterpController : public Controller
|
||||
{
|
||||
public:
|
||||
NiFloatDataPtr data;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
void post(NIFFile *nif);
|
||||
};
|
||||
|
||||
class NiRollController : public Controller
|
||||
{
|
||||
public:
|
||||
NiFloatDataPtr data;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
void post(NIFFile *nif);
|
||||
};
|
||||
class NiAlphaController : public NiFloatInterpController { };
|
||||
class NiRollController : public NiFloatInterpController { };
|
||||
|
||||
class NiGeomMorpherController : public Controller
|
||||
{
|
||||
|
|
|
@ -33,13 +33,33 @@ void NiSkinInstance::post(NIFFile *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();
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
nif->skip(2); // Keep flags and compress flags
|
||||
|
||||
if (nif->getBoolean())
|
||||
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())
|
||||
{
|
||||
nif->getVector3s(normals, verts);
|
||||
if (dataFlags & 0x1000)
|
||||
{
|
||||
nif->getVector3s(tangents, verts);
|
||||
nif->getVector3s(bitangents, verts);
|
||||
}
|
||||
}
|
||||
|
||||
center = nif->getVector3();
|
||||
radius = nif->getFloat();
|
||||
|
@ -47,14 +67,27 @@ void NiGeometryData::read(NIFStream *nif)
|
|||
if (nif->getBoolean())
|
||||
nif->getVector4s(colors, verts);
|
||||
|
||||
// In Morrowind this field only corresponds to the number of UV sets.
|
||||
// NifTools research is inaccurate.
|
||||
int uvs = nif->getUShort();
|
||||
|
||||
if(nif->getInt())
|
||||
// Only the first 6 bits are used as a count. I think the rest are
|
||||
// flags of some sort.
|
||||
unsigned int numUVs = dataFlags;
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||
{
|
||||
uvlist.resize(uvs);
|
||||
for(int i = 0;i < uvs;i++)
|
||||
numUVs = nif->getUShort();
|
||||
// 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);
|
||||
// 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)
|
||||
|
@ -75,13 +114,17 @@ void NiTriShapeData::read(NIFStream *nif)
|
|||
// We have three times as many vertices as triangles, so this
|
||||
// is always equal to tris*3.
|
||||
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
|
||||
// vertices. We don't actually need need this for anything, so
|
||||
// just skip it.
|
||||
int verts = nif->getUShort();
|
||||
for(int i=0;i < verts;i++)
|
||||
unsigned short verts = nif->getUShort();
|
||||
for (unsigned short i=0; i < verts; i++)
|
||||
{
|
||||
// Number of vertices matching vertex 'i'
|
||||
int num = nif->getUShort();
|
||||
|
@ -101,7 +144,11 @@ void NiTriStripsData::read(NIFStream *nif)
|
|||
std::vector<unsigned short> lengths;
|
||||
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;
|
||||
|
||||
strips.resize(numStrips);
|
||||
|
@ -140,27 +187,37 @@ void NiAutoNormalParticlesData::read(NIFStream *nif)
|
|||
NiGeometryData::read(nif);
|
||||
|
||||
// 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();
|
||||
|
||||
// Particle sizes
|
||||
if (nif->getBoolean())
|
||||
{
|
||||
// Particle sizes
|
||||
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)
|
||||
{
|
||||
NiAutoNormalParticlesData::read(nif);
|
||||
|
||||
if (nif->getBoolean())
|
||||
{
|
||||
// Rotation quaternions.
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0) && nif->getBoolean())
|
||||
nif->getQuaternions(rotations, vertices.size());
|
||||
}
|
||||
}
|
||||
|
||||
void NiPosData::read(NIFStream *nif)
|
||||
|
@ -188,12 +245,27 @@ void NiPixelData::read(NIFStream *nif)
|
|||
{
|
||||
fmt = (Format)nif->getUInt();
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
colorMask[i] = nif->getUInt();
|
||||
bpp = nif->getUInt();
|
||||
if (nif->getVersion() < NIFStream::generateVersion(10,4,0,2))
|
||||
{
|
||||
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);
|
||||
|
||||
numberOfMipmaps = nif->getUInt();
|
||||
|
@ -213,8 +285,10 @@ void NiPixelData::read(NIFStream *nif)
|
|||
|
||||
// Read the data
|
||||
unsigned int numPixels = nif->getUInt();
|
||||
if (numPixels)
|
||||
nif->getUChars(data, numPixels);
|
||||
bool hasFaces = nif->getVersion() >= NIFStream::generateVersion(10,4,0,2);
|
||||
unsigned int numFaces = hasFaces ? nif->getUInt() : 1;
|
||||
if (numPixels && numFaces)
|
||||
nif->getUChars(data, numPixels * numFaces);
|
||||
}
|
||||
|
||||
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))
|
||||
nif->skip(4); // NiSkinPartition link
|
||||
|
||||
// Has vertex weights flag
|
||||
if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean())
|
||||
return;
|
||||
|
||||
bones.resize(boneNum);
|
||||
for (BoneInfo &bi : bones)
|
||||
{
|
||||
|
@ -272,7 +350,7 @@ void NiMorphData::read(NIFStream *nif)
|
|||
{
|
||||
int morphCount = nif->getInt();
|
||||
int vertCount = nif->getInt();
|
||||
/*relative targets?*/nif->getChar();
|
||||
nif->getChar(); // Relative targets, always 1
|
||||
|
||||
mMorphs.resize(morphCount);
|
||||
for(int i = 0;i < morphCount;i++)
|
||||
|
@ -290,7 +368,8 @@ void NiKeyframeData::read(NIFStream *nif)
|
|||
if(mRotations->mInterpolationType == InterpolationType_XYZ)
|
||||
{
|
||||
//Chomp unused float
|
||||
nif->getFloat();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
||||
nif->getFloat();
|
||||
mXRotations = std::make_shared<FloatKeyMap>();
|
||||
mYRotations = std::make_shared<FloatKeyMap>();
|
||||
mZRotations = std::make_shared<FloatKeyMap>();
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Nif
|
|||
class NiGeometryData : public Record
|
||||
{
|
||||
public:
|
||||
std::vector<osg::Vec3f> vertices, normals;
|
||||
std::vector<osg::Vec3f> vertices, normals, tangents, bitangents;
|
||||
std::vector<osg::Vec4f> colors;
|
||||
std::vector< std::vector<osg::Vec2f> > uvlist;
|
||||
osg::Vec3f center;
|
||||
|
@ -73,13 +73,13 @@ struct NiLinesData : public NiGeometryData
|
|||
class NiAutoNormalParticlesData : public NiGeometryData
|
||||
{
|
||||
public:
|
||||
int numParticles;
|
||||
|
||||
float particleRadius;
|
||||
int numParticles{0};
|
||||
|
||||
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);
|
||||
};
|
||||
|
@ -87,8 +87,6 @@ public:
|
|||
class NiRotatingParticlesData : public NiAutoNormalParticlesData
|
||||
{
|
||||
public:
|
||||
std::vector<osg::Quat> rotations;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
};
|
||||
|
||||
|
@ -133,7 +131,8 @@ public:
|
|||
Format fmt;
|
||||
|
||||
unsigned int colorMask[4];
|
||||
unsigned int bpp;
|
||||
unsigned int bpp, pixelTiling{0};
|
||||
bool sRGB{false};
|
||||
|
||||
NiPalettePtr palette;
|
||||
unsigned int numberOfMipmaps;
|
||||
|
|
|
@ -28,6 +28,10 @@ void NiTextureEffect::read(NIFStream *nif)
|
|||
// Texture Filtering
|
||||
nif->skip(4);
|
||||
|
||||
// Max anisotropy samples
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20,5,0,4))
|
||||
nif->skip(2);
|
||||
|
||||
clamp = nif->getUInt();
|
||||
|
||||
textureType = (TextureType)nif->getUInt();
|
||||
|
@ -36,14 +40,12 @@ void NiTextureEffect::read(NIFStream *nif)
|
|||
|
||||
texture.read(nif);
|
||||
|
||||
/*
|
||||
byte = 0
|
||||
vector4 = [1,0,0,0]
|
||||
short = 0
|
||||
short = -75
|
||||
short = 0
|
||||
*/
|
||||
nif->skip(23);
|
||||
nif->skip(1); // Use clipping plane
|
||||
nif->skip(16); // Clipping plane dimensions vector
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,2,0,0))
|
||||
nif->skip(4); // PS2-specific shorts
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,1,0,12))
|
||||
nif->skip(2); // Unknown short
|
||||
}
|
||||
|
||||
void NiTextureEffect::post(NIFFile *nif)
|
||||
|
|
|
@ -34,6 +34,9 @@ struct NiDynamicEffect : public Node
|
|||
void read(NIFStream *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();
|
||||
for (unsigned int i=0; i<numAffectedNodes; ++i)
|
||||
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
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
#endif
|
||||
|
|
|
@ -16,108 +16,106 @@ NIFFile::NIFFile(Files::IStreamPtr stream, const std::string &name)
|
|||
|
||||
NIFFile::~NIFFile()
|
||||
{
|
||||
for (std::vector<Record*>::iterator it = records.begin() ; it != records.end(); ++it)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
for (Record* record : records)
|
||||
delete record;
|
||||
}
|
||||
|
||||
template <typename NodeType> static Record* construct() { return new NodeType; }
|
||||
|
||||
struct RecordFactoryEntry {
|
||||
|
||||
typedef Record* (*create_t) ();
|
||||
using create_t = Record* (*)();
|
||||
|
||||
create_t mCreate;
|
||||
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.
|
||||
static std::map<std::string,RecordFactoryEntry> makeFactory()
|
||||
{
|
||||
std::map<std::string,RecordFactoryEntry> newFactory;
|
||||
newFactory.insert(makeEntry("NiNode", &construct <NiNode> , RC_NiNode ));
|
||||
newFactory.insert(makeEntry("NiSwitchNode", &construct <NiSwitchNode> , RC_NiSwitchNode ));
|
||||
newFactory.insert(makeEntry("NiLODNode", &construct <NiLODNode> , RC_NiLODNode ));
|
||||
newFactory.insert(makeEntry("AvoidNode", &construct <NiNode> , RC_AvoidNode ));
|
||||
newFactory.insert(makeEntry("NiCollisionSwitch", &construct <NiNode> , RC_NiCollisionSwitch ));
|
||||
newFactory.insert(makeEntry("NiBSParticleNode", &construct <NiNode> , RC_NiBSParticleNode ));
|
||||
newFactory.insert(makeEntry("NiBSAnimationNode", &construct <NiNode> , RC_NiBSAnimationNode ));
|
||||
newFactory.insert(makeEntry("NiBillboardNode", &construct <NiNode> , RC_NiBillboardNode ));
|
||||
newFactory.insert(makeEntry("NiTriShape", &construct <NiTriShape> , RC_NiTriShape ));
|
||||
newFactory.insert(makeEntry("NiTriStrips", &construct <NiTriStrips> , RC_NiTriStrips ));
|
||||
newFactory.insert(makeEntry("NiLines", &construct <NiLines> , RC_NiLines ));
|
||||
newFactory.insert(makeEntry("NiRotatingParticles", &construct <NiRotatingParticles> , RC_NiRotatingParticles ));
|
||||
newFactory.insert(makeEntry("NiAutoNormalParticles", &construct <NiAutoNormalParticles> , RC_NiAutoNormalParticles ));
|
||||
newFactory.insert(makeEntry("NiCamera", &construct <NiCamera> , RC_NiCamera ));
|
||||
newFactory.insert(makeEntry("RootCollisionNode", &construct <NiNode> , RC_RootCollisionNode ));
|
||||
newFactory.insert(makeEntry("NiTexturingProperty", &construct <NiTexturingProperty> , RC_NiTexturingProperty ));
|
||||
newFactory.insert(makeEntry("NiFogProperty", &construct <NiFogProperty> , RC_NiFogProperty ));
|
||||
newFactory.insert(makeEntry("NiMaterialProperty", &construct <NiMaterialProperty> , RC_NiMaterialProperty ));
|
||||
newFactory.insert(makeEntry("NiZBufferProperty", &construct <NiZBufferProperty> , RC_NiZBufferProperty ));
|
||||
newFactory.insert(makeEntry("NiAlphaProperty", &construct <NiAlphaProperty> , RC_NiAlphaProperty ));
|
||||
newFactory.insert(makeEntry("NiVertexColorProperty", &construct <NiVertexColorProperty> , RC_NiVertexColorProperty ));
|
||||
newFactory.insert(makeEntry("NiShadeProperty", &construct <NiShadeProperty> , RC_NiShadeProperty ));
|
||||
newFactory.insert(makeEntry("NiDitherProperty", &construct <NiDitherProperty> , RC_NiDitherProperty ));
|
||||
newFactory.insert(makeEntry("NiWireframeProperty", &construct <NiWireframeProperty> , RC_NiWireframeProperty ));
|
||||
newFactory.insert(makeEntry("NiSpecularProperty", &construct <NiSpecularProperty> , RC_NiSpecularProperty ));
|
||||
newFactory.insert(makeEntry("NiStencilProperty", &construct <NiStencilProperty> , RC_NiStencilProperty ));
|
||||
newFactory.insert(makeEntry("NiVisController", &construct <NiVisController> , RC_NiVisController ));
|
||||
newFactory.insert(makeEntry("NiGeomMorpherController", &construct <NiGeomMorpherController> , RC_NiGeomMorpherController ));
|
||||
newFactory.insert(makeEntry("NiKeyframeController", &construct <NiKeyframeController> , RC_NiKeyframeController ));
|
||||
newFactory.insert(makeEntry("NiAlphaController", &construct <NiAlphaController> , RC_NiAlphaController ));
|
||||
newFactory.insert(makeEntry("NiRollController", &construct <NiRollController> , RC_NiRollController ));
|
||||
newFactory.insert(makeEntry("NiUVController", &construct <NiUVController> , RC_NiUVController ));
|
||||
newFactory.insert(makeEntry("NiPathController", &construct <NiPathController> , RC_NiPathController ));
|
||||
newFactory.insert(makeEntry("NiMaterialColorController", &construct <NiMaterialColorController> , RC_NiMaterialColorController ));
|
||||
newFactory.insert(makeEntry("NiBSPArrayController", &construct <NiBSPArrayController> , RC_NiBSPArrayController ));
|
||||
newFactory.insert(makeEntry("NiParticleSystemController", &construct <NiParticleSystemController> , RC_NiParticleSystemController ));
|
||||
newFactory.insert(makeEntry("NiFlipController", &construct <NiFlipController> , RC_NiFlipController ));
|
||||
newFactory.insert(makeEntry("NiAmbientLight", &construct <NiLight> , RC_NiLight ));
|
||||
newFactory.insert(makeEntry("NiDirectionalLight", &construct <NiLight> , RC_NiLight ));
|
||||
newFactory.insert(makeEntry("NiPointLight", &construct <NiPointLight> , RC_NiLight ));
|
||||
newFactory.insert(makeEntry("NiSpotLight", &construct <NiSpotLight> , RC_NiLight ));
|
||||
newFactory.insert(makeEntry("NiTextureEffect", &construct <NiTextureEffect> , RC_NiTextureEffect ));
|
||||
newFactory.insert(makeEntry("NiVertWeightsExtraData", &construct <NiVertWeightsExtraData> , RC_NiVertWeightsExtraData ));
|
||||
newFactory.insert(makeEntry("NiTextKeyExtraData", &construct <NiTextKeyExtraData> , RC_NiTextKeyExtraData ));
|
||||
newFactory.insert(makeEntry("NiStringExtraData", &construct <NiStringExtraData> , RC_NiStringExtraData ));
|
||||
newFactory.insert(makeEntry("NiGravity", &construct <NiGravity> , RC_NiGravity ));
|
||||
newFactory.insert(makeEntry("NiPlanarCollider", &construct <NiPlanarCollider> , RC_NiPlanarCollider ));
|
||||
newFactory.insert(makeEntry("NiSphericalCollider", &construct <NiSphericalCollider> , RC_NiSphericalCollider ));
|
||||
newFactory.insert(makeEntry("NiParticleGrowFade", &construct <NiParticleGrowFade> , RC_NiParticleGrowFade ));
|
||||
newFactory.insert(makeEntry("NiParticleColorModifier", &construct <NiParticleColorModifier> , RC_NiParticleColorModifier ));
|
||||
newFactory.insert(makeEntry("NiParticleRotation", &construct <NiParticleRotation> , RC_NiParticleRotation ));
|
||||
newFactory.insert(makeEntry("NiFloatData", &construct <NiFloatData> , RC_NiFloatData ));
|
||||
newFactory.insert(makeEntry("NiTriShapeData", &construct <NiTriShapeData> , RC_NiTriShapeData ));
|
||||
newFactory.insert(makeEntry("NiTriStripsData", &construct <NiTriStripsData> , RC_NiTriStripsData ));
|
||||
newFactory.insert(makeEntry("NiLinesData", &construct <NiLinesData> , RC_NiLinesData ));
|
||||
newFactory.insert(makeEntry("NiVisData", &construct <NiVisData> , RC_NiVisData ));
|
||||
newFactory.insert(makeEntry("NiColorData", &construct <NiColorData> , RC_NiColorData ));
|
||||
newFactory.insert(makeEntry("NiPixelData", &construct <NiPixelData> , RC_NiPixelData ));
|
||||
newFactory.insert(makeEntry("NiMorphData", &construct <NiMorphData> , RC_NiMorphData ));
|
||||
newFactory.insert(makeEntry("NiKeyframeData", &construct <NiKeyframeData> , RC_NiKeyframeData ));
|
||||
newFactory.insert(makeEntry("NiSkinData", &construct <NiSkinData> , RC_NiSkinData ));
|
||||
newFactory.insert(makeEntry("NiUVData", &construct <NiUVData> , RC_NiUVData ));
|
||||
newFactory.insert(makeEntry("NiPosData", &construct <NiPosData> , RC_NiPosData ));
|
||||
newFactory.insert(makeEntry("NiRotatingParticlesData", &construct <NiRotatingParticlesData> , RC_NiRotatingParticlesData ));
|
||||
newFactory.insert(makeEntry("NiAutoNormalParticlesData", &construct <NiAutoNormalParticlesData> , RC_NiAutoNormalParticlesData ));
|
||||
newFactory.insert(makeEntry("NiSequenceStreamHelper", &construct <NiSequenceStreamHelper> , RC_NiSequenceStreamHelper ));
|
||||
newFactory.insert(makeEntry("NiSourceTexture", &construct <NiSourceTexture> , RC_NiSourceTexture ));
|
||||
newFactory.insert(makeEntry("NiSkinInstance", &construct <NiSkinInstance> , RC_NiSkinInstance ));
|
||||
newFactory.insert(makeEntry("NiLookAtController", &construct <NiLookAtController> , RC_NiLookAtController ));
|
||||
newFactory.insert(makeEntry("NiPalette", &construct <NiPalette> , RC_NiPalette ));
|
||||
return newFactory;
|
||||
std::map<std::string,RecordFactoryEntry> factory;
|
||||
factory["NiNode"] = {&construct <NiNode> , RC_NiNode };
|
||||
factory["NiSwitchNode"] = {&construct <NiSwitchNode> , RC_NiSwitchNode };
|
||||
factory["NiLODNode"] = {&construct <NiLODNode> , RC_NiLODNode };
|
||||
factory["AvoidNode"] = {&construct <NiNode> , RC_AvoidNode };
|
||||
factory["NiCollisionSwitch"] = {&construct <NiNode> , RC_NiCollisionSwitch };
|
||||
factory["NiBSParticleNode"] = {&construct <NiNode> , RC_NiBSParticleNode };
|
||||
factory["NiBSAnimationNode"] = {&construct <NiNode> , RC_NiBSAnimationNode };
|
||||
factory["NiBillboardNode"] = {&construct <NiNode> , RC_NiBillboardNode };
|
||||
factory["NiTriShape"] = {&construct <NiTriShape> , RC_NiTriShape };
|
||||
factory["NiTriStrips"] = {&construct <NiTriStrips> , RC_NiTriStrips };
|
||||
factory["NiLines"] = {&construct <NiLines> , RC_NiLines };
|
||||
factory["NiRotatingParticles"] = {&construct <NiRotatingParticles> , RC_NiRotatingParticles };
|
||||
factory["NiAutoNormalParticles"] = {&construct <NiAutoNormalParticles> , RC_NiAutoNormalParticles };
|
||||
factory["NiCamera"] = {&construct <NiCamera> , RC_NiCamera };
|
||||
factory["RootCollisionNode"] = {&construct <NiNode> , RC_RootCollisionNode };
|
||||
factory["NiTexturingProperty"] = {&construct <NiTexturingProperty> , RC_NiTexturingProperty };
|
||||
factory["NiFogProperty"] = {&construct <NiFogProperty> , RC_NiFogProperty };
|
||||
factory["NiMaterialProperty"] = {&construct <NiMaterialProperty> , RC_NiMaterialProperty };
|
||||
factory["NiZBufferProperty"] = {&construct <NiZBufferProperty> , RC_NiZBufferProperty };
|
||||
factory["NiAlphaProperty"] = {&construct <NiAlphaProperty> , RC_NiAlphaProperty };
|
||||
factory["NiVertexColorProperty"] = {&construct <NiVertexColorProperty> , RC_NiVertexColorProperty };
|
||||
factory["NiShadeProperty"] = {&construct <NiShadeProperty> , RC_NiShadeProperty };
|
||||
factory["NiDitherProperty"] = {&construct <NiDitherProperty> , RC_NiDitherProperty };
|
||||
factory["NiWireframeProperty"] = {&construct <NiWireframeProperty> , RC_NiWireframeProperty };
|
||||
factory["NiSpecularProperty"] = {&construct <NiSpecularProperty> , RC_NiSpecularProperty };
|
||||
factory["NiStencilProperty"] = {&construct <NiStencilProperty> , RC_NiStencilProperty };
|
||||
factory["NiVisController"] = {&construct <NiVisController> , RC_NiVisController };
|
||||
factory["NiGeomMorpherController"] = {&construct <NiGeomMorpherController> , RC_NiGeomMorpherController };
|
||||
factory["NiKeyframeController"] = {&construct <NiKeyframeController> , RC_NiKeyframeController };
|
||||
factory["NiAlphaController"] = {&construct <NiAlphaController> , RC_NiAlphaController };
|
||||
factory["NiRollController"] = {&construct <NiRollController> , RC_NiRollController };
|
||||
factory["NiUVController"] = {&construct <NiUVController> , RC_NiUVController };
|
||||
factory["NiPathController"] = {&construct <NiPathController> , RC_NiPathController };
|
||||
factory["NiMaterialColorController"] = {&construct <NiMaterialColorController> , RC_NiMaterialColorController };
|
||||
factory["NiBSPArrayController"] = {&construct <NiBSPArrayController> , RC_NiBSPArrayController };
|
||||
factory["NiParticleSystemController"] = {&construct <NiParticleSystemController> , RC_NiParticleSystemController };
|
||||
factory["NiFlipController"] = {&construct <NiFlipController> , RC_NiFlipController };
|
||||
factory["NiAmbientLight"] = {&construct <NiLight> , RC_NiLight };
|
||||
factory["NiDirectionalLight"] = {&construct <NiLight> , RC_NiLight };
|
||||
factory["NiPointLight"] = {&construct <NiPointLight> , RC_NiLight };
|
||||
factory["NiSpotLight"] = {&construct <NiSpotLight> , RC_NiLight };
|
||||
factory["NiTextureEffect"] = {&construct <NiTextureEffect> , RC_NiTextureEffect };
|
||||
factory["NiVertWeightsExtraData"] = {&construct <NiVertWeightsExtraData> , RC_NiVertWeightsExtraData };
|
||||
factory["NiTextKeyExtraData"] = {&construct <NiTextKeyExtraData> , RC_NiTextKeyExtraData };
|
||||
factory["NiStringExtraData"] = {&construct <NiStringExtraData> , RC_NiStringExtraData };
|
||||
factory["NiGravity"] = {&construct <NiGravity> , RC_NiGravity };
|
||||
factory["NiPlanarCollider"] = {&construct <NiPlanarCollider> , RC_NiPlanarCollider };
|
||||
factory["NiSphericalCollider"] = {&construct <NiSphericalCollider> , RC_NiSphericalCollider };
|
||||
factory["NiParticleGrowFade"] = {&construct <NiParticleGrowFade> , RC_NiParticleGrowFade };
|
||||
factory["NiParticleColorModifier"] = {&construct <NiParticleColorModifier> , RC_NiParticleColorModifier };
|
||||
factory["NiParticleRotation"] = {&construct <NiParticleRotation> , RC_NiParticleRotation };
|
||||
factory["NiFloatData"] = {&construct <NiFloatData> , RC_NiFloatData };
|
||||
factory["NiTriShapeData"] = {&construct <NiTriShapeData> , RC_NiTriShapeData };
|
||||
factory["NiTriStripsData"] = {&construct <NiTriStripsData> , RC_NiTriStripsData };
|
||||
factory["NiLinesData"] = {&construct <NiLinesData> , RC_NiLinesData };
|
||||
factory["NiVisData"] = {&construct <NiVisData> , RC_NiVisData };
|
||||
factory["NiColorData"] = {&construct <NiColorData> , RC_NiColorData };
|
||||
factory["NiPixelData"] = {&construct <NiPixelData> , RC_NiPixelData };
|
||||
factory["NiMorphData"] = {&construct <NiMorphData> , RC_NiMorphData };
|
||||
factory["NiKeyframeData"] = {&construct <NiKeyframeData> , RC_NiKeyframeData };
|
||||
factory["NiSkinData"] = {&construct <NiSkinData> , RC_NiSkinData };
|
||||
factory["NiUVData"] = {&construct <NiUVData> , RC_NiUVData };
|
||||
factory["NiPosData"] = {&construct <NiPosData> , RC_NiPosData };
|
||||
factory["NiRotatingParticlesData"] = {&construct <NiRotatingParticlesData> , RC_NiRotatingParticlesData };
|
||||
factory["NiAutoNormalParticlesData"] = {&construct <NiAutoNormalParticlesData> , RC_NiAutoNormalParticlesData };
|
||||
factory["NiSequenceStreamHelper"] = {&construct <NiSequenceStreamHelper> , RC_NiSequenceStreamHelper };
|
||||
factory["NiSourceTexture"] = {&construct <NiSourceTexture> , RC_NiSourceTexture };
|
||||
factory["NiSkinInstance"] = {&construct <NiSkinInstance> , RC_NiSkinInstance };
|
||||
factory["NiLookAtController"] = {&construct <NiLookAtController> , RC_NiLookAtController };
|
||||
factory["NiPalette"] = {&construct <NiPalette> , RC_NiPalette };
|
||||
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
|
||||
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.
|
||||
if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW)
|
||||
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
|
||||
size_t recNum = nif.getInt();
|
||||
size_t recNum = nif.getUInt();
|
||||
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++)
|
||||
{
|
||||
Record *r = nullptr;
|
||||
|
||||
std::string rec = nif.getString();
|
||||
std::string rec = hasRecTypeListings ? recTypes[recTypeIndices[i]] : nif.getString();
|
||||
if(rec.empty())
|
||||
{
|
||||
std::stringstream error;
|
||||
|
@ -164,6 +224,17 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
|||
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);
|
||||
|
||||
if (entry != factories.end())
|
||||
|
@ -201,8 +272,8 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
|||
}
|
||||
|
||||
// Once parsing is done, do post-processing.
|
||||
for(size_t i=0; i<recNum; i++)
|
||||
records[i]->post(this);
|
||||
for (Record* record : records)
|
||||
record->post(this);
|
||||
}
|
||||
|
||||
void NIFFile::setUseSkinning(bool skinning)
|
||||
|
|
|
@ -25,18 +25,19 @@ namespace Nif
|
|||
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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
return getSizedString();
|
||||
return getVersion() < generateVersion(20,1,0,1) ? getSizedString() : file->getString(getUInt());
|
||||
}
|
||||
|
||||
|
||||
// Convenience utility functions: get the versions of the currently read file
|
||||
unsigned int NIFStream::getVersion() const { return file->getVersion(); }
|
||||
unsigned int NIFStream::getUserVersion() const { return file->getBethVersion(); }
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
PropertyList props;
|
||||
|
||||
// Bounding box info
|
||||
bool hasBounds;
|
||||
bool hasBounds{false};
|
||||
osg::Vec3f boundPos;
|
||||
Matrix3 boundRot;
|
||||
osg::Vec3f boundXYZ; // Box size
|
||||
|
@ -39,12 +39,15 @@ public:
|
|||
{
|
||||
Named::read(nif);
|
||||
|
||||
flags = nif->getUShort();
|
||||
flags = nif->getBethVersion() <= 26 ? nif->getUShort() : nif->getUInt();
|
||||
trafo = nif->getTrafo();
|
||||
velocity = nif->getVector3();
|
||||
props.read(nif);
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||
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)
|
||||
{
|
||||
nif->getInt(); // always 1
|
||||
|
@ -52,6 +55,9 @@ public:
|
|||
boundRot = nif->getMatrix3();
|
||||
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;
|
||||
|
||||
|
@ -102,7 +108,8 @@ struct NiNode : Node
|
|||
{
|
||||
Node::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
|
||||
// occasionally get wrong orientation. Only for NiNode-s for now, but
|
||||
|
@ -130,7 +137,39 @@ struct NiNode : 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;
|
||||
MaterialData materialData;
|
||||
};
|
||||
|
||||
struct NiTriShape : NiGeometry
|
||||
|
@ -149,6 +188,7 @@ struct NiTriShape : NiGeometry
|
|||
Node::read(nif);
|
||||
data.read(nif);
|
||||
skin.read(nif);
|
||||
materialData.read(nif);
|
||||
}
|
||||
|
||||
void post(NIFFile *nif)
|
||||
|
@ -170,6 +210,7 @@ struct NiTriStrips : NiGeometry
|
|||
Node::read(nif);
|
||||
data.read(nif);
|
||||
skin.read(nif);
|
||||
materialData.read(nif);
|
||||
}
|
||||
|
||||
void post(NIFFile *nif)
|
||||
|
@ -207,6 +248,8 @@ struct NiCamera : Node
|
|||
{
|
||||
struct Camera
|
||||
{
|
||||
unsigned short cameraFlags{0};
|
||||
|
||||
// Camera frustrum
|
||||
float left, right, top, bottom, nearDist, farDist;
|
||||
|
||||
|
@ -216,15 +259,21 @@ struct NiCamera : Node
|
|||
// Level of detail modifier
|
||||
float LOD;
|
||||
|
||||
// Orthographic projection usage flag
|
||||
bool orthographic{false};
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
cameraFlags = nif->getUShort();
|
||||
left = nif->getFloat();
|
||||
right = nif->getFloat();
|
||||
top = nif->getFloat();
|
||||
bottom = nif->getFloat();
|
||||
nearDist = nif->getFloat();
|
||||
farDist = nif->getFloat();
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
orthographic = nif->getBoolean();
|
||||
vleft = nif->getFloat();
|
||||
vright = nif->getFloat();
|
||||
vtop = nif->getFloat();
|
||||
|
@ -243,6 +292,8 @@ struct NiCamera : Node
|
|||
|
||||
nif->getInt(); // -1
|
||||
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.
|
||||
struct NiSwitchNode : public NiNode
|
||||
{
|
||||
unsigned int switchFlags{0};
|
||||
unsigned int initialIndex;
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
NiNode::read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
switchFlags = nif->getUShort();
|
||||
initialIndex = nif->getUInt();
|
||||
}
|
||||
};
|
||||
|
@ -310,6 +364,12 @@ struct NiLODNode : public NiSwitchNode
|
|||
NiSwitchNode::read(nif);
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
||||
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();
|
||||
for (unsigned int i=0; i<numLodLevels; ++i)
|
||||
{
|
||||
|
|
|
@ -6,25 +6,44 @@
|
|||
namespace Nif
|
||||
{
|
||||
|
||||
void Property::read(NIFStream *nif)
|
||||
{
|
||||
Named::read(nif);
|
||||
flags = nif->getUShort();
|
||||
}
|
||||
|
||||
void NiTexturingProperty::Texture::read(NIFStream *nif)
|
||||
{
|
||||
inUse = nif->getBoolean();
|
||||
if(!inUse) return;
|
||||
|
||||
texture.read(nif);
|
||||
clamp = nif->getUInt();
|
||||
nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible
|
||||
uvSet = nif->getUInt();
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
{
|
||||
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.
|
||||
nif->skip(4);
|
||||
nif->skip(2); // Unknown short
|
||||
if (nif->getVersion() < NIFStream::generateVersion(10,4,0,2))
|
||||
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)
|
||||
|
@ -35,7 +54,10 @@ void NiTexturingProperty::Texture::post(NIFFile *nif)
|
|||
void NiTexturingProperty::read(NIFStream *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();
|
||||
|
||||
|
@ -51,32 +73,53 @@ void NiTexturingProperty::read(NIFStream *nif)
|
|||
envMapLumaBias = nif->getVector2();
|
||||
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)
|
||||
{
|
||||
Property::post(nif);
|
||||
for(int i = 0;i < 7;i++)
|
||||
for (size_t i = 0; i < textures.size(); i++)
|
||||
textures[i].post(nif);
|
||||
for (size_t i = 0; i < shaderTextures.size(); i++)
|
||||
shaderTextures[i].post(nif);
|
||||
}
|
||||
|
||||
void NiFogProperty::read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
|
||||
mFlags = nif->getUShort();
|
||||
mFogDepth = nif->getFloat();
|
||||
mColour = nif->getVector3();
|
||||
}
|
||||
|
||||
void S_MaterialProperty::read(NIFStream *nif)
|
||||
{
|
||||
ambient = nif->getVector3();
|
||||
diffuse = nif->getVector3();
|
||||
if (nif->getBethVersion() < 26)
|
||||
{
|
||||
ambient = nif->getVector3();
|
||||
diffuse = nif->getVector3();
|
||||
}
|
||||
specular = nif->getVector3();
|
||||
emissive = nif->getVector3();
|
||||
glossiness = nif->getFloat();
|
||||
alpha = nif->getFloat();
|
||||
if (nif->getBethVersion() > 21)
|
||||
emissive *= nif->getFloat();
|
||||
}
|
||||
|
||||
void S_VertexColorProperty::read(NIFStream *nif)
|
||||
|
@ -92,14 +135,29 @@ void S_AlphaProperty::read(NIFStream *nif)
|
|||
|
||||
void S_StencilProperty::read(NIFStream *nif)
|
||||
{
|
||||
enabled = nif->getChar();
|
||||
compareFunc = nif->getInt();
|
||||
stencilRef = nif->getUInt();
|
||||
stencilMask = nif->getUInt();
|
||||
failAction = nif->getInt();
|
||||
zFailAction = nif->getInt();
|
||||
zPassAction = nif->getInt();
|
||||
drawMode = nif->getInt();
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
{
|
||||
enabled = nif->getChar();
|
||||
compareFunc = nif->getInt();
|
||||
stencilRef = nif->getUInt();
|
||||
stencilMask = nif->getUInt();
|
||||
failAction = 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
|
||||
{
|
||||
|
||||
class Property : public Named
|
||||
{
|
||||
public:
|
||||
// The meaning of these depends on the actual property type.
|
||||
unsigned int flags;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
};
|
||||
class Property : public Named { };
|
||||
|
||||
class NiTexturingProperty : public Property
|
||||
{
|
||||
public:
|
||||
unsigned short flags{0u};
|
||||
|
||||
// A sub-texture
|
||||
struct Texture
|
||||
{
|
||||
|
@ -92,6 +87,7 @@ public:
|
|||
};
|
||||
|
||||
std::vector<Texture> textures;
|
||||
std::vector<Texture> shaderTextures;
|
||||
|
||||
osg::Vec2f envMapLumaBias;
|
||||
osg::Vec4f bumpMapMatrix;
|
||||
|
@ -103,28 +99,80 @@ public:
|
|||
class NiFogProperty : public Property
|
||||
{
|
||||
public:
|
||||
unsigned short mFlags;
|
||||
float mFogDepth;
|
||||
osg::Vec3f mColour;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
};
|
||||
|
||||
// These contain no other data than the 'flags' field in Property
|
||||
class NiShadeProperty : public Property { };
|
||||
class NiDitherProperty : public Property { };
|
||||
class NiZBufferProperty : public Property { };
|
||||
class NiSpecularProperty : public Property { };
|
||||
class NiWireframeProperty : public Property { };
|
||||
// These contain no other data than the 'flags' field
|
||||
struct NiShadeProperty : public Property
|
||||
{
|
||||
unsigned short flags{0u};
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
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
|
||||
template <typename T>
|
||||
struct StructPropT : Property
|
||||
{
|
||||
T data;
|
||||
unsigned short flags;
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
flags = nif->getUShort();
|
||||
data.read(nif);
|
||||
}
|
||||
};
|
||||
|
@ -132,7 +180,8 @@ struct StructPropT : Property
|
|||
struct S_MaterialProperty
|
||||
{
|
||||
// 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;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
|
@ -246,9 +295,35 @@ struct S_StencilProperty
|
|||
};
|
||||
|
||||
class NiAlphaProperty : public StructPropT<S_AlphaProperty> { };
|
||||
class NiMaterialProperty : public StructPropT<S_MaterialProperty> { };
|
||||
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
|
||||
#endif
|
||||
|
|
|
@ -101,7 +101,15 @@ enum RecordType
|
|||
RC_RootCollisionNode,
|
||||
RC_NiSphericalCollider,
|
||||
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
|
||||
|
|
|
@ -310,11 +310,6 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
|||
|
||||
RollController::RollController(const Nif::NiFloatData *data)
|
||||
: 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)
|
||||
, Controller(copy)
|
||||
, 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)
|
||||
: mTexSlot(ctrl->mTexSlot)
|
||||
: mTexSlot(0) // always affects diffuse
|
||||
, mDelta(ctrl->mDelta)
|
||||
, 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)
|
||||
: StateSetUpdater(copy, copyop)
|
||||
, Controller(copy)
|
||||
|
|
|
@ -268,11 +268,11 @@ namespace NifOsg
|
|||
{
|
||||
private:
|
||||
FloatInterpolator mData;
|
||||
double mStartingTime;
|
||||
double mStartingTime{0};
|
||||
|
||||
public:
|
||||
RollController(const Nif::NiFloatData *data);
|
||||
RollController();
|
||||
RollController() = default;
|
||||
RollController(const RollController& copy, const osg::CopyOp& copyop);
|
||||
|
||||
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
|
||||
|
@ -326,14 +326,14 @@ namespace NifOsg
|
|||
class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
|
||||
{
|
||||
private:
|
||||
int mTexSlot;
|
||||
float mDelta;
|
||||
int mTexSlot{0};
|
||||
float mDelta{0.f};
|
||||
std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;
|
||||
|
||||
public:
|
||||
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();
|
||||
FlipController() = default;
|
||||
FlipController(const FlipController& copy, const osg::CopyOp& copyop);
|
||||
|
||||
META_Object(NifOsg, FlipController)
|
||||
|
|
|
@ -799,22 +799,23 @@ namespace NifOsg
|
|||
{
|
||||
const Nif::NiFlipController* flipctrl = static_cast<const Nif::NiFlipController*>(ctrl.getPtr());
|
||||
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)
|
||||
{
|
||||
Nif::NiSourceTexturePtr st = flipctrl->mSources[i];
|
||||
if (st.empty())
|
||||
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::Texture2D> texture (new osg::Texture2D(image));
|
||||
if (image)
|
||||
|
@ -1238,15 +1239,13 @@ namespace NifOsg
|
|||
std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->name);
|
||||
|
||||
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++)
|
||||
{
|
||||
influence.mWeights.emplace_back(weights[j].vertex, weights[j].weight);
|
||||
}
|
||||
influence.mWeights.push_back({weights[j].vertex, weights[j].weight});
|
||||
influence.mInvBindMatrix = data->bones[i].trafo.toMatrix();
|
||||
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);
|
||||
|
||||
|
@ -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.
|
||||
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)
|
||||
{
|
||||
|
@ -1477,32 +1476,46 @@ namespace NifOsg
|
|||
}
|
||||
}
|
||||
|
||||
const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i];
|
||||
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;
|
||||
}
|
||||
|
||||
unsigned int uvSet = 0;
|
||||
// create a new texture, will later attempt to share using the SharedStateManager
|
||||
osg::ref_ptr<osg::Texture2D> texture2d;
|
||||
if (!tex.texture.empty())
|
||||
if (texprop->textures[i].inUse)
|
||||
{
|
||||
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());
|
||||
const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i];
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
// Texture only comes from NiFlipController, so tex is ignored, set defaults
|
||||
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);
|
||||
texture2d->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||
texture2d->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||
uvSet = 0;
|
||||
}
|
||||
|
||||
unsigned int texUnit = boundTextures.size();
|
||||
|
||||
|
@ -1590,7 +1603,7 @@ namespace NifOsg
|
|||
break;
|
||||
}
|
||||
|
||||
boundTextures.push_back(tex.uvSet);
|
||||
boundTextures.push_back(uvSet);
|
||||
}
|
||||
}
|
||||
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
|
||||
|
@ -1717,7 +1730,7 @@ namespace NifOsg
|
|||
osg::StateSet* stateset = node->getOrCreateStateSet();
|
||||
|
||||
// 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);
|
||||
mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF);
|
||||
|
||||
|
@ -1736,7 +1749,8 @@ namespace NifOsg
|
|||
case Nif::RC_NiSpecularProperty:
|
||||
{
|
||||
// Specular property can turn specular lighting off.
|
||||
specFlags = property->flags;
|
||||
auto specprop = static_cast<const Nif::NiSpecularProperty*>(property);
|
||||
specEnabled = specprop->flags & 1;
|
||||
break;
|
||||
}
|
||||
case Nif::RC_NiMaterialProperty:
|
||||
|
@ -1820,7 +1834,7 @@ namespace NifOsg
|
|||
}
|
||||
|
||||
// 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));
|
||||
|
||||
if (lightmode == 0)
|
||||
|
|
|
@ -137,14 +137,13 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv)
|
|||
}
|
||||
|
||||
mBoneNodesVector.clear();
|
||||
for (auto& bonePair : mBoneSphereVector->mData)
|
||||
for (auto& boundPair : mBoneSphereVector->mData)
|
||||
{
|
||||
const std::string& boneName = bonePair.first;
|
||||
Bone* bone = mSkeleton->getBone(boneName);
|
||||
Bone* bone = mSkeleton->getBone(boundPair.name);
|
||||
if (!bone)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -155,12 +154,11 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv)
|
|||
{
|
||||
for (auto &weight : pair.first)
|
||||
{
|
||||
const std::string& boneName = weight.first.first;
|
||||
Bone* bone = mSkeleton->getBone(boneName);
|
||||
Bone* bone = mSkeleton->getBone(weight.boneName);
|
||||
if (!bone)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -218,7 +216,7 @@ void RigGeometry::cull(osg::NodeVisitor* nv)
|
|||
if (bone == nullptr)
|
||||
continue;
|
||||
|
||||
accumulateMatrix(weight.first.second, bone->mMatrixInSkeletonSpace, weight.second, resultMat);
|
||||
accumulateMatrix(weight.bindMatrix, bone->mMatrixInSkeletonSpace, weight.value, resultMat);
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -281,7 +279,7 @@ void RigGeometry::updateBounds(osg::NodeVisitor *nv)
|
|||
continue;
|
||||
|
||||
index++;
|
||||
osg::BoundingSpheref bs = boundPair.second;
|
||||
osg::BoundingSpheref bs = boundPair.sphere;
|
||||
if (mGeomToSkelMatrix)
|
||||
transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs);
|
||||
else
|
||||
|
@ -337,30 +335,21 @@ void RigGeometry::setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap)
|
|||
{
|
||||
mInfluenceMap = influenceMap;
|
||||
|
||||
typedef std::map<unsigned short, std::vector<BoneWeight> > Vertex2BoneMap;
|
||||
using Vertex2BoneMap = std::map<unsigned short, std::vector<BoneWeight>>;
|
||||
Vertex2BoneMap vertex2BoneMap;
|
||||
mBoneSphereVector = new BoneSphereVector;
|
||||
mBoneSphereVector->mData.reserve(mInfluenceMap->mData.size());
|
||||
mBone2VertexVector = new Bone2VertexVector;
|
||||
for (auto& influencePair : mInfluenceMap->mData)
|
||||
for (const BoneData& bone : mInfluenceMap->mData)
|
||||
{
|
||||
const std::string& boneName = influencePair.first;
|
||||
const BoneInfluence& bi = influencePair.second;
|
||||
mBoneSphereVector->mData.emplace_back(boneName, bi.mBoundSphere);
|
||||
|
||||
for (auto& weightPair: bi.mWeights)
|
||||
{
|
||||
std::vector<BoneWeight>& vec = vertex2BoneMap[weightPair.first];
|
||||
|
||||
vec.emplace_back(std::make_pair(boneName, bi.mInvBindMatrix), weightPair.second);
|
||||
}
|
||||
mBoneSphereVector->mData.push_back({bone.name, bone.influence.mBoundSphere});
|
||||
for (auto& weight : bone.influence.mWeights)
|
||||
vertex2BoneMap[weight.vertex].push_back({bone.name, bone.influence.mInvBindMatrix, weight.value});
|
||||
}
|
||||
|
||||
Bone2VertexMap bone2VertexMap;
|
||||
for (auto& vertexPair : vertex2BoneMap)
|
||||
{
|
||||
bone2VertexMap[vertexPair.second].emplace_back(vertexPair.first);
|
||||
}
|
||||
|
||||
mBone2VertexVector->mData.reserve(bone2VertexMap.size());
|
||||
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.
|
||||
virtual void compileGLObjects(osg::RenderInfo& renderInfo) const {}
|
||||
|
||||
struct VertexWeight
|
||||
{
|
||||
unsigned short vertex;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct BoneInfluence
|
||||
{
|
||||
osg::Matrixf mInvBindMatrix;
|
||||
osg::BoundingSpheref mBoundSphere;
|
||||
// <vertex index, weight>
|
||||
std::vector<std::pair<unsigned short, float>> mWeights;
|
||||
std::vector<VertexWeight> mWeights;
|
||||
};
|
||||
|
||||
struct BoneData
|
||||
{
|
||||
std::string name;
|
||||
BoneInfluence influence;
|
||||
bool operator<(const BoneData& other) const
|
||||
{
|
||||
return name < other.name;
|
||||
}
|
||||
};
|
||||
|
||||
struct InfluenceMap : public osg::Referenced
|
||||
{
|
||||
std::vector<std::pair<std::string, BoneInfluence>> mData;
|
||||
std::vector<BoneData> mData;
|
||||
};
|
||||
|
||||
void setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap);
|
||||
|
@ -79,23 +94,36 @@ namespace SceneUtil
|
|||
|
||||
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;
|
||||
|
||||
typedef std::vector<unsigned short> VertexList;
|
||||
|
||||
typedef std::map<std::vector<BoneWeight>, VertexList> Bone2VertexMap;
|
||||
using VertexList = std::vector<unsigned short>;
|
||||
using BoneWeightList = std::vector<BoneWeight>;
|
||||
using Bone2VertexMap = std::map<BoneWeightList, VertexList>;
|
||||
|
||||
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;
|
||||
|
||||
struct BoneSphere
|
||||
{
|
||||
std::string name;
|
||||
osg::BoundingSpheref sphere;
|
||||
};
|
||||
|
||||
struct BoneSphereVector : public osg::Referenced
|
||||
{
|
||||
std::vector<std::pair<std::string, osg::BoundingSpheref>> mData;
|
||||
std::vector<BoneSphere> mData;
|
||||
};
|
||||
osg::ref_ptr<BoneSphereVector> mBoneSphereVector;
|
||||
std::vector<Bone*> mBoneNodesVector;
|
||||
|
|
|
@ -120,6 +120,29 @@ void GraphicsWindowSDL2::init()
|
|||
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
// 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";
|
||||
return false;
|
||||
|
@ -107,7 +107,7 @@ namespace Shader
|
|||
else
|
||||
{
|
||||
lineDirectivePosition = 0;
|
||||
lineNumber = 1;
|
||||
lineNumber = 0;
|
||||
}
|
||||
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
|
||||
``~/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
|
||||
----
|
||||
|
||||
|
|
|
@ -43,16 +43,16 @@ Savegames
|
|||
Screenshots
|
||||
-----------
|
||||
|
||||
+--------------+-----------------------------------------------------------------------------------------------+
|
||||
| OS | Location |
|
||||
+==============+===============================================================================================+
|
||||
| Linux | ``$HOME/.local/share/openmw`` |
|
||||
+--------------+-----------------------------------------------------------------------------------------------+
|
||||
| Mac | ``$HOME/Library/Application\ Support/openmw`` |
|
||||
+--------------+---------------+-------------------------------------------------------------------------------+
|
||||
| Windows | File Explorer | ``Documents\My Games\OpenMW`` |
|
||||
| | | |
|
||||
| | PowerShell | ``Join-Path ([environment]::GetFolderPath("mydocuments")) "My Games\OpenMW"`` |
|
||||
| | | |
|
||||
| | Example | ``C:\Users\Username\Documents\My Games\OpenMW`` |
|
||||
+--------------+---------------+-------------------------------------------------------------------------------+
|
||||
+--------------+-----------------------------------------------------------------------------------------------------------+
|
||||
| OS | Location |
|
||||
+==============+===========================================================================================================+
|
||||
| Linux | ``$HOME/.local/share/openmw/screenshots`` |
|
||||
+--------------+-----------------------------------------------------------------------------------------------------------+
|
||||
| Mac | ``$HOME/Library/Application\ Support/openmw/screenshots`` |
|
||||
+--------------+---------------+-------------------------------------------------------------------------------------------+
|
||||
| Windows | File Explorer | ``Documents\My Games\OpenMW\screenshots`` |
|
||||
| | | |
|
||||
| | PowerShell | ``Join-Path ([environment]::GetFolderPath("mydocuments")) "My Games\OpenMW\screenshots"`` |
|
||||
| | | |
|
||||
| | 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.
|
||||
|
||||
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
|
||||
----------------
|
||||
----------------------
|
||||
|
||||
:Type: boolean
|
||||
:Range: True/False
|
||||
|
|
|
@ -325,6 +325,15 @@ uncapped damage fatigue = false
|
|||
# Turn lower body to movement direction. 'true' makes diagonal movement more realistic.
|
||||
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.
|
||||
swim upward correction = false
|
||||
|
||||
|
|
|
@ -43,6 +43,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<widget class="QCheckBox" name="enableNavigatorCheckBox">
|
||||
<property name="toolTip">
|
||||
|
@ -233,6 +243,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<widget class="QCheckBox" name="distantLandCheckBox">
|
||||
<property name="toolTip">
|
||||
|
@ -283,7 +303,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="animSourcesCheckBox">
|
||||
<property name="toolTip">
|
||||
<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