summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/actions/godot-build/action.yml36
-rw-r--r--.github/actions/godot-cache/action.yml22
-rw-r--r--.github/actions/godot-deps/action.yml27
-rw-r--r--.github/actions/upload-artifact/action.yml19
-rw-r--r--.github/workflows/android_builds.yml61
-rw-r--r--.github/workflows/ios_builds.yml57
-rw-r--r--.github/workflows/javascript_builds.yml68
-rw-r--r--.github/workflows/linux_builds.yml256
-rw-r--r--.github/workflows/macos_builds.yml130
-rw-r--r--.github/workflows/windows_builds.yml159
-rw-r--r--core/multiplayer/multiplayer_replicator.cpp7
-rw-r--r--core/variant/variant.cpp20
-rw-r--r--doc/classes/ConfigFile.xml2
-rw-r--r--drivers/vulkan/vulkan_context.cpp11
-rw-r--r--drivers/vulkan/vulkan_context.h1
-rw-r--r--editor/action_map_editor.cpp2
-rw-r--r--editor/code_editor.cpp28
-rw-r--r--editor/debugger/editor_debugger_node.cpp8
-rw-r--r--editor/debugger/editor_debugger_node.h2
-rw-r--r--editor/debugger/editor_debugger_server.cpp17
-rw-r--r--editor/debugger/editor_debugger_server.h2
-rw-r--r--editor/editor_autoload_settings.cpp28
-rw-r--r--editor/editor_autoload_settings.h1
-rw-r--r--editor/editor_inspector.cpp10
-rw-r--r--editor/import/resource_importer_scene.cpp189
-rw-r--r--editor/import/resource_importer_scene.h202
-rw-r--r--editor/import/scene_import_settings.cpp74
-rw-r--r--editor/import/scene_import_settings.h4
-rw-r--r--editor/import/scene_importer_mesh.cpp4
-rw-r--r--editor/import/scene_importer_mesh.h2
-rw-r--r--editor/plugins/mesh_instance_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/script_text_editor.cpp2
-rw-r--r--main/main.cpp17
-rw-r--r--modules/gdscript/gdscript_vm.cpp15
-rw-r--r--modules/mono/csharp_script.cpp3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs6
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp7
-rw-r--r--modules/raycast/SCsub3
-rw-r--r--modules/raycast/godot_update_embree.py12
-rw-r--r--modules/vhacd/register_types.cpp23
-rw-r--r--modules/websocket/editor_debugger_server_websocket.cpp14
-rw-r--r--modules/websocket/editor_debugger_server_websocket.h2
-rw-r--r--scene/3d/mesh_instance_3d.cpp3
-rw-r--r--scene/3d/skeleton_ik_3d.cpp2
-rw-r--r--scene/gui/code_edit.cpp4
-rw-r--r--scene/gui/rich_text_label.cpp4
-rw-r--r--scene/resources/mesh.cpp8
-rw-r--r--scene/resources/mesh.h35
-rw-r--r--thirdparty/embree/include/embree3/rtcore_config.h6
-rw-r--r--thirdparty/embree/kernels/bvh/bvh_intersector_hybrid.cpp917
-rw-r--r--thirdparty/embree/kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp59
-rw-r--r--thirdparty/embree/kernels/bvh/bvh_intersector_stream.cpp528
-rw-r--r--thirdparty/embree/kernels/bvh/bvh_intersector_stream_bvh4.cpp36
-rw-r--r--thirdparty/embree/kernels/bvh/bvh_intersector_stream_filters.cpp657
-rw-r--r--thirdparty/embree/kernels/config.h2
-rw-r--r--thirdparty/embree/kernels/hash.h2
57 files changed, 3199 insertions, 628 deletions
diff --git a/.github/actions/godot-build/action.yml b/.github/actions/godot-build/action.yml
new file mode 100644
index 0000000000..5ed64e7de2
--- /dev/null
+++ b/.github/actions/godot-build/action.yml
@@ -0,0 +1,36 @@
+name: Build Godot
+description: Build Godot with the provided options.
+inputs:
+ target:
+ description: The scons target (debug/release_debug/release).
+ default: "debug"
+ tools:
+ description: If tools are to be built.
+ default: false
+ tests:
+ description: If tests are to be built.
+ default: false
+ platform:
+ description: The Godot platform to build.
+ required: false
+ sconsflags:
+ default: ""
+ scons-cache:
+ description: The scons cache path.
+ default: "${{ github.workspace }}/.scons-cache/"
+ scons-cache-limit:
+ description: The scons cache size limit.
+ default: 4096
+runs:
+ using: "composite"
+ steps:
+ - name: Scons Build
+ shell: sh
+ env:
+ SCONSFLAGS: ${{ inputs.sconsflags }}
+ SCONS_CACHE: ${{ inputs.scons-cache }}
+ SCONS_CACHE_LIMIT: ${{ inputs.scons-cache-limit }}
+ run: |
+ echo "Building with flags:" ${{ env.SCONSFLAGS }}
+ scons p=${{ inputs.platform }} target=${{ inputs.target }} tools=${{ inputs.tools }} tests=${{ inputs.tests }} --jobs=2 ${{ env.SCONSFLAGS }}
+ ls -l bin/
diff --git a/.github/actions/godot-cache/action.yml b/.github/actions/godot-cache/action.yml
new file mode 100644
index 0000000000..db14a0b97a
--- /dev/null
+++ b/.github/actions/godot-cache/action.yml
@@ -0,0 +1,22 @@
+name: Setup Godot build cache
+description: Setup Godot build cache.
+inputs:
+ cache-name:
+ description: The cache base name (job name by default).
+ default: "${{github.job}}"
+ scons-cache:
+ description: The scons cache path.
+ default: "${{github.workspace}}/.scons-cache/"
+runs:
+ using: "composite"
+ steps:
+ # Upload cache on completion and check it out now
+ - name: Load .scons_cache directory
+ uses: actions/cache@v2
+ with:
+ path: ${{inputs.scons-cache}}
+ key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+ restore-keys: |
+ ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+ ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
+ ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}
diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml
new file mode 100644
index 0000000000..ee4d7d3751
--- /dev/null
+++ b/.github/actions/godot-deps/action.yml
@@ -0,0 +1,27 @@
+name: Setup python and scons
+description: Setup python, install the pip version of scons.
+inputs:
+ python-version:
+ description: The python version to use.
+ default: "3.x"
+ python-arch:
+ description: The python architecture.
+ default: "x64"
+runs:
+ using: "composite"
+ steps:
+ # Use python 3.x release (works cross platform)
+ - name: Set up Python 3.x
+ uses: actions/setup-python@v2
+ with:
+ # Semantic version range syntax or exact version of a Python version
+ python-version: ${{ inputs.python-version }}
+ # Optional - x64 or x86 architecture, defaults to x64
+ architecture: ${{ inputs.python-arch }}
+
+ - name: Setup scons
+ shell: bash
+ run: |
+ python -c "import sys; print(sys.version)"
+ python -m pip install scons
+ scons --version
diff --git a/.github/actions/upload-artifact/action.yml b/.github/actions/upload-artifact/action.yml
new file mode 100644
index 0000000000..bc1871b914
--- /dev/null
+++ b/.github/actions/upload-artifact/action.yml
@@ -0,0 +1,19 @@
+name: Upload Godot artifact
+description: Upload the Godot artifact.
+inputs:
+ name:
+ description: The artifact name.
+ default: "${{ github.job }}"
+ path:
+ description: The path to upload.
+ required: true
+ default: "bin/*"
+runs:
+ using: "composite"
+ steps:
+ - name: Upload Godot Artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ inputs.name }}
+ path: ${{ inputs.path }}
+ retention-days: 14
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml
index 5efeba4b5f..74f8fa8eae 100644
--- a/.github/workflows/android_builds.yml
+++ b/.github/workflows/android_builds.yml
@@ -4,8 +4,7 @@ on: [push, pull_request]
# Global Settings
env:
GODOT_BASE_BRANCH: master
- SCONSFLAGS: platform=android verbose=yes warnings=extra werror=yes debug_symbols=no --jobs=2 module_text_server_fb_enabled=yes
- SCONS_CACHE_LIMIT: 4096
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-android
@@ -14,7 +13,6 @@ concurrency:
jobs:
android-template:
runs-on: "ubuntu-20.04"
-
name: Template (target=release, tools=no)
steps:
@@ -32,48 +30,37 @@ jobs:
with:
java-version: 8
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
- id: android-template-cache
- uses: actions/cache@v2
- with:
- path: ${{github.workspace}}/.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+ - name: Setup Godot build cache
+ uses: ./.github/actions/godot-cache
continue-on-error: true
- # Use python 3.x release (works cross platform)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
+ - name: Setup python and scons
+ uses: ./.github/actions/godot-deps
+
+ - name: Compilation (armv7)
+ uses: ./.github/actions/godot-build
with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
+ sconsflags: ${{ env.SCONSFLAGS }} android_arch=armv7
+ platform: android
+ target: release
+ tools: false
+ tests: false
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
+ - name: Compilation (arm64v8)
+ uses: ./.github/actions/godot-build
+ with:
+ sconsflags: ${{ env.SCONSFLAGS }} android_arch=arm64v8
+ platform: android
+ target: release
+ tools: false
+ tests: false
- - name: Compilation
- env:
- SCONS_CACHE: ${{github.workspace}}/.scons_cache/
+ - name: Generate Godot templates
run: |
- scons target=release tools=no android_arch=armv7
- scons target=release tools=no android_arch=arm64v8
cd platform/android/java
./gradlew generateGodotTemplates
cd ../../..
ls -l bin/
- - uses: actions/upload-artifact@v2
- with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
+ - name: Upload artifact
+ uses: ./.github/actions/upload-artifact
diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml
index 69809c6cb6..721d574dbe 100644
--- a/.github/workflows/ios_builds.yml
+++ b/.github/workflows/ios_builds.yml
@@ -4,8 +4,7 @@ on: [push, pull_request]
# Global Settings
env:
GODOT_BASE_BRANCH: master
- SCONSFLAGS: platform=iphone verbose=yes warnings=extra werror=yes debug_symbols=no --jobs=2 module_text_server_fb_enabled=yes
- SCONS_CACHE_LIMIT: 4096
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-ios
@@ -19,45 +18,21 @@ jobs:
steps:
- uses: actions/checkout@v2
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
- id: ios-template-cache
- uses: actions/cache@v2
- with:
- path: ${{github.workspace}}/.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+ - name: Setup Godot build cache
+ uses: ./.github/actions/godot-cache
continue-on-error: true
- # Use python 3.x release (works cross platform)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # You can test your matrix by printing the current Python version
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
-
- - name: Compilation
- env:
- SCONS_CACHE: ${{github.workspace}}/.scons_cache/
- run: |
- scons target=release tools=no
- ls -l bin/
-
- - uses: actions/upload-artifact@v2
+ - name: Setup python and scons
+ uses: ./.github/actions/godot-deps
+
+ - name: Compilation (armv7)
+ uses: ./.github/actions/godot-build
with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
+ sconsflags: ${{ env.SCONSFLAGS }}
+ platform: iphone
+ target: release
+ tools: false
+ tests: false
+
+ - name: Upload artifact
+ uses: ./.github/actions/upload-artifact
diff --git a/.github/workflows/javascript_builds.yml b/.github/workflows/javascript_builds.yml
index 25a063c3b2..9163baab0f 100644
--- a/.github/workflows/javascript_builds.yml
+++ b/.github/workflows/javascript_builds.yml
@@ -4,10 +4,9 @@ on: [push, pull_request]
# Global Settings
env:
GODOT_BASE_BRANCH: master
- SCONSFLAGS: platform=javascript verbose=yes warnings=extra werror=yes debug_symbols=no --jobs=2
- SCONS_CACHE_LIMIT: 4096
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no
EM_VERSION: 2.0.27
- EM_CACHE_FOLDER: 'emsdk-cache'
+ EM_CACHE_FOLDER: "emsdk-cache"
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-javascript
@@ -21,26 +20,6 @@ jobs:
steps:
- uses: actions/checkout@v2
- # Azure repositories are not reliable, we need to prevent azure giving us packages.
- - name: Make apt sources.list use the default Ubuntu repositories
- run: |
- sudo rm -f /etc/apt/sources.list.d/*
- sudo cp -f misc/ci/sources.list /etc/apt/sources.list
- sudo apt-get update
-
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
- id: javascript-template-cache
- uses: actions/cache@v2
- with:
- path: ${{github.workspace}}/.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
- continue-on-error: true
-
# Additional cache for Emscripten generated system libraries
- name: Load Emscripten cache
id: javascript-template-emscripten-cache
@@ -49,23 +28,6 @@ jobs:
path: ${{env.EM_CACHE_FOLDER}}
key: ${{env.EM_VERSION}}-${{github.job}}
- # Use python 3.x release (works cross platform)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # You can test your matrix by printing the current Python version
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
-
- name: Set up Emscripten latest
uses: mymindstorm/setup-emsdk@v10
with:
@@ -76,15 +38,21 @@ jobs:
run: |
emcc -v
- - name: Compilation
- env:
- SCONS_CACHE: ${{github.workspace}}/.scons_cache/
- run: |
- scons target=release tools=no use_closure_compiler=yes
- ls -l bin/
+ - name: Setup Godot build cache
+ uses: ./.github/actions/godot-cache
+ continue-on-error: true
+
+ - name: Setup python and scons
+ uses: ./.github/actions/godot-deps
- - uses: actions/upload-artifact@v2
+ - name: Compilation
+ uses: ./.github/actions/godot-build
with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
+ sconsflags: ${{ env.SCONSFLAGS }}
+ platform: javascript
+ target: release
+ tools: false
+ tests: false
+
+ - name: Upload artifact
+ uses: ./.github/actions/upload-artifact
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 6fe76345f3..54e9ef4c66 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -4,79 +4,91 @@ on: [push, pull_request]
# Global Settings
env:
GODOT_BASE_BRANCH: master
- SCONSFLAGS: platform=linuxbsd verbose=yes warnings=extra werror=yes debug_symbols=no --jobs=2 module_text_server_fb_enabled=yes
- SCONS_CACHE_LIMIT: 4096
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-linux
cancel-in-progress: true
jobs:
- linux-editor:
+ build-linux:
runs-on: "ubuntu-20.04"
- name: Editor (target=release_debug, tools=yes, tests=yes)
+ name: ${{ matrix.name }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - name: Editor (target=release_debug, tools=yes, tests=yes)
+ cache-name: linux-editor
+ target: release_debug
+ tools: true
+ tests: true
+ sconsflags: ""
+ doc-test: true
+ bin: "./bin/godot.linuxbsd.opt.tools.64"
+ artifact: true
+
+ - name: Editor and sanitizers (target=debug, tools=yes, tests=yes, use_asan=yes, use_ubsan=yes)
+ cache-name: linux-editor-sanitizers
+ target: debug
+ tools: true
+ tests: true
+ sconsflags: use_asan=yes use_ubsan=yes
+ proj-test: true
+ bin: "./bin/godot.linuxbsd.tools.64s"
+ # Skip 2GiB artifact speeding up action.
+ artifact: false
+
+ - name: Template w/ Mono (target=release, tools=no)
+ cache-name: linux-template-mono
+ target: release
+ tools: false
+ tests: false
+ sconsflags: module_mono_enabled=yes mono_glue=no
+ artifact: true
steps:
- uses: actions/checkout@v2
- # Azure repositories are not reliable, we need to prevent azure giving us packages.
- - name: Make apt sources.list use the default Ubuntu repositories
+ - name: Linux dependencies
+ shell: bash
run: |
+ # Azure repositories are not reliable, we need to prevent azure giving us packages.
sudo rm -f /etc/apt/sources.list.d/*
sudo cp -f misc/ci/sources.list /etc/apt/sources.list
sudo apt-get update
-
- # Install all packages (except scons)
- - name: Configure dependencies
- run: |
+ # The actual dependencies
sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
- libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm
+ libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev \
+ libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm xvfb wget unzip
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
- id: linux-editor-cache
- uses: actions/cache@v2
+ - name: Setup Godot build cache
+ uses: ./.github/actions/godot-cache
with:
- path: ${{github.workspace}}/.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+ cache-name: ${{ matrix.cache-name }}
continue-on-error: true
- # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
+ - name: Setup python and scons
+ uses: ./.github/actions/godot-deps
- # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
- name: Compilation
- env:
- SCONS_CACHE: ${{github.workspace}}/.scons_cache/
- run: |
- scons tools=yes tests=yes target=release_debug
- ls -l bin/
+ uses: ./.github/actions/godot-build
+ with:
+ sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }}
+ platform: linuxbsd
+ target: ${{ matrix.target }}
+ tools: ${{ matrix.tools }}
+ tests: ${{ matrix.tests }}
# Execute unit tests for the editor
- - name: Unit Tests
+ - name: Unit tests
+ if: ${{ matrix.tests }}
run: |
- ./bin/godot.linuxbsd.opt.tools.64 --test
+ ${{ matrix.bin }} --test
# Download, unzip and setup SwiftShader library [4466040]
- name: Download SwiftShader
+ if: ${{ matrix.tests }}
run: |
wget https://github.com/qarmin/gtk_library_store/releases/download/3.24.0/swiftshader2.zip
unzip swiftshader2.zip
@@ -86,93 +98,16 @@ jobs:
# Check class reference
- name: Check for class reference updates
+ if: ${{ matrix.doc-test }}
run: |
echo "Running --doctool to see if this changes the public API without updating the documentation."
echo -e "If a diff is shown, it means that your code/doc changes are incomplete and you should update the class reference with --doctool.\n\n"
- VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run bin/godot.linuxbsd.opt.tools.64 --doctool . 2>&1 > /dev/null || true
+ VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --doctool . 2>&1 > /dev/null || true
git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$'
- - uses: actions/upload-artifact@v2
- with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
-
- linux-editor-sanitizers:
- runs-on: "ubuntu-20.04"
- name: Editor and sanitizers (target=debug, tools=yes, tests=yes, use_asan=yes, use_ubsan=yes)
-
- steps:
- - uses: actions/checkout@v2
-
- # Azure repositories are not reliable, we need to prevent azure giving us packages.
- - name: Make apt sources.list use the default Ubuntu repositories
- run: |
- sudo rm -f /etc/apt/sources.list.d/*
- sudo cp -f misc/ci/sources.list /etc/apt/sources.list
- sudo apt-get update
-
- # Install all packages (except scons)
- - name: Configure dependencies
- run: |
- sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
- libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm \
- xvfb wget unzip
-
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
- id: linux-sanitizers-cache
- uses: actions/cache@v2
- with:
- path: ${{github.workspace}}/.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
- continue-on-error: true
-
- # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
-
- # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
- - name: Compilation
- env:
- SCONS_CACHE: ${{github.workspace}}/.scons_cache/
- run: |
- scons tools=yes tests=yes target=debug debug_symbols=no use_asan=yes use_ubsan=yes
- ls -l bin/
-
- # Execute unit tests for the editor
- - name: Unit Tests
- run: |
- ./bin/godot.linuxbsd.tools.64s --test
-
- # Download, unzip and setup SwiftShader library [4466040]
- - name: Download SwiftShader
- run: |
- wget https://github.com/qarmin/gtk_library_store/releases/download/3.24.0/swiftshader2.zip
- unzip swiftshader2.zip
- rm swiftshader2.zip
- curr="$(pwd)/libvk_swiftshader.so"
- sed -i "s|PATH_TO_CHANGE|$curr|" vk_swiftshader_icd.json
-
# Download and extract zip archive with project, folder is renamed to be able to easy change used project
- name: Download test project
+ if: ${{ matrix.proj-test }}
run: |
wget https://github.com/qarmin/RegressionTestProject/archive/4.0.zip
unzip 4.0.zip
@@ -180,75 +115,20 @@ jobs:
# Editor is quite complicated piece of software, so it is easy to introduce bug here
- name: Open and close editor
+ if: ${{ matrix.proj-test }}
run: |
- VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run bin/godot.linuxbsd.tools.64s --audio-driver Dummy -e -q --path test_project 2>&1 | tee sanitizers_log.txt || true
+ VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --audio-driver Dummy -e -q --path test_project 2>&1 | tee sanitizers_log.txt || true
misc/scripts/check_ci_log.py sanitizers_log.txt
# Run test project
- name: Run project
+ if: ${{ matrix.proj-test }}
run: |
- VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run bin/godot.linuxbsd.tools.64s 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
+ VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run ${{ matrix.bin }} 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
misc/scripts/check_ci_log.py sanitizers_log.txt
- linux-template-mono:
- runs-on: "ubuntu-20.04"
- name: Template w/ Mono (target=release, tools=no)
-
- steps:
- - uses: actions/checkout@v2
-
- # Azure repositories are not reliable, we need to prevent azure giving us packages.
- - name: Make apt sources.list use the default Ubuntu repositories
- run: |
- sudo rm -f /etc/apt/sources.list.d/*
- sudo cp -f misc/ci/sources.list /etc/apt/sources.list
- sudo apt-get update
-
- # Install all packages (except scons)
- - name: Configure dependencies
- run: |
- sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
- libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm
-
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
- id: linux-template-cache
- uses: actions/cache@v2
- with:
- path: ${{github.workspace}}/.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
- continue-on-error: true
-
- # Use python 3.x release (works cross platform)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # You can test your matrix by printing the current Python version
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
-
- - name: Compilation
- env:
- SCONS_CACHE: ${{github.workspace}}/.scons_cache/
- run: |
- scons target=release tools=no module_mono_enabled=yes mono_glue=no
- ls -l bin/
-
- - uses: actions/upload-artifact@v2
+ - name: Upload artifact
+ uses: ./.github/actions/upload-artifact
+ if: ${{ matrix.artifact }}
with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
+ name: ${{ matrix.cache-name }}
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml
index 2c9e0fa1a0..38d43a4b94 100644
--- a/.github/workflows/macos_builds.yml
+++ b/.github/workflows/macos_builds.yml
@@ -4,117 +4,61 @@ on: [push, pull_request]
# Global Settings
env:
GODOT_BASE_BRANCH: master
- SCONSFLAGS: platform=osx verbose=yes warnings=extra werror=yes debug_symbols=no --jobs=2 module_text_server_fb_enabled=yes
- SCONS_CACHE_LIMIT: 4096
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-macos
cancel-in-progress: true
jobs:
- macos-editor:
+ build-macos:
runs-on: "macos-latest"
-
- name: Editor (target=release_debug, tools=yes, tests=yes)
+ name: ${{ matrix.name }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - name: Editor (target=release_debug, tools=yes, tests=yes)
+ cache-name: macos-editor
+ target: release_debug
+ tools: true
+ tests: true
+ bin: "./bin/godot.osx.opt.tools.64"
+
+ - name: Template (target=release, tools=no)
+ cache-name: macos-template
+ target: release
+ tools: false
+ tests: false
steps:
- uses: actions/checkout@v2
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
- id: macos-editor-cache
- uses: actions/cache@v2
+ - name: Setup Godot build cache
+ uses: ./.github/actions/godot-cache
with:
- path: ${{github.workspace}}/.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+ cache-name: ${{ matrix.cache-name }}
continue-on-error: true
- # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
+ - name: Setup python and scons
+ uses: ./.github/actions/godot-deps
- # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
- name: Compilation
- env:
- SCONS_CACHE: ${{github.workspace}}/.scons_cache/
- run: |
- scons tools=yes tests=yes target=release_debug
- ls -l bin/
-
- # Execute unit tests for the editor
- - name: Unit Tests
- run: |
- ./bin/godot.osx.opt.tools.64 --test
-
- - uses: actions/upload-artifact@v2
+ uses: ./.github/actions/godot-build
with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
+ sconsflags: ${{ env.SCONSFLAGS }}
+ platform: osx
+ target: ${{ matrix.target }}
+ tools: ${{ matrix.tools }}
+ tests: ${{ matrix.tests }}
- macos-template:
- runs-on: "macos-latest"
- name: Template (target=release, tools=no)
-
- steps:
- - uses: actions/checkout@v2
-
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
- id: macos-template-cache
- uses: actions/cache@v2
- with:
- path: ${{github.workspace}}/.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
- continue-on-error: true
-
- # Use python 3.x release (works cross platform)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # You can test your matrix by printing the current Python version
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
-
- - name: Compilation
- env:
- SCONS_CACHE: ${{github.workspace}}/.scons_cache/
+ # Execute unit tests for the editor
+ - name: Unit tests
+ if: ${{ matrix.tests }}
run: |
- scons target=release tools=no
- ls -l bin/
+ ${{ matrix.bin }} --test
- - uses: actions/upload-artifact@v2
+ - name: Upload artifact
+ uses: ./.github/actions/upload-artifact
with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
+ name: ${{ matrix.cache-name }}
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index 53febd353b..ee689612f5 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -5,122 +5,65 @@ on: [push, pull_request]
# SCONS_CACHE for windows must be set in the build environment
env:
GODOT_BASE_BRANCH: master
- SCONSFLAGS: platform=windows verbose=yes warnings=all werror=yes debug_symbols=no --jobs=2 module_text_server_fb_enabled=yes
+ SCONSFLAGS: verbose=yes warnings=all werror=yes debug_symbols=no module_text_server_fb_enabled=yes
SCONS_CACHE_MSVC_CONFIG: true
- SCONS_CACHE_LIMIT: 3072
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-windows
cancel-in-progress: true
jobs:
- windows-editor:
+ build-windows:
# Windows 10 with latest image
runs-on: "windows-latest"
-
- # Windows Editor - checkout with the plugin
- name: Editor (target=release_debug, tools=yes, tests=yes)
-
- steps:
- - uses: actions/checkout@v2
-
- # Upload cache on completion and check it out now
- # Editing this is pretty dangerous for Windows since it can break and needs to be properly tested with a fresh cache.
- - name: Load .scons_cache directory
- id: windows-editor-cache
- uses: actions/cache@v2
- with:
- path: /.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
- continue-on-error: true
-
- # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
-
- # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
- - name: Compilation
- env:
- SCONS_CACHE: /.scons_cache/
- run: |
- scons tools=yes tests=yes target=release_debug
- ls -l bin/
-
- # Execute unit tests for the editor
- - name: Unit Tests
- run: |
- ./bin/godot.windows.opt.tools.64.exe --test
-
- - uses: actions/upload-artifact@v2
- with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
-
- windows-template:
- runs-on: "windows-latest"
- name: Template (target=release, tools=no)
+ name: ${{ matrix.name }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - name: Editor (target=release_debug, tools=yes, tests=yes)
+ cache-name: windows-editor
+ target: release_debug
+ tools: true
+ tests: true
+ bin: "./bin/godot.windows.opt.tools.64.exe"
+
+ - name: Template (target=release, tools=no)
+ cache-name: windows-template
+ target: release
+ tools: false
+ tests: false
steps:
- - uses: actions/checkout@v2
-
- # Upload cache on completion and check it out now
- # Editing this is pretty dangerous for Windows since it can break and needs to be properly tested with a fresh cache.
- - name: Load .scons_cache directory
- id: windows-template-cache
- uses: RevoluPowered/cache@v2.1
- with:
- path: /.scons_cache/
- key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- restore-keys: |
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
- continue-on-error: true
-
- # Use python 3.x release (works cross platform)
- - name: Set up Python 3.x
- uses: actions/setup-python@v2
- with:
- # Semantic version range syntax or exact version of a Python version
- python-version: '3.x'
- # Optional - x64 or x86 architecture, defaults to x64
- architecture: 'x64'
-
- # You can test your matrix by printing the current Python version
- - name: Configuring Python packages
- run: |
- python -c "import sys; print(sys.version)"
- python -m pip install scons
- python --version
- scons --version
-
- - name: Compilation
- env:
- SCONS_CACHE: /.scons_cache/
- run: |
- scons target=release tools=no
- ls -l bin/
-
- - uses: actions/upload-artifact@v2
- with:
- name: ${{ github.job }}
- path: bin/*
- retention-days: 14
+ - uses: actions/checkout@v2
+
+ - name: Setup Godot build cache
+ uses: ./.github/actions/godot-cache
+ with:
+ cache-name: ${{ matrix.cache-name }}
+ continue-on-error: true
+
+
+ - name: Setup python and scons
+ uses: ./.github/actions/godot-deps
+
+ - name: Compilation
+ uses: ./.github/actions/godot-build
+ with:
+ sconsflags: ${{ env.SCONSFLAGS }}
+ platform: windows
+ target: ${{ matrix.target }}
+ tools: ${{ matrix.tools }}
+ tests: ${{ matrix.tests }}
+ scons-cache-limit: 3072
+
+ # Execute unit tests for the editor
+ - name: Unit tests
+ if: ${{ matrix.tests }}
+ run: |
+ ${{ matrix.bin }} --test
+
+ - name: Upload artifact
+ uses: ./.github/actions/upload-artifact
+ with:
+ name: ${{ matrix.cache-name }}
diff --git a/core/multiplayer/multiplayer_replicator.cpp b/core/multiplayer/multiplayer_replicator.cpp
index 17af2c5ef8..a4ea74327c 100644
--- a/core/multiplayer/multiplayer_replicator.cpp
+++ b/core/multiplayer/multiplayer_replicator.cpp
@@ -350,9 +350,9 @@ void MultiplayerReplicator::process_sync(int p_from, const uint8_t *p_packet, in
}
}
PackedByteArray pba;
- pba.resize(p_packet_len - SPAWN_CMD_OFFSET);
+ pba.resize(p_packet_len - SYNC_CMD_OFFSET);
if (pba.size()) {
- memcpy(pba.ptrw(), p_packet, p_packet_len - SPAWN_CMD_OFFSET);
+ memcpy(pba.ptrw(), p_packet + SYNC_CMD_OFFSET, p_packet_len - SYNC_CMD_OFFSET);
}
Variant args[4] = { p_from, id, objs, pba };
Variant *argp[4] = { args, &args[1], &args[2], &args[3] };
@@ -749,6 +749,9 @@ Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_s
uint8_t *ptr = packet_cache.ptrw();
ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC;
encode_uint64(p_scene_id, &ptr[1]);
+ if (p_data.size()) {
+ memcpy(&ptr[SYNC_CMD_OFFSET], p_data.ptr(), p_data.size());
+ }
Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
peer->set_target_peer(p_peer_id);
peer->set_transfer_channel(p_channel);
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index c5cada674f..0dbeb6e4cb 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -2832,7 +2832,7 @@ uint32_t Variant::hash() const {
return _data._bool ? 1 : 0;
} break;
case INT: {
- return _data._int;
+ return hash_one_uint64((uint64_t)_data._int);
} break;
case FLOAT: {
return hash_djb2_one_float(_data._float);
@@ -2847,8 +2847,8 @@ uint32_t Variant::hash() const {
return hash_djb2_one_float(reinterpret_cast<const Vector2 *>(_data._mem)->y, hash);
} break;
case VECTOR2I: {
- uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->x);
- return hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->y, hash);
+ uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector2i *>(_data._mem)->x);
+ return hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector2i *>(_data._mem)->y, hash);
} break;
case RECT2: {
uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->position.x);
@@ -2857,10 +2857,10 @@ uint32_t Variant::hash() const {
return hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->size.y, hash);
} break;
case RECT2I: {
- uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.x);
- hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash);
- hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash);
- return hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash);
+ uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->position.x);
+ hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash);
+ hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash);
+ return hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash);
} break;
case TRANSFORM2D: {
uint32_t hash = 5831;
@@ -2878,9 +2878,9 @@ uint32_t Variant::hash() const {
return hash_djb2_one_float(reinterpret_cast<const Vector3 *>(_data._mem)->z, hash);
} break;
case VECTOR3I: {
- uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->x);
- hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->y, hash);
- return hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->z, hash);
+ uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->x);
+ hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->y, hash);
+ return hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->z, hash);
} break;
case PLANE: {
uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Plane *>(_data._mem)->normal.x);
diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml
index 249e2a8f80..ce976e3d8b 100644
--- a/doc/classes/ConfigFile.xml
+++ b/doc/classes/ConfigFile.xml
@@ -84,6 +84,7 @@
}
[/csharp]
[/codeblocks]
+ Any operation that mutates the ConfigFile such as [method set_value], [method clear], or [method erase_section], only changes what is loaded in memory. If you want to write the change to a file, you have to save the changes with [method save], [method save_encrypted], or [method save_encrypted_pass].
Keep in mind that section and property names can't contain spaces. Anything after a space will be ignored on save and on load.
ConfigFiles can also contain manually written comment lines starting with a semicolon ([code];[/code]). Those lines will be ignored when parsing the file. Note that comments will be lost when saving the ConfigFile. This can still be useful for dedicated server configuration files, which are typically never overwritten without explicit user action.
[b]Note:[/b] The file extension given to a ConfigFile does not have any impact on its formatting or behavior. By convention, the [code].cfg[/code] extension is used here, but any other extension such as [code].ini[/code] is also valid. Since neither [code].cfg[/code] nor [code].ini[/code] are standardized, Godot's ConfigFile formatting may differ from files written by other programs.
@@ -94,6 +95,7 @@
<method name="clear">
<return type="void" />
<description>
+ Removes the entire contents of the config.
</description>
</method>
<method name="erase_section">
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index c14e3f0e93..bb0123e536 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -275,22 +275,21 @@ Error VulkanContext::_obtain_vulkan_version() {
if (res == VK_SUCCESS) {
vulkan_major = VK_VERSION_MAJOR(api_version);
vulkan_minor = VK_VERSION_MINOR(api_version);
- uint32_t vulkan_patch = VK_VERSION_PATCH(api_version);
-
- print_line("Vulkan API " + itos(vulkan_major) + "." + itos(vulkan_minor) + "." + itos(vulkan_patch));
+ vulkan_patch = VK_VERSION_PATCH(api_version);
} else {
// according to the documentation this shouldn't fail with anything except a memory allocation error
// in which case we're in deep trouble anyway
ERR_FAIL_V(ERR_CANT_CREATE);
}
} else {
- print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0");
+ print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0.");
}
// we don't go above 1.2
if ((vulkan_major > 1) || (vulkan_major == 1 && vulkan_minor > 2)) {
vulkan_major = 1;
vulkan_minor = 2;
+ vulkan_patch = 0;
}
return OK;
@@ -759,7 +758,9 @@ Error VulkanContext::_create_physical_device() {
}
}
- print_line("Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name);
+ print_line(
+ "Vulkan API " + itos(vulkan_major) + "." + itos(vulkan_minor) + "." + itos(vulkan_patch) +
+ " - " + "Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name);
device_api_version = gpu_props.apiVersion;
diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h
index 19ea806616..ae7c697be8 100644
--- a/drivers/vulkan/vulkan_context.h
+++ b/drivers/vulkan/vulkan_context.h
@@ -85,6 +85,7 @@ private:
// Vulkan 1.0 doesn't return version info so we assume this by default until we know otherwise
uint32_t vulkan_major = 1;
uint32_t vulkan_minor = 0;
+ uint32_t vulkan_patch = 0;
SubgroupCapabilities subgroup_capabilities;
MultiviewCapabilities multiview_capabilities;
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 7aa63f899b..6789b5be00 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -248,10 +248,8 @@ void InputEventConfigurationDialog::_listen_window_input(const Ref<InputEvent> &
k->set_pressed(false); // to avoid serialisation of 'pressed' property - doesn't matter for actions anyway.
// Maintain physical keycode option state
if (physical_key_checkbox->is_pressed()) {
- k->set_physical_keycode(k->get_keycode());
k->set_keycode(KEY_NONE);
} else {
- k->set_keycode((Key)k->get_physical_keycode());
k->set_physical_keycode(KEY_NONE);
}
}
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 89c2e49814..5599076c40 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -715,7 +715,27 @@ void CodeTextEditor::input(const Ref<InputEvent> &event) {
ERR_FAIL_COND(event.is_null());
const Ref<InputEventKey> key_event = event;
- if (!key_event.is_valid() || !key_event->is_pressed() || !text_editor->has_focus()) {
+
+ if (!key_event.is_valid()) {
+ return;
+ }
+ if (!key_event->is_pressed()) {
+ return;
+ }
+
+ if (!text_editor->has_focus()) {
+ if ((find_replace_bar != nullptr && find_replace_bar->is_visible()) && (find_replace_bar->has_focus() || find_replace_bar->is_ancestor_of(get_focus_owner()))) {
+ if (ED_IS_SHORTCUT("script_text_editor/find_next", key_event)) {
+ find_replace_bar->search_next();
+ accept_event();
+ return;
+ }
+ if (ED_IS_SHORTCUT("script_text_editor/find_previous", key_event)) {
+ find_replace_bar->search_prev();
+ accept_event();
+ return;
+ }
+ }
return;
}
@@ -1282,7 +1302,9 @@ void CodeTextEditor::_delete_line(int p_line) {
text_editor->set_caret_column(0);
}
text_editor->backspace();
- text_editor->unfold_line(p_line);
+ if (p_line < text_editor->get_line_count()) {
+ text_editor->unfold_line(p_line);
+ }
text_editor->set_caret_line(p_line);
}
@@ -1735,7 +1757,7 @@ void CodeTextEditor::goto_prev_bookmark() {
text_editor->set_caret_line(bmarks[bmarks.size() - 1]);
text_editor->center_viewport_to_caret();
} else {
- for (int i = bmarks.size(); i >= 0; i--) {
+ for (int i = bmarks.size() - 1; i >= 0; i--) {
int bmark_line = bmarks[i];
if (bmark_line < line) {
text_editor->unfold_line(bmark_line);
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 07c02eb022..be84e8dec5 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -183,16 +183,16 @@ ScriptEditorDebugger *EditorDebuggerNode::get_default_debugger() const {
return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(0));
}
-Error EditorDebuggerNode::start(const String &p_protocol) {
+Error EditorDebuggerNode::start(const String &p_uri) {
stop();
+ ERR_FAIL_COND_V(p_uri.find("://") < 0, ERR_INVALID_PARAMETER);
if (EDITOR_GET("run/output/always_open_output_on_play")) {
EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log());
} else {
EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
}
-
- server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_protocol));
- const Error err = server->start();
+ server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_uri.substr(0, p_uri.find("://") + 3)));
+ const Error err = server->start(p_uri);
if (err != OK) {
return err;
}
diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h
index 39a95326be..4d9e846834 100644
--- a/editor/debugger/editor_debugger_node.h
+++ b/editor/debugger/editor_debugger_node.h
@@ -188,7 +188,7 @@ public:
void set_camera_override(CameraOverride p_override);
CameraOverride get_camera_override();
- Error start(const String &p_protocol = "tcp://");
+ Error start(const String &p_uri = "tcp://");
void stop();
diff --git a/editor/debugger/editor_debugger_server.cpp b/editor/debugger/editor_debugger_server.cpp
index e8524e0702..8c3833af50 100644
--- a/editor/debugger/editor_debugger_server.cpp
+++ b/editor/debugger/editor_debugger_server.cpp
@@ -45,7 +45,7 @@ private:
public:
static EditorDebuggerServer *create(const String &p_protocol);
virtual void poll() {}
- virtual Error start();
+ virtual Error start(const String &p_uri);
virtual void stop();
virtual bool is_active() const;
virtual bool is_connection_available() const;
@@ -63,11 +63,18 @@ EditorDebuggerServerTCP::EditorDebuggerServerTCP() {
server.instantiate();
}
-Error EditorDebuggerServerTCP::start() {
- int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
- const Error err = server->listen(remote_port);
+Error EditorDebuggerServerTCP::start(const String &p_uri) {
+ int bind_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
+ String bind_host = (String)EditorSettings::get_singleton()->get("network/debug/remote_host");
+ if (!p_uri.is_empty() && p_uri != "tcp://") {
+ String scheme, path;
+ Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
+ ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
+ }
+ const Error err = server->listen(bind_port, bind_host);
if (err != OK) {
- EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR);
+ EditorNode::get_log()->add_message(String("Error listening on port ") + itos(bind_port), EditorLog::MSG_TYPE_ERROR);
return err;
}
return err;
diff --git a/editor/debugger/editor_debugger_server.h b/editor/debugger/editor_debugger_server.h
index 6216d0df3d..844d1a9e5a 100644
--- a/editor/debugger/editor_debugger_server.h
+++ b/editor/debugger/editor_debugger_server.h
@@ -48,7 +48,7 @@ public:
static void register_protocol_handler(const String &p_protocol, CreateServerFunc p_func);
static EditorDebuggerServer *create(const String &p_protocol);
virtual void poll() = 0;
- virtual Error start() = 0;
+ virtual Error start(const String &p_uri = "") = 0;
virtual void stop() = 0;
virtual bool is_active() const = 0;
virtual bool is_connection_available() const = 0;
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index 362c4c3457..fcf79a80a7 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -64,7 +64,12 @@ void EditorAutoloadSettings::_notification(int p_what) {
bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, String *r_error) {
if (!p_name.is_valid_identifier()) {
if (r_error) {
- *r_error = TTR("Invalid name.") + "\n" + TTR("Valid characters:") + " a-z, A-Z, 0-9 or _";
+ *r_error = TTR("Invalid name.") + " ";
+ if (p_name.size() > 0 && p_name.left(1).is_numeric()) {
+ *r_error += TTR("Cannot begin with a digit.");
+ } else {
+ *r_error += TTR("Valid characters:") + " a-z, A-Z, 0-9 or _";
+ }
}
return false;
@@ -72,7 +77,7 @@ bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, Strin
if (ClassDB::class_exists(p_name)) {
if (r_error) {
- *r_error = TTR("Invalid name.") + "\n" + TTR("Must not collide with an existing engine class name.");
+ *r_error = TTR("Invalid name.") + " " + TTR("Must not collide with an existing engine class name.");
}
return false;
@@ -89,7 +94,7 @@ bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, Strin
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
if (Variant::get_type_name(Variant::Type(i)) == p_name) {
if (r_error) {
- *r_error = TTR("Invalid name.") + "\n" + TTR("Must not collide with an existing built-in type name.");
+ *r_error = TTR("Invalid name.") + " " + TTR("Must not collide with an existing built-in type name.");
}
return false;
@@ -99,7 +104,7 @@ bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, Strin
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
if (CoreConstants::get_global_constant_name(i) == p_name) {
if (r_error) {
- *r_error = TTR("Invalid name.") + "\n" + TTR("Must not collide with an existing global constant name.");
+ *r_error = TTR("Invalid name.") + " " + TTR("Must not collide with an existing global constant name.");
}
return false;
@@ -112,7 +117,7 @@ bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, Strin
for (const String &E : keywords) {
if (E == p_name) {
if (r_error) {
- *r_error = TTR("Invalid name.") + "\n" + TTR("Keyword cannot be used as an autoload name.");
+ *r_error = TTR("Invalid name.") + " " + TTR("Keyword cannot be used as an autoload name.");
}
return false;
@@ -346,8 +351,11 @@ void EditorAutoloadSettings::_autoload_path_text_changed(const String p_path) {
}
void EditorAutoloadSettings::_autoload_text_changed(const String p_name) {
- add_autoload->set_disabled(
- autoload_add_path->get_text() == "" || !_autoload_name_is_valid(p_name, nullptr));
+ String error_string;
+ bool is_name_valid = _autoload_name_is_valid(p_name, &error_string);
+ add_autoload->set_disabled(autoload_add_path->get_text() == "" || !is_name_valid);
+ error_message->set_text(error_string);
+ error_message->set_visible(autoload_add_name->get_text() != "" && !is_name_valid);
}
Node *EditorAutoloadSettings::_create_autoload(const String &p_path) {
@@ -828,6 +836,12 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
HBoxContainer *hbc = memnew(HBoxContainer);
add_child(hbc);
+ error_message = memnew(Label);
+ error_message->hide();
+ error_message->set_align(Label::Align::ALIGN_RIGHT);
+ error_message->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ add_child(error_message);
+
Label *l = memnew(Label);
l->set_text(TTR("Path:"));
hbc->add_child(l);
diff --git a/editor/editor_autoload_settings.h b/editor/editor_autoload_settings.h
index b709728856..b8e054cd14 100644
--- a/editor/editor_autoload_settings.h
+++ b/editor/editor_autoload_settings.h
@@ -70,6 +70,7 @@ class EditorAutoloadSettings : public VBoxContainer {
LineEdit *autoload_add_name;
Button *add_autoload;
LineEdit *autoload_add_path;
+ Label *error_message;
Button *browse_button;
EditorFileDialog *file_dialog;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 298c1ed917..6bbf0f965e 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -1266,9 +1266,13 @@ void EditorInspectorSection::_notification(int p_what) {
}
header_height += get_theme_constant(SNAME("vseparation"), SNAME("Tree"));
+ Rect2 header_rect = Rect2(Vector2(), Vector2(get_size().width, header_height));
Color c = bg_color;
c.a *= 0.4;
- draw_rect(Rect2(Vector2(), Vector2(get_size().width, header_height)), c);
+ if (foldable && header_rect.has_point(get_local_mouse_position())) {
+ c = c.lightened(Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) ? -0.05 : 0.2);
+ }
+ draw_rect(header_rect, c);
const int arrow_margin = 2;
const int arrow_width = arrow.is_valid() ? arrow->get_width() : 0;
@@ -1315,12 +1319,14 @@ void EditorInspectorSection::_notification(int p_what) {
if (dropping) {
dropping_unfold_timer->start();
}
+ update();
} break;
case NOTIFICATION_MOUSE_EXIT: {
if (dropping) {
dropping_unfold_timer->stop();
}
+ update();
} break;
}
}
@@ -1395,6 +1401,8 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) {
} else {
fold();
}
+ } else if (mb.is_valid() && !mb->is_pressed()) {
+ update();
}
}
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index c2244befa1..9f617be4ee 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -233,13 +233,14 @@ static String _fixstr(const String &p_what, const String &p_str) {
return what;
}
-static void _pre_gen_shape_list(const Ref<EditorSceneImporterMesh> &mesh, List<Ref<Shape3D>> &r_shape_list, bool p_convex) {
+static void _pre_gen_shape_list(Ref<EditorSceneImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) {
ERR_FAIL_NULL_MSG(mesh, "Cannot generate shape list with null mesh value");
if (!p_convex) {
Ref<Shape3D> shape = mesh->create_trimesh_shape();
r_shape_list.push_back(shape);
} else {
- Vector<Ref<Shape3D>> cd = mesh->convex_decompose();
+ Vector<Ref<Shape3D>> cd;
+ cd.push_back(mesh->get_mesh()->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false));
if (cd.size()) {
for (int i = 0; i < cd.size(); i++) {
r_shape_list.push_back(cd[i]);
@@ -248,7 +249,7 @@ static void _pre_gen_shape_list(const Ref<EditorSceneImporterMesh> &mesh, List<R
}
}
-Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map) {
+Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> &collision_map) {
// children first
for (int i = 0; i < p_node->get_child_count(); i++) {
Node *r = _pre_fix_node(p_node->get_child(i), p_root, collision_map);
@@ -335,7 +336,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
- List<Ref<Shape3D>> shapes;
+ Vector<Ref<Shape3D>> shapes;
String fixed_name;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
@@ -406,7 +407,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
- List<Ref<Shape3D>> shapes;
+ Vector<Ref<Shape3D>> shapes;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
} else {
@@ -431,7 +432,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
- List<Ref<Shape3D>> shapes;
+ Vector<Ref<Shape3D>> shapes;
String fixed_name;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
@@ -490,7 +491,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (!mesh.is_null()) {
- List<Ref<Shape3D>> shapes;
+ Vector<Ref<Shape3D>> shapes;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
} else if (_teststr(mesh->get_name(), "col")) {
@@ -516,7 +517,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
return p_node;
}
-Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) {
+Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) {
// children first
for (int i = 0; i < p_node->get_child_count(); i++) {
Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps);
@@ -579,28 +580,35 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
if (node_settings.has("generate/physics")) {
- int mesh_physics_mode = node_settings["generate/physics"];
-
- if (mesh_physics_mode != MESH_PHYSICS_DISABLED) {
- List<Ref<Shape3D>> shapes;
+ int mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_DISABLED;
+
+ const bool generate_collider = node_settings["generate/physics"];
+ if (generate_collider) {
+ mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;
+ if (node_settings.has("physics/body_type")) {
+ const BodyType body_type = (BodyType)node_settings["physics/body_type"].operator int();
+ switch (body_type) {
+ case BODY_TYPE_STATIC:
+ mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;
+ break;
+ case BODY_TYPE_DYNAMIC:
+ mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_RIGID_BODY_AND_MESH;
+ break;
+ case BODY_TYPE_AREA:
+ mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_AREA_ONLY;
+ break;
+ }
+ }
+ }
+ if (mesh_physics_mode != MeshPhysicsMode::MESH_PHYSICS_DISABLED) {
+ Vector<Ref<Shape3D>> shapes;
if (collision_map.has(m)) {
shapes = collision_map[m];
} else {
- switch (mesh_physics_mode) {
- case MESH_PHYSICS_MESH_AND_STATIC_COLLIDER: {
- _pre_gen_shape_list(m, shapes, false);
- } break;
- case MESH_PHYSICS_RIGID_BODY_AND_MESH: {
- _pre_gen_shape_list(m, shapes, true);
- } break;
- case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {
- _pre_gen_shape_list(m, shapes, false);
- } break;
- case MESH_PHYSICS_AREA_ONLY: {
- _pre_gen_shape_list(m, shapes, true);
- } break;
- }
+ shapes = get_collision_shapes(
+ m->get_mesh(),
+ node_settings);
}
if (shapes.size()) {
@@ -609,13 +617,15 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
case MESH_PHYSICS_MESH_AND_STATIC_COLLIDER: {
StaticBody3D *col = memnew(StaticBody3D);
p_node->add_child(col);
+ col->set_owner(p_node->get_owner());
+ col->set_transform(get_collision_shapes_transform(node_settings));
base = col;
} break;
case MESH_PHYSICS_RIGID_BODY_AND_MESH: {
RigidBody3D *rigid_body = memnew(RigidBody3D);
rigid_body->set_name(p_node->get_name());
p_node->replace_by(rigid_body);
- rigid_body->set_transform(mi->get_transform());
+ rigid_body->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
p_node = rigid_body;
mi->set_transform(Transform3D());
rigid_body->add_child(mi);
@@ -624,7 +634,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
} break;
case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {
StaticBody3D *col = memnew(StaticBody3D);
- col->set_transform(mi->get_transform());
+ col->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
col->set_name(p_node->get_name());
p_node->replace_by(col);
memdelete(p_node);
@@ -633,7 +643,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
} break;
case MESH_PHYSICS_AREA_ONLY: {
Area3D *area = memnew(Area3D);
- area->set_transform(mi->get_transform());
+ area->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
area->set_name(p_node->get_name());
p_node->replace_by(area);
memdelete(p_node);
@@ -933,8 +943,35 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
} break;
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/physics", PROPERTY_HINT_ENUM, "Disabled,Mesh + Static Collider,Rigid Body + Mesh,Static Collider Only,Area Only"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "Static,Dynamic,Area"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/shape_type", PROPERTY_HINT_ENUM, "Decompose Convex,Simple Convex,Trimesh,Box,Sphere,Cylinder,Capsule", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
+
+ // Decomposition
+ Mesh::ConvexDecompositionSettings decomposition_default;
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/advanced", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/precision", PROPERTY_HINT_RANGE, "1,10,1"), 5));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/max_concavity", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.max_concavity));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/symmetry_planes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.symmetry_planes_clipping_bias));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/revolution_axes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.revolution_axes_clipping_bias));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/min_volume_per_convex_hull", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.min_volume_per_convex_hull));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/resolution", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.resolution));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_num_vertices_per_convex_hull", PROPERTY_HINT_RANGE, "5,512,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.max_num_vertices_per_convex_hull));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/plane_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.plane_downsampling));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/convexhull_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.convexhull_downsampling));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.normalize_mesh));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), static_cast<int>(decomposition_default.mode)));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.convexhull_approximation));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.max_convex_hulls));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.project_hull_vertices));
+
+ // Primitives: Box, Sphere, Cylinder, Capsule.
+ r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3(2.0, 2.0, 2.0)));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3()));
} break;
case INTERNAL_IMPORT_CATEGORY_MESH: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
@@ -985,6 +1022,65 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
case INTERNAL_IMPORT_CATEGORY_NODE: {
} break;
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
+ const bool generate_physics =
+ p_options.has("generate/physics") &&
+ p_options["generate/physics"].operator bool();
+
+ if (
+ p_option == "physics/body_type" ||
+ p_option == "physics/shape_type") {
+ // Show if need to generate collisions.
+ return generate_physics;
+ }
+
+ if (p_option.find("decomposition/") >= 0) {
+ // Show if need to generate collisions.
+ if (generate_physics &&
+ // Show if convex is enabled.
+ p_options["physics/shape_type"] == Variant(SHAPE_TYPE_DECOMPOSE_CONVEX)) {
+ if (p_option == "decomposition/advanced") {
+ return true;
+ }
+
+ const bool decomposition_advanced =
+ p_options.has("decomposition/advanced") &&
+ p_options["decomposition/advanced"].operator bool();
+
+ if (p_option == "decomposition/precision") {
+ return !decomposition_advanced;
+ } else {
+ return decomposition_advanced;
+ }
+ }
+
+ return false;
+ }
+
+ if (p_option == "primitive/position" || p_option == "primitive/rotation") {
+ const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
+ return generate_physics &&
+ physics_shape >= SHAPE_TYPE_BOX;
+ }
+
+ if (p_option == "primitive/size") {
+ const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
+ return generate_physics &&
+ physics_shape == SHAPE_TYPE_BOX;
+ }
+
+ if (p_option == "primitive/radius") {
+ const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
+ return generate_physics && (physics_shape == SHAPE_TYPE_SPHERE ||
+ physics_shape == SHAPE_TYPE_CYLINDER ||
+ physics_shape == SHAPE_TYPE_CAPSULE);
+ }
+
+ if (p_option == "primitive/height") {
+ const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
+ return generate_physics &&
+ (physics_shape == SHAPE_TYPE_CYLINDER ||
+ physics_shape == SHAPE_TYPE_CAPSULE);
+ }
} break;
case INTERNAL_IMPORT_CATEGORY_MESH: {
if (p_option == "save_to_file/path" || p_option == "save_to_file/make_streamable") {
@@ -1021,6 +1117,33 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
return true;
}
+bool ResourceImporterScene::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const {
+ switch (p_category) {
+ case INTERNAL_IMPORT_CATEGORY_NODE: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
+ if (
+ p_option == "generate/physics" ||
+ p_option == "physics/shape_type" ||
+ p_option.find("decomposition/") >= 0 ||
+ p_option.find("primitive/") >= 0) {
+ return true;
+ }
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MESH: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
+ } break;
+ default: {
+ }
+ }
+ return false;
+}
+
void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), "Node3D"));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), "Scene Root"));
@@ -1275,7 +1398,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
}
-void ResourceImporterScene::_add_shapes(Node *p_node, const List<Ref<Shape3D>> &p_shapes) {
+void ResourceImporterScene::_add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes) {
for (const Ref<Shape3D> &E : p_shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(E);
@@ -1316,7 +1439,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
return nullptr;
}
- Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> collision_map;
+ Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
_pre_fix_node(scene, scene, collision_map);
@@ -1392,7 +1515,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
}
Set<Ref<EditorSceneImporterMesh>> scanned_meshes;
- Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> collision_map;
+ Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
_pre_fix_node(scene, scene, collision_map);
_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 542959be02..e232b715be 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -63,7 +63,6 @@ public:
IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 4,
IMPORT_GENERATE_TANGENT_ARRAYS = 8,
IMPORT_USE_NAMED_SKIN_BINDS = 16,
-
};
virtual uint32_t get_import_flags() const;
@@ -125,9 +124,25 @@ class ResourceImporterScene : public ResourceImporter {
MESH_OVERRIDE_DISABLE,
};
+ enum BodyType {
+ BODY_TYPE_STATIC,
+ BODY_TYPE_DYNAMIC,
+ BODY_TYPE_AREA
+ };
+
+ enum ShapeType {
+ SHAPE_TYPE_DECOMPOSE_CONVEX,
+ SHAPE_TYPE_SIMPLE_CONVEX,
+ SHAPE_TYPE_TRIMESH,
+ SHAPE_TYPE_BOX,
+ SHAPE_TYPE_SPHERE,
+ SHAPE_TYPE_CYLINDER,
+ SHAPE_TYPE_CAPSULE,
+ };
+
void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches);
- void _add_shapes(Node *p_node, const List<Ref<Shape3D>> &p_shapes);
+ void _add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes);
public:
static ResourceImporterScene *get_singleton() { return singleton; }
@@ -159,14 +174,15 @@ public:
void get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const;
bool get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const;
+ bool get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const;
virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
// Import scenes *after* everything else (such as textures).
virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
- Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map);
- Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
+ Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> &collision_map);
+ Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks);
void _create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all);
@@ -184,6 +200,12 @@ public:
virtual bool can_import_threaded() const override { return false; }
ResourceImporterScene();
+
+ template <class M>
+ static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options);
+
+ template <class M>
+ static Transform3D get_collision_shapes_transform(const M &p_options);
};
class EditorSceneImporterESCN : public EditorSceneImporter {
@@ -196,4 +218,176 @@ public:
virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) override;
};
+#include "scene/resources/box_shape_3d.h"
+#include "scene/resources/capsule_shape_3d.h"
+#include "scene/resources/cylinder_shape_3d.h"
+#include "scene/resources/sphere_shape_3d.h"
+
+template <class M>
+Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options) {
+ ShapeType generate_shape_type = SHAPE_TYPE_DECOMPOSE_CONVEX;
+ if (p_options.has(SNAME("physics/shape_type"))) {
+ generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int();
+ }
+
+ if (generate_shape_type == SHAPE_TYPE_DECOMPOSE_CONVEX) {
+ Mesh::ConvexDecompositionSettings decomposition_settings;
+ bool advanced = false;
+ if (p_options.has(SNAME("decomposition/advanced"))) {
+ advanced = p_options[SNAME("decomposition/advanced")];
+ }
+
+ if (advanced) {
+ if (p_options.has(SNAME("decomposition/max_concavity"))) {
+ decomposition_settings.max_concavity = p_options[SNAME("decomposition/max_concavity")];
+ }
+
+ if (p_options.has(SNAME("decomposition/symmetry_planes_clipping_bias"))) {
+ decomposition_settings.symmetry_planes_clipping_bias = p_options[SNAME("decomposition/symmetry_planes_clipping_bias")];
+ }
+
+ if (p_options.has(SNAME("decomposition/revolution_axes_clipping_bias"))) {
+ decomposition_settings.revolution_axes_clipping_bias = p_options[SNAME("decomposition/revolution_axes_clipping_bias")];
+ }
+
+ if (p_options.has(SNAME("decomposition/min_volume_per_convex_hull"))) {
+ decomposition_settings.min_volume_per_convex_hull = p_options[SNAME("decomposition/min_volume_per_convex_hull")];
+ }
+
+ if (p_options.has(SNAME("decomposition/resolution"))) {
+ decomposition_settings.resolution = p_options[SNAME("decomposition/resolution")];
+ }
+
+ if (p_options.has(SNAME("decomposition/max_num_vertices_per_convex_hull"))) {
+ decomposition_settings.max_num_vertices_per_convex_hull = p_options[SNAME("decomposition/max_num_vertices_per_convex_hull")];
+ }
+
+ if (p_options.has(SNAME("decomposition/plane_downsampling"))) {
+ decomposition_settings.plane_downsampling = p_options[SNAME("decomposition/plane_downsampling")];
+ }
+
+ if (p_options.has(SNAME("decomposition/convexhull_downsampling"))) {
+ decomposition_settings.convexhull_downsampling = p_options[SNAME("decomposition/convexhull_downsampling")];
+ }
+
+ if (p_options.has(SNAME("decomposition/normalize_mesh"))) {
+ decomposition_settings.normalize_mesh = p_options[SNAME("decomposition/normalize_mesh")];
+ }
+
+ if (p_options.has(SNAME("decomposition/mode"))) {
+ decomposition_settings.mode = (Mesh::ConvexDecompositionSettings::Mode)p_options[SNAME("decomposition/mode")].operator int();
+ }
+
+ if (p_options.has(SNAME("decomposition/convexhull_approximation"))) {
+ decomposition_settings.convexhull_approximation = p_options[SNAME("decomposition/convexhull_approximation")];
+ }
+
+ if (p_options.has(SNAME("decomposition/max_convex_hulls"))) {
+ decomposition_settings.max_convex_hulls = p_options[SNAME("decomposition/max_convex_hulls")];
+ }
+
+ if (p_options.has(SNAME("decomposition/project_hull_vertices"))) {
+ decomposition_settings.project_hull_vertices = p_options[SNAME("decomposition/project_hull_vertices")];
+ }
+ } else {
+ int precision_level = 5;
+ if (p_options.has(SNAME("decomposition/precision"))) {
+ precision_level = p_options[SNAME("decomposition/precision")];
+ }
+
+ const real_t precision = real_t(precision_level - 1) / 9.0;
+
+ decomposition_settings.max_concavity = Math::lerp(real_t(1.0), real_t(0.001), precision);
+ decomposition_settings.min_volume_per_convex_hull = Math::lerp(real_t(0.01), real_t(0.0001), precision);
+ decomposition_settings.resolution = Math::lerp(10'000, 100'000, precision);
+ decomposition_settings.max_num_vertices_per_convex_hull = Math::lerp(32, 64, precision);
+ decomposition_settings.plane_downsampling = Math::lerp(3, 16, precision);
+ decomposition_settings.convexhull_downsampling = Math::lerp(3, 16, precision);
+ decomposition_settings.max_convex_hulls = Math::lerp(1, 32, precision);
+ }
+
+ return p_mesh->convex_decompose(decomposition_settings);
+ } else if (generate_shape_type == SHAPE_TYPE_SIMPLE_CONVEX) {
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(p_mesh->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false));
+ return shapes;
+ } else if (generate_shape_type == SHAPE_TYPE_TRIMESH) {
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(p_mesh->create_trimesh_shape());
+ return shapes;
+ } else if (generate_shape_type == SHAPE_TYPE_BOX) {
+ Ref<BoxShape3D> box;
+ box.instantiate();
+ if (p_options.has(SNAME("primitive/size"))) {
+ box->set_size(p_options[SNAME("primitive/size")]);
+ }
+
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(box);
+ return shapes;
+
+ } else if (generate_shape_type == SHAPE_TYPE_SPHERE) {
+ Ref<SphereShape3D> sphere;
+ sphere.instantiate();
+ if (p_options.has(SNAME("primitive/radius"))) {
+ sphere->set_radius(p_options[SNAME("primitive/radius")]);
+ }
+
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(sphere);
+ return shapes;
+ } else if (generate_shape_type == SHAPE_TYPE_CYLINDER) {
+ Ref<CylinderShape3D> cylinder;
+ cylinder.instantiate();
+ if (p_options.has(SNAME("primitive/height"))) {
+ cylinder->set_height(p_options[SNAME("primitive/height")]);
+ }
+ if (p_options.has(SNAME("primitive/radius"))) {
+ cylinder->set_radius(p_options[SNAME("primitive/radius")]);
+ }
+
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(cylinder);
+ return shapes;
+ } else if (generate_shape_type == SHAPE_TYPE_CAPSULE) {
+ Ref<CapsuleShape3D> capsule;
+ capsule.instantiate();
+ if (p_options.has(SNAME("primitive/height"))) {
+ capsule->set_height(p_options[SNAME("primitive/height")]);
+ }
+ if (p_options.has(SNAME("primitive/radius"))) {
+ capsule->set_radius(p_options[SNAME("primitive/radius")]);
+ }
+
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(capsule);
+ return shapes;
+ }
+ return Vector<Ref<Shape3D>>();
+}
+
+template <class M>
+Transform3D ResourceImporterScene::get_collision_shapes_transform(const M &p_options) {
+ Transform3D transform;
+
+ ShapeType generate_shape_type = SHAPE_TYPE_DECOMPOSE_CONVEX;
+ if (p_options.has(SNAME("physics/shape_type"))) {
+ generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int();
+ }
+
+ if (generate_shape_type == SHAPE_TYPE_BOX ||
+ generate_shape_type == SHAPE_TYPE_SPHERE ||
+ generate_shape_type == SHAPE_TYPE_CYLINDER ||
+ generate_shape_type == SHAPE_TYPE_CAPSULE) {
+ if (p_options.has(SNAME("primitive/position"))) {
+ transform.origin = p_options[SNAME("primitive/position")];
+ }
+
+ if (p_options.has(SNAME("primitive/rotation"))) {
+ transform.basis.set_euler((p_options[SNAME("primitive/rotation")].operator Vector3() / 180.0) * Math_PI);
+ }
+ }
+ return transform;
+}
+
#endif // RESOURCEIMPORTERSCENE_H
diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp
index 19a8f209bb..4bcb6863fb 100644
--- a/editor/import/scene_import_settings.cpp
+++ b/editor/import/scene_import_settings.cpp
@@ -53,6 +53,11 @@ class SceneImportSettingsData : public Object {
}
current[p_name] = p_value;
+
+ if (ResourceImporterScene::get_singleton()->get_internal_option_update_view_required(category, p_name, current)) {
+ SceneImportSettings::get_singleton()->update_view();
+ }
+
return true;
}
return false;
@@ -317,6 +322,13 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
if (mesh_node && mesh_node->get_mesh().is_valid()) {
_fill_mesh(scene_tree, mesh_node->get_mesh(), item);
+ // Add the collider view.
+ MeshInstance3D *collider_view = memnew(MeshInstance3D);
+ collider_view->set_name("collider_view");
+ collider_view->set_visible(false);
+ mesh_node->add_child(collider_view);
+ collider_view->set_owner(mesh_node);
+
Transform3D accum_xform;
Node3D *base = mesh_node;
while (base) {
@@ -346,6 +358,54 @@ void SceneImportSettings::_update_scene() {
_fill_scene(scene, nullptr);
}
+void SceneImportSettings::_update_view_gizmos() {
+ for (const KeyValue<String, NodeData> &e : node_map) {
+ bool generate_collider = false;
+ if (e.value.settings.has(SNAME("generate/physics"))) {
+ generate_collider = e.value.settings[SNAME("generate/physics")];
+ }
+
+ MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(e.value.node);
+ if (mesh_node == nullptr || mesh_node->get_mesh().is_null()) {
+ // Nothing to do
+ continue;
+ }
+
+ MeshInstance3D *collider_view = static_cast<MeshInstance3D *>(mesh_node->find_node("collider_view"));
+ CRASH_COND_MSG(collider_view == nullptr, "This is unreachable, since the collider view is always created even when the collision is not used! If this is triggered there is a bug on the function `_fill_scene`.");
+
+ collider_view->set_visible(generate_collider);
+ if (generate_collider) {
+ // This collider_view doesn't have a mesh so we need to generate a new one.
+
+ // Generate the mesh collider.
+ Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh_node->get_mesh(), e.value.settings);
+ const Transform3D transform = ResourceImporterScene::get_collision_shapes_transform(e.value.settings);
+
+ Ref<ArrayMesh> collider_view_mesh;
+ collider_view_mesh.instantiate();
+ for (Ref<Shape3D> shape : shapes) {
+ Ref<ArrayMesh> debug_shape_mesh;
+ if (shape.is_valid()) {
+ debug_shape_mesh = shape->get_debug_mesh();
+ }
+ if (debug_shape_mesh.is_valid()) {
+ collider_view_mesh->add_surface_from_arrays(
+ debug_shape_mesh->surface_get_primitive_type(0),
+ debug_shape_mesh->surface_get_arrays(0));
+
+ collider_view_mesh->surface_set_material(
+ collider_view_mesh->get_surface_count() - 1,
+ collider_mat);
+ }
+ }
+
+ collider_view->set_mesh(collider_view_mesh);
+ collider_view->set_transform(transform);
+ }
+ }
+}
+
void SceneImportSettings::_update_camera() {
AABB camera_aabb;
@@ -404,11 +464,16 @@ void SceneImportSettings::_load_default_subresource_settings(Map<StringName, Var
}
}
+void SceneImportSettings::update_view() {
+ _update_view_gizmos();
+}
+
void SceneImportSettings::open_settings(const String &p_path) {
if (scene) {
memdelete(scene);
scene = nullptr;
}
+ scene_import_settings_data->settings = nullptr;
scene = ResourceImporterScene::get_singleton()->pre_import(p_path);
if (scene == nullptr) {
EditorNode::get_singleton()->show_warning(TTR("Error opening scene"));
@@ -463,6 +528,7 @@ void SceneImportSettings::open_settings(const String &p_path) {
}
popup_centered_ratio();
+ _update_view_gizmos();
_update_camera();
set_title(vformat(TTR("Advanced Import Settings for '%s'"), base_path.get_file()));
@@ -629,6 +695,7 @@ void SceneImportSettings::_material_tree_selected() {
_select(material_tree, type, import_id);
}
+
void SceneImportSettings::_mesh_tree_selected() {
if (selecting) {
return;
@@ -640,6 +707,7 @@ void SceneImportSettings::_mesh_tree_selected() {
_select(mesh_tree, type, import_id);
}
+
void SceneImportSettings::_scene_tree_selected() {
if (selecting) {
return;
@@ -1144,6 +1212,12 @@ SceneImportSettings::SceneImportSettings() {
material_preview.instantiate();
}
+ {
+ collider_mat.instantiate();
+ collider_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ collider_mat->set_albedo(Color(0.5, 0.5, 1.0));
+ }
+
inspector = memnew(EditorInspector);
inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h
index ddcf4a6d5d..c7c94af493 100644
--- a/editor/import/scene_import_settings.h
+++ b/editor/import/scene_import_settings.h
@@ -84,6 +84,8 @@ class SceneImportSettings : public ConfirmationDialog {
MeshInstance3D *mesh_preview;
Ref<SphereMesh> material_preview;
+ Ref<StandardMaterial3D> collider_mat;
+
float cam_rot_x;
float cam_rot_y;
float cam_zoom;
@@ -145,6 +147,7 @@ class SceneImportSettings : public ConfirmationDialog {
bool selecting = false;
+ void _update_view_gizmos();
void _update_camera();
void _select(Tree *p_from, String p_type, String p_id);
void _material_tree_selected();
@@ -190,6 +193,7 @@ protected:
void _notification(int p_what);
public:
+ void update_view();
void open_settings(const String &p_path);
static SceneImportSettings *get_singleton();
SceneImportSettings();
diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp
index d8248e2670..55bea50432 100644
--- a/editor/import/scene_importer_mesh.cpp
+++ b/editor/import/scene_importer_mesh.cpp
@@ -524,12 +524,12 @@ Vector<Face3> EditorSceneImporterMesh::get_faces() const {
return faces;
}
-Vector<Ref<Shape3D>> EditorSceneImporterMesh::convex_decompose() const {
+Vector<Ref<Shape3D>> EditorSceneImporterMesh::convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const {
ERR_FAIL_COND_V(!Mesh::convex_composition_function, Vector<Ref<Shape3D>>());
const Vector<Face3> faces = get_faces();
- Vector<Vector<Face3>> decomposed = Mesh::convex_composition_function(faces, -1);
+ Vector<Vector<Face3>> decomposed = Mesh::convex_composition_function(faces, p_settings);
Vector<Ref<Shape3D>> ret;
diff --git a/editor/import/scene_importer_mesh.h b/editor/import/scene_importer_mesh.h
index c8e25244fa..d32b1fdf74 100644
--- a/editor/import/scene_importer_mesh.h
+++ b/editor/import/scene_importer_mesh.h
@@ -109,7 +109,7 @@ public:
Ref<EditorSceneImporterMesh> get_shadow_mesh() const;
Vector<Face3> get_faces() const;
- Vector<Ref<Shape3D>> convex_decompose() const;
+ Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const;
Ref<Shape3D> create_trimesh_shape() const;
Ref<NavigationMesh> create_navigation_mesh();
Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache);
diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
index 9a2b222f21..574d3ef27e 100644
--- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
@@ -202,7 +202,8 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
return;
}
- Vector<Ref<Shape3D>> shapes = mesh->convex_decompose();
+ Mesh::ConvexDecompositionSettings settings;
+ Vector<Ref<Shape3D>> shapes = mesh->convex_decompose(settings);
if (!shapes.size()) {
err_dialog->set_text(TTR("Couldn't create any collision shapes."));
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 48239a5d99..89d91cc079 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1242,7 +1242,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
tx->set_caret_line(bpoints[bpoints.size() - 1]);
tx->center_viewport_to_caret();
} else {
- for (int i = bpoints.size(); i >= 0; i--) {
+ for (int i = bpoints.size() - 1; i >= 0; i--) {
int bline = bpoints[i];
if (bline < line) {
tx->unfold_line(bline);
diff --git a/main/main.cpp b/main/main.cpp
index fe6df43364..4b035fa511 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -145,6 +145,7 @@ static bool auto_quit = false;
static OS::ProcessID allow_focus_steal_pid = 0;
#ifdef TOOLS_ENABLED
static bool auto_build_solutions = false;
+static String debug_server_uri;
#endif
// Display
@@ -286,6 +287,7 @@ void Main::print_help(const char *p_binary) {
#ifdef TOOLS_ENABLED
OS::get_singleton()->print(" -e, --editor Start the editor instead of running the scene.\n");
OS::get_singleton()->print(" -p, --project-manager Start the project manager, even if a project is auto-detected.\n");
+ OS::get_singleton()->print(" --debug-server <uri> Start the editor debug server (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007)\n");
#endif
OS::get_singleton()->print(" -q, --quit Quit after the first iteration.\n");
OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n");
@@ -875,6 +877,18 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "-p" || I->get() == "--project-manager") { // starts project manager
project_manager = true;
+ } else if (I->get() == "--debug-server") {
+ if (I->next()) {
+ debug_server_uri = I->next()->get();
+ if (debug_server_uri.find("://") == -1) { // wrong address
+ OS::get_singleton()->print("Invalid debug server uri. It should be of the form <protocol>://<bind_address>:<port>.\n");
+ goto error;
+ }
+ N = I->next()->next();
+ } else {
+ OS::get_singleton()->print("Missing remote debug server uri, aborting.\n");
+ goto error;
+ }
} else if (I->get() == "--build-solutions") { // Build the scripting solution such C#
auto_build_solutions = true;
@@ -2347,6 +2361,9 @@ bool Main::start() {
}
}
DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_EDITOR);
+ if (!debug_server_uri.is_empty()) {
+ EditorDebuggerNode::get_singleton()->start(debug_server_uri);
+ }
}
#endif
if (!editor) {
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 64fd7eca8a..bf21c8510a 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -1232,6 +1232,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
+#ifdef DEBUG_ENABLED
+ if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) {
+ err_text = "Trying to cast a freed object.";
+ OPCODE_BREAK;
+ }
+#endif
+
Callable::CallError err;
Variant::construct(to_type, *dst, (const Variant **)&src, 1, err);
@@ -1256,6 +1263,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(!nc);
#ifdef DEBUG_ENABLED
+ if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) {
+ err_text = "Trying to cast a freed object.";
+ OPCODE_BREAK;
+ }
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Invalid cast: can't convert a non-object value to an object type.";
OPCODE_BREAK;
@@ -1284,6 +1295,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(!base_type);
#ifdef DEBUG_ENABLED
+ if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) {
+ err_text = "Trying to cast a freed object.";
+ OPCODE_BREAK;
+ }
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
OPCODE_BREAK;
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index fab950019f..17846eb281 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -43,8 +43,10 @@
#include "core/os/thread.h"
#ifdef TOOLS_ENABLED
+#include "core/os/keyboard.h"
#include "editor/bindings_generator.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/node_dock.h"
#endif
@@ -1353,6 +1355,7 @@ void CSharpLanguage::_editor_init_callback() {
// Enable it as a plugin
EditorNode::add_editor_plugin(godotsharp_editor);
+ ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KEY_MASK_ALT | KEY_B);
godotsharp_editor->enable_plugin();
get_singleton()->godotsharp_editor = godotsharp_editor;
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 73cabf8561..98c6881166 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -418,11 +418,15 @@ namespace GodotTools
AddToolSubmenuItem("C#", _menuPopup);
+ var buildSolutionShortcut = (Shortcut)EditorShortcut("mono/build_solution");
+
_toolBarBuildButton = new Button
{
Text = "Build",
- HintTooltip = "Build solution",
- FocusMode = Control.FocusModeEnum.None
+ HintTooltip = "Build Solution".TTR(),
+ FocusMode = Control.FocusModeEnum.None,
+ Shortcut = buildSolutionShortcut,
+ ShortcutInTooltip = true
};
_toolBarBuildButton.PressedSignal += BuildSolutionPressed;
AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
index 793f84fd77..5c5ced8c29 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
@@ -13,6 +13,9 @@ namespace GodotTools.Internals
public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_EditorDef(setting, defaultValue, restartIfChanged);
+ public static object EditorShortcut(string setting) =>
+ internal_EditorShortcut(setting);
+
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static string TTR(this string text) => internal_TTR(text);
@@ -28,6 +31,9 @@ namespace GodotTools.Internals
private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
[MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern object internal_EditorShortcut(string setting);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_TTR(string text);
}
}
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 6692a6efec..9a61b63c12 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -306,6 +306,12 @@ MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_d
return GDMonoMarshal::variant_to_mono_object(result);
}
+MonoObject *godot_icall_Globals_EditorShortcut(MonoString *p_setting) {
+ String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
+ Ref<Shortcut> result = ED_GET_SHORTCUT(setting);
+ return GDMonoMarshal::variant_to_mono_object(result);
+}
+
MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
String text = GDMonoMarshal::mono_string_to_godot(p_text);
return GDMonoMarshal::mono_string_from_godot(TTR(text));
@@ -380,6 +386,7 @@ void register_editor_internal_calls() {
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorShortcut", godot_icall_Globals_EditorShortcut);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR);
// Utils.OS
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index 6e7b3e7b8d..1fdc8fe1b3 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -55,6 +55,9 @@ if env["builtin_embree"]:
"kernels/bvh/bvh_builder_sah_mb.cpp",
"kernels/bvh/bvh_builder_twolevel.cpp",
"kernels/bvh/bvh_intersector1_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_stream_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_stream_filters.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in embree_src]
diff --git a/modules/raycast/godot_update_embree.py b/modules/raycast/godot_update_embree.py
index 31a25a318f..e31d88b741 100644
--- a/modules/raycast/godot_update_embree.py
+++ b/modules/raycast/godot_update_embree.py
@@ -61,6 +61,11 @@ cpp_files = [
"kernels/bvh/bvh_builder_twolevel.cpp",
"kernels/bvh/bvh_intersector1.cpp",
"kernels/bvh/bvh_intersector1_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_stream_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_stream_filters.cpp",
+ "kernels/bvh/bvh_intersector_hybrid.cpp",
+ "kernels/bvh/bvh_intersector_stream.cpp",
]
os.chdir("../../thirdparty")
@@ -117,7 +122,7 @@ with open(os.path.join(dest_dir, "kernels/config.h"), "w") as config_file:
/* #undef EMBREE_GEOMETRY_INSTANCE */
/* #undef EMBREE_GEOMETRY_GRID */
/* #undef EMBREE_GEOMETRY_POINT */
-/* #undef EMBREE_RAY_PACKETS */
+#define EMBREE_RAY_PACKETS
/* #undef EMBREE_COMPACT_POLYS */
#define EMBREE_CURVE_SELF_INTERSECTION_AVOIDANCE_FACTOR 2.0
@@ -249,3 +254,8 @@ with open(os.path.join(dest_dir, "include/embree3/rtcore_config.h"), "w") as con
os.chdir("..")
shutil.rmtree("embree-tmp")
+
+subprocess.run(["git", "restore", "embree/patches"])
+
+for patch in os.listdir("embree/patches"):
+ subprocess.run(["git", "apply", "embree/patches/" + patch])
diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp
index 2b48e94604..88b2a568ea 100644
--- a/modules/vhacd/register_types.cpp
+++ b/modules/vhacd/register_types.cpp
@@ -32,7 +32,23 @@
#include "scene/resources/mesh.h"
#include "thirdparty/vhacd/public/VHACD.h"
-static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, int p_max_convex_hulls = -1) {
+static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, const Mesh::ConvexDecompositionSettings &p_settings) {
+ VHACD::IVHACD::Parameters params;
+ params.m_concavity = p_settings.max_concavity;
+ params.m_alpha = p_settings.symmetry_planes_clipping_bias;
+ params.m_beta = p_settings.revolution_axes_clipping_bias;
+ params.m_minVolumePerCH = p_settings.min_volume_per_convex_hull;
+ params.m_resolution = p_settings.resolution;
+ params.m_maxNumVerticesPerCH = p_settings.max_num_vertices_per_convex_hull;
+ params.m_planeDownsampling = p_settings.plane_downsampling;
+ params.m_convexhullDownsampling = p_settings.convexhull_downsampling;
+ params.m_pca = p_settings.normalize_mesh;
+ params.m_mode = p_settings.mode;
+ params.m_convexhullApproximation = p_settings.convexhull_approximation;
+ params.m_oclAcceleration = true;
+ params.m_maxConvexHulls = p_settings.max_convex_hulls;
+ params.m_projectHullVertices = p_settings.project_hull_vertices;
+
Vector<real_t> vertices;
vertices.resize(p_faces.size() * 9);
Vector<uint32_t> indices;
@@ -47,11 +63,6 @@ static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, int
}
}
- VHACD::IVHACD::Parameters params;
- if (p_max_convex_hulls > 0) {
- params.m_maxConvexHulls = p_max_convex_hulls;
- }
-
VHACD::IVHACD *decomposer = VHACD::CreateVHACD();
decomposer->Compute(vertices.ptr(), vertices.size() / 3, indices.ptr(), indices.size() / 3, params);
diff --git a/modules/websocket/editor_debugger_server_websocket.cpp b/modules/websocket/editor_debugger_server_websocket.cpp
index 2e61cbfc08..d248433d82 100644
--- a/modules/websocket/editor_debugger_server_websocket.cpp
+++ b/modules/websocket/editor_debugger_server_websocket.cpp
@@ -48,11 +48,19 @@ void EditorDebuggerServerWebSocket::poll() {
server->poll();
}
-Error EditorDebuggerServerWebSocket::start() {
- int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
+Error EditorDebuggerServerWebSocket::start(const String &p_uri) {
+ int bind_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
+ String bind_host = EditorSettings::get_singleton()->get("network/debug/remote_host");
+ if (!p_uri.is_empty() && p_uri != "ws://") {
+ String scheme, path;
+ Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
+ ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
+ }
+ server->set_bind_ip(bind_host);
Vector<String> compatible_protocols;
compatible_protocols.push_back("binary"); // compatibility with EMSCRIPTEN TCP-to-WebSocket layer.
- return server->listen(remote_port, compatible_protocols);
+ return server->listen(bind_port, compatible_protocols);
}
void EditorDebuggerServerWebSocket::stop() {
diff --git a/modules/websocket/editor_debugger_server_websocket.h b/modules/websocket/editor_debugger_server_websocket.h
index d9543bb647..14ab0109b2 100644
--- a/modules/websocket/editor_debugger_server_websocket.h
+++ b/modules/websocket/editor_debugger_server_websocket.h
@@ -48,7 +48,7 @@ public:
void _peer_disconnected(int p_peer, bool p_was_clean);
void poll() override;
- Error start() override;
+ Error start(const String &p_uri) override;
void stop() override;
bool is_active() const override;
bool is_connection_available() const override;
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index de6925244a..7e7db57af3 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -274,7 +274,8 @@ Node *MeshInstance3D::create_multiple_convex_collisions_node() {
return nullptr;
}
- Vector<Ref<Shape3D>> shapes = mesh->convex_decompose();
+ Mesh::ConvexDecompositionSettings settings;
+ Vector<Ref<Shape3D>> shapes = mesh->convex_decompose(settings);
if (!shapes.size()) {
return nullptr;
}
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index a891566633..466f67afb8 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -542,7 +542,7 @@ Transform3D SkeletonIK3D::_get_target_transform() {
target_node_override = Object::cast_to<Node3D>(get_node(target_node_path_override));
}
- if (target_node_override) {
+ if (target_node_override && target_node_override->is_inside_tree()) {
return target_node_override->get_global_transform();
} else {
return target;
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 3beff57027..e7769f9372 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -2034,7 +2034,9 @@ String CodeEdit::get_text_for_symbol_lookup() {
void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) {
symbol_lookup_word = p_valid ? symbol_lookup_new_word : "";
symbol_lookup_new_word = "";
- _set_symbol_lookup_word(symbol_lookup_word);
+ if (lookup_symbol_word != symbol_lookup_word) {
+ _set_symbol_lookup_word(symbol_lookup_word);
+ }
}
void CodeEdit::_bind_methods() {
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index aeadfd78ee..562bac60c2 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -366,7 +366,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
if (p_line > 0) {
- l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y;
+ l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
} else {
l.offset.y = 0;
}
@@ -614,7 +614,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
*r_char_offset = l.char_offset + l.char_count;
if (p_line > 0) {
- l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y;
+ l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
} else {
l.offset.y = 0;
}
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 71d0c69d55..1edc3ff38b 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -224,7 +224,9 @@ Vector<Face3> Mesh::get_faces() const {
Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const {
if (p_simplify) {
- Vector<Ref<Shape3D>> decomposed = convex_decompose(1);
+ ConvexDecompositionSettings settings;
+ settings.max_convex_hulls = 1;
+ Vector<Ref<Shape3D>> decomposed = convex_decompose(settings);
if (decomposed.size() == 1) {
return decomposed[0];
} else {
@@ -565,12 +567,12 @@ void Mesh::clear_cache() const {
debug_lines.clear();
}
-Vector<Ref<Shape3D>> Mesh::convex_decompose(int p_max_convex_hulls) const {
+Vector<Ref<Shape3D>> Mesh::convex_decompose(const ConvexDecompositionSettings &p_settings) const {
ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape3D>>());
const Vector<Face3> faces = get_faces();
- Vector<Vector<Face3>> decomposed = convex_composition_function(faces, p_max_convex_hulls);
+ const Vector<Vector<Face3>> decomposed = convex_composition_function(faces, p_settings);
Vector<Ref<Shape3D>> ret;
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index aa4ed1cb13..4d0ee0f247 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -160,11 +160,42 @@ public:
Size2i get_lightmap_size_hint() const;
void clear_cache() const;
- typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &p_faces, int p_max_convex_hulls);
+ struct ConvexDecompositionSettings {
+ enum Mode : int {
+ CONVEX_DECOMPOSITION_MODE_VOXEL = 0,
+ CONVEX_DECOMPOSITION_MODE_TETRAHEDRON
+ };
+
+ /// Maximum concavity. [Range: 0.0 -> 1.0]
+ real_t max_concavity = 1.0;
+ /// Controls the bias toward clipping along symmetry planes. [Range: 0.0 -> 1.0]
+ real_t symmetry_planes_clipping_bias = 0.05;
+ /// Controls the bias toward clipping along revolution axes. [Range: 0.0 -> 1.0]
+ real_t revolution_axes_clipping_bias = 0.05;
+ real_t min_volume_per_convex_hull = 0.0001;
+ /// Maximum number of voxels generated during the voxelization stage.
+ uint32_t resolution = 10'000;
+ uint32_t max_num_vertices_per_convex_hull = 32;
+ /// Controls the granularity of the search for the "best" clipping plane.
+ /// [Range: 1 -> 16]
+ uint32_t plane_downsampling = 4;
+ /// Controls the precision of the convex-hull generation process during the
+ /// clipping plane selection stage.
+ /// [Range: 1 -> 16]
+ uint32_t convexhull_downsampling = 4;
+ /// enable/disable normalizing the mesh before applying the convex decomposition.
+ bool normalize_mesh = false;
+ Mode mode = CONVEX_DECOMPOSITION_MODE_VOXEL;
+ bool convexhull_approximation = true;
+ /// This is the maximum number of convex hulls to produce from the merge operation.
+ uint32_t max_convex_hulls = 1;
+ bool project_hull_vertices = true;
+ };
+ typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &p_faces, const ConvexDecompositionSettings &p_settings);
static ConvexDecompositionFunc convex_composition_function;
- Vector<Ref<Shape3D>> convex_decompose(int p_max_convex_hulls = -1) const;
+ Vector<Ref<Shape3D>> convex_decompose(const ConvexDecompositionSettings &p_settings) const;
virtual int get_builtin_bind_pose_count() const;
virtual Transform3D get_builtin_bind_pose(int p_index) const;
diff --git a/thirdparty/embree/include/embree3/rtcore_config.h b/thirdparty/embree/include/embree3/rtcore_config.h
index 3a9819c9f1..62b7b6f4dc 100644
--- a/thirdparty/embree/include/embree3/rtcore_config.h
+++ b/thirdparty/embree/include/embree3/rtcore_config.h
@@ -6,9 +6,9 @@
#define RTC_VERSION_MAJOR 3
#define RTC_VERSION_MINOR 13
-#define RTC_VERSION_PATCH 0
-#define RTC_VERSION 31300
-#define RTC_VERSION_STRING "3.13.0"
+#define RTC_VERSION_PATCH 1
+#define RTC_VERSION 31301
+#define RTC_VERSION_STRING "3.13.1"
#define RTC_MAX_INSTANCE_LEVEL_COUNT 1
diff --git a/thirdparty/embree/kernels/bvh/bvh_intersector_hybrid.cpp b/thirdparty/embree/kernels/bvh/bvh_intersector_hybrid.cpp
new file mode 100644
index 0000000000..6e9a5a538e
--- /dev/null
+++ b/thirdparty/embree/kernels/bvh/bvh_intersector_hybrid.cpp
@@ -0,0 +1,917 @@
+// Copyright 2009-2021 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#include "bvh_intersector_hybrid.h"
+#include "bvh_traverser1.h"
+#include "node_intersector1.h"
+#include "node_intersector_packet.h"
+
+#include "../geometry/intersector_iterators.h"
+#include "../geometry/triangle_intersector.h"
+#include "../geometry/trianglev_intersector.h"
+#include "../geometry/trianglev_mb_intersector.h"
+#include "../geometry/trianglei_intersector.h"
+#include "../geometry/quadv_intersector.h"
+#include "../geometry/quadi_intersector.h"
+#include "../geometry/curveNv_intersector.h"
+#include "../geometry/curveNi_intersector.h"
+#include "../geometry/curveNi_mb_intersector.h"
+#include "../geometry/linei_intersector.h"
+#include "../geometry/subdivpatch1_intersector.h"
+#include "../geometry/object_intersector.h"
+#include "../geometry/instance_intersector.h"
+#include "../geometry/subgrid_intersector.h"
+#include "../geometry/subgrid_mb_intersector.h"
+#include "../geometry/curve_intersector_virtual.h"
+
+#define SWITCH_DURING_DOWN_TRAVERSAL 1
+#define FORCE_SINGLE_MODE 0
+
+#define ENABLE_FAST_COHERENT_CODEPATHS 1
+
+namespace embree
+{
+ namespace isa
+ {
+ template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
+ void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::intersect1(Accel::Intersectors* This,
+ const BVH* bvh,
+ NodeRef root,
+ size_t k,
+ Precalculations& pre,
+ RayHitK<K>& ray,
+ const TravRayK<K, robust>& tray,
+ IntersectContext* context)
+ {
+ /* stack state */
+ StackItemT<NodeRef> stack[stackSizeSingle]; // stack of nodes
+ StackItemT<NodeRef>* stackPtr = stack + 1; // current stack pointer
+ StackItemT<NodeRef>* stackEnd = stack + stackSizeSingle;
+ stack[0].ptr = root;
+ stack[0].dist = neg_inf;
+
+ /* load the ray into SIMD registers */
+ TravRay<N,robust> tray1;
+ tray1.template init<K>(k, tray.org, tray.dir, tray.rdir, tray.nearXYZ, tray.tnear[k], tray.tfar[k]);
+
+ /* pop loop */
+ while (true) pop:
+ {
+ /* pop next node */
+ if (unlikely(stackPtr == stack)) break;
+ stackPtr--;
+ NodeRef cur = NodeRef(stackPtr->ptr);
+
+ /* if popped node is too far, pop next one */
+ if (unlikely(*(float*)&stackPtr->dist > ray.tfar[k]))
+ continue;
+
+ /* downtraversal loop */
+ while (true)
+ {
+ /* intersect node */
+ size_t mask; vfloat<N> tNear;
+ STAT3(normal.trav_nodes, 1, 1, 1);
+ bool nodeIntersected = BVHNNodeIntersector1<N, types, robust>::intersect(cur, tray1, ray.time()[k], tNear, mask);
+ if (unlikely(!nodeIntersected)) { STAT3(normal.trav_nodes,-1,-1,-1); break; }
+
+ /* if no child is hit, pop next node */
+ if (unlikely(mask == 0))
+ goto pop;
+
+ /* select next child and push other children */
+ BVHNNodeTraverser1Hit<N, types>::traverseClosestHit(cur, mask, tNear, stackPtr, stackEnd);
+ }
+
+ /* this is a leaf node */
+ assert(cur != BVH::emptyNode);
+ STAT3(normal.trav_leaves, 1, 1, 1);
+ size_t num; Primitive* prim = (Primitive*)cur.leaf(num);
+
+ size_t lazy_node = 0;
+ PrimitiveIntersectorK::intersect(This, pre, ray, k, context, prim, num, tray1, lazy_node);
+
+ tray1.tfar = ray.tfar[k];
+
+ if (unlikely(lazy_node)) {
+ stackPtr->ptr = lazy_node;
+ stackPtr->dist = neg_inf;
+ stackPtr++;
+ }
+ }
+ }
+
+ template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
+ void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::intersect(vint<K>* __restrict__ valid_i,
+ Accel::Intersectors* __restrict__ This,
+ RayHitK<K>& __restrict__ ray,
+ IntersectContext* __restrict__ context)
+ {
+ BVH* __restrict__ bvh = (BVH*)This->ptr;
+
+ /* we may traverse an empty BVH in case all geometry was invalid */
+ if (bvh->root == BVH::emptyNode)
+ return;
+
+#if ENABLE_FAST_COHERENT_CODEPATHS == 1
+ assert(context);
+ if (unlikely(types == BVH_AN1 && context->user && context->isCoherent()))
+ {
+ intersectCoherent(valid_i, This, ray, context);
+ return;
+ }
+#endif
+
+ /* filter out invalid rays */
+ vbool<K> valid = *valid_i == -1;
+#if defined(EMBREE_IGNORE_INVALID_RAYS)
+ valid &= ray.valid();
+#endif
+
+ /* return if there are no valid rays */
+ size_t valid_bits = movemask(valid);
+
+#if defined(__AVX__)
+ STAT3(normal.trav_hit_boxes[popcnt(movemask(valid))], 1, 1, 1);
+#endif
+
+ if (unlikely(valid_bits == 0)) return;
+
+ /* verify correct input */
+ assert(all(valid, ray.valid()));
+ assert(all(valid, ray.tnear() >= 0.0f));
+ assert(!(types & BVH_MB) || all(valid, (ray.time() >= 0.0f) & (ray.time() <= 1.0f)));
+ Precalculations pre(valid, ray);
+
+ /* load ray */
+ TravRayK<K, robust> tray(ray.org, ray.dir, single ? N : 0);
+ const vfloat<K> org_ray_tnear = max(ray.tnear(), 0.0f);
+ const vfloat<K> org_ray_tfar = max(ray.tfar , 0.0f);
+
+ if (single)
+ {
+ tray.tnear = select(valid, org_ray_tnear, vfloat<K>(pos_inf));
+ tray.tfar = select(valid, org_ray_tfar , vfloat<K>(neg_inf));
+
+ for (; valid_bits!=0; ) {
+ const size_t i = bscf(valid_bits);
+ intersect1(This, bvh, bvh->root, i, pre, ray, tray, context);
+ }
+ return;
+ }
+
+ /* determine switch threshold based on flags */
+ const size_t switchThreshold = (context->user && context->isCoherent()) ? 2 : switchThresholdIncoherent;
+
+ vint<K> octant = ray.octant();
+ octant = select(valid, octant, vint<K>(0xffffffff));
+
+ /* test whether we have ray with opposing direction signs in the packet */
+ bool split = false;
+ {
+ size_t bits = valid_bits;
+ vbool<K> vsplit( false );
+ do
+ {
+ const size_t valid_index = bsf(bits);
+ vbool<K> octant_valid = octant[valid_index] == octant;
+ bits &= ~(size_t)movemask(octant_valid);
+ vsplit |= vint<K>(octant[valid_index]) == (octant^vint<K>(0x7));
+ } while (bits);
+ if (any(vsplit)) split = true;
+ }
+
+ do
+ {
+ const size_t valid_index = bsf(valid_bits);
+ const vint<K> diff_octant = vint<K>(octant[valid_index])^octant;
+ const vint<K> count_diff_octant = \
+ ((diff_octant >> 2) & 1) +
+ ((diff_octant >> 1) & 1) +
+ ((diff_octant >> 0) & 1);
+
+ vbool<K> octant_valid = (count_diff_octant <= 1) & (octant != vint<K>(0xffffffff));
+ if (!single || !split) octant_valid = valid; // deactivate octant sorting in pure chunk mode, otherwise instance traversal performance goes down
+
+
+ octant = select(octant_valid,vint<K>(0xffffffff),octant);
+ valid_bits &= ~(size_t)movemask(octant_valid);
+
+ tray.tnear = select(octant_valid, org_ray_tnear, vfloat<K>(pos_inf));
+ tray.tfar = select(octant_valid, org_ray_tfar , vfloat<K>(neg_inf));
+
+ /* allocate stack and push root node */
+ vfloat<K> stack_near[stackSizeChunk];
+ NodeRef stack_node[stackSizeChunk];
+ stack_node[0] = BVH::invalidNode;
+ stack_near[0] = inf;
+ stack_node[1] = bvh->root;
+ stack_near[1] = tray.tnear;
+ NodeRef* stackEnd MAYBE_UNUSED = stack_node+stackSizeChunk;
+ NodeRef* __restrict__ sptr_node = stack_node + 2;
+ vfloat<K>* __restrict__ sptr_near = stack_near + 2;
+
+ while (1) pop:
+ {
+ /* pop next node from stack */
+ assert(sptr_node > stack_node);
+ sptr_node--;
+ sptr_near--;
+ NodeRef cur = *sptr_node;
+ if (unlikely(cur == BVH::invalidNode)) {
+ assert(sptr_node == stack_node);
+ break;
+ }
+
+ /* cull node if behind closest hit point */
+ vfloat<K> curDist = *sptr_near;
+ const vbool<K> active = curDist < tray.tfar;
+ if (unlikely(none(active)))
+ continue;
+
+ /* switch to single ray traversal */
+#if (!defined(__WIN32__) || defined(__X86_64__)) && defined(__SSE4_2__)
+#if FORCE_SINGLE_MODE == 0
+ if (single)
+#endif
+ {
+ size_t bits = movemask(active);
+#if FORCE_SINGLE_MODE == 0
+ if (unlikely(popcnt(bits) <= switchThreshold))
+#endif
+ {
+ for (; bits!=0; ) {
+ const size_t i = bscf(bits);
+ intersect1(This, bvh, cur, i, pre, ray, tray, context);
+ }
+ tray.tfar = min(tray.tfar, ray.tfar);
+ continue;
+ }
+ }
+#endif
+ while (likely(!cur.isLeaf()))
+ {
+ /* process nodes */
+ const vbool<K> valid_node = tray.tfar > curDist;
+ STAT3(normal.trav_nodes, 1, popcnt(valid_node), K);
+ const NodeRef nodeRef = cur;
+ const BaseNode* __restrict__ const node = nodeRef.baseNode();
+
+ /* set cur to invalid */
+ cur = BVH::emptyNode;
+ curDist = pos_inf;
+
+ size_t num_child_hits = 0;
+
+ for (unsigned i = 0; i < N; i++)
+ {
+ const NodeRef child = node->children[i];
+ if (unlikely(child == BVH::emptyNode)) break;
+ vfloat<K> lnearP;
+ vbool<K> lhit = valid_node;
+ BVHNNodeIntersectorK<N, K, types, robust>::intersect(nodeRef, i, tray, ray.time(), lnearP, lhit);
+
+ /* if we hit the child we choose to continue with that child if it
+ is closer than the current next child, or we push it onto the stack */
+ if (likely(any(lhit)))
+ {
+ assert(sptr_node < stackEnd);
+ assert(child != BVH::emptyNode);
+ const vfloat<K> childDist = select(lhit, lnearP, inf);
+ /* push cur node onto stack and continue with hit child */
+ if (any(childDist < curDist))
+ {
+ if (likely(cur != BVH::emptyNode)) {
+ num_child_hits++;
+ *sptr_node = cur; sptr_node++;
+ *sptr_near = curDist; sptr_near++;
+ }
+ curDist = childDist;
+ cur = child;
+ }
+
+ /* push hit child onto stack */
+ else {
+ num_child_hits++;
+ *sptr_node = child; sptr_node++;
+ *sptr_near = childDist; sptr_near++;
+ }
+ }
+ }
+
+#if defined(__AVX__)
+ //STAT3(normal.trav_hit_boxes[num_child_hits], 1, 1, 1);
+#endif
+
+ if (unlikely(cur == BVH::emptyNode))
+ goto pop;
+
+ /* improved distance sorting for 3 or more hits */
+ if (unlikely(num_child_hits >= 2))
+ {
+ if (any(sptr_near[-2] < sptr_near[-1]))
+ {
+ std::swap(sptr_near[-2],sptr_near[-1]);
+ std::swap(sptr_node[-2],sptr_node[-1]);
+ }
+ if (unlikely(num_child_hits >= 3))
+ {
+ if (any(sptr_near[-3] < sptr_near[-1]))
+ {
+ std::swap(sptr_near[-3],sptr_near[-1]);
+ std::swap(sptr_node[-3],sptr_node[-1]);
+ }
+ if (any(sptr_near[-3] < sptr_near[-2]))
+ {
+ std::swap(sptr_near[-3],sptr_near[-2]);
+ std::swap(sptr_node[-3],sptr_node[-2]);
+ }
+ }
+ }
+
+#if SWITCH_DURING_DOWN_TRAVERSAL == 1
+ if (single)
+ {
+ // seems to be the best place for testing utilization
+ if (unlikely(popcnt(tray.tfar > curDist) <= switchThreshold))
+ {
+ *sptr_node++ = cur;
+ *sptr_near++ = curDist;
+ goto pop;
+ }
+ }
+#endif
+ }
+
+ /* return if stack is empty */
+ if (unlikely(cur == BVH::invalidNode)) {
+ assert(sptr_node == stack_node);
+ break;
+ }
+
+ /* intersect leaf */
+ assert(cur != BVH::emptyNode);
+ const vbool<K> valid_leaf = tray.tfar > curDist;
+ STAT3(normal.trav_leaves, 1, popcnt(valid_leaf), K);
+ if (unlikely(none(valid_leaf))) continue;
+ size_t items; const Primitive* prim = (Primitive*)cur.leaf(items);
+
+ size_t lazy_node = 0;
+ PrimitiveIntersectorK::intersect(valid_leaf, This, pre, ray, context, prim, items, tray, lazy_node);
+ tray.tfar = select(valid_leaf, ray.tfar, tray.tfar);
+
+ if (unlikely(lazy_node)) {
+ *sptr_node = lazy_node; sptr_node++;
+ *sptr_near = neg_inf; sptr_near++;
+ }
+ }
+ } while(valid_bits);
+ }
+
+
+ template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
+ void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::intersectCoherent(vint<K>* __restrict__ valid_i,
+ Accel::Intersectors* __restrict__ This,
+ RayHitK<K>& __restrict__ ray,
+ IntersectContext* context)
+ {
+ BVH* __restrict__ bvh = (BVH*)This->ptr;
+
+ /* filter out invalid rays */
+ vbool<K> valid = *valid_i == -1;
+#if defined(EMBREE_IGNORE_INVALID_RAYS)
+ valid &= ray.valid();
+#endif
+
+ /* return if there are no valid rays */
+ size_t valid_bits = movemask(valid);
+ if (unlikely(valid_bits == 0)) return;
+
+ /* verify correct input */
+ assert(all(valid, ray.valid()));
+ assert(all(valid, ray.tnear() >= 0.0f));
+ assert(!(types & BVH_MB) || all(valid, (ray.time() >= 0.0f) & (ray.time() <= 1.0f)));
+ Precalculations pre(valid, ray);
+
+ /* load ray */
+ TravRayK<K, robust> tray(ray.org, ray.dir, single ? N : 0);
+ const vfloat<K> org_ray_tnear = max(ray.tnear(), 0.0f);
+ const vfloat<K> org_ray_tfar = max(ray.tfar , 0.0f);
+
+ vint<K> octant = ray.octant();
+ octant = select(valid, octant, vint<K>(0xffffffff));
+
+ do
+ {
+ const size_t valid_index = bsf(valid_bits);
+ const vbool<K> octant_valid = octant[valid_index] == octant;
+ valid_bits &= ~(size_t)movemask(octant_valid);
+
+ tray.tnear = select(octant_valid, org_ray_tnear, vfloat<K>(pos_inf));
+ tray.tfar = select(octant_valid, org_ray_tfar , vfloat<K>(neg_inf));
+
+ Frustum<robust> frustum;
+ frustum.template init<K>(octant_valid, tray.org, tray.rdir, tray.tnear, tray.tfar, N);
+
+ StackItemT<NodeRef> stack[stackSizeSingle]; // stack of nodes
+ StackItemT<NodeRef>* stackPtr = stack + 1; // current stack pointer
+ stack[0].ptr = bvh->root;
+ stack[0].dist = neg_inf;
+
+ while (1) pop:
+ {
+ /* pop next node from stack */
+ if (unlikely(stackPtr == stack)) break;
+
+ stackPtr--;
+ NodeRef cur = NodeRef(stackPtr->ptr);
+
+ /* cull node if behind closest hit point */
+ vfloat<K> curDist = *(float*)&stackPtr->dist;
+ const vbool<K> active = curDist < tray.tfar;
+ if (unlikely(none(active))) continue;
+
+ while (likely(!cur.isLeaf()))
+ {
+ /* process nodes */
+ //STAT3(normal.trav_nodes, 1, popcnt(valid_node), K);
+ const NodeRef nodeRef = cur;
+ const AABBNode* __restrict__ const node = nodeRef.getAABBNode();
+
+ vfloat<N> fmin;
+ size_t m_frustum_node = intersectNodeFrustum<N>(node, frustum, fmin);
+
+ if (unlikely(!m_frustum_node)) goto pop;
+ cur = BVH::emptyNode;
+ curDist = pos_inf;
+
+#if defined(__AVX__)
+ //STAT3(normal.trav_hit_boxes[popcnt(m_frustum_node)], 1, 1, 1);
+#endif
+ size_t num_child_hits = 0;
+ do {
+ const size_t i = bscf(m_frustum_node);
+ vfloat<K> lnearP;
+ vbool<K> lhit = false; // motion blur is not supported, so the initial value will be ignored
+ STAT3(normal.trav_nodes, 1, 1, 1);
+ BVHNNodeIntersectorK<N, K, types, robust>::intersect(nodeRef, i, tray, ray.time(), lnearP, lhit);
+
+ if (likely(any(lhit)))
+ {
+ const vfloat<K> childDist = fmin[i];
+ const NodeRef child = node->child(i);
+ BVHN<N>::prefetch(child);
+ if (any(childDist < curDist))
+ {
+ if (likely(cur != BVH::emptyNode)) {
+ num_child_hits++;
+ stackPtr->ptr = cur;
+ *(float*)&stackPtr->dist = toScalar(curDist);
+ stackPtr++;
+ }
+ curDist = childDist;
+ cur = child;
+ }
+ /* push hit child onto stack */
+ else {
+ num_child_hits++;
+ stackPtr->ptr = child;
+ *(float*)&stackPtr->dist = toScalar(childDist);
+ stackPtr++;
+ }
+ }
+ } while(m_frustum_node);
+
+ if (unlikely(cur == BVH::emptyNode)) goto pop;
+
+ /* improved distance sorting for 3 or more hits */
+ if (unlikely(num_child_hits >= 2))
+ {
+ if (stackPtr[-2].dist < stackPtr[-1].dist)
+ std::swap(stackPtr[-2],stackPtr[-1]);
+ if (unlikely(num_child_hits >= 3))
+ {
+ if (stackPtr[-3].dist < stackPtr[-1].dist)
+ std::swap(stackPtr[-3],stackPtr[-1]);
+ if (stackPtr[-3].dist < stackPtr[-2].dist)
+ std::swap(stackPtr[-3],stackPtr[-2]);
+ }
+ }
+ }
+
+ /* intersect leaf */
+ assert(cur != BVH::invalidNode);
+ assert(cur != BVH::emptyNode);
+ const vbool<K> valid_leaf = tray.tfar > curDist;
+ STAT3(normal.trav_leaves, 1, popcnt(valid_leaf), K);
+ if (unlikely(none(valid_leaf))) continue;
+ size_t items; const Primitive* prim = (Primitive*)cur.leaf(items);
+
+ size_t lazy_node = 0;
+ PrimitiveIntersectorK::intersect(valid_leaf, This, pre, ray, context, prim, items, tray, lazy_node);
+
+ /* reduce max distance interval on successful intersection */
+ if (likely(any((ray.tfar < tray.tfar) & valid_leaf)))
+ {
+ tray.tfar = select(valid_leaf, ray.tfar, tray.tfar);
+ frustum.template updateMaxDist<K>(tray.tfar);
+ }
+
+ if (unlikely(lazy_node)) {
+ stackPtr->ptr = lazy_node;
+ stackPtr->dist = neg_inf;
+ stackPtr++;
+ }
+ }
+
+ } while(valid_bits);
+ }
+
+ // ===================================================================================================================================================================
+ // ===================================================================================================================================================================
+ // ===================================================================================================================================================================
+
+ template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
+ bool BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::occluded1(Accel::Intersectors* This,
+ const BVH* bvh,
+ NodeRef root,
+ size_t k,
+ Precalculations& pre,
+ RayK<K>& ray,
+ const TravRayK<K, robust>& tray,
+ IntersectContext* context)
+ {
+ /* stack state */
+ NodeRef stack[stackSizeSingle]; // stack of nodes that still need to get traversed
+ NodeRef* stackPtr = stack+1; // current stack pointer
+ NodeRef* stackEnd = stack+stackSizeSingle;
+ stack[0] = root;
+
+ /* load the ray into SIMD registers */
+ TravRay<N,robust> tray1;
+ tray1.template init<K>(k, tray.org, tray.dir, tray.rdir, tray.nearXYZ, tray.tnear[k], tray.tfar[k]);
+
+ /* pop loop */
+ while (true) pop:
+ {
+ /* pop next node */
+ if (unlikely(stackPtr == stack)) break;
+ stackPtr--;
+ NodeRef cur = (NodeRef)*stackPtr;
+
+ /* downtraversal loop */
+ while (true)
+ {
+ /* intersect node */
+ size_t mask; vfloat<N> tNear;
+ STAT3(shadow.trav_nodes, 1, 1, 1);
+ bool nodeIntersected = BVHNNodeIntersector1<N, types, robust>::intersect(cur, tray1, ray.time()[k], tNear, mask);
+ if (unlikely(!nodeIntersected)) { STAT3(shadow.trav_nodes,-1,-1,-1); break; }
+
+ /* if no child is hit, pop next node */
+ if (unlikely(mask == 0))
+ goto pop;
+
+ /* select next child and push other children */
+ BVHNNodeTraverser1Hit<N, types>::traverseAnyHit(cur, mask, tNear, stackPtr, stackEnd);
+ }
+
+ /* this is a leaf node */
+ assert(cur != BVH::emptyNode);
+ STAT3(shadow.trav_leaves, 1, 1, 1);
+ size_t num; Primitive* prim = (Primitive*)cur.leaf(num);
+
+ size_t lazy_node = 0;
+ if (PrimitiveIntersectorK::occluded(This, pre, ray, k, context, prim, num, tray1, lazy_node)) {
+ ray.tfar[k] = neg_inf;
+ return true;
+ }
+
+ if (unlikely(lazy_node)) {
+ *stackPtr = lazy_node;
+ stackPtr++;
+ }
+ }
+ return false;
+ }
+
+ template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
+ void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::occluded(vint<K>* __restrict__ valid_i,
+ Accel::Intersectors* __restrict__ This,
+ RayK<K>& __restrict__ ray,
+ IntersectContext* context)
+ {
+ BVH* __restrict__ bvh = (BVH*)This->ptr;
+
+ /* we may traverse an empty BVH in case all geometry was invalid */
+ if (bvh->root == BVH::emptyNode)
+ return;
+
+#if ENABLE_FAST_COHERENT_CODEPATHS == 1
+ assert(context);
+ if (unlikely(types == BVH_AN1 && context->user && context->isCoherent()))
+ {
+ occludedCoherent(valid_i, This, ray, context);
+ return;
+ }
+#endif
+
+ /* filter out already occluded and invalid rays */
+ vbool<K> valid = (*valid_i == -1) & (ray.tfar >= 0.0f);
+#if defined(EMBREE_IGNORE_INVALID_RAYS)
+ valid &= ray.valid();
+#endif
+
+ /* return if there are no valid rays */
+ const size_t valid_bits = movemask(valid);
+ if (unlikely(valid_bits == 0)) return;
+
+ /* verify correct input */
+ assert(all(valid, ray.valid()));
+ assert(all(valid, ray.tnear() >= 0.0f));
+ assert(!(types & BVH_MB) || all(valid, (ray.time() >= 0.0f) & (ray.time() <= 1.0f)));
+ Precalculations pre(valid, ray);
+
+ /* load ray */
+ TravRayK<K, robust> tray(ray.org, ray.dir, single ? N : 0);
+ const vfloat<K> org_ray_tnear = max(ray.tnear(), 0.0f);
+ const vfloat<K> org_ray_tfar = max(ray.tfar , 0.0f);
+
+ tray.tnear = select(valid, org_ray_tnear, vfloat<K>(pos_inf));
+ tray.tfar = select(valid, org_ray_tfar , vfloat<K>(neg_inf));
+
+ vbool<K> terminated = !valid;
+ const vfloat<K> inf = vfloat<K>(pos_inf);
+
+ /* determine switch threshold based on flags */
+ const size_t switchThreshold = (context->user && context->isCoherent()) ? 2 : switchThresholdIncoherent;
+
+ /* allocate stack and push root node */
+ vfloat<K> stack_near[stackSizeChunk];
+ NodeRef stack_node[stackSizeChunk];
+ stack_node[0] = BVH::invalidNode;
+ stack_near[0] = inf;
+ stack_node[1] = bvh->root;
+ stack_near[1] = tray.tnear;
+ NodeRef* stackEnd MAYBE_UNUSED = stack_node+stackSizeChunk;
+ NodeRef* __restrict__ sptr_node = stack_node + 2;
+ vfloat<K>* __restrict__ sptr_near = stack_near + 2;
+
+ while (1) pop:
+ {
+ /* pop next node from stack */
+ assert(sptr_node > stack_node);
+ sptr_node--;
+ sptr_near--;
+ NodeRef cur = *sptr_node;
+ if (unlikely(cur == BVH::invalidNode)) {
+ assert(sptr_node == stack_node);
+ break;
+ }
+
+ /* cull node if behind closest hit point */
+ vfloat<K> curDist = *sptr_near;
+ const vbool<K> active = curDist < tray.tfar;
+ if (unlikely(none(active)))
+ continue;
+
+ /* switch to single ray traversal */
+#if (!defined(__WIN32__) || defined(__X86_64__)) && defined(__SSE4_2__)
+#if FORCE_SINGLE_MODE == 0
+ if (single)
+#endif
+ {
+ size_t bits = movemask(active);
+#if FORCE_SINGLE_MODE == 0
+ if (unlikely(popcnt(bits) <= switchThreshold))
+#endif
+ {
+ for (; bits!=0; ) {
+ const size_t i = bscf(bits);
+ if (occluded1(This, bvh, cur, i, pre, ray, tray, context))
+ set(terminated, i);
+ }
+ if (all(terminated)) break;
+ tray.tfar = select(terminated, vfloat<K>(neg_inf), tray.tfar);
+ continue;
+ }
+ }
+#endif
+
+ while (likely(!cur.isLeaf()))
+ {
+ /* process nodes */
+ const vbool<K> valid_node = tray.tfar > curDist;
+ STAT3(shadow.trav_nodes, 1, popcnt(valid_node), K);
+ const NodeRef nodeRef = cur;
+ const BaseNode* __restrict__ const node = nodeRef.baseNode();
+
+ /* set cur to invalid */
+ cur = BVH::emptyNode;
+ curDist = pos_inf;
+
+ for (unsigned i = 0; i < N; i++)
+ {
+ const NodeRef child = node->children[i];
+ if (unlikely(child == BVH::emptyNode)) break;
+ vfloat<K> lnearP;
+ vbool<K> lhit = valid_node;
+ BVHNNodeIntersectorK<N, K, types, robust>::intersect(nodeRef, i, tray, ray.time(), lnearP, lhit);
+
+ /* if we hit the child we push the previously hit node onto the stack, and continue with the currently hit child */
+ if (likely(any(lhit)))
+ {
+ assert(sptr_node < stackEnd);
+ assert(child != BVH::emptyNode);
+ const vfloat<K> childDist = select(lhit, lnearP, inf);
+
+ /* push 'cur' node onto stack and continue with hit child */
+ if (likely(cur != BVH::emptyNode)) {
+ *sptr_node = cur; sptr_node++;
+ *sptr_near = curDist; sptr_near++;
+ }
+ curDist = childDist;
+ cur = child;
+ }
+ }
+ if (unlikely(cur == BVH::emptyNode))
+ goto pop;
+
+#if SWITCH_DURING_DOWN_TRAVERSAL == 1
+ if (single)
+ {
+ // seems to be the best place for testing utilization
+ if (unlikely(popcnt(tray.tfar > curDist) <= switchThreshold))
+ {
+ *sptr_node++ = cur;
+ *sptr_near++ = curDist;
+ goto pop;
+ }
+ }
+#endif
+ }
+
+ /* return if stack is empty */
+ if (unlikely(cur == BVH::invalidNode)) {
+ assert(sptr_node == stack_node);
+ break;
+ }
+
+
+ /* intersect leaf */
+ assert(cur != BVH::emptyNode);
+ const vbool<K> valid_leaf = tray.tfar > curDist;
+ STAT3(shadow.trav_leaves, 1, popcnt(valid_leaf), K);
+ if (unlikely(none(valid_leaf))) continue;
+ size_t items; const Primitive* prim = (Primitive*) cur.leaf(items);
+
+ size_t lazy_node = 0;
+ terminated |= PrimitiveIntersectorK::occluded(!terminated, This, pre, ray, context, prim, items, tray, lazy_node);
+ if (all(terminated)) break;
+ tray.tfar = select(terminated, vfloat<K>(neg_inf), tray.tfar); // ignore node intersections for terminated rays
+
+ if (unlikely(lazy_node)) {
+ *sptr_node = lazy_node; sptr_node++;
+ *sptr_near = neg_inf; sptr_near++;
+ }
+ }
+
+ vfloat<K>::store(valid & terminated, &ray.tfar, neg_inf);
+ }
+
+
+ template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
+ void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::occludedCoherent(vint<K>* __restrict__ valid_i,
+ Accel::Intersectors* __restrict__ This,
+ RayK<K>& __restrict__ ray,
+ IntersectContext* context)
+ {
+ BVH* __restrict__ bvh = (BVH*)This->ptr;
+
+ /* filter out invalid rays */
+ vbool<K> valid = *valid_i == -1;
+#if defined(EMBREE_IGNORE_INVALID_RAYS)
+ valid &= ray.valid();
+#endif
+
+ /* return if there are no valid rays */
+ size_t valid_bits = movemask(valid);
+ if (unlikely(valid_bits == 0)) return;
+
+ /* verify correct input */
+ assert(all(valid, ray.valid()));
+ assert(all(valid, ray.tnear() >= 0.0f));
+ assert(!(types & BVH_MB) || all(valid, (ray.time() >= 0.0f) & (ray.time() <= 1.0f)));
+ Precalculations pre(valid,ray);
+
+ /* load ray */
+ TravRayK<K, robust> tray(ray.org, ray.dir, single ? N : 0);
+ const vfloat<K> org_ray_tnear = max(ray.tnear(), 0.0f);
+ const vfloat<K> org_ray_tfar = max(ray.tfar , 0.0f);
+
+ vbool<K> terminated = !valid;
+
+ vint<K> octant = ray.octant();
+ octant = select(valid, octant, vint<K>(0xffffffff));
+
+ do
+ {
+ const size_t valid_index = bsf(valid_bits);
+ vbool<K> octant_valid = octant[valid_index] == octant;
+ valid_bits &= ~(size_t)movemask(octant_valid);
+
+ tray.tnear = select(octant_valid, org_ray_tnear, vfloat<K>(pos_inf));
+ tray.tfar = select(octant_valid, org_ray_tfar, vfloat<K>(neg_inf));
+
+ Frustum<robust> frustum;
+ frustum.template init<K>(octant_valid, tray.org, tray.rdir, tray.tnear, tray.tfar, N);
+
+ StackItemMaskT<NodeRef> stack[stackSizeSingle]; // stack of nodes
+ StackItemMaskT<NodeRef>* stackPtr = stack + 1; // current stack pointer
+ stack[0].ptr = bvh->root;
+ stack[0].mask = movemask(octant_valid);
+
+ while (1) pop:
+ {
+ /* pop next node from stack */
+ if (unlikely(stackPtr == stack)) break;
+
+ stackPtr--;
+ NodeRef cur = NodeRef(stackPtr->ptr);
+
+ /* cull node of active rays have already been terminated */
+ size_t m_active = (size_t)stackPtr->mask & (~(size_t)movemask(terminated));
+
+ if (unlikely(m_active == 0)) continue;
+
+ while (likely(!cur.isLeaf()))
+ {
+ /* process nodes */
+ //STAT3(normal.trav_nodes, 1, popcnt(valid_node), K);
+ const NodeRef nodeRef = cur;
+ const AABBNode* __restrict__ const node = nodeRef.getAABBNode();
+
+ vfloat<N> fmin;
+ size_t m_frustum_node = intersectNodeFrustum<N>(node, frustum, fmin);
+
+ if (unlikely(!m_frustum_node)) goto pop;
+ cur = BVH::emptyNode;
+ m_active = 0;
+
+#if defined(__AVX__)
+ //STAT3(normal.trav_hit_boxes[popcnt(m_frustum_node)], 1, 1, 1);
+#endif
+ size_t num_child_hits = 0;
+ do {
+ const size_t i = bscf(m_frustum_node);
+ vfloat<K> lnearP;
+ vbool<K> lhit = false; // motion blur is not supported, so the initial value will be ignored
+ STAT3(normal.trav_nodes, 1, 1, 1);
+ BVHNNodeIntersectorK<N, K, types, robust>::intersect(nodeRef, i, tray, ray.time(), lnearP, lhit);
+
+ if (likely(any(lhit)))
+ {
+ const NodeRef child = node->child(i);
+ assert(child != BVH::emptyNode);
+ BVHN<N>::prefetch(child);
+ if (likely(cur != BVH::emptyNode)) {
+ num_child_hits++;
+ stackPtr->ptr = cur;
+ stackPtr->mask = m_active;
+ stackPtr++;
+ }
+ cur = child;
+ m_active = movemask(lhit);
+ }
+ } while(m_frustum_node);
+
+ if (unlikely(cur == BVH::emptyNode)) goto pop;
+ }
+
+ /* intersect leaf */
+ assert(cur != BVH::invalidNode);
+ assert(cur != BVH::emptyNode);
+#if defined(__AVX__)
+ STAT3(normal.trav_leaves, 1, popcnt(m_active), K);
+#endif
+ if (unlikely(!m_active)) continue;
+ size_t items; const Primitive* prim = (Primitive*)cur.leaf(items);
+
+ size_t lazy_node = 0;
+ terminated |= PrimitiveIntersectorK::occluded(!terminated, This, pre, ray, context, prim, items, tray, lazy_node);
+ octant_valid &= !terminated;
+ if (unlikely(none(octant_valid))) break;
+ tray.tfar = select(terminated, vfloat<K>(neg_inf), tray.tfar); // ignore node intersections for terminated rays
+
+ if (unlikely(lazy_node)) {
+ stackPtr->ptr = lazy_node;
+ stackPtr->mask = movemask(octant_valid);
+ stackPtr++;
+ }
+ }
+ } while(valid_bits);
+
+ vfloat<K>::store(valid & terminated, &ray.tfar, neg_inf);
+ }
+ }
+}
diff --git a/thirdparty/embree/kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp b/thirdparty/embree/kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp
new file mode 100644
index 0000000000..2137da6a25
--- /dev/null
+++ b/thirdparty/embree/kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp
@@ -0,0 +1,59 @@
+// Copyright 2009-2021 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#include "bvh_intersector_hybrid.cpp"
+
+namespace embree
+{
+ namespace isa
+ {
+ ////////////////////////////////////////////////////////////////////////////////
+ /// BVH4Intersector4 Definitions
+ ////////////////////////////////////////////////////////////////////////////////
+
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4Intersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA ArrayIntersectorK_1<4 COMMA TriangleMIntersectorKMoeller <4 COMMA 4 COMMA true> > >));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4Intersector4HybridMoellerNoFilter, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA ArrayIntersectorK_1<4 COMMA TriangleMIntersectorKMoeller <4 COMMA 4 COMMA false> > >));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4iIntersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA ArrayIntersectorK_1<4 COMMA TriangleMiIntersectorKMoeller <4 COMMA 4 COMMA true> > >));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4vIntersector4HybridPluecker, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA true COMMA ArrayIntersectorK_1<4 COMMA TriangleMvIntersectorKPluecker<4 COMMA 4 COMMA true> > >));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4iIntersector4HybridPluecker, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA true COMMA ArrayIntersectorK_1<4 COMMA TriangleMiIntersectorKPluecker<4 COMMA 4 COMMA true> > >));
+
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4vMBIntersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA false COMMA ArrayIntersectorK_1<4 COMMA TriangleMvMBIntersectorKMoeller <4 COMMA 4 COMMA true> > >));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4iMBIntersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA false COMMA ArrayIntersectorK_1<4 COMMA TriangleMiMBIntersectorKMoeller <4 COMMA 4 COMMA true> > >));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4vMBIntersector4HybridPluecker, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA true COMMA ArrayIntersectorK_1<4 COMMA TriangleMvMBIntersectorKPluecker<4 COMMA 4 COMMA true> > >));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTOR4(BVH4Triangle4iMBIntersector4HybridPluecker, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA true COMMA ArrayIntersectorK_1<4 COMMA TriangleMiMBIntersectorKPluecker<4 COMMA 4 COMMA true> > >));
+
+ IF_ENABLED_QUADS(DEFINE_INTERSECTOR4(BVH4Quad4vIntersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA ArrayIntersectorK_1<4 COMMA QuadMvIntersectorKMoeller <4 COMMA 4 COMMA true > > >));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTOR4(BVH4Quad4vIntersector4HybridMoellerNoFilter,BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA ArrayIntersectorK_1<4 COMMA QuadMvIntersectorKMoeller <4 COMMA 4 COMMA false> > >));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTOR4(BVH4Quad4iIntersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA ArrayIntersectorK_1<4 COMMA QuadMiIntersectorKMoeller <4 COMMA 4 COMMA true > > >));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTOR4(BVH4Quad4vIntersector4HybridPluecker, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA true COMMA ArrayIntersectorK_1<4 COMMA QuadMvIntersectorKPluecker<4 COMMA 4 COMMA true > > >));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTOR4(BVH4Quad4iIntersector4HybridPluecker, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA true COMMA ArrayIntersectorK_1<4 COMMA QuadMiIntersectorKPluecker<4 COMMA 4 COMMA true > > >));
+
+ IF_ENABLED_QUADS(DEFINE_INTERSECTOR4(BVH4Quad4iMBIntersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA false COMMA ArrayIntersectorK_1<4 COMMA QuadMiMBIntersectorKMoeller <4 COMMA 4 COMMA true > > >));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTOR4(BVH4Quad4iMBIntersector4HybridPluecker,BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA true COMMA ArrayIntersectorK_1<4 COMMA QuadMiMBIntersectorKPluecker<4 COMMA 4 COMMA true > > >));
+
+ IF_ENABLED_CURVES_OR_POINTS(DEFINE_INTERSECTOR4(BVH4OBBVirtualCurveIntersector4Hybrid, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1_UN1 COMMA false COMMA VirtualCurveIntersectorK<4> >));
+ IF_ENABLED_CURVES_OR_POINTS(DEFINE_INTERSECTOR4(BVH4OBBVirtualCurveIntersector4HybridMB,BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D_UN2 COMMA false COMMA VirtualCurveIntersectorK<4> >));
+
+ IF_ENABLED_CURVES_OR_POINTS(DEFINE_INTERSECTOR4(BVH4OBBVirtualCurveIntersectorRobust4Hybrid, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1_UN1 COMMA true COMMA VirtualCurveIntersectorK<4> >));
+ IF_ENABLED_CURVES_OR_POINTS(DEFINE_INTERSECTOR4(BVH4OBBVirtualCurveIntersectorRobust4HybridMB,BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D_UN2 COMMA true COMMA VirtualCurveIntersectorK<4> >));
+
+ //IF_ENABLED_SUBDIV(DEFINE_INTERSECTOR4(BVH4SubdivPatch1Intersector4, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA true COMMA SubdivPatch1Intersector4>));
+ IF_ENABLED_SUBDIV(DEFINE_INTERSECTOR4(BVH4SubdivPatch1Intersector4, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA true COMMA SubdivPatch1Intersector4>));
+ IF_ENABLED_SUBDIV(DEFINE_INTERSECTOR4(BVH4SubdivPatch1MBIntersector4, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA false COMMA SubdivPatch1MBIntersector4>));
+ //IF_ENABLED_SUBDIV(DEFINE_INTERSECTOR4(BVH4SubdivPatch1MBIntersector4, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA false COMMA SubdivPatch1MBIntersector4>));
+
+ IF_ENABLED_USER(DEFINE_INTERSECTOR4(BVH4VirtualIntersector4Chunk, BVHNIntersectorKChunk<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA ArrayIntersectorK_1<4 COMMA ObjectIntersector4> >));
+ IF_ENABLED_USER(DEFINE_INTERSECTOR4(BVH4VirtualMBIntersector4Chunk, BVHNIntersectorKChunk<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA false COMMA ArrayIntersectorK_1<4 COMMA ObjectIntersector4MB> >));
+
+ IF_ENABLED_INSTANCE(DEFINE_INTERSECTOR4(BVH4InstanceIntersector4Chunk, BVHNIntersectorKChunk<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA ArrayIntersectorK_1<4 COMMA InstanceIntersectorK<4>> >));
+ IF_ENABLED_INSTANCE(DEFINE_INTERSECTOR4(BVH4InstanceMBIntersector4Chunk, BVHNIntersectorKChunk<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA false COMMA ArrayIntersectorK_1<4 COMMA InstanceIntersectorKMB<4>> >));
+
+ IF_ENABLED_GRIDS(DEFINE_INTERSECTOR4(BVH4GridIntersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA SubGridIntersectorKMoeller <4 COMMA 4 COMMA true> >));
+ //IF_ENABLED_GRIDS(DEFINE_INTERSECTOR4(BVH4GridIntersector4HybridMoeller, BVHNIntersectorKChunk<4 COMMA 4 COMMA BVH_AN1 COMMA false COMMA SubGridIntersectorKMoeller <4 COMMA 4 COMMA true> >));
+
+ IF_ENABLED_GRIDS(DEFINE_INTERSECTOR4(BVH4GridMBIntersector4HybridMoeller, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN2_AN4D COMMA true COMMA SubGridMBIntersectorKPluecker <4 COMMA 4 COMMA true> >));
+ IF_ENABLED_GRIDS(DEFINE_INTERSECTOR4(BVH4GridIntersector4HybridPluecker, BVHNIntersectorKHybrid<4 COMMA 4 COMMA BVH_AN1 COMMA true COMMA SubGridIntersectorKPluecker <4 COMMA 4 COMMA true> >));
+
+ }
+}
+
diff --git a/thirdparty/embree/kernels/bvh/bvh_intersector_stream.cpp b/thirdparty/embree/kernels/bvh/bvh_intersector_stream.cpp
new file mode 100644
index 0000000000..4a74d8468d
--- /dev/null
+++ b/thirdparty/embree/kernels/bvh/bvh_intersector_stream.cpp
@@ -0,0 +1,528 @@
+// Copyright 2009-2021 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#include "bvh_intersector_stream.h"
+
+#include "../geometry/intersector_iterators.h"
+#include "../geometry/triangle_intersector.h"
+#include "../geometry/trianglev_intersector.h"
+#include "../geometry/trianglev_mb_intersector.h"
+#include "../geometry/trianglei_intersector.h"
+#include "../geometry/quadv_intersector.h"
+#include "../geometry/quadi_intersector.h"
+#include "../geometry/linei_intersector.h"
+#include "../geometry/subdivpatch1_intersector.h"
+#include "../geometry/object_intersector.h"
+#include "../geometry/instance_intersector.h"
+
+#include "../common/scene.h"
+#include <bitset>
+
+namespace embree
+{
+ namespace isa
+ {
+ __aligned(64) static const int shiftTable[32] = {
+ (int)1 << 0, (int)1 << 1, (int)1 << 2, (int)1 << 3, (int)1 << 4, (int)1 << 5, (int)1 << 6, (int)1 << 7,
+ (int)1 << 8, (int)1 << 9, (int)1 << 10, (int)1 << 11, (int)1 << 12, (int)1 << 13, (int)1 << 14, (int)1 << 15,
+ (int)1 << 16, (int)1 << 17, (int)1 << 18, (int)1 << 19, (int)1 << 20, (int)1 << 21, (int)1 << 22, (int)1 << 23,
+ (int)1 << 24, (int)1 << 25, (int)1 << 26, (int)1 << 27, (int)1 << 28, (int)1 << 29, (int)1 << 30, (int)1 << 31
+ };
+
+ template<int N, int types, bool robust, typename PrimitiveIntersector>
+ __forceinline void BVHNIntersectorStream<N, types, robust, PrimitiveIntersector>::intersect(Accel::Intersectors* __restrict__ This,
+ RayHitN** inputPackets,
+ size_t numOctantRays,
+ IntersectContext* context)
+ {
+ /* we may traverse an empty BVH in case all geometry was invalid */
+ BVH* __restrict__ bvh = (BVH*) This->ptr;
+ if (bvh->root == BVH::emptyNode)
+ return;
+
+ // Only the coherent code path is implemented
+ assert(context->isCoherent());
+ intersectCoherent(This, (RayHitK<VSIZEL>**)inputPackets, numOctantRays, context);
+ }
+
+ template<int N, int types, bool robust, typename PrimitiveIntersector>
+ template<int K>
+ __forceinline void BVHNIntersectorStream<N, types, robust, PrimitiveIntersector>::intersectCoherent(Accel::Intersectors* __restrict__ This,
+ RayHitK<K>** inputPackets,
+ size_t numOctantRays,
+ IntersectContext* context)
+ {
+ assert(context->isCoherent());
+
+ BVH* __restrict__ bvh = (BVH*) This->ptr;
+ __aligned(64) StackItemMaskCoherent stack[stackSizeSingle]; // stack of nodes
+ assert(numOctantRays <= MAX_INTERNAL_STREAM_SIZE);
+
+ __aligned(64) TravRayKStream<K, robust> packets[MAX_INTERNAL_STREAM_SIZE/K];
+ __aligned(64) Frustum<robust> frustum;
+
+ bool commonOctant = true;
+ const size_t m_active = initPacketsAndFrustum((RayK<K>**)inputPackets, numOctantRays, packets, frustum, commonOctant);
+ if (unlikely(m_active == 0)) return;
+
+ /* case of non-common origin */
+ if (unlikely(!commonOctant))
+ {
+ const size_t numPackets = (numOctantRays+K-1)/K;
+ for (size_t i = 0; i < numPackets; i++)
+ This->intersect(inputPackets[i]->tnear() <= inputPackets[i]->tfar, *inputPackets[i], context);
+ return;
+ }
+
+ stack[0].mask = m_active;
+ stack[0].parent = 0;
+ stack[0].child = bvh->root;
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ StackItemMaskCoherent* stackPtr = stack + 1;
+
+ while (1) pop:
+ {
+ if (unlikely(stackPtr == stack)) break;
+
+ STAT3(normal.trav_stack_pop,1,1,1);
+ stackPtr--;
+ /*! pop next node */
+ NodeRef cur = NodeRef(stackPtr->child);
+ size_t m_trav_active = stackPtr->mask;
+ assert(m_trav_active);
+ NodeRef parent = stackPtr->parent;
+
+ while (1)
+ {
+ if (unlikely(cur.isLeaf())) break;
+ const AABBNode* __restrict__ const node = cur.getAABBNode();
+ parent = cur;
+
+ __aligned(64) size_t maskK[N];
+ for (size_t i = 0; i < N; i++)
+ maskK[i] = m_trav_active;
+ vfloat<N> dist;
+ const size_t m_node_hit = traverseCoherentStream(m_trav_active, packets, node, frustum, maskK, dist);
+ if (unlikely(m_node_hit == 0)) goto pop;
+
+ BVHNNodeTraverserStreamHitCoherent<N, types>::traverseClosestHit(cur, m_trav_active, vbool<N>((int)m_node_hit), dist, (size_t*)maskK, stackPtr);
+ assert(m_trav_active);
+ }
+
+ /* non-root and leaf => full culling test for all rays */
+ if (unlikely(parent != 0 && cur.isLeaf()))
+ {
+ const AABBNode* __restrict__ const node = parent.getAABBNode();
+ size_t boxID = 0xff;
+ for (size_t i = 0; i < N; i++)
+ if (node->child(i) == cur) { boxID = i; break; }
+ assert(boxID < N);
+ assert(cur == node->child(boxID));
+ m_trav_active = intersectAABBNodePacket(m_trav_active, packets, node, boxID, frustum.nf);
+ }
+
+ /*! this is a leaf node */
+ assert(cur != BVH::emptyNode);
+ STAT3(normal.trav_leaves, 1, 1, 1);
+ size_t num; PrimitiveK<K>* prim = (PrimitiveK<K>*)cur.leaf(num);
+
+ size_t bits = m_trav_active;
+
+ /*! intersect stream of rays with all primitives */
+ size_t lazy_node = 0;
+#if defined(__SSE4_2__)
+ STAT_USER(1,(popcnt(bits)+K-1)/K*4);
+#endif
+ while(bits)
+ {
+ size_t i = bsf(bits) / K;
+ const size_t m_isec = ((((size_t)1 << K)-1) << (i*K));
+ assert(m_isec & bits);
+ bits &= ~m_isec;
+
+ TravRayKStream<K, robust>& p = packets[i];
+ vbool<K> m_valid = p.tnear <= p.tfar;
+ PrimitiveIntersectorK<K>::intersectK(m_valid, This, *inputPackets[i], context, prim, num, lazy_node);
+ p.tfar = min(p.tfar, inputPackets[i]->tfar);
+ };
+
+ } // traversal + intersection
+ }
+
+ template<int N, int types, bool robust, typename PrimitiveIntersector>
+ __forceinline void BVHNIntersectorStream<N, types, robust, PrimitiveIntersector>::occluded(Accel::Intersectors* __restrict__ This,
+ RayN** inputPackets,
+ size_t numOctantRays,
+ IntersectContext* context)
+ {
+ /* we may traverse an empty BVH in case all geometry was invalid */
+ BVH* __restrict__ bvh = (BVH*) This->ptr;
+ if (bvh->root == BVH::emptyNode)
+ return;
+
+ if (unlikely(context->isCoherent()))
+ occludedCoherent(This, (RayK<VSIZEL>**)inputPackets, numOctantRays, context);
+ else
+ occludedIncoherent(This, (RayK<VSIZEX>**)inputPackets, numOctantRays, context);
+ }
+
+ template<int N, int types, bool robust, typename PrimitiveIntersector>
+ template<int K>
+ __noinline void BVHNIntersectorStream<N, types, robust, PrimitiveIntersector>::occludedCoherent(Accel::Intersectors* __restrict__ This,
+ RayK<K>** inputPackets,
+ size_t numOctantRays,
+ IntersectContext* context)
+ {
+ assert(context->isCoherent());
+
+ BVH* __restrict__ bvh = (BVH*)This->ptr;
+ __aligned(64) StackItemMaskCoherent stack[stackSizeSingle]; // stack of nodes
+ assert(numOctantRays <= MAX_INTERNAL_STREAM_SIZE);
+
+ /* inactive rays should have been filtered out before */
+ __aligned(64) TravRayKStream<K, robust> packets[MAX_INTERNAL_STREAM_SIZE/K];
+ __aligned(64) Frustum<robust> frustum;
+
+ bool commonOctant = true;
+ size_t m_active = initPacketsAndFrustum(inputPackets, numOctantRays, packets, frustum, commonOctant);
+
+ /* valid rays */
+ if (unlikely(m_active == 0)) return;
+
+ /* case of non-common origin */
+ if (unlikely(!commonOctant))
+ {
+ const size_t numPackets = (numOctantRays+K-1)/K;
+ for (size_t i = 0; i < numPackets; i++)
+ This->occluded(inputPackets[i]->tnear() <= inputPackets[i]->tfar, *inputPackets[i], context);
+ return;
+ }
+
+ stack[0].mask = m_active;
+ stack[0].parent = 0;
+ stack[0].child = bvh->root;
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ StackItemMaskCoherent* stackPtr = stack + 1;
+
+ while (1) pop:
+ {
+ if (unlikely(stackPtr == stack)) break;
+
+ STAT3(normal.trav_stack_pop,1,1,1);
+ stackPtr--;
+ /*! pop next node */
+ NodeRef cur = NodeRef(stackPtr->child);
+ size_t m_trav_active = stackPtr->mask & m_active;
+ if (unlikely(!m_trav_active)) continue;
+ assert(m_trav_active);
+ NodeRef parent = stackPtr->parent;
+
+ while (1)
+ {
+ if (unlikely(cur.isLeaf())) break;
+ const AABBNode* __restrict__ const node = cur.getAABBNode();
+ parent = cur;
+
+ __aligned(64) size_t maskK[N];
+ for (size_t i = 0; i < N; i++)
+ maskK[i] = m_trav_active;
+
+ vfloat<N> dist;
+ const size_t m_node_hit = traverseCoherentStream(m_trav_active, packets, node, frustum, maskK, dist);
+ if (unlikely(m_node_hit == 0)) goto pop;
+
+ BVHNNodeTraverserStreamHitCoherent<N, types>::traverseAnyHit(cur, m_trav_active, vbool<N>((int)m_node_hit), (size_t*)maskK, stackPtr);
+ assert(m_trav_active);
+ }
+
+ /* non-root and leaf => full culling test for all rays */
+ if (unlikely(parent != 0 && cur.isLeaf()))
+ {
+ const AABBNode* __restrict__ const node = parent.getAABBNode();
+ size_t boxID = 0xff;
+ for (size_t i = 0; i < N; i++)
+ if (node->child(i) == cur) { boxID = i; break; }
+ assert(boxID < N);
+ assert(cur == node->child(boxID));
+ m_trav_active = intersectAABBNodePacket(m_trav_active, packets, node, boxID, frustum.nf);
+ }
+
+ /*! this is a leaf node */
+ assert(cur != BVH::emptyNode);
+ STAT3(normal.trav_leaves, 1, 1, 1);
+ size_t num; PrimitiveK<K>* prim = (PrimitiveK<K>*)cur.leaf(num);
+
+ size_t bits = m_trav_active & m_active;
+ /*! intersect stream of rays with all primitives */
+ size_t lazy_node = 0;
+#if defined(__SSE4_2__)
+ STAT_USER(1,(popcnt(bits)+K-1)/K*4);
+#endif
+ while (bits)
+ {
+ size_t i = bsf(bits) / K;
+ const size_t m_isec = ((((size_t)1 << K)-1) << (i*K));
+ assert(m_isec & bits);
+ bits &= ~m_isec;
+ TravRayKStream<K, robust>& p = packets[i];
+ vbool<K> m_valid = p.tnear <= p.tfar;
+ vbool<K> m_hit = PrimitiveIntersectorK<K>::occludedK(m_valid, This, *inputPackets[i], context, prim, num, lazy_node);
+ inputPackets[i]->tfar = select(m_hit & m_valid, vfloat<K>(neg_inf), inputPackets[i]->tfar);
+ m_active &= ~((size_t)movemask(m_hit) << (i*K));
+ }
+
+ } // traversal + intersection
+ }
+
+
+ template<int N, int types, bool robust, typename PrimitiveIntersector>
+ template<int K>
+ __forceinline void BVHNIntersectorStream<N, types, robust, PrimitiveIntersector>::occludedIncoherent(Accel::Intersectors* __restrict__ This,
+ RayK<K>** inputPackets,
+ size_t numOctantRays,
+ IntersectContext* context)
+ {
+ assert(!context->isCoherent());
+ assert(types & BVH_FLAG_ALIGNED_NODE);
+
+ __aligned(64) TravRayKStream<K,robust> packet[MAX_INTERNAL_STREAM_SIZE/K];
+
+ assert(numOctantRays <= 32);
+ const size_t numPackets = (numOctantRays+K-1)/K;
+ size_t m_active = 0;
+ for (size_t i = 0; i < numPackets; i++)
+ {
+ const vfloat<K> tnear = inputPackets[i]->tnear();
+ const vfloat<K> tfar = inputPackets[i]->tfar;
+ vbool<K> m_valid = (tnear <= tfar) & (tnear >= 0.0f);
+ m_active |= (size_t)movemask(m_valid) << (K*i);
+ const Vec3vf<K>& org = inputPackets[i]->org;
+ const Vec3vf<K>& dir = inputPackets[i]->dir;
+ vfloat<K> packet_min_dist = max(tnear, 0.0f);
+ vfloat<K> packet_max_dist = select(m_valid, tfar, neg_inf);
+ new (&packet[i]) TravRayKStream<K,robust>(org, dir, packet_min_dist, packet_max_dist);
+ }
+
+ BVH* __restrict__ bvh = (BVH*)This->ptr;
+
+ StackItemMaskT<NodeRef> stack[stackSizeSingle]; // stack of nodes
+ StackItemMaskT<NodeRef>* stackPtr = stack + 1; // current stack pointer
+ stack[0].ptr = bvh->root;
+ stack[0].mask = m_active;
+
+ size_t terminated = ~m_active;
+
+ /* near/far offsets based on first ray */
+ const NearFarPrecalculations nf(Vec3fa(packet[0].rdir.x[0], packet[0].rdir.y[0], packet[0].rdir.z[0]), N);
+
+ while (1) pop:
+ {
+ if (unlikely(stackPtr == stack)) break;
+ STAT3(shadow.trav_stack_pop,1,1,1);
+ stackPtr--;
+ NodeRef cur = NodeRef(stackPtr->ptr);
+ size_t cur_mask = stackPtr->mask & (~terminated);
+ if (unlikely(cur_mask == 0)) continue;
+
+ while (true)
+ {
+ /*! stop if we found a leaf node */
+ if (unlikely(cur.isLeaf())) break;
+ const AABBNode* __restrict__ const node = cur.getAABBNode();
+
+ const vint<N> vmask = traverseIncoherentStream(cur_mask, packet, node, nf, shiftTable);
+
+ size_t mask = movemask(vmask != vint<N>(zero));
+ if (unlikely(mask == 0)) goto pop;
+
+ __aligned(64) unsigned int child_mask[N];
+ vint<N>::storeu(child_mask, vmask); // this explicit store here causes much better code generation
+
+ /*! one child is hit, continue with that child */
+ size_t r = bscf(mask);
+ assert(r < N);
+ cur = node->child(r);
+ BVHN<N>::prefetch(cur,types);
+ cur_mask = child_mask[r];
+
+ /* simple in order sequence */
+ assert(cur != BVH::emptyNode);
+ if (likely(mask == 0)) continue;
+ stackPtr->ptr = cur;
+ stackPtr->mask = cur_mask;
+ stackPtr++;
+
+ for (; ;)
+ {
+ r = bscf(mask);
+ assert(r < N);
+
+ cur = node->child(r);
+ BVHN<N>::prefetch(cur,types);
+ cur_mask = child_mask[r];
+ assert(cur != BVH::emptyNode);
+ if (likely(mask == 0)) break;
+ stackPtr->ptr = cur;
+ stackPtr->mask = cur_mask;
+ stackPtr++;
+ }
+ }
+
+ /*! this is a leaf node */
+ assert(cur != BVH::emptyNode);
+ STAT3(shadow.trav_leaves,1,1,1);
+ size_t num; PrimitiveK<K>* prim = (PrimitiveK<K>*)cur.leaf(num);
+
+ size_t bits = cur_mask;
+ size_t lazy_node = 0;
+
+ for (; bits != 0;)
+ {
+ const size_t rayID = bscf(bits);
+
+ RayK<K> &ray = *inputPackets[rayID / K];
+ const size_t k = rayID % K;
+ if (PrimitiveIntersectorK<K>::occluded(This, ray, k, context, prim, num, lazy_node))
+ {
+ ray.tfar[k] = neg_inf;
+ terminated |= (size_t)1 << rayID;
+ }
+
+ /* lazy node */
+ if (unlikely(lazy_node))
+ {
+ stackPtr->ptr = lazy_node;
+ stackPtr->mask = cur_mask;
+ stackPtr++;
+ }
+ }
+
+ if (unlikely(terminated == (size_t)-1)) break;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// ArrayIntersectorKStream Definitions
+ ////////////////////////////////////////////////////////////////////////////////
+
+ template<bool filter>
+ struct Triangle4IntersectorStreamMoeller {
+ template<int K> using Type = ArrayIntersectorKStream<K,TriangleMIntersectorKMoeller<4 COMMA K COMMA true>>;
+ };
+
+ template<bool filter>
+ struct Triangle4vIntersectorStreamPluecker {
+ template<int K> using Type = ArrayIntersectorKStream<K,TriangleMvIntersectorKPluecker<4 COMMA K COMMA true>>;
+ };
+
+ template<bool filter>
+ struct Triangle4iIntersectorStreamMoeller {
+ template<int K> using Type = ArrayIntersectorKStream<K,TriangleMiIntersectorKMoeller<4 COMMA K COMMA true>>;
+ };
+
+ template<bool filter>
+ struct Triangle4iIntersectorStreamPluecker {
+ template<int K> using Type = ArrayIntersectorKStream<K,TriangleMiIntersectorKPluecker<4 COMMA K COMMA true>>;
+ };
+
+ template<bool filter>
+ struct Quad4vIntersectorStreamMoeller {
+ template<int K> using Type = ArrayIntersectorKStream<K,QuadMvIntersectorKMoeller<4 COMMA K COMMA true>>;
+ };
+
+ template<bool filter>
+ struct Quad4iIntersectorStreamMoeller {
+ template<int K> using Type = ArrayIntersectorKStream<K,QuadMiIntersectorKMoeller<4 COMMA K COMMA true>>;
+ };
+
+ template<bool filter>
+ struct Quad4vIntersectorStreamPluecker {
+ template<int K> using Type = ArrayIntersectorKStream<K,QuadMvIntersectorKPluecker<4 COMMA K COMMA true>>;
+ };
+
+ template<bool filter>
+ struct Quad4iIntersectorStreamPluecker {
+ template<int K> using Type = ArrayIntersectorKStream<K,QuadMiIntersectorKPluecker<4 COMMA K COMMA true>>;
+ };
+
+ struct ObjectIntersectorStream {
+ template<int K> using Type = ArrayIntersectorKStream<K,ObjectIntersectorK<K COMMA false>>;
+ };
+
+ struct InstanceIntersectorStream {
+ template<int K> using Type = ArrayIntersectorKStream<K,InstanceIntersectorK<K>>;
+ };
+
+ // =====================================================================================================
+ // =====================================================================================================
+ // =====================================================================================================
+
+ template<int N>
+ void BVHNIntersectorStreamPacketFallback<N>::intersect(Accel::Intersectors* __restrict__ This,
+ RayHitN** inputRays,
+ size_t numTotalRays,
+ IntersectContext* context)
+ {
+ if (unlikely(context->isCoherent()))
+ intersectK(This, (RayHitK<VSIZEL>**)inputRays, numTotalRays, context);
+ else
+ intersectK(This, (RayHitK<VSIZEX>**)inputRays, numTotalRays, context);
+ }
+
+ template<int N>
+ void BVHNIntersectorStreamPacketFallback<N>::occluded(Accel::Intersectors* __restrict__ This,
+ RayN** inputRays,
+ size_t numTotalRays,
+ IntersectContext* context)
+ {
+ if (unlikely(context->isCoherent()))
+ occludedK(This, (RayK<VSIZEL>**)inputRays, numTotalRays, context);
+ else
+ occludedK(This, (RayK<VSIZEX>**)inputRays, numTotalRays, context);
+ }
+
+ template<int N>
+ template<int K>
+ __noinline void BVHNIntersectorStreamPacketFallback<N>::intersectK(Accel::Intersectors* __restrict__ This,
+ RayHitK<K>** inputRays,
+ size_t numTotalRays,
+ IntersectContext* context)
+ {
+ /* fallback to packets */
+ for (size_t i = 0; i < numTotalRays; i += K)
+ {
+ const vint<K> vi = vint<K>(int(i)) + vint<K>(step);
+ vbool<K> valid = vi < vint<K>(int(numTotalRays));
+ RayHitK<K>& ray = *(inputRays[i / K]);
+ valid &= ray.tnear() <= ray.tfar;
+ This->intersect(valid, ray, context);
+ }
+ }
+
+ template<int N>
+ template<int K>
+ __noinline void BVHNIntersectorStreamPacketFallback<N>::occludedK(Accel::Intersectors* __restrict__ This,
+ RayK<K>** inputRays,
+ size_t numTotalRays,
+ IntersectContext* context)
+ {
+ /* fallback to packets */
+ for (size_t i = 0; i < numTotalRays; i += K)
+ {
+ const vint<K> vi = vint<K>(int(i)) + vint<K>(step);
+ vbool<K> valid = vi < vint<K>(int(numTotalRays));
+ RayK<K>& ray = *(inputRays[i / K]);
+ valid &= ray.tnear() <= ray.tfar;
+ This->occluded(valid, ray, context);
+ }
+ }
+ }
+}
diff --git a/thirdparty/embree/kernels/bvh/bvh_intersector_stream_bvh4.cpp b/thirdparty/embree/kernels/bvh/bvh_intersector_stream_bvh4.cpp
new file mode 100644
index 0000000000..c3e5f137b8
--- /dev/null
+++ b/thirdparty/embree/kernels/bvh/bvh_intersector_stream_bvh4.cpp
@@ -0,0 +1,36 @@
+// Copyright 2009-2021 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#include "bvh_intersector_stream.cpp"
+
+namespace embree
+{
+ namespace isa
+ {
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// General BVHIntersectorStreamPacketFallback Intersector
+ ////////////////////////////////////////////////////////////////////////////////
+
+ DEFINE_INTERSECTORN(BVH4IntersectorStreamPacketFallback,BVHNIntersectorStreamPacketFallback<4>);
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// BVH4IntersectorStream Definitions
+ ////////////////////////////////////////////////////////////////////////////////
+
+ IF_ENABLED_TRIS(DEFINE_INTERSECTORN(BVH4Triangle4iIntersectorStreamMoeller, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA false COMMA Triangle4iIntersectorStreamMoeller<true>>));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTORN(BVH4Triangle4vIntersectorStreamPluecker, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA true COMMA Triangle4vIntersectorStreamPluecker<true>>));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTORN(BVH4Triangle4iIntersectorStreamPluecker, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA true COMMA Triangle4iIntersectorStreamPluecker<true>>));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTORN(BVH4Triangle4IntersectorStreamMoeller, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA false COMMA Triangle4IntersectorStreamMoeller<true>>));
+ IF_ENABLED_TRIS(DEFINE_INTERSECTORN(BVH4Triangle4IntersectorStreamMoellerNoFilter, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA false COMMA Triangle4IntersectorStreamMoeller<false>>));
+
+ IF_ENABLED_QUADS(DEFINE_INTERSECTORN(BVH4Quad4vIntersectorStreamMoeller, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA false COMMA Quad4vIntersectorStreamMoeller<true>>));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTORN(BVH4Quad4vIntersectorStreamMoellerNoFilter,BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA false COMMA Quad4vIntersectorStreamMoeller<false>>));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTORN(BVH4Quad4iIntersectorStreamMoeller, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA false COMMA Quad4iIntersectorStreamMoeller<true>>));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTORN(BVH4Quad4vIntersectorStreamPluecker, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA true COMMA Quad4vIntersectorStreamPluecker<true>>));
+ IF_ENABLED_QUADS(DEFINE_INTERSECTORN(BVH4Quad4iIntersectorStreamPluecker, BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA true COMMA Quad4iIntersectorStreamPluecker<true>>));
+
+ IF_ENABLED_USER(DEFINE_INTERSECTORN(BVH4VirtualIntersectorStream,BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA false COMMA ObjectIntersectorStream>));
+ IF_ENABLED_INSTANCE(DEFINE_INTERSECTORN(BVH4InstanceIntersectorStream,BVHNIntersectorStream<4 COMMA BVH_AN1 COMMA false COMMA InstanceIntersectorStream>));
+ }
+}
diff --git a/thirdparty/embree/kernels/bvh/bvh_intersector_stream_filters.cpp b/thirdparty/embree/kernels/bvh/bvh_intersector_stream_filters.cpp
new file mode 100644
index 0000000000..b858eb163f
--- /dev/null
+++ b/thirdparty/embree/kernels/bvh/bvh_intersector_stream_filters.cpp
@@ -0,0 +1,657 @@
+// Copyright 2009-2021 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#include "bvh_intersector_stream_filters.h"
+#include "bvh_intersector_stream.h"
+
+namespace embree
+{
+ namespace isa
+ {
+ template<int K, bool intersect>
+ __noinline void RayStreamFilter::filterAOS(Scene* scene, void* _rayN, size_t N, size_t stride, IntersectContext* context)
+ {
+ RayStreamAOS rayN(_rayN);
+
+ /* use fast path for coherent ray mode */
+ if (unlikely(context->isCoherent()))
+ {
+ __aligned(64) RayTypeK<K, intersect> rays[MAX_INTERNAL_STREAM_SIZE / K];
+ __aligned(64) RayTypeK<K, intersect>* rayPtrs[MAX_INTERNAL_STREAM_SIZE / K];
+
+ for (size_t i = 0; i < N; i += MAX_INTERNAL_STREAM_SIZE)
+ {
+ const size_t size = min(N - i, MAX_INTERNAL_STREAM_SIZE);
+
+ /* convert from AOS to SOA */
+ for (size_t j = 0; j < size; j += K)
+ {
+ const vint<K> vij = vint<K>(int(i+j)) + vint<K>(step);
+ const vbool<K> valid = vij < vint<K>(int(N));
+ const vint<K> offset = vij * int(stride);
+ const size_t packetIndex = j / K;
+
+ RayTypeK<K, intersect> ray = rayN.getRayByOffset<K>(valid, offset);
+ ray.tnear() = select(valid, ray.tnear(), zero);
+ ray.tfar = select(valid, ray.tfar, neg_inf);
+
+ rays[packetIndex] = ray;
+ rayPtrs[packetIndex] = &rays[packetIndex]; // rayPtrs might get reordered for occludedN
+ }
+
+ /* trace stream */
+ scene->intersectors.intersectN(rayPtrs, size, context);
+
+ /* convert from SOA to AOS */
+ for (size_t j = 0; j < size; j += K)
+ {
+ const vint<K> vij = vint<K>(int(i+j)) + vint<K>(step);
+ const vbool<K> valid = vij < vint<K>(int(N));
+ const vint<K> offset = vij * int(stride);
+ const size_t packetIndex = j / K;
+ rayN.setHitByOffset(valid, offset, rays[packetIndex]);
+ }
+ }
+ }
+ else if (unlikely(!intersect))
+ {
+ /* octant sorting for occlusion rays */
+ __aligned(64) unsigned int octants[8][MAX_INTERNAL_STREAM_SIZE];
+ __aligned(64) RayK<K> rays[MAX_INTERNAL_STREAM_SIZE / K];
+ __aligned(64) RayK<K>* rayPtrs[MAX_INTERNAL_STREAM_SIZE / K];
+
+ unsigned int raysInOctant[8];
+ for (unsigned int i = 0; i < 8; i++)
+ raysInOctant[i] = 0;
+ size_t inputRayID = 0;
+
+ for (;;)
+ {
+ int curOctant = -1;
+
+ /* sort rays into octants */
+ for (; inputRayID < N;)
+ {
+ const Ray& ray = rayN.getRayByOffset(inputRayID * stride);
+
+ /* skip invalid rays */
+ if (unlikely(ray.tnear() > ray.tfar || ray.tfar < 0.0f)) { inputRayID++; continue; } // ignore invalid or already occluded rays
+#if defined(EMBREE_IGNORE_INVALID_RAYS)
+ if (unlikely(!ray.valid())) { inputRayID++; continue; }
+#endif
+
+ const unsigned int octantID = movemask(vfloat4(Vec3fa(ray.dir)) < 0.0f) & 0x7;
+
+ assert(octantID < 8);
+ octants[octantID][raysInOctant[octantID]++] = (unsigned int)inputRayID;
+ inputRayID++;
+ if (unlikely(raysInOctant[octantID] == MAX_INTERNAL_STREAM_SIZE))
+ {
+ curOctant = octantID;
+ break;
+ }
+ }
+
+ /* need to flush rays in octant? */
+ if (unlikely(curOctant == -1))
+ {
+ for (unsigned int i = 0; i < 8; i++)
+ if (raysInOctant[i]) { curOctant = i; break; }
+ }
+
+ /* all rays traced? */
+ if (unlikely(curOctant == -1))
+ break;
+
+ unsigned int* const rayIDs = &octants[curOctant][0];
+ const unsigned int numOctantRays = raysInOctant[curOctant];
+ assert(numOctantRays);
+
+ for (unsigned int j = 0; j < numOctantRays; j += K)
+ {
+ const vint<K> vi = vint<K>(int(j)) + vint<K>(step);
+ const vbool<K> valid = vi < vint<K>(int(numOctantRays));
+ const vint<K> offset = *(vint<K>*)&rayIDs[j] * int(stride);
+ RayK<K>& ray = rays[j/K];
+ rayPtrs[j/K] = &ray;
+ ray = rayN.getRayByOffset<K>(valid, offset);
+ ray.tnear() = select(valid, ray.tnear(), zero);
+ ray.tfar = select(valid, ray.tfar, neg_inf);
+ }
+
+ scene->intersectors.occludedN(rayPtrs, numOctantRays, context);
+
+ for (unsigned int j = 0; j < numOctantRays; j += K)
+ {
+ const vint<K> vi = vint<K>(int(j)) + vint<K>(step);
+ const vbool<K> valid = vi < vint<K>(int(numOctantRays));
+ const vint<K> offset = *(vint<K>*)&rayIDs[j] * int(stride);
+ rayN.setHitByOffset<K>(valid, offset, rays[j/K]);
+ }
+
+ raysInOctant[curOctant] = 0;
+ }
+ }
+ else
+ {
+ /* fallback to packets */
+ for (size_t i = 0; i < N; i += K)
+ {
+ const vint<K> vi = vint<K>(int(i)) + vint<K>(step);
+ vbool<K> valid = vi < vint<K>(int(N));
+ const vint<K> offset = vi * int(stride);
+
+ RayTypeK<K, intersect> ray = rayN.getRayByOffset<K>(valid, offset);
+ valid &= ray.tnear() <= ray.tfar;
+
+ scene->intersectors.intersect(valid, ray, context);
+
+ rayN.setHitByOffset<K>(valid, offset, ray);
+ }
+ }
+ }
+
+ template<int K, bool intersect>
+ __noinline void RayStreamFilter::filterAOP(Scene* scene, void** _rayN, size_t N, IntersectContext* context)
+ {
+ RayStreamAOP rayN(_rayN);
+
+ /* use fast path for coherent ray mode */
+ if (unlikely(context->isCoherent()))
+ {
+ __aligned(64) RayTypeK<K, intersect> rays[MAX_INTERNAL_STREAM_SIZE / K];
+ __aligned(64) RayTypeK<K, intersect>* rayPtrs[MAX_INTERNAL_STREAM_SIZE / K];
+
+ for (size_t i = 0; i < N; i += MAX_INTERNAL_STREAM_SIZE)
+ {
+ const size_t size = min(N - i, MAX_INTERNAL_STREAM_SIZE);
+
+ /* convert from AOP to SOA */
+ for (size_t j = 0; j < size; j += K)
+ {
+ const vint<K> vij = vint<K>(int(i+j)) + vint<K>(step);
+ const vbool<K> valid = vij < vint<K>(int(N));
+ const size_t packetIndex = j / K;
+
+ RayTypeK<K, intersect> ray = rayN.getRayByIndex<K>(valid, vij);
+ ray.tnear() = select(valid, ray.tnear(), zero);
+ ray.tfar = select(valid, ray.tfar, neg_inf);
+
+ rays[packetIndex] = ray;
+ rayPtrs[packetIndex] = &rays[packetIndex]; // rayPtrs might get reordered for occludedN
+ }
+
+ /* trace stream */
+ scene->intersectors.intersectN(rayPtrs, size, context);
+
+ /* convert from SOA to AOP */
+ for (size_t j = 0; j < size; j += K)
+ {
+ const vint<K> vij = vint<K>(int(i+j)) + vint<K>(step);
+ const vbool<K> valid = vij < vint<K>(int(N));
+ const size_t packetIndex = j / K;
+
+ rayN.setHitByIndex<K>(valid, vij, rays[packetIndex]);
+ }
+ }
+ }
+ else if (unlikely(!intersect))
+ {
+ /* octant sorting for occlusion rays */
+ __aligned(64) unsigned int octants[8][MAX_INTERNAL_STREAM_SIZE];
+ __aligned(64) RayK<K> rays[MAX_INTERNAL_STREAM_SIZE / K];
+ __aligned(64) RayK<K>* rayPtrs[MAX_INTERNAL_STREAM_SIZE / K];
+
+ unsigned int raysInOctant[8];
+ for (unsigned int i = 0; i < 8; i++)
+ raysInOctant[i] = 0;
+ size_t inputRayID = 0;
+
+ for (;;)
+ {
+ int curOctant = -1;
+
+ /* sort rays into octants */
+ for (; inputRayID < N;)
+ {
+ const Ray& ray = rayN.getRayByIndex(inputRayID);
+
+ /* skip invalid rays */
+ if (unlikely(ray.tnear() > ray.tfar || ray.tfar < 0.0f)) { inputRayID++; continue; } // ignore invalid or already occluded rays
+#if defined(EMBREE_IGNORE_INVALID_RAYS)
+ if (unlikely(!ray.valid())) { inputRayID++; continue; }
+#endif
+
+ const unsigned int octantID = movemask(lt_mask(ray.dir,Vec3fa(0.0f)));
+
+ assert(octantID < 8);
+ octants[octantID][raysInOctant[octantID]++] = (unsigned int)inputRayID;
+ inputRayID++;
+ if (unlikely(raysInOctant[octantID] == MAX_INTERNAL_STREAM_SIZE))
+ {
+ curOctant = octantID;
+ break;
+ }
+ }
+
+ /* need to flush rays in octant? */
+ if (unlikely(curOctant == -1))
+ {
+ for (unsigned int i = 0; i < 8; i++)
+ if (raysInOctant[i]) { curOctant = i; break; }
+ }
+
+ /* all rays traced? */
+ if (unlikely(curOctant == -1))
+ break;
+
+ unsigned int* const rayIDs = &octants[curOctant][0];
+ const unsigned int numOctantRays = raysInOctant[curOctant];
+ assert(numOctantRays);
+
+ for (unsigned int j = 0; j < numOctantRays; j += K)
+ {
+ const vint<K> vi = vint<K>(int(j)) + vint<K>(step);
+ const vbool<K> valid = vi < vint<K>(int(numOctantRays));
+ const vint<K> index = *(vint<K>*)&rayIDs[j];
+ RayK<K>& ray = rays[j/K];
+ rayPtrs[j/K] = &ray;
+ ray = rayN.getRayByIndex<K>(valid, index);
+ ray.tnear() = select(valid, ray.tnear(), zero);
+ ray.tfar = select(valid, ray.tfar, neg_inf);
+ }
+
+ scene->intersectors.occludedN(rayPtrs, numOctantRays, context);
+
+ for (unsigned int j = 0; j < numOctantRays; j += K)
+ {
+ const vint<K> vi = vint<K>(int(j)) + vint<K>(step);
+ const vbool<K> valid = vi < vint<K>(int(numOctantRays));
+ const vint<K> index = *(vint<K>*)&rayIDs[j];
+ rayN.setHitByIndex<K>(valid, index, rays[j/K]);
+ }
+
+ raysInOctant[curOctant] = 0;
+ }
+ }
+ else
+ {
+ /* fallback to packets */
+ for (size_t i = 0; i < N; i += K)
+ {
+ const vint<K> vi = vint<K>(int(i)) + vint<K>(step);
+ vbool<K> valid = vi < vint<K>(int(N));
+
+ RayTypeK<K, intersect> ray = rayN.getRayByIndex<K>(valid, vi);
+ valid &= ray.tnear() <= ray.tfar;
+
+ scene->intersectors.intersect(valid, ray, context);
+
+ rayN.setHitByIndex<K>(valid, vi, ray);
+ }
+ }
+ }
+
+ template<int K, bool intersect>
+ __noinline void RayStreamFilter::filterSOA(Scene* scene, char* rayData, size_t N, size_t numPackets, size_t stride, IntersectContext* context)
+ {
+ const size_t rayDataAlignment = (size_t)rayData % (K*sizeof(float));
+ const size_t offsetAlignment = (size_t)stride % (K*sizeof(float));
+
+ /* fast path for packets with the correct width and data alignment */
+ if (likely(N == K &&
+ !rayDataAlignment &&
+ !offsetAlignment))
+ {
+ if (unlikely(context->isCoherent()))
+ {
+ __aligned(64) RayTypeK<K, intersect>* rayPtrs[MAX_INTERNAL_STREAM_SIZE / K];
+
+ size_t packetIndex = 0;
+ for (size_t i = 0; i < numPackets; i++)
+ {
+ const size_t offset = i * stride;
+ RayTypeK<K, intersect>& ray = *(RayTypeK<K, intersect>*)(rayData + offset);
+ rayPtrs[packetIndex++] = &ray;
+
+ /* trace as stream */
+ if (unlikely(packetIndex == MAX_INTERNAL_STREAM_SIZE / K))
+ {
+ const size_t size = packetIndex*K;
+ scene->intersectors.intersectN(rayPtrs, size, context);
+ packetIndex = 0;
+ }
+ }
+
+ /* flush remaining packets */
+ if (unlikely(packetIndex > 0))
+ {
+ const size_t size = packetIndex*K;
+ scene->intersectors.intersectN(rayPtrs, size, context);
+ }
+ }
+ else if (unlikely(!intersect))
+ {
+ /* octant sorting for occlusion rays */
+ RayStreamSOA rayN(rayData, K);
+
+ __aligned(64) unsigned int octants[8][MAX_INTERNAL_STREAM_SIZE];
+ __aligned(64) RayK<K> rays[MAX_INTERNAL_STREAM_SIZE / K];
+ __aligned(64) RayK<K>* rayPtrs[MAX_INTERNAL_STREAM_SIZE / K];
+
+ unsigned int raysInOctant[8];
+ for (unsigned int i = 0; i < 8; i++)
+ raysInOctant[i] = 0;
+ size_t inputRayID = 0;
+
+ for (;;)
+ {
+ int curOctant = -1;
+
+ /* sort rays into octants */
+ for (; inputRayID < N*numPackets;)
+ {
+ const size_t offset = (inputRayID / K) * stride + (inputRayID % K) * sizeof(float);
+
+ /* skip invalid rays */
+ if (unlikely(!rayN.isValidByOffset(offset))) { inputRayID++; continue; } // ignore invalid or already occluded rays
+ #if defined(EMBREE_IGNORE_INVALID_RAYS)
+ __aligned(64) Ray ray = rayN.getRayByOffset(offset);
+ if (unlikely(!ray.valid())) { inputRayID++; continue; }
+ #endif
+
+ const unsigned int octantID = (unsigned int)rayN.getOctantByOffset(offset);
+
+ assert(octantID < 8);
+ octants[octantID][raysInOctant[octantID]++] = (unsigned int)offset;
+ inputRayID++;
+ if (unlikely(raysInOctant[octantID] == MAX_INTERNAL_STREAM_SIZE))
+ {
+ curOctant = octantID;
+ break;
+ }
+ }
+
+ /* need to flush rays in octant? */
+ if (unlikely(curOctant == -1))
+ {
+ for (unsigned int i = 0; i < 8; i++)
+ if (raysInOctant[i]) { curOctant = i; break; }
+ }
+
+ /* all rays traced? */
+ if (unlikely(curOctant == -1))
+ break;
+
+ unsigned int* const rayOffsets = &octants[curOctant][0];
+ const unsigned int numOctantRays = raysInOctant[curOctant];
+ assert(numOctantRays);
+
+ for (unsigned int j = 0; j < numOctantRays; j += K)
+ {
+ const vint<K> vi = vint<K>(int(j)) + vint<K>(step);
+ const vbool<K> valid = vi < vint<K>(int(numOctantRays));
+ const vint<K> offset = *(vint<K>*)&rayOffsets[j];
+ RayK<K>& ray = rays[j/K];
+ rayPtrs[j/K] = &ray;
+ ray = rayN.getRayByOffset<K>(valid, offset);
+ ray.tnear() = select(valid, ray.tnear(), zero);
+ ray.tfar = select(valid, ray.tfar, neg_inf);
+ }
+
+ scene->intersectors.occludedN(rayPtrs, numOctantRays, context);
+
+ for (unsigned int j = 0; j < numOctantRays; j += K)
+ {
+ const vint<K> vi = vint<K>(int(j)) + vint<K>(step);
+ const vbool<K> valid = vi < vint<K>(int(numOctantRays));
+ const vint<K> offset = *(vint<K>*)&rayOffsets[j];
+ rayN.setHitByOffset(valid, offset, rays[j/K]);
+ }
+ raysInOctant[curOctant] = 0;
+ }
+ }
+ else
+ {
+ /* fallback to packets */
+ for (size_t i = 0; i < numPackets; i++)
+ {
+ const size_t offset = i * stride;
+ RayTypeK<K, intersect>& ray = *(RayTypeK<K, intersect>*)(rayData + offset);
+ const vbool<K> valid = ray.tnear() <= ray.tfar;
+
+ scene->intersectors.intersect(valid, ray, context);
+ }
+ }
+ }
+ else
+ {
+ /* fallback to packets for arbitrary packet size and alignment */
+ for (size_t i = 0; i < numPackets; i++)
+ {
+ const size_t offsetN = i * stride;
+ RayStreamSOA rayN(rayData + offsetN, N);
+
+ for (size_t j = 0; j < N; j += K)
+ {
+ const size_t offset = j * sizeof(float);
+ vbool<K> valid = (vint<K>(int(j)) + vint<K>(step)) < vint<K>(int(N));
+ RayTypeK<K, intersect> ray = rayN.getRayByOffset<K>(valid, offset);
+ valid &= ray.tnear() <= ray.tfar;
+
+ scene->intersectors.intersect(valid, ray, context);
+
+ rayN.setHitByOffset(valid, offset, ray);
+ }
+ }
+ }
+ }
+
+ template<int K, bool intersect>
+ __noinline void RayStreamFilter::filterSOP(Scene* scene, const void* _rayN, size_t N, IntersectContext* context)
+ {
+ RayStreamSOP& rayN = *(RayStreamSOP*)_rayN;
+
+ /* use fast path for coherent ray mode */
+ if (unlikely(context->isCoherent()))
+ {
+ __aligned(64) RayTypeK<K, intersect> rays[MAX_INTERNAL_STREAM_SIZE / K];
+ __aligned(64) RayTypeK<K, intersect>* rayPtrs[MAX_INTERNAL_STREAM_SIZE / K];
+
+ for (size_t i = 0; i < N; i += MAX_INTERNAL_STREAM_SIZE)
+ {
+ const size_t size = min(N - i, MAX_INTERNAL_STREAM_SIZE);
+
+ /* convert from SOP to SOA */
+ for (size_t j = 0; j < size; j += K)
+ {
+ const vint<K> vij = vint<K>(int(i+j)) + vint<K>(step);
+ const vbool<K> valid = vij < vint<K>(int(N));
+ const size_t offset = (i+j) * sizeof(float);
+ const size_t packetIndex = j / K;
+
+ RayTypeK<K, intersect> ray = rayN.getRayByOffset<K>(valid, offset);
+ ray.tnear() = select(valid, ray.tnear(), zero);
+ ray.tfar = select(valid, ray.tfar, neg_inf);
+
+ rays[packetIndex] = ray;
+ rayPtrs[packetIndex] = &rays[packetIndex]; // rayPtrs might get reordered for occludedN
+ }
+
+ /* trace stream */
+ scene->intersectors.intersectN(rayPtrs, size, context);
+
+ /* convert from SOA to SOP */
+ for (size_t j = 0; j < size; j += K)
+ {
+ const vint<K> vij = vint<K>(int(i+j)) + vint<K>(step);
+ const vbool<K> valid = vij < vint<K>(int(N));
+ const size_t offset = (i+j) * sizeof(float);
+ const size_t packetIndex = j / K;
+
+ rayN.setHitByOffset(valid, offset, rays[packetIndex]);
+ }
+ }
+ }
+ else if (unlikely(!intersect))
+ {
+ /* octant sorting for occlusion rays */
+ __aligned(64) unsigned int octants[8][MAX_INTERNAL_STREAM_SIZE];
+ __aligned(64) RayK<K> rays[MAX_INTERNAL_STREAM_SIZE / K];
+ __aligned(64) RayK<K>* rayPtrs[MAX_INTERNAL_STREAM_SIZE / K];
+
+ unsigned int raysInOctant[8];
+ for (unsigned int i = 0; i < 8; i++)
+ raysInOctant[i] = 0;
+ size_t inputRayID = 0;
+
+ for (;;)
+ {
+ int curOctant = -1;
+
+ /* sort rays into octants */
+ for (; inputRayID < N;)
+ {
+ const size_t offset = inputRayID * sizeof(float);
+ /* skip invalid rays */
+ if (unlikely(!rayN.isValidByOffset(offset))) { inputRayID++; continue; } // ignore invalid or already occluded rays
+#if defined(EMBREE_IGNORE_INVALID_RAYS)
+ __aligned(64) Ray ray = rayN.getRayByOffset(offset);
+ if (unlikely(!ray.valid())) { inputRayID++; continue; }
+#endif
+
+ const unsigned int octantID = (unsigned int)rayN.getOctantByOffset(offset);
+
+ assert(octantID < 8);
+ octants[octantID][raysInOctant[octantID]++] = (unsigned int)offset;
+ inputRayID++;
+ if (unlikely(raysInOctant[octantID] == MAX_INTERNAL_STREAM_SIZE))
+ {
+ curOctant = octantID;
+ break;
+ }
+ }
+
+ /* need to flush rays in octant? */
+ if (unlikely(curOctant == -1))
+ {
+ for (unsigned int i = 0; i < 8; i++)
+ if (raysInOctant[i]) { curOctant = i; break; }
+ }
+
+ /* all rays traced? */
+ if (unlikely(curOctant == -1))
+ break;
+
+ unsigned int* const rayOffsets = &octants[curOctant][0];
+ const unsigned int numOctantRays = raysInOctant[curOctant];
+ assert(numOctantRays);
+
+ for (unsigned int j = 0; j < numOctantRays; j += K)
+ {
+ const vint<K> vi = vint<K>(int(j)) + vint<K>(step);
+ const vbool<K> valid = vi < vint<K>(int(numOctantRays));
+ const vint<K> offset = *(vint<K>*)&rayOffsets[j];
+ RayK<K>& ray = rays[j/K];
+ rayPtrs[j/K] = &ray;
+ ray = rayN.getRayByOffset<K>(valid, offset);
+ ray.tnear() = select(valid, ray.tnear(), zero);
+ ray.tfar = select(valid, ray.tfar, neg_inf);
+ }
+
+ scene->intersectors.occludedN(rayPtrs, numOctantRays, context);
+
+ for (unsigned int j = 0; j < numOctantRays; j += K)
+ {
+ const vint<K> vi = vint<K>(int(j)) + vint<K>(step);
+ const vbool<K> valid = vi < vint<K>(int(numOctantRays));
+ const vint<K> offset = *(vint<K>*)&rayOffsets[j];
+ rayN.setHitByOffset(valid, offset, rays[j/K]);
+ }
+
+ raysInOctant[curOctant] = 0;
+ }
+ }
+ else
+ {
+ /* fallback to packets */
+ for (size_t i = 0; i < N; i += K)
+ {
+ const vint<K> vi = vint<K>(int(i)) + vint<K>(step);
+ vbool<K> valid = vi < vint<K>(int(N));
+ const size_t offset = i * sizeof(float);
+
+ RayTypeK<K, intersect> ray = rayN.getRayByOffset<K>(valid, offset);
+ valid &= ray.tnear() <= ray.tfar;
+
+ scene->intersectors.intersect(valid, ray, context);
+
+ rayN.setHitByOffset(valid, offset, ray);
+ }
+ }
+ }
+
+
+ void RayStreamFilter::intersectAOS(Scene* scene, RTCRayHit* _rayN, size_t N, size_t stride, IntersectContext* context) {
+ if (unlikely(context->isCoherent()))
+ filterAOS<VSIZEL, true>(scene, _rayN, N, stride, context);
+ else
+ filterAOS<VSIZEX, true>(scene, _rayN, N, stride, context);
+ }
+
+ void RayStreamFilter::occludedAOS(Scene* scene, RTCRay* _rayN, size_t N, size_t stride, IntersectContext* context) {
+ if (unlikely(context->isCoherent()))
+ filterAOS<VSIZEL, false>(scene, _rayN, N, stride, context);
+ else
+ filterAOS<VSIZEX, false>(scene, _rayN, N, stride, context);
+ }
+
+ void RayStreamFilter::intersectAOP(Scene* scene, RTCRayHit** _rayN, size_t N, IntersectContext* context) {
+ if (unlikely(context->isCoherent()))
+ filterAOP<VSIZEL, true>(scene, (void**)_rayN, N, context);
+ else
+ filterAOP<VSIZEX, true>(scene, (void**)_rayN, N, context);
+ }
+
+ void RayStreamFilter::occludedAOP(Scene* scene, RTCRay** _rayN, size_t N, IntersectContext* context) {
+ if (unlikely(context->isCoherent()))
+ filterAOP<VSIZEL, false>(scene, (void**)_rayN, N, context);
+ else
+ filterAOP<VSIZEX, false>(scene, (void**)_rayN, N, context);
+ }
+
+ void RayStreamFilter::intersectSOA(Scene* scene, char* rayData, size_t N, size_t numPackets, size_t stride, IntersectContext* context) {
+ if (unlikely(context->isCoherent()))
+ filterSOA<VSIZEL, true>(scene, rayData, N, numPackets, stride, context);
+ else
+ filterSOA<VSIZEX, true>(scene, rayData, N, numPackets, stride, context);
+ }
+
+ void RayStreamFilter::occludedSOA(Scene* scene, char* rayData, size_t N, size_t numPackets, size_t stride, IntersectContext* context) {
+ if (unlikely(context->isCoherent()))
+ filterSOA<VSIZEL, false>(scene, rayData, N, numPackets, stride, context);
+ else
+ filterSOA<VSIZEX, false>(scene, rayData, N, numPackets, stride, context);
+ }
+
+ void RayStreamFilter::intersectSOP(Scene* scene, const RTCRayHitNp* _rayN, size_t N, IntersectContext* context) {
+ if (unlikely(context->isCoherent()))
+ filterSOP<VSIZEL, true>(scene, _rayN, N, context);
+ else
+ filterSOP<VSIZEX, true>(scene, _rayN, N, context);
+ }
+
+ void RayStreamFilter::occludedSOP(Scene* scene, const RTCRayNp* _rayN, size_t N, IntersectContext* context) {
+ if (unlikely(context->isCoherent()))
+ filterSOP<VSIZEL, false>(scene, _rayN, N, context);
+ else
+ filterSOP<VSIZEX, false>(scene, _rayN, N, context);
+ }
+
+
+ RayStreamFilterFuncs rayStreamFilterFuncs() {
+ return RayStreamFilterFuncs(RayStreamFilter::intersectAOS, RayStreamFilter::intersectAOP, RayStreamFilter::intersectSOA, RayStreamFilter::intersectSOP,
+ RayStreamFilter::occludedAOS, RayStreamFilter::occludedAOP, RayStreamFilter::occludedSOA, RayStreamFilter::occludedSOP);
+ }
+ };
+};
diff --git a/thirdparty/embree/kernels/config.h b/thirdparty/embree/kernels/config.h
index 80a8ab2a56..2bf7e93587 100644
--- a/thirdparty/embree/kernels/config.h
+++ b/thirdparty/embree/kernels/config.h
@@ -16,7 +16,7 @@
/* #undef EMBREE_GEOMETRY_INSTANCE */
/* #undef EMBREE_GEOMETRY_GRID */
/* #undef EMBREE_GEOMETRY_POINT */
-/* #undef EMBREE_RAY_PACKETS */
+#define EMBREE_RAY_PACKETS
/* #undef EMBREE_COMPACT_POLYS */
#define EMBREE_CURVE_SELF_INTERSECTION_AVOIDANCE_FACTOR 2.0
diff --git a/thirdparty/embree/kernels/hash.h b/thirdparty/embree/kernels/hash.h
index 10f315cee7..470e15f03e 100644
--- a/thirdparty/embree/kernels/hash.h
+++ b/thirdparty/embree/kernels/hash.h
@@ -2,4 +2,4 @@
// Copyright 2009-2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
-#define RTC_HASH "7c53133eb21424f7f0ae1e25bf357e358feaf6ab"
+#define RTC_HASH "12b99393438a4cc9e478e33459eed78bec6233fd"