mirror of
https://github.com/OpenMW/openmw.git
synced 2026-01-04 19:43:13 +00:00
Merge branch openmw:master into master
This commit is contained in:
commit
0d6c9c2303
543 changed files with 42338 additions and 7765 deletions
|
|
@ -13,3 +13,7 @@ HeaderFilterRegex: '(apps|components)/'
|
|||
CheckOptions:
|
||||
- key: readability-identifier-naming.ConceptCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.NamespaceCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.NamespaceIgnoredRegexp
|
||||
value: 'osg(DB|FX|Particle|Shadow|Viewer|Util)?'
|
||||
|
|
|
|||
9
.github/workflows/push.yml
vendored
9
.github/workflows/push.yml
vendored
|
|
@ -73,7 +73,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Building Dependencies
|
||||
run: CI/before_install.osx.sh
|
||||
run: CI/before_install.macos.sh
|
||||
|
||||
- name: Prime ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
|
|
@ -82,11 +82,9 @@ jobs:
|
|||
max-size: 1000M
|
||||
|
||||
- name: Configure
|
||||
run: CI/before_script.osx.sh
|
||||
run: CI/before_script.macos.sh
|
||||
- name: Build
|
||||
run: |
|
||||
cd build
|
||||
make -j $(sysctl -n hw.logicalcpu) package
|
||||
run: CI/macos/build.sh
|
||||
|
||||
Output-Envs:
|
||||
name: Read .env file and expose it as output
|
||||
|
|
@ -106,7 +104,6 @@ jobs:
|
|||
fail-fast: true
|
||||
matrix:
|
||||
image:
|
||||
- "2019"
|
||||
- "2022"
|
||||
|
||||
uses: ./.github/workflows/windows.yml
|
||||
|
|
|
|||
30
.github/workflows/windows.yml
vendored
30
.github/workflows/windows.yml
vendored
|
|
@ -4,9 +4,13 @@ on:
|
|||
workflow_call:
|
||||
inputs:
|
||||
image:
|
||||
description: MSVC image (2019/2022)
|
||||
description: Window Server image
|
||||
required: true
|
||||
type: string
|
||||
msvc:
|
||||
description: MSVC version (2019/2022)
|
||||
default: "2022"
|
||||
type: string
|
||||
vcpkg-deps-tag:
|
||||
description: Git tag of our deps
|
||||
required: true
|
||||
|
|
@ -45,7 +49,7 @@ jobs:
|
|||
- name: Download prebuilt vcpkg packages
|
||||
working-directory: ${{ github.workspace }}/deps
|
||||
run: |
|
||||
$MANIFEST = "vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}.txt"
|
||||
$MANIFEST = "vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}.txt"
|
||||
curl --fail --retry 3 -L -o "$MANIFEST" "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/$MANIFEST"
|
||||
$lines = Get-Content "$MANIFEST"
|
||||
$URL = $lines[0]
|
||||
|
|
@ -61,7 +65,7 @@ jobs:
|
|||
|
||||
- name: Extract archived prebuilt vcpkg packages
|
||||
working-directory: ${{ github.workspace }}/deps
|
||||
run: 7z x -y -ovcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }} $env:archive
|
||||
run: 7z x -y -ovcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }} $env:archive
|
||||
|
||||
- name: Cache Qt
|
||||
id: qt-cache
|
||||
|
|
@ -94,12 +98,12 @@ jobs:
|
|||
-B ${{ github.workspace }}/build
|
||||
-G Ninja
|
||||
-D CMAKE_BUILD_TYPE=${{ inputs.build-type }}
|
||||
-D CMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/scripts/buildsystems/vcpkg.cmake'
|
||||
-D CMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/scripts/buildsystems/vcpkg.cmake'
|
||||
-D CMAKE_PREFIX_PATH='${{ github.workspace }}/deps/Qt/6.6.3/msvc2019_64'
|
||||
${{ inputs.package && '-D CMAKE_CXX_FLAGS_RELEASE="/O2 /Ob2 /DNDEBUG /Zi"' || '' }}
|
||||
${{ inputs.package && '-D "CMAKE_EXE_LINKER_FLAGS_RELEASE=/DEBUG /INCREMENTAL:NO"' || '' }}
|
||||
-D LuaJit_INCLUDE_DIR='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/include/luajit'
|
||||
-D LuaJit_LIBRARY='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/lib/lua51.lib'
|
||||
-D LuaJit_INCLUDE_DIR='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/include/luajit'
|
||||
-D LuaJit_LIBRARY='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/lib/lua51.lib'
|
||||
-D BUILD_BENCHMARKS=${{ ! inputs.package }}
|
||||
-D BUILD_COMPONENTS_TESTS=${{ ! inputs.package }}
|
||||
-D BUILD_OPENMW_TESTS=${{ ! inputs.package }}
|
||||
|
|
@ -114,9 +118,9 @@ jobs:
|
|||
|
||||
- name: Copy missing DLLs
|
||||
run: |
|
||||
cp ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/Release/MyGUIEngine.dll ${{ github.workspace }}/build
|
||||
cp -Filter *.dll -Recurse ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/osgPlugins-3.6.5 ${{ github.workspace }}/build
|
||||
cp ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/*.dll ${{ github.workspace }}/build
|
||||
cp ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/Release/MyGUIEngine.dll ${{ github.workspace }}/build
|
||||
cp -Filter *.dll -Recurse ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/osgPlugins-3.6.5 ${{ github.workspace }}/build
|
||||
cp ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/*.dll ${{ github.workspace }}/build
|
||||
|
||||
- name: Copy Qt DLLs
|
||||
working-directory: ${{ github.workspace }}/deps/Qt/6.6.3/msvc2019_64
|
||||
|
|
@ -172,7 +176,7 @@ jobs:
|
|||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
job_url=$(gh run --repo ${{ github.repository }} view ${{ github.run_id }} --json jobs --jq '.jobs[] | select(.name == "windows-${{ inputs.image }}") | .url')
|
||||
job_url=$(gh run --repo ${{ github.repository }} view ${{ github.run_id }} --json jobs --jq '.jobs[] | select(.name == "windows-${{ inputs.msvc }}") | .url')
|
||||
printf "Ref ${{ github.ref }}\nJob ${job_url}\nCommit ${{ github.sha }}\n" > install/CI-ID.txt
|
||||
cp install/CI-ID.txt pdb/CI-ID.txt
|
||||
cp install/CI-ID.txt SymStore/CI-ID.txt
|
||||
|
|
@ -180,19 +184,19 @@ jobs:
|
|||
- name: Store OpenMW archived pdb files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openmw-windows-${{ inputs.image }}-pdb-${{ github.sha }}
|
||||
name: openmw-windows-${{ inputs.msvc }}-pdb-${{ github.sha }}
|
||||
path: ${{ github.workspace }}/pdb/*
|
||||
|
||||
- name: Store OpenMW build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openmw-windows-${{ inputs.image }}-${{ github.sha }}
|
||||
name: openmw-windows-${{ inputs.msvc }}-${{ github.sha }}
|
||||
path: ${{ github.workspace }}/install/*
|
||||
|
||||
- name: Store symbol server artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openmw-windows-${{ inputs.image }}-sym-store-${{ github.sha }}
|
||||
name: openmw-windows-${{ inputs.msvc }}-sym-store-${{ github.sha }}
|
||||
path: ${{ github.workspace }}/SymStore/*
|
||||
|
||||
- name: Upload to symbol server
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ Ubuntu_GCC_preprocess:
|
|||
- pip3 install --user click termtables
|
||||
script:
|
||||
- CI/ubuntu_gcc_preprocess.sh
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||
|
||||
.Ubuntu:
|
||||
extends: .Ubuntu_Image
|
||||
|
|
@ -77,7 +80,7 @@ Ubuntu_GCC_preprocess:
|
|||
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi
|
||||
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_esm_refid_benchmark; fi
|
||||
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_settings_access_benchmark; fi
|
||||
- ccache -s
|
||||
- ccache -svv
|
||||
- df -h
|
||||
- if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then gcovr --xml-pretty --exclude-unreachable-branches --print-summary --root "${CI_PROJECT_DIR}" -j $(nproc) -o ../coverage.xml; fi
|
||||
- ls | grep -v -e '^extern$' -e '^install$' -e '^components-tests.xml$' -e '^openmw-tests.xml$' -e '^openmw-cs-tests.xml$' | xargs -I '{}' rm -rf './{}'
|
||||
|
|
@ -124,7 +127,7 @@ Coverity:
|
|||
- cov-analysis-linux64-*/bin/cov-configure --template --comptype prefix --compiler ccache
|
||||
# Remove the specific targets and build everything once we can do it under 3h
|
||||
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc)
|
||||
- ccache -s
|
||||
- ccache -svv
|
||||
after_script:
|
||||
- tar cfz cov-int.tar.gz cov-int
|
||||
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
|
||||
|
|
@ -268,6 +271,9 @@ Ubuntu_GCC_tests_asan:
|
|||
when: always
|
||||
reports:
|
||||
junit: build/*-tests.xml
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||
|
||||
Ubuntu_GCC_tests_ubsan:
|
||||
extends: Ubuntu_GCC
|
||||
|
|
@ -285,6 +291,9 @@ Ubuntu_GCC_tests_ubsan:
|
|||
when: always
|
||||
reports:
|
||||
junit: build/*-tests.xml
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||
|
||||
.Ubuntu_GCC_tests_tsan:
|
||||
extends: Ubuntu_GCC
|
||||
|
|
@ -322,6 +331,9 @@ Ubuntu_GCC_tests_coverage:
|
|||
coverage_format: cobertura
|
||||
path: coverage.xml
|
||||
junit: build/*-tests.xml
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||
|
||||
.Ubuntu_Static_Deps:
|
||||
extends: Ubuntu_Clang
|
||||
|
|
@ -396,12 +408,15 @@ Ubuntu_Clang:
|
|||
- cd build
|
||||
- find . -name *.o -exec touch {} \;
|
||||
- cmake --build . -- -j $(nproc) ${BUILD_TARGETS}
|
||||
- ccache -s
|
||||
- ccache -svv
|
||||
artifacts:
|
||||
paths:
|
||||
- build/
|
||||
expire_in: 12h
|
||||
timeout: 3h
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||
|
||||
Ubuntu_Clang_Tidy_components:
|
||||
extends: .Ubuntu_Clang_Tidy_Base
|
||||
|
|
@ -414,7 +429,7 @@ Ubuntu_Clang_Tidy_openmw:
|
|||
needs:
|
||||
- Ubuntu_Clang_Tidy_components
|
||||
variables:
|
||||
BUILD_TARGETS: openmw
|
||||
BUILD_TARGETS: openmw openmw-tests
|
||||
timeout: 3h
|
||||
|
||||
Ubuntu_Clang_Tidy_openmw-cs:
|
||||
|
|
@ -430,7 +445,7 @@ Ubuntu_Clang_Tidy_other:
|
|||
needs:
|
||||
- Ubuntu_Clang_Tidy_components
|
||||
variables:
|
||||
BUILD_TARGETS: bsatool esmtool openmw-launcher openmw-iniimporter openmw-essimporter openmw-wizard niftest components-tests openmw-tests openmw-cs-tests openmw-navmeshtool openmw-bulletobjecttool
|
||||
BUILD_TARGETS: components-tests bsatool esmtool openmw-launcher openmw-iniimporter openmw-essimporter openmw-wizard niftest openmw-navmeshtool openmw-bulletobjecttool
|
||||
timeout: 3h
|
||||
|
||||
.Ubuntu_Clang_tests:
|
||||
|
|
@ -467,7 +482,7 @@ Ubuntu_Clang_tests_Debug:
|
|||
stage: test
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||
EXAMPLE_SUITE_REVISION: f51b832e033429a7cdc520e0e48d7dfdb9141caa
|
||||
EXAMPLE_SUITE_REVISION: 599987b52bd2d064e26299d3317b11049499f32b
|
||||
cache:
|
||||
paths:
|
||||
- .cache/pip
|
||||
|
|
@ -502,19 +517,27 @@ Ubuntu_GCC_integration_tests_asan:
|
|||
.MacOS:
|
||||
stage: build
|
||||
rules:
|
||||
- if: $CI_PROJECT_ID == "7107382"
|
||||
- if: $CI_PROJECT_ID != "7107382"
|
||||
when: never
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
when: manual
|
||||
- if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule"
|
||||
image: macos-15-xcode-16
|
||||
tags:
|
||||
- saas-macos-medium-m1
|
||||
cache:
|
||||
paths:
|
||||
- ccache/
|
||||
script:
|
||||
- CI/before_install.osx.sh
|
||||
- CI/before_install.macos.sh
|
||||
- export CCACHE_BASEDIR="$(pwd)"
|
||||
- export CCACHE_DIR="$(pwd)/ccache"
|
||||
- mkdir -pv "${CCACHE_DIR}"
|
||||
- ccache -z -M "${CCACHE_SIZE}"
|
||||
- CI/before_script.osx.sh
|
||||
- cd build; make -j $(sysctl -n hw.logicalcpu) package
|
||||
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME##*/}.dmg"; done
|
||||
- CI/macos/ccache_prep.sh
|
||||
- CI/before_script.macos.sh
|
||||
- CI/macos/build.sh
|
||||
- cd build
|
||||
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${DMG_IDENTIFIER}_${CI_COMMIT_REF_NAME##*/}.dmg"; done
|
||||
- |
|
||||
if [[ -n "${AWS_ACCESS_KEY_ID}" ]]; then
|
||||
echo "[default]" > ~/.s3cfg
|
||||
|
|
@ -529,20 +552,33 @@ Ubuntu_GCC_integration_tests_asan:
|
|||
s3cmd put "${dmg}" s3://openmw-artifacts/${artifactDirectory}
|
||||
done
|
||||
fi
|
||||
- ccache -s
|
||||
- ../CI/macos/ccache_save.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- build/OpenMW-*.dmg
|
||||
|
||||
macOS14_Xcode15_arm64:
|
||||
macOS15_Xcode16_amd64:
|
||||
extends: .MacOS
|
||||
image: macos-14-xcode-15
|
||||
tags:
|
||||
- saas-macos-medium-m1
|
||||
cache:
|
||||
key: macOS14_Xcode15_arm64.v1
|
||||
key: macOS15_Xcode16_amd64.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 3G
|
||||
DMG_IDENTIFIER: amd64
|
||||
MACOS_AMD64: true
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
HOMEBREW_NO_EMOJI: true
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: true
|
||||
|
||||
macOS15_Xcode16_arm64:
|
||||
extends: .MacOS
|
||||
cache:
|
||||
key: macOS15_Xcode16_arm64.v1
|
||||
variables:
|
||||
DMG_IDENTIFIER: arm64
|
||||
CCACHE_SIZE: 3G
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
HOMEBREW_NO_EMOJI: true
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: true
|
||||
|
||||
.Compress_And_Upload_Symbols_Base:
|
||||
extends: .Ubuntu_Image
|
||||
|
|
@ -667,7 +703,7 @@ macOS14_Xcode15_arm64:
|
|||
- Get-Volume
|
||||
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
||||
cache:
|
||||
key: ninja-2022-v12
|
||||
key: ninja-2022-v13
|
||||
paths:
|
||||
- ccache
|
||||
- deps
|
||||
|
|
@ -825,7 +861,7 @@ macOS14_Xcode15_arm64:
|
|||
- Get-Volume
|
||||
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
||||
cache:
|
||||
key: msbuild-2022-v12
|
||||
key: msbuild-2022-v13
|
||||
paths:
|
||||
- deps
|
||||
- MSVC2022_64/deps/Qt
|
||||
|
|
@ -926,7 +962,7 @@ Windows_MSBuild_CacheInit:
|
|||
- cd build
|
||||
- cmake --build . -- -j $(nproc)
|
||||
# - cmake --install . # no one uses builds anyway, disable until 'no space left' is resolved
|
||||
- ccache -s
|
||||
- ccache -svv
|
||||
- df -h
|
||||
- ls | grep -v -e '^extern$' -e '^install$' | xargs -I '{}' rm -rf './{}'
|
||||
- cd ..
|
||||
|
|
@ -968,7 +1004,7 @@ Windows_MSBuild_CacheInit:
|
|||
- flatpak build-bundle ./repo openmw.flatpak org.openmw.OpenMW.devel
|
||||
cache:
|
||||
key: flatpak
|
||||
paths:
|
||||
paths:
|
||||
- ".flatpak-builder"
|
||||
artifacts:
|
||||
untracked: false
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ python:
|
|||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.8"
|
||||
python: "3.9"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ If you feel your name is missing from this list, please add it to `AUTHORS.md`.
|
|||
Programmers
|
||||
-----------
|
||||
|
||||
Bret Curtis (psi29a) - Project leader 2019-present
|
||||
Alexey Dobrokhotov (Capo) - Project leader 2025-present
|
||||
Bret Curtis (psi29a) - Project leader 2019-2025
|
||||
Marc Zinnschlag (Zini) - Project leader 2010-2018
|
||||
Nicolay Korslund - Project leader 2008-2010
|
||||
scrawl - Top contributor
|
||||
|
|
@ -49,10 +50,10 @@ Programmers
|
|||
Berulacks
|
||||
Bo Svensson
|
||||
Britt Mathis (galdor557)
|
||||
Capostrophic
|
||||
Carl Maxwell
|
||||
cc9cii
|
||||
Cédric Mocquillon
|
||||
Charles Horn
|
||||
Chris Boyce (slothlife)
|
||||
Chris Robinson (KittyCat)
|
||||
Chris Vigil
|
||||
|
|
@ -196,7 +197,7 @@ Programmers
|
|||
Qlonever
|
||||
Radu-Marius Popovici (rpopovici)
|
||||
Rafael Moura (dhustkoder)
|
||||
Randy Davin (Kindi)
|
||||
Randy Davin (Kuyondo)
|
||||
rdimesio
|
||||
rexelion
|
||||
riothamus
|
||||
|
|
|
|||
64
CHANGELOG.md
64
CHANGELOG.md
|
|
@ -1,3 +1,65 @@
|
|||
0.50.0
|
||||
------
|
||||
|
||||
Bug #2967: Inventory windows don't update when changing items by script
|
||||
Bug #5331: Pathfinding works incorrectly when actor is moved from one interior cell to another
|
||||
Bug #6039: Next Spell keybind fails while selected enchanted item has multiple copies
|
||||
Bug #6573: Editor: Selection behaves incorrectly on high-DPI displays
|
||||
Bug #6792: Birth sign info box has no line breaks
|
||||
Bug #7371: Equipping item from inventory does not play a Down sound when equipping fails
|
||||
Bug #7622: Player's marksman weapons don't work on close actors underwater
|
||||
Bug #7649: The sound and vfx of resisted enchanted items' magic still play
|
||||
Bug #7740: Magic items in the HUD aren't composited correctly
|
||||
Bug #7799: Picking up ingredients while object paging active grid is on may cause a hiccup
|
||||
Bug #7871: Kwama Queen doesn't start combat with player
|
||||
Bug #8245: The console command ShowVars does not list global mwscripts
|
||||
Bug #8265: Topics are linked incorrectly
|
||||
Bug #8303: On target spells cast by non-actors should fire underwater
|
||||
Bug #8318: Missing global variables are not handled gracefully in dialogue conditions
|
||||
Bug #8333: Quest status subrecords should not actually cause parsing to skip remaining data
|
||||
Bug #8340: Multi-effect enchantments are too expensive
|
||||
Bug #8341: Repeat shader visitor passes discard parallax
|
||||
Bug #8349: Travel to non-existent cell causes persistent black screen
|
||||
Bug #8359: Some quick keys menu related issues
|
||||
Bug #8371: Silence affects powers
|
||||
Bug #8375: Moon phase cycle doesn't match Morrowind
|
||||
Bug #8383: Casting bound helm or boots on beast races doesn't cleanup properly
|
||||
Bug #8385: Russian encoding broken with locale parameters and calendar
|
||||
Bug #8408: OpenMW doesn't report all the potential resting hindrances
|
||||
Bug #8414: Waterwalking works when collision is disabled
|
||||
Bug #8431: Behaviour of removed items from a container is buggy
|
||||
Bug #8432: Changing to and from an interior cell doesn't update collision
|
||||
Bug #8436: Spell selection in a pinned spellbook window doesn't update
|
||||
Bug #8437: Pinned inventory window's pin button doesn't look pressed
|
||||
Bug #8446: Travel prices are strangely inconsistent
|
||||
Bug #8459: Changing magic effect base cost doesn't change spell price
|
||||
Bug #8466: Showmap "" reveals nameless cells
|
||||
Bug #8485: Witchwither disease and probably other common diseases don't work correctly
|
||||
Bug #8490: Normals on Water disappear when Water Shader is Enabled but Refraction is Disabled
|
||||
Bug #8500: OpenMW Alarm behaviour doesn't match morrowind.exe
|
||||
Bug #8519: Multiple bounty is sometimes assigned to player when detected during a pickpocketing action
|
||||
Bug #8585: Dialogue topic list doesn't have enough padding
|
||||
Bug #8587: Minor INI importer problems
|
||||
Bug #8593: Render targets do not generate mipmaps
|
||||
Bug #8598: Post processing shaders don't interact with the vfs correctly
|
||||
Bug #8599: Non-ASCII paths in BSA files don't work
|
||||
Bug #8609: The crosshair is too large
|
||||
Bug #8610: Terrain normal maps using NormalGL format instead of NormalDX
|
||||
Bug #8612: Using aiactivate on an ingredient when graphical herbalism is enabled triggers non-stop pickup sounds
|
||||
Bug #8615: Rest/wait time progress speed is different from vanilla
|
||||
Feature #2522: Support quick item transfer
|
||||
Feature #3769: Allow GetSpellEffects on enchantments
|
||||
Feature #8112: Expose landscape record data to Lua
|
||||
Feature #8113: Support extended selection in autodetected subdirectory dialog
|
||||
Feature #8139: Editor: Redesign the selection markers
|
||||
Feature #8285: Expose list of active shaders in postprocessing API
|
||||
Feature #8313: Show the character name in the savegame details
|
||||
Feature #8320: Add access mwscript source text to lua api
|
||||
Feature #8334: Lua: AddTopic equivalent
|
||||
Feature #8355: Lua: Window visibility checking in interfaces.UI
|
||||
Feature #8580: Sort characters in the save loading menu
|
||||
Feature #8597: Lua: Add more built-in event handlers
|
||||
|
||||
0.49.0
|
||||
------
|
||||
|
||||
|
|
@ -236,6 +298,8 @@
|
|||
Bug #8465: Blue screen w/ antialiasing and post-processing on macOS
|
||||
Bug #8503: Camera does not handle NaN gracefully
|
||||
Bug #8541: Lua: util.color:asHex produces wrong output for some colors
|
||||
Bug #8567: Token replacement does not work via CLI and relative paths passed via the command line are not relative to the CWD
|
||||
Bug #8576: Crash on exit when unresolving containers with scripted items
|
||||
Feature #1415: Infinite fall failsafe
|
||||
Feature #2566: Handle NAM9 records for manual cell references
|
||||
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
|
||||
|
|
|
|||
7
CI/before_install.macos.sh
Executable file
7
CI/before_install.macos.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
if [[ "${MACOS_AMD64}" ]]; then
|
||||
./CI/macos/before_install.amd64.sh
|
||||
else
|
||||
./CI/macos/before_install.arm64.sh
|
||||
fi
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
export HOMEBREW_NO_EMOJI=1
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
export HOMEBREW_AUTOREMOVE=1
|
||||
|
||||
brew tap --repair
|
||||
brew update --quiet
|
||||
|
||||
brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd
|
||||
|
||||
command -v ccache >/dev/null 2>&1 || brew install ccache
|
||||
command -v cmake >/dev/null 2>&1 || brew install cmake
|
||||
command -v qmake >/dev/null 2>&1 || brew install qt@5
|
||||
export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"
|
||||
|
||||
# Install deps
|
||||
brew install openal-soft icu4c yaml-cpp sqlite
|
||||
|
||||
ccache --version
|
||||
cmake --version
|
||||
qmake --version
|
||||
|
||||
if [[ "${MACOS_AMD64}" ]]; then
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240802.zip -o ~/openmw-deps.zip
|
||||
unzip -o ~/openmw-deps.zip -d /tmp > /dev/null
|
||||
else
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240818-arm64.tar.xz -o ~/openmw-deps.tar.xz
|
||||
tar xf ~/openmw-deps.tar.xz -C /tmp > /dev/null
|
||||
fi
|
||||
77
CI/before_script.macos.sh
Executable file
77
CI/before_script.macos.sh
Executable file
|
|
@ -0,0 +1,77 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
# Silence a git warning
|
||||
git config --global advice.detachedHead false
|
||||
|
||||
rm -fr build
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
DEPENDENCIES_ROOT="/tmp/openmw-deps"
|
||||
|
||||
if [[ "${MACOS_AMD64}" ]]; then
|
||||
QT_PATH=$(arch -x86_64 /usr/local/bin/brew --prefix qt@6)
|
||||
ICU_PATH=$(arch -x86_64 /usr/local/bin/brew --prefix icu4c)
|
||||
OPENAL_PATH=$(arch -x86_64 /usr/local/bin/brew --prefix openal-soft)
|
||||
CCACHE_EXECUTABLE=$(arch -x86_64 /usr/local/bin/brew --prefix ccache)/bin/ccache
|
||||
else
|
||||
QT_PATH=$(brew --prefix qt@6)
|
||||
ICU_PATH=$(brew --prefix icu4c)
|
||||
OPENAL_PATH=$(brew --prefix openal-soft)
|
||||
CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache
|
||||
fi
|
||||
|
||||
declare -a CMAKE_CONF_OPTS=(
|
||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH;$OPENAL_PATH"
|
||||
-D CMAKE_C_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE"
|
||||
-D CMAKE_CXX_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE"
|
||||
-D CMAKE_CXX_FLAGS="-stdlib=libc++"
|
||||
-D CMAKE_C_COMPILER="clang"
|
||||
-D CMAKE_CXX_COMPILER="clang++"
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="13.6"
|
||||
-D OPENMW_USE_SYSTEM_RECASTNAVIGATION=TRUE
|
||||
-D Boost_INCLUDE_DIR="$DEPENDENCIES_ROOT/include"
|
||||
-D OSGPlugins_LIB_DIR="$DEPENDENCIES_ROOT/lib/osgPlugins-3.6.5"
|
||||
-D ICU_ROOT="$ICU_PATH"
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE
|
||||
)
|
||||
|
||||
declare -a BUILD_OPTS=(
|
||||
-D BUILD_OPENMW=TRUE
|
||||
-D BUILD_OPENCS=TRUE
|
||||
-D BUILD_ESMTOOL=TRUE
|
||||
-D BUILD_BSATOOL=TRUE
|
||||
-D BUILD_ESSIMPORTER=TRUE
|
||||
-D BUILD_NIFTEST=TRUE
|
||||
-D BUILD_NAVMESHTOOL=TRUE
|
||||
-D BUILD_BULLETOBJECTTOOL=TRUE
|
||||
-G"Unix Makefiles"
|
||||
)
|
||||
|
||||
if [[ "${MACOS_AMD64}" ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-D CMAKE_OSX_ARCHITECTURES="x86_64"
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ "${CMAKE_BUILD_TYPE}" ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
)
|
||||
else
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-D CMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ "${MACOS_AMD64}" ]]; then
|
||||
arch -x86_64 cmake \
|
||||
"${CMAKE_CONF_OPTS[@]}" \
|
||||
"${BUILD_OPTS[@]}" \
|
||||
..
|
||||
else
|
||||
cmake \
|
||||
"${CMAKE_CONF_OPTS[@]}" \
|
||||
"${BUILD_OPTS[@]}" \
|
||||
..
|
||||
fi
|
||||
|
|
@ -377,6 +377,8 @@ case $VS_VERSION in
|
|||
MSVC_DISPLAY_YEAR="2022"
|
||||
|
||||
QT_MSVC_YEAR="2019"
|
||||
|
||||
VCPKG_TRIPLET="x64-windows"
|
||||
;;
|
||||
|
||||
16|16.0|2019 )
|
||||
|
|
@ -386,6 +388,8 @@ case $VS_VERSION in
|
|||
MSVC_DISPLAY_YEAR="2019"
|
||||
|
||||
QT_MSVC_YEAR="2019"
|
||||
|
||||
VCPKG_TRIPLET="x64-windows-2019"
|
||||
;;
|
||||
|
||||
15|15.0|2017 )
|
||||
|
|
@ -546,7 +550,7 @@ fi
|
|||
QT_VER='6.6.3'
|
||||
AQT_VERSION='v3.1.15'
|
||||
|
||||
VCPKG_TAG="2024-11-10"
|
||||
VCPKG_TAG="2025-07-23"
|
||||
VCPKG_PATH="vcpkg-x64-${VS_VERSION:?}-${VCPKG_TAG:?}"
|
||||
VCPKG_PDB_PATH="vcpkg-x64-${VS_VERSION:?}-pdb-${VCPKG_TAG:?}"
|
||||
VCPKG_MANIFEST="${VCPKG_PATH:?}.txt"
|
||||
|
|
@ -633,16 +637,16 @@ printf "vcpkg packages ${VCPKG_TAG:?}... "
|
|||
fi
|
||||
|
||||
add_cmake_opts -DCMAKE_TOOLCHAIN_FILE="$(real_pwd)/${VCPKG_PATH:?}/scripts/buildsystems/vcpkg.cmake"
|
||||
add_cmake_opts -DLuaJit_INCLUDE_DIR="$(real_pwd)/${VCPKG_PATH:?}/installed/x64-windows/include/luajit"
|
||||
add_cmake_opts -DLuaJit_LIBRARY="$(real_pwd)/${VCPKG_PATH:?}/installed/x64-windows/lib/lua51.lib"
|
||||
add_cmake_opts -DLuaJit_INCLUDE_DIR="$(real_pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/include/luajit"
|
||||
add_cmake_opts -DLuaJit_LIBRARY="$(real_pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/lib/lua51.lib"
|
||||
|
||||
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
|
||||
if [[ ${CONFIGURATION:?} == "Debug" ]]; then
|
||||
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/x64-windows/debug/bin"
|
||||
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/debug/bin"
|
||||
|
||||
add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Debug/MyGUIEngine_d.dll"
|
||||
else
|
||||
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/x64-windows/bin"
|
||||
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/bin"
|
||||
|
||||
add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Release/MyGUIEngine.dll"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
# Silence a git warning
|
||||
git config --global advice.detachedHead false
|
||||
|
||||
rm -fr build
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
DEPENDENCIES_ROOT="/tmp/openmw-deps"
|
||||
|
||||
QT_PATH=$(brew --prefix qt@5)
|
||||
ICU_PATH=$(brew --prefix icu4c)
|
||||
OPENAL_PATH=$(brew --prefix openal-soft)
|
||||
CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache
|
||||
|
||||
declare -a CMAKE_CONF_OPTS=(
|
||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH;$OPENAL_PATH"
|
||||
-D CMAKE_C_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE"
|
||||
-D CMAKE_CXX_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE"
|
||||
-D CMAKE_CXX_FLAGS="-stdlib=libc++"
|
||||
-D CMAKE_C_COMPILER="clang"
|
||||
-D CMAKE_CXX_COMPILER="clang++"
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="13.6"
|
||||
-D OPENMW_USE_SYSTEM_RECASTNAVIGATION=TRUE
|
||||
-D Boost_INCLUDE_DIR="$DEPENDENCIES_ROOT/include"
|
||||
-D OSGPlugins_LIB_DIR="$DEPENDENCIES_ROOT/lib/osgPlugins-3.6.5"
|
||||
-D ICU_ROOT="$ICU_PATH"
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE
|
||||
)
|
||||
|
||||
if [[ "${CMAKE_BUILD_TYPE}" ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
)
|
||||
else
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-D CMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
)
|
||||
fi
|
||||
|
||||
cmake \
|
||||
"${CMAKE_CONF_OPTS[@]}" \
|
||||
-D BUILD_OPENMW=TRUE \
|
||||
-D BUILD_OPENCS=TRUE \
|
||||
-D BUILD_ESMTOOL=TRUE \
|
||||
-D BUILD_BSATOOL=TRUE \
|
||||
-D BUILD_ESSIMPORTER=TRUE \
|
||||
-D BUILD_NIFTEST=TRUE \
|
||||
-D BUILD_NAVMESHTOOL=TRUE \
|
||||
-D BUILD_BULLETOBJECTTOOL=TRUE \
|
||||
-G"Unix Makefiles" \
|
||||
..
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
git ls-files -- ':(exclude)extern/' '*.cpp' '*.hpp' '*.h' |
|
||||
grep -vP '/[a-z0-9]+\.(cpp|hpp|h)$' |
|
||||
grep -vFf CI/file_name_exceptions.txt &&
|
||||
grep -vP '/[a-z0-9]+\.(cpp|hpp|h)$' &&
|
||||
( echo 'File names do not follow the naming convention, see https://wiki.openmw.org/index.php?title=Naming_Conventions#Files'; exit -1 )
|
||||
exit 0
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
apps/openmw/android_main.cpp
|
||||
apps/openmw/mwsound/efx-presets.h
|
||||
apps/openmw/mwsound/ffmpeg_decoder.cpp
|
||||
apps/openmw/mwsound/ffmpeg_decoder.hpp
|
||||
apps/openmw/mwsound/openal_output.cpp
|
||||
apps/openmw/mwsound/openal_output.hpp
|
||||
apps/openmw/mwsound/sound_buffer.cpp
|
||||
apps/openmw/mwsound/sound_buffer.hpp
|
||||
apps/openmw/mwsound/sound_decoder.hpp
|
||||
apps/openmw/mwsound/sound_output.hpp
|
||||
apps/components_tests/esm/test_fixed_string.cpp
|
||||
apps/components_tests/files/conversion_tests.cpp
|
||||
apps/components_tests/lua/test_async.cpp
|
||||
apps/components_tests/lua/test_configuration.cpp
|
||||
apps/components_tests/lua/test_l10n.cpp
|
||||
apps/components_tests/lua/test_lua.cpp
|
||||
apps/components_tests/lua/test_scriptscontainer.cpp
|
||||
apps/components_tests/lua/test_serialization.cpp
|
||||
apps/components_tests/lua/test_storage.cpp
|
||||
apps/components_tests/lua/test_ui_content.cpp
|
||||
apps/components_tests/lua/test_utilpackage.cpp
|
||||
apps/components_tests/lua/test_inputactions.cpp
|
||||
apps/components_tests/lua/test_yaml.cpp
|
||||
apps/components_tests/misc/test_endianness.cpp
|
||||
apps/components_tests/misc/test_resourcehelpers.cpp
|
||||
apps/components_tests/misc/test_stringops.cpp
|
||||
apps/openmw_tests/mwdialogue/test_keywordsearch.cpp
|
||||
apps/openmw_tests/mwscript/test_scripts.cpp
|
||||
apps/openmw_tests/mwscript/test_utils.hpp
|
||||
apps/openmw_tests/mwworld/test_store.cpp
|
||||
components/bsa/bsa_file.cpp
|
||||
components/bsa/bsa_file.hpp
|
||||
components/crashcatcher/windows_crashcatcher.cpp
|
||||
components/crashcatcher/windows_crashcatcher.hpp
|
||||
components/crashcatcher/windows_crashmonitor.cpp
|
||||
components/crashcatcher/windows_crashmonitor.hpp
|
||||
components/crashcatcher/windows_crashshm.hpp
|
||||
components/fx/lexer_types.hpp
|
||||
components/fx/parse_constants.hpp
|
||||
components/platform/file.posix.cpp
|
||||
components/platform/file.stdio.cpp
|
||||
components/platform/file.win32.cpp
|
||||
components/sdlutil/gl4es_init.cpp
|
||||
components/sdlutil/gl4es_init.h
|
||||
components/to_utf8/gen_iconv.cpp
|
||||
components/to_utf8/tables_gen.hpp
|
||||
components/to_utf8/to_utf8.cpp
|
||||
components/to_utf8/to_utf8.hpp
|
||||
8
CI/macos/before_install.amd64.sh
Executable file
8
CI/macos/before_install.amd64.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
|
||||
arch -x86_64 /usr/local/bin/brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd ccache cmake qt@6 openal-soft icu4c yaml-cpp sqlite
|
||||
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240802.zip -o ~/openmw-deps.zip
|
||||
unzip -o ~/openmw-deps.zip -d /tmp > /dev/null
|
||||
9
CI/macos/before_install.arm64.sh
Executable file
9
CI/macos/before_install.arm64.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
brew tap --repair
|
||||
brew update --quiet
|
||||
|
||||
brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd ccache cmake qt@6 openal-soft icu4c yaml-cpp sqlite
|
||||
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240818-arm64.tar.xz -o ~/openmw-deps.tar.xz
|
||||
tar xf ~/openmw-deps.tar.xz -C /tmp > /dev/null
|
||||
9
CI/macos/build.sh
Executable file
9
CI/macos/build.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
cd build
|
||||
|
||||
if [[ "${MACOS_AMD64}" ]]; then
|
||||
arch -x86_64 make -j $(sysctl -n hw.logicalcpu) package
|
||||
else
|
||||
make -j $(sysctl -n hw.logicalcpu) package
|
||||
fi
|
||||
7
CI/macos/ccache_prep.sh
Executable file
7
CI/macos/ccache_prep.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
if [[ "${MACOS_AMD64}" ]]; then
|
||||
arch -x86_64 ccache -z -M "${CCACHE_SIZE}"
|
||||
else
|
||||
ccache -z -M "${CCACHE_SIZE}"
|
||||
fi
|
||||
7
CI/macos/ccache_save.sh
Executable file
7
CI/macos/ccache_save.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
if [[ "${MACOS_AMD64}" ]]; then
|
||||
arch -x86_64 ccache -svv
|
||||
else
|
||||
ccache -svv
|
||||
fi
|
||||
150
CMakeLists.txt
150
CMakeLists.txt
|
|
@ -80,10 +80,10 @@ endif()
|
|||
message(STATUS "Configuring OpenMW...")
|
||||
|
||||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 49)
|
||||
set(OPENMW_VERSION_MINOR 50)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
set(OPENMW_LUA_API_REVISION 76)
|
||||
set(OPENMW_POSTPROCESSING_API_REVISION 2)
|
||||
set(OPENMW_LUA_API_REVISION 82)
|
||||
set(OPENMW_POSTPROCESSING_API_REVISION 3)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
set(OPENMW_VERSION_TAGHASH "")
|
||||
|
|
@ -466,7 +466,7 @@ find_package(Boost 1.70.0 CONFIG REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONA
|
|||
if(OPENMW_USE_SYSTEM_MYGUI)
|
||||
find_package(MyGUI 3.4.3 REQUIRED)
|
||||
endif()
|
||||
find_package(SDL2 2.0.10 REQUIRED)
|
||||
find_package(SDL2 2.0.20 REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
|
|
@ -590,30 +590,10 @@ if(OPENMW_LTO_BUILD)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
set(OPENMW_CXX_FLAGS "-Wall -Wextra -Wundef -Wextra-semi -Wno-unused-parameter -pedantic -Wno-long-long -Wnon-virtual-dtor -Wunused ${OPENMW_CXX_FLAGS}")
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438
|
||||
set(OPENMW_CXX_FLAGS "-Wno-array-bounds ${OPENMW_CXX_FLAGS}")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
|
||||
set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-potentially-evaluated-expression")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-unused-but-set-parameter -Wduplicated-branches -Wduplicated-cond -Wlogical-op")
|
||||
endif()
|
||||
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
if (APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
|
||||
endif()
|
||||
|
||||
# Extern
|
||||
|
||||
|
|
@ -624,8 +604,42 @@ if (BUILD_OPENCS OR BUILD_OPENCS_TESTS)
|
|||
add_subdirectory (extern/osgQt)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
add_compile_options("/W4")
|
||||
|
||||
set(WARNINGS_DISABLE
|
||||
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
||||
4127 # Conditional expression is constant
|
||||
4996 # Function was declared deprecated
|
||||
5054 # Deprecated operations between enumerations of different types caused by Qt headers
|
||||
)
|
||||
|
||||
foreach(d ${WARNINGS_DISABLE})
|
||||
add_compile_options("/wd${d}")
|
||||
endforeach(d)
|
||||
|
||||
if(OPENMW_MSVC_WERROR)
|
||||
add_compile_options("/WX")
|
||||
endif()
|
||||
else ()
|
||||
add_compile_options("-Wall" "-Wextra" "-Wundef" "-Wextra-semi" "-Wno-unused-parameter" "-pedantic" "-Wno-long-long" "-Wnon-virtual-dtor" "-Wunused")
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
|
||||
add_compile_options("-Wno-potentially-evaluated-expression")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
add_compile_options("-Wno-unused-but-set-parameter" "-Wduplicated-branches" "-Wduplicated-cond" "-Wlogical-op")
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438
|
||||
add_compile_options("-Wno-array-bounds")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (OPENMW_CXX_FLAGS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENMW_CXX_FLAGS}")
|
||||
separate_arguments(OPENMW_CXX_FLAGS NATIVE_COMMAND "${OPENMW_CXX_FLAGS}")
|
||||
add_compile_options(${OPENMW_CXX_FLAGS})
|
||||
endif()
|
||||
|
||||
# Components
|
||||
|
|
@ -715,87 +729,9 @@ if (WIN32)
|
|||
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||
endif()
|
||||
|
||||
# Play a bit with the warning levels
|
||||
|
||||
set(WARNINGS "/W4")
|
||||
|
||||
set(WARNINGS_DISABLE
|
||||
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
||||
4127 # Conditional expression is constant
|
||||
4996 # Function was declared deprecated
|
||||
5054 # Deprecated operations between enumerations of different types caused by Qt headers
|
||||
)
|
||||
|
||||
foreach(d ${WARNINGS_DISABLE})
|
||||
list(APPEND WARNINGS "/wd${d}")
|
||||
endforeach(d)
|
||||
|
||||
if(OPENMW_MSVC_WERROR)
|
||||
list(APPEND WARNINGS "/WX")
|
||||
endif()
|
||||
|
||||
target_compile_options(components PRIVATE ${WARNINGS})
|
||||
target_compile_options(osg-ffmpeg-videoplayer PRIVATE ${WARNINGS})
|
||||
|
||||
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
|
||||
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
|
||||
endif()
|
||||
|
||||
if (BUILD_BSATOOL)
|
||||
target_compile_options(bsatool PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_ESMTOOL)
|
||||
target_compile_options(esmtool PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_ESSIMPORTER)
|
||||
target_compile_options(openmw-essimporter PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_LAUNCHER)
|
||||
target_compile_options(openmw-launcher PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_MWINIIMPORTER)
|
||||
target_compile_options(openmw-iniimporter PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENCS)
|
||||
target_compile_options(openmw-cs PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENMW)
|
||||
target_compile_options(openmw PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_WIZARD)
|
||||
target_compile_options(openmw-wizard PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_COMPONENTS_TESTS)
|
||||
target_compile_options(components-tests PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_BENCHMARKS)
|
||||
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_NAVMESHTOOL)
|
||||
target_compile_options(openmw-navmeshtool PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_BULLETOBJECTTOOL)
|
||||
target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS} ${MT_BUILD})
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENCS_TESTS)
|
||||
target_compile_options(openmw-cs-tests PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENMW_TESTS)
|
||||
target_compile_options(openmw-tests PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
endif(MSVC)
|
||||
|
||||
# TODO: At some point release builds should not use the console but rather write to a log file
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ OpenMW is an open-source open-world RPG game engine that supports playing Morrow
|
|||
|
||||
OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set.
|
||||
|
||||
* Version: 0.49.0
|
||||
* Version: 0.50.0
|
||||
* License: GPLv3 (see [LICENSE](https://gitlab.com/OpenMW/openmw/-/raw/master/LICENSE) for more information)
|
||||
* Website: https://www.openmw.org
|
||||
* IRC: #openmw on irc.libera.chat
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include <components/resource/niffilemanager.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
|
@ -125,6 +125,7 @@ namespace
|
|||
}
|
||||
|
||||
Files::ConfigurationManager config;
|
||||
config.processPaths(variables, std::filesystem::current_path());
|
||||
config.readConfiguration(variables, desc);
|
||||
|
||||
Debug::setupLogging(config.getLogPath(), applicationName);
|
||||
|
|
@ -154,7 +155,7 @@ namespace
|
|||
|
||||
VFS::Manager vfs;
|
||||
|
||||
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
||||
VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder());
|
||||
|
||||
Settings::Manager::load(config);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,29 +4,28 @@ include_directories(SYSTEM ${GMOCK_INCLUDE_DIRS})
|
|||
file(GLOB UNITTEST_SRC_FILES
|
||||
main.cpp
|
||||
|
||||
esm/test_fixed_string.cpp
|
||||
esm/variant.cpp
|
||||
esm/testfixedstring.cpp
|
||||
esm/testrefid.cpp
|
||||
esm/variant.cpp
|
||||
|
||||
lua/test_lua.cpp
|
||||
lua/test_scriptscontainer.cpp
|
||||
lua/test_utilpackage.cpp
|
||||
lua/test_serialization.cpp
|
||||
lua/test_configuration.cpp
|
||||
lua/test_l10n.cpp
|
||||
lua/test_storage.cpp
|
||||
lua/test_async.cpp
|
||||
lua/test_inputactions.cpp
|
||||
lua/test_yaml.cpp
|
||||
|
||||
lua/test_ui_content.cpp
|
||||
lua/testasync.cpp
|
||||
lua/testconfiguration.cpp
|
||||
lua/testinputactions.cpp
|
||||
lua/testl10n.cpp
|
||||
lua/testlua.cpp
|
||||
lua/testscriptscontainer.cpp
|
||||
lua/testserialization.cpp
|
||||
lua/teststorage.cpp
|
||||
lua/testuicontent.cpp
|
||||
lua/testutilpackage.cpp
|
||||
lua/testyaml.cpp
|
||||
|
||||
misc/compression.cpp
|
||||
misc/progressreporter.cpp
|
||||
misc/test_endianness.cpp
|
||||
misc/test_resourcehelpers.cpp
|
||||
misc/test_stringops.cpp
|
||||
misc/testendianness.cpp
|
||||
misc/testmathutil.cpp
|
||||
misc/testresourcehelpers.cpp
|
||||
misc/teststringops.cpp
|
||||
|
||||
nifloader/testbulletnifloader.cpp
|
||||
|
||||
|
|
@ -64,8 +63,8 @@ file(GLOB UNITTEST_SRC_FILES
|
|||
esmloader/esmdata.cpp
|
||||
esmloader/record.cpp
|
||||
|
||||
files/conversiontests.cpp
|
||||
files/hash.cpp
|
||||
files/conversion_tests.cpp
|
||||
|
||||
toutf8/toutf8.cpp
|
||||
|
||||
|
|
|
|||
|
|
@ -723,7 +723,7 @@ namespace ESM
|
|||
TEST_P(Esm3SaveLoadRecordTest, landShouldNotChange)
|
||||
{
|
||||
LandRecordData data;
|
||||
std::iota(data.mHeights.begin(), data.mHeights.end(), 1);
|
||||
std::iota(data.mHeights.begin(), data.mHeights.end(), 1.0f);
|
||||
std::for_each(data.mHeights.begin(), data.mHeights.end(), [](float& v) { v *= Land::sHeightScale; });
|
||||
data.mMinHeight = *std::min_element(data.mHeights.begin(), data.mHeights.end());
|
||||
data.mMaxHeight = *std::max_element(data.mHeights.begin(), data.mHeights.end());
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include <components/esmloader/load.hpp>
|
||||
#include <components/files/collections.hpp>
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
namespace
|
||||
{
|
||||
using namespace testing;
|
||||
using namespace fx::Lexer;
|
||||
using namespace Fx::Lexer;
|
||||
|
||||
struct LexerTest : Test
|
||||
{
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ namespace
|
|||
)" };
|
||||
|
||||
using namespace testing;
|
||||
using namespace fx;
|
||||
using namespace Fx;
|
||||
|
||||
struct TechniqueTest : Test
|
||||
{
|
||||
|
|
@ -113,7 +113,8 @@ namespace
|
|||
|
||||
void compile(const std::string& name)
|
||||
{
|
||||
mTechnique = std::make_unique<Technique>(*mVFS.get(), mImageManager, name, 1, 1, true, true);
|
||||
mTechnique = std::make_unique<Technique>(
|
||||
*mVFS.get(), mImageManager, Technique::makeFileName(name), name, 1, 1, true, true);
|
||||
mTechnique->compile();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ namespace
|
|||
constexpr VFS::Path::NormalizedView test2EnPath("l10n/test2/en.yaml");
|
||||
constexpr VFS::Path::NormalizedView test3EnPath("l10n/test3/en.yaml");
|
||||
constexpr VFS::Path::NormalizedView test3DePath("l10n/test3/de.yaml");
|
||||
constexpr VFS::Path::NormalizedView test4RuPath("l10n/test4/ru.yaml");
|
||||
constexpr VFS::Path::NormalizedView test4EnPath("l10n/test4/en.yaml");
|
||||
|
||||
VFSTestFile invalidScript("not a script");
|
||||
VFSTestFile incorrectScript(
|
||||
|
|
@ -69,6 +71,16 @@ currency: "You have {money, number, currency}"
|
|||
VFSTestFile test2En(R"X(
|
||||
good_morning: "Morning!"
|
||||
you_have_arrows: "Arrows count: {count}"
|
||||
)X");
|
||||
|
||||
VFSTestFile test4Ru(R"X(
|
||||
skill_increase: "Ваш навык {навык} увеличился до {value}"
|
||||
acrobatics: "Акробатика"
|
||||
)X");
|
||||
|
||||
VFSTestFile test4En(R"X(
|
||||
stat_increase: "Your {stat} has increased to {value}"
|
||||
speed: "Speed"
|
||||
)X");
|
||||
|
||||
struct LuaL10nTest : Test
|
||||
|
|
@ -80,6 +92,8 @@ you_have_arrows: "Arrows count: {count}"
|
|||
{ test2EnPath, &test2En },
|
||||
{ test3EnPath, &test1En },
|
||||
{ test3DePath, &test1De },
|
||||
{ test4RuPath, &test4Ru },
|
||||
{ test4EnPath, &test4En },
|
||||
});
|
||||
|
||||
LuaUtil::ScriptsConfiguration mCfg;
|
||||
|
|
@ -91,7 +105,7 @@ you_have_arrows: "Arrows count: {count}"
|
|||
lua.protectedCall([&](LuaUtil::LuaView& view) {
|
||||
sol::state_view& l = view.sol();
|
||||
internal::CaptureStdout();
|
||||
l10n::Manager l10nManager(mVFS.get());
|
||||
L10n::Manager l10nManager(mVFS.get());
|
||||
l10nManager.setPreferredLocales({ "de", "en" });
|
||||
EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n");
|
||||
|
||||
|
|
@ -169,6 +183,18 @@ you_have_arrows: "Arrows count: {count}"
|
|||
l.safe_script("t3 = l10n('Test3', 'de')");
|
||||
l10nManager.setPreferredLocales({ "en" });
|
||||
EXPECT_EQ(get<std::string>(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!");
|
||||
|
||||
// Test that formatting arguments use a correct encoding
|
||||
l.safe_script("t4 = l10n('Test4', 'ru')");
|
||||
l10nManager.setPreferredLocales({ "ru", "en" });
|
||||
EXPECT_EQ(get<std::string>(l, "t4('skill_increase', {навык='Акробатика', value=100})"),
|
||||
"Ваш навык Акробатика увеличился до 100");
|
||||
EXPECT_EQ(get<std::string>(l, "t4('skill_increase', {навык=t4('acrobatics'), value=100})"),
|
||||
"Ваш навык Акробатика увеличился до 100");
|
||||
EXPECT_EQ(get<std::string>(l, "t4('stat_increase', {stat='Speed', value=100})"),
|
||||
"Your Speed has increased to 100");
|
||||
EXPECT_EQ(get<std::string>(l, "t4('stat_increase', {stat=t4('speed'), value=100})"),
|
||||
"Your Speed has increased to 100");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -9,22 +9,33 @@ namespace
|
|||
{
|
||||
using namespace testing;
|
||||
|
||||
struct LuaUtilPackageTest : Test
|
||||
{
|
||||
LuaUtil::LuaState mLuaState{ nullptr, nullptr };
|
||||
|
||||
LuaUtilPackageTest()
|
||||
{
|
||||
mLuaState.addInternalLibSearchPath(
|
||||
std::filesystem::path{ OPENMW_PROJECT_SOURCE_DIR } / "components" / "lua");
|
||||
sol::state_view sol = mLuaState.unsafeState();
|
||||
sol["util"] = LuaUtil::initUtilPackage(sol);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T get(sol::state& lua, const std::string& luaCode)
|
||||
T get(sol::state_view& lua, const std::string& luaCode)
|
||||
{
|
||||
return lua.safe_script("return " + luaCode).get<T>();
|
||||
}
|
||||
|
||||
std::string getAsString(sol::state& lua, std::string luaCode)
|
||||
std::string getAsString(sol::state_view& lua, std::string luaCode)
|
||||
{
|
||||
return LuaUtil::toString(lua.safe_script("return " + luaCode));
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, Vector2)
|
||||
TEST_F(LuaUtilPackageTest, Vector2)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
sol::state_view lua = mLuaState.unsafeState();
|
||||
lua.safe_script("v = util.vector2(3, 4)");
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 3);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 4);
|
||||
|
|
@ -55,11 +66,9 @@ namespace
|
|||
EXPECT_TRUE(get<bool>(lua, "swizzle['01'] == util.vector2(0, 1) and swizzle['0y'] == util.vector2(0, 2)"));
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, Vector3)
|
||||
TEST_F(LuaUtilPackageTest, Vector3)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
sol::state_view lua = mLuaState.unsafeState();
|
||||
lua.safe_script("v = util.vector3(5, 12, 13)");
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
|
||||
|
|
@ -94,11 +103,9 @@ namespace
|
|||
get<bool>(lua, "swizzle['001'] == util.vector3(0, 0, 1) and swizzle['0yx'] == util.vector3(0, 2, 1)"));
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, Vector4)
|
||||
TEST_F(LuaUtilPackageTest, Vector4)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
sol::state_view lua = mLuaState.unsafeState();
|
||||
lua.safe_script("v = util.vector4(5, 12, 13, 15)");
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
|
||||
|
|
@ -136,11 +143,9 @@ namespace
|
|||
lua, "swizzle['0001'] == util.vector4(0, 0, 0, 1) and swizzle['0yx1'] == util.vector4(0, 2, 1, 1)"));
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, Color)
|
||||
TEST_F(LuaUtilPackageTest, Color)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
sol::state_view lua = mLuaState.unsafeState();
|
||||
lua.safe_script("brown = util.color.rgba(0.75, 0.25, 0, 1)");
|
||||
EXPECT_EQ(get<std::string>(lua, "tostring(brown)"), "(0.75, 0.25, 0, 1)");
|
||||
lua.safe_script("blue = util.color.rgb(0, 1, 0, 1)");
|
||||
|
|
@ -155,11 +160,9 @@ namespace
|
|||
EXPECT_TRUE(get<bool>(lua, "red:asRgb() == util.vector3(1, 0, 0)"));
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, Transform)
|
||||
TEST_F(LuaUtilPackageTest, Transform)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
sol::state_view lua = mLuaState.unsafeState();
|
||||
lua["T"] = lua["util"]["transform"];
|
||||
lua["v"] = lua["util"]["vector3"];
|
||||
EXPECT_ERROR(lua.safe_script("T.identity = nil"), "attempt to index");
|
||||
|
|
@ -191,11 +194,9 @@ namespace
|
|||
EXPECT_LT(get<float>(lua, "(rz_move_rx:inverse() * v(0, 1, 2) - v(1, 2, 3)):length()"), 1e-6);
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, UtilityFunctions)
|
||||
TEST_F(LuaUtilPackageTest, UtilityFunctions)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
sol::state_view lua = mLuaState.unsafeState();
|
||||
lua.safe_script("v = util.vector2(1, 0):rotate(math.rad(120))");
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), -0.5f);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 0.86602539f);
|
||||
|
|
@ -203,6 +204,10 @@ namespace
|
|||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(0.1, 0, 1.5)"), 0.1f);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(-0.1, 0, 1.5)"), 0);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(2.1, 0, 1.5)"), 1.5f);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.round(2.1)"), 2.0f);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.round(-2.1)"), -2.0f);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.remap(5, 0, 10, 0, 100)"), 50.0f);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.remap(-5, 0, 10, 0, 100)"), -50.0f);
|
||||
lua.safe_script("t = util.makeReadOnly({x = 1})");
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "t.x"), 1);
|
||||
EXPECT_ERROR(lua.safe_script("t.y = 2"), "userdata value");
|
||||
|
|
@ -26,6 +26,15 @@ namespace
|
|||
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({});
|
||||
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
|
||||
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/foo.mp3");
|
||||
|
||||
auto correctESM4SoundPath = [](auto path, auto* vfs) {
|
||||
return Misc::ResourceHelpers::correctResourcePath({ { "sound" } }, path, vfs, ".mp3");
|
||||
};
|
||||
|
||||
EXPECT_EQ(correctESM4SoundPath("foo.WAV", mVFS.get()), "sound\\foo.mp3");
|
||||
EXPECT_EQ(correctESM4SoundPath("SOUND/foo.WAV", mVFS.get()), "sound\\foo.mp3");
|
||||
EXPECT_EQ(correctESM4SoundPath("DATA\\SOUND\\foo.WAV", mVFS.get()), "sound\\foo.mp3");
|
||||
EXPECT_EQ(correctESM4SoundPath("\\Data/Sound\\foo.WAV", mVFS.get()), "sound\\foo.mp3");
|
||||
}
|
||||
|
||||
namespace
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <components/misc/strings/conversion.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#include <components/esm4/readerutils.hpp>
|
||||
#include <components/esm4/records.hpp>
|
||||
#include <components/esm4/typetraits.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
namespace EsmTool
|
||||
{
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ namespace ESSImport
|
|||
{
|
||||
ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory;
|
||||
|
||||
for (size_t i = 0; i < invState.mItems.size(); ++i)
|
||||
for (uint32_t i = 0; i < static_cast<uint32_t>(invState.mItems.size()); ++i)
|
||||
{
|
||||
// FIXME: in case of conflict (multiple items with this refID) use the already equipped one?
|
||||
if (invState.mItems[i].mRef.mRefID == refr.mActorData.mSelectedEnchantItem)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include "importercontext.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ Allowed options)");
|
|||
bpo::notify(variables);
|
||||
|
||||
Files::ConfigurationManager cfgManager(true);
|
||||
cfgManager.processPaths(variables, std::filesystem::current_path());
|
||||
cfgManager.readConfiguration(variables, desc);
|
||||
|
||||
const auto& essFile = variables["mwsave"].as<Files::MaybeQuotedPath>();
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@
|
|||
#include "utils/profilescombobox.hpp"
|
||||
#include "utils/textinputdialog.hpp"
|
||||
|
||||
#include "ui_directorypicker.h"
|
||||
|
||||
const char* Launcher::DataFilesPage::mDefaultContentListName = "Default";
|
||||
|
||||
namespace
|
||||
|
|
@ -155,6 +153,7 @@ namespace Launcher
|
|||
Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
|
||||
Config::LauncherSettings& launcherSettings, MainDialog* parent)
|
||||
: QWidget(parent)
|
||||
, mDirectoryPickerDialog(new QDialog(this))
|
||||
, mMainDialog(parent)
|
||||
, mCfgMgr(cfg)
|
||||
, mGameSettings(gameSettings)
|
||||
|
|
@ -163,6 +162,7 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C
|
|||
, mReloadCellsThread(&DataFilesPage::reloadCells, this)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
mDirectoryPicker.setupUi(mDirectoryPickerDialog);
|
||||
setObjectName("DataFilesPage");
|
||||
mSelector = new ContentSelectorView::ContentSelector(ui.contentSelectorWidget, /*showOMWScripts=*/true);
|
||||
const QString encoding = mGameSettings.value("encoding", { "win1252" }).value;
|
||||
|
|
@ -266,6 +266,7 @@ void Launcher::DataFilesPage::buildView()
|
|||
|
||||
buildArchiveContextMenu();
|
||||
buildDataFilesContextMenu();
|
||||
buildDirectoryPickerContextMenu();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::slotCopySelectedItemsPaths()
|
||||
|
|
@ -298,8 +299,10 @@ void Launcher::DataFilesPage::buildArchiveContextMenu()
|
|||
&DataFilesPage::slotShowArchiveContextMenu);
|
||||
|
||||
mArchiveContextMenu = new QMenu(ui.archiveListWidget);
|
||||
mArchiveContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems()));
|
||||
mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems()));
|
||||
mArchiveContextMenu->addAction(tr("&Check Selected"), this,
|
||||
[this]() { setCheckStateForMultiSelectedItems(ui.archiveListWidget, Qt::Checked); });
|
||||
mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this,
|
||||
[this]() { setCheckStateForMultiSelectedItems(ui.archiveListWidget, Qt::Unchecked); });
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::buildDataFilesContextMenu()
|
||||
|
|
@ -314,6 +317,18 @@ void Launcher::DataFilesPage::buildDataFilesContextMenu()
|
|||
tr("&Open Path in File Explorer"), this, &Launcher::DataFilesPage::slotOpenSelectedItemsPaths);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::buildDirectoryPickerContextMenu()
|
||||
{
|
||||
connect(mDirectoryPicker.dirListWidget, &QListWidget::customContextMenuRequested, this,
|
||||
&DataFilesPage::slotShowDirectoryPickerContextMenu);
|
||||
|
||||
mDirectoryPickerMenu = new QMenu(mDirectoryPicker.dirListWidget);
|
||||
mDirectoryPickerMenu->addAction(tr("&Check Selected"), this,
|
||||
[this]() { setCheckStateForMultiSelectedItems(mDirectoryPicker.dirListWidget, Qt::Checked); });
|
||||
mDirectoryPickerMenu->addAction(tr("&Uncheck Selected"), this,
|
||||
[this]() { setCheckStateForMultiSelectedItems(mDirectoryPicker.dirListWidget, Qt::Unchecked); });
|
||||
}
|
||||
|
||||
bool Launcher::DataFilesPage::loadSettings()
|
||||
{
|
||||
ui.navMeshMaxSizeSpinBox->setValue(getMaxNavMeshDbFileSizeMiB());
|
||||
|
|
@ -821,28 +836,22 @@ void Launcher::DataFilesPage::addSubdirectories(bool append)
|
|||
return;
|
||||
}
|
||||
|
||||
QDialog dialog;
|
||||
Ui::SelectSubdirs select;
|
||||
|
||||
select.setupUi(&dialog);
|
||||
mDirectoryPicker.dirListWidget->clear();
|
||||
|
||||
for (const auto& dir : subdirs)
|
||||
{
|
||||
if (!ui.directoryListWidget->findItems(dir, Qt::MatchFixedString).isEmpty())
|
||||
continue;
|
||||
const auto lastRow = select.dirListWidget->count();
|
||||
select.dirListWidget->addItem(dir);
|
||||
select.dirListWidget->item(lastRow)->setCheckState(Qt::Unchecked);
|
||||
QListWidgetItem* newDir = new QListWidgetItem(dir, mDirectoryPicker.dirListWidget);
|
||||
newDir->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
|
||||
dialog.show();
|
||||
|
||||
if (dialog.exec() == QDialog::Rejected)
|
||||
if (mDirectoryPickerDialog->exec() == QDialog::Rejected)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < select.dirListWidget->count(); ++i)
|
||||
for (int i = 0; i < mDirectoryPicker.dirListWidget->count(); ++i)
|
||||
{
|
||||
const auto* dir = select.dirListWidget->item(i);
|
||||
const auto* dir = mDirectoryPicker.dirListWidget->item(i);
|
||||
if (dir->checkState() == Qt::Checked)
|
||||
{
|
||||
ui.directoryListWidget->insertItem(selectedRow, dir->text());
|
||||
|
|
@ -893,38 +902,35 @@ void Launcher::DataFilesPage::removeDirectory()
|
|||
refreshDataFilesView();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::showContextMenu(QMenu* menu, QListWidget* list, const QPoint& pos)
|
||||
{
|
||||
QPoint globalPos = list->viewport()->mapToGlobal(pos);
|
||||
menu->exec(globalPos);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::slotShowArchiveContextMenu(const QPoint& pos)
|
||||
{
|
||||
QPoint globalPos = ui.archiveListWidget->viewport()->mapToGlobal(pos);
|
||||
mArchiveContextMenu->exec(globalPos);
|
||||
showContextMenu(mArchiveContextMenu, ui.archiveListWidget, pos);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::slotShowDataFilesContextMenu(const QPoint& pos)
|
||||
{
|
||||
QPoint globalPos = ui.directoryListWidget->viewport()->mapToGlobal(pos);
|
||||
mDataFilesContextMenu->exec(globalPos);
|
||||
showContextMenu(mDataFilesContextMenu, ui.directoryListWidget, pos);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(bool checked)
|
||||
void Launcher::DataFilesPage::slotShowDirectoryPickerContextMenu(const QPoint& pos)
|
||||
{
|
||||
Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked;
|
||||
showContextMenu(mDirectoryPickerMenu, mDirectoryPicker.dirListWidget, pos);
|
||||
}
|
||||
|
||||
for (QListWidgetItem* selectedItem : ui.archiveListWidget->selectedItems())
|
||||
void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(QListWidget* list, Qt::CheckState checkState)
|
||||
{
|
||||
for (QListWidgetItem* selectedItem : list->selectedItems())
|
||||
{
|
||||
selectedItem->setCheckState(checkState);
|
||||
}
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::slotUncheckMultiSelectedItems()
|
||||
{
|
||||
setCheckStateForMultiSelectedItems(false);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::slotCheckMultiSelectedItems()
|
||||
{
|
||||
setCheckStateForMultiSelectedItems(true);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::moveSources(QListWidget* sourceList, int step)
|
||||
{
|
||||
const QList<QPair<int, QListWidgetItem*>> sortedItems = sortedSelectedItems(sourceList, step > 0);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define DATAFILESPAGE_H
|
||||
|
||||
#include "ui_datafilespage.h"
|
||||
#include "ui_directorypicker.h"
|
||||
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
|
|
@ -46,8 +47,11 @@ namespace Launcher
|
|||
|
||||
ContentSelectorView::ContentSelector* mSelector;
|
||||
Ui::DataFilesPage ui;
|
||||
QDialog* mDirectoryPickerDialog;
|
||||
Ui::SelectSubdirs mDirectoryPicker;
|
||||
QMenu* mArchiveContextMenu;
|
||||
QMenu* mDataFilesContextMenu;
|
||||
QMenu* mDirectoryPickerMenu;
|
||||
|
||||
public:
|
||||
explicit DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
|
||||
|
|
@ -87,8 +91,7 @@ namespace Launcher
|
|||
|
||||
void slotShowArchiveContextMenu(const QPoint& pos);
|
||||
void slotShowDataFilesContextMenu(const QPoint& pos);
|
||||
void slotCheckMultiSelectedItems();
|
||||
void slotUncheckMultiSelectedItems();
|
||||
void slotShowDirectoryPickerContextMenu(const QPoint& pos);
|
||||
|
||||
void on_newProfileAction_triggered();
|
||||
void on_cloneProfileAction_triggered();
|
||||
|
|
@ -145,7 +148,9 @@ namespace Launcher
|
|||
void buildView();
|
||||
void buildArchiveContextMenu();
|
||||
void buildDataFilesContextMenu();
|
||||
void setCheckStateForMultiSelectedItems(bool checked);
|
||||
void buildDirectoryPickerContextMenu();
|
||||
void showContextMenu(QMenu* menu, QListWidget* list, const QPoint& pos);
|
||||
void setCheckStateForMultiSelectedItems(QListWidget* list, Qt::CheckState checkState);
|
||||
void setProfile(int index, bool savePrevious);
|
||||
void setProfile(const QString& previous, const QString& current, bool savePrevious);
|
||||
void removeProfile(const QString& profile);
|
||||
|
|
|
|||
|
|
@ -242,11 +242,36 @@ void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
|
|||
{
|
||||
if (mode == Settings::WindowMode::Fullscreen || mode == Settings::WindowMode::WindowedFullscreen)
|
||||
{
|
||||
QString customSizeMessage = tr("Custom window size is available only in Windowed mode.");
|
||||
QString windowBorderMessage = tr("Window border is available only in Windowed mode.");
|
||||
|
||||
standardRadioButton->toggle();
|
||||
customRadioButton->setEnabled(false);
|
||||
customWidthSpinBox->setEnabled(false);
|
||||
customHeightSpinBox->setEnabled(false);
|
||||
windowBorderCheckBox->setEnabled(false);
|
||||
windowBorderCheckBox->setToolTip(windowBorderMessage);
|
||||
customWidthSpinBox->setToolTip(customSizeMessage);
|
||||
customHeightSpinBox->setToolTip(customSizeMessage);
|
||||
customRadioButton->setToolTip(customSizeMessage);
|
||||
}
|
||||
|
||||
if (mode == Settings::WindowMode::Fullscreen)
|
||||
{
|
||||
resolutionComboBox->setEnabled(true);
|
||||
resolutionComboBox->setToolTip("");
|
||||
standardRadioButton->setToolTip("");
|
||||
}
|
||||
else if (mode == Settings::WindowMode::WindowedFullscreen)
|
||||
{
|
||||
QString fullScreenMessage = tr("Windowed Fullscreen mode always uses the native display resolution.");
|
||||
|
||||
resolutionComboBox->setEnabled(false);
|
||||
resolutionComboBox->setToolTip(fullScreenMessage);
|
||||
standardRadioButton->setToolTip(fullScreenMessage);
|
||||
|
||||
// Assume that a first item is a native screen resolution
|
||||
resolutionComboBox->setCurrentIndex(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -254,6 +279,13 @@ void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
|
|||
customWidthSpinBox->setEnabled(true);
|
||||
customHeightSpinBox->setEnabled(true);
|
||||
windowBorderCheckBox->setEnabled(true);
|
||||
resolutionComboBox->setEnabled(true);
|
||||
resolutionComboBox->setToolTip("");
|
||||
standardRadioButton->setToolTip("");
|
||||
windowBorderCheckBox->setToolTip("");
|
||||
customWidthSpinBox->setToolTip("");
|
||||
customHeightSpinBox->setToolTip("");
|
||||
customRadioButton->setToolTip("");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ int runLauncher(int argc, char* argv[])
|
|||
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
|
||||
}
|
||||
|
||||
l10n::installQtTranslations(app, "launcher", resourcesPath);
|
||||
L10n::installQtTranslations(app, "launcher", resourcesPath);
|
||||
|
||||
Launcher::MainDialog mainWin(configurationManager);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,14 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="dirListWidget"/>
|
||||
<widget class="QListWidget" name="dirListWidget">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,23 @@
|
|||
|
||||
namespace sfs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
// from configfileparser.cpp
|
||||
std::string trim_ws(const std::string& s)
|
||||
{
|
||||
std::string::size_type n, n2;
|
||||
n = s.find_first_not_of(" \t\r\n");
|
||||
if (n == std::string::npos)
|
||||
return std::string();
|
||||
else
|
||||
{
|
||||
n2 = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(n, n2 - n + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MwIniImporter::MwIniImporter()
|
||||
: mVerbose(false)
|
||||
, mEncoding(ToUTF8::WINDOWS_1250)
|
||||
|
|
@ -352,12 +369,10 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::filesystem::pat
|
|||
std::string line;
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
|
||||
// we cant say comment by only looking at first char anymore
|
||||
int comment_pos = static_cast<int>(line.find('#'));
|
||||
if (comment_pos > 0)
|
||||
// ignore comments - keep in sync with configfileparser.cpp
|
||||
if (line.find('#') == line.find_first_not_of(" \t\r\n"))
|
||||
{
|
||||
line = line.substr(0, comment_pos);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.empty())
|
||||
|
|
@ -373,6 +388,8 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::filesystem::pat
|
|||
|
||||
std::string key(line.substr(0, pos));
|
||||
std::string value(line.substr(pos + 1));
|
||||
key = trim_ws(key);
|
||||
value = trim_ws(value);
|
||||
|
||||
if (map.find(key) == map.end())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
class MwIniImporter
|
||||
{
|
||||
|
|
|
|||
|
|
@ -126,12 +126,20 @@ int wmain(int argc, wchar_t* wargv[])
|
|||
MwIniImporter importer;
|
||||
importer.setVerbose(vm.count("verbose") != 0);
|
||||
|
||||
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
|
||||
|
||||
// Font encoding settings
|
||||
std::string encoding(vm["encoding"].as<std::string>());
|
||||
std::string encoding;
|
||||
if (vm["encoding"].defaulted() && cfg.contains("encoding") && !cfg["encoding"].empty())
|
||||
encoding = cfg["encoding"].back();
|
||||
else
|
||||
{
|
||||
encoding = vm["encoding"].as<std::string>();
|
||||
cfg["encoding"] = { encoding };
|
||||
}
|
||||
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
|
||||
|
||||
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
|
||||
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
|
||||
|
||||
if (!vm.count("fonts"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include <components/resource/niffilemanager.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
|
@ -143,6 +143,7 @@ namespace NavMeshTool
|
|||
}
|
||||
|
||||
Files::ConfigurationManager config;
|
||||
config.processPaths(variables, std::filesystem::current_path());
|
||||
config.readConfiguration(variables, desc);
|
||||
|
||||
Debug::setupLogging(config.getLogPath(), applicationName);
|
||||
|
|
@ -188,7 +189,7 @@ namespace NavMeshTool
|
|||
|
||||
VFS::Manager vfs;
|
||||
|
||||
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
||||
VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder());
|
||||
|
||||
Settings::Manager::load(config);
|
||||
|
||||
|
|
|
|||
|
|
@ -201,8 +201,8 @@ namespace NavMeshTool
|
|||
{
|
||||
if (!land.has_value() || osg::Vec2i(land->mX, land->mY) != cellPosition
|
||||
|| (land->mDataTypes & ESM::Land::DATA_VHGT) == 0)
|
||||
return { HeightfieldPlane{ ESM::Land::DEFAULT_HEIGHT }, ESM::Land::DEFAULT_HEIGHT,
|
||||
ESM::Land::DEFAULT_HEIGHT };
|
||||
return { HeightfieldPlane{ static_cast<float>(ESM::Land::DEFAULT_HEIGHT) },
|
||||
static_cast<float>(ESM::Land::DEFAULT_HEIGHT), static_cast<float>(ESM::Land::DEFAULT_HEIGHT) };
|
||||
|
||||
ESM::Land::LandData& landData = *landDatas.emplace_back(std::make_unique<ESM::Land::LandData>());
|
||||
land->loadData(ESM::Land::DATA_VHGT, landData);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ bool isBSA(const std::filesystem::path& path)
|
|||
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
|
||||
{
|
||||
if (isBSA(path))
|
||||
return VFS::makeBsaArchive(path);
|
||||
return VFS::makeBsaArchive(path, nullptr);
|
||||
if (std::filesystem::is_directory(path))
|
||||
return std::make_unique<VFS::FileSystemArchive>(path);
|
||||
return nullptr;
|
||||
|
|
@ -198,7 +198,7 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
|||
{
|
||||
try
|
||||
{
|
||||
readVFS(VFS::makeBsaArchive(file.second), file.second, quiet);
|
||||
readVFS(VFS::makeBsaArchive(file.second, nullptr), file.second, quiet);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ opencs_units (view/render
|
|||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
||||
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
|
||||
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands
|
||||
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands objectmarker
|
||||
)
|
||||
|
||||
opencs_units (view/render
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
#include <components/misc/rng.hpp>
|
||||
#include <components/nifosg/nifloader.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include "view/doc/viewmanager.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include <apps/opencs/model/world/universalid.hpp>
|
||||
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/idcompletionmanager.hpp"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include "loader.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,8 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#endif
|
||||
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
|
@ -55,16 +52,17 @@ void CSMDoc::Runner::start(bool delayed)
|
|||
|
||||
QString path = "openmw";
|
||||
#ifdef Q_OS_WIN
|
||||
path.append(QString(".exe"));
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
path = dir.absoluteFilePath(path.prepend("OpenMW.app/Contents/MacOS/"));
|
||||
#else
|
||||
path.prepend(QString("./"));
|
||||
path.append(QLatin1String(".exe"));
|
||||
#endif
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
#ifdef Q_OS_MAC
|
||||
// the CS and engine are in separate .app directories
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
path.prepend("OpenMW.app/Contents/MacOS/");
|
||||
#endif
|
||||
path = dir.absoluteFilePath(path);
|
||||
|
||||
mStartup = new QTemporaryFile(this);
|
||||
mStartup->open();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include "operation.hpp"
|
||||
#include "savingstate.hpp"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
#include <components/misc/algorithm.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ namespace CSMPrefs
|
|||
}
|
||||
}
|
||||
|
||||
bool ShortcutManager::getModifier(const std::string& name, int& modifier) const
|
||||
bool ShortcutManager::getModifier(std::string_view name, int& modifier) const
|
||||
{
|
||||
ModifierMap::const_iterator item = mModifiers.find(name);
|
||||
if (item != mModifiers.end())
|
||||
|
|
@ -175,14 +175,14 @@ namespace CSMPrefs
|
|||
return concat;
|
||||
}
|
||||
|
||||
void ShortcutManager::convertFromString(const std::string& data, QKeySequence& sequence) const
|
||||
void ShortcutManager::convertFromString(std::string_view data, QKeySequence& sequence) const
|
||||
{
|
||||
const int MaxKeys = 4; // A limitation of QKeySequence
|
||||
|
||||
size_t end = data.find(';');
|
||||
size_t size = std::min(end, data.size());
|
||||
|
||||
std::string value = data.substr(0, size);
|
||||
std::string_view value = data.substr(0, size);
|
||||
size_t start = 0;
|
||||
|
||||
int keyPos = 0;
|
||||
|
|
@ -195,7 +195,7 @@ namespace CSMPrefs
|
|||
end = data.find('+', start);
|
||||
end = std::min(end, value.size());
|
||||
|
||||
std::string name = value.substr(start, end - start);
|
||||
std::string_view name = value.substr(start, end - start);
|
||||
|
||||
if (name == "Ctrl")
|
||||
{
|
||||
|
|
@ -242,12 +242,12 @@ namespace CSMPrefs
|
|||
sequence = QKeySequence(keys[0], keys[1], keys[2], keys[3]);
|
||||
}
|
||||
|
||||
void ShortcutManager::convertFromString(const std::string& data, int& modifier) const
|
||||
void ShortcutManager::convertFromString(std::string_view data, int& modifier) const
|
||||
{
|
||||
size_t start = data.find(';') + 1;
|
||||
start = std::min(start, data.size());
|
||||
|
||||
std::string name = data.substr(start);
|
||||
std::string_view name = data.substr(start);
|
||||
KeyMap::const_iterator searchResult = mKeys.find(name);
|
||||
if (searchResult != mKeys.end())
|
||||
{
|
||||
|
|
@ -259,7 +259,7 @@ namespace CSMPrefs
|
|||
}
|
||||
}
|
||||
|
||||
void ShortcutManager::convertFromString(const std::string& data, QKeySequence& sequence, int& modifier) const
|
||||
void ShortcutManager::convertFromString(std::string_view data, QKeySequence& sequence, int& modifier) const
|
||||
{
|
||||
convertFromString(data, sequence);
|
||||
convertFromString(data, modifier);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace CSMPrefs
|
|||
bool getSequence(std::string_view name, QKeySequence& sequence) const;
|
||||
void setSequence(std::string_view name, const QKeySequence& sequence);
|
||||
|
||||
bool getModifier(const std::string& name, int& modifier) const;
|
||||
bool getModifier(std::string_view name, int& modifier) const;
|
||||
void setModifier(std::string_view name, int modifier);
|
||||
|
||||
std::string convertToString(const QKeySequence& sequence) const;
|
||||
|
|
@ -39,10 +39,10 @@ namespace CSMPrefs
|
|||
|
||||
std::string convertToString(const QKeySequence& sequence, int modifier) const;
|
||||
|
||||
void convertFromString(const std::string& data, QKeySequence& sequence) const;
|
||||
void convertFromString(const std::string& data, int& modifier) const;
|
||||
void convertFromString(std::string_view data, QKeySequence& sequence) const;
|
||||
void convertFromString(std::string_view data, int& modifier) const;
|
||||
|
||||
void convertFromString(const std::string& data, QKeySequence& sequence, int& modifier) const;
|
||||
void convertFromString(std::string_view data, QKeySequence& sequence, int& modifier) const;
|
||||
|
||||
/// Replaces "{sequence-name}" or "{modifier-name}" with the appropriate text
|
||||
QString processToolTip(const QString& toolTip) const;
|
||||
|
|
@ -53,7 +53,7 @@ namespace CSMPrefs
|
|||
typedef std::map<std::string, QKeySequence, std::less<>> SequenceMap;
|
||||
typedef std::map<std::string, int, std::less<>> ModifierMap;
|
||||
typedef std::map<int, std::string> NameMap;
|
||||
typedef std::map<std::string, int> KeyMap;
|
||||
typedef std::map<std::string, int, std::less<>> KeyMap;
|
||||
|
||||
ShortcutMap mShortcuts;
|
||||
SequenceMap mSequences;
|
||||
|
|
|
|||
|
|
@ -180,7 +180,10 @@ void CSMPrefs::State::declare()
|
|||
declareInt(mValues->mRendering.mCameraOrthoSize, "Orthographic Projection Size Parameter")
|
||||
.setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world.")
|
||||
.setRange(10, 10000);
|
||||
declareDouble(mValues->mRendering.mObjectMarkerAlpha, "Object Marker Transparency").setPrecision(2).setRange(0, 1);
|
||||
declareDouble(mValues->mRendering.mObjectMarkerScale, "Object Marker Scale Factor")
|
||||
.setPrecision(2)
|
||||
.setRange(.01f, 100.f)
|
||||
.setTooltip("Multiplier for the size of object selection markers.");
|
||||
declareBool(mValues->mRendering.mSceneUseGradient, "Use Gradient Background");
|
||||
declareColour(mValues->mRendering.mSceneDayBackgroundColour, "Day Background Colour");
|
||||
declareColour(mValues->mRendering.mSceneDayGradientColour, "Day Gradient Colour")
|
||||
|
|
@ -376,6 +379,7 @@ void CSMPrefs::State::declare()
|
|||
declareShortcut(mValues->mKeyBindings.mSceneScaleSubmode, "Scale Object Submode");
|
||||
declareShortcut(mValues->mKeyBindings.mSceneRotateSubmode, "Rotate Object Submode");
|
||||
declareShortcut(mValues->mKeyBindings.mSceneCameraCycle, "Cycle Camera Mode");
|
||||
declareShortcut(mValues->mKeyBindings.mSceneToggleMarker, "Toggle Selection Marker");
|
||||
|
||||
declareSubcategory("1st/Free Camera");
|
||||
declareShortcut(mValues->mKeyBindings.mFreeForward, "Forward");
|
||||
|
|
@ -498,7 +502,7 @@ CSMPrefs::ShortcutSetting& CSMPrefs::State::declareShortcut(
|
|||
// Setup with actual data
|
||||
QKeySequence sequence;
|
||||
|
||||
getShortcutManager().convertFromString(value, sequence);
|
||||
getShortcutManager().convertFromString(value.get(), sequence);
|
||||
getShortcutManager().setSequence(value.mName, sequence);
|
||||
|
||||
CSMPrefs::ShortcutSetting* setting
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ namespace CSMPrefs
|
|||
Settings::SettingValue<int> mCameraFov{ mIndex, sName, "camera-fov", 90 };
|
||||
Settings::SettingValue<bool> mCameraOrtho{ mIndex, sName, "camera-ortho", false };
|
||||
Settings::SettingValue<int> mCameraOrthoSize{ mIndex, sName, "camera-ortho-size", 100 };
|
||||
Settings::SettingValue<double> mObjectMarkerAlpha{ mIndex, sName, "object-marker-alpha", 0.5 };
|
||||
Settings::SettingValue<double> mObjectMarkerScale{ mIndex, sName, "object-marker-scale", 5.0 };
|
||||
Settings::SettingValue<bool> mSceneUseGradient{ mIndex, sName, "scene-use-gradient", true };
|
||||
Settings::SettingValue<std::string> mSceneDayBackgroundColour{ mIndex, sName, "scene-day-background-colour",
|
||||
"#6e7880" };
|
||||
|
|
@ -491,7 +491,7 @@ namespace CSMPrefs
|
|||
Settings::SettingValue<std::string> mSceneScaleSubmode{ mIndex, sName, "scene-submode-scale", "V" };
|
||||
Settings::SettingValue<std::string> mSceneRotateSubmode{ mIndex, sName, "scene-submode-rotate", "R" };
|
||||
Settings::SettingValue<std::string> mSceneCameraCycle{ mIndex, sName, "scene-cam-cycle", "Tab" };
|
||||
Settings::SettingValue<std::string> mSceneToggleMarkers{ mIndex, sName, "scene-toggle-markers", "F4" };
|
||||
Settings::SettingValue<std::string> mSceneToggleMarker{ mIndex, sName, "scene-toggle-marker", "F4" };
|
||||
Settings::SettingValue<std::string> mFreeForward{ mIndex, sName, "free-forward", "W" };
|
||||
Settings::SettingValue<std::string> mFreeBackward{ mIndex, sName, "free-backward", "S" };
|
||||
Settings::SettingValue<std::string> mFreeLeft{ mIndex, sName, "free-left", "A" };
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include "../doc/operation.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include <apps/opencs/model/world/idcollection.hpp>
|
||||
#include <apps/opencs/model/world/record.hpp>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <apps/opencs/model/world/universalid.hpp>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
|
|||
, mArchives(archives)
|
||||
, mVFS(std::make_unique<VFS::Manager>())
|
||||
{
|
||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true);
|
||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder());
|
||||
|
||||
mResourcesManager.setVFS(mVFS.get());
|
||||
|
||||
|
|
@ -1465,7 +1465,7 @@ std::vector<ESM::RefId> CSMWorld::Data::getIds(bool listDeleted) const
|
|||
void CSMWorld::Data::assetsChanged()
|
||||
{
|
||||
mVFS.get()->reset();
|
||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true);
|
||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder());
|
||||
|
||||
const UniversalId assetTableIds[] = { UniversalId::Type_Meshes, UniversalId::Type_Icons, UniversalId::Type_Musics,
|
||||
UniversalId::Type_SoundsRes, UniversalId::Type_Textures, UniversalId::Type_Videos };
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
#include <components/esm3/selectiongroup.hpp>
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/misc/algorithm.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
|
||||
#include "cell.hpp"
|
||||
#include "idcollection.hpp"
|
||||
|
|
|
|||
|
|
@ -625,8 +625,6 @@ bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue(
|
|||
default:
|
||||
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
|
|
@ -651,8 +649,6 @@ bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue(
|
|||
default:
|
||||
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant CSMWorld::ConstInfoSelectWrapper::getValue() const
|
||||
|
|
|
|||
|
|
@ -753,7 +753,6 @@ void CSMWorld::RefIdCollection::cloneRecord(
|
|||
bool CSMWorld::RefIdCollection::touchRecord(const ESM::RefId& id)
|
||||
{
|
||||
throw std::runtime_error("RefIdCollection::touchRecord is unimplemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
void CSMWorld::RefIdCollection::appendRecord(std::unique_ptr<RecordBase> record, UniversalId::Type type)
|
||||
|
|
|
|||
|
|
@ -25,9 +25,10 @@
|
|||
#include "cellwater.hpp"
|
||||
#include "instancedragmodes.hpp"
|
||||
#include "mask.hpp"
|
||||
#include "object.hpp"
|
||||
#include "objectmarker.hpp"
|
||||
#include "pathgrid.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
#include "worldspacewidget.hpp"
|
||||
|
||||
#include <apps/opencs/model/world/cell.hpp>
|
||||
#include <apps/opencs/model/world/cellcoordinates.hpp>
|
||||
|
|
@ -107,9 +108,6 @@ bool CSVRender::Cell::addObjects(int start, int end)
|
|||
|
||||
auto object = std::make_unique<Object>(mData, mCellNode, id, false);
|
||||
|
||||
if (mSubModeElementMask & Mask_Reference)
|
||||
object->setSubMode(mSubMode);
|
||||
|
||||
mObjects.insert(std::make_pair(id, object.release()));
|
||||
modified = true;
|
||||
}
|
||||
|
|
@ -168,9 +166,10 @@ void CSVRender::Cell::unloadLand()
|
|||
mCellBorder.reset();
|
||||
}
|
||||
|
||||
CSVRender::Cell::Cell(
|
||||
CSMDoc::Document& document, osg::Group* rootNode, const std::string& id, bool deleted, bool isExterior)
|
||||
: mData(document.getData())
|
||||
CSVRender::Cell::Cell(CSMDoc::Document& document, ObjectMarker* selectionMarker, osg::Group* rootNode,
|
||||
const std::string& id, bool deleted, bool isExterior)
|
||||
: mSelectionMarker(selectionMarker)
|
||||
, mData(document.getData())
|
||||
, mId(ESM::RefId::stringRefId(id))
|
||||
, mDeleted(deleted)
|
||||
, mSubMode(0)
|
||||
|
|
@ -466,7 +465,10 @@ void CSVRender::Cell::setSelection(int elementMask, Selection mode)
|
|||
}
|
||||
|
||||
iter->second->setSelected(selected);
|
||||
if (selected)
|
||||
mSelectionMarker->addToSelectionHistory(iter->second->getReferenceId(), false);
|
||||
}
|
||||
mSelectionMarker->updateSelectionMarker();
|
||||
}
|
||||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||
{
|
||||
|
|
@ -506,8 +508,10 @@ void CSVRender::Cell::selectAllWithSameParentId(int elementMask)
|
|||
if (!iter->second->getSelected() && ids.find(iter->second->getReferenceableId()) != ids.end())
|
||||
{
|
||||
iter->second->setSelected(true);
|
||||
mSelectionMarker->addToSelectionHistory(iter->second->getReferenceId(), false);
|
||||
}
|
||||
}
|
||||
mSelectionMarker->updateSelectionMarker();
|
||||
}
|
||||
|
||||
void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)
|
||||
|
|
@ -520,6 +524,9 @@ void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)
|
|||
|
||||
else if (dragMode == DragMode_Select_Invert)
|
||||
object->setSelected(!object->getSelected());
|
||||
|
||||
if (object->getSelected())
|
||||
mSelectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||
}
|
||||
|
||||
void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode)
|
||||
|
|
@ -542,6 +549,8 @@ void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
mSelectionMarker->updateSelectionMarker();
|
||||
}
|
||||
|
||||
void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode)
|
||||
|
|
@ -555,6 +564,8 @@ void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distan
|
|||
if (distanceFromObject < distance)
|
||||
handleSelectDrag(object.second, dragMode);
|
||||
}
|
||||
|
||||
mSelectionMarker->updateSelectionMarker();
|
||||
}
|
||||
|
||||
void CSVRender::Cell::setCellArrows(int mask)
|
||||
|
|
@ -625,9 +636,11 @@ void CSVRender::Cell::selectFromGroup(const std::vector<std::string>& group)
|
|||
if (objectName == object->getReferenceId())
|
||||
{
|
||||
object->setSelected(true, osg::Vec4f(1, 0, 1, 1));
|
||||
mSelectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
mSelectionMarker->updateSelectionMarker();
|
||||
}
|
||||
|
||||
void CSVRender::Cell::unhideAll()
|
||||
|
|
@ -673,8 +686,7 @@ void CSVRender::Cell::setSubMode(int subMode, unsigned int elementMask)
|
|||
mSubModeElementMask = elementMask;
|
||||
|
||||
if (elementMask & Mask_Reference)
|
||||
for (std::map<std::string, Object*>::const_iterator iter(mObjects.begin()); iter != mObjects.end(); ++iter)
|
||||
iter->second->setSubMode(subMode);
|
||||
mSelectionMarker->setSubMode(subMode);
|
||||
}
|
||||
|
||||
void CSVRender::Cell::reset(unsigned int elementMask)
|
||||
|
|
@ -685,3 +697,11 @@ void CSVRender::Cell::reset(unsigned int elementMask)
|
|||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||
mPathgrid->resetIndicators();
|
||||
}
|
||||
|
||||
CSVRender::Object* CSVRender::Cell::getObjectByReferenceId(const std::string& referenceId)
|
||||
{
|
||||
if (auto iter = mObjects.find(Misc::StringUtils::lowerCase(referenceId)); iter != mObjects.end())
|
||||
return iter->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
#include <osg/Vec3d>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include "../../model/doc/document.hpp"
|
||||
#include "../../model/world/cellcoordinates.hpp"
|
||||
#include "instancedragmodes.hpp"
|
||||
#include "worldspacewidget.hpp"
|
||||
#include <components/esm/refid.hpp>
|
||||
#include <components/misc/algorithm.hpp>
|
||||
|
||||
|
|
@ -44,8 +44,11 @@ namespace CSVRender
|
|||
class CellBorder;
|
||||
class CellMarker;
|
||||
|
||||
class ObjectMarker;
|
||||
|
||||
class Cell
|
||||
{
|
||||
ObjectMarker* const mSelectionMarker;
|
||||
CSMWorld::Data& mData;
|
||||
ESM::RefId mId;
|
||||
osg::ref_ptr<osg::Group> mCellNode;
|
||||
|
|
@ -90,8 +93,8 @@ namespace CSVRender
|
|||
public:
|
||||
/// \note Deleted covers both cells that are deleted and cells that don't exist in
|
||||
/// the first place.
|
||||
Cell(CSMDoc::Document& document, osg::Group* rootNode, const std::string& id, bool deleted = false,
|
||||
bool isExterior = false);
|
||||
Cell(CSMDoc::Document& document, ObjectMarker* selectionMarker, osg::Group* rootNode, const std::string& id,
|
||||
bool deleted = false, bool isExterior = false);
|
||||
|
||||
~Cell();
|
||||
|
||||
|
|
@ -182,6 +185,8 @@ namespace CSVRender
|
|||
/// true state.
|
||||
void reset(unsigned int elementMask);
|
||||
|
||||
CSVRender::Object* getObjectByReferenceId(const std::string& referenceId);
|
||||
|
||||
friend class CellNodeCallback;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,16 +171,18 @@ osg::Vec3f CSVRender::InstanceMode::getProjectionSpaceCoords(const osg::Vec3f& p
|
|||
|
||||
osg::Vec3f CSVRender::InstanceMode::getMousePlaneCoords(const QPoint& point, const osg::Vec3d& dragStart)
|
||||
{
|
||||
osg::Matrix viewMatrix;
|
||||
viewMatrix.invert(getWorldspaceWidget().getCamera()->getViewMatrix());
|
||||
osg::Matrix projMatrix;
|
||||
projMatrix.invert(getWorldspaceWidget().getCamera()->getProjectionMatrix());
|
||||
osg::Matrix combined = projMatrix * viewMatrix;
|
||||
const osg::Matrix viewMatrix = getWorldspaceWidget().getCamera()->getViewMatrix();
|
||||
const osg::Matrix projMatrix = getWorldspaceWidget().getCamera()->getProjectionMatrix();
|
||||
const osg::Matrix combined = osg::Matrix::inverse(viewMatrix * projMatrix);
|
||||
|
||||
/* calculate viewport normalized coordinates
|
||||
note: is there a reason to use getCamera()->getViewport()->computeWindowMatrix() instead? */
|
||||
float x = (point.x() * 2) / getWorldspaceWidget().getCamera()->getViewport()->width() - 1.0f;
|
||||
float y = 1.0f - (point.y() * 2) / getWorldspaceWidget().getCamera()->getViewport()->height();
|
||||
const float scale = getWorldspaceWidget().devicePixelRatioF();
|
||||
const osg::Viewport* viewport = getWorldspaceWidget().getCamera()->getViewport();
|
||||
float x = point.x() * scale / viewport->width();
|
||||
float y = point.y() * scale / viewport->height();
|
||||
x = x * 2.0f - 1.0f;
|
||||
y = 1.0f - y * 2.0f;
|
||||
|
||||
osg::Vec3f mousePlanePoint = osg::Vec3f(x, y, dragStart.z()) * combined;
|
||||
|
||||
|
|
@ -360,7 +362,29 @@ CSVRender::InstanceMode::InstanceMode(
|
|||
|
||||
for (const char axis : "xyz")
|
||||
connect(new CSMPrefs::Shortcut(std::string("scene-axis-") + axis, worldspaceWidget),
|
||||
qOverload<>(&CSMPrefs::Shortcut::activated), this, [this, axis] { this->setDragAxis(axis); });
|
||||
qOverload<>(&CSMPrefs::Shortcut::activated), this, [this, axis] {
|
||||
this->setDragAxis(axis);
|
||||
std::string axisStr(1, toupper(axis));
|
||||
switch (getSubMode())
|
||||
{
|
||||
case (Object::Mode_Move):
|
||||
axisStr += "_Axis";
|
||||
break;
|
||||
case (Object::Mode_Rotate):
|
||||
axisStr += "_Axis_Rot";
|
||||
break;
|
||||
case (Object::Mode_Scale):
|
||||
axisStr += "_Axis_Scale";
|
||||
break;
|
||||
}
|
||||
|
||||
auto selectionMarker = getWorldspaceWidget().getSelectionMarker();
|
||||
|
||||
if (mDragAxis != -1)
|
||||
selectionMarker->updateMarkerHighlight(axisStr, axis - 'x');
|
||||
else
|
||||
selectionMarker->resetMarkerHighlight();
|
||||
});
|
||||
}
|
||||
|
||||
void CSVRender::InstanceMode::activate(CSVWidget::SceneToolbar* toolbar)
|
||||
|
|
@ -458,52 +482,58 @@ void CSVRender::InstanceMode::secondaryEditPressed(const WorldspaceHitResult& hi
|
|||
|
||||
void CSVRender::InstanceMode::primarySelectPressed(const WorldspaceHitResult& hit)
|
||||
{
|
||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
||||
auto& worldspaceWidget = getWorldspaceWidget();
|
||||
|
||||
if (hit.tag)
|
||||
worldspaceWidget.clearSelection(Mask_Reference);
|
||||
|
||||
if (!hit.tag)
|
||||
return;
|
||||
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||
{
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||
{
|
||||
// hit an Object, select it
|
||||
CSVRender::Object* object = objectTag->mObject;
|
||||
object->setSelected(true);
|
||||
return;
|
||||
}
|
||||
// hit an Object, select it
|
||||
CSVRender::Object* object = objectTag->mObject;
|
||||
object->setSelected(true);
|
||||
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||
}
|
||||
}
|
||||
|
||||
void CSVRender::InstanceMode::secondarySelectPressed(const WorldspaceHitResult& hit)
|
||||
{
|
||||
if (hit.tag)
|
||||
if (!hit.tag)
|
||||
return;
|
||||
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||
{
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||
{
|
||||
// hit an Object, toggle its selection state
|
||||
CSVRender::Object* object = objectTag->mObject;
|
||||
object->setSelected(!object->getSelected());
|
||||
return;
|
||||
}
|
||||
// hit an Object, toggle its selection state
|
||||
CSVRender::Object* object = objectTag->mObject;
|
||||
object->setSelected(!object->getSelected());
|
||||
|
||||
const auto selectionMarker = getWorldspaceWidget().getSelectionMarker();
|
||||
|
||||
if (object->getSelected())
|
||||
selectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||
|
||||
selectionMarker->updateSelectionMarker();
|
||||
}
|
||||
}
|
||||
|
||||
void CSVRender::InstanceMode::tertiarySelectPressed(const WorldspaceHitResult& hit)
|
||||
{
|
||||
auto* snapTarget = dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get());
|
||||
|
||||
if (snapTarget)
|
||||
if (auto* snapTarget
|
||||
= dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get()))
|
||||
{
|
||||
snapTarget->mObject->setSnapTarget(false);
|
||||
}
|
||||
|
||||
if (hit.tag)
|
||||
if (!hit.tag)
|
||||
return;
|
||||
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||
{
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||
{
|
||||
// hit an Object, toggle its selection state
|
||||
CSVRender::Object* object = objectTag->mObject;
|
||||
object->setSnapTarget(!object->getSnapTarget());
|
||||
return;
|
||||
}
|
||||
// hit an Object, toggle its selection state
|
||||
CSVRender::Object* object = objectTag->mObject;
|
||||
object->setSnapTarget(!object->getSnapTarget());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -512,23 +542,26 @@ bool CSVRender::InstanceMode::primaryEditStartDrag(const QPoint& pos)
|
|||
if (mDragMode != DragMode_None || mLocked)
|
||||
return false;
|
||||
|
||||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
|
||||
auto& worldspaceWidget = getWorldspaceWidget();
|
||||
|
||||
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
||||
WorldspaceHitResult hit = worldspaceWidget.mousePick(pos, worldspaceWidget.getInteractionMask());
|
||||
|
||||
std::vector<osg::ref_ptr<TagBase>> selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||
if (selection.empty())
|
||||
{
|
||||
// Only change selection at the start of drag if no object is already selected
|
||||
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
||||
{
|
||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
||||
worldspaceWidget.clearSelection(Mask_Reference);
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||
{
|
||||
CSVRender::Object* object = objectTag->mObject;
|
||||
object->setSelected(true);
|
||||
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||
}
|
||||
}
|
||||
|
||||
selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
||||
selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||
if (selection.empty())
|
||||
return false;
|
||||
}
|
||||
|
|
@ -589,23 +622,26 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag(const QPoint& pos)
|
|||
if (mDragMode != DragMode_None || mLocked)
|
||||
return false;
|
||||
|
||||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
|
||||
auto& worldspaceWidget = getWorldspaceWidget();
|
||||
|
||||
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
||||
WorldspaceHitResult hit = worldspaceWidget.mousePick(pos, worldspaceWidget.getInteractionMask());
|
||||
|
||||
std::vector<osg::ref_ptr<TagBase>> selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||
if (selection.empty())
|
||||
{
|
||||
// Only change selection at the start of drag if no object is already selected
|
||||
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
||||
{
|
||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
||||
worldspaceWidget.clearSelection(Mask_Reference);
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||
{
|
||||
CSVRender::Object* object = objectTag->mObject;
|
||||
object->setSelected(true);
|
||||
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||
}
|
||||
}
|
||||
|
||||
selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
||||
selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||
if (selection.empty())
|
||||
return false;
|
||||
}
|
||||
|
|
@ -639,10 +675,10 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag(const QPoint& pos)
|
|||
mDragMode = DragMode_Scale_Snap;
|
||||
|
||||
// Calculate scale factor
|
||||
std::vector<osg::ref_ptr<TagBase>> editedSelection = getWorldspaceWidget().getEdited(Mask_Reference);
|
||||
std::vector<osg::ref_ptr<TagBase>> editedSelection = worldspaceWidget.getEdited(Mask_Reference);
|
||||
osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection));
|
||||
|
||||
int widgetHeight = getWorldspaceWidget().height();
|
||||
int widgetHeight = worldspaceWidget.height();
|
||||
|
||||
float dx = pos.x() - center.x();
|
||||
float dy = (widgetHeight - pos.y()) - center.y();
|
||||
|
|
|
|||
|
|
@ -18,25 +18,11 @@
|
|||
#include <apps/opencs/model/world/universalid.hpp>
|
||||
#include <apps/opencs/view/render/tagbase.hpp>
|
||||
|
||||
#include <osg/Array>
|
||||
#include <osg/BoundingSphere>
|
||||
#include <osg/GL>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Group>
|
||||
#include <osg/Math>
|
||||
#include <osg/Node>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Quat>
|
||||
#include <osg/Shape>
|
||||
#include <osg/ShapeDrawable>
|
||||
#include <osg/StateAttribute>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Vec3>
|
||||
|
||||
#include <osgFX/Scribe>
|
||||
|
||||
#include "../../model/prefs/state.hpp"
|
||||
#include "../../model/world/cellcoordinates.hpp"
|
||||
#include "../../model/world/commandmacro.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
|
|
@ -63,11 +49,6 @@ namespace ESM
|
|||
struct Light;
|
||||
}
|
||||
|
||||
const float CSVRender::Object::MarkerShaftWidth = 30;
|
||||
const float CSVRender::Object::MarkerShaftBaseLength = 70;
|
||||
const float CSVRender::Object::MarkerHeadWidth = 50;
|
||||
const float CSVRender::Object::MarkerHeadLength = 50;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
|
@ -95,12 +76,6 @@ QString CSVRender::ObjectTag::getToolTip(bool /*hideBasics*/, const WorldspaceHi
|
|||
return QString::fromUtf8(mObject->getReferenceableId().c_str());
|
||||
}
|
||||
|
||||
CSVRender::ObjectMarkerTag::ObjectMarkerTag(Object* object, int axis)
|
||||
: ObjectTag(object)
|
||||
, mAxis(axis)
|
||||
{
|
||||
}
|
||||
|
||||
void CSVRender::Object::clear() {}
|
||||
|
||||
void CSVRender::Object::update()
|
||||
|
|
@ -204,238 +179,6 @@ const CSMWorld::CellRef& CSVRender::Object::getReference() const
|
|||
return mData.getReferences().getRecord(mReferenceId).get();
|
||||
}
|
||||
|
||||
void CSVRender::Object::updateMarker()
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (mMarker[i])
|
||||
{
|
||||
mRootNode->removeChild(mMarker[i]);
|
||||
mMarker[i] = osg::ref_ptr<osg::Node>();
|
||||
}
|
||||
|
||||
if (mSelected)
|
||||
{
|
||||
if (mSubMode == 0)
|
||||
{
|
||||
mMarker[i] = makeMoveOrScaleMarker(i);
|
||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
||||
|
||||
mRootNode->addChild(mMarker[i]);
|
||||
}
|
||||
else if (mSubMode == 1)
|
||||
{
|
||||
mMarker[i] = makeRotateMarker(i);
|
||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
||||
|
||||
mRootNode->addChild(mMarker[i]);
|
||||
}
|
||||
else if (mSubMode == 2)
|
||||
{
|
||||
mMarker[i] = makeMoveOrScaleMarker(i);
|
||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
||||
|
||||
mRootNode->addChild(mMarker[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node> CSVRender::Object::makeMoveOrScaleMarker(int axis)
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
|
||||
|
||||
float shaftLength = MarkerShaftBaseLength + mBaseNode->getBound().radius();
|
||||
|
||||
// shaft
|
||||
osg::Vec3Array* vertices = new osg::Vec3Array;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
float length = i ? shaftLength : MarkerShaftWidth;
|
||||
|
||||
vertices->push_back(getMarkerPosition(-MarkerShaftWidth / 2, -MarkerShaftWidth / 2, length, axis));
|
||||
vertices->push_back(getMarkerPosition(-MarkerShaftWidth / 2, MarkerShaftWidth / 2, length, axis));
|
||||
vertices->push_back(getMarkerPosition(MarkerShaftWidth / 2, MarkerShaftWidth / 2, length, axis));
|
||||
vertices->push_back(getMarkerPosition(MarkerShaftWidth / 2, -MarkerShaftWidth / 2, length, axis));
|
||||
}
|
||||
|
||||
// head backside
|
||||
vertices->push_back(getMarkerPosition(-MarkerHeadWidth / 2, -MarkerHeadWidth / 2, shaftLength, axis));
|
||||
vertices->push_back(getMarkerPosition(-MarkerHeadWidth / 2, MarkerHeadWidth / 2, shaftLength, axis));
|
||||
vertices->push_back(getMarkerPosition(MarkerHeadWidth / 2, MarkerHeadWidth / 2, shaftLength, axis));
|
||||
vertices->push_back(getMarkerPosition(MarkerHeadWidth / 2, -MarkerHeadWidth / 2, shaftLength, axis));
|
||||
|
||||
// head
|
||||
vertices->push_back(getMarkerPosition(0, 0, shaftLength + MarkerHeadLength, axis));
|
||||
|
||||
geometry->setVertexArray(vertices);
|
||||
|
||||
osg::DrawElementsUShort* primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, 0);
|
||||
|
||||
// shaft
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
int i2 = i == 3 ? 0 : i + 1;
|
||||
primitives->push_back(i);
|
||||
primitives->push_back(4 + i);
|
||||
primitives->push_back(i2);
|
||||
|
||||
primitives->push_back(4 + i);
|
||||
primitives->push_back(4 + i2);
|
||||
primitives->push_back(i2);
|
||||
}
|
||||
|
||||
// cap
|
||||
primitives->push_back(0);
|
||||
primitives->push_back(1);
|
||||
primitives->push_back(2);
|
||||
|
||||
primitives->push_back(2);
|
||||
primitives->push_back(3);
|
||||
primitives->push_back(0);
|
||||
|
||||
// head, backside
|
||||
primitives->push_back(0 + 8);
|
||||
primitives->push_back(1 + 8);
|
||||
primitives->push_back(2 + 8);
|
||||
|
||||
primitives->push_back(2 + 8);
|
||||
primitives->push_back(3 + 8);
|
||||
primitives->push_back(0 + 8);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
primitives->push_back(12);
|
||||
primitives->push_back(8 + (i == 3 ? 0 : i + 1));
|
||||
primitives->push_back(8 + i);
|
||||
}
|
||||
|
||||
geometry->addPrimitiveSet(primitives);
|
||||
|
||||
osg::Vec4Array* colours = new osg::Vec4Array;
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
colours->push_back(
|
||||
osg::Vec4f(axis == 0 ? 1.0f : 0.2f, axis == 1 ? 1.0f : 0.2f, axis == 2 ? 1.0f : 0.2f, mMarkerTransparency));
|
||||
|
||||
for (int i = 8; i < 8 + 4 + 1; ++i)
|
||||
colours->push_back(
|
||||
osg::Vec4f(axis == 0 ? 1.0f : 0.0f, axis == 1 ? 1.0f : 0.0f, axis == 2 ? 1.0f : 0.0f, mMarkerTransparency));
|
||||
|
||||
geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX);
|
||||
|
||||
setupCommonMarkerState(geometry);
|
||||
|
||||
osg::ref_ptr<osg::Group> group(new osg::Group);
|
||||
group->addChild(geometry);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker(int axis)
|
||||
{
|
||||
const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius());
|
||||
const float OuterRadius = InnerRadius + MarkerShaftWidth;
|
||||
|
||||
const float SegmentDistance = 100.f;
|
||||
const size_t SegmentCount = std::clamp<int>(OuterRadius * 2 * osg::PI / SegmentDistance, 24, 64);
|
||||
const size_t VerticesPerSegment = 4;
|
||||
const size_t IndicesPerSegment = 24;
|
||||
|
||||
const size_t VertexCount = SegmentCount * VerticesPerSegment;
|
||||
const size_t IndexCount = SegmentCount * IndicesPerSegment;
|
||||
|
||||
const float Angle = 2 * osg::PI / SegmentCount;
|
||||
|
||||
const unsigned short IndexPattern[IndicesPerSegment]
|
||||
= { 0, 4, 5, 0, 5, 1, 2, 6, 4, 2, 4, 0, 3, 7, 6, 3, 6, 2, 1, 5, 7, 1, 7, 3 };
|
||||
|
||||
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
|
||||
|
||||
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(VertexCount);
|
||||
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
|
||||
osg::ref_ptr<osg::DrawElementsUShort> primitives
|
||||
= new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, IndexCount);
|
||||
|
||||
// prevent some depth collision issues from overlaps
|
||||
osg::Vec3f offset = getMarkerPosition(0, MarkerShaftWidth / 4, 0, axis);
|
||||
|
||||
for (size_t i = 0; i < SegmentCount; ++i)
|
||||
{
|
||||
size_t index = i * VerticesPerSegment;
|
||||
|
||||
float innerX = InnerRadius * std::cos(i * Angle);
|
||||
float innerY = InnerRadius * std::sin(i * Angle);
|
||||
|
||||
float outerX = OuterRadius * std::cos(i * Angle);
|
||||
float outerY = OuterRadius * std::sin(i * Angle);
|
||||
|
||||
vertices->at(index++) = getMarkerPosition(innerX, innerY, MarkerShaftWidth / 2, axis) + offset;
|
||||
vertices->at(index++) = getMarkerPosition(innerX, innerY, -MarkerShaftWidth / 2, axis) + offset;
|
||||
vertices->at(index++) = getMarkerPosition(outerX, outerY, MarkerShaftWidth / 2, axis) + offset;
|
||||
vertices->at(index++) = getMarkerPosition(outerX, outerY, -MarkerShaftWidth / 2, axis) + offset;
|
||||
}
|
||||
|
||||
colors->at(0)
|
||||
= osg::Vec4f(axis == 0 ? 1.0f : 0.2f, axis == 1 ? 1.0f : 0.2f, axis == 2 ? 1.0f : 0.2f, mMarkerTransparency);
|
||||
|
||||
for (size_t i = 0; i < SegmentCount; ++i)
|
||||
{
|
||||
size_t indices[IndicesPerSegment];
|
||||
for (size_t j = 0; j < IndicesPerSegment; ++j)
|
||||
{
|
||||
indices[j] = i * VerticesPerSegment + j;
|
||||
|
||||
if (indices[j] >= VertexCount)
|
||||
indices[j] -= VertexCount;
|
||||
}
|
||||
|
||||
size_t elementOffset = i * IndicesPerSegment;
|
||||
for (size_t j = 0; j < IndicesPerSegment; ++j)
|
||||
{
|
||||
primitives->setElement(elementOffset++, indices[IndexPattern[j]]);
|
||||
}
|
||||
}
|
||||
|
||||
geometry->setVertexArray(vertices);
|
||||
geometry->setColorArray(colors, osg::Array::BIND_OVERALL);
|
||||
geometry->addPrimitiveSet(primitives);
|
||||
|
||||
setupCommonMarkerState(geometry);
|
||||
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group();
|
||||
group->addChild(geometry);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
void CSVRender::Object::setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry)
|
||||
{
|
||||
osg::ref_ptr<osg::StateSet> state = geometry->getOrCreateStateSet();
|
||||
state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
state->setMode(GL_BLEND, osg::StateAttribute::ON);
|
||||
|
||||
state->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
||||
}
|
||||
|
||||
osg::Vec3f CSVRender::Object::getMarkerPosition(float x, float y, float z, int axis)
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case 2:
|
||||
return osg::Vec3f(x, y, z);
|
||||
case 0:
|
||||
return osg::Vec3f(z, x, y);
|
||||
case 1:
|
||||
return osg::Vec3f(y, z, x);
|
||||
|
||||
default:
|
||||
|
||||
throw std::logic_error("invalid axis for marker geometry");
|
||||
}
|
||||
}
|
||||
|
||||
CSVRender::Object::Object(
|
||||
CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero)
|
||||
: mData(data)
|
||||
|
|
@ -446,8 +189,6 @@ CSVRender::Object::Object(
|
|||
, mForceBaseToZero(forceBaseToZero)
|
||||
, mScaleOverride(1)
|
||||
, mOverrideFlags(0)
|
||||
, mSubMode(-1)
|
||||
, mMarkerTransparency(0.5f)
|
||||
{
|
||||
mRootNode = new osg::PositionAttitudeTransform;
|
||||
|
||||
|
|
@ -476,7 +217,6 @@ CSVRender::Object::Object(
|
|||
|
||||
adjustTransform();
|
||||
update();
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
CSVRender::Object::~Object()
|
||||
|
|
@ -506,9 +246,6 @@ void CSVRender::Object::setSelected(bool selected, const osg::Vec4f& color)
|
|||
}
|
||||
else
|
||||
mRootNode->addChild(mBaseNode);
|
||||
|
||||
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
bool CSVRender::Object::getSelected() const
|
||||
|
|
@ -536,9 +273,6 @@ void CSVRender::Object::setSnapTarget(bool isSnapTarget)
|
|||
}
|
||||
else
|
||||
mRootNode->addChild(mBaseNode);
|
||||
|
||||
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
bool CSVRender::Object::getSnapTarget() const
|
||||
|
|
@ -566,7 +300,6 @@ bool CSVRender::Object::referenceableDataChanged(const QModelIndex& topLeft, con
|
|||
{
|
||||
adjustTransform();
|
||||
update();
|
||||
updateMarker();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +347,6 @@ bool CSVRender::Object::referenceDataChanged(const QModelIndex& topLeft, const Q
|
|||
= ESM::RefId::stringRefId(references.getData(index, columnIndex).toString().toUtf8().constData());
|
||||
|
||||
update();
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -626,7 +358,6 @@ bool CSVRender::Object::referenceDataChanged(const QModelIndex& topLeft, const Q
|
|||
void CSVRender::Object::reloadAssets()
|
||||
{
|
||||
update();
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
std::string CSVRender::Object::getReferenceId() const
|
||||
|
|
@ -720,12 +451,6 @@ void CSVRender::Object::setScale(float scale)
|
|||
adjustTransform();
|
||||
}
|
||||
|
||||
void CSVRender::Object::setMarkerTransparency(float value)
|
||||
{
|
||||
mMarkerTransparency = value;
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
|
||||
{
|
||||
const CSMWorld::RefCollection& collection = mData.getReferences();
|
||||
|
|
@ -796,18 +521,8 @@ void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
|
|||
mOverrideFlags = 0;
|
||||
}
|
||||
|
||||
void CSVRender::Object::setSubMode(int subMode)
|
||||
{
|
||||
if (subMode != mSubMode)
|
||||
{
|
||||
mSubMode = subMode;
|
||||
updateMarker();
|
||||
}
|
||||
}
|
||||
|
||||
void CSVRender::Object::reset()
|
||||
{
|
||||
mOverrideFlags = 0;
|
||||
adjustTransform();
|
||||
updateMarker();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,14 +58,6 @@ namespace CSVRender
|
|||
QString getToolTip(bool hideBasics, const WorldspaceHitResult& hit) const override;
|
||||
};
|
||||
|
||||
class ObjectMarkerTag : public ObjectTag
|
||||
{
|
||||
public:
|
||||
ObjectMarkerTag(Object* object, int axis);
|
||||
|
||||
int mAxis;
|
||||
};
|
||||
|
||||
class Object
|
||||
{
|
||||
public:
|
||||
|
|
@ -76,12 +68,22 @@ namespace CSVRender
|
|||
Override_Scale = 4
|
||||
};
|
||||
|
||||
private:
|
||||
static const float MarkerShaftWidth;
|
||||
static const float MarkerShaftBaseLength;
|
||||
static const float MarkerHeadWidth;
|
||||
static const float MarkerHeadLength;
|
||||
enum SubMode
|
||||
{
|
||||
Mode_Move,
|
||||
Mode_Rotate,
|
||||
Mode_Scale,
|
||||
Mode_None,
|
||||
};
|
||||
|
||||
enum Axis
|
||||
{
|
||||
Axis_X,
|
||||
Axis_Y,
|
||||
Axis_Z
|
||||
};
|
||||
|
||||
private:
|
||||
CSMWorld::Data& mData;
|
||||
ESM::RefId mReferenceId;
|
||||
ESM::RefId mReferenceableId;
|
||||
|
|
@ -96,9 +98,6 @@ namespace CSVRender
|
|||
ESM::Position mPositionOverride;
|
||||
float mScaleOverride;
|
||||
int mOverrideFlags;
|
||||
osg::ref_ptr<osg::Node> mMarker[3];
|
||||
int mSubMode;
|
||||
float mMarkerTransparency;
|
||||
std::unique_ptr<Actor> mActor;
|
||||
|
||||
/// Not implemented
|
||||
|
|
@ -120,16 +119,6 @@ namespace CSVRender
|
|||
/// Throws an exception if *this was constructed with referenceable
|
||||
const CSMWorld::CellRef& getReference() const;
|
||||
|
||||
void updateMarker();
|
||||
|
||||
osg::ref_ptr<osg::Node> makeMoveOrScaleMarker(int axis);
|
||||
osg::ref_ptr<osg::Node> makeRotateMarker(int axis);
|
||||
|
||||
/// Sets up a stateset with properties common to all marker types.
|
||||
void setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry);
|
||||
|
||||
osg::Vec3f getMarkerPosition(float x, float y, float z, int axis);
|
||||
|
||||
public:
|
||||
Object(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, bool referenceable,
|
||||
bool forceBaseToZero = false);
|
||||
|
|
@ -199,8 +188,6 @@ namespace CSVRender
|
|||
/// Apply override changes via command and end edit mode
|
||||
void apply(CSMWorld::CommandMacro& commands);
|
||||
|
||||
void setSubMode(int subMode);
|
||||
|
||||
/// Erase all overrides and restore the visual representation of the object to its
|
||||
/// true state.
|
||||
void reset();
|
||||
|
|
|
|||
307
apps/opencs/view/render/objectmarker.cpp
Normal file
307
apps/opencs/view/render/objectmarker.cpp
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
#include <unordered_set>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <osg/ClipPlane>
|
||||
#include <osg/Material>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
|
||||
#include "../../model/prefs/state.hpp"
|
||||
#include "objectmarker.hpp"
|
||||
#include "worldspacewidget.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
class FindMaterialVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
FindMaterialVisitor(CSVRender::NodeMap& map)
|
||||
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||
, mMap(map)
|
||||
{
|
||||
}
|
||||
|
||||
void apply(osg::Geometry& node) override
|
||||
{
|
||||
osg::StateSet* state = node.getStateSet();
|
||||
if (state->getAttribute(osg::StateAttribute::MATERIAL))
|
||||
mMap.emplace(node.getName(), &node);
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
private:
|
||||
CSVRender::NodeMap& mMap;
|
||||
};
|
||||
|
||||
class ToCamera : public SceneUtil::NodeCallback<ToCamera, osg::Node*, osgUtil::CullVisitor*>
|
||||
{
|
||||
public:
|
||||
ToCamera(osg::ref_ptr<osg::ClipPlane> clipPlane)
|
||||
: mClipPlane(std::move(clipPlane))
|
||||
{
|
||||
}
|
||||
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
|
||||
{
|
||||
osg::Vec3f normal = cv->getEyePoint();
|
||||
mClipPlane->setClipPlane(normal.x(), normal.y(), normal.z(), 0);
|
||||
traverse(node, cv);
|
||||
}
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::ClipPlane> mClipPlane;
|
||||
};
|
||||
|
||||
auto addTagToActiveMarkerNodes = [](CSVRender::NodeMap& mMarkerNodes, CSVRender::Object* object,
|
||||
std::initializer_list<std::string> suffixes) {
|
||||
for (const auto& markerSuffix : suffixes)
|
||||
{
|
||||
for (char axis = 'X'; axis <= 'Z'; ++axis)
|
||||
mMarkerNodes[axis + markerSuffix]->setUserData(new CSVRender::ObjectMarkerTag(object, axis - 'X'));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
ObjectMarkerTag::ObjectMarkerTag(Object* object, int axis)
|
||||
: ObjectTag(object)
|
||||
, mAxis(axis)
|
||||
{
|
||||
}
|
||||
|
||||
ObjectMarker::ObjectMarker(WorldspaceWidget* worldspaceWidget, Resource::ResourceSystem* resourceSystem)
|
||||
: mWorldspaceWidget(worldspaceWidget)
|
||||
, mResourceSystem(resourceSystem)
|
||||
, mMarkerScale(CSMPrefs::get()["Rendering"]["object-marker-scale"].toDouble())
|
||||
, mSubMode(Object::Mode_None)
|
||||
{
|
||||
mBaseNode = new osg::PositionAttitudeTransform;
|
||||
mBaseNode->setNodeMask(Mask_Reference);
|
||||
mBaseNode->setScale(osg::Vec3f(mMarkerScale, mMarkerScale, mMarkerScale));
|
||||
|
||||
mRootNode = new osg::PositionAttitudeTransform;
|
||||
mRootNode->addChild(mBaseNode);
|
||||
worldspaceWidget->setSelectionMarkerRoot(mRootNode);
|
||||
|
||||
QFile file(":render/selection-marker");
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
throw std::runtime_error("Failed to open selection marker file");
|
||||
|
||||
auto markerData = file.readAll();
|
||||
|
||||
mResourceSystem->getSceneManager()->loadSelectionMarker(mBaseNode, markerData.data(), markerData.size());
|
||||
|
||||
osg::ref_ptr<osg::StateSet> baseNodeState = mBaseNode->getOrCreateStateSet();
|
||||
baseNodeState->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
||||
baseNodeState->setRenderBinDetails(1000, "RenderBin");
|
||||
|
||||
FindMaterialVisitor matMapper(mMarkerNodes);
|
||||
|
||||
mBaseNode->accept(matMapper);
|
||||
|
||||
for (const auto& [name, node] : mMarkerNodes)
|
||||
{
|
||||
osg::StateSet* state = node->getStateSet();
|
||||
osg::Material* mat = static_cast<osg::Material*>(state->getAttribute(osg::StateAttribute::MATERIAL));
|
||||
osg::Vec4f emis = mat->getEmission(osg::Material::FRONT_AND_BACK);
|
||||
mat->setEmission(osg::Material::FRONT_AND_BACK, emis / 4);
|
||||
mOriginalColors.emplace(name, emis);
|
||||
}
|
||||
|
||||
SceneUtil::NodeMap sceneNodes;
|
||||
SceneUtil::NodeMapVisitor nodeMapper(sceneNodes);
|
||||
mBaseNode->accept(nodeMapper);
|
||||
|
||||
mMarkerNodes.insert(sceneNodes.begin(), sceneNodes.end());
|
||||
|
||||
osg::ref_ptr<osg::Node> rotateMarkers = mMarkerNodes["rotateMarkers"];
|
||||
osg::ClipPlane* clip = new osg::ClipPlane(0);
|
||||
rotateMarkers->setCullCallback(new ToCamera(clip));
|
||||
rotateMarkers->getStateSet()->setAttributeAndModes(clip, osg::StateAttribute::ON);
|
||||
}
|
||||
|
||||
void ObjectMarker::toggleVisibility()
|
||||
{
|
||||
bool isVisible = mBaseNode->getNodeMask() == Mask_Reference;
|
||||
mBaseNode->setNodeMask(isVisible ? Mask_Hidden : Mask_Reference);
|
||||
}
|
||||
|
||||
void ObjectMarker::updateScale(const float scale)
|
||||
{
|
||||
mMarkerScale = scale;
|
||||
mBaseNode->setScale(osg::Vec3f(scale, scale, scale));
|
||||
}
|
||||
|
||||
void ObjectMarker::setSubMode(const int subMode)
|
||||
{
|
||||
if (subMode == mSubMode)
|
||||
return;
|
||||
mSubMode = subMode;
|
||||
resetMarkerHighlight();
|
||||
updateSelectionMarker();
|
||||
}
|
||||
|
||||
bool ObjectMarker::hitBehindMarker(const osg::Vec3d& hitPos, osg::ref_ptr<osg::Camera> camera)
|
||||
{
|
||||
if (mSubMode != Object::Mode_Rotate)
|
||||
return false;
|
||||
|
||||
osg::Vec3d center, eye, forwardVector, _;
|
||||
std::vector<osg::Node*> rotMark = mMarkerNodes["rotateMarkers"]->getParentalNodePaths()[0];
|
||||
const osg::Vec3f markerPos = osg::computeLocalToWorld(rotMark).getTrans();
|
||||
|
||||
camera->getViewMatrixAsLookAt(eye, center, _);
|
||||
forwardVector = center - eye;
|
||||
forwardVector.normalize();
|
||||
|
||||
return (hitPos - markerPos) * forwardVector > 0;
|
||||
}
|
||||
|
||||
bool ObjectMarker::attachMarker(const std::string& refId)
|
||||
{
|
||||
const auto& object = mWorldspaceWidget->getObjectByReferenceId(refId);
|
||||
|
||||
if (!object)
|
||||
removeFromSelectionHistory(refId);
|
||||
|
||||
if (!object || !object->getSelected())
|
||||
return false;
|
||||
|
||||
if (!object->getRootNode()->addChild(mRootNode))
|
||||
throw std::runtime_error("Failed to add marker to object");
|
||||
|
||||
std::string parentMarkerNode;
|
||||
|
||||
switch (mSubMode)
|
||||
{
|
||||
case (Object::Mode_Rotate):
|
||||
parentMarkerNode = "rotateMarkers";
|
||||
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis_Rot" });
|
||||
break;
|
||||
case (Object::Mode_Scale):
|
||||
parentMarkerNode = "scaleMarkers";
|
||||
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis_Scale", "_Wall_Scale" });
|
||||
break;
|
||||
case (Object::Mode_Move):
|
||||
default:
|
||||
parentMarkerNode = "moveMarkers";
|
||||
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis", "_Wall" });
|
||||
break;
|
||||
}
|
||||
|
||||
mMarkerNodes[parentMarkerNode]->asGroup()->setNodeMask(Mask_Reference);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectMarker::detachMarker()
|
||||
{
|
||||
for (std::size_t index = mRootNode->getNumParents(); index > 0;)
|
||||
mRootNode->getParent(--index)->removeChild(mRootNode);
|
||||
|
||||
osg::ref_ptr<osg::Group> widgetRoot = mMarkerNodes["unitArrows"]->asGroup();
|
||||
for (std::size_t index = widgetRoot->getNumChildren(); index > 0;)
|
||||
widgetRoot->getChild(--index)->setNodeMask(Mask_Hidden);
|
||||
}
|
||||
|
||||
void ObjectMarker::addToSelectionHistory(const std::string& refId, bool update)
|
||||
{
|
||||
auto foundObject = std::find_if(mSelectionHistory.begin(), mSelectionHistory.end(),
|
||||
[&refId](const std::string& objId) { return objId == refId; });
|
||||
|
||||
if (foundObject == mSelectionHistory.end())
|
||||
mSelectionHistory.push_back(refId);
|
||||
else
|
||||
std::rotate(foundObject, foundObject + 1, mSelectionHistory.end());
|
||||
|
||||
if (update)
|
||||
updateSelectionMarker(refId);
|
||||
}
|
||||
|
||||
void ObjectMarker::removeFromSelectionHistory(const std::string& refId)
|
||||
{
|
||||
mSelectionHistory.erase(std::remove_if(mSelectionHistory.begin(), mSelectionHistory.end(),
|
||||
[&refId](const std::string& objId) { return objId == refId; }),
|
||||
mSelectionHistory.end());
|
||||
}
|
||||
|
||||
void ObjectMarker::updateSelectionMarker(const std::string& refId)
|
||||
{
|
||||
if (mSelectionHistory.empty())
|
||||
return;
|
||||
|
||||
detachMarker();
|
||||
|
||||
if (refId.empty())
|
||||
{
|
||||
for (std::size_t index = mSelectionHistory.size(); index > 0;)
|
||||
if (attachMarker(mSelectionHistory[--index]))
|
||||
break;
|
||||
}
|
||||
else
|
||||
attachMarker(refId);
|
||||
}
|
||||
|
||||
void ObjectMarker::resetMarkerHighlight()
|
||||
{
|
||||
if (mLastHighlightedNodes.empty())
|
||||
return;
|
||||
|
||||
for (const auto& [nodeName, mat] : mLastHighlightedNodes)
|
||||
mat->setEmission(osg::Material::FRONT_AND_BACK, mat->getEmission(osg::Material::FRONT_AND_BACK) / 4);
|
||||
|
||||
mLastHighlightedNodes.clear();
|
||||
mLastHitNode.clear();
|
||||
}
|
||||
|
||||
void ObjectMarker::updateMarkerHighlight(const std::string_view hitNode, const int axis)
|
||||
{
|
||||
if (hitNode == mLastHitNode)
|
||||
return;
|
||||
|
||||
resetMarkerHighlight();
|
||||
|
||||
std::string colorName;
|
||||
|
||||
switch (axis)
|
||||
{
|
||||
case Object::Axis_X:
|
||||
colorName = "red";
|
||||
break;
|
||||
case Object::Axis_Y:
|
||||
colorName = "green";
|
||||
break;
|
||||
case Object::Axis_Z:
|
||||
colorName = "blue";
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Invalid axis for highlighting: " + std::to_string(axis));
|
||||
}
|
||||
|
||||
std::vector<std::string> targetMaterials = { colorName + "-material" };
|
||||
|
||||
if (mSubMode != Object::Mode_Rotate)
|
||||
targetMaterials.emplace_back(colorName + "_alpha-material");
|
||||
|
||||
for (const auto& materialNodeName : targetMaterials)
|
||||
{
|
||||
osg::ref_ptr<osg::Node> matNode = mMarkerNodes[materialNodeName];
|
||||
osg::StateSet* state = matNode->getStateSet();
|
||||
osg::StateAttribute* matAttr = state->getAttribute(osg::StateAttribute::MATERIAL);
|
||||
|
||||
osg::Material* mat = static_cast<osg::Material*>(matAttr);
|
||||
mat->setEmission(osg::Material::FRONT_AND_BACK, mOriginalColors[materialNodeName]);
|
||||
|
||||
mLastHighlightedNodes.emplace(std::make_pair(matNode->getName(), mat));
|
||||
}
|
||||
|
||||
mLastHitNode = hitNode;
|
||||
}
|
||||
}
|
||||
77
apps/opencs/view/render/objectmarker.hpp
Normal file
77
apps/opencs/view/render/objectmarker.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef OPENCS_VIEW_OBJECT_MARKER_H
|
||||
#define OPENCS_VIEW_OBJECT_MARKER_H
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Camera;
|
||||
class Material;
|
||||
}
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
using NodeMap = std::unordered_map<std::string, osg::ref_ptr<osg::Node>>;
|
||||
class WorldspaceWidget;
|
||||
|
||||
class ObjectMarkerTag : public ObjectTag
|
||||
{
|
||||
public:
|
||||
ObjectMarkerTag(Object* object, int axis);
|
||||
|
||||
int mAxis;
|
||||
};
|
||||
|
||||
class ObjectMarker
|
||||
{
|
||||
friend class WorldspaceWidget;
|
||||
|
||||
WorldspaceWidget* mWorldspaceWidget;
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
NodeMap mMarkerNodes;
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> mRootNode;
|
||||
std::unordered_map<std::string, osg::Vec4f> mOriginalColors;
|
||||
std::vector<std::string> mSelectionHistory;
|
||||
std::string mLastHitNode;
|
||||
std::unordered_map<std::string, osg::Material*> mLastHighlightedNodes;
|
||||
float mMarkerScale;
|
||||
int mSubMode;
|
||||
|
||||
ObjectMarker(WorldspaceWidget* worldspaceWidget, Resource::ResourceSystem* resourceSystem);
|
||||
|
||||
static std::unique_ptr<ObjectMarker> create(WorldspaceWidget* widget, Resource::ResourceSystem* resourceSystem)
|
||||
{
|
||||
return std::unique_ptr<ObjectMarker>(new ObjectMarker(widget, resourceSystem));
|
||||
}
|
||||
|
||||
bool attachMarker(const std::string& refId);
|
||||
|
||||
void removeFromSelectionHistory(const std::string& refId);
|
||||
|
||||
public:
|
||||
ObjectMarker(ObjectMarker&) = delete;
|
||||
ObjectMarker(ObjectMarker&&) = delete;
|
||||
ObjectMarker& operator=(const ObjectMarker&) = delete;
|
||||
ObjectMarker& operator=(ObjectMarker&&) = delete;
|
||||
|
||||
void toggleVisibility();
|
||||
|
||||
bool hitBehindMarker(const osg::Vec3d& hitPos, osg::ref_ptr<osg::Camera> camera);
|
||||
|
||||
void detachMarker();
|
||||
|
||||
void addToSelectionHistory(const std::string& refId, bool update = true);
|
||||
|
||||
void updateSelectionMarker(const std::string& refId = std::string());
|
||||
|
||||
void resetMarkerHighlight();
|
||||
|
||||
void updateMarkerHighlight(const std::string_view hitNode, const int axis);
|
||||
|
||||
void setSubMode(const int subMode);
|
||||
|
||||
void updateScale(const float scale);
|
||||
};
|
||||
}
|
||||
#endif // OPENCS_VIEW_OBJECT_MARKER_H
|
||||
|
|
@ -86,8 +86,8 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
|
|||
{
|
||||
modified = true;
|
||||
|
||||
auto cell
|
||||
= std::make_unique<Cell>(mDocument, mRootNode, iter->first.getId(mWorldspace), deleted, true);
|
||||
auto cell = std::make_unique<Cell>(getDocument(), mSelectionMarker.get(), mRootNode,
|
||||
iter->first.getId(mWorldspace), deleted, true);
|
||||
|
||||
delete iter->second;
|
||||
iter->second = cell.release();
|
||||
|
|
@ -465,7 +465,8 @@ void CSVRender::PagedWorldspaceWidget::addCellToScene(const CSMWorld::CellCoordi
|
|||
|
||||
bool deleted = index == -1 || cells.getRecord(index).mState == CSMWorld::RecordBase::State_Deleted;
|
||||
|
||||
auto cell = std::make_unique<Cell>(mDocument, mRootNode, coordinates.getId(mWorldspace), deleted, true);
|
||||
auto cell = std::make_unique<Cell>(
|
||||
getDocument(), mSelectionMarker.get(), mRootNode, coordinates.getId(mWorldspace), deleted, true);
|
||||
EditMode* editMode = getEditMode();
|
||||
cell->setSubMode(editMode->getSubMode(), editMode->getInteractionMask());
|
||||
|
||||
|
|
@ -750,6 +751,7 @@ void CSVRender::PagedWorldspaceWidget::clearSelection(int elementMask)
|
|||
iter->second->setSelection(elementMask, Cell::Selection_Clear);
|
||||
|
||||
flagAsModified();
|
||||
mSelectionMarker->detachMarker();
|
||||
}
|
||||
|
||||
void CSVRender::PagedWorldspaceWidget::invertSelection(int elementMask)
|
||||
|
|
@ -907,6 +909,7 @@ void CSVRender::PagedWorldspaceWidget::setSubMode(int subMode, unsigned int elem
|
|||
{
|
||||
for (std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator iter = mCells.begin(); iter != mCells.end(); ++iter)
|
||||
iter->second->setSubMode(subMode, elementMask);
|
||||
mSelectionMarker->updateSelectionMarker();
|
||||
}
|
||||
|
||||
void CSVRender::PagedWorldspaceWidget::reset(unsigned int elementMask)
|
||||
|
|
@ -986,3 +989,12 @@ void CSVRender::PagedWorldspaceWidget::loadSouthCell()
|
|||
{
|
||||
addCellToSceneFromCamera(0, -1);
|
||||
}
|
||||
|
||||
CSVRender::Object* CSVRender::PagedWorldspaceWidget::getObjectByReferenceId(const std::string& referenceId)
|
||||
{
|
||||
for (const auto& [_, cell] : mCells)
|
||||
if (const auto& object = cell->getObjectByReferenceId(referenceId))
|
||||
return object;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,6 +174,8 @@ namespace CSVRender
|
|||
/// Erase all overrides and restore the visual representation to its true state.
|
||||
void reset(unsigned int elementMask) override;
|
||||
|
||||
CSVRender::Object* getObjectByReferenceId(const std::string& referenceId) override;
|
||||
|
||||
protected:
|
||||
void addVisibilitySelectorButtons(CSVWidget::SceneToolToggle2* tool) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -445,6 +445,32 @@ namespace CSVRender
|
|||
mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp);
|
||||
mCamPositionSet = true;
|
||||
}
|
||||
|
||||
if (mSelectionMarkerNode)
|
||||
{
|
||||
osg::MatrixList worldMats = mSelectionMarkerNode->getWorldMatrices();
|
||||
if (!worldMats.empty())
|
||||
{
|
||||
osg::Matrixd markerWorldMat = worldMats[0];
|
||||
|
||||
osg::Vec3f eye, _;
|
||||
mView->getCamera()->getViewMatrix().getLookAt(eye, _, _);
|
||||
osg::Vec3f cameraLocalPos = eye * osg::Matrixd::inverse(markerWorldMat);
|
||||
|
||||
bool isInFrontRightQuadrant = (cameraLocalPos.x() > 0.1f) && (cameraLocalPos.y() > 0.1f);
|
||||
bool isSignificantlyBehind = (cameraLocalPos.x() < 1.f) && (cameraLocalPos.y() < 1.f);
|
||||
|
||||
if (!isInFrontRightQuadrant && isSignificantlyBehind)
|
||||
{
|
||||
osg::Quat current = mSelectionMarkerNode->getAttitude();
|
||||
mSelectionMarkerNode->setAttitude(current * osg::Quat(osg::PI, osg::Vec3f(0, 0, 1)));
|
||||
}
|
||||
|
||||
float distance = (markerWorldMat.getTrans() - eye).length();
|
||||
float scale = std::max(distance / 75.0f, 1.0f);
|
||||
mSelectionMarkerNode->setScale(osg::Vec3(scale, scale, scale));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneWidget::settingChanged(const CSMPrefs::Setting* setting)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/Vec4f>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
|
|
@ -105,6 +106,11 @@ namespace CSVRender
|
|||
|
||||
void setExterior(bool isExterior);
|
||||
|
||||
void setSelectionMarkerRoot(osg::ref_ptr<osg::PositionAttitudeTransform> selectionMarker)
|
||||
{
|
||||
mSelectionMarkerNode = selectionMarker;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setLighting(Lighting* lighting);
|
||||
///< \attention The ownership of \a lighting is not transferred to *this.
|
||||
|
|
@ -122,6 +128,7 @@ namespace CSVRender
|
|||
|
||||
Lighting* mLighting;
|
||||
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> mSelectionMarkerNode;
|
||||
osg::ref_ptr<osg::Camera> mGradientCamera;
|
||||
osg::Vec4f mDefaultAmbient;
|
||||
bool mHasDefaultAmbient;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget(
|
|||
|
||||
update();
|
||||
|
||||
mCell = std::make_unique<Cell>(document, mRootNode, mCellId);
|
||||
mCell = std::make_unique<Cell>(document, mSelectionMarker.get(), mRootNode, mCellId);
|
||||
}
|
||||
|
||||
void CSVRender::UnpagedWorldspaceWidget::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
|
|
@ -127,7 +127,7 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop(
|
|||
|
||||
mCellId = universalIdData.begin()->getId();
|
||||
|
||||
mCell = std::make_unique<Cell>(getDocument(), mRootNode, mCellId);
|
||||
mCell = std::make_unique<Cell>(getDocument(), mSelectionMarker.get(), mRootNode, mCellId);
|
||||
mCamPositionSet = false;
|
||||
mOrbitCamControl->reset();
|
||||
|
||||
|
|
@ -141,6 +141,7 @@ void CSVRender::UnpagedWorldspaceWidget::clearSelection(int elementMask)
|
|||
{
|
||||
mCell->setSelection(elementMask, Cell::Selection_Clear);
|
||||
flagAsModified();
|
||||
mSelectionMarker->detachMarker();
|
||||
}
|
||||
|
||||
void CSVRender::UnpagedWorldspaceWidget::invertSelection(int elementMask)
|
||||
|
|
@ -218,6 +219,7 @@ std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::UnpagedWorldspaceWidget
|
|||
void CSVRender::UnpagedWorldspaceWidget::setSubMode(int subMode, unsigned int elementMask)
|
||||
{
|
||||
mCell->setSubMode(subMode, elementMask);
|
||||
mSelectionMarker->updateSelectionMarker();
|
||||
}
|
||||
|
||||
void CSVRender::UnpagedWorldspaceWidget::reset(unsigned int elementMask)
|
||||
|
|
@ -383,3 +385,8 @@ CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget:
|
|||
return ignored;
|
||||
}
|
||||
}
|
||||
|
||||
CSVRender::Object* CSVRender::UnpagedWorldspaceWidget::getObjectByReferenceId(const std::string& referenceId)
|
||||
{
|
||||
return mCell->getObjectByReferenceId(referenceId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,8 @@ namespace CSVRender
|
|||
/// Erase all overrides and restore the visual representation to its true state.
|
||||
void reset(unsigned int elementMask) override;
|
||||
|
||||
CSVRender::Object* getObjectByReferenceId(const std::string& id) override;
|
||||
|
||||
private:
|
||||
void referenceableDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@
|
|||
#include "cameracontroller.hpp"
|
||||
#include "instancemode.hpp"
|
||||
#include "mask.hpp"
|
||||
#include "object.hpp"
|
||||
#include "pathgridmode.hpp"
|
||||
|
||||
CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidget* parent)
|
||||
|
|
@ -74,8 +73,8 @@ CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidge
|
|||
, mToolTipPos(-1, -1)
|
||||
, mShowToolTips(false)
|
||||
, mToolTipDelay(0)
|
||||
, mInConstructor(true)
|
||||
, mSelectedNavigationMode(0)
|
||||
, mSelectionMarker(ObjectMarker::create(this, document.getData().getResourceSystem().get()))
|
||||
{
|
||||
setAcceptDrops(true);
|
||||
|
||||
|
|
@ -145,13 +144,14 @@ CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidge
|
|||
&WorldspaceWidget::unhideAll);
|
||||
|
||||
connect(new CSMPrefs::Shortcut("scene-clear-selection", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||
[this] { this->clearSelection(Mask_Reference); });
|
||||
[this] { clearSelection(Mask_Reference); });
|
||||
|
||||
CSMPrefs::Shortcut* switchPerspectiveShortcut = new CSMPrefs::Shortcut("scene-cam-cycle", this);
|
||||
connect(switchPerspectiveShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||
&WorldspaceWidget::cycleNavigationMode);
|
||||
|
||||
mInConstructor = false;
|
||||
connect(new CSMPrefs::Shortcut("scene-toggle-marker", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||
[this]() { mSelectionMarker->toggleVisibility(); });
|
||||
}
|
||||
|
||||
void CSVRender::WorldspaceWidget::settingChanged(const CSMPrefs::Setting* setting)
|
||||
|
|
@ -162,17 +162,8 @@ void CSVRender::WorldspaceWidget::settingChanged(const CSMPrefs::Setting* settin
|
|||
mDragWheelFactor = setting->toDouble();
|
||||
else if (*setting == "3D Scene Input/drag-shift-factor")
|
||||
mDragShiftFactor = setting->toDouble();
|
||||
else if (*setting == "Rendering/object-marker-alpha" && !mInConstructor)
|
||||
{
|
||||
float alpha = setting->toDouble();
|
||||
// getSelection is virtual, thus this can not be called from the constructor
|
||||
auto selection = getSelection(Mask_Reference);
|
||||
for (osg::ref_ptr<TagBase> tag : selection)
|
||||
{
|
||||
if (auto objTag = dynamic_cast<ObjectTag*>(tag.get()))
|
||||
objTag->mObject->setMarkerTransparency(alpha);
|
||||
}
|
||||
}
|
||||
else if (*setting == "Rendering/object-marker-scale")
|
||||
mSelectionMarker->updateScale(setting->toDouble());
|
||||
else if (*setting == "Tooltips/scene-delay")
|
||||
mToolTipDelay = setting->toInt();
|
||||
else if (*setting == "Tooltips/scene")
|
||||
|
|
@ -396,8 +387,29 @@ CSMDoc::Document& CSVRender::WorldspaceWidget::getDocument()
|
|||
return mDocument;
|
||||
}
|
||||
|
||||
CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||
const QPoint& localPos, unsigned int interactionMask) const
|
||||
template <typename Tag>
|
||||
std::optional<CSVRender::WorldspaceHitResult> CSVRender::WorldspaceWidget::checkTag(
|
||||
const osgUtil::LineSegmentIntersector::Intersection& intersection) const
|
||||
{
|
||||
for (auto* node : intersection.nodePath)
|
||||
{
|
||||
if (auto* tag = dynamic_cast<Tag*>(node->getUserData()))
|
||||
{
|
||||
WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() };
|
||||
if (intersection.indexList.size() >= 3)
|
||||
{
|
||||
hit.index0 = intersection.indexList[0];
|
||||
hit.index1 = intersection.indexList[1];
|
||||
hit.index2 = intersection.indexList[2];
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::tuple<osg::Vec3d, osg::Vec3d, osg::Vec3d> CSVRender::WorldspaceWidget::getStartEndDirection(
|
||||
int pointX, int pointY) const
|
||||
{
|
||||
// may be okay to just use devicePixelRatio() directly
|
||||
QScreen* screen = SceneWidget::windowHandle() && SceneWidget::windowHandle()->screen()
|
||||
|
|
@ -405,8 +417,8 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
|||
: QGuiApplication::primaryScreen();
|
||||
|
||||
// (0,0) is considered the lower left corner of an OpenGL window
|
||||
int x = localPos.x() * screen->devicePixelRatio();
|
||||
int y = height() * screen->devicePixelRatio() - localPos.y() * screen->devicePixelRatio();
|
||||
int x = pointX * screen->devicePixelRatio();
|
||||
int y = height() * screen->devicePixelRatio() - pointY * screen->devicePixelRatio();
|
||||
|
||||
// Convert from screen space to world space
|
||||
osg::Matrixd wpvMat;
|
||||
|
|
@ -418,6 +430,13 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
|||
osg::Vec3d start = wpvMat.preMult(osg::Vec3d(x, y, 0));
|
||||
osg::Vec3d end = wpvMat.preMult(osg::Vec3d(x, y, 1));
|
||||
osg::Vec3d direction = end - start;
|
||||
return { start, end, direction };
|
||||
}
|
||||
|
||||
CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||
const QPoint& localPos, unsigned int interactionMask) const
|
||||
{
|
||||
auto [start, end, direction] = getStartEndDirection(localPos.x(), localPos.y());
|
||||
|
||||
// Get intersection
|
||||
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
|
||||
|
|
@ -430,51 +449,46 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
|||
|
||||
mView->getCamera()->accept(visitor);
|
||||
|
||||
// Get relevant data
|
||||
for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();
|
||||
it != intersector->getIntersections().end(); ++it)
|
||||
{
|
||||
osgUtil::LineSegmentIntersector::Intersection intersection = *it;
|
||||
auto intersections = intersector->getIntersections();
|
||||
|
||||
// reject back-facing polygons
|
||||
if (direction * intersection.getWorldIntersectNormal() > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::vector<osgUtil::LineSegmentIntersector::Intersection> validIntersections
|
||||
= { intersections.begin(), intersections.end() };
|
||||
|
||||
for (std::vector<osg::Node*>::iterator nodeIter = intersection.nodePath.begin();
|
||||
nodeIter != intersection.nodePath.end(); ++nodeIter)
|
||||
{
|
||||
osg::Node* node = *nodeIter;
|
||||
if (osg::ref_ptr<CSVRender::TagBase> tag = dynamic_cast<CSVRender::TagBase*>(node->getUserData()))
|
||||
{
|
||||
WorldspaceHitResult hit = { true, std::move(tag), 0, 0, 0, intersection.getWorldIntersectPoint() };
|
||||
if (intersection.indexList.size() >= 3)
|
||||
{
|
||||
hit.index0 = intersection.indexList[0];
|
||||
hit.index1 = intersection.indexList[1];
|
||||
hit.index2 = intersection.indexList[2];
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
const auto& removeBackfaces = [direction = direction](const osgUtil::LineSegmentIntersector::Intersection& i) {
|
||||
return direction * i.getWorldIntersectNormal() > 0;
|
||||
};
|
||||
|
||||
// Something untagged, probably terrain
|
||||
WorldspaceHitResult hit = { true, nullptr, 0, 0, 0, intersection.getWorldIntersectPoint() };
|
||||
if (intersection.indexList.size() >= 3)
|
||||
{
|
||||
hit.index0 = intersection.indexList[0];
|
||||
hit.index1 = intersection.indexList[1];
|
||||
hit.index2 = intersection.indexList[2];
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
validIntersections.erase(std::remove_if(validIntersections.begin(), validIntersections.end(), removeBackfaces),
|
||||
validIntersections.end());
|
||||
|
||||
// Default placement
|
||||
direction.normalize();
|
||||
direction *= CSMPrefs::get()["3D Scene Editing"]["distance"].toInt();
|
||||
|
||||
WorldspaceHitResult hit = { false, nullptr, 0, 0, 0, start + direction };
|
||||
if (validIntersections.empty())
|
||||
return WorldspaceHitResult{ false, nullptr, 0, 0, 0, start + direction };
|
||||
|
||||
const auto& firstHit = validIntersections.front();
|
||||
|
||||
for (const auto& hit : validIntersections)
|
||||
if (const auto& markerHit = checkTag<ObjectMarkerTag>(hit))
|
||||
{
|
||||
if (mSelectionMarker->hitBehindMarker(markerHit->worldPos, mView->getCamera()))
|
||||
return WorldspaceHitResult{ false, nullptr, 0, 0, 0, start + direction };
|
||||
else
|
||||
return *markerHit;
|
||||
}
|
||||
if (auto hit = checkTag<TagBase>(firstHit))
|
||||
return *hit;
|
||||
|
||||
// Something untagged, probably terrain
|
||||
WorldspaceHitResult hit = { true, nullptr, 0, 0, 0, firstHit.getWorldIntersectPoint() };
|
||||
if (firstHit.indexList.size() >= 3)
|
||||
{
|
||||
hit.index0 = firstHit.indexList[0];
|
||||
hit.index1 = firstHit.indexList[1];
|
||||
hit.index2 = firstHit.indexList[2];
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
|
@ -632,6 +646,41 @@ void CSVRender::WorldspaceWidget::elementSelectionChanged()
|
|||
|
||||
void CSVRender::WorldspaceWidget::updateOverlay() {}
|
||||
|
||||
void CSVRender::WorldspaceWidget::handleMarkerHighlight(const int x, const int y)
|
||||
{
|
||||
auto [start, end, _] = getStartEndDirection(x, y);
|
||||
|
||||
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
|
||||
new osgUtil::LineSegmentIntersector(osgUtil::Intersector::MODEL, start, end));
|
||||
|
||||
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
|
||||
osgUtil::IntersectionVisitor visitor(intersector);
|
||||
|
||||
visitor.setTraversalMask(Mask_Reference);
|
||||
|
||||
mView->getCamera()->accept(visitor);
|
||||
|
||||
bool hitMarker = false;
|
||||
for (const auto& intersection : intersector->getIntersections())
|
||||
{
|
||||
if (mSelectionMarker->hitBehindMarker(intersection.getWorldIntersectPoint(), mView->getCamera()))
|
||||
continue;
|
||||
|
||||
for (const auto& node : intersection.nodePath)
|
||||
{
|
||||
if (const auto& marker = dynamic_cast<ObjectMarkerTag*>(node->getUserData()))
|
||||
{
|
||||
hitMarker = true;
|
||||
mSelectionMarker->updateMarkerHighlight(node->getName(), marker->mAxis);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hitMarker)
|
||||
mSelectionMarker->resetMarkerHighlight();
|
||||
}
|
||||
|
||||
void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent()).mouseMoveEvent(event);
|
||||
|
|
@ -685,6 +734,8 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
|||
}
|
||||
}
|
||||
|
||||
const QPointF& pos = event->localPos();
|
||||
handleMarkerHighlight(pos.x(), pos.y());
|
||||
SceneWidget::mouseMoveEvent(event);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <apps/opencs/view/render/tagbase.hpp>
|
||||
|
||||
#include "instancedragmodes.hpp"
|
||||
#include "objectmarker.hpp"
|
||||
#include "scenewidget.hpp"
|
||||
|
||||
class QDragEnterEvent;
|
||||
|
|
@ -89,7 +90,6 @@ namespace CSVRender
|
|||
QPoint mToolTipPos;
|
||||
bool mShowToolTips;
|
||||
int mToolTipDelay;
|
||||
bool mInConstructor;
|
||||
int mSelectedNavigationMode;
|
||||
|
||||
public:
|
||||
|
|
@ -186,6 +186,12 @@ namespace CSVRender
|
|||
|
||||
virtual void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) = 0;
|
||||
|
||||
template <typename Tag>
|
||||
std::optional<WorldspaceHitResult> checkTag(
|
||||
const osgUtil::LineSegmentIntersector::Intersection& intersection) const;
|
||||
|
||||
std::tuple<osg::Vec3d, osg::Vec3d, osg::Vec3d> getStartEndDirection(int pointX, int pointY) const;
|
||||
|
||||
/// Return the next intersection with scene elements matched by
|
||||
/// \a interactionMask based on \a localPos and the camera vector.
|
||||
/// If there is no such intersection, instead a point "in front" of \a localPos will be
|
||||
|
|
@ -216,7 +222,14 @@ namespace CSVRender
|
|||
|
||||
EditMode* getEditMode();
|
||||
|
||||
virtual CSVRender::Object* getObjectByReferenceId(const std::string& id) = 0;
|
||||
|
||||
ObjectMarker* getSelectionMarker() { return mSelectionMarker.get(); }
|
||||
const ObjectMarker* getSelectionMarker() const { return mSelectionMarker.get(); }
|
||||
|
||||
protected:
|
||||
const std::unique_ptr<CSVRender::ObjectMarker> mSelectionMarker;
|
||||
|
||||
/// Visual elements in a scene
|
||||
/// @note do not change the enumeration values, they are used in pre-existing button file names!
|
||||
enum ButtonId
|
||||
|
|
@ -252,6 +265,10 @@ namespace CSVRender
|
|||
void cycleNavigationMode();
|
||||
|
||||
private:
|
||||
bool hitBehindMarker(const osg::Vec3d& hitPos) const;
|
||||
|
||||
void handleMarkerHighlight(const int x, const int y);
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
|
||||
void dropEvent(QDropEvent* event) override;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ set(OPENMW_HEADERS
|
|||
profile.hpp
|
||||
)
|
||||
|
||||
source_group(apps/openmw FILES main.cpp android_main.cpp ${OPENMW_SOURCES} ${OPENMW_HEADERS} ${OPENMW_RESOURCES})
|
||||
source_group(apps/openmw FILES main.cpp androidmain.cpp ${OPENMW_SOURCES} ${OPENMW_HEADERS} ${OPENMW_RESOURCES})
|
||||
|
||||
add_openmw_dir (mwrender
|
||||
actors objects renderingmanager animation rotatecontroller sky skyutil npcanimation esm4npcanimation vismask
|
||||
|
|
@ -44,7 +44,7 @@ add_openmw_dir (mwgui
|
|||
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
|
||||
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
|
||||
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher
|
||||
postprocessorhud settings
|
||||
postprocessorhud settings worlditemmodel itemtransfer
|
||||
)
|
||||
|
||||
add_openmw_dir (mwdialogue
|
||||
|
|
@ -60,9 +60,9 @@ add_openmw_dir (mwscript
|
|||
|
||||
add_openmw_dir (mwlua
|
||||
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
||||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
||||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings coremwscriptbindings
|
||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings dialoguebindings
|
||||
postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker magicbindings factionbindings
|
||||
postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker landbindings magicbindings factionbindings
|
||||
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings
|
||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
||||
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
||||
|
|
@ -71,8 +71,8 @@ add_openmw_dir (mwlua
|
|||
)
|
||||
|
||||
add_openmw_dir (mwsound
|
||||
soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output
|
||||
loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater
|
||||
soundmanagerimp openaloutput ffmpegdecoder sound soundbuffer sounddecoder soundoutput
|
||||
loudness movieaudiofactory alext efx efxpresets regionsoundselector watersoundupdater
|
||||
)
|
||||
|
||||
add_openmw_dir (mwworld
|
||||
|
|
@ -127,7 +127,7 @@ if(BUILD_OPENMW)
|
|||
if (ANDROID)
|
||||
add_library(openmw SHARED
|
||||
main.cpp
|
||||
android_main.cpp
|
||||
androidmain.cpp
|
||||
)
|
||||
else()
|
||||
openmw_add_executable(openmw
|
||||
|
|
@ -138,6 +138,12 @@ if(BUILD_OPENMW)
|
|||
endif()
|
||||
|
||||
target_link_libraries(openmw openmw-lib)
|
||||
|
||||
# Workaround necessary to ensure osgAnimation::MatrixLinearSampler dynamic casts work under Clang
|
||||
# NOTE: it's unclear whether the broken behavior is spec-compliant
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
set_target_properties(openmw PROPERTIES ENABLE_EXPORTS ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING
|
||||
|
|
|
|||
|
|
@ -404,8 +404,8 @@ OMW::Engine::~Engine()
|
|||
mMechanicsManager = nullptr;
|
||||
mDialogueManager = nullptr;
|
||||
mJournal = nullptr;
|
||||
mScriptManager = nullptr;
|
||||
mWindowManager = nullptr;
|
||||
mScriptManager = nullptr;
|
||||
mWorld = nullptr;
|
||||
mStereoManager = nullptr;
|
||||
mSoundManager = nullptr;
|
||||
|
|
@ -433,6 +433,8 @@ OMW::Engine::~Engine()
|
|||
}
|
||||
|
||||
SDL_Quit();
|
||||
|
||||
Log(Debug::Info) << "Quitting peacefully.";
|
||||
}
|
||||
|
||||
// Set data dir
|
||||
|
|
@ -729,7 +731,7 @@ void OMW::Engine::prepareEngine()
|
|||
|
||||
mVFS = std::make_unique<VFS::Manager>();
|
||||
|
||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true, &mEncoder.get()->getStatelessEncoder());
|
||||
|
||||
mResourceSystem = std::make_unique<Resource::ResourceSystem>(
|
||||
mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder());
|
||||
|
|
@ -753,7 +755,7 @@ void OMW::Engine::prepareEngine()
|
|||
|
||||
mViewer->addEventHandler(mScreenCaptureHandler);
|
||||
|
||||
mL10nManager = std::make_unique<l10n::Manager>(mVFS.get());
|
||||
mL10nManager = std::make_unique<L10n::Manager>(mVFS.get());
|
||||
mL10nManager->setPreferredLocales(Settings::general().mPreferredLocales, Settings::general().mGmstOverridesL10n);
|
||||
mEnvironment.setL10nManager(*mL10nManager);
|
||||
|
||||
|
|
@ -1069,8 +1071,6 @@ void OMW::Engine::go()
|
|||
Settings::Manager::saveUser(mCfgMgr.getUserConfigPath() / "settings.cfg");
|
||||
Settings::ShaderManager::get().save();
|
||||
mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath());
|
||||
|
||||
Log(Debug::Info) << "Quitting peacefully.";
|
||||
}
|
||||
|
||||
void OMW::Engine::setCompileAll(bool all)
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ namespace MWDialogue
|
|||
class Journal;
|
||||
}
|
||||
|
||||
namespace l10n
|
||||
namespace L10n
|
||||
{
|
||||
class Manager;
|
||||
}
|
||||
|
|
@ -141,7 +141,7 @@ namespace OMW
|
|||
std::unique_ptr<MWState::StateManager> mStateManager;
|
||||
std::unique_ptr<MWLua::LuaManager> mLuaManager;
|
||||
std::unique_ptr<MWLua::Worker> mLuaWorker;
|
||||
std::unique_ptr<l10n::Manager> mL10nManager;
|
||||
std::unique_ptr<L10n::Manager> mL10nManager;
|
||||
MWBase::Environment mEnvironment;
|
||||
ToUTF8::FromType mEncoding;
|
||||
std::unique_ptr<ToUTF8::Utf8Encoder> mEncoder;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ bool parseOptions(int argc, char** argv, OMW::Engine& engine, Files::Configurati
|
|||
return false;
|
||||
}
|
||||
|
||||
cfgMgr.processPaths(variables, std::filesystem::current_path());
|
||||
|
||||
cfgMgr.readConfiguration(variables, desc);
|
||||
|
||||
Debug::setupLogging(cfgMgr.getLogPath(), "OpenMW");
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Resource
|
|||
class ResourceSystem;
|
||||
}
|
||||
|
||||
namespace l10n
|
||||
namespace L10n
|
||||
{
|
||||
class Manager;
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ namespace MWBase
|
|||
StateManager* mStateManager = nullptr;
|
||||
LuaManager* mLuaManager = nullptr;
|
||||
Resource::ResourceSystem* mResourceSystem = nullptr;
|
||||
l10n::Manager* mL10nManager = nullptr;
|
||||
L10n::Manager* mL10nManager = nullptr;
|
||||
float mFrameRateLimit = 0;
|
||||
float mFrameDuration = 0;
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ namespace MWBase
|
|||
|
||||
void setResourceSystem(Resource::ResourceSystem& value) { mResourceSystem = &value; }
|
||||
|
||||
void setL10nManager(l10n::Manager& value) { mL10nManager = &value; }
|
||||
void setL10nManager(L10n::Manager& value) { mL10nManager = &value; }
|
||||
|
||||
Misc::NotNullPtr<World> getWorld() const { return mWorld; }
|
||||
Misc::NotNullPtr<MWWorld::WorldModel> getWorldModel() const { return mWorldModel; }
|
||||
|
|
@ -122,7 +122,7 @@ namespace MWBase
|
|||
|
||||
Misc::NotNullPtr<Resource::ResourceSystem> getResourceSystem() const { return mResourceSystem; }
|
||||
|
||||
Misc::NotNullPtr<l10n::Manager> getL10nManager() const { return mL10nManager; }
|
||||
Misc::NotNullPtr<L10n::Manager> getL10nManager() const { return mL10nManager; }
|
||||
|
||||
float getFrameRateLimit() const { return mFrameRateLimit; }
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ namespace MWBase
|
|||
virtual void executeAction(int action) = 0;
|
||||
|
||||
virtual bool controlsDisabled() = 0;
|
||||
|
||||
virtual void saveBindings() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef GAME_MWBASE_LUAMANAGER_H
|
||||
#define GAME_MWBASE_LUAMANAGER_H
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
|
@ -75,6 +76,7 @@ namespace MWBase
|
|||
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
||||
// `arg` is either forwarded from MWGui::pushGuiMode or empty
|
||||
virtual void uiModeChanged(const MWWorld::Ptr& arg) = 0;
|
||||
virtual void savePermanentStorage(const std::filesystem::path& userConfigPath) = 0;
|
||||
|
||||
// TODO: notify LuaManager about other events
|
||||
// virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue