summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/linux_builds.yml41
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--SConstruct26
-rw-r--r--core/config/project_settings.cpp7
-rw-r--r--core/input/input.cpp23
-rw-r--r--core/input/input_map.cpp47
-rw-r--r--core/input/input_map.h1
-rw-r--r--core/io/file_access_pack.h1
-rw-r--r--core/io/resource_importer.cpp28
-rw-r--r--core/io/resource_importer.h5
-rw-r--r--core/math/color.h8
-rw-r--r--core/templates/map.h2
-rw-r--r--core/templates/thread_work_pool.h11
-rw-r--r--doc/classes/Area2D.xml4
-rw-r--r--doc/classes/Area3D.xml4
-rw-r--r--doc/classes/CapsuleMesh.xml3
-rw-r--r--doc/classes/ColorPicker.xml4
-rw-r--r--doc/classes/Image.xml3
-rw-r--r--doc/classes/LineEdit.xml76
-rw-r--r--doc/classes/MeshInstance3D.xml8
-rw-r--r--doc/classes/Node.xml2
-rw-r--r--doc/classes/PackedByteArray.xml240
-rw-r--r--doc/classes/PackedFloat32Array.xml8
-rw-r--r--doc/classes/ProjectSettings.xml2
-rw-r--r--doc/classes/RenderingServer.xml4
-rw-r--r--doc/classes/VisualShader.xml4
-rw-r--r--editor/animation_track_editor.cpp2
-rw-r--r--editor/code_editor.cpp6
-rw-r--r--editor/debugger/script_editor_debugger.cpp77
-rw-r--r--editor/debugger/script_editor_debugger.h5
-rw-r--r--editor/editor_about.cpp7
-rw-r--r--editor/editor_about.h4
-rw-r--r--editor/editor_file_system.cpp64
-rw-r--r--editor/editor_file_system.h16
-rw-r--r--editor/editor_help.cpp2
-rw-r--r--editor/editor_themes.cpp2
-rw-r--r--editor/import/resource_importer_scene.cpp2
-rw-r--r--editor/import/resource_importer_scene.h2
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp2
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp4
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp12
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/shader_editor_plugin.cpp4
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp97
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h14
-rw-r--r--editor/project_manager.cpp43
-rw-r--r--editor/project_manager.h4
-rw-r--r--editor/rename_dialog.cpp2
-rw-r--r--editor/scene_tree_dock.cpp6
-rw-r--r--editor/script_create_dialog.cpp34
-rw-r--r--editor/script_create_dialog.h2
-rw-r--r--main/main.cpp7
-rw-r--r--misc/dist/html/editor.html65
-rw-r--r--misc/dist/osx_template.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json2
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json2
-rwxr-xr-xmisc/scripts/check_ci_log.py65
-rw-r--r--modules/etcpak/SCsub16
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp (renamed from modules/etcpak/image_etcpak.cpp)148
-rw-r--r--modules/etcpak/image_compress_etcpak.h (renamed from modules/etcpak/image_etcpak.h)17
-rw-r--r--modules/etcpak/register_types.cpp2
-rw-r--r--modules/etcpak/register_types.h5
-rw-r--r--modules/fbx/data/fbx_material.cpp4
-rw-r--r--modules/fbx/data/fbx_mesh_data.cpp35
-rw-r--r--modules/fbx/data/fbx_mesh_data.h18
-rw-r--r--modules/fbx/data/pivot_transform.cpp2
-rw-r--r--modules/fbx/editor_scene_importer_fbx.cpp46
-rw-r--r--modules/fbx/fbx_parser/FBXAnimation.cpp19
-rw-r--r--modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp41
-rw-r--r--modules/fbx/fbx_parser/FBXDeformer.cpp8
-rw-r--r--modules/fbx/fbx_parser/FBXDocument.cpp99
-rw-r--r--modules/fbx/fbx_parser/FBXDocument.h125
-rw-r--r--modules/fbx/fbx_parser/FBXDocumentUtil.cpp31
-rw-r--r--modules/fbx/fbx_parser/FBXDocumentUtil.h7
-rw-r--r--modules/fbx/fbx_parser/FBXMaterial.cpp25
-rw-r--r--modules/fbx/fbx_parser/FBXModel.cpp5
-rw-r--r--modules/fbx/fbx_parser/FBXNodeAttribute.cpp11
-rw-r--r--modules/fbx/fbx_parser/FBXParser.cpp36
-rw-r--r--modules/fbx/fbx_parser/FBXParser.h7
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.cpp41
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.h28
-rw-r--r--modules/fbx/fbx_parser/FBXTokenizer.cpp4
-rw-r--r--modules/fbx/fbx_parser/FBXTokenizer.h4
-rw-r--r--modules/gdscript/gdscript.cpp8
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp2
-rw-r--r--modules/gdscript/gdscript_parser.cpp3
-rw-r--r--modules/gdscript/register_types.cpp8
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp584
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.h126
-rw-r--r--modules/gdscript/tests/gdscript_test_runner_suite.h53
-rw-r--r--modules/gdscript/tests/scripts/.gitignore2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/missing-argument.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/missing-argument.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/missing-colon.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/missing-colon.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.out3
-rw-r--r--modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.out2
-rw-r--r--modules/gdscript/tests/scripts/parser-features/variable-declaration.gd12
-rw-r--r--modules/gdscript/tests/scripts/parser-features/variable-declaration.out7
-rw-r--r--modules/gdscript/tests/scripts/parser-warnings/unused-variable.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser-warnings/unused-variable.out5
-rw-r--r--modules/gdscript/tests/scripts/project.godot10
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp76
-rw-r--r--modules/gdscript/tests/test_gdscript.h8
-rw-r--r--modules/gltf/gltf_document.cpp5
-rw-r--r--modules/lightmapper_rd/lm_blendseams.glsl4
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl2
-rw-r--r--modules/lightmapper_rd/lm_raster.glsl4
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.h2
-rw-r--r--modules/squish/image_decompress_squish.cpp (renamed from modules/squish/image_compress_squish.cpp)4
-rw-r--r--modules/squish/image_decompress_squish.h (renamed from modules/squish/image_compress_squish.h)8
-rw-r--r--modules/squish/register_types.cpp3
-rw-r--r--modules/visual_script/visual_script_editor.cpp8
-rw-r--r--platform/android/export/export.cpp38
-rw-r--r--platform/android/java/app/config.gradle2
-rw-r--r--platform/android/java/build.gradle36
-rw-r--r--platform/android/java/gradle.properties2
-rw-r--r--platform/linuxbsd/detect.py28
-rw-r--r--platform/osx/detect.py20
-rw-r--r--platform/server/detect.py28
-rw-r--r--scene/2d/area_2d.cpp9
-rw-r--r--scene/3d/area_3d.cpp11
-rw-r--r--scene/3d/baked_lightmap.cpp2
-rw-r--r--scene/3d/gi_probe.cpp2
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp2
-rw-r--r--scene/3d/mesh_instance_3d.cpp48
-rw-r--r--scene/3d/mesh_instance_3d.h8
-rw-r--r--scene/3d/skeleton_3d.cpp1
-rw-r--r--scene/3d/skeleton_ik_3d.cpp4
-rw-r--r--scene/3d/soft_body_3d.cpp4
-rw-r--r--scene/gui/line_edit.cpp335
-rw-r--r--scene/gui/line_edit.h48
-rw-r--r--scene/gui/text_edit.cpp26
-rw-r--r--scene/resources/default_theme/default_theme.cpp2
-rw-r--r--scene/resources/particles_material.cpp257
-rw-r--r--scene/resources/sky_material.cpp6
-rw-r--r--scene/resources/visual_shader.cpp80
-rw-r--r--scene/resources/visual_shader.h1
-rw-r--r--scene/resources/world_3d.cpp2
-rw-r--r--scene/scene_string_names.cpp4
-rw-r--r--scene/scene_string_names.h4
-rw-r--r--servers/physics_2d/body_2d_sw.cpp2
-rw-r--r--servers/physics_2d/body_2d_sw.h8
-rw-r--r--servers/physics_2d/broad_phase_2d_hash_grid.cpp79
-rw-r--r--servers/physics_2d/broad_phase_2d_hash_grid.h11
-rw-r--r--servers/physics_2d/constraint_2d_sw.h8
-rw-r--r--servers/physics_2d/joints_2d_sw.cpp13
-rw-r--r--servers/physics_2d/step_2d_sw.cpp191
-rw-r--r--servers/physics_2d/step_2d_sw.h13
-rw-r--r--servers/physics_3d/body_3d_sw.cpp2
-rw-r--r--servers/physics_3d/body_3d_sw.h8
-rw-r--r--servers/physics_3d/body_pair_3d_sw.cpp10
-rw-r--r--servers/physics_3d/constraint_3d_sw.h8
-rw-r--r--servers/physics_3d/joints/cone_twist_joint_3d_sw.cpp4
-rw-r--r--servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp4
-rw-r--r--servers/physics_3d/joints/hinge_joint_3d_sw.cpp4
-rw-r--r--servers/physics_3d/joints/pin_joint_3d_sw.cpp4
-rw-r--r--servers/physics_3d/joints/slider_joint_3d_sw.cpp4
-rw-r--r--servers/physics_3d/step_3d_sw.cpp194
-rw-r--r--servers/physics_3d/step_3d_sw.h13
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp5
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp5
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp7
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp7
-rw-r--r--servers/rendering/renderer_rd/shader_compiler_rd.cpp83
-rw-r--r--servers/rendering/renderer_rd/shader_compiler_rd.h23
-rw-r--r--servers/rendering/renderer_rd/shader_rd.cpp402
-rw-r--r--servers/rendering/renderer_rd/shader_rd.h60
-rw-r--r--servers/rendering/renderer_rd/shaders/bokeh_dof.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl58
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_sdf.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/cluster_debug.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/cluster_render.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/cluster_store.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/copy.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/copy_to_fb.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/cube_to_dp.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/cubemap_filter.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/gi.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/giprobe.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/giprobe_debug.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/giprobe_sdf.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/giprobe_write.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/luminance_reduce.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/particles.glsl236
-rw-r--r--servers/rendering/renderer_rd/shaders/particles_copy.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/resolve.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/roughness_limiter.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl54
-rw-r--r--servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/screen_space_reflection_filter.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/screen_space_reflection_scale.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/sdfgi_fields.glsl182
-rw-r--r--servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/skeleton.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/sky.glsl32
-rw-r--r--servers/rendering/renderer_rd/shaders/sort.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/specular_merge.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/ssao.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/ssao_blur.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/ssao_downsample.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/ssao_interleave.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/tonemap.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/volumetric_fog.glsl2
-rw-r--r--servers/rendering/renderer_scene.h2
-rw-r--r--servers/rendering/renderer_scene_cull.cpp2
-rw-r--r--servers/rendering/renderer_scene_cull.h2
-rw-r--r--servers/rendering/rendering_server_default.h2
-rw-r--r--servers/rendering/shader_language.cpp46
-rw-r--r--servers/rendering/shader_language.h20
-rw-r--r--servers/rendering/shader_types.cpp97
-rw-r--r--servers/rendering_server.cpp2
-rw-r--r--servers/rendering_server.h2
-rw-r--r--tests/test_shader_lang.cpp2
-rw-r--r--thirdparty/README.md14
-rw-r--r--thirdparty/etcpak/Bitmap.cpp216
-rw-r--r--thirdparty/etcpak/Bitmap.hpp50
-rw-r--r--thirdparty/etcpak/BitmapDownsampled.cpp86
-rw-r--r--thirdparty/etcpak/BitmapDownsampled.hpp13
-rw-r--r--thirdparty/etcpak/BlockData.cpp1296
-rw-r--r--thirdparty/etcpak/BlockData.hpp56
-rw-r--r--thirdparty/etcpak/ColorSpace.cpp114
-rw-r--r--thirdparty/etcpak/ColorSpace.hpp36
-rw-r--r--thirdparty/etcpak/DataProvider.cpp77
-rw-r--r--thirdparty/etcpak/DataProvider.hpp41
-rw-r--r--thirdparty/etcpak/Debug.cpp31
-rw-r--r--thirdparty/etcpak/Debug.hpp27
-rw-r--r--thirdparty/etcpak/Error.cpp48
-rw-r--r--thirdparty/etcpak/Error.hpp9
-rw-r--r--thirdparty/etcpak/MipMap.hpp11
-rw-r--r--thirdparty/etcpak/Semaphore.hpp46
-rw-r--r--thirdparty/etcpak/System.cpp68
-rw-r--r--thirdparty/etcpak/System.hpp15
-rw-r--r--thirdparty/etcpak/TaskDispatch.cpp115
-rw-r--r--thirdparty/etcpak/TaskDispatch.hpp34
-rw-r--r--thirdparty/etcpak/Timing.cpp8
-rw-r--r--thirdparty/etcpak/Timing.hpp8
-rw-r--r--thirdparty/etcpak/lz4/lz4.c1516
-rw-r--r--thirdparty/etcpak/lz4/lz4.h360
-rw-r--r--thirdparty/etcpak/mmap.cpp38
-rw-r--r--thirdparty/etcpak/mmap.hpp19
-rw-r--r--thirdparty/etcpak/patches/libpng-unbundle.patch13
-rw-r--r--thirdparty/etcpak/patches/llvm-c++11-narrowing-errors.patch64
-rw-r--r--thirdparty/etcpak/patches/windows-mingw-fixes.patch63
-rw-r--r--thirdparty/meshoptimizer/LICENSE.md2
-rw-r--r--thirdparty/meshoptimizer/clusterizer.cpp616
-rw-r--r--thirdparty/meshoptimizer/indexgenerator.cpp206
-rw-r--r--thirdparty/meshoptimizer/meshoptimizer.h104
-rw-r--r--thirdparty/meshoptimizer/simplifier.cpp2
-rw-r--r--thirdparty/meshoptimizer/vertexcodec.cpp14
274 files changed, 4358 insertions, 7010 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 15a7be9c4f..7b144e6e43 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -76,9 +76,9 @@ jobs:
path: bin/*
retention-days: 14
- linux-editor-sanitizers-mono:
+ linux-editor-sanitizers:
runs-on: "ubuntu-20.04"
- name: Editor w/ Mono and sanitizers (target=debug, tools=yes, tests=yes, use_asan=yes, use_ubsan=yes)
+ name: Editor and sanitizers (target=debug, tools=yes, tests=yes, use_asan=yes, use_ubsan=yes)
steps:
- uses: actions/checkout@v2
@@ -94,7 +94,8 @@ jobs:
- 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 libudev-dev libxi-dev libxrandr-dev yasm
+ libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-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
@@ -126,17 +127,47 @@ jobs:
scons --version
# We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
+ # [Workaround] SwiftShader doesn't support tesselation, so we skip Godot check about it
- name: Compilation
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: |
- scons tools=yes tests=yes target=debug module_mono_enabled=yes mono_glue=no use_asan=yes use_ubsan=yes
+ sed -i "s|ERR_FAIL_COND_V(p_rasterization_state.patch_control_points|//ERR_FAIL_COND_V(p_rasterization_state.patch_control_points|" drivers/vulkan/rendering_device_vulkan.cpp
+ 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.mono --test
+ ./bin/godot.linuxbsd.tools.64s --test
+
+ # Download, unzip and setup SwiftShader library [d4550ab8d3f]
+ - name: Download SwiftShader
+ run: |
+ wget https://github.com/qarmin/gtk_library_store/releases/download/3.24.0/swiftshader.zip
+ unzip swiftshader.zip
+ rm swiftshader.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
+ run: |
+ wget https://github.com/qarmin/RegressionTestProject/archive/4.0.zip
+ unzip 4.0.zip
+ mv "RegressionTestProject-4.0" "test_project"
+
+ # Editor is quite complicated piece of software, so it is easy to introduce bug here
+ - name: Open and close editor
+ 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
+ misc/scripts/check_ci_log.py sanitizers_log.txt
+
+ # Run test project
+ - name: Run project
+ 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
+ misc/scripts/check_ci_log.py sanitizers_log.txt
linux-template-mono:
runs-on: "ubuntu-20.04"
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 653ebd46cd..078aac1f13 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -261,7 +261,7 @@ License: Apache-2.0
Files: ./thirdparty/meshoptimizer/
Comment: meshoptimizer
-Copyright: 2016-2020, Arseny Kapoulkine
+Copyright: 2016-2021, Arseny Kapoulkine
License: Expat
Files: ./thirdparty/minimp3/
diff --git a/SConstruct b/SConstruct
index 3edf81129b..2d9802f293 100644
--- a/SConstruct
+++ b/SConstruct
@@ -137,6 +137,7 @@ opts.Add("extra_suffix", "Custom extra suffix added to the base filename of all
opts.Add(BoolVariable("vsproj", "Generate a Visual Studio solution", False))
opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False))
opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False))
+opts.Add(BoolVariable("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True))
opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", False))
opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "")
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
@@ -259,16 +260,21 @@ for path in module_search_paths:
# Add module options.
for name, path in modules_detected.items():
- enabled = True
- sys.path.insert(0, path)
- import config
-
- try:
- enabled = config.is_enabled()
- except AttributeError:
- pass
- sys.path.remove(path)
- sys.modules.pop("config")
+ if env_base["modules_enabled_by_default"]:
+ enabled = True
+
+ sys.path.insert(0, path)
+ import config
+
+ try:
+ enabled = config.is_enabled()
+ except AttributeError:
+ pass
+ sys.path.remove(path)
+ sys.modules.pop("config")
+ else:
+ enabled = False
+
opts.Add(BoolVariable("module_" + name + "_enabled", "Enable module '%s'" % (name,), enabled))
methods.write_modules(modules_detected)
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index f87dc6704e..25dd408dce 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -467,16 +467,17 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
d->change_dir(p_path);
String current_dir = d->get_current_dir();
- String candidate = current_dir;
bool found = false;
Error err;
while (true) {
+ // Set the resource path early so things can be resolved when loading.
+ resource_path = current_dir;
+ resource_path = resource_path.replace("\\", "/"); // Windows path to Unix path just in case.
err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary"));
if (err == OK) {
// Optional, we don't mind if it fails.
_load_settings_text(current_dir.plus_file("override.cfg"));
- candidate = current_dir;
found = true;
break;
}
@@ -493,8 +494,6 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
}
}
- resource_path = candidate;
- resource_path = resource_path.replace("\\", "/"); // Windows path to Unix path just in case.
memdelete(d);
if (!found) {
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 627944210f..2304c05bf8 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -1329,9 +1329,10 @@ void Input::add_joy_mapping(String p_mapping, bool p_update_existing) {
if (p_update_existing) {
Vector<String> entry = p_mapping.split(",");
String uid = entry[0];
- for (int i = 0; i < joy_names.size(); i++) {
- if (uid == joy_names[i].uid) {
- joy_names[i].mapping = map_db.size() - 1;
+ for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) {
+ Joypad &joy = E->get();
+ if (joy.uid == uid) {
+ joy.mapping = map_db.size() - 1;
}
}
}
@@ -1343,9 +1344,10 @@ void Input::remove_joy_mapping(String p_guid) {
map_db.remove(i);
}
}
- for (int i = 0; i < joy_names.size(); i++) {
- if (joy_names[i].uid == p_guid) {
- joy_names[i].mapping = -1;
+ for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) {
+ Joypad &joy = E->get();
+ if (joy.uid == p_guid) {
+ joy.mapping = -1;
}
}
}
@@ -1361,8 +1363,13 @@ void Input::set_fallback_mapping(String p_guid) {
//platforms that use the remapping system can override and call to these ones
bool Input::is_joy_known(int p_device) {
- int mapping = joy_names[p_device].mapping;
- return mapping != -1 ? (mapping != fallback_mapping) : false;
+ if (joy_names.has(p_device)) {
+ int mapping = joy_names[p_device].mapping;
+ if (mapping != -1 && mapping != fallback_mapping) {
+ return true;
+ }
+ }
+ return false;
}
String Input::get_joy_guid(int p_device) const {
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 7d85fd6492..aab4e6593c 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -54,8 +54,36 @@ void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings);
}
+/**
+ * Returns an nonexistent action error message with a suggestion of the closest
+ * matching action name (if possible).
+ */
+String InputMap::_suggest_actions(const StringName &p_action) const {
+ List<StringName> actions = get_actions();
+ StringName closest_action;
+ float closest_similarity = 0.0;
+
+ // Find the most action with the most similar name.
+ for (List<StringName>::Element *E = actions.front(); E; E = E->next()) {
+ const float similarity = String(E->get()).similarity(p_action);
+
+ if (similarity > closest_similarity) {
+ closest_action = E->get();
+ closest_similarity = similarity;
+ }
+ }
+
+ String error_message = vformat("The InputMap action \"%s\" doesn't exist.", p_action);
+
+ if (closest_similarity >= 0.4) {
+ // Only include a suggestion in the error message if it's similar enough.
+ error_message += vformat(" Did you mean \"%s\"?", closest_action);
+ }
+ return error_message;
+}
+
void InputMap::add_action(const StringName &p_action, float p_deadzone) {
- ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action \"" + String(p_action) + "\".");
input_map[p_action] = Action();
static int last_id = 1;
input_map[p_action].id = last_id;
@@ -64,7 +92,8 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) {
}
void InputMap::erase_action(const StringName &p_action) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
+
input_map.erase(p_action);
}
@@ -122,20 +151,20 @@ bool InputMap::has_action(const StringName &p_action) const {
}
float InputMap::action_get_deadzone(const StringName &p_action) {
- ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, _suggest_actions(p_action));
return input_map[p_action].deadzone;
}
void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
input_map[p_action].deadzone = p_deadzone;
}
void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object.");
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
if (_find_event(input_map[p_action], p_event, true)) {
return; // Already addded.
}
@@ -144,12 +173,12 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent
}
bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, _suggest_actions(p_action));
return (_find_event(input_map[p_action], p_event, true) != nullptr);
}
void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true);
if (E) {
@@ -161,7 +190,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve
}
void InputMap::action_erase_events(const StringName &p_action) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
input_map[p_action].inputs.clear();
}
@@ -193,7 +222,7 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const {
OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action);
- ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_V_MSG(!E, false, _suggest_actions(p_action));
Ref<InputEventAction> input_event_action = p_event;
if (input_event_action.is_valid()) {
diff --git a/core/input/input_map.h b/core/input/input_map.h
index 99c71e1e53..0e0567464a 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -61,6 +61,7 @@ private:
Array _action_get_events(const StringName &p_action);
Array _get_actions();
+ String _suggest_actions(const StringName &p_action) const;
protected:
static void _bind_methods();
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 343adbe592..955108f455 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -36,6 +36,7 @@
#include "core/string/print_string.h"
#include "core/templates/list.h"
#include "core/templates/map.h"
+#include "core/templates/set.h"
// Godot's packed file magic header ("GDPC" in ASCII).
#define PACK_HEADER_MAGIC 0x43504447
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index 5ca0eb884a..b503655edd 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -192,6 +192,34 @@ bool ResourceFormatImporter::recognize_path(const String &p_path, const String &
return FileAccess::exists(p_path + ".import");
}
+Error ResourceFormatImporter::get_import_order_threads_and_importer(const String &p_path, int &r_order, bool &r_can_threads, String &r_importer) const {
+ r_order = 0;
+ r_importer = "";
+
+ r_can_threads = false;
+ Ref<ResourceImporter> importer;
+
+ if (FileAccess::exists(p_path + ".import")) {
+ PathAndType pat;
+ Error err = _get_path_and_type(p_path, pat);
+
+ if (err == OK) {
+ importer = get_importer_by_name(pat.importer);
+ }
+ } else {
+ importer = get_importer_by_extension(p_path.get_extension().to_lower());
+ }
+
+ if (importer.is_valid()) {
+ r_order = importer->get_import_order();
+ r_importer = importer->get_importer_name();
+ r_can_threads = importer->can_import_threaded();
+ return OK;
+ } else {
+ return ERR_INVALID_PARAMETER;
+ }
+}
+
int ResourceFormatImporter::get_import_order(const String &p_path) const {
Ref<ResourceImporter> importer;
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index eeb486073e..a14d6ba52c 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -72,6 +72,8 @@ public:
virtual int get_import_order(const String &p_path) const;
+ Error get_import_order_threads_and_importer(const String &p_path, int &r_order, bool &r_can_threads, String &r_importer) const;
+
String get_internal_resource_path(const String &p_path) const;
void get_internal_resource_path_list(const String &p_path, List<String> *r_paths);
@@ -126,6 +128,9 @@ public:
virtual String get_option_group_file() const { return String(); }
virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0;
+ virtual bool can_import_threaded() const { return true; }
+ virtual void import_threaded_begin() {}
+ virtual void import_threaded_end() {}
virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant>> &p_source_file_options, const Map<String, String> &p_base_paths) { return ERR_UNAVAILABLE; }
virtual bool are_import_settings_valid(const String &p_path) const { return true; }
diff --git a/core/math/color.h b/core/math/color.h
index 5eb8b1119a..e404d80c8a 100644
--- a/core/math/color.h
+++ b/core/math/color.h
@@ -197,13 +197,13 @@ struct Color {
// For the binder.
_FORCE_INLINE_ void set_r8(int32_t r8) { r = (CLAMP(r8, 0, 255) / 255.0); }
- _FORCE_INLINE_ int32_t get_r8() const { return int32_t(CLAMP(r * 255.0, 0.0, 255.0)); }
+ _FORCE_INLINE_ int32_t get_r8() const { return int32_t(CLAMP(Math::round(r * 255.0f), 0.0f, 255.0f)); }
_FORCE_INLINE_ void set_g8(int32_t g8) { g = (CLAMP(g8, 0, 255) / 255.0); }
- _FORCE_INLINE_ int32_t get_g8() const { return int32_t(CLAMP(g * 255.0, 0.0, 255.0)); }
+ _FORCE_INLINE_ int32_t get_g8() const { return int32_t(CLAMP(Math::round(g * 255.0f), 0.0f, 255.0f)); }
_FORCE_INLINE_ void set_b8(int32_t b8) { b = (CLAMP(b8, 0, 255) / 255.0); }
- _FORCE_INLINE_ int32_t get_b8() const { return int32_t(CLAMP(b * 255.0, 0.0, 255.0)); }
+ _FORCE_INLINE_ int32_t get_b8() const { return int32_t(CLAMP(Math::round(b * 255.0f), 0.0f, 255.0f)); }
_FORCE_INLINE_ void set_a8(int32_t a8) { a = (CLAMP(a8, 0, 255) / 255.0); }
- _FORCE_INLINE_ int32_t get_a8() const { return int32_t(CLAMP(a * 255.0, 0.0, 255.0)); }
+ _FORCE_INLINE_ int32_t get_a8() const { return int32_t(CLAMP(Math::round(a * 255.0f), 0.0f, 255.0f)); }
_FORCE_INLINE_ void set_h(float p_h) { set_hsv(p_h, get_s(), get_v()); }
_FORCE_INLINE_ void set_s(float p_s) { set_hsv(get_h(), p_s, get_v()); }
diff --git a/core/templates/map.h b/core/templates/map.h
index 51a237472d..7dfee13d2c 100644
--- a/core/templates/map.h
+++ b/core/templates/map.h
@@ -32,7 +32,7 @@
#define MAP_H
#include "core/error/error_macros.h"
-#include "core/templates/set.h"
+#include "core/os/memory.h"
// based on the very nice implementation of rb-trees by:
// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
diff --git a/core/templates/thread_work_pool.h b/core/templates/thread_work_pool.h
index 19ab1dda3a..9f7a692cc5 100644
--- a/core/templates/thread_work_pool.h
+++ b/core/templates/thread_work_pool.h
@@ -83,7 +83,7 @@ public:
ERR_FAIL_COND(!threads); //never initialized
ERR_FAIL_COND(current_work != nullptr);
- index.store(0);
+ index.store(0, std::memory_order_release);
Work<C, M, U> *w = memnew((Work<C, M, U>));
w->instance = p_instance;
@@ -104,8 +104,15 @@ public:
return current_work != nullptr;
}
+ bool is_done_dispatching() const {
+ ERR_FAIL_COND_V(current_work == nullptr, false);
+ return index.load(std::memory_order_acquire) >= current_work->max_elements;
+ }
+
uint32_t get_work_index() const {
- return index;
+ ERR_FAIL_COND_V(current_work == nullptr, 0);
+ uint32_t idx = index.load(std::memory_order_acquire);
+ return MIN(idx, current_work->max_elements);
}
void end_work() {
diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml
index 9711a2a35b..ed11d26271 100644
--- a/doc/classes/Area2D.xml
+++ b/doc/classes/Area2D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Area2D" inherits="CollisionObject2D" version="4.0">
<brief_description>
- 2D area for detection and 2D physics influence.
+ 2D area for detection and physics and audio influence.
</brief_description>
<description>
- 2D area that detects [CollisionObject2D] nodes overlapping, entering, or exiting. Can also alter or override local physics parameters (gravity, damping).
+ 2D area that detects [CollisionObject2D] nodes overlapping, entering, or exiting. Can also alter or override local physics parameters (gravity, damping) and route audio to a custom audio bus.
</description>
<tutorials>
<link title="Using Area2D">https://docs.godotengine.org/en/latest/tutorials/physics/using_area_2d.html</link>
diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml
index 4271769155..206a2a61b4 100644
--- a/doc/classes/Area3D.xml
+++ b/doc/classes/Area3D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Area3D" inherits="CollisionObject3D" version="4.0">
<brief_description>
- General-purpose area node for detection and 3D physics influence.
+ 3D area for detection and physics and audio influence.
</brief_description>
<description>
- 3D area that detects [CollisionObject3D] nodes overlapping, entering, or exiting. Can also alter or override local physics parameters (gravity, damping).
+ 3D area that detects [CollisionObject3D] nodes overlapping, entering, or exiting. Can also alter or override local physics parameters (gravity, damping) and route audio to custom audio buses.
</description>
<tutorials>
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
diff --git a/doc/classes/CapsuleMesh.xml b/doc/classes/CapsuleMesh.xml
index fab11d44cc..031abd0112 100644
--- a/doc/classes/CapsuleMesh.xml
+++ b/doc/classes/CapsuleMesh.xml
@@ -12,7 +12,8 @@
</methods>
<members>
<member name="mid_height" type="float" setter="set_mid_height" getter="get_mid_height" default="1.0">
- Height of the capsule mesh from the center point.
+ Height of the middle cylindrical part of the capsule (without the hemispherical ends).
+ [b]Note:[/b] The capsule's total height is equal to [member mid_height] + 2 * [member radius].
</member>
<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" default="64">
Number of radial segments on the capsule mesh.
diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml
index 83223bb645..fddfd27573 100644
--- a/doc/classes/ColorPicker.xml
+++ b/doc/classes/ColorPicker.xml
@@ -51,7 +51,7 @@
If [code]true[/code], allows editing the color with Hue/Saturation/Value sliders.
[b]Note:[/b] Cannot be enabled if raw mode is on.
</member>
- <member name="picker_shape" type="int" setter="set_picker_shape" getter="get_picker_shape" default="0">
+ <member name="picker_shape" type="int" setter="set_picker_shape" getter="get_picker_shape" enum="ColorPicker.PickerShapeType" default="0">
The shape of the color space view. See [enum PickerShapeType].
</member>
<member name="presets_enabled" type="bool" setter="set_presets_enabled" getter="are_presets_enabled" default="true">
@@ -122,6 +122,8 @@
<theme_item name="overbright_indicator" type="Texture2D">
The indicator used to signalize that the color value is outside the 0-1 range.
</theme_item>
+ <theme_item name="picker_cursor" type="Texture2D">
+ </theme_item>
<theme_item name="preset_bg" type="Texture2D">
</theme_item>
<theme_item name="screen_picker" type="Texture2D">
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 9d87c9bf9a..91a07f66e0 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -186,7 +186,8 @@
<return type="int" enum="Error">
</return>
<description>
- Decompresses the image if it is compressed. Returns an error if decompress function is not available.
+ Decompresses the image if it is VRAM compressed in a supported format. Returns [constant OK] if the format is supported, otherwise [constant ERR_UNAVAILABLE].
+ [b]Note:[/b] The following formats can be decompressed: DXT, RGTC, BPTC, PVRTC1. The formats ETC1 and ETC2 are not supported.
</description>
</method>
<method name="detect_alpha" qualifiers="const">
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index 360f5c451e..7adf19632e 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -12,34 +12,25 @@
- [kbd]Ctrl + Z[/kbd]: Undo
- [kbd]Ctrl + ~[/kbd]: Swap input direction.
- [kbd]Ctrl + Shift + Z[/kbd]: Redo
- - [kbd]Ctrl + U[/kbd]: Delete text from the cursor position to the beginning of the line
- - [kbd]Ctrl + K[/kbd]: Delete text from the cursor position to the end of the line
+ - [kbd]Ctrl + U[/kbd]: Delete text from the caret position to the beginning of the line
+ - [kbd]Ctrl + K[/kbd]: Delete text from the caret position to the end of the line
- [kbd]Ctrl + A[/kbd]: Select all text
- - [kbd]Up Arrow[/kbd]/[kbd]Down Arrow[/kbd]: Move the cursor to the beginning/end of the line
+ - [kbd]Up Arrow[/kbd]/[kbd]Down Arrow[/kbd]: Move the caret to the beginning/end of the line
On macOS, some extra keyboard shortcuts are available:
- - [kbd]Ctrl + F[/kbd]: Same as [kbd]Right Arrow[/kbd], move the cursor one character right
- - [kbd]Ctrl + B[/kbd]: Same as [kbd]Left Arrow[/kbd], move the cursor one character left
- - [kbd]Ctrl + P[/kbd]: Same as [kbd]Up Arrow[/kbd], move the cursor to the previous line
- - [kbd]Ctrl + N[/kbd]: Same as [kbd]Down Arrow[/kbd], move the cursor to the next line
- - [kbd]Ctrl + D[/kbd]: Same as [kbd]Delete[/kbd], delete the character on the right side of cursor
- - [kbd]Ctrl + H[/kbd]: Same as [kbd]Backspace[/kbd], delete the character on the left side of the cursor
- - [kbd]Ctrl + A[/kbd]: Same as [kbd]Home[/kbd], move the cursor to the beginning of the line
- - [kbd]Ctrl + E[/kbd]: Same as [kbd]End[/kbd], move the cursor to the end of the line
- - [kbd]Cmd + Left Arrow[/kbd]: Same as [kbd]Home[/kbd], move the cursor to the beginning of the line
- - [kbd]Cmd + Right Arrow[/kbd]: Same as [kbd]End[/kbd], move the cursor to the end of the line
+ - [kbd]Ctrl + F[/kbd]: Same as [kbd]Right Arrow[/kbd], move the caret one character right
+ - [kbd]Ctrl + B[/kbd]: Same as [kbd]Left Arrow[/kbd], move the caret one character left
+ - [kbd]Ctrl + P[/kbd]: Same as [kbd]Up Arrow[/kbd], move the caret to the previous line
+ - [kbd]Ctrl + N[/kbd]: Same as [kbd]Down Arrow[/kbd], move the caret to the next line
+ - [kbd]Ctrl + D[/kbd]: Same as [kbd]Delete[/kbd], delete the character on the right side of caret
+ - [kbd]Ctrl + H[/kbd]: Same as [kbd]Backspace[/kbd], delete the character on the left side of the caret
+ - [kbd]Ctrl + A[/kbd]: Same as [kbd]Home[/kbd], move the caret to the beginning of the line
+ - [kbd]Ctrl + E[/kbd]: Same as [kbd]End[/kbd], move the caret to the end of the line
+ - [kbd]Cmd + Left Arrow[/kbd]: Same as [kbd]Home[/kbd], move the caret to the beginning of the line
+ - [kbd]Cmd + Right Arrow[/kbd]: Same as [kbd]End[/kbd], move the caret to the end of the line
</description>
<tutorials>
</tutorials>
<methods>
- <method name="append_at_cursor">
- <return type="void">
- </return>
- <argument index="0" name="text" type="String">
- </argument>
- <description>
- Adds [code]text[/code] after the cursor. If the resulting value is longer than [member max_length], nothing happens.
- </description>
- </method>
<method name="clear">
<return type="void">
</return>
@@ -54,11 +45,11 @@
Removes all OpenType features.
</description>
</method>
- <method name="delete_char_at_cursor">
+ <method name="delete_char_at_caret">
<return type="void">
</return>
<description>
- Deletes one character at the cursor's current position (equivalent to pressing [kbd]Delete[/kbd]).
+ Deletes one character at the caret's current position (equivalent to pressing [kbd]Delete[/kbd]).
</description>
</method>
<method name="delete_text">
@@ -99,7 +90,16 @@
<return type="int">
</return>
<description>
- Returns the scroll offset due to [member caret_position], as a number of characters.
+ Returns the scroll offset due to [member caret_column], as a number of characters.
+ </description>
+ </method>
+ <method name="insert_text_at_caret">
+ <return type="void">
+ </return>
+ <argument index="0" name="text" type="String">
+ </argument>
+ <description>
+ Inserts [code]text[/code] at the caret. If the resulting value is longer than [member max_length], nothing happens.
</description>
</method>
<method name="menu_option">
@@ -159,21 +159,21 @@
<member name="align" type="int" setter="set_align" getter="get_align" enum="LineEdit.Align" default="0">
Text alignment as defined in the [enum Align] enum.
</member>
- <member name="caret_blink" type="bool" setter="cursor_set_blink_enabled" getter="cursor_get_blink_enabled" default="false">
- If [code]true[/code], the caret (visual cursor) blinks.
+ <member name="caret_blink" type="bool" setter="set_caret_blink_enabled" getter="is_caret_blink_enabled" default="false">
+ If [code]true[/code], the caret (text cursor) blinks.
</member>
- <member name="caret_blink_speed" type="float" setter="cursor_set_blink_speed" getter="cursor_get_blink_speed" default="0.65">
+ <member name="caret_blink_speed" type="float" setter="set_caret_blink_speed" getter="get_caret_blink_speed" default="0.65">
Duration (in seconds) of a caret's blinking cycle.
</member>
- <member name="caret_force_displayed" type="bool" setter="cursor_set_force_displayed" getter="cursor_get_force_displayed" default="false">
+ <member name="caret_column" type="int" setter="set_caret_column" getter="get_caret_column" default="0">
+ The caret's column position inside the [LineEdit]. When set, the text may scroll to accommodate it.
</member>
- <member name="caret_mid_grapheme" type="bool" setter="set_mid_grapheme_caret_enabled" getter="get_mid_grapheme_caret_enabled" default="false">
+ <member name="caret_force_displayed" type="bool" setter="set_caret_force_displayed" getter="is_caret_force_displayed" default="false">
+ </member>
+ <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false">
Allow moving caret, selecting and removing the individual composite character components.
Note: [kbd]Backspace[/kbd] is always removing individual composite character components.
</member>
- <member name="caret_position" type="int" setter="set_cursor_position" getter="get_cursor_position" default="0">
- The cursor's position inside the [LineEdit]. When set, the text may scroll to accommodate it.
- </member>
<member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled" default="false">
If [code]true[/code], the [LineEdit] will show a clear button if [code]text[/code] is not empty, which can be used to clear the text quickly.
</member>
@@ -186,7 +186,7 @@
<member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true">
If [code]false[/code], existing text cannot be modified and new text cannot be added.
</member>
- <member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length" getter="get_expand_to_text_length" default="false">
+ <member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length_enabled" getter="is_expand_to_text_length_enabled" default="false">
If [code]true[/code], the [LineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened.
</member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" enum="Control.FocusMode" default="2" />
@@ -276,7 +276,7 @@
Copies the selected text.
</constant>
<constant name="MENU_PASTE" value="2" enum="MenuItems">
- Pastes the clipboard text over the selected text (or at the cursor's position).
+ Pastes the clipboard text over the selected text (or at the caret's position).
Non-printable escape characters are automatically stripped from the OS clipboard via [method String.strip_escapes].
</constant>
<constant name="MENU_CLEAR" value="3" enum="MenuItems">
@@ -359,6 +359,9 @@
</constant>
</constants>
<theme_items>
+ <theme_item name="caret_color" type="Color" default="Color( 0.94, 0.94, 0.94, 1 )">
+ Color of the [LineEdit]'s caret (text cursor).
+ </theme_item>
<theme_item name="clear" type="Texture2D">
Texture for the clear button. See [member clear_button_enabled].
</theme_item>
@@ -368,9 +371,6 @@
<theme_item name="clear_button_color_pressed" type="Color" default="Color( 1, 1, 1, 1 )">
Color used for the clear button when it's pressed.
</theme_item>
- <theme_item name="cursor_color" type="Color" default="Color( 0.94, 0.94, 0.94, 1 )">
- Color of the [LineEdit]'s visual cursor (caret).
- </theme_item>
<theme_item name="focus" type="StyleBox">
Background used when [LineEdit] has GUI focus.
</theme_item>
diff --git a/doc/classes/MeshInstance3D.xml b/doc/classes/MeshInstance3D.xml
index 82cd392cd3..e1a6cf44a7 100644
--- a/doc/classes/MeshInstance3D.xml
+++ b/doc/classes/MeshInstance3D.xml
@@ -43,7 +43,7 @@
Returns the [Material] that will be used by the [Mesh] when drawing. This can return the [member GeometryInstance3D.material_override], the surface override [Material] defined in this [MeshInstance3D], or the surface [Material] defined in the [Mesh]. For example, if [member GeometryInstance3D.material_override] is used, all surfaces will return the override material.
</description>
</method>
- <method name="get_surface_material" qualifiers="const">
+ <method name="get_surface_override_material" qualifiers="const">
<return type="Material">
</return>
<argument index="0" name="surface" type="int">
@@ -52,14 +52,14 @@
Returns the override [Material] for the specified surface of the [Mesh] resource.
</description>
</method>
- <method name="get_surface_material_count" qualifiers="const">
+ <method name="get_surface_override_material_count" qualifiers="const">
<return type="int">
</return>
<description>
- Returns the number of surface materials.
+ Returns the number of surface override materials. This is equivalent to [method Mesh.get_surface_count].
</description>
</method>
- <method name="set_surface_material">
+ <method name="set_surface_override_material">
<return type="void">
</return>
<argument index="0" name="surface" type="int">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index b5335e47cd..523f3a0c17 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -38,7 +38,7 @@
</description>
</method>
<method name="_get_configuration_warnings" qualifiers="virtual">
- <return type="Array">
+ <return type="String[]">
</return>
<description>
The elements in the array returned from this method are displayed as warnings in the Scene Dock if the script that overrides it is a [code]tool[/code] script.
diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml
index 21f835a53c..24178c3ff6 100644
--- a/doc/classes/PackedByteArray.xml
+++ b/doc/classes/PackedByteArray.xml
@@ -61,6 +61,114 @@
Returns a new [PackedByteArray] with the data compressed. Set the compression mode using one of [enum File.CompressionMode]'s constants.
</description>
</method>
+ <method name="decode_double" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_float" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_half" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_s16" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_s32" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_s64" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_s8" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_u16" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_u32" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_u64" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_u8" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_var" qualifiers="const">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="allow_objects" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="decode_var_size" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="allow_objects" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="decompress" qualifiers="const">
<return type="PackedByteArray">
</return>
@@ -92,6 +200,128 @@
Creates a copy of the array, and returns it.
</description>
</method>
+ <method name="encode_double">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_float">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_half">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_s16">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_s32">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_s64">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_s8">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_u16">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_u32">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_u64">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_u8">
+ <return type="void">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="encode_var">
+ <return type="int">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="value" type="Variant">
+ </argument>
+ <argument index="2" name="allow_objects" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_string_from_ascii" qualifiers="const">
<return type="String">
</return>
@@ -129,6 +359,16 @@
Returns [code]true[/code] if the array contains [code]value[/code].
</description>
</method>
+ <method name="has_encoded_var" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="byte_offset" type="int">
+ </argument>
+ <argument index="1" name="allow_objects" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="hex_encode" qualifiers="const">
<return type="String">
</return>
diff --git a/doc/classes/PackedFloat32Array.xml b/doc/classes/PackedFloat32Array.xml
index 6be1d24b5d..5e0008852c 100644
--- a/doc/classes/PackedFloat32Array.xml
+++ b/doc/classes/PackedFloat32Array.xml
@@ -111,6 +111,14 @@
<description>
</description>
</method>
+ <method name="operator []" qualifiers="operator">
+ <return type="float">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="push_back">
<return type="bool">
</return>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 5cac715408..c8fd0d667b 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1149,7 +1149,7 @@
<member name="navigation/3d/default_cell_size" type="float" setter="" getter="" default="0.3">
Default cell size for 3D navigation maps. See [method NavigationServer3D.map_set_cell_size].
</member>
- <member name="navigation/3d/default_edge_connection_margin" type="float" setter="" getter="" default="5.0">
+ <member name="navigation/3d/default_edge_connection_margin" type="float" setter="" getter="" default="0.3">
Default edge connection margin for 3D navigation maps. See [method NavigationServer3D.map_set_edge_connection_margin].
</member>
<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index f82301bcf4..d6eaa1b88b 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1317,7 +1317,7 @@
Sets the scenario that the instance is in. The scenario is the 3D world that the objects will be displayed in.
</description>
</method>
- <method name="instance_set_surface_material">
+ <method name="instance_set_surface_override_material">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
@@ -1327,7 +1327,7 @@
<argument index="2" name="material" type="RID">
</argument>
<description>
- Sets the material of a specific surface. Equivalent to [method MeshInstance3D.set_surface_material].
+ Sets the override material of a specific surface. Equivalent to [method MeshInstance3D.set_surface_override_material].
</description>
</method>
<method name="instance_set_transform">
diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml
index c29c30289a..ff00a848b9 100644
--- a/doc/classes/VisualShader.xml
+++ b/doc/classes/VisualShader.xml
@@ -228,7 +228,9 @@
</constant>
<constant name="TYPE_END" value="5" enum="Type">
</constant>
- <constant name="TYPE_MAX" value="6" enum="Type">
+ <constant name="TYPE_SKY" value="6" enum="Type">
+ </constant>
+ <constant name="TYPE_MAX" value="7" enum="Type">
Represents the size of the [enum Type] enum.
</constant>
<constant name="NODE_ID_INVALID" value="-1">
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 4fe2d2bb2a..9db2f0a287 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -2734,7 +2734,7 @@ void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
path_popup->set_size(path_rect.size);
path_popup->popup();
path->grab_focus();
- path->set_cursor_position(path->get_text().length());
+ path->set_caret_column(path->get_text().length());
clicking_on_name = false;
}
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index ac8bef817b..1c62c3d3e1 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -142,7 +142,7 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
bool found = text_editor->search(text, p_flags, p_from_line, p_from_col, line, col);
if (found) {
- if (!preserve_cursor) {
+ if (!preserve_cursor && !is_selection_only()) {
text_editor->unfold_line(line);
text_editor->cursor_set_line(line, false);
text_editor->cursor_set_column(col + text.length(), false);
@@ -488,10 +488,10 @@ void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) {
if (!get_search_text().is_empty()) {
if (p_focus_replace) {
replace_text->select_all();
- replace_text->set_cursor_position(replace_text->get_text().length());
+ replace_text->set_caret_column(replace_text->get_text().length());
} else {
search_text->select_all();
- search_text->set_cursor_position(search_text->get_text().length());
+ search_text->set_caret_column(search_text->get_text().length());
}
results_count = -1;
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index c92e94270e..1d95161e6c 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -35,6 +35,8 @@
#include "core/debugger/remote_debugger.h"
#include "core/io/marshalls.h"
#include "core/string/ustring.h"
+#include "core/version.h"
+#include "core/version_hash.gen.h"
#include "editor/debugger/editor_network_profiler.h"
#include "editor/debugger/editor_performance_profiler.h"
#include "editor/debugger/editor_profiler.h"
@@ -1371,7 +1373,8 @@ void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) {
item_menu->set_size(Size2(1, 1));
if (error_tree->is_anything_selected()) {
- item_menu->add_icon_item(get_theme_icon("ActionCopy", "EditorIcons"), TTR("Copy Error"), 0);
+ item_menu->add_icon_item(get_theme_icon("ActionCopy", "EditorIcons"), TTR("Copy Error"), ACTION_COPY_ERROR);
+ item_menu->add_icon_item(get_theme_icon("Instance", "EditorIcons"), TTR("Open C++ Source on GitHub"), ACTION_OPEN_SOURCE);
}
if (item_menu->get_item_count() > 0) {
@@ -1381,30 +1384,64 @@ void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) {
}
void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {
- TreeItem *ti = error_tree->get_selected();
- while (ti->get_parent() != error_tree->get_root()) {
- ti = ti->get_parent();
- }
+ switch (p_option) {
+ case ACTION_COPY_ERROR: {
+ TreeItem *ti = error_tree->get_selected();
+ while (ti->get_parent() != error_tree->get_root()) {
+ ti = ti->get_parent();
+ }
- String type;
+ String type;
- if (ti->get_icon(0) == get_theme_icon("Warning", "EditorIcons")) {
- type = "W ";
- } else if (ti->get_icon(0) == get_theme_icon("Error", "EditorIcons")) {
- type = "E ";
- }
+ if (ti->get_icon(0) == get_theme_icon("Warning", "EditorIcons")) {
+ type = "W ";
+ } else if (ti->get_icon(0) == get_theme_icon("Error", "EditorIcons")) {
+ type = "E ";
+ }
- String text = ti->get_text(0) + " ";
- int rpad_len = text.length();
+ String text = ti->get_text(0) + " ";
+ int rpad_len = text.length();
- text = type + text + ti->get_text(1) + "\n";
- TreeItem *ci = ti->get_children();
- while (ci) {
- text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n";
- ci = ci->get_next();
- }
+ text = type + text + ti->get_text(1) + "\n";
+ TreeItem *ci = ti->get_children();
+ while (ci) {
+ text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n";
+ ci = ci->get_next();
+ }
- DisplayServer::get_singleton()->clipboard_set(text);
+ DisplayServer::get_singleton()->clipboard_set(text);
+ } break;
+
+ case ACTION_OPEN_SOURCE: {
+ TreeItem *ti = error_tree->get_selected();
+ while (ti->get_parent() != error_tree->get_root()) {
+ ti = ti->get_parent();
+ }
+
+ // We only need the first child here (C++ source stack trace).
+ TreeItem *ci = ti->get_children();
+ // Parse back the `file:line @ method()` string.
+ const Vector<String> file_line_number = ci->get_text(1).split("@")[0].strip_edges().split(":");
+ ERR_FAIL_COND_MSG(file_line_number.size() < 2, "Incorrect C++ source stack trace file:line format (please report).");
+ const String file = file_line_number[0];
+ const int line_number = file_line_number[1].to_int();
+
+ // Construct a GitHub repository URL and open it in the user's default web browser.
+ if (String(VERSION_HASH).length() >= 1) {
+ // Git commit hash information available; use it for greater accuracy, including for development versions.
+ OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d",
+ VERSION_HASH,
+ file,
+ line_number));
+ } else {
+ // Git commit hash information unavailable; fall back to tagged releases.
+ OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s-stable/%s#L%d",
+ VERSION_NUMBER,
+ file,
+ line_number));
+ }
+ } break;
+ }
}
void ScriptEditorDebugger::_tab_changed(int p_tab) {
diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h
index e5fb3c35a9..a5731c9f9c 100644
--- a/editor/debugger/script_editor_debugger.h
+++ b/editor/debugger/script_editor_debugger.h
@@ -74,6 +74,11 @@ private:
PROFILER_SCRIPTS_SERVERS
};
+ enum Actions {
+ ACTION_COPY_ERROR,
+ ACTION_OPEN_SOURCE,
+ };
+
AcceptDialog *msgdialog;
LineEdit *clicked_ctrl;
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index 2ed937b6ff..d962658484 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -38,16 +38,15 @@
#include "core/version_hash.gen.h"
void EditorAbout::_theme_changed() {
- Control *base = EditorNode::get_singleton()->get_gui_base();
- Ref<Font> font = base->get_theme_font("source", "EditorFonts");
- int font_size = base->get_theme_font_size("source_size", "EditorFonts");
+ const Ref<Font> font = get_theme_font("source", "EditorFonts");
+ const int font_size = get_theme_font_size("source_size", "EditorFonts");
_tpl_text->add_theme_font_override("normal_font", font);
_tpl_text->add_theme_font_size_override("normal_font_size", font_size);
_tpl_text->add_theme_constant_override("line_separation", 6 * EDSCALE);
_license_text->add_theme_font_override("normal_font", font);
_license_text->add_theme_font_size_override("normal_font_size", font_size);
_license_text->add_theme_constant_override("line_separation", 6 * EDSCALE);
- _logo->set_texture(base->get_theme_icon("Logo", "EditorIcons"));
+ _logo->set_texture(get_theme_icon("Logo", "EditorIcons"));
}
void EditorAbout::_notification(int p_what) {
diff --git a/editor/editor_about.h b/editor/editor_about.h
index efb7245e78..2823220a8a 100644
--- a/editor/editor_about.h
+++ b/editor/editor_about.h
@@ -44,6 +44,10 @@
#include "editor_scale.h"
+/**
+ * NOTE: Do not assume the EditorNode singleton to be available in this class' methods.
+ * EditorAbout is also used from the project manager where EditorNode isn't initialized.
+ */
class EditorAbout : public AcceptDialog {
GDCLASS(EditorAbout, AcceptDialog);
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index fb0dc57501..59d3b09678 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1922,6 +1922,11 @@ void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file
_reimport_file(p_file, &p_custom_params, p_importer);
}
+void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_import_data) {
+ p_import_data->max_index = MAX(p_import_data->reimport_from + int(p_index), p_import_data->max_index);
+ _reimport_file(p_import_data->reimport_files[p_import_data->reimport_from + p_index].path);
+}
+
void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
{
// Ensure that ProjectSettings::IMPORTED_FILES_PATH exists.
@@ -1939,7 +1944,8 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
importing = true;
EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size());
- Vector<ImportFile> files;
+ Vector<ImportFile> reimport_files;
+
Set<String> groups_to_reimport;
for (int i = 0; i < p_files.size(); i++) {
@@ -1957,8 +1963,8 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
//it's a regular file
ImportFile ifile;
ifile.path = p_files[i];
- ifile.order = ResourceFormatImporter::get_singleton()->get_import_order(p_files[i]);
- files.push_back(ifile);
+ ResourceFormatImporter::get_singleton()->get_import_order_threads_and_importer(p_files[i], ifile.order, ifile.threaded, ifile.importer);
+ reimport_files.push_back(ifile);
}
//group may have changed, so also update group reference
@@ -1969,11 +1975,51 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
}
}
- files.sort();
+ reimport_files.sort();
- for (int i = 0; i < files.size(); i++) {
- pr.step(files[i].path.get_file(), i);
- _reimport_file(files[i].path);
+ bool use_threads = GLOBAL_GET("editor/import/use_multiple_threads");
+
+ int from = 0;
+ for (int i = 0; i < reimport_files.size(); i++) {
+ if (use_threads && reimport_files[i].threaded) {
+ if (i + 1 == reimport_files.size() || reimport_files[i + 1].importer != reimport_files[from].importer) {
+ if (from - i == 0) {
+ //single file, do not use threads
+ pr.step(reimport_files[i].path.get_file(), i);
+ _reimport_file(reimport_files[i].path);
+ } else {
+ Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(reimport_files[from].importer);
+ ERR_CONTINUE(!importer.is_valid());
+
+ importer->import_threaded_begin();
+
+ ImportThreadData data;
+ data.max_index = from;
+ data.reimport_from = from;
+ data.reimport_files = reimport_files.ptr();
+
+ import_threads.begin_work(i - from + 1, this, &EditorFileSystem::_reimport_thread, &data);
+ int current_index = from - 1;
+ do {
+ if (current_index < data.max_index) {
+ current_index = data.max_index;
+ pr.step(reimport_files[current_index].path.get_file(), current_index);
+ }
+ OS::get_singleton()->delay_usec(1);
+ } while (!import_threads.is_done_dispatching());
+
+ import_threads.end_work();
+
+ importer->import_threaded_end();
+ }
+
+ from = i + 1;
+ }
+
+ } else {
+ pr.step(reimport_files[i].path.get_file(), i);
+ _reimport_file(reimport_files[i].path);
+ }
}
//reimport groups
@@ -2111,7 +2157,7 @@ void EditorFileSystem::_update_extensions() {
EditorFileSystem::EditorFileSystem() {
ResourceLoader::import = _resource_import;
reimport_on_missing_imported_files = GLOBAL_DEF("editor/import/reimport_missing_imported_files", true);
-
+ GLOBAL_DEF("editor/import/use_multiple_threads", true);
singleton = this;
filesystem = memnew(EditorFileSystemDirectory); //like, empty
filesystem->parent = nullptr;
@@ -2138,7 +2184,9 @@ EditorFileSystem::EditorFileSystem() {
first_scan = true;
scan_changes_pending = false;
revalidate_import_files = false;
+ import_threads.init();
}
EditorFileSystem::~EditorFileSystem() {
+ import_threads.finish();
}
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 6f4f058503..9c9076106c 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -36,7 +36,9 @@
#include "core/os/thread_safe.h"
#include "core/templates/safe_refcount.h"
#include "core/templates/set.h"
+#include "core/templates/thread_work_pool.h"
#include "scene/main/node.h"
+
class FileAccess;
struct EditorProgressBG;
@@ -214,9 +216,11 @@ class EditorFileSystem : public Node {
struct ImportFile {
String path;
+ String importer;
+ bool threaded = false;
int order = 0;
bool operator<(const ImportFile &p_if) const {
- return order < p_if.order;
+ return order == p_if.order ? (importer < p_if.importer) : (order < p_if.order);
}
};
@@ -236,6 +240,16 @@ class EditorFileSystem : public Node {
Set<String> group_file_cache;
+ ThreadWorkPool import_threads;
+
+ struct ImportThreadData {
+ const ImportFile *reimport_files;
+ int reimport_from;
+ int max_index = 0;
+ };
+
+ void _reimport_thread(uint32_t p_index, ImportThreadData *p_import_data);
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index a747652a2f..6039f64b7c 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1801,7 +1801,7 @@ void FindBar::popup_search() {
if (!search_text->get_text().is_empty()) {
search_text->select_all();
- search_text->set_cursor_position(search_text->get_text().length());
+ search_text->set_caret_column(search_text->get_text().length());
if (grabbed_focus) {
_search();
}
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 4c5c3af765..7cc9ebd63e 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -963,7 +963,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("read_only", "LineEdit", font_disabled_color);
theme->set_color("font_color", "LineEdit", font_color);
theme->set_color("font_selected_color", "LineEdit", mono_color);
- theme->set_color("cursor_color", "LineEdit", font_color);
+ theme->set_color("caret_color", "LineEdit", font_color);
theme->set_color("selection_color", "LineEdit", selection_color);
theme->set_color("clear_button_color", "LineEdit", font_color);
theme->set_color("clear_button_color_pressed", "LineEdit", accent_color);
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 9041b815ca..4bb56beaeb 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1241,7 +1241,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
if (mesh.is_valid()) {
mesh_node->set_mesh(mesh);
for (int i = 0; i < mesh->get_surface_count(); i++) {
- mesh_node->set_surface_material(i, src_mesh_node->get_surface_material(i));
+ mesh_node->set_surface_override_material(i, src_mesh_node->get_surface_material(i));
}
}
}
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 6c6af57c4c..00039f2ac6 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -173,6 +173,8 @@ public:
virtual bool has_advanced_options() const override;
virtual void show_advanced_options(const String &p_path) override;
+ virtual bool can_import_threaded() const override { return false; }
+
ResourceImporterScene();
};
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index 025fcaf818..f7c0ebcfaf 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -698,7 +698,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
max_value->set_step(0.01);
label_value = memnew(LineEdit);
- label_value->set_expand_to_text_length(true);
+ label_value->set_expand_to_text_length_enabled(true);
// now add
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index af9c391174..e719df53d5 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -942,7 +942,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
left_vbox->add_spacer();
label_y = memnew(LineEdit);
left_vbox->add_child(label_y);
- label_y->set_expand_to_text_length(true);
+ label_y->set_expand_to_text_length_enabled(true);
left_vbox->add_spacer();
min_y_value = memnew(SpinBox);
left_vbox->add_child(min_y_value);
@@ -978,7 +978,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
bottom_vbox->add_spacer();
label_x = memnew(LineEdit);
bottom_vbox->add_child(label_x);
- label_x->set_expand_to_text_length(true);
+ label_x->set_expand_to_text_length_enabled(true);
bottom_vbox->add_spacer();
max_x_value = memnew(SpinBox);
bottom_vbox->add_child(max_x_value);
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index fdbbe5184b..48fb507bb1 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -136,7 +136,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() {
if (String(E->get()) != "output") {
LineEdit *name = memnew(LineEdit);
name->set_text(E->get());
- name->set_expand_to_text_length(true);
+ name->set_expand_to_text_length_enabled(true);
node->add_child(name);
node->set_slot(0, false, 0, Color(), true, 0, get_theme_color("font_color", "Label"));
name->connect("text_entered", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed), varray(agnode), CONNECT_DEFERRED);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index b678197037..fc3e15aa52 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -804,11 +804,15 @@ void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_n
bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append) {
bool still_selected = true;
- if (p_append) {
+ if (p_append && !editor_selection->get_selected_node_list().is_empty()) {
if (editor_selection->is_selected(item)) {
// Already in the selection, remove it from the selected nodes
editor_selection->remove_node(item);
still_selected = false;
+
+ if (editor_selection->get_selected_node_list().size() == 1) {
+ editor->push_item(editor_selection->get_selected_node_list()[0]);
+ }
} else {
// Add the item to the selection
editor_selection->add_node(item);
@@ -2589,6 +2593,9 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
}
_find_canvas_items_in_rect(Rect2(bsfrom, bsto - bsfrom), scene, &selitems);
+ if (selitems.size() == 1 && editor_selection->get_selected_node_list().is_empty()) {
+ editor->push_item(selitems[0]);
+ }
for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) {
editor_selection->add_node(E->get());
}
@@ -5378,9 +5385,6 @@ void CanvasItemEditor::_focus_selection(int p_op) {
rect = rect.merge(canvas_item_rect);
}
};
- if (count == 0) {
- return;
- }
if (p_op == VIEW_CENTER_TO_SELECTION) {
center = rect.position + rect.size / 2;
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index f8932cd534..6f1f243444 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -93,7 +93,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
mesh = mesh->duplicate();
for (int j = 0; j < mesh->get_surface_count(); ++j) {
- Ref<Material> mat = mi->get_surface_material(j);
+ Ref<Material> mat = mi->get_surface_override_material(j);
if (mat.is_valid()) {
mesh->surface_set_material(j, mat);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 3df092bc13..13c7814dac 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -3560,10 +3560,6 @@ void Node3DEditorViewport::reset() {
}
void Node3DEditorViewport::focus_selection() {
- if (!get_selected_count()) {
- return;
- }
-
Vector3 center;
int count = 0;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 8f8a4b3054..ed3b746678 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -205,7 +205,7 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptCo
ShaderLanguage sl;
String calltip;
- sl.complete(p_code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type, r_options, calltip);
+ sl.complete(p_code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type, r_options, calltip);
get_text_editor()->set_code_hint(calltip);
}
@@ -219,7 +219,7 @@ void ShaderTextEditor::_validate_script() {
ShaderLanguage sl;
- Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type);
+ Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type);
if (err != OK) {
String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text();
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index b2fa9c540e..dea85e8799 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -1133,16 +1133,24 @@ void VisualShaderEditor::_update_options_menu() {
}
void VisualShaderEditor::_set_mode(int p_which) {
- if (p_which == VisualShader::MODE_PARTICLES) {
+ if (p_which == VisualShader::MODE_SKY) {
+ edit_type_standart->set_visible(false);
+ edit_type_particles->set_visible(false);
+ edit_type_sky->set_visible(true);
+ edit_type = edit_type_sky;
+ mode = MODE_FLAGS_SKY;
+ } else if (p_which == VisualShader::MODE_PARTICLES) {
edit_type_standart->set_visible(false);
edit_type_particles->set_visible(true);
+ edit_type_sky->set_visible(false);
edit_type = edit_type_particles;
- particles_mode = true;
+ mode = MODE_FLAGS_PARTICLES;
} else {
edit_type_particles->set_visible(false);
edit_type_standart->set_visible(true);
+ edit_type_sky->set_visible(false);
edit_type = edit_type_standart;
- particles_mode = false;
+ mode = MODE_FLAGS_SPATIAL_CANVASITEM;
}
visual_shader->set_shader_type(get_current_shader_type());
}
@@ -1303,8 +1311,10 @@ void VisualShaderEditor::_update_graph() {
VisualShader::Type VisualShaderEditor::get_current_shader_type() const {
VisualShader::Type type;
- if (particles_mode) {
+ if (mode & MODE_FLAGS_PARTICLES) {
type = VisualShader::Type(edit_type->get_selected() + 3);
+ } else if (mode & MODE_FLAGS_SKY) {
+ type = VisualShader::Type(edit_type->get_selected() + 6);
} else {
type = VisualShader::Type(edit_type->get_selected());
}
@@ -3025,7 +3035,14 @@ void VisualShaderEditor::_paste_nodes(bool p_use_custom_position, const Vector2
}
void VisualShaderEditor::_mode_selected(int p_id) {
- visual_shader->set_shader_type(particles_mode ? VisualShader::Type(p_id + 3) : VisualShader::Type(p_id));
+ int offset = 0;
+ if (mode & MODE_FLAGS_PARTICLES) {
+ offset = 3;
+ } else if (mode & MODE_FLAGS_SKY) {
+ offset = 6;
+ }
+
+ visual_shader->set_shader_type(VisualShader::Type(p_id + offset));
_update_options_menu();
_update_graph();
}
@@ -3398,7 +3415,7 @@ void VisualShaderEditor::_update_preview() {
ShaderLanguage sl;
- Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type);
+ Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type);
for (int i = 0; i < preview_text->get_line_count(); i++) {
preview_text->set_line_as_marked(i, false);
@@ -3531,10 +3548,17 @@ VisualShaderEditor::VisualShaderEditor() {
edit_type_particles->select(0);
edit_type_particles->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected));
+ edit_type_sky = memnew(OptionButton);
+ edit_type_sky->add_item(TTR("Sky"));
+ edit_type_sky->select(0);
+ edit_type_sky->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected));
+
edit_type = edit_type_standart;
graph->get_zoom_hbox()->add_child(edit_type_particles);
graph->get_zoom_hbox()->move_child(edit_type_particles, 0);
+ graph->get_zoom_hbox()->add_child(edit_type_sky);
+ graph->get_zoom_hbox()->move_child(edit_type_sky, 0);
graph->get_zoom_hbox()->add_child(edit_type_standart);
graph->get_zoom_hbox()->move_child(edit_type_standart, 0);
@@ -3671,7 +3695,7 @@ VisualShaderEditor::VisualShaderEditor() {
comment_title_change_popup = memnew(PopupPanel);
comment_title_change_edit = memnew(LineEdit);
- comment_title_change_edit->set_expand_to_text_length(true);
+ comment_title_change_edit->set_expand_to_text_length_enabled(true);
comment_title_change_edit->connect("text_changed", callable_mp(this, &VisualShaderEditor::_comment_title_text_changed));
comment_title_change_edit->connect("text_entered", callable_mp(this, &VisualShaderEditor::_comment_title_text_entered));
comment_title_change_popup->add_child(comment_title_change_edit);
@@ -3782,6 +3806,7 @@ VisualShaderEditor::VisualShaderEditor() {
const String input_param_for_vertex_and_fragment_shader_modes = TTR("'%s' input parameter for vertex and fragment shader modes.");
const String input_param_for_fragment_and_light_shader_modes = TTR("'%s' input parameter for fragment and light shader modes.");
const String input_param_for_fragment_shader_mode = TTR("'%s' input parameter for fragment shader mode.");
+ const String input_param_for_sky_shader_mode = TTR("'%s' input parameter for sky shader mode.");
const String input_param_for_light_shader_mode = TTR("'%s' input parameter for light shader mode.");
const String input_param_for_vertex_shader_mode = TTR("'%s' input parameter for vertex shader mode.");
const String input_param_for_emit_shader_mode = TTR("'%s' input parameter for emit shader mode.");
@@ -3911,35 +3936,35 @@ VisualShaderEditor::VisualShaderEditor() {
// SKY INPUTS
- add_options.push_back(AddOption("AtCubeMapPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "at_cubemap_pass"), "at_cubemap_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("AtHalfResPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "at_half_res_pass"), "at_half_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("AtQuarterResPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "at_quarter_res_pass"), "at_quarter_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("EyeDir", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "eyedir"), "eyedir", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("HalfResColor", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "half_res_color"), "half_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("HalfResAlpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "half_res_alpha"), "half_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light0_color"), "light0_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Direction", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light0_direction"), "light0_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Enabled", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light0_enabled"), "light0_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Energy", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light0_energy"), "light0_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light1_color"), "light1_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Direction", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light1_direction"), "light1_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Enabled", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light1_enabled"), "light1_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Energy", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light1_energy"), "light1_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light2_color"), "light2_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Direction", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light2_direction"), "light2_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Enabled", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light2_enabled"), "light2_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Energy", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light2_energy"), "light2_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light3_color"), "light3_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Direction", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light3_direction"), "light3_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Enabled", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light3_enabled"), "light3_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Energy", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light3_energy"), "light3_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Position", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "position"), "position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("QuarterResColor", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "quarter_res_color"), "quarter_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("QuarterResAlpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "quarter_res_alpha"), "quarter_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Radiance", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "radiance"), "radiance", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_uv"), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("SkyCoords", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "sky_coords"), "sky_coords", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
- add_options.push_back(AddOption("Time", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY));
+ add_options.push_back(AddOption("AtCubeMapPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_cubemap_pass"), "at_cubemap_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("AtHalfResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_half_res_pass"), "at_half_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("AtQuarterResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_quarter_res_pass"), "at_quarter_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("EyeDir", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "eyedir"), "eyedir", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("HalfResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_color"), "half_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("HalfResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_alpha"), "half_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_color"), "light0_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_direction"), "light0_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_enabled"), "light0_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_energy"), "light0_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_color"), "light1_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_direction"), "light1_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_enabled"), "light1_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_energy"), "light1_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_color"), "light2_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_direction"), "light2_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_enabled"), "light2_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_energy"), "light2_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_color"), "light3_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_direction"), "light3_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_enabled"), "light3_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_energy"), "light3_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Position", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "position"), "position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("QuarterResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_color"), "quarter_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("QuarterResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_alpha"), "quarter_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Radiance", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "radiance"), "radiance", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("ScreenUV", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "screen_uv"), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("SkyCoords", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "sky_coords"), "sky_coords", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Time", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
// SCALAR
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 517dc6056f..6d57d38cab 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -141,6 +141,7 @@ class VisualShaderEditor : public VBoxContainer {
OptionButton *edit_type = nullptr;
OptionButton *edit_type_standart;
OptionButton *edit_type_particles;
+ OptionButton *edit_type_sky;
PanelContainer *error_panel;
Label *error_label;
@@ -169,7 +170,14 @@ class VisualShaderEditor : public VBoxContainer {
bool preview_first = true;
bool preview_showed = false;
- bool particles_mode;
+
+ enum ShaderModeFlags {
+ MODE_FLAGS_SPATIAL_CANVASITEM = 1,
+ MODE_FLAGS_SKY = 2,
+ MODE_FLAGS_PARTICLES = 4
+ };
+
+ int mode = MODE_FLAGS_SPATIAL_CANVASITEM;
enum TypeFlags {
TYPE_FLAGS_VERTEX = 1,
@@ -183,6 +191,10 @@ class VisualShaderEditor : public VBoxContainer {
TYPE_FLAGS_END = 4
};
+ enum SkyTypeFlags {
+ TYPE_FLAGS_SKY = 1,
+ };
+
enum ToolsMenuOptions {
EXPAND_ALL,
COLLAPSE_ALL
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index d3def86bd1..e51e8ee82e 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -296,7 +296,7 @@ private:
String sp = _test_path();
if (sp != "") {
// If the project name is empty or default, infer the project name from the selected folder name
- if (project_name->get_text() == "" || project_name->get_text() == TTR("New Game Project")) {
+ if (project_name->get_text().strip_edges() == "" || project_name->get_text().strip_edges() == TTR("New Game Project")) {
sp = sp.replace("\\", "/");
int lidx = sp.rfind("/");
@@ -380,16 +380,17 @@ private:
}
void _create_folder() {
- if (project_name->get_text() == "" || created_folder_path != "" || project_name->get_text().ends_with(".") || project_name->get_text().ends_with(" ")) {
- set_message(TTR("Invalid Project Name."), MESSAGE_WARNING);
+ const String project_name_no_edges = project_name->get_text().strip_edges();
+ if (project_name_no_edges == "" || created_folder_path != "" || project_name_no_edges.ends_with(".")) {
+ set_message(TTR("Invalid project name."), MESSAGE_WARNING);
return;
}
DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (d->change_dir(project_path->get_text()) == OK) {
- if (!d->dir_exists(project_name->get_text())) {
- if (d->make_dir(project_name->get_text()) == OK) {
- d->change_dir(project_name->get_text());
+ if (!d->dir_exists(project_name_no_edges)) {
+ if (d->make_dir(project_name_no_edges) == OK) {
+ d->change_dir(project_name_no_edges);
String dir_str = d->get_current_dir();
project_path->set_text(dir_str);
_path_text_changed(dir_str);
@@ -415,7 +416,7 @@ private:
_test_path();
- if (p_text == "") {
+ if (p_text.strip_edges() == "") {
set_message(TTR("It would be a good idea to name your project."), MESSAGE_ERROR);
}
}
@@ -442,7 +443,7 @@ private:
set_message(vformat(TTR("Couldn't load project.godot in project path (error %d). It may be missing or corrupted."), err), MESSAGE_ERROR);
} else {
ProjectSettings::CustomMap edited_settings;
- edited_settings["application/config/name"] = project_name->get_text();
+ edited_settings["application/config/name"] = project_name->get_text().strip_edges();
if (current->save_custom(dir2.plus_file("project.godot"), edited_settings, Vector<String>(), true) != OK) {
set_message(TTR("Couldn't edit project.godot in project path."), MESSAGE_ERROR);
@@ -483,7 +484,7 @@ private:
initial_settings["rendering/textures/vram_compression/import_etc2"] = false;
initial_settings["rendering/textures/vram_compression/import_etc"] = true;
}
- initial_settings["application/config/name"] = project_name->get_text();
+ initial_settings["application/config/name"] = project_name->get_text().strip_edges();
initial_settings["application/config/icon"] = "res://icon.png";
initial_settings["rendering/environment/defaults/default_environment"] = "res://default_env.tres";
@@ -1851,6 +1852,9 @@ void ProjectManager::_notification(int p_what) {
case NOTIFICATION_WM_CLOSE_REQUEST: {
_dim_window();
} break;
+ case NOTIFICATION_WM_ABOUT: {
+ _show_about();
+ } break;
}
}
@@ -2254,6 +2258,10 @@ void ProjectManager::_erase_missing_projects() {
erase_missing_ask->popup_centered();
}
+void ProjectManager::_show_about() {
+ about->popup_centered(Size2(780, 500) * EDSCALE);
+}
+
void ProjectManager::_language_selected(int p_id) {
String lang = language_btn->get_item_metadata(p_id);
EditorSettings::get_singleton()->set("interface/editor/editor_language", lang);
@@ -2443,12 +2451,7 @@ ProjectManager::ProjectManager() {
}
// TRANSLATORS: This refers to the application where users manage their Godot projects.
- if (TS->is_locale_right_to_left(TranslationServer::get_singleton()->get_tool_locale())) {
- // For RTL languages, embed translated part of the title (using control characters) to ensure correct order.
- DisplayServer::get_singleton()->window_set_title(VERSION_NAME + String(" - ") + String::chr(0x202B) + TTR("Project Manager") + String::chr(0x202C) + String::chr(0x200E) + " - " + String::chr(0xA9) + " 2007-2021 Juan Linietsky, Ariel Manzur & Godot Contributors");
- } else {
- DisplayServer::get_singleton()->window_set_title(VERSION_NAME + String(" - ") + TTR("Project Manager") + " - " + String::chr(0xA9) + " 2007-2021 Juan Linietsky, Ariel Manzur & Godot Contributors");
- }
+ DisplayServer::get_singleton()->window_set_title(VERSION_NAME + String(" - ") + TTR("Project Manager"));
FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
@@ -2582,6 +2585,13 @@ ProjectManager::ProjectManager() {
erase_missing_btn->set_text(TTR("Remove Missing"));
erase_missing_btn->connect("pressed", callable_mp(this, &ProjectManager::_erase_missing_projects));
tree_vb->add_child(erase_missing_btn);
+
+ tree_vb->add_spacer();
+
+ about_btn = memnew(Button);
+ about_btn->set_text(TTR("About"));
+ about_btn->connect("pressed", callable_mp(this, &ProjectManager::_show_about));
+ tree_vb->add_child(about_btn);
}
{
@@ -2715,6 +2725,9 @@ ProjectManager::ProjectManager() {
open_templates->get_ok_button()->set_text(TTR("Open Asset Library"));
open_templates->connect("confirmed", callable_mp(this, &ProjectManager::_open_asset_library));
add_child(open_templates);
+
+ about = memnew(EditorAbout);
+ add_child(about);
}
_load_recent_projects();
diff --git a/editor/project_manager.h b/editor/project_manager.h
index d13315c022..a66b7c4ab6 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -31,6 +31,7 @@
#ifndef PROJECT_MANAGER_H
#define PROJECT_MANAGER_H
+#include "editor/editor_about.h"
#include "editor/plugins/asset_library_editor_plugin.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/file_dialog.h"
@@ -62,6 +63,7 @@ class ProjectManager : public Control {
Button *rename_btn;
Button *erase_btn;
Button *erase_missing_btn;
+ Button *about_btn;
EditorAssetLibrary *asset_library;
@@ -78,6 +80,7 @@ class ProjectManager : public Control {
ConfirmationDialog *multi_scan_ask;
ConfirmationDialog *ask_update_settings;
ConfirmationDialog *open_templates;
+ EditorAbout *about;
HBoxContainer *settings_hb;
@@ -100,6 +103,7 @@ class ProjectManager : public Control {
void _erase_missing_projects();
void _erase_project_confirm();
void _erase_missing_projects_confirm();
+ void _show_about();
void _update_project_buttons();
void _language_selected(int p_id);
void _restart_confirm();
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index b51524b299..0f15d4b119 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -632,7 +632,7 @@ void RenameDialog::_insert_text(String text) {
if (_is_main_field(focus_owner)) {
focus_owner->selection_delete();
- focus_owner->append_at_cursor(text);
+ focus_owner->insert_text_at_caret(text);
_update_preview();
}
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 5e6ebc22a3..a6d1a118b8 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -140,7 +140,11 @@ void SceneTreeDock::instance_scenes(const Vector<String> &p_files, Node *p_paren
parent = scene_tree->get_selected();
}
- if (!parent || !edited_scene) {
+ if (!parent) {
+ parent = edited_scene;
+ }
+
+ if (!parent) {
if (p_files.size() == 1) {
accept->set_text(TTR("No parent to instance a child at."));
} else {
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index b707f6c353..f3addd8904 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -87,8 +87,8 @@ void ScriptCreateDialog::_path_hbox_sorted() {
// First set cursor to the end of line to scroll LineEdit view
// to the right and then set the actual cursor position.
- file_path->set_cursor_position(file_path->get_text().length());
- file_path->set_cursor_position(filename_start_pos);
+ file_path->set_caret_column(file_path->get_text().length());
+ file_path->set_caret_column(filename_start_pos);
file_path->grab_focus();
}
@@ -238,6 +238,14 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
return "";
}
+String ScriptCreateDialog::_get_class_name() const {
+ if (has_named_classes) {
+ return class_name->get_text();
+ } else {
+ return ProjectSettings::get_singleton()->localize_path(file_path->get_text()).get_file().get_basename();
+ }
+}
+
void ScriptCreateDialog::_class_name_changed(const String &p_name) {
if (_validate_class(class_name->get_text())) {
is_class_name_valid = true;
@@ -287,13 +295,7 @@ void ScriptCreateDialog::ok_pressed() {
}
void ScriptCreateDialog::_create_new() {
- String cname_param;
-
- if (has_named_classes) {
- cname_param = class_name->get_text();
- } else {
- cname_param = ProjectSettings::get_singleton()->localize_path(file_path->get_text()).get_file().get_basename();
- }
+ String cname_param = _get_class_name();
Ref<Script> scr;
if (script_template != "") {
@@ -555,7 +557,7 @@ void ScriptCreateDialog::_file_selected(const String &p_file) {
String filename = p.get_file().get_basename();
int select_start = p.rfind(filename);
file_path->select(select_start, select_start + filename.length());
- file_path->set_cursor_position(select_start + filename.length());
+ file_path->set_caret_column(select_start + filename.length());
file_path->grab_focus();
}
}
@@ -687,6 +689,10 @@ void ScriptCreateDialog::_update_dialog() {
builtin_warning_label->set_visible(is_built_in);
+ // Check if the script name is the same as the parent class.
+ // This warning isn't relevant if the script is built-in.
+ script_name_warning_label->set_visible(!is_built_in && _get_class_name() == parent_name->get_text());
+
if (is_built_in) {
get_ok_button()->set_text(TTR("Create"));
parent_name->set_editable(true);
@@ -768,6 +774,14 @@ ScriptCreateDialog::ScriptCreateDialog() {
builtin_warning_label->set_autowrap(true);
builtin_warning_label->hide();
+ script_name_warning_label = memnew(Label);
+ script_name_warning_label->set_text(
+ TTR("Warning: Having the script name be the same as a built-in type is usually not desired."));
+ vb->add_child(script_name_warning_label);
+ script_name_warning_label->add_theme_color_override("font_color", Color(1, 0.85, 0.4));
+ script_name_warning_label->set_autowrap(true);
+ script_name_warning_label->hide();
+
status_panel = memnew(PanelContainer);
status_panel->set_h_size_flags(Control::SIZE_FILL);
status_panel->add_child(vb);
diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h
index e898b6f927..d6417b9d33 100644
--- a/editor/script_create_dialog.h
+++ b/editor/script_create_dialog.h
@@ -50,6 +50,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
Label *error_label;
Label *path_error_label;
Label *builtin_warning_label;
+ Label *script_name_warning_label;
PanelContainer *status_panel;
LineEdit *parent_name;
Button *parent_browse_button;
@@ -110,6 +111,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
bool _validate_parent(const String &p_string);
bool _validate_class(const String &p_string);
String _validate_path(const String &p_path, bool p_file_must_exist);
+ String _get_class_name() const;
void _class_name_changed(const String &p_name);
void _parent_name_changed(const String &p_parent);
void _template_changed(int p_template = 0);
diff --git a/main/main.cpp b/main/main.cpp
index 4103fad17c..bf7b88bdc9 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -375,8 +375,8 @@ void Main::print_help(const char *p_binary) {
#ifdef TESTS_ENABLED
OS::get_singleton()->print(" --test [--help] Run unit tests. Use --test --help for more information.\n");
#endif
- OS::get_singleton()->print("\n");
#endif
+ OS::get_singleton()->print("\n");
}
#ifdef TESTS_ENABLED
@@ -390,6 +390,8 @@ Error Main::test_setup() {
register_core_types();
register_core_driver_types();
+ packed_data = memnew(PackedData);
+
globals = memnew(ProjectSettings);
GLOBAL_DEF("debug/settings/crash_handler/message",
@@ -459,6 +461,9 @@ void Main::test_cleanup() {
if (globals) {
memdelete(globals);
}
+ if (packed_data) {
+ memdelete(packed_data);
+ }
if (engine) {
memdelete(engine);
}
diff --git a/misc/dist/html/editor.html b/misc/dist/html/editor.html
index 4785f54973..347c22adf8 100644
--- a/misc/dist/html/editor.html
+++ b/misc/dist/html/editor.html
@@ -58,6 +58,29 @@
filter: brightness(82.5%);
}
+ .welcome-modal {
+ display: none;
+ position: fixed;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: hsla(0, 0%, 0%, 0.5);
+ }
+
+ .welcome-modal-content {
+ background-color: #333b4f;
+ box-shadow: 0 0.25rem 0.25rem hsla(0, 0%, 0%, 0.5);
+ line-height: 1.5;
+ max-width: 38rem;
+ margin: 4rem auto 0 auto;
+ color: white;
+ border-radius: 0.5rem;
+ padding: 1rem 1rem 2rem 1rem;
+ }
+
#tabs-buttons {
/* Match the default background color of the editor window for a seamless appearance. */
background-color: #202531;
@@ -206,6 +229,36 @@
</style>
</head>
<body>
+ <div
+ id="welcome-modal"
+ class="welcome-modal"
+ role="dialog"
+ aria-labelledby="welcome-modal-title"
+ aria-describedby="welcome-modal-description"
+ onclick="if (event.target === this) closeWelcomeModal(false)"
+ >
+ <div class="welcome-modal-content">
+ <h2 id="welcome-modal-title">Important - Please read before continuing</h2>
+ <div id="welcome-modal-description">
+ <p>
+ The Godot Web Editor has some limitations compared to the native version.
+ Its main focus is education and experimentation;
+ <strong>it is not recommended for production</strong>.
+ </p>
+ <p>
+ Refer to the
+ <a
+ href="https://docs.godotengine.org/en/latest/tutorials/editor/using_the_web_editor.html"
+ target="_blank"
+ rel="noopener"
+ >Web editor documentation</a> for usage instructions and limitations.
+ </p>
+ </div>
+ <button id="welcome-modal-dismiss" class="btn" type="button" onclick="closeWelcomeModal(true)" style="margin-top: 1rem">
+ OK, don't show again
+ </button>
+ </div>
+ </div>
<div id="tabs-buttons">
<button id="btn-tab-loader" class="btn tab-btn" onclick="showTab('loader')">Loader</button>
<button id="btn-tab-editor" class="btn tab-btn" disabled="disabled" onclick="showTab('editor')">Editor</button>
@@ -274,7 +327,19 @@
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("service.worker.js");
}
+
+ if (localStorage.getItem("welcomeModalDismissed") !== 'true') {
+ document.getElementById("welcome-modal").style.display = "block";
+ document.getElementById("welcome-modal-dismiss").focus();
+ }
});
+
+ function closeWelcomeModal(dontShowAgain) {
+ document.getElementById("welcome-modal").style.display = "none";
+ if (dontShowAgain) {
+ localStorage.setItem("welcomeModalDismissed", 'true');
+ }
+ }
</script>
<script src="godot.tools.js"></script>
<script>//<![CDATA[
diff --git a/misc/dist/osx_template.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json b/misc/dist/osx_template.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json
index 6bf2edb02d..c4f8f71d0e 100644
--- a/misc/dist/osx_template.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json
+++ b/misc/dist/osx_template.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json
@@ -2,6 +2,6 @@
"file_format_version" : "1.0.0",
"ICD": {
"library_path": "../../../Frameworks/libMoltenVK.dylib",
- "api_version" : "1.0.0"
+ "api_version" : "1.1.0"
}
}
diff --git a/misc/dist/osx_tools.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json b/misc/dist/osx_tools.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json
index 6bf2edb02d..c4f8f71d0e 100644
--- a/misc/dist/osx_tools.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json
+++ b/misc/dist/osx_tools.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json
@@ -2,6 +2,6 @@
"file_format_version" : "1.0.0",
"ICD": {
"library_path": "../../../Frameworks/libMoltenVK.dylib",
- "api_version" : "1.0.0"
+ "api_version" : "1.1.0"
}
}
diff --git a/misc/scripts/check_ci_log.py b/misc/scripts/check_ci_log.py
new file mode 100755
index 0000000000..f2cdf95c7b
--- /dev/null
+++ b/misc/scripts/check_ci_log.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+
+if len(sys.argv) < 2:
+ print("ERROR: You must run program with file name as argument.")
+ sys.exit(1)
+
+fname = sys.argv[1]
+
+fileread = open(fname.strip(), "r")
+file_contents = fileread.read()
+
+# If find "ERROR: AddressSanitizer:", then happens invalid read or write
+# This is critical bug, so we need to fix this as fast as possible
+
+if file_contents.find("ERROR: AddressSanitizer:") != -1:
+ print("FATAL ERROR: An incorrectly used memory was found.")
+ sys.exit(1)
+
+# There is also possible, that program crashed with or without backtrace.
+
+if (
+ file_contents.find("Program crashed with signal") != -1
+ or file_contents.find("Dumping the backtrace") != -1
+ or file_contents.find("Segmentation fault (core dumped)") != -1
+):
+ print("FATAL ERROR: Godot has been crashed.")
+ sys.exit(1)
+
+# Finding memory leaks in Godot is quite difficult, because we need to take into
+# account leaks also in external libraries. They are usually provided without
+# debugging symbols, so the leak report from it usually has only 2/3 lines,
+# so searching for 5 element - "#4 0x" - should correctly detect the vast
+# majority of memory leaks
+
+if file_contents.find("ERROR: LeakSanitizer:") != -1:
+ if file_contents.find("#4 0x") != -1:
+ print("ERROR: Memory leak was found")
+ sys.exit(1)
+
+# It may happen that Godot detects leaking nodes/resources and removes them, so
+# this possibility should also be handled as a potential error, even if
+# LeakSanitizer doesn't report anything
+
+if file_contents.find("ObjectDB instances leaked at exit") != -1:
+ print("ERROR: Memory leak was found")
+ sys.exit(1)
+
+# In test project may be put several assert functions which will control if
+# project is executed with right parameters etc. which normally will not stop
+# execution of project
+
+if file_contents.find("Assertion failed") != -1:
+ print("ERROR: Assertion failed in project, check exectution log for more info")
+ sys.exit(1)
+
+# For now Godot leaks a lot of rendering stuff so for now we just show info
+# about it and this needs to be reenabled after fixing this memory leaks.
+
+if file_contents.find("were leaked") != -1 or file_contents.find("were never freed") != -1:
+ print("WARNING: Memory leak was found")
+
+sys.exit(0)
diff --git a/modules/etcpak/SCsub b/modules/etcpak/SCsub
index 821c6801b4..2d3b69be75 100644
--- a/modules/etcpak/SCsub
+++ b/modules/etcpak/SCsub
@@ -11,31 +11,15 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/etcpak/"
thirdparty_sources = [
- "Bitmap.cpp",
- "BitmapDownsampled.cpp",
- "BlockData.cpp",
- "ColorSpace.cpp",
- "DataProvider.cpp",
- "Debug.cpp",
"Dither.cpp",
- "Error.cpp",
- "mmap.cpp",
"ProcessDxtc.cpp",
"ProcessRGB.cpp",
- "System.cpp",
"Tables.cpp",
- "TaskDispatch.cpp",
- "Timing.cpp",
- "lz4/lz4.c",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_etcpak.Prepend(CPPPATH=[thirdparty_dir])
-# Also requires libpng headers
-if env["builtin_libpng"]:
- env_etcpak.Prepend(CPPPATH=["#thirdparty/libpng"])
-
env_thirdparty = env_etcpak.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
diff --git a/modules/etcpak/image_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index 251d2cd7b0..abc3c26188 100644
--- a/modules/etcpak/image_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* image_etcpak.cpp */
+/* image_compress_etcpak.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,21 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "image_etcpak.h"
+#include "image_compress_etcpak.h"
-#include "core/os/copymem.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "thirdparty/etcpak/ProcessDxtc.hpp"
#include "thirdparty/etcpak/ProcessRGB.hpp"
-// thresholds for the early compression-mode decision scheme in QuickETC2
-// which can be changed by the option -e
-float ecmd_threshold[3] = { 0.03f, 0.09f, 0.38f };
-
-EtcpakType _determine_etc_type(Image::UsedChannels p_source) {
- switch (p_source) {
+EtcpakType _determine_etc_type(Image::UsedChannels p_channels) {
+ switch (p_channels) {
case Image::USED_CHANNELS_L:
return EtcpakType::ETCPAK_TYPE_ETC1;
case Image::USED_CHANNELS_LA:
@@ -60,8 +55,8 @@ EtcpakType _determine_etc_type(Image::UsedChannels p_source) {
}
}
-EtcpakType _determine_dxt_type(Image::UsedChannels p_source) {
- switch (p_source) {
+EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) {
+ switch (p_channels) {
case Image::USED_CHANNELS_L:
return EtcpakType::ETCPAK_TYPE_DXT1;
case Image::USED_CHANNELS_LA:
@@ -78,93 +73,112 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_source) {
return EtcpakType::ETCPAK_TYPE_DXT5;
}
}
-void _compress_etc2(Image *p_img, float p_lossy_quality, Image::UsedChannels p_source) {
- EtcpakType type = _determine_etc_type(p_source);
- _compress_etcpak(type, p_img, p_lossy_quality, false, p_source);
+
+void _compress_etc1(Image *r_img, float p_lossy_quality) {
+ _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img, p_lossy_quality);
}
-void _compress_bc(Image *p_img, float p_lossy_quality, Image::UsedChannels p_source) {
- EtcpakType type = _determine_dxt_type(p_source);
- _compress_etcpak(type, p_img, p_lossy_quality, false, p_source);
+
+void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) {
+ EtcpakType type = _determine_etc_type(p_channels);
+ _compress_etcpak(type, r_img, p_lossy_quality);
}
-void _compress_etc1(Image *p_img, float p_lossy_quality) {
- _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, p_img, p_lossy_quality, true, Image::USED_CHANNELS_RGB);
+
+void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) {
+ EtcpakType type = _determine_dxt_type(p_channels);
+ _compress_etcpak(type, r_img, p_lossy_quality);
}
-void _compress_etcpak(EtcpakType p_compresstype, Image *p_img, float p_lossy_quality, bool force_etc1_format, Image::UsedChannels p_channels) {
- uint64_t t = OS::get_singleton()->get_ticks_msec();
- Image::Format img_format = p_img->get_format();
+void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality) {
+ uint64_t start_time = OS::get_singleton()->get_ticks_msec();
+ // TODO: See how to handle lossy quality.
+
+ Image::Format img_format = r_img->get_format();
if (img_format >= Image::FORMAT_DXT1) {
- return; //do not compress, already compressed
+ return; // Do not compress, already compressed.
}
-
if (img_format > Image::FORMAT_RGBA8) {
// TODO: we should be able to handle FORMAT_RGBA4444 and FORMAT_RGBA5551 eventually
return;
}
- Image::Format format = Image::FORMAT_RGBA8;
- if (p_img->get_format() != Image::FORMAT_RGBA8) {
- p_img->convert(Image::FORMAT_RGBA8);
+ // Use RGBA8 to convert.
+ if (img_format != Image::FORMAT_RGBA8) {
+ r_img->convert(Image::FORMAT_RGBA8);
}
- if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1 || force_etc1_format) {
- format = Image::FORMAT_ETC;
+
+ // Determine output format based on Etcpak type.
+ Image::Format target_format = Image::FORMAT_RGBA8;
+ if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
+ target_format = Image::FORMAT_ETC;
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
- format = Image::FORMAT_ETC2_RGB8;
+ target_format = Image::FORMAT_ETC2_RGB8;
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
- format = Image::FORMAT_ETC2_RA_AS_RG;
- p_img->convert_rg_to_ra_rgba8();
+ target_format = Image::FORMAT_ETC2_RA_AS_RG;
+ r_img->convert_rg_to_ra_rgba8();
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
- format = Image::FORMAT_ETC2_RGBA8;
+ target_format = Image::FORMAT_ETC2_RGBA8;
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
- format = Image::FORMAT_DXT1;
+ target_format = Image::FORMAT_DXT1;
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
- format = Image::FORMAT_DXT5_RA_AS_RG;
- p_img->convert_rg_to_ra_rgba8();
+ target_format = Image::FORMAT_DXT5_RA_AS_RG;
+ r_img->convert_rg_to_ra_rgba8();
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) {
- format = Image::FORMAT_DXT5;
+ target_format = Image::FORMAT_DXT5;
} else {
- ERR_FAIL();
+ ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format.");
}
- const bool mipmap = p_img->has_mipmaps();
- print_verbose("Encoding format: " + Image::get_format_name(format));
+ // Compress image data and (if required) mipmaps.
+
+ const bool mipmaps = r_img->has_mipmaps();
+ const int width = r_img->get_width();
+ const int height = r_img->get_height();
+ const uint8_t *src_read = r_img->get_data().ptr();
- Ref<Image> new_img;
- new_img.instance();
- new_img->create(p_img->get_width(), p_img->get_height(), mipmap, format);
- Vector<uint8_t> data = new_img->get_data();
- uint8_t *wr = data.ptrw();
+ print_verbose(vformat("ETCPAK: Encoding image size %dx%d to format %s.", width, height, Image::get_format_name(target_format)));
- Ref<Image> image = p_img->duplicate();
- int mmc = 1 + (mipmap ? Image::get_image_required_mipmaps(new_img->get_width(), new_img->get_height(), format) : 0);
- for (int i = 0; i < mmc; i++) {
- int ofs, size, mip_w, mip_h;
- new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, mip_w, mip_h);
+ int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
+ Vector<uint8_t> dest_data;
+ dest_data.resize(dest_size);
+ uint8_t *dest_write = dest_data.ptrw();
+
+ int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+
+ for (int i = 0; i < mip_count + 1; i++) {
+ // Get write mip metrics for target image.
+ int mip_w, mip_h;
+ int mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, mip_w, mip_h);
+ // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
+ ERR_FAIL_COND(mip_ofs % 8 != 0);
+ uint64_t *dest_mip_write = (uint64_t *)&dest_write[mip_ofs];
+
+ // Block size. Align stride to multiple of 4 (RGBA8).
mip_w = (mip_w + 3) & ~3;
mip_h = (mip_h + 3) & ~3;
- Vector<uint8_t> dst_data;
- dst_data.resize(size);
- int mipmap_ofs = image->get_mipmap_offset(i);
-
- const uint32_t *image_read = (const uint32_t *)&image->get_data().ptr()[mipmap_ofs];
- uint64_t *dst_write = (uint64_t *)dst_data.ptrw();
- if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1 || force_etc1_format) {
- CompressEtc1RgbDither(image_read, dst_write, mip_w * mip_h / 16, mip_w);
+ const uint32_t blocks = mip_w * mip_h / 16;
+
+ // Get mip data from source image for reading.
+ int src_mip_ofs = r_img->get_mipmap_offset(i);
+ const uint32_t *src_mip_read = (const uint32_t *)&src_read[src_mip_ofs];
+
+ if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
+ CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2 || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
- CompressEtc2Rgb(image_read, dst_write, mip_w * mip_h / 16, mip_w);
+ CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
- CompressEtc2Rgba(image_read, dst_write, mip_w * mip_h / 16, mip_w);
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5 || p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
- CompressDxt5(image_read, dst_write, mip_w * mip_h / 16, mip_w);
+ CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
- CompressDxt1Dither(image_read, dst_write, mip_w * mip_h / 16, mip_w);
+ CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5 || p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
+ CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w);
} else {
- ERR_FAIL();
+ ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format.");
}
- copymem(&wr[ofs], dst_data.ptr(), size);
}
- p_img->create(new_img->get_width(), new_img->get_height(), mipmap, format, data);
- print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - t)));
+ // Replace original image with compressed one.
+ r_img->create(width, height, mipmaps, target_format, dest_data);
+
+ print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time)));
}
diff --git a/modules/etcpak/image_etcpak.h b/modules/etcpak/image_compress_etcpak.h
index 0137bab7cc..ccf157fada 100644
--- a/modules/etcpak/image_etcpak.h
+++ b/modules/etcpak/image_compress_etcpak.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* image_etcpak.h */
+/* image_compress_etcpak.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef IMAGE_ETCPAK_H
-#define IMAGE_ETCPAK_H
+#ifndef IMAGE_COMPRESS_ETCPAK_H
+#define IMAGE_COMPRESS_ETCPAK_H
#include "core/io/image.h"
@@ -43,9 +43,10 @@ enum class EtcpakType {
ETCPAK_TYPE_DXT5_RA_AS_RG,
};
-void _compress_etcpak(EtcpakType p_compresstype, Image *p_img, float p_lossy_quality, bool force_etc1_format, Image::UsedChannels p_channels);
-void _compress_etc1(Image *p_img, float p_lossy_quality);
-void _compress_etc2(Image *p_img, float p_lossy_quality, Image::UsedChannels p_source);
-void _compress_bc(Image *p_img, float p_lossy_quality, Image::UsedChannels p_source);
+void _compress_etc1(Image *r_img, float p_lossy_quality);
+void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels);
+void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels);
-#endif // IMAGE_ETCPAK_H
+void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality);
+
+#endif // IMAGE_COMPRESS_ETCPAK_H
diff --git a/modules/etcpak/register_types.cpp b/modules/etcpak/register_types.cpp
index fcc0bc8b6f..d57d2f747a 100644
--- a/modules/etcpak/register_types.cpp
+++ b/modules/etcpak/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "image_etcpak.h"
+#include "image_compress_etcpak.h"
void register_etcpak_types() {
Image::_image_compress_etc1_func = _compress_etc1;
diff --git a/modules/etcpak/register_types.h b/modules/etcpak/register_types.h
index 9b300a3275..a9e10a4aae 100644
--- a/modules/etcpak/register_types.h
+++ b/modules/etcpak/register_types.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef ETCPAK_REGISTER_TYPES_H
+#define ETCPAK_REGISTER_TYPES_H
+
void register_etcpak_types();
void unregister_etcpak_types();
+
+#endif // ETCPAK_REGISTER_TYPES_H
diff --git a/modules/fbx/data/fbx_material.cpp b/modules/fbx/data/fbx_material.cpp
index 5995097b2f..d54ac86e9f 100644
--- a/modules/fbx/data/fbx_material.cpp
+++ b/modules/fbx/data/fbx_material.cpp
@@ -277,7 +277,7 @@ Ref<StandardMaterial3D> FBXMaterial::import_material(ImportState &state) {
}
/// ALL below is related to properties
- for (FBXDocParser::LazyPropertyMap::value_type iter : material->Props()->GetLazyProperties()) {
+ for (FBXDocParser::LazyPropertyMap::value_type iter : material->GetLazyProperties()) {
const std::string name = iter.first;
if (name.empty()) {
@@ -317,7 +317,7 @@ Ref<StandardMaterial3D> FBXMaterial::import_material(ImportState &state) {
ERR_CONTINUE_MSG(desc == PROPERTY_DESC_NOT_FOUND, "The FBX material parameter: `" + String(name.c_str()) + "` was not recognized. Please open an issue so we can add the support to it.");
- const FBXDocParser::PropertyTable *tbl = material->Props();
+ const FBXDocParser::PropertyTable *tbl = material;
FBXDocParser::PropertyPtr prop = tbl->Get(name);
ERR_CONTINUE_MSG(prop == nullptr, "This file may be corrupted because is not possible to extract the material parameter: " + String(name.c_str()));
diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp
index b088dd8640..304d1598f6 100644
--- a/modules/fbx/data/fbx_mesh_data.cpp
+++ b/modules/fbx/data/fbx_mesh_data.cpp
@@ -101,20 +101,6 @@ HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2>> *p_data, Hash
return collection;
}
-typedef int Vertex;
-typedef int SurfaceId;
-typedef int PolygonId;
-typedef int DataIndex;
-
-struct SurfaceData {
- Ref<SurfaceTool> surface_tool;
- OrderedHashMap<Vertex, int> lookup_table; // proposed fix is to replace lookup_table[vertex_id] to give the position of the vertices_map[int] index.
- LocalVector<Vertex> vertices_map; // this must be ordered the same as insertion <-- slow to do find() operation.
- Ref<Material> material;
- HashMap<PolygonId, Vector<DataIndex>> surface_polygon_vertex;
- Array morphs;
-};
-
EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) {
mesh_geometry = p_mesh_geometry;
// todo: make this just use a uint64_t FBX ID this is a copy of our original materials unfortunately.
@@ -307,11 +293,9 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
// Triangulate the various polygons and add the indices.
for (const PolygonId *polygon_id = surface->surface_polygon_vertex.next(nullptr); polygon_id != nullptr; polygon_id = surface->surface_polygon_vertex.next(polygon_id)) {
const Vector<DataIndex> *indices = surface->surface_polygon_vertex.getptr(*polygon_id);
-
triangulate_polygon(
- surface->surface_tool,
+ surface,
*indices,
- surface->vertices_map,
vertices);
}
}
@@ -336,7 +320,7 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
morph_st->begin(Mesh::PRIMITIVE_TRIANGLES);
for (unsigned int vi = 0; vi < surface->vertices_map.size(); vi += 1) {
- const Vertex vertex = surface->vertices_map[vi];
+ const Vertex &vertex = surface->vertices_map[vi];
add_vertex(
state,
morph_st,
@@ -398,6 +382,9 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
EditorSceneImporterMeshNode3D *godot_mesh = memnew(EditorSceneImporterMeshNode3D);
godot_mesh->set_mesh(mesh);
+ const String name = ImportUtils::FBXNodeToName(model->Name());
+ godot_mesh->set_name(name); // hurry up compiling >.<
+ mesh->set_name("mesh3d-" + name);
return godot_mesh;
}
@@ -816,8 +803,10 @@ void FBXMeshData::add_vertex(
p_surface_tool->add_vertex((p_vertices_position[p_vertex] + p_morph_value) * p_scale);
}
-void FBXMeshData::triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, const Vector<Vertex> p_surface_vertex_map, const std::vector<Vector3> &p_vertices) const {
+void FBXMeshData::triangulate_polygon(SurfaceData *surface, const Vector<int> &p_polygon_vertex, const std::vector<Vector3> &p_vertices) const {
+ Ref<SurfaceTool> st(surface->surface_tool);
const int polygon_vertex_count = p_polygon_vertex.size();
+ //const Vector<Vertex>& p_surface_vertex_map
if (polygon_vertex_count == 1) {
// point to triangle
st->add_index(p_polygon_vertex[0]);
@@ -856,9 +845,9 @@ void FBXMeshData::triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon
is_simple_convex = true;
Vector3 first_vec;
for (int i = 0; i < polygon_vertex_count; i += 1) {
- const Vector3 p1 = p_vertices[p_surface_vertex_map[p_polygon_vertex[i]]];
- const Vector3 p2 = p_vertices[p_surface_vertex_map[p_polygon_vertex[(i + 1) % polygon_vertex_count]]];
- const Vector3 p3 = p_vertices[p_surface_vertex_map[p_polygon_vertex[(i + 2) % polygon_vertex_count]]];
+ const Vector3 p1 = p_vertices[surface->vertices_map[p_polygon_vertex[i]]];
+ const Vector3 p2 = p_vertices[surface->vertices_map[p_polygon_vertex[(i + 1) % polygon_vertex_count]]];
+ const Vector3 p3 = p_vertices[surface->vertices_map[p_polygon_vertex[(i + 2) % polygon_vertex_count]]];
const Vector3 edge1 = p1 - p2;
const Vector3 edge2 = p3 - p2;
@@ -893,7 +882,7 @@ void FBXMeshData::triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon
std::vector<Vector3> poly_vertices(polygon_vertex_count);
for (int i = 0; i < polygon_vertex_count; i += 1) {
- poly_vertices[i] = p_vertices[p_surface_vertex_map[p_polygon_vertex[i]]];
+ poly_vertices[i] = p_vertices[surface->vertices_map[p_polygon_vertex[i]]];
}
const Vector3 poly_norm = get_poly_normal(poly_vertices);
diff --git a/modules/fbx/data/fbx_mesh_data.h b/modules/fbx/data/fbx_mesh_data.h
index 77510ff2ec..575f833584 100644
--- a/modules/fbx/data/fbx_mesh_data.h
+++ b/modules/fbx/data/fbx_mesh_data.h
@@ -32,6 +32,8 @@
#define FBX_MESH_DATA_H
#include "core/templates/hash_map.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/ordered_hash_map.h"
#include "editor/import/resource_importer_scene.h"
#include "editor/import/scene_importer_mesh_node_3d.h"
#include "scene/3d/mesh_instance_3d.h"
@@ -47,6 +49,20 @@ struct FBXMeshData;
struct FBXBone;
struct ImportState;
+typedef int Vertex;
+typedef int SurfaceId;
+typedef int PolygonId;
+typedef int DataIndex;
+
+struct SurfaceData {
+ Ref<SurfaceTool> surface_tool;
+ OrderedHashMap<Vertex, int> lookup_table; // proposed fix is to replace lookup_table[vertex_id] to give the position of the vertices_map[int] index.
+ LocalVector<Vertex> vertices_map; // this must be ordered the same as insertion <-- slow to do find() operation.
+ Ref<Material> material;
+ HashMap<PolygonId, Vector<DataIndex>> surface_polygon_vertex;
+ Array morphs;
+};
+
struct VertexWeightMapping {
Vector<real_t> weights;
Vector<int> bones;
@@ -127,7 +143,7 @@ private:
const Vector3 &p_morph_value = Vector3(),
const Vector3 &p_morph_normal = Vector3());
- void triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, Vector<int> p_surface_vertex_map, const std::vector<Vector3> &p_vertices) const;
+ void triangulate_polygon(SurfaceData *surface, const Vector<int> &p_polygon_vertex, const std::vector<Vector3> &p_vertices) const;
/// This function is responsible to convert the FBX polygon vertex to
/// vertex index.
diff --git a/modules/fbx/data/pivot_transform.cpp b/modules/fbx/data/pivot_transform.cpp
index 1895af6f9f..f4055c830f 100644
--- a/modules/fbx/data/pivot_transform.cpp
+++ b/modules/fbx/data/pivot_transform.cpp
@@ -33,7 +33,7 @@
#include "tools/import_utils.h"
void PivotTransform::ReadTransformChain() {
- const FBXDocParser::PropertyTable *props = fbx_model->Props();
+ const FBXDocParser::PropertyTable *props = fbx_model;
const FBXDocParser::Model::RotOrder &rot = fbx_model->RotationOrder();
const FBXDocParser::TransformInheritance &inheritType = fbx_model->InheritType();
inherit_type = inheritType; // copy the inherit type we need it in the second step.
diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp
index 55d524883f..b23a58a414 100644
--- a/modules/fbx/editor_scene_importer_fbx.cpp
+++ b/modules/fbx/editor_scene_importer_fbx.cpp
@@ -44,7 +44,6 @@
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
-#include "scene/3d/mesh_instance_3d.h"
#include "scene/main/node.h"
#include "scene/resources/material.h"
@@ -121,15 +120,27 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
print_verbose("[doc] opening fbx file: " + p_path);
print_verbose("[doc] fbx header: " + fbx_header_string);
+ bool corrupt = false;
// safer to check this way as there can be different formatted headers
if (fbx_header_string.find("Kaydara FBX Binary", 0) != -1) {
is_binary = true;
print_verbose("[doc] is binary");
- FBXDocParser::TokenizeBinary(tokens, (const char *)data.ptrw(), (size_t)data.size());
+
+ FBXDocParser::TokenizeBinary(tokens, (const char *)data.ptrw(), (size_t)data.size(), corrupt);
+
} else {
print_verbose("[doc] is ascii");
- FBXDocParser::Tokenize(tokens, (const char *)data.ptrw(), (size_t)data.size());
+ FBXDocParser::Tokenize(tokens, (const char *)data.ptrw(), (size_t)data.size(), corrupt);
+ }
+
+ if (corrupt) {
+ for (FBXDocParser::TokenPtr token : tokens) {
+ delete token;
+ }
+ tokens.clear();
+ ERR_PRINT(vformat("Cannot import FBX file: %s the file is corrupt so we safely exited parsing the file.", p_path));
+ return memnew(Node3D);
}
// The import process explained:
@@ -141,6 +152,16 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
// use this information to construct a very rudimentary
// parse-tree representing the FBX scope structure
FBXDocParser::Parser parser(tokens, is_binary);
+
+ if (parser.IsCorrupt()) {
+ for (FBXDocParser::TokenPtr token : tokens) {
+ delete token;
+ }
+ tokens.clear();
+ ERR_PRINT(vformat("Cannot import FBX file: %s the file is corrupt so we safely exited parsing the file.", p_path));
+ return memnew(Node3D);
+ }
+
FBXDocParser::ImportSettings settings;
settings.strictMode = false;
@@ -153,12 +174,10 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
// safety for version handling
if (doc.IsSafeToImport()) {
bool is_blender_fbx = false;
- //const FBXDocParser::PropertyPtr app_vendor = p_document->GlobalSettingsPtr()->Props()
- // p_document->Creator()
- const FBXDocParser::PropertyTable *import_props = doc.GetMetadataProperties();
- const FBXDocParser::PropertyPtr app_name = import_props->Get("Original|ApplicationName");
- const FBXDocParser::PropertyPtr app_vendor = import_props->Get("Original|ApplicationVendor");
- const FBXDocParser::PropertyPtr app_version = import_props->Get("Original|ApplicationVersion");
+ const FBXDocParser::PropertyTable &import_props = doc.GetMetadataProperties();
+ const FBXDocParser::PropertyPtr app_name = import_props.Get("Original|ApplicationName");
+ const FBXDocParser::PropertyPtr app_vendor = import_props.Get("Original|ApplicationVendor");
+ const FBXDocParser::PropertyPtr app_version = import_props.Get("Original|ApplicationVersion");
//
if (app_name) {
const FBXDocParser::TypedProperty<std::string> *app_name_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_name);
@@ -200,6 +219,11 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
return spatial;
} else {
+ for (FBXDocParser::TokenPtr token : tokens) {
+ delete token;
+ }
+ tokens.clear();
+
ERR_PRINT(vformat("Cannot import FBX file: %s. It uses file format %d which is unsupported by Godot. Please re-export it or convert it to a newer format.", p_path, doc.FBXVersion()));
}
}
@@ -892,7 +916,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
uint64_t target_id = target->ID();
String target_name = ImportUtils::FBXNodeToName(target->Name());
- const FBXDocParser::PropertyTable *properties = curve_node->Props();
+ const FBXDocParser::PropertyTable *properties = curve_node;
bool got_x = false, got_y = false, got_z = false;
float offset_x = FBXDocParser::PropertyGet<float>(properties, "d|X", got_x);
float offset_y = FBXDocParser::PropertyGet<float>(properties, "d|Y", got_y);
@@ -1047,7 +1071,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
Ref<FBXNode> target_node = state.fbx_target_map[target_id];
const FBXDocParser::Model *model = target_node->fbx_model;
- const FBXDocParser::PropertyTable *props = model->Props();
+ const FBXDocParser::PropertyTable *props = dynamic_cast<const FBXDocParser::PropertyTable *>(model);
Map<StringName, FBXTrack> &track_data = track->value();
FBXTrack &translation_keys = track_data[StringName("T")];
diff --git a/modules/fbx/fbx_parser/FBXAnimation.cpp b/modules/fbx/fbx_parser/FBXAnimation.cpp
index 4ab5edebb1..1690df6943 100644
--- a/modules/fbx/fbx_parser/FBXAnimation.cpp
+++ b/modules/fbx/fbx_parser/FBXAnimation.cpp
@@ -130,9 +130,7 @@ AnimationCurve::~AnimationCurve() {
AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name,
const Document &doc, const char *const *target_prop_whitelist /*= NULL*/,
size_t whitelist_size /*= 0*/) :
- Object(id, element, name), doc(doc) {
- const ScopePtr sc = GetRequiredScope(element);
-
+ Object(id, element, name), target(), doc(doc) {
// find target node
const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
@@ -154,8 +152,6 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, co
prop = con->PropertyName();
break;
}
-
- props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false);
}
// ------------------------------------------------------------------------------------------------
@@ -187,10 +183,6 @@ const AnimationMap &AnimationCurveNode::Curves() const {
// ------------------------------------------------------------------------------------------------
AnimationLayer::AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Object(id, element, name), doc(doc) {
- const ScopePtr sc = GetRequiredScope(element);
-
- // note: the props table here bears little importance and is usually absent
- props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true);
}
// ------------------------------------------------------------------------------------------------
@@ -248,11 +240,6 @@ const AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_pro
// ------------------------------------------------------------------------------------------------
AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Object(id, element, name) {
- const ScopePtr sc = GetRequiredScope(element);
-
- // note: we don't currently use any of these properties so we shouldn't bother if it is missing
- props = GetPropertyTable(doc, "AnimationStack.FbxAnimStack", element, sc, true);
-
// resolve attached animation layers
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer");
layers.reserve(conns.size());
@@ -282,9 +269,5 @@ AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std:
// ------------------------------------------------------------------------------------------------
AnimationStack::~AnimationStack() {
- if (props != nullptr) {
- delete props;
- props = nullptr;
- }
}
} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
index 1d2b7765c5..1eee10b251 100644
--- a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
+++ b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
@@ -130,6 +130,7 @@ Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset
line(offset),
column(BINARY_MARKER) {
#ifdef DEBUG_ENABLED
+ // contents is bad.. :/
contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
#endif
// calc length
@@ -232,9 +233,11 @@ unsigned int ReadString(const char *&sbegin_out, const char *&send_out, const ch
}
// ------------------------------------------------------------------------------------------------
-void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end) {
+void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end, bool &corrupt) {
if (Offset(cursor, end) < 1) {
TokenizeError("cannot ReadData, out of bounds reading length", input, cursor);
+ corrupt = true;
+ return;
}
const char type = *cursor;
@@ -328,9 +331,7 @@ void ReadData(const char *&sbegin_out, const char *&send_out, const char *input,
}
cursor += comp_len;
break;
- }
-
- // string
+ } // string
case 'S': {
const char *sb, *se;
// 0 characters can legally happen in such strings
@@ -338,11 +339,15 @@ void ReadData(const char *&sbegin_out, const char *&send_out, const char *input,
break;
}
default:
+ corrupt = true; // must exit
TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1), input, cursor);
+ return;
}
if (cursor > end) {
+ corrupt = true; // must exit
TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1), input, cursor);
+ return;
}
// the type code is contained in the returned range
@@ -350,7 +355,7 @@ void ReadData(const char *&sbegin_out, const char *&send_out, const char *input,
}
// ------------------------------------------------------------------------------------------------
-bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits) {
+bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits, bool &corrupt) {
// the first word contains the offset at which this block ends
const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
@@ -364,8 +369,12 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
if (end_offset > Offset(input, end)) {
TokenizeError("block offset is out of range", input, cursor);
+ corrupt = true;
+ return false;
} else if (end_offset < Offset(input, cursor)) {
TokenizeError("block offset is negative out of range", input, cursor);
+ corrupt = true;
+ return false;
}
// the second data word contains the number of properties in the scope
@@ -375,7 +384,7 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
// now comes the name of the scope/key
- const char *sbeg, *send;
+ const char *sbeg = nullptr, *send = nullptr;
ReadString(sbeg, send, input, cursor, end);
output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor)));
@@ -383,7 +392,10 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
// now come the individual properties
const char *begin_cursor = cursor;
for (unsigned int i = 0; i < prop_count; ++i) {
- ReadData(sbeg, send, input, cursor, begin_cursor + prop_length);
+ ReadData(sbeg, send, input, cursor, begin_cursor + prop_length, corrupt);
+ if (corrupt) {
+ return false;
+ }
output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor)));
@@ -394,6 +406,8 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
if (Offset(begin_cursor, cursor) != prop_length) {
TokenizeError("property length not reached, something is wrong", input, cursor);
+ corrupt = true;
+ return false;
}
// at the end of each nested block, there is a NUL record to indicate
@@ -410,13 +424,18 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
// XXX this is vulnerable to stack overflowing ..
while (Offset(input, cursor) < end_offset - sentinel_block_length) {
- ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits);
+ ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits, corrupt);
+ if (corrupt) {
+ return false;
+ }
}
output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor)));
for (unsigned int i = 0; i < sentinel_block_length; ++i) {
if (cursor[i] != '\0') {
TokenizeError("failed to read nested block sentinel, expected all bytes to be 0", input, cursor);
+ corrupt = true;
+ return false;
}
}
cursor += sentinel_block_length;
@@ -424,6 +443,8 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
if (Offset(input, cursor) != end_offset) {
TokenizeError("scope length not reached, something is wrong", input, cursor);
+ corrupt = true;
+ return false;
}
return true;
@@ -432,7 +453,7 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
// ------------------------------------------------------------------------------------------------
// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
-void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length) {
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, bool &corrupt) {
if (length < 0x1b) {
//TokenizeError("file is too short",0);
}
@@ -459,7 +480,7 @@ void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length)
const bool is64bits = version >= 7500;
const char *end = input + length;
while (cursor < end) {
- if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
+ if (!ReadScope(output_tokens, input, cursor, input + length, is64bits, corrupt)) {
break;
}
}
diff --git a/modules/fbx/fbx_parser/FBXDeformer.cpp b/modules/fbx/fbx_parser/FBXDeformer.cpp
index 4b774e6b2a..039718ae15 100644
--- a/modules/fbx/fbx_parser/FBXDeformer.cpp
+++ b/modules/fbx/fbx_parser/FBXDeformer.cpp
@@ -89,10 +89,6 @@ using namespace Util;
// ------------------------------------------------------------------------------------------------
Deformer::Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name) {
- const ScopePtr sc = GetRequiredScope(element);
-
- const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
- props = GetPropertyTable(doc, "Deformer.Fbx" + classname, element, sc, true);
}
// ------------------------------------------------------------------------------------------------
@@ -101,10 +97,6 @@ Deformer::~Deformer() {
Constraint::Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name) {
- const ScopePtr sc = GetRequiredScope(element);
- const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
- // used something.fbx as this is a cache name.
- props = GetPropertyTable(doc, "Something.Fbx" + classname, element, sc, true);
}
Constraint::~Constraint() {
diff --git a/modules/fbx/fbx_parser/FBXDocument.cpp b/modules/fbx/fbx_parser/FBXDocument.cpp
index d156db201b..bb85d6ff7c 100644
--- a/modules/fbx/fbx_parser/FBXDocument.cpp
+++ b/modules/fbx/fbx_parser/FBXDocument.cpp
@@ -228,7 +228,7 @@ ObjectPtr LazyObject::LoadObject() {
// ------------------------------------------------------------------------------------------------
Object::Object(uint64_t id, const ElementPtr element, const std::string &name) :
- element(element), name(name), id(id) {
+ PropertyTable(element), element(element), name(name), id(id) {
}
// ------------------------------------------------------------------------------------------------
@@ -237,17 +237,13 @@ Object::~Object() {
}
// ------------------------------------------------------------------------------------------------
-FileGlobalSettings::FileGlobalSettings(const Document &doc, const PropertyTable *props) :
- props(props), doc(doc) {
+FileGlobalSettings::FileGlobalSettings(const Document &doc) :
+ PropertyTable(), doc(doc) {
// empty
}
// ------------------------------------------------------------------------------------------------
FileGlobalSettings::~FileGlobalSettings() {
- if (props != nullptr) {
- delete props;
- props = nullptr;
- }
}
// ------------------------------------------------------------------------------------------------
@@ -287,15 +283,12 @@ Document::~Document() {
delete v.second;
}
- if (metadata_properties != nullptr) {
- delete metadata_properties;
- }
// clear globals import pointer
globals.reset();
}
// ------------------------------------------------------------------------------------------------
-static const unsigned int LowerSupportedVersion = 7300;
+static const unsigned int LowerSupportedVersion = 7100;
static const unsigned int UpperSupportedVersion = 7700;
bool Document::ReadHeader() {
@@ -306,6 +299,11 @@ bool Document::ReadHeader() {
DOMError("no FBXHeaderExtension dictionary found");
}
+ if (parser.IsCorrupt()) {
+ DOMError("File is corrupt");
+ return false;
+ }
+
const ScopePtr shead = ehead->Compound();
fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead, "FBXVersion", ehead), 0));
@@ -325,18 +323,11 @@ bool Document::ReadHeader() {
creator = ParseTokenAsString(GetRequiredToken(ecreator, 0));
}
- //
// Scene Info
- //
-
const ElementPtr scene_info = shead->GetElement("SceneInfo");
if (scene_info) {
- PropertyTable *fileExportProps = const_cast<PropertyTable *>(GetPropertyTable(*this, "", scene_info, scene_info->Compound(), true));
-
- if (fileExportProps) {
- metadata_properties = fileExportProps;
- }
+ metadata_properties.Setup(scene_info);
}
const ElementPtr etimestamp = shead->GetElement("CreationTimeStamp");
@@ -358,23 +349,7 @@ bool Document::ReadHeader() {
void Document::ReadGlobalSettings() {
ERR_FAIL_COND_MSG(globals != nullptr, "Global settings is already setup this is a serious error and should be reported");
- const ScopePtr sc = parser.GetRootScope();
- const ElementPtr ehead = sc->GetElement("GlobalSettings");
- if (nullptr == ehead || !ehead->Compound()) {
- DOMWarning("no GlobalSettings dictionary found");
- globals = std::make_shared<FileGlobalSettings>(*this, new PropertyTable());
- return;
- }
-
- const PropertyTable *props = GetPropertyTable(*this, "", ehead, ehead->Compound(), true);
-
- //double v = PropertyGet<float>( *props, std::string("UnitScaleFactor"), 1.0 );
-
- if (!props) {
- DOMError("GlobalSettings dictionary contains no property table");
- }
-
- globals = std::make_shared<FileGlobalSettings>(*this, props);
+ globals = std::make_shared<FileGlobalSettings>(*this);
}
// ------------------------------------------------------------------------------------------------
@@ -445,58 +420,6 @@ void Document::ReadObjects() {
// ------------------------------------------------------------------------------------------------
void Document::ReadPropertyTemplates() {
- const ScopePtr sc = parser.GetRootScope();
- // read property templates from "Definitions" section
- const ElementPtr edefs = sc->GetElement("Definitions");
- if (!edefs || !edefs->Compound()) {
- DOMWarning("no Definitions dictionary found");
- return;
- }
-
- const ScopePtr sdefs = edefs->Compound();
- const ElementCollection otypes = sdefs->GetCollection("ObjectType");
- for (ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
- const ElementPtr el = (*it).second;
- const ScopePtr sc_2 = el->Compound();
- if (!sc_2) {
- DOMWarning("expected nested scope in ObjectType, ignoring", el);
- continue;
- }
-
- const TokenList &tok = el->Tokens();
- if (tok.empty()) {
- DOMWarning("expected name for ObjectType element, ignoring", el);
- continue;
- }
-
- const std::string &oname = ParseTokenAsString(tok[0]);
-
- const ElementCollection templs = sc_2->GetCollection("PropertyTemplate");
- for (ElementMap::const_iterator iter = templs.first; iter != templs.second; ++iter) {
- const ElementPtr el_2 = (*iter).second;
- const ScopePtr sc_3 = el_2->Compound();
- if (!sc_3) {
- DOMWarning("expected nested scope in PropertyTemplate, ignoring", el);
- continue;
- }
-
- const TokenList &tok_2 = el_2->Tokens();
- if (tok_2.empty()) {
- DOMWarning("expected name for PropertyTemplate element, ignoring", el);
- continue;
- }
-
- const std::string &pname = ParseTokenAsString(tok_2[0]);
-
- const ElementPtr Properties70 = sc_3->GetElement("Properties70");
- if (Properties70) {
- // PropertyTable(const ElementPtr element, const PropertyTable* templateProps);
- const PropertyTable *props = new PropertyTable(Properties70, nullptr);
-
- templates[oname + "." + pname] = props;
- }
- }
- }
}
// ------------------------------------------------------------------------------------------------
diff --git a/modules/fbx/fbx_parser/FBXDocument.h b/modules/fbx/fbx_parser/FBXDocument.h
index 20e635a6a4..49b7c11c31 100644
--- a/modules/fbx/fbx_parser/FBXDocument.h
+++ b/modules/fbx/fbx_parser/FBXDocument.h
@@ -130,7 +130,7 @@ private:
};
/** Base class for in-memory (DOM) representations of FBX objects */
-class Object {
+class Object : public PropertyTable {
public:
Object(uint64_t id, const ElementPtr element, const std::string &name);
@@ -149,9 +149,9 @@ public:
}
protected:
- const ElementPtr element;
+ const ElementPtr element = nullptr;
const std::string name;
- const uint64_t id = 0;
+ const uint64_t id;
};
/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table,
@@ -159,22 +159,13 @@ protected:
class NodeAttribute : public Object {
public:
NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
-
virtual ~NodeAttribute();
-
- const PropertyTable *Props() const {
- return props;
- }
-
-private:
- const PropertyTable *props;
};
/** DOM base class for FBX camera settings attached to a node */
class CameraSwitcher : public NodeAttribute {
public:
CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
-
virtual ~CameraSwitcher();
int CameraID() const {
@@ -190,26 +181,26 @@ public:
}
private:
- int cameraId;
+ int cameraId = 0;
std::string cameraName;
std::string cameraIndexName;
};
#define fbx_stringize(a) #a
-#define fbx_simple_property(name, type, default_value) \
- type name() const { \
- return PropertyGet<type>(Props(), fbx_stringize(name), (default_value)); \
+#define fbx_simple_property(name, type, default_value) \
+ type name() const { \
+ return PropertyGet<type>(this, fbx_stringize(name), (default_value)); \
}
// XXX improve logging
-#define fbx_simple_enum_property(name, type, default_value) \
- type name() const { \
- const int ival = PropertyGet<int>(Props(), fbx_stringize(name), static_cast<int>(default_value)); \
- if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \
- return static_cast<type>(default_value); \
- } \
- return static_cast<type>(ival); \
+#define fbx_simple_enum_property(name, type, default_value) \
+ type name() const { \
+ const int ival = PropertyGet<int>(this, fbx_stringize(name), static_cast<int>(default_value)); \
+ if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \
+ return static_cast<type>(default_value); \
+ } \
+ return static_cast<type>(ival); \
}
class FbxPoseNode;
@@ -256,7 +247,7 @@ public:
}
private:
- uint64_t target_id;
+ uint64_t target_id = 0;
Transform transform;
};
@@ -264,7 +255,6 @@ private:
class Camera : public NodeAttribute {
public:
Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
-
virtual ~Camera();
fbx_simple_property(Position, Vector3, Vector3(0, 0, 0));
@@ -380,7 +370,6 @@ public:
};
Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
-
virtual ~Model();
fbx_simple_property(QuaternionInterpolate, int, 0);
@@ -466,10 +455,6 @@ public:
return culling;
}
- const PropertyTable *Props() const {
- return props;
- }
-
/** Get material links */
const std::vector<const Material *> &GetMaterials() const {
return materials;
@@ -498,13 +483,11 @@ private:
std::string shading;
std::string culling;
- const PropertyTable *props = nullptr;
};
class ModelLimbNode : public Model {
public:
ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
-
virtual ~ModelLimbNode();
};
@@ -512,7 +495,6 @@ public:
class Texture : public Object {
public:
Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
-
virtual ~Texture();
const std::string &Type() const {
@@ -539,10 +521,6 @@ public:
return uvScaling;
}
- const PropertyTable *Props() const {
- return props;
- }
-
// return a 4-tuple
const unsigned int *Crop() const {
return crop;
@@ -560,10 +538,8 @@ private:
std::string relativeFileName;
std::string fileName;
std::string alphaSource;
- const PropertyTable *props = nullptr;
unsigned int crop[4] = { 0 };
-
const Video *media = nullptr;
};
@@ -626,8 +602,8 @@ public:
private:
std::vector<const Texture *> textures;
- BlendMode blendMode;
- float alpha;
+ BlendMode blendMode = BlendMode::BlendMode_Additive;
+ float alpha = 0;
};
typedef std::map<std::string, const Texture *> TextureMap;
@@ -656,10 +632,6 @@ public:
return relativeFileName;
}
- const PropertyTable *Props() const {
- return props;
- }
-
const uint8_t *Content() const {
return content;
}
@@ -687,7 +659,6 @@ private:
std::string type;
std::string relativeFileName;
std::string fileName;
- const PropertyTable *props = nullptr;
uint64_t contentLength = 0;
uint8_t *content = nullptr;
@@ -708,10 +679,6 @@ public:
return multilayer;
}
- const PropertyTable *Props() const {
- return props;
- }
-
const TextureMap &Textures() const {
return textures;
}
@@ -722,8 +689,7 @@ public:
private:
std::string shading;
- bool multilayer;
- const PropertyTable *props;
+ bool multilayer = false;
TextureMap textures;
LayeredTextureMap layeredTextures;
@@ -791,10 +757,6 @@ public:
virtual ~AnimationCurveNode();
- const PropertyTable *Props() const {
- return props;
- }
-
const AnimationMap &Curves() const;
/** Object the curve is assigned to, this can be NULL if the
@@ -819,7 +781,6 @@ public:
private:
Object *target = nullptr;
- const PropertyTable *props;
mutable AnimationMap curves;
std::string prop;
const Document &doc;
@@ -837,18 +798,12 @@ public:
AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
virtual ~AnimationLayer();
- const PropertyTable *Props() const {
- //ai_assert(props.get());
- return props;
- }
-
/* the optional white list specifies a list of property names for which the caller
wants animations for. Curves not matching this list will not be added to the
animation layer. */
const AnimationCurveNodeList Nodes(const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0) const;
private:
- const PropertyTable *props;
const Document &doc;
};
@@ -863,16 +818,11 @@ public:
fbx_simple_property(ReferenceStart, int64_t, 0L);
fbx_simple_property(ReferenceStop, int64_t, 0L);
- const PropertyTable *Props() const {
- return props;
- }
-
const AnimationLayerList &Layers() const {
return layers;
}
private:
- const PropertyTable *props = nullptr;
AnimationLayerList layers;
};
@@ -881,14 +831,6 @@ class Deformer : public Object {
public:
Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~Deformer();
-
- const PropertyTable *Props() const {
- //ai_assert(props.get());
- return props;
- }
-
-private:
- const PropertyTable *props;
};
/** Constraints are from Maya they can help us with BoneAttachments :) **/
@@ -896,9 +838,6 @@ class Constraint : public Object {
public:
Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~Constraint();
-
-private:
- const PropertyTable *props;
};
typedef std::vector<float> WeightArray;
@@ -924,7 +863,7 @@ public:
}
private:
- float percent;
+ float percent = 0;
WeightArray fullWeights;
std::vector<const ShapeGeometry *> shapeGeometries;
};
@@ -1006,7 +945,7 @@ private:
Transform transformLink;
Transform transformAssociateModel;
SkinLinkMode link_mode;
- bool valid_transformAssociateModel;
+ bool valid_transformAssociateModel = false;
const Model *node = nullptr;
};
@@ -1037,8 +976,8 @@ public:
}
private:
- float accuracy;
- SkinType skinType;
+ float accuracy = 0;
+ SkinType skinType = SkinType::Skin_Linear;
std::vector<const Cluster *> clusters;
};
@@ -1087,10 +1026,10 @@ public:
}
public:
- uint64_t insertionOrder;
+ uint64_t insertionOrder = 0;
const std::string prop;
- uint64_t src, dest;
+ uint64_t src = 0, dest = 0;
const Document &doc;
};
@@ -1105,15 +1044,10 @@ typedef std::multimap<uint64_t, const Connection *> ConnectionMap;
/** DOM class for global document settings, a single instance per document can
* be accessed via Document.Globals(). */
-class FileGlobalSettings {
+class FileGlobalSettings : public PropertyTable {
public:
- FileGlobalSettings(const Document &doc, const PropertyTable *props);
-
- ~FileGlobalSettings();
-
- const PropertyTable *Props() const {
- return props;
- }
+ FileGlobalSettings(const Document &doc);
+ virtual ~FileGlobalSettings();
const Document &GetDocument() const {
return doc;
@@ -1158,7 +1092,6 @@ public:
fbx_simple_property(CustomFrameRate, float, -1.0f);
private:
- const PropertyTable *props = nullptr;
const Document &doc;
};
@@ -1196,7 +1129,7 @@ public:
return globals.get();
}
- const PropertyTable *GetMetadataProperties() const {
+ const PropertyTable &GetMetadataProperties() const {
return metadata_properties;
}
@@ -1293,7 +1226,7 @@ private:
std::vector<uint64_t> materials;
std::vector<uint64_t> skins;
mutable std::vector<const AnimationStack *> animationStacksResolved;
- PropertyTable *metadata_properties = nullptr;
+ PropertyTable metadata_properties;
std::shared_ptr<FileGlobalSettings> globals = nullptr;
};
} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.cpp b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp
index df50a32c39..3930e005c3 100644
--- a/modules/fbx/fbx_parser/FBXDocumentUtil.cpp
+++ b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp
@@ -137,36 +137,5 @@ void DOMWarning(const std::string &message, const std::shared_ptr<Element> eleme
print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
}
-// ------------------------------------------------------------------------------------------------
-// fetch a property table and the corresponding property template
-const PropertyTable *GetPropertyTable(const Document &doc,
- const std::string &templateName,
- const ElementPtr element,
- const ScopePtr sc,
- bool no_warn /*= false*/) {
- // todo: make this an abstraction
- const ElementPtr Properties70 = sc->GetElement("Properties70");
- const PropertyTable *templateProps = static_cast<const PropertyTable *>(nullptr);
-
- if (templateName.length()) {
- PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName);
- if (it != doc.Templates().end()) {
- templateProps = (*it).second;
- }
- }
-
- if (!Properties70 || !Properties70->Compound()) {
- if (!no_warn) {
- DOMWarning("property table (Properties70) not found", element);
- }
- if (templateProps) {
- return new const PropertyTable(templateProps);
- } else {
- return new const PropertyTable();
- }
- }
-
- return new PropertyTable(Properties70, templateProps);
-}
} // namespace Util
} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.h b/modules/fbx/fbx_parser/FBXDocumentUtil.h
index daa9de4a33..ba86191c4a 100644
--- a/modules/fbx/fbx_parser/FBXDocumentUtil.h
+++ b/modules/fbx/fbx_parser/FBXDocumentUtil.h
@@ -98,13 +98,6 @@ void DOMWarning(const std::string &message, const Element *element);
void DOMWarning(const std::string &message, const std::shared_ptr<Token> token);
void DOMWarning(const std::string &message, const std::shared_ptr<Element> element);
-// fetch a property table and the corresponding property template
-const PropertyTable *GetPropertyTable(const Document &doc,
- const std::string &templateName,
- const ElementPtr element,
- const ScopePtr sc,
- bool no_warn = false);
-
// ------------------------------------------------------------------------------------------------
template <typename T>
const T *ProcessSimpleConnection(const Connection &con,
diff --git a/modules/fbx/fbx_parser/FBXMaterial.cpp b/modules/fbx/fbx_parser/FBXMaterial.cpp
index 219da1b2f4..08fff5714a 100644
--- a/modules/fbx/fbx_parser/FBXMaterial.cpp
+++ b/modules/fbx/fbx_parser/FBXMaterial.cpp
@@ -118,8 +118,6 @@ Material::Material(uint64_t id, const ElementPtr element, const Document &doc, c
DOMWarning("shading mode not recognized: " + shading, element);
}
- props = GetPropertyTable(doc, templateName, element, sc);
-
// resolve texture links
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
for (const Connection *con : conns) {
@@ -163,10 +161,6 @@ Material::Material(uint64_t id, const ElementPtr element, const Document &doc, c
// ------------------------------------------------------------------------------------------------
Material::~Material() {
- if (props != nullptr) {
- delete props;
- props = nullptr;
- }
}
// ------------------------------------------------------------------------------------------------
@@ -219,17 +213,15 @@ Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, con
alphaSource = ParseTokenAsString(GetRequiredToken(Texture_Alpha_Source, 0));
}
- props = GetPropertyTable(doc, "Texture.FbxFileTexture", element, sc);
-
// 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available.
- bool ok;
- const Vector3 &scaling = PropertyGet<Vector3>(props, "Scaling", ok);
+ bool ok = true;
+ const Vector3 &scaling = PropertyGet<Vector3>(this, "Scaling", ok);
if (ok) {
uvScaling.x = scaling.x;
uvScaling.y = scaling.y;
}
- const Vector3 &trans = PropertyGet<Vector3>(props, "Translation", ok);
+ const Vector3 &trans = PropertyGet<Vector3>(this, "Translation", ok);
if (ok) {
uvTrans.x = trans.x;
uvTrans.y = trans.y;
@@ -254,10 +246,6 @@ Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, con
}
Texture::~Texture() {
- if (props != nullptr) {
- delete props;
- props = nullptr;
- }
}
LayeredTexture::LayeredTexture(uint64_t id, const ElementPtr element, const Document & /*doc*/, const std::string &name) :
@@ -390,18 +378,11 @@ Video::Video(uint64_t id, const ElementPtr element, const Document &doc, const s
// runtimeError.what());
}
}
-
- props = GetPropertyTable(doc, "Video.FbxVideo", element, sc);
}
Video::~Video() {
if (content) {
delete[] content;
}
-
- if (props != nullptr) {
- delete props;
- props = nullptr;
- }
}
} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXModel.cpp b/modules/fbx/fbx_parser/FBXModel.cpp
index 767994441f..03c9de0c35 100644
--- a/modules/fbx/fbx_parser/FBXModel.cpp
+++ b/modules/fbx/fbx_parser/FBXModel.cpp
@@ -98,16 +98,11 @@ Model::Model(uint64_t id, const ElementPtr element, const Document &doc, const s
culling = ParseTokenAsString(GetRequiredToken(Culling, 0));
}
- props = GetPropertyTable(doc, "Model.FbxNode", element, sc);
ResolveLinks(element, doc);
}
// ------------------------------------------------------------------------------------------------
Model::~Model() {
- if (props != nullptr) {
- delete props;
- props = nullptr;
- }
}
ModelLimbNode::ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
diff --git a/modules/fbx/fbx_parser/FBXNodeAttribute.cpp b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp
index 2749fc9f4d..15184a0f5d 100644
--- a/modules/fbx/fbx_parser/FBXNodeAttribute.cpp
+++ b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp
@@ -84,16 +84,7 @@ using namespace Util;
// ------------------------------------------------------------------------------------------------
NodeAttribute::NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
- Object(id, element, name), props() {
- const ScopePtr sc = GetRequiredScope(element);
-
- const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
-
- // hack on the deriving type but Null/LimbNode attributes are the only case in which
- // the property table is by design absent and no warning should be generated
- // for it.
- const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode");
- props = GetPropertyTable(doc, "NodeAttribute.Fbx" + classname, element, sc, is_null_or_limb);
+ Object(id, element, name) {
}
// ------------------------------------------------------------------------------------------------
diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp
index 166d98bb8c..82d532e0b8 100644
--- a/modules/fbx/fbx_parser/FBXParser.cpp
+++ b/modules/fbx/fbx_parser/FBXParser.cpp
@@ -131,6 +131,8 @@ Element::Element(const TokenPtr key_token, Parser &parser) :
if (!n) {
print_error("unexpected end of file, expected bracket, comma or key" + String(parser.LastToken()->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
}
const TokenType ty = n->Type();
@@ -143,6 +145,8 @@ Element::Element(const TokenPtr key_token, Parser &parser) :
if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) {
print_error("unexpected token; expected bracket, comma or key" + String(n->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
}
}
@@ -150,11 +154,17 @@ Element::Element(const TokenPtr key_token, Parser &parser) :
compound = new_Scope(parser);
parser.scopes.push_back(compound);
+ if (parser.corrupt) {
+ return;
+ }
+
// current token should be a TOK_CLOSE_BRACKET
n = parser.CurrentToken();
if (n && n->Type() != TokenType_CLOSE_BRACKET) {
print_error("expected closing bracket" + String(n->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
}
parser.AdvanceToNextToken();
@@ -173,22 +183,31 @@ Scope::Scope(Parser &parser, bool topLevel) {
TokenPtr t = parser.CurrentToken();
if (t->Type() != TokenType_OPEN_BRACKET) {
print_error("expected open bracket" + String(t->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
}
}
TokenPtr n = parser.AdvanceToNextToken();
if (n == nullptr) {
print_error("unexpected end of file");
+ parser.corrupt = true;
+ return;
}
// note: empty scopes are allowed
while (n && n->Type() != TokenType_CLOSE_BRACKET) {
if (n->Type() != TokenType_KEY) {
print_error("unexpected token, expected TOK_KEY" + String(n->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
}
const std::string str = n->StringContents();
+ if (parser.corrupt) {
+ return;
+ }
// std::multimap<std::string, ElementPtr> (key and value)
elements.insert(ElementMap::value_type(str, new_Element(n, parser)));
@@ -216,7 +235,7 @@ Scope::~Scope() {
// ------------------------------------------------------------------------------------------------
Parser::Parser(const TokenList &tokens, bool is_binary) :
- tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
+ corrupt(false), tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
root = new_Scope(*this, true);
scopes.push_back(root);
}
@@ -1231,6 +1250,21 @@ ScopePtr GetRequiredScope(const ElementPtr el) {
}
// ------------------------------------------------------------------------------------------------
+// extract optional compound scope
+ScopePtr GetOptionalScope(const ElementPtr el) {
+ if (el) {
+ ScopePtr s = el->Compound();
+ TokenPtr token = el->KeyToken();
+
+ if (token && s) {
+ return s;
+ }
+ }
+
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
// get token at a particular index
TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index) {
if (el) {
diff --git a/modules/fbx/fbx_parser/FBXParser.h b/modules/fbx/fbx_parser/FBXParser.h
index 37d27d3dca..bfbcb22ffa 100644
--- a/modules/fbx/fbx_parser/FBXParser.h
+++ b/modules/fbx/fbx_parser/FBXParser.h
@@ -199,6 +199,10 @@ public:
return is_binary;
}
+ bool IsCorrupt() const {
+ return corrupt;
+ }
+
private:
friend class Scope;
friend class Element;
@@ -208,6 +212,7 @@ private:
TokenPtr CurrentToken() const;
private:
+ bool corrupt = false;
ScopeList scopes;
const TokenList &tokens;
@@ -249,6 +254,8 @@ bool HasElement(const ScopePtr sc, const std::string &index);
// extract a required element from a scope, abort if the element cannot be found
ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
ScopePtr GetRequiredScope(const ElementPtr el); // New in 2020. (less likely to destroy application)
+ScopePtr GetOptionalScope(const ElementPtr el); // New in 2021. (even LESS likely to destroy application now)
+
ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
// extract required compound scope
ScopePtr GetRequiredScope(const ElementPtr el);
diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp
index 84e71512d6..1b3f29ec04 100644
--- a/modules/fbx/fbx_parser/FBXProperties.cpp
+++ b/modules/fbx/fbx_parser/FBXProperties.cpp
@@ -145,19 +145,33 @@ std::string PeekPropertyName(const Element &element) {
} // namespace
// ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable() {
+PropertyTable::PropertyTable() :
+ element(nullptr) {
}
-// ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable(const PropertyTable *templateProps) :
- templateProps(templateProps), element() {
+// Is used when dealing with FBX Objects not metadata.
+PropertyTable::PropertyTable(const ElementPtr element) :
+ element(element) {
+ Setup(element);
}
// ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *templateProps) :
- templateProps(templateProps), element(element) {
- const ScopePtr scope = GetRequiredScope(element);
- ERR_FAIL_COND(!scope);
+PropertyTable::~PropertyTable() {
+ for (PropertyMap::value_type &v : props) {
+ delete v.second;
+ }
+}
+
+void PropertyTable::Setup(ElementPtr ptr) {
+ const ScopePtr sc = GetRequiredScope(ptr);
+ const ElementPtr Properties70 = sc->GetElement("Properties70");
+ const ScopePtr scope = GetOptionalScope(Properties70);
+
+ // no scope, no care.
+ if (!scope) {
+ return; // NOTE: this is not an error this is actually a Object, without properties, here we will nullptr it.
+ }
+
for (const ElementMap::value_type &v : scope->Elements()) {
if (v.first != "P") {
DOMWarning("expected only P elements in property table", v.second);
@@ -182,13 +196,6 @@ PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *temp
}
// ------------------------------------------------------------------------------------------------
-PropertyTable::~PropertyTable() {
- for (PropertyMap::value_type &v : props) {
- delete v.second;
- }
-}
-
-// ------------------------------------------------------------------------------------------------
PropertyPtr PropertyTable::Get(const std::string &name) const {
PropertyMap::const_iterator it = props.find(name);
if (it == props.end()) {
@@ -203,10 +210,6 @@ PropertyPtr PropertyTable::Get(const std::string &name) const {
if (it == props.end()) {
// check property template
- if (templateProps) {
- return templateProps->Get(name);
- }
-
return nullptr;
}
}
diff --git a/modules/fbx/fbx_parser/FBXProperties.h b/modules/fbx/fbx_parser/FBXProperties.h
index 0595b25fa7..bfd27ac94e 100644
--- a/modules/fbx/fbx_parser/FBXProperties.h
+++ b/modules/fbx/fbx_parser/FBXProperties.h
@@ -137,36 +137,31 @@ class PropertyTable {
public:
// in-memory property table with no source element
PropertyTable();
- PropertyTable(const PropertyTable *templateProps);
- PropertyTable(const ElementPtr element, const PropertyTable *templateProps);
- ~PropertyTable();
+ PropertyTable(const ElementPtr element);
+ virtual ~PropertyTable();
PropertyPtr Get(const std::string &name) const;
+ void Setup(ElementPtr ptr);
// PropertyTable's need not be coupled with FBX elements so this can be NULL
- ElementPtr GetElement() const {
+ ElementPtr GetElement() {
return element;
}
- PropertyMap &GetProperties() const {
+ PropertyMap &GetProperties() {
return props;
}
- const LazyPropertyMap &GetLazyProperties() const {
+ const LazyPropertyMap &GetLazyProperties() {
return lazyProps;
}
- const PropertyTable *TemplateProps() const {
- return templateProps;
- }
-
DirectPropertyMap GetUnparsedProperties() const;
private:
LazyPropertyMap lazyProps;
mutable PropertyMap props;
- const PropertyTable *templateProps = nullptr;
- const ElementPtr element = nullptr;
+ ElementPtr element = nullptr;
};
// ------------------------------------------------------------------------------------------------
@@ -191,16 +186,11 @@ template <typename T>
inline T PropertyGet(const PropertyTable *in, const std::string &name, bool &result, bool useTemplate = false) {
PropertyPtr prop = in->Get(name);
if (nullptr == prop) {
- if (!useTemplate) {
- result = false;
- return T();
- }
- const PropertyTable *templ = in->TemplateProps();
- if (nullptr == templ) {
+ if (nullptr == in) {
result = false;
return T();
}
- prop = templ->Get(name);
+ prop = in->Get(name);
if (nullptr == prop) {
result = false;
return T();
diff --git a/modules/fbx/fbx_parser/FBXTokenizer.cpp b/modules/fbx/fbx_parser/FBXTokenizer.cpp
index ea4568fe32..81c5b128e8 100644
--- a/modules/fbx/fbx_parser/FBXTokenizer.cpp
+++ b/modules/fbx/fbx_parser/FBXTokenizer.cpp
@@ -141,7 +141,7 @@ void ProcessDataToken(TokenList &output_tokens, const char *&start, const char *
} // namespace
// ------------------------------------------------------------------------------------------------
-void Tokenize(TokenList &output_tokens, const char *input, size_t length) {
+void Tokenize(TokenList &output_tokens, const char *input, size_t length, bool &corrupt) {
// line and column numbers numbers are one-based
unsigned int line = 1;
unsigned int column = 1;
@@ -185,6 +185,8 @@ void Tokenize(TokenList &output_tokens, const char *input, size_t length) {
case '\"':
if (token_begin) {
TokenizeError("unexpected double-quote", line, column);
+ corrupt = true;
+ return;
}
token_begin = cur;
in_double_quotes = true;
diff --git a/modules/fbx/fbx_parser/FBXTokenizer.h b/modules/fbx/fbx_parser/FBXTokenizer.h
index 1e7e5e6535..184d0fd894 100644
--- a/modules/fbx/fbx_parser/FBXTokenizer.h
+++ b/modules/fbx/fbx_parser/FBXTokenizer.h
@@ -187,7 +187,7 @@ typedef std::vector<TokenPtr> TokenList;
* @param output_tokens Receives a list of all tokens in the input data.
* @param input_buffer Textual input buffer to be processed, 0-terminated.
* @print_error if something goes wrong */
-void Tokenize(TokenList &output_tokens, const char *input, size_t length);
+void Tokenize(TokenList &output_tokens, const char *input, size_t length, bool &corrupt);
/** Tokenizer function for binary FBX files.
*
@@ -197,7 +197,7 @@ void Tokenize(TokenList &output_tokens, const char *input, size_t length);
* @param input_buffer Binary input buffer to be processed.
* @param length Length of input buffer, in bytes. There is no 0-terminal.
* @print_error if something goes wrong */
-void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length);
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, bool &corrupt);
} // namespace FBXDocParser
#endif // FBX_TOKENIZER_H
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index c9c5d00aa5..5f590383d0 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -45,6 +45,10 @@
#include "gdscript_parser.h"
#include "gdscript_warning.h"
+#ifdef TESTS_ENABLED
+#include "tests/gdscript_test_runner.h"
+#endif
+
///////////////////////////
GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) {
@@ -1766,6 +1770,10 @@ void GDScriptLanguage::init() {
for (List<Engine::Singleton>::Element *E = singletons.front(); E; E = E->next()) {
_add_global(E->get().name, E->get().ptr);
}
+
+#ifdef TESTS_ENABLED
+ GDScriptTests::GDScriptTestRunner::handle_cmdline();
+#endif
}
String GDScriptLanguage::get_type() const {
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 21b221b6ba..89c5f5482b 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -571,7 +571,7 @@ void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const A
}
void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) {
- append(GDScriptFunction::OPCODE_IS_BUILTIN, 3);
+ append(GDScriptFunction::OPCODE_IS_BUILTIN, 2);
append(p_source);
append(p_target);
append(p_type);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 695154e9a9..ca8bb8fcae 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -811,6 +811,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
VariableNode *variable = alloc_node<VariableNode>();
variable->identifier = parse_identifier();
+ variable->export_info.name = variable->identifier->name;
if (match(GDScriptTokenizer::Token::COLON)) {
if (check(GDScriptTokenizer::Token::NEWLINE)) {
@@ -860,8 +861,6 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
end_statement("variable declaration");
- variable->export_info.name = variable->identifier->name;
-
return variable;
}
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 19fd3daf20..2d2f94f5e0 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -163,19 +163,19 @@ void unregister_gdscript_types() {
#ifdef TESTS_ENABLED
void test_tokenizer() {
- TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER);
+ GDScriptTests::test(GDScriptTests::TestType::TEST_TOKENIZER);
}
void test_parser() {
- TestGDScript::test(TestGDScript::TestType::TEST_PARSER);
+ GDScriptTests::test(GDScriptTests::TestType::TEST_PARSER);
}
void test_compiler() {
- TestGDScript::test(TestGDScript::TestType::TEST_COMPILER);
+ GDScriptTests::test(GDScriptTests::TestType::TEST_COMPILER);
}
void test_bytecode() {
- TestGDScript::test(TestGDScript::TestType::TEST_BYTECODE);
+ GDScriptTests::test(GDScriptTests::TestType::TEST_BYTECODE);
}
REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer);
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
new file mode 100644
index 0000000000..f53c3046e6
--- /dev/null
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -0,0 +1,584 @@
+/*************************************************************************/
+/* gdscript_test_runner.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gdscript_test_runner.h"
+
+#include "../gdscript.h"
+#include "../gdscript_analyzer.h"
+#include "../gdscript_compiler.h"
+#include "../gdscript_parser.h"
+
+#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
+#include "core/io/file_access_pack.h"
+#include "core/os/dir_access.h"
+#include "core/os/os.h"
+#include "core/string/string_builder.h"
+#include "scene/resources/packed_scene.h"
+
+#include "tests/test_macros.h"
+
+namespace GDScriptTests {
+
+void init_autoloads() {
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+
+ // First pass, add the constants so they exist before any script is loaded.
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->get();
+
+ if (info.is_singleton) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(info.name, Variant());
+ }
+ }
+ }
+
+ // Second pass, load into global constants.
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->get();
+
+ if (!info.is_singleton) {
+ // Skip non-singletons since we don't have a scene tree here anyway.
+ continue;
+ }
+
+ RES res = ResourceLoader::load(info.path);
+ ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
+ Node *n = nullptr;
+ if (res->is_class("PackedScene")) {
+ Ref<PackedScene> ps = res;
+ n = ps->instance();
+ } else if (res->is_class("Script")) {
+ Ref<Script> script_res = res;
+ StringName ibt = script_res->get_instance_base_type();
+ bool valid_type = ClassDB::is_parent_class(ibt, "Node");
+ ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path);
+
+ Object *obj = ClassDB::instance(ibt);
+
+ ERR_CONTINUE_MSG(obj == nullptr,
+ "Cannot instance script for autoload, expected 'Node' inheritance, got: " +
+ String(ibt));
+
+ n = Object::cast_to<Node>(obj);
+ n->set_script(script_res);
+ }
+
+ ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
+ n->set_name(info.name);
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(info.name, n);
+ }
+ }
+}
+
+void init_language(const String &p_base_path) {
+ // Setup project settings since it's needed by the languages to get the global scripts.
+ // This also sets up the base resource path.
+ Error err = ProjectSettings::get_singleton()->setup(p_base_path, String(), true);
+ if (err) {
+ print_line("Could not load project settings.");
+ // Keep going since some scripts still work without this.
+ }
+
+ // Initialize the language for the test routine.
+ GDScriptLanguage::get_singleton()->init();
+ init_autoloads();
+}
+
+void finish_language() {
+ GDScriptLanguage::get_singleton()->finish();
+ ScriptServer::global_classes_clear();
+}
+
+StringName GDScriptTestRunner::test_function_name;
+
+GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language) {
+ test_function_name = StaticCString::create("test");
+ do_init_languages = p_init_language;
+
+ source_dir = p_source_dir;
+ if (!source_dir.ends_with("/")) {
+ source_dir += "/";
+ }
+
+ if (do_init_languages) {
+ init_language(p_source_dir);
+
+ // Enable all warnings for GDScript, so we can test them.
+ ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
+ for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
+ String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
+ ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
+ }
+ }
+
+ // Enable printing to show results
+ _print_line_enabled = true;
+ _print_error_enabled = true;
+}
+
+GDScriptTestRunner::~GDScriptTestRunner() {
+ test_function_name = StringName();
+ if (do_init_languages) {
+ finish_language();
+ }
+}
+
+int GDScriptTestRunner::run_tests() {
+ if (!make_tests()) {
+ FAIL("An error occurred while making the tests.");
+ return -1;
+ }
+
+ if (!generate_class_index()) {
+ FAIL("An error occurred while generating class index.");
+ return -1;
+ }
+
+ int failed = 0;
+ for (int i = 0; i < tests.size(); i++) {
+ GDScriptTest test = tests[i];
+ GDScriptTest::TestResult result = test.run_test();
+
+ String expected = FileAccess::get_file_as_string(test.get_output_file());
+ INFO(test.get_source_file());
+ if (!result.passed) {
+ INFO(expected);
+ failed++;
+ }
+
+ CHECK_MESSAGE(result.passed, (result.passed ? String() : result.output));
+ }
+
+ return failed;
+}
+
+bool GDScriptTestRunner::generate_outputs() {
+ is_generating = true;
+
+ if (!make_tests()) {
+ print_line("Failed to generate a test output.");
+ return false;
+ }
+
+ if (!generate_class_index()) {
+ return false;
+ }
+
+ for (int i = 0; i < tests.size(); i++) {
+ OS::get_singleton()->print(".");
+ GDScriptTest test = tests[i];
+ bool result = test.generate_output();
+
+ if (!result) {
+ print_line("\nCould not generate output for " + test.get_source_file());
+ return false;
+ }
+ }
+ print_line("\nGenerated output files for " + itos(tests.size()) + " tests successfully.");
+
+ return true;
+}
+
+bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
+ Error err = OK;
+ DirAccessRef dir(DirAccess::open(p_dir, &err));
+
+ if (err != OK) {
+ return false;
+ }
+
+ String current_dir = dir->get_current_dir();
+
+ dir->list_dir_begin();
+ String next = dir->get_next();
+
+ while (!next.is_empty()) {
+ if (dir->current_is_dir()) {
+ if (next == "." || next == "..") {
+ next = dir->get_next();
+ continue;
+ }
+ if (!make_tests_for_dir(current_dir.plus_file(next))) {
+ return false;
+ }
+ } else {
+ if (next.get_extension().to_lower() == "gd") {
+ String out_file = next.get_basename() + ".out";
+ if (!is_generating && !dir->file_exists(out_file)) {
+ ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
+ }
+ GDScriptTest test(current_dir.plus_file(next), current_dir.plus_file(out_file), source_dir);
+ tests.push_back(test);
+ }
+ }
+
+ next = dir->get_next();
+ }
+
+ dir->list_dir_end();
+
+ return true;
+}
+
+bool GDScriptTestRunner::make_tests() {
+ Error err = OK;
+ DirAccessRef dir(DirAccess::open(source_dir, &err));
+
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Could not open specified test directory.");
+
+ return make_tests_for_dir(dir->get_current_dir());
+}
+
+bool GDScriptTestRunner::generate_class_index() {
+ StringName gdscript_name = GDScriptLanguage::get_singleton()->get_name();
+ for (int i = 0; i < tests.size(); i++) {
+ GDScriptTest test = tests[i];
+ String base_type;
+
+ String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(test.get_source_file(), &base_type);
+ if (class_name == String()) {
+ continue;
+ }
+ ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false,
+ "Class name '" + class_name + "' from " + test.get_source_file() + " is already used in " + ScriptServer::get_global_class_path(class_name));
+
+ ScriptServer::add_global_class(class_name, base_type, gdscript_name, test.get_source_file());
+ }
+ return true;
+}
+
+GDScriptTest::GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir) {
+ source_file = p_source_path;
+ output_file = p_output_path;
+ base_dir = p_base_dir;
+ _print_handler.printfunc = print_handler;
+ _error_handler.errfunc = error_handler;
+}
+
+void GDScriptTestRunner::handle_cmdline() {
+ List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
+ // TODO: this could likely be ported to use test commands:
+ // https://github.com/godotengine/godot/pull/41355
+ // Currently requires to startup the whole engine, which is slow.
+ String test_cmd = "--gdscript-test";
+ String gen_cmd = "--gdscript-generate-tests";
+
+ for (List<String>::Element *E = cmdline_args.front(); E != nullptr; E = E->next()) {
+ String &cmd = E->get();
+ if (cmd == test_cmd || cmd == gen_cmd) {
+ if (E->next() == nullptr) {
+ ERR_PRINT("Needed a path for the test files.");
+ exit(-1);
+ }
+
+ const String &path = E->next()->get();
+
+ GDScriptTestRunner runner(path, false);
+ int failed = 0;
+ if (cmd == test_cmd) {
+ failed = runner.run_tests();
+ } else {
+ bool completed = runner.generate_outputs();
+ failed = completed ? 0 : -1;
+ }
+ exit(failed);
+ }
+ }
+}
+
+void GDScriptTest::enable_stdout() {
+ // TODO: this could likely be handled by doctest or `tests/test_macros.h`.
+ OS::get_singleton()->set_stdout_enabled(true);
+ OS::get_singleton()->set_stderr_enabled(true);
+}
+
+void GDScriptTest::disable_stdout() {
+ // TODO: this could likely be handled by doctest or `tests/test_macros.h`.
+ OS::get_singleton()->set_stdout_enabled(false);
+ OS::get_singleton()->set_stderr_enabled(false);
+}
+
+void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_error) {
+ TestResult *result = (TestResult *)p_this;
+ result->output += p_message + "\n";
+}
+
+void GDScriptTest::error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, ErrorHandlerType p_type) {
+ ErrorHandlerData *data = (ErrorHandlerData *)p_this;
+ GDScriptTest *self = data->self;
+ TestResult *result = data->result;
+
+ result->status = GDTEST_RUNTIME_ERROR;
+
+ StringBuilder builder;
+ builder.append(">> ");
+ switch (p_type) {
+ case ERR_HANDLER_ERROR:
+ builder.append("ERROR");
+ break;
+ case ERR_HANDLER_WARNING:
+ builder.append("WARNING");
+ break;
+ case ERR_HANDLER_SCRIPT:
+ builder.append("SCRIPT ERROR");
+ break;
+ case ERR_HANDLER_SHADER:
+ builder.append("SHADER ERROR");
+ break;
+ default:
+ builder.append("Unknown error type");
+ break;
+ }
+
+ builder.append("\n>> ");
+ builder.append(p_function);
+ builder.append("\n>> ");
+ builder.append(p_function);
+ builder.append("\n>> ");
+ builder.append(String(p_file).trim_prefix(self->base_dir));
+ builder.append("\n>> ");
+ builder.append(itos(p_line));
+ builder.append("\n>> ");
+ builder.append(p_error);
+ if (strlen(p_explanation) > 0) {
+ builder.append("\n>> ");
+ builder.append(p_explanation);
+ }
+ builder.append("\n");
+
+ result->output = builder.as_string();
+}
+
+bool GDScriptTest::check_output(const String &p_output) const {
+ Error err = OK;
+ String expected = FileAccess::get_file_as_string(output_file, &err);
+
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error when opening the output file.");
+
+ String got = p_output.strip_edges(); // TODO: may be hacky.
+ got += "\n"; // Make sure to insert newline for CI static checks.
+
+ return got == expected;
+}
+
+String GDScriptTest::get_text_for_status(GDScriptTest::TestStatus p_status) const {
+ switch (p_status) {
+ case GDTEST_OK:
+ return "GDTEST_OK";
+ case GDTEST_LOAD_ERROR:
+ return "GDTEST_LOAD_ERROR";
+ case GDTEST_PARSER_ERROR:
+ return "GDTEST_PARSER_ERROR";
+ case GDTEST_ANALYZER_ERROR:
+ return "GDTEST_ANALYZER_ERROR";
+ case GDTEST_COMPILER_ERROR:
+ return "GDTEST_COMPILER_ERROR";
+ case GDTEST_RUNTIME_ERROR:
+ return "GDTEST_RUNTIME_ERROR";
+ }
+ return "";
+}
+
+GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
+ disable_stdout();
+
+ TestResult result;
+ result.status = GDTEST_OK;
+ result.output = String();
+
+ Error err = OK;
+
+ // Create script.
+ Ref<GDScript> script;
+ script.instance();
+ script->set_path(source_file);
+ script->set_script_path(source_file);
+ err = script->load_source_code(source_file);
+ if (err != OK) {
+ enable_stdout();
+ result.status = GDTEST_LOAD_ERROR;
+ result.passed = false;
+ ERR_FAIL_V_MSG(result, "\nCould not load source code for: '" + source_file + "'");
+ }
+
+ // Test parsing.
+ GDScriptParser parser;
+ err = parser.parse(script->get_source_code(), source_file, false);
+ if (err != OK) {
+ enable_stdout();
+ result.status = GDTEST_PARSER_ERROR;
+ result.output = get_text_for_status(result.status) + "\n";
+
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (auto *E = errors.front(); E; E = E->next()) {
+ result.output += E->get().message + "\n"; // TODO: line, column?
+ break; // Only the first error since the following might be cascading.
+ }
+ if (!p_is_generating) {
+ result.passed = check_output(result.output);
+ }
+ return result;
+ }
+
+ // Test type-checking.
+ GDScriptAnalyzer analyzer(&parser);
+ err = analyzer.analyze();
+ if (err != OK) {
+ enable_stdout();
+ result.status = GDTEST_ANALYZER_ERROR;
+ result.output = get_text_for_status(result.status) + "\n";
+
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (auto *E = errors.front(); E; E = E->next()) {
+ result.output += E->get().message + "\n"; // TODO: line, column?
+ break; // Only the first error since the following might be cascading.
+ }
+ if (!p_is_generating) {
+ result.passed = check_output(result.output);
+ }
+ return result;
+ }
+
+ StringBuilder warning_string;
+ for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E != nullptr; E = E->next()) {
+ const GDScriptWarning warning = E->get();
+ warning_string.append(">> WARNING");
+ warning_string.append("\n>> Line: ");
+ warning_string.append(itos(warning.start_line));
+ warning_string.append("\n>> ");
+ warning_string.append(warning.get_name());
+ warning_string.append("\n>> ");
+ warning_string.append(warning.get_message());
+ warning_string.append("\n");
+ }
+ result.output += warning_string.as_string();
+
+ // Test compiling.
+ GDScriptCompiler compiler;
+ err = compiler.compile(&parser, script.ptr(), false);
+ if (err != OK) {
+ enable_stdout();
+ result.status = GDTEST_COMPILER_ERROR;
+ result.output = get_text_for_status(result.status) + "\n";
+ result.output = compiler.get_error();
+ if (!p_is_generating) {
+ result.passed = check_output(result.output);
+ }
+ return result;
+ }
+
+ // Test running.
+ const Map<StringName, GDScriptFunction *>::Element *test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name);
+ if (test_function_element == nullptr) {
+ enable_stdout();
+ result.status = GDTEST_LOAD_ERROR;
+ result.output = "";
+ result.passed = false;
+ ERR_FAIL_V_MSG(result, "\nCould not find test function on: '" + source_file + "'");
+ }
+
+ script->reload();
+
+ // Create object instance for test.
+ Object *obj = ClassDB::instance(script->get_native()->get_name());
+ Ref<Reference> obj_ref;
+ if (obj->is_reference()) {
+ obj_ref = Ref<Reference>(Object::cast_to<Reference>(obj));
+ }
+ obj->set_script(script);
+ GDScriptInstance *instance = static_cast<GDScriptInstance *>(obj->get_script_instance());
+
+ // Setup output handlers.
+ ErrorHandlerData error_data(&result, this);
+
+ _print_handler.userdata = &result;
+ _error_handler.userdata = &error_data;
+ add_print_handler(&_print_handler);
+ add_error_handler(&_error_handler);
+
+ // Call test function.
+ Callable::CallError call_err;
+ instance->call(GDScriptTestRunner::test_function_name, nullptr, 0, call_err);
+
+ // Tear down output handlers.
+ remove_print_handler(&_print_handler);
+ remove_error_handler(&_error_handler);
+
+ // Check results.
+ if (call_err.error != Callable::CallError::CALL_OK) {
+ enable_stdout();
+ result.status = GDTEST_LOAD_ERROR;
+ result.passed = false;
+ ERR_FAIL_V_MSG(result, "\nCould not call test function on: '" + source_file + "'");
+ }
+
+ result.output = get_text_for_status(result.status) + "\n" + result.output;
+ if (!p_is_generating) {
+ result.passed = check_output(result.output);
+ }
+
+ if (obj_ref.is_null()) {
+ memdelete(obj);
+ }
+
+ enable_stdout();
+ return result;
+}
+
+GDScriptTest::TestResult GDScriptTest::run_test() {
+ return execute_test_code(false);
+}
+
+bool GDScriptTest::generate_output() {
+ TestResult result = execute_test_code(true);
+ if (result.status == GDTEST_LOAD_ERROR) {
+ return false;
+ }
+
+ Error err = OK;
+ FileAccessRef out_file = FileAccess::open(output_file, FileAccess::WRITE, &err);
+ if (err != OK) {
+ return false;
+ }
+
+ String output = result.output.strip_edges(); // TODO: may be hacky.
+ output += "\n"; // Make sure to insert newline for CI static checks.
+
+ out_file->store_string(output);
+ out_file->close();
+
+ return true;
+}
+
+} // namespace GDScriptTests
diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h
new file mode 100644
index 0000000000..9b2d14a371
--- /dev/null
+++ b/modules/gdscript/tests/gdscript_test_runner.h
@@ -0,0 +1,126 @@
+/*************************************************************************/
+/* gdscript_test_runner.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GDSCRIPT_TEST_H
+#define GDSCRIPT_TEST_H
+
+#include "../gdscript.h"
+#include "core/error/error_macros.h"
+#include "core/string/print_string.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
+
+namespace GDScriptTests {
+
+void init_autoloads();
+void init_language(const String &p_base_path);
+void finish_language();
+
+// Single test instance in a suite.
+class GDScriptTest {
+public:
+ enum TestStatus {
+ GDTEST_OK,
+ GDTEST_LOAD_ERROR,
+ GDTEST_PARSER_ERROR,
+ GDTEST_ANALYZER_ERROR,
+ GDTEST_COMPILER_ERROR,
+ GDTEST_RUNTIME_ERROR,
+ };
+
+ struct TestResult {
+ TestStatus status;
+ String output;
+ bool passed;
+ };
+
+private:
+ struct ErrorHandlerData {
+ TestResult *result;
+ GDScriptTest *self;
+ ErrorHandlerData(TestResult *p_result, GDScriptTest *p_this) {
+ result = p_result;
+ self = p_this;
+ }
+ };
+
+ String source_file;
+ String output_file;
+ String base_dir;
+
+ PrintHandlerList _print_handler;
+ ErrorHandlerList _error_handler;
+
+ void enable_stdout();
+ void disable_stdout();
+ bool check_output(const String &p_output) const;
+ String get_text_for_status(TestStatus p_status) const;
+
+ TestResult execute_test_code(bool p_is_generating);
+
+public:
+ static void print_handler(void *p_this, const String &p_message, bool p_error);
+ static void error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, ErrorHandlerType p_type);
+ TestResult run_test();
+ bool generate_output();
+
+ const String &get_source_file() const { return source_file; }
+ const String &get_output_file() const { return output_file; }
+
+ GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir);
+ GDScriptTest() :
+ GDScriptTest(String(), String(), String()) {} // Needed to use in Vector.
+};
+
+class GDScriptTestRunner {
+ String source_dir;
+ Vector<GDScriptTest> tests;
+
+ bool is_generating = false;
+ bool do_init_languages = false;
+
+ bool make_tests();
+ bool make_tests_for_dir(const String &p_dir);
+ bool generate_class_index();
+
+public:
+ static StringName test_function_name;
+
+ static void handle_cmdline();
+ int run_tests();
+ bool generate_outputs();
+
+ GDScriptTestRunner(const String &p_source_dir, bool p_init_language);
+ ~GDScriptTestRunner();
+};
+
+} // namespace GDScriptTests
+
+#endif // GDSCRIPT_TEST_H
diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h
new file mode 100644
index 0000000000..136907b316
--- /dev/null
+++ b/modules/gdscript/tests/gdscript_test_runner_suite.h
@@ -0,0 +1,53 @@
+/*************************************************************************/
+/* gdscript_test_runner_suite.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GDSCRIPT_TEST_RUNNER_SUITE_H
+#define GDSCRIPT_TEST_RUNNER_SUITE_H
+
+#include "gdscript_test_runner.h"
+#include "tests/test_macros.h"
+
+namespace GDScriptTests {
+
+TEST_SUITE("[Modules][GDScript]") {
+ // GDScript 2.0 is still under heavy construction.
+ // Allow the tests to fail, but do not ignore errors during development.
+ // Update the scripts and expected output as needed.
+ TEST_CASE("Script compilation and runtime") {
+ GDScriptTestRunner runner("modules/gdscript/tests/scripts", true);
+ int fail_count = runner.run_tests();
+ INFO("Make sure `*.out` files have expected results.");
+ REQUIRE_MESSAGE(fail_count == 0, "All GDScript tests should pass.");
+ }
+}
+
+} // namespace GDScriptTests
+
+#endif // GDSCRIPT_TEST_RUNNER_SUITE_H
diff --git a/modules/gdscript/tests/scripts/.gitignore b/modules/gdscript/tests/scripts/.gitignore
new file mode 100644
index 0000000000..94c5b1bf6b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/.gitignore
@@ -0,0 +1,2 @@
+# Ignore metadata if someone open this on Godot.
+/.godot
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-argument.gd b/modules/gdscript/tests/scripts/parser-errors/missing-argument.gd
new file mode 100644
index 0000000000..c56ad94095
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/missing-argument.gd
@@ -0,0 +1,6 @@
+func args(a, b):
+ print(a)
+ print(b)
+
+func test():
+ args(1,)
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-argument.out b/modules/gdscript/tests/scripts/parser-errors/missing-argument.out
new file mode 100644
index 0000000000..fc2a891109
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/missing-argument.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Too few arguments for "args()" call. Expected at least 2 but received 1.
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.gd b/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.gd
new file mode 100644
index 0000000000..a1077e1985
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.gd
@@ -0,0 +1,2 @@
+func test():
+ var a = ("missing paren ->"
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.out b/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.out
new file mode 100644
index 0000000000..7326afa33d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected closing ")" after grouping expression.
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-colon.gd b/modules/gdscript/tests/scripts/parser-errors/missing-colon.gd
new file mode 100644
index 0000000000..62cb633e9e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/missing-colon.gd
@@ -0,0 +1,3 @@
+func test():
+ if true # Missing colon here.
+ print("true")
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-colon.out b/modules/gdscript/tests/scripts/parser-errors/missing-colon.out
new file mode 100644
index 0000000000..687b963bc8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/missing-colon.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected ":" after "if" condition.
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.gd b/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.gd
new file mode 100644
index 0000000000..116b0151da
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.gd
@@ -0,0 +1,6 @@
+func args(a, b):
+ print(a)
+ print(b)
+
+func test():
+ args(1,2
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.out b/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.out
new file mode 100644
index 0000000000..34ea7ac323
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected closing ")" after call arguments.
diff --git a/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.gd b/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.gd
new file mode 100644
index 0000000000..9ad77f1432
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.gd
@@ -0,0 +1,3 @@
+func test():
+ print("Using spaces")
+ print("Using tabs")
diff --git a/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.out b/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.out
new file mode 100644
index 0000000000..6390de9788
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Used "\t" for indentation instead " " as used before in the file.
diff --git a/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.gd b/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.gd
new file mode 100644
index 0000000000..3875ce3936
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.gd
@@ -0,0 +1,3 @@
+extends Node
+func test():
+ var a = $ # Expected some node path.
diff --git a/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.out b/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.out
new file mode 100644
index 0000000000..b3dc181a22
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expect node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.gd b/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.gd
new file mode 100644
index 0000000000..1836d42226
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.gd
@@ -0,0 +1,3 @@
+extends Node
+func test():
+ $MyNode/23 # Can't use number here.
diff --git a/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.out b/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.out
new file mode 100644
index 0000000000..dcb4ccecb0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expect node path after "/".
diff --git a/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.gd b/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.gd
new file mode 100644
index 0000000000..6fd2692d47
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.gd
@@ -0,0 +1,3 @@
+extends Node
+func test():
+ $23 # Can't use number here.
diff --git a/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.out b/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.out
new file mode 100644
index 0000000000..b3dc181a22
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expect node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.gd b/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.gd
new file mode 100644
index 0000000000..08f2eedb2d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.gd
@@ -0,0 +1,2 @@
+func test():
+ print("A"); print("B")
diff --git a/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.out b/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.out
new file mode 100644
index 0000000000..fc03f3efe8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+A
+B
diff --git a/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.gd b/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.gd
new file mode 100644
index 0000000000..6097b11b10
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.gd
@@ -0,0 +1,7 @@
+# See https://github.com/godotengine/godot/issues/41066.
+
+func f(p, ): ## <-- no errors
+ print(p)
+
+func test():
+ f(0, ) ## <-- no error
diff --git a/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.out b/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.out
new file mode 100644
index 0000000000..94e2ec2af8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+0
diff --git a/modules/gdscript/tests/scripts/parser-features/variable-declaration.gd b/modules/gdscript/tests/scripts/parser-features/variable-declaration.gd
new file mode 100644
index 0000000000..3b48f10ca7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-features/variable-declaration.gd
@@ -0,0 +1,12 @@
+var a # No init.
+var b = 42 # Init.
+
+func test():
+ var c # No init, local.
+ var d = 23 # Init, local.
+
+ a = 1
+ c = 2
+
+ prints(a, b, c, d)
+ print("OK")
diff --git a/modules/gdscript/tests/scripts/parser-features/variable-declaration.out b/modules/gdscript/tests/scripts/parser-features/variable-declaration.out
new file mode 100644
index 0000000000..2e0a63c024
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-features/variable-declaration.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> UNASSIGNED_VARIABLE
+>> The variable 'c' was used but never assigned a value.
+1 42 2 23
+OK
diff --git a/modules/gdscript/tests/scripts/parser-warnings/unused-variable.gd b/modules/gdscript/tests/scripts/parser-warnings/unused-variable.gd
new file mode 100644
index 0000000000..68e3bd424f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-warnings/unused-variable.gd
@@ -0,0 +1,2 @@
+func test():
+ var unused = "not used"
diff --git a/modules/gdscript/tests/scripts/parser-warnings/unused-variable.out b/modules/gdscript/tests/scripts/parser-warnings/unused-variable.out
new file mode 100644
index 0000000000..270e0e69c0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser-warnings/unused-variable.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNUSED_VARIABLE
+>> The local variable 'unused' is declared but never used in the block. If this is intended, prefix it with an underscore: '_unused'
diff --git a/modules/gdscript/tests/scripts/project.godot b/modules/gdscript/tests/scripts/project.godot
new file mode 100644
index 0000000000..25b49c0abd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/project.godot
@@ -0,0 +1,10 @@
+; This is not an actual project.
+; This config only exists to properly set up the test environment.
+; It also helps for opening Godot to edit the scripts, but please don't
+; let the editor changes be saved.
+
+config_version=4
+
+[application]
+
+config/name="GDScript Integration Test Suite"
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index 3cc0eee672..e70f221c0a 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -47,7 +47,7 @@
#include "editor/editor_settings.h"
#endif
-namespace TestGDScript {
+namespace GDScriptTests {
static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) {
GDScriptTokenizer tokenizer;
@@ -183,60 +183,6 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
}
}
-void init_autoloads() {
- Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
-
- // First pass, add the constants so they exist before any script is loaded.
- for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
- const ProjectSettings::AutoloadInfo &info = E->get();
-
- if (info.is_singleton) {
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->add_global_constant(info.name, Variant());
- }
- }
- }
-
- // Second pass, load into global constants.
- for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
- const ProjectSettings::AutoloadInfo &info = E->get();
-
- if (!info.is_singleton) {
- // Skip non-singletons since we don't have a scene tree here anyway.
- continue;
- }
-
- RES res = ResourceLoader::load(info.path);
- ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
- Node *n = nullptr;
- if (res->is_class("PackedScene")) {
- Ref<PackedScene> ps = res;
- n = ps->instance();
- } else if (res->is_class("Script")) {
- Ref<Script> script_res = res;
- StringName ibt = script_res->get_instance_base_type();
- bool valid_type = ClassDB::is_parent_class(ibt, "Node");
- ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path);
-
- Object *obj = ClassDB::instance(ibt);
-
- ERR_CONTINUE_MSG(obj == nullptr,
- "Cannot instance script for autoload, expected 'Node' inheritance, got: " +
- String(ibt));
-
- n = Object::cast_to<Node>(obj);
- n->set_script(script_res);
- }
-
- ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
- n->set_name(info.name);
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->add_global_constant(info.name, n);
- }
- }
-}
-
void test(TestType p_type) {
List<String> cmdlargs = OS::get_singleton()->get_cmdline_args();
@@ -253,20 +199,8 @@ void test(TestType p_type) {
FileAccessRef fa = FileAccess::open(test, FileAccess::READ);
ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test);
- // Init PackedData since it's used by ProjectSettings.
- PackedData *packed_data = memnew(PackedData);
-
- // Setup project settings since it's needed by the languages to get the global scripts.
- // This also sets up the base resource path.
- Error err = ProjectSettings::get_singleton()->setup(fa->get_path_absolute().get_base_dir(), String(), true);
- if (err) {
- print_line("Could not load project settings.");
- // Keep going since some scripts still work without this.
- }
-
// Initialize the language for the test routine.
- ScriptServer::init_languages();
- init_autoloads();
+ init_language(fa->get_path_absolute().get_base_dir());
Vector<uint8_t> buf;
int flen = fa->get_len();
@@ -300,8 +234,6 @@ void test(TestType p_type) {
print_line("Not implemented.");
}
- // Destroy stuff we set up earlier.
- ScriptServer::finish_languages();
- memdelete(packed_data);
+ finish_language();
}
-} // namespace TestGDScript
+} // namespace GDScriptTests
diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h
index bbda46cdad..c7ee5a2208 100644
--- a/modules/gdscript/tests/test_gdscript.h
+++ b/modules/gdscript/tests/test_gdscript.h
@@ -31,7 +31,10 @@
#ifndef TEST_GDSCRIPT_H
#define TEST_GDSCRIPT_H
-namespace TestGDScript {
+#include "gdscript_test_runner.h"
+#include "tests/test_macros.h"
+
+namespace GDScriptTests {
enum TestType {
TEST_TOKENIZER,
@@ -41,6 +44,7 @@ enum TestType {
};
void test(TestType p_type);
-} // namespace TestGDScript
+
+} // namespace GDScriptTests
#endif // TEST_GDSCRIPT_H
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 0b70175a24..027a054b70 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -3293,6 +3293,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
}
img->decompress();
img->convert(Image::FORMAT_RGBA8);
+ img->convert_ra_rgba8_to_rg();
for (int32_t y = 0; y < img->get_height(); y++) {
for (int32_t x = 0; x < img->get_width(); x++) {
Color c = img->get_pixel(x, y);
@@ -4958,8 +4959,8 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_instance(Ref<GLTFState> state, MeshIns
if (godot_array_mesh.is_valid()) {
surface_name = godot_array_mesh->surface_get_name(surface_i);
}
- if (p_mesh_instance->get_surface_material(surface_i).is_valid()) {
- mat = p_mesh_instance->get_surface_material(surface_i);
+ if (p_mesh_instance->get_surface_override_material(surface_i).is_valid()) {
+ mat = p_mesh_instance->get_surface_override_material(surface_i);
}
if (p_mesh_instance->get_material_override().is_valid()) {
mat = p_mesh_instance->get_material_override();
diff --git a/modules/lightmapper_rd/lm_blendseams.glsl b/modules/lightmapper_rd/lm_blendseams.glsl
index e47e5fcc51..374c48082e 100644
--- a/modules/lightmapper_rd/lm_blendseams.glsl
+++ b/modules/lightmapper_rd/lm_blendseams.glsl
@@ -7,7 +7,7 @@ triangles = "#define MODE_TRIANGLES";
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "lm_common_inc.glsl"
@@ -74,7 +74,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "lm_common_inc.glsl"
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index eb9d817f99..3dd96893fb 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -10,7 +10,7 @@ light_probes = "#define MODE_LIGHT_PROBES";
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
// One 2D local group focusing in one layer at a time, though all
// in parallel (no barriers) makes more sense than a 3D local group
diff --git a/modules/lightmapper_rd/lm_raster.glsl b/modules/lightmapper_rd/lm_raster.glsl
index 6c2904192b..55ca193cc1 100644
--- a/modules/lightmapper_rd/lm_raster.glsl
+++ b/modules/lightmapper_rd/lm_raster.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "lm_common_inc.glsl"
@@ -56,7 +56,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "lm_common_inc.glsl"
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
index 159a2ed7b6..366662ff81 100644
--- a/modules/mono/mono_gd/gd_mono_wasm_m2n.h
+++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
@@ -176,7 +176,7 @@ T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) {
} else if constexpr (cookie == 'F') {
return *reinterpret_cast<float *>(&p_margs->fargs[fidx(p_idx)]);
} else if constexpr (cookie == 'D') {
- return (T)(size_t)p_margs->fargs[p_idx];
+ return (T)p_margs->fargs[p_idx];
}
}
diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_decompress_squish.cpp
index fb0c7aba1d..1450b0fe88 100644
--- a/modules/squish/image_compress_squish.cpp
+++ b/modules/squish/image_decompress_squish.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* image_compress_squish.cpp */
+/* image_decompress_squish.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "image_compress_squish.h"
+#include "image_decompress_squish.h"
#include <squish.h>
diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_decompress_squish.h
index ebc5a41887..fff5839ac4 100644
--- a/modules/squish/image_compress_squish.h
+++ b/modules/squish/image_decompress_squish.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* image_compress_squish.h */
+/* image_decompress_squish.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef IMAGE_COMPRESS_SQUISH_H
-#define IMAGE_COMPRESS_SQUISH_H
+#ifndef IMAGE_DECOMPRESS_SQUISH_H
+#define IMAGE_DECOMPRESS_SQUISH_H
#include "core/io/image.h"
void image_decompress_squish(Image *p_image);
-#endif // IMAGE_COMPRESS_SQUISH_H
+#endif // IMAGE_DECOMPRESS_SQUISH_H
diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp
index c51cdc9521..51aab040e7 100644
--- a/modules/squish/register_types.cpp
+++ b/modules/squish/register_types.cpp
@@ -29,7 +29,8 @@
/*************************************************************************/
#include "register_types.h"
-#include "image_compress_squish.h"
+
+#include "image_decompress_squish.h"
void register_squish_types() {
Image::_image_decompress_bc = image_decompress_squish;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 3cdf60708b..02ec9ccd06 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -710,7 +710,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
has_gnode_text = true;
LineEdit *line_edit = memnew(LineEdit);
line_edit->set_text(node->get_text());
- line_edit->set_expand_to_text_length(true);
+ line_edit->set_expand_to_text_length_enabled(true);
line_edit->add_theme_font_override("font", get_theme_font("source", "EditorFonts"));
gnode->add_child(line_edit);
line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E->get()));
@@ -843,7 +843,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
hbc->add_child(name_box);
name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
name_box->set_text(left_name);
- name_box->set_expand_to_text_length(true);
+ name_box->set_expand_to_text_length_enabled(true);
name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get()));
name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, true));
} else {
@@ -938,7 +938,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
hbc->add_child(name_box);
name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
name_box->set_text(right_name);
- name_box->set_expand_to_text_length(true);
+ name_box->set_expand_to_text_length_enabled(true);
name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get()));
name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, false));
} else {
@@ -4322,7 +4322,7 @@ VisualScriptEditor::VisualScriptEditor() {
function_name_box = memnew(LineEdit);
function_name_edit->add_child(function_name_box);
function_name_box->connect("gui_input", callable_mp(this, &VisualScriptEditor::_fn_name_box_input));
- function_name_box->set_expand_to_text_length(true);
+ function_name_box->set_expand_to_text_length_enabled(true);
add_child(function_name_edit);
/// Actual Graph ///
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 7e3bdfe3b4..f3502b2220 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -1819,7 +1819,7 @@ public:
p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
}
- String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk");
+ String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
#define CLEANUP_AND_RETURN(m_err) \
{ \
@@ -1836,6 +1836,7 @@ public:
List<String> args;
int rv;
+ String output;
bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install");
String version_name = p_preset->get("version/name");
@@ -1853,7 +1854,9 @@ public:
args.push_back("uninstall");
args.push_back(get_package_name(package_name));
- err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
}
print_line("Installing to device (please wait...): " + devices[p_device].name);
@@ -1868,7 +1871,9 @@ public:
args.push_back("-r");
args.push_back(tmp_export_path);
- err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
if (err || rv != 0) {
EditorNode::add_io_error("Could not install to device.");
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
@@ -1885,7 +1890,9 @@ public:
args.push_back(devices[p_device].id);
args.push_back("reverse");
args.push_back("--remove-all");
- OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
@@ -1896,7 +1903,9 @@ public:
args.push_back("tcp:" + itos(dbg_port));
args.push_back("tcp:" + itos(dbg_port));
- OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
print_line("Reverse result: " + itos(rv));
}
@@ -1910,7 +1919,9 @@ public:
args.push_back("tcp:" + itos(fs_port));
args.push_back("tcp:" + itos(fs_port));
- err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
print_line("Reverse result2: " + itos(rv));
}
} else {
@@ -1938,7 +1949,9 @@ public:
args.push_back("-n");
args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp");
- err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
if (err || rv != 0) {
EditorNode::add_io_error("Could not execute on device.");
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
@@ -2335,6 +2348,7 @@ public:
return ERR_FILE_CANT_OPEN;
}
+ String output;
List<String> args;
args.push_back("sign");
args.push_back("--verbose");
@@ -2350,7 +2364,9 @@ public:
print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
}
int retval;
- OS::get_singleton()->execute(apksigner, args, nullptr, &retval);
+ output.clear();
+ OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ print_verbose(output);
if (retval) {
EditorNode::add_io_error("'apksigner' returned with error #" + itos(retval));
return ERR_CANT_CREATE;
@@ -2368,7 +2384,9 @@ public:
print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
}
- OS::get_singleton()->execute(apksigner, args, nullptr, &retval);
+ output.clear();
+ OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ print_verbose(output);
if (retval) {
EditorNode::add_io_error("'apksigner' verification of " + export_label + " failed.");
return ERR_CANT_CREATE;
@@ -2673,7 +2691,7 @@ public:
FileAccess *dst_f = nullptr;
io2.opaque = &dst_f;
- String tmp_unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
+ String tmp_unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
#define CLEANUP_AND_RETURN(m_err) \
{ \
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index c0ae4007d2..ad9a19e2af 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -239,5 +239,5 @@ ext.shouldSign = { ->
}
ext.shouldNotStrip = { ->
- return isAndroidStudio()
+ return isAndroidStudio() || project.hasProperty("doNotStrip")
}
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index ec02b0fc7a..81570d9d86 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -122,16 +122,17 @@ task zipCustomBuild(type: Zip) {
destinationDir(file(binDir))
}
-/**
- * Master task used to coordinate the tasks defined above to generate the set of Godot templates.
- */
-task generateGodotTemplates(type: GradleBuild) {
+def templateExcludedBuildTask() {
// We exclude these gradle tasks so we can run the scons command manually.
+ def excludedTasks = []
for (String buildType : supportedTargets) {
- startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType)
+ excludedTasks += ":lib:" + getSconsTaskName(buildType)
}
+ return excludedTasks
+}
- tasks = []
+def templateBuildTasks() {
+ def tasks = []
// Only build the apks and aar files for which we have native shared libraries.
for (String target : supportedTargets) {
@@ -152,6 +153,29 @@ task generateGodotTemplates(type: GradleBuild) {
}
}
+ return tasks
+}
+
+/**
+ * Master task used to coordinate the tasks defined above to generate the set of Godot templates.
+ */
+task generateGodotTemplates(type: GradleBuild) {
+ startParameter.excludedTaskNames = templateExcludedBuildTask()
+ tasks = templateBuildTasks()
+
+ finalizedBy 'zipCustomBuild'
+}
+
+/**
+ * Generates the same output as generateGodotTemplates but with dev symbols
+ */
+task generateDevTemplate (type: GradleBuild) {
+ // add parameter to set symbols to true
+ startParameter.projectProperties += [doNotStrip: true]
+
+ startParameter.excludedTaskNames = templateExcludedBuildTask()
+ tasks = templateBuildTasks()
+
finalizedBy 'zipCustomBuild'
}
diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties
index 2dc069ad2f..6b3b62a9da 100644
--- a/platform/android/java/gradle.properties
+++ b/platform/android/java/gradle.properties
@@ -12,7 +12,7 @@ android.useAndroidX=true
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
+org.gradle.jvmargs=-Xmx4536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 6b527c6fb5..646ae4d457 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -67,10 +67,10 @@ def get_opts():
BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True),
BoolVariable("use_coverage", "Test Godot coverage", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
- BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
- BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
- BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
- BoolVariable("use_msan", "Use LLVM/GCC compiler memory sanitizer (MSAN))", False),
+ BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False),
+ BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN)", False),
+ BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
+ BoolVariable("use_msan", "Use LLVM compiler memory sanitizer (MSAN)", False),
BoolVariable("pulseaudio", "Detect and use PulseAudio", True),
BoolVariable("udev", "Use udev for gamepad connection callbacks", True),
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
@@ -147,11 +147,23 @@ def configure(env):
env.extra_suffix += "s"
if env["use_ubsan"]:
- env.Append(CCFLAGS=["-fsanitize=undefined"])
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin"
+ ]
+ )
env.Append(LINKFLAGS=["-fsanitize=undefined"])
+ if env["use_llvm"]:
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=nullability-return,nullability-arg,function,nullability-assign,implicit-integer-sign-change"
+ ]
+ )
+ else:
+ env.Append(CCFLAGS=["-fsanitize=bounds-strict"])
if env["use_asan"]:
- env.Append(CCFLAGS=["-fsanitize=address"])
+ env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"])
env.Append(LINKFLAGS=["-fsanitize=address"])
if env["use_lsan"]:
@@ -162,8 +174,10 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize=thread"])
env.Append(LINKFLAGS=["-fsanitize=thread"])
- if env["use_msan"]:
+ if env["use_msan"] and env["use_llvm"]:
env.Append(CCFLAGS=["-fsanitize=memory"])
+ env.Append(CCFLAGS=["-fsanitize-memory-track-origins"])
+ env.Append(CCFLAGS=["-fsanitize-recover=memory"])
env.Append(LINKFLAGS=["-fsanitize=memory"])
if env["use_lto"]:
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 5b320da82f..317e79d0ea 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -34,9 +34,8 @@ def get_opts():
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
- BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
- BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
- BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
+ BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False),
+ BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
]
@@ -132,21 +131,22 @@ def configure(env):
env["AS"] = basecmd + "as"
env.Append(CPPDEFINES=["__MACPORTS__"]) # hack to fix libvpx MM256_BROADCASTSI128_SI256 define
- if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
+ if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
env.extra_suffix += "s"
if env["use_ubsan"]:
- env.Append(CCFLAGS=["-fsanitize=undefined"])
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin"
+ ]
+ )
env.Append(LINKFLAGS=["-fsanitize=undefined"])
+ env.Append(CCFLAGS=["-fsanitize=nullability-return,nullability-arg,function,nullability-assign"])
if env["use_asan"]:
- env.Append(CCFLAGS=["-fsanitize=address"])
+ env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"])
env.Append(LINKFLAGS=["-fsanitize=address"])
- if env["use_lsan"]:
- env.Append(CCFLAGS=["-fsanitize=leak"])
- env.Append(LINKFLAGS=["-fsanitize=leak"])
-
if env["use_tsan"]:
env.Append(CCFLAGS=["-fsanitize=thread"])
env.Append(LINKFLAGS=["-fsanitize=thread"])
diff --git a/platform/server/detect.py b/platform/server/detect.py
index 16ddbe1768..478bcad212 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -35,11 +35,11 @@ def get_opts():
BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True),
BoolVariable("use_coverage", "Test Godot coverage", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
- BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
- BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
- BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
+ BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False),
+ BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN)", False),
+ BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
+ BoolVariable("use_msan", "Use LLVM compiler memory sanitizer (MSAN)", False),
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
- BoolVariable("use_msan", "Use LLVM/GCC compiler memory sanitizer (MSAN))", False),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
]
@@ -104,11 +104,23 @@ def configure(env):
env.extra_suffix += "s"
if env["use_ubsan"]:
- env.Append(CCFLAGS=["-fsanitize=undefined"])
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin"
+ ]
+ )
env.Append(LINKFLAGS=["-fsanitize=undefined"])
+ if env["use_llvm"]:
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=nullability-return,nullability-arg,function,nullability-assign,implicit-integer-sign-change"
+ ]
+ )
+ else:
+ env.Append(CCFLAGS=["-fsanitize=bounds-strict"])
if env["use_asan"]:
- env.Append(CCFLAGS=["-fsanitize=address"])
+ env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"])
env.Append(LINKFLAGS=["-fsanitize=address"])
if env["use_lsan"]:
@@ -119,8 +131,10 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize=thread"])
env.Append(LINKFLAGS=["-fsanitize=thread"])
- if env["use_msan"]:
+ if env["use_msan"] and env["use_llvm"]:
env.Append(CCFLAGS=["-fsanitize=memory"])
+ env.Append(CCFLAGS=["-fsanitize-memory-track-origins"])
+ env.Append(CCFLAGS=["-fsanitize-recover=memory"])
env.Append(LINKFLAGS=["-fsanitize=memory"])
if env["use_lto"]:
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 49d1654e3f..96b4abe1ba 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -600,6 +600,11 @@ void Area2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("area_entered", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D")));
ADD_SIGNAL(MethodInfo("area_exited", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D")));
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
+
+ ADD_GROUP("Physics Overrides", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
@@ -607,9 +612,7 @@ void Area2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.001"), "set_gravity", "get_gravity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
+
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index 749cf4ff9d..7e57ef90da 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -650,6 +650,11 @@ void Area3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("area_entered", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D")));
ADD_SIGNAL(MethodInfo("area_exited", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D")));
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
+
+ ADD_GROUP("Physics Overrides", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
@@ -657,15 +662,15 @@ void Area3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_gravity", "get_gravity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
+
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
ADD_GROUP("Audio Bus", "audio_bus_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus_name", "get_audio_bus_name");
+
ADD_GROUP("Reverb Bus", "reverb_bus_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverb_bus_enable"), "set_use_reverb_bus", "is_using_reverb_bus");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "reverb_bus_name", PROPERTY_HINT_ENUM, ""), "set_reverb_bus", "get_reverb_bus");
diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
index 95ffbe48c1..2e1b77dfe5 100644
--- a/scene/3d/baked_lightmap.cpp
+++ b/scene/3d/baked_lightmap.cpp
@@ -259,7 +259,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound>
if (all_override.is_valid()) {
mf.overrides.push_back(all_override);
} else {
- mf.overrides.push_back(mi->get_surface_material(i));
+ mf.overrides.push_back(mi->get_surface_override_material(i));
}
}
diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp
index 0da53d0101..4d7fc29f15 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -343,7 +343,7 @@ void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
pm.local_xform = xf;
pm.mesh = mesh;
for (int i = 0; i < mesh->get_surface_count(); i++) {
- pm.instance_materials.push_back(mi->get_surface_material(i));
+ pm.instance_materials.push_back(mi->get_surface_override_material(i));
}
pm.override_material = mi->get_material_override();
plot_meshes.push_back(pm);
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
index 97241be60f..628b823f89 100644
--- a/scene/3d/gpu_particles_collision_3d.cpp
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -346,7 +346,7 @@ void GPUParticlesCollisionSDF::_compute_sdf(ComputeSDFParams *params) {
ThreadWorkPool work_pool;
work_pool.init();
work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF::_compute_sdf_z, params);
- while (work_pool.get_work_index() < (uint32_t)params->size.z) {
+ while (!work_pool.is_done_dispatching()) {
OS::get_singleton()->delay_usec(10000);
bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF");
}
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index b997c64b29..7623ede0fc 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -51,13 +51,13 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
- if (p_name.operator String().begins_with("material/")) {
+ if (p_name.operator String().begins_with("surface_material_override/")) {
int idx = p_name.operator String().get_slicec('/', 1).to_int();
- if (idx >= materials.size() || idx < 0) {
+ if (idx >= surface_override_materials.size() || idx < 0) {
return false;
}
- set_surface_material(idx, p_value);
+ set_surface_override_material(idx, p_value);
return true;
}
@@ -75,12 +75,12 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const {
return true;
}
- if (p_name.operator String().begins_with("material/")) {
+ if (p_name.operator String().begins_with("surface_material_override/")) {
int idx = p_name.operator String().get_slicec('/', 1).to_int();
- if (idx >= materials.size() || idx < 0) {
+ if (idx >= surface_override_materials.size() || idx < 0) {
return false;
}
- r_ret = materials[idx];
+ r_ret = surface_override_materials[idx];
return true;
}
return false;
@@ -100,7 +100,7 @@ void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const {
if (mesh.is_valid()) {
for (int i = 0; i < mesh->get_surface_count(); i++) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "material/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_material_override/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE));
}
}
}
@@ -126,7 +126,7 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) {
}
mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed));
- materials.resize(mesh->get_surface_count());
+ surface_override_materials.resize(mesh->get_surface_count());
set_base(mesh->get_rid());
} else {
@@ -277,26 +277,26 @@ void MeshInstance3D::_notification(int p_what) {
}
}
-int MeshInstance3D::get_surface_material_count() const {
- return materials.size();
+int MeshInstance3D::get_surface_override_material_count() const {
+ return surface_override_materials.size();
}
-void MeshInstance3D::set_surface_material(int p_surface, const Ref<Material> &p_material) {
- ERR_FAIL_INDEX(p_surface, materials.size());
+void MeshInstance3D::set_surface_override_material(int p_surface, const Ref<Material> &p_material) {
+ ERR_FAIL_INDEX(p_surface, surface_override_materials.size());
- materials.write[p_surface] = p_material;
+ surface_override_materials.write[p_surface] = p_material;
- if (materials[p_surface].is_valid()) {
- RS::get_singleton()->instance_set_surface_material(get_instance(), p_surface, materials[p_surface]->get_rid());
+ if (surface_override_materials[p_surface].is_valid()) {
+ RS::get_singleton()->instance_set_surface_override_material(get_instance(), p_surface, surface_override_materials[p_surface]->get_rid());
} else {
- RS::get_singleton()->instance_set_surface_material(get_instance(), p_surface, RID());
+ RS::get_singleton()->instance_set_surface_override_material(get_instance(), p_surface, RID());
}
}
-Ref<Material> MeshInstance3D::get_surface_material(int p_surface) const {
- ERR_FAIL_INDEX_V(p_surface, materials.size(), Ref<Material>());
+Ref<Material> MeshInstance3D::get_surface_override_material(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surface_override_materials.size(), Ref<Material>());
- return materials[p_surface];
+ return surface_override_materials[p_surface];
}
Ref<Material> MeshInstance3D::get_active_material(int p_surface) const {
@@ -305,7 +305,7 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const {
return material_override;
}
- Ref<Material> surface_material = get_surface_material(p_surface);
+ Ref<Material> surface_material = get_surface_override_material(p_surface);
if (surface_material.is_valid()) {
return surface_material;
}
@@ -320,7 +320,7 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const {
void MeshInstance3D::_mesh_changed() {
ERR_FAIL_COND(mesh.is_null());
- materials.resize(mesh->get_surface_count());
+ surface_override_materials.resize(mesh->get_surface_count());
}
void MeshInstance3D::create_debug_tangents() {
@@ -408,9 +408,9 @@ void MeshInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_skin", "skin"), &MeshInstance3D::set_skin);
ClassDB::bind_method(D_METHOD("get_skin"), &MeshInstance3D::get_skin);
- ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance3D::get_surface_material_count);
- ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance3D::set_surface_material);
- ClassDB::bind_method(D_METHOD("get_surface_material", "surface"), &MeshInstance3D::get_surface_material);
+ ClassDB::bind_method(D_METHOD("get_surface_override_material_count"), &MeshInstance3D::get_surface_override_material_count);
+ ClassDB::bind_method(D_METHOD("set_surface_override_material", "surface", "material"), &MeshInstance3D::set_surface_override_material);
+ ClassDB::bind_method(D_METHOD("get_surface_override_material", "surface"), &MeshInstance3D::get_surface_override_material);
ClassDB::bind_method(D_METHOD("get_active_material", "surface"), &MeshInstance3D::get_active_material);
ClassDB::bind_method(D_METHOD("create_trimesh_collision"), &MeshInstance3D::create_trimesh_collision);
diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h
index eb300784b1..8aec227337 100644
--- a/scene/3d/mesh_instance_3d.h
+++ b/scene/3d/mesh_instance_3d.h
@@ -52,7 +52,7 @@ protected:
};
Map<StringName, BlendShapeTrack> blend_shape_tracks;
- Vector<Ref<Material>> materials;
+ Vector<Ref<Material>> surface_override_materials;
void _mesh_changed();
void _resolve_skeleton_path();
@@ -75,9 +75,9 @@ public:
void set_skeleton_path(const NodePath &p_skeleton);
NodePath get_skeleton_path();
- int get_surface_material_count() const;
- void set_surface_material(int p_surface, const Ref<Material> &p_material);
- Ref<Material> get_surface_material(int p_surface) const;
+ int get_surface_override_material_count() const;
+ void set_surface_override_material(int p_surface, const Ref<Material> &p_material);
+ Ref<Material> get_surface_override_material(int p_surface) const;
Ref<Material> get_active_material(int p_surface) const;
Node *create_trimesh_collision_node();
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index db5fc7593e..ebbb8985c9 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -387,6 +387,7 @@ void Skeleton3D::_notification(int p_what) {
void Skeleton3D::clear_bones_global_pose_override() {
for (int i = 0; i < bones.size(); i += 1) {
bones.write[i].global_pose_override_amount = 0;
+ bones.write[i].global_pose_override_reset = true;
}
_make_dirty();
}
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 6cde6a9b17..898f94ccc1 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -270,7 +270,6 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
return; // Skip solving
}
- // This line below is part of the problem - removing it fixes the issue with BoneAttachment nodes...
p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, true);
if (p_task->chain.middle_chain_item) {
@@ -567,6 +566,9 @@ void SkeletonIK3D::start(bool p_one_time) {
void SkeletonIK3D::stop() {
set_process_internal(false);
+ if (skeleton) {
+ skeleton->clear_bones_global_pose_override();
+ }
}
Transform SkeletonIK3D::_get_target_transform() {
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index 08e25b589e..98ac6aa65e 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -452,7 +452,7 @@ void SoftBody3D::become_mesh_owner() {
mesh_owner = true;
Vector<Ref<Material>> copy_materials;
- copy_materials.append_array(materials);
+ copy_materials.append_array(surface_override_materials);
ERR_FAIL_COND(!mesh->get_surface_count());
@@ -472,7 +472,7 @@ void SoftBody3D::become_mesh_owner() {
set_mesh(soft_mesh);
for (int i = copy_materials.size() - 1; 0 <= i; --i) {
- set_surface_material(i, copy_materials[i]);
+ set_surface_override_material(i, copy_materials[i]);
}
}
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 2d8eb3191c..124a07fa65 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -51,13 +51,13 @@ void LineEdit::_swap_current_input_direction() {
} else {
input_direction = TEXT_DIRECTION_LTR;
}
- set_cursor_position(get_cursor_position());
+ set_caret_column(get_caret_column());
update();
}
-void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) {
+void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
if (selection.enabled && !p_select) {
- set_cursor_position(selection.begin);
+ set_caret_column(selection.begin);
deselect();
return;
}
@@ -65,7 +65,7 @@ void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) {
shift_selection_check_pre(p_select);
if (p_move_by_word) {
- int cc = cursor_pos;
+ int cc = caret_column;
Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
for (int i = words.size() - 1; i >= 0; i--) {
@@ -75,21 +75,21 @@ void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) {
}
}
- set_cursor_position(cc);
+ set_caret_column(cc);
} else {
- if (mid_grapheme_caret_enabled) {
- set_cursor_position(get_cursor_position() - 1);
+ if (caret_mid_grapheme_enabled) {
+ set_caret_column(get_caret_column() - 1);
} else {
- set_cursor_position(TS->shaped_text_prev_grapheme_pos(text_rid, get_cursor_position()));
+ set_caret_column(TS->shaped_text_prev_grapheme_pos(text_rid, get_caret_column()));
}
}
shift_selection_check_post(p_select);
}
-void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) {
+void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
if (selection.enabled && !p_select) {
- set_cursor_position(selection.end);
+ set_caret_column(selection.end);
deselect();
return;
}
@@ -97,7 +97,7 @@ void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) {
shift_selection_check_pre(p_select);
if (p_move_by_word) {
- int cc = cursor_pos;
+ int cc = caret_column;
Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
for (int i = 0; i < words.size(); i++) {
@@ -107,27 +107,27 @@ void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) {
}
}
- set_cursor_position(cc);
+ set_caret_column(cc);
} else {
- if (mid_grapheme_caret_enabled) {
- set_cursor_position(get_cursor_position() + 1);
+ if (caret_mid_grapheme_enabled) {
+ set_caret_column(get_caret_column() + 1);
} else {
- set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, get_cursor_position()));
+ set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, get_caret_column()));
}
}
shift_selection_check_post(p_select);
}
-void LineEdit::_move_cursor_start(bool p_select) {
+void LineEdit::_move_caret_start(bool p_select) {
shift_selection_check_pre(p_select);
- set_cursor_position(0);
+ set_caret_column(0);
shift_selection_check_post(p_select);
}
-void LineEdit::_move_cursor_end(bool p_select) {
+void LineEdit::_move_caret_end(bool p_select) {
shift_selection_check_pre(p_select);
- set_cursor_position(text.length());
+ set_caret_column(text.length());
shift_selection_check_post(p_select);
}
@@ -138,7 +138,7 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
if (p_all_to_left) {
deselect();
- text = text.substr(0, cursor_pos);
+ text = text.substr(0, caret_column);
_text_changed();
return;
}
@@ -149,7 +149,7 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
}
if (p_word) {
- int cc = cursor_pos;
+ int cc = caret_column;
Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
for (int i = words.size() - 1; i >= 0; i--) {
@@ -158,9 +158,9 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
}
}
- delete_text(cc, cursor_pos);
+ delete_text(cc, caret_column);
- set_cursor_position(cc);
+ set_caret_column(cc);
} else {
delete_char();
}
@@ -173,9 +173,9 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
if (p_all_to_right) {
deselect();
- text = text.substr(cursor_pos, text.length() - cursor_pos);
+ text = text.substr(caret_column, text.length() - caret_column);
_shape();
- set_cursor_position(0);
+ set_caret_column(0);
_text_changed();
return;
}
@@ -187,12 +187,12 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
int text_len = text.length();
- if (cursor_pos == text_len) {
+ if (caret_column == text_len) {
return; // Nothing to do.
}
if (p_word) {
- int cc = cursor_pos;
+ int cc = caret_column;
Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
for (int i = 0; i < words.size(); i++) {
if (words[i].y > cc) {
@@ -201,15 +201,15 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
}
}
- delete_text(cursor_pos, cc);
+ delete_text(caret_column, cc);
} else {
- if (mid_grapheme_caret_enabled) {
- set_cursor_position(cursor_pos + 1);
+ if (caret_mid_grapheme_enabled) {
+ set_caret_column(caret_column + 1);
delete_char();
} else {
- int cc = cursor_pos;
- set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, cursor_pos));
- delete_text(cc, cursor_pos);
+ int cc = caret_column;
+ set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, caret_column));
+ delete_text(cc, caret_column);
}
}
}
@@ -250,10 +250,10 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
shift_selection_check_pre(b->get_shift());
- set_cursor_at_pixel_pos(b->get_position().x);
+ set_caret_at_pixel_pos(b->get_position().x);
if (b->get_shift()) {
- selection_fill_at_cursor();
+ selection_fill_at_caret();
selection.creating = true;
} else {
@@ -265,18 +265,18 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
selection.end = text.length();
selection.doubleclick = true;
selection.last_dblclk = 0;
- cursor_pos = selection.begin;
+ caret_column = selection.begin;
} else if (b->is_doubleclick()) {
// Double-click select word.
Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
for (int i = 0; i < words.size(); i++) {
- if (words[i].x < cursor_pos && words[i].y > cursor_pos) {
+ if (words[i].x < caret_column && words[i].y > caret_column) {
selection.enabled = true;
selection.begin = words[i].x;
selection.end = words[i].y;
selection.doubleclick = true;
selection.last_dblclk = OS::get_singleton()->get_ticks_msec();
- cursor_pos = selection.end;
+ caret_column = selection.end;
break;
}
}
@@ -285,9 +285,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
selection.drag_attempt = false;
- if ((cursor_pos < selection.begin) || (cursor_pos > selection.end) || !selection.enabled) {
+ if ((caret_column < selection.begin) || (caret_column > selection.end) || !selection.enabled) {
deselect();
- selection.cursor_start = cursor_pos;
+ selection.start_column = caret_column;
selection.creating = true;
} else if (selection.enabled) {
selection.drag_attempt = true;
@@ -331,8 +331,8 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (m->get_button_mask() & MOUSE_BUTTON_LEFT) {
if (selection.creating) {
- set_cursor_at_pixel_pos(m->get_position().x);
- selection_fill_at_cursor();
+ set_caret_at_pixel_pos(m->get_position().x);
+ selection_fill_at_caret();
}
}
}
@@ -346,7 +346,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (context_menu_enabled) {
if (k->is_action("ui_menu", true)) {
- Point2 pos = Point2(get_cursor_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2);
+ Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2);
menu->set_position(get_global_transform().xform(pos));
menu->set_size(Vector2(1, 1));
_generate_context_menu();
@@ -445,34 +445,34 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
k->set_shift(false);
if (k->is_action("ui_text_caret_word_left", true)) {
- _move_cursor_left(shift_pressed, true);
+ _move_caret_left(shift_pressed, true);
accept_event();
return;
}
if (k->is_action("ui_text_caret_left", true)) {
- _move_cursor_left(shift_pressed);
+ _move_caret_left(shift_pressed);
accept_event();
return;
}
if (k->is_action("ui_text_caret_word_right", true)) {
- _move_cursor_right(shift_pressed, true);
+ _move_caret_right(shift_pressed, true);
accept_event();
return;
}
if (k->is_action("ui_text_caret_right", true)) {
- _move_cursor_right(shift_pressed, false);
+ _move_caret_right(shift_pressed, false);
accept_event();
return;
}
// Up = Home, Down = End
if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) {
- _move_cursor_start(shift_pressed);
+ _move_caret_start(shift_pressed);
accept_event();
return;
}
if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) {
- _move_cursor_end(shift_pressed);
+ _move_caret_end(shift_pressed);
accept_event();
return;
}
@@ -495,7 +495,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
selection_delete();
char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 };
int prev_len = text.length();
- append_at_cursor(ucodestr);
+ insert_text_at_caret(ucodestr);
if (text.length() != prev_len) {
_text_changed();
}
@@ -542,15 +542,15 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
Control::drop_data(p_point, p_data);
if (p_data.get_type() == Variant::STRING) {
- set_cursor_at_pixel_pos(p_point.x);
+ set_caret_at_pixel_pos(p_point.x);
int selected = selection.end - selection.begin;
text.erase(selection.begin, selected);
_shape();
- append_at_cursor(p_data);
- selection.begin = cursor_pos - selected;
- selection.end = cursor_pos;
+ insert_text_at_caret(p_data);
+ selection.begin = caret_column - selected;
+ selection.end = caret_column;
}
}
@@ -575,8 +575,8 @@ void LineEdit::_notification(int p_what) {
#ifdef TOOLS_ENABLED
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) {
- cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
- cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
+ set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) {
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed));
@@ -587,7 +587,7 @@ void LineEdit::_notification(int p_what) {
case NOTIFICATION_RESIZED: {
_fit_to_width();
scroll_offset = 0;
- set_cursor_position(get_cursor_position());
+ set_caret_column(get_caret_column());
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
@@ -674,7 +674,7 @@ void LineEdit::_notification(int p_what) {
Color selection_color = get_theme_color("selection_color");
Color font_color = is_editable() ? get_theme_color("font_color") : get_theme_color("font_uneditable_color");
Color font_selected_color = get_theme_color("font_selected_color");
- Color cursor_color = get_theme_color("cursor_color");
+ Color caret_color = get_theme_color("caret_color");
// Draw placeholder color.
if (using_placeholder) {
@@ -778,7 +778,7 @@ void LineEdit::_notification(int p_what) {
// Normal caret.
Rect2 l_caret, t_caret;
TextServer::Direction l_dir, t_dir;
- TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir);
if (l_caret == Rect2() && t_caret == Rect2()) {
// No carets, add one at the start.
@@ -791,28 +791,28 @@ void LineEdit::_notification(int p_what) {
l_dir = TextServer::DIRECTION_LTR;
l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h));
}
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color);
} else {
if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) {
// Draw extra marker on top of mid caret.
Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width);
trect.position += ofs;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color);
}
l_caret.position += ofs;
l_caret.size.x = caret_width;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color);
t_caret.position += ofs;
t_caret.size.x = caret_width;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, caret_color);
}
} else {
{
// IME intermediate text range.
- Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos, cursor_pos + ime_text.length());
+ Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column, caret_column + ime_text.length());
for (int i = 0; i < sel.size(); i++) {
Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
@@ -825,12 +825,12 @@ void LineEdit::_notification(int p_what) {
rect.size.x = ofs_max - rect.position.x;
}
rect.size.y = caret_width;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color);
}
}
{
// IME caret.
- Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos + ime_selection.x, cursor_pos + ime_selection.x + ime_selection.y);
+ Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column + ime_selection.x, caret_column + ime_selection.x + ime_selection.y);
for (int i = 0; i < sel.size(); i++) {
Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
@@ -843,7 +843,7 @@ void LineEdit::_notification(int p_what) {
rect.size.x = ofs_max - rect.position.x;
}
rect.size.y = caret_width * 3;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color);
}
}
}
@@ -869,8 +869,8 @@ void LineEdit::_notification(int p_what) {
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
- Point2 cursor_pos = Point2(get_cursor_position(), 1) * get_minimum_size().height;
- DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id());
+ Point2 caret_column = Point2(get_caret_column(), 1) * get_minimum_size().height;
+ DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + caret_column, get_viewport()->get_window_id());
}
show_virtual_keyboard();
@@ -887,7 +887,7 @@ void LineEdit::_notification(int p_what) {
ime_text = "";
ime_selection = Point2();
_shape();
- set_cursor_position(cursor_pos); // Update scroll_offset
+ set_caret_column(caret_column); // Update scroll_offset
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
@@ -899,7 +899,7 @@ void LineEdit::_notification(int p_what) {
ime_text = DisplayServer::get_singleton()->ime_get_text();
ime_selection = DisplayServer::get_singleton()->ime_get_selection();
_shape();
- set_cursor_position(cursor_pos); // Update scroll_offset
+ set_caret_column(caret_column); // Update scroll_offset
update();
}
@@ -933,7 +933,7 @@ void LineEdit::paste_text() {
if (selection.enabled) {
selection_delete();
}
- append_at_cursor(paste_buffer);
+ insert_text_at_caret(paste_buffer);
if (!text_changed_dirty) {
if (is_inside_tree() && text.length() != prev_len) {
@@ -961,7 +961,7 @@ void LineEdit::undo() {
TextOperation op = undo_stack_pos->get();
text = op.text;
scroll_offset = op.scroll_offset;
- set_cursor_position(op.cursor_pos);
+ set_caret_column(op.caret_column);
_shape();
_emit_text_change();
@@ -982,7 +982,7 @@ void LineEdit::redo() {
TextOperation op = undo_stack_pos->get();
text = op.text;
scroll_offset = op.scroll_offset;
- set_cursor_position(op.cursor_pos);
+ set_caret_column(op.caret_column);
_shape();
_emit_text_change();
@@ -990,7 +990,7 @@ void LineEdit::redo() {
void LineEdit::shift_selection_check_pre(bool p_shift) {
if (!selection.enabled && p_shift) {
- selection.cursor_start = cursor_pos;
+ selection.start_column = caret_column;
}
if (!p_shift) {
deselect();
@@ -999,11 +999,11 @@ void LineEdit::shift_selection_check_pre(bool p_shift) {
void LineEdit::shift_selection_check_post(bool p_shift) {
if (p_shift) {
- selection_fill_at_cursor();
+ selection_fill_at_caret();
}
}
-void LineEdit::set_cursor_at_pixel_pos(int p_x) {
+void LineEdit::set_caret_at_pixel_pos(int p_x) {
Ref<StyleBox> style = get_theme_stylebox("normal");
bool rtl = is_layout_rtl();
@@ -1048,10 +1048,10 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
}
int ofs = TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset);
- set_cursor_position(ofs);
+ set_caret_column(ofs);
}
-Vector2i LineEdit::get_cursor_pixel_pos() {
+Vector2i LineEdit::get_caret_pixel_pos() {
Ref<StyleBox> style = get_theme_stylebox("normal");
bool rtl = is_layout_rtl();
@@ -1100,9 +1100,9 @@ Vector2i LineEdit::get_cursor_pixel_pos() {
TextServer::Direction l_dir, t_dir;
// Get position of the start of caret.
if (ime_text.length() != 0 && ime_selection.x != 0) {
- TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x, l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x, l_caret, l_dir, t_caret, t_dir);
} else {
- TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir);
}
if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
@@ -1114,9 +1114,9 @@ Vector2i LineEdit::get_cursor_pixel_pos() {
// Get position of the end of caret.
if (ime_text.length() != 0) {
if (ime_selection.y != 0) {
- TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir);
} else {
- TS->shaped_text_get_carets(text_rid, cursor_pos + ime_text.size(), l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size(), l_caret, l_dir, t_caret, t_dir);
}
if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
ret.y = x_ofs + l_caret.position.x + scroll_offset;
@@ -1130,19 +1130,19 @@ Vector2i LineEdit::get_cursor_pixel_pos() {
return ret;
}
-void LineEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) {
- mid_grapheme_caret_enabled = p_enabled;
+void LineEdit::set_caret_mid_grapheme_enabled(const bool p_enabled) {
+ caret_mid_grapheme_enabled = p_enabled;
}
-bool LineEdit::get_mid_grapheme_caret_enabled() const {
- return mid_grapheme_caret_enabled;
+bool LineEdit::is_caret_mid_grapheme_enabled() const {
+ return caret_mid_grapheme_enabled;
}
-bool LineEdit::cursor_get_blink_enabled() const {
+bool LineEdit::is_caret_blink_enabled() const {
return caret_blink_enabled;
}
-void LineEdit::cursor_set_blink_enabled(const bool p_enabled) {
+void LineEdit::set_caret_blink_enabled(const bool p_enabled) {
caret_blink_enabled = p_enabled;
if (has_focus() || caret_force_displayed) {
@@ -1160,21 +1160,21 @@ void LineEdit::cursor_set_blink_enabled(const bool p_enabled) {
notify_property_list_changed();
}
-bool LineEdit::cursor_get_force_displayed() const {
+bool LineEdit::is_caret_force_displayed() const {
return caret_force_displayed;
}
-void LineEdit::cursor_set_force_displayed(const bool p_enabled) {
+void LineEdit::set_caret_force_displayed(const bool p_enabled) {
caret_force_displayed = p_enabled;
- cursor_set_blink_enabled(caret_blink_enabled);
+ set_caret_blink_enabled(caret_blink_enabled);
update();
}
-float LineEdit::cursor_get_blink_speed() const {
+float LineEdit::get_caret_blink_speed() const {
return caret_blink_timer->get_wait_time();
}
-void LineEdit::cursor_set_blink_speed(const float p_speed) {
+void LineEdit::set_caret_blink_speed(const float p_speed) {
ERR_FAIL_COND(p_speed <= 0);
caret_blink_timer->set_wait_time(p_speed);
}
@@ -1198,14 +1198,14 @@ void LineEdit::_toggle_draw_caret() {
}
void LineEdit::delete_char() {
- if ((text.length() <= 0) || (cursor_pos == 0)) {
+ if ((text.length() <= 0) || (caret_column == 0)) {
return;
}
- text.erase(cursor_pos - 1, 1);
+ text.erase(caret_column - 1, 1);
_shape();
- set_cursor_position(get_cursor_position() - 1);
+ set_caret_column(get_caret_column() - 1);
_text_changed();
}
@@ -1217,10 +1217,10 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) {
text.erase(p_from_column, p_to_column - p_from_column);
_shape();
- cursor_pos -= CLAMP(cursor_pos - p_from_column, 0, p_to_column - p_from_column);
+ caret_column -= CLAMP(caret_column - p_from_column, 0, p_to_column - p_from_column);
- if (cursor_pos >= text.length()) {
- cursor_pos = text.length();
+ if (caret_column >= text.length()) {
+ caret_column = text.length();
}
if (!text_changed_dirty) {
@@ -1233,10 +1233,11 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) {
void LineEdit::set_text(String p_text) {
clear_internal();
- append_at_cursor(p_text);
+ insert_text_at_caret(p_text);
+ _create_undo_state();
update();
- cursor_pos = 0;
+ caret_column = 0;
scroll_offset = 0;
}
@@ -1346,7 +1347,7 @@ void LineEdit::show_virtual_keyboard() {
if (selection.enabled) {
DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end);
} else {
- DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, cursor_pos);
+ DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, caret_column);
}
}
}
@@ -1375,16 +1376,16 @@ float LineEdit::get_placeholder_alpha() const {
return placeholder_alpha;
}
-void LineEdit::set_cursor_position(int p_pos) {
- if (p_pos > (int)text.length()) {
- p_pos = text.length();
+void LineEdit::set_caret_column(int p_column) {
+ if (p_column > (int)text.length()) {
+ p_column = text.length();
}
- if (p_pos < 0) {
- p_pos = 0;
+ if (p_column < 0) {
+ p_column = 0;
}
- cursor_pos = p_pos;
+ caret_column = p_column;
// Fit to window.
@@ -1439,7 +1440,7 @@ void LineEdit::set_cursor_position(int p_pos) {
}
// Note: Use two coordinates to fit IME input range.
- Vector2i primary_catret_offset = get_cursor_pixel_pos();
+ Vector2i primary_catret_offset = get_caret_pixel_pos();
if (MIN(primary_catret_offset.x, primary_catret_offset.y) <= x_ofs) {
scroll_offset += (x_ofs - MIN(primary_catret_offset.x, primary_catret_offset.y));
@@ -1451,8 +1452,8 @@ void LineEdit::set_cursor_position(int p_pos) {
update();
}
-int LineEdit::get_cursor_position() const {
- return cursor_pos;
+int LineEdit::get_caret_column() const {
+ return caret_column;
}
void LineEdit::set_scroll_offset(int p_pos) {
@@ -1466,17 +1467,17 @@ int LineEdit::get_scroll_offset() const {
return scroll_offset;
}
-void LineEdit::append_at_cursor(String p_text) {
+void LineEdit::insert_text_at_caret(String p_text) {
if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) {
- String pre = text.substr(0, cursor_pos);
- String post = text.substr(cursor_pos, text.length() - cursor_pos);
+ String pre = text.substr(0, caret_column);
+ String post = text.substr(caret_column, text.length() - caret_column);
text = pre + p_text + post;
_shape();
- TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, cursor_pos, cursor_pos + p_text.length());
+ TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length());
if (dir != TextServer::DIRECTION_AUTO) {
input_direction = (TextDirection)dir;
}
- set_cursor_position(cursor_pos + p_text.length());
+ set_caret_column(caret_column + p_text.length());
} else {
emit_signal("text_change_rejected");
}
@@ -1485,7 +1486,7 @@ void LineEdit::append_at_cursor(String p_text) {
void LineEdit::clear_internal() {
deselect();
_clear_undo_stack();
- cursor_pos = 0;
+ caret_column = 0;
scroll_offset = 0;
undo_text = "";
text = "";
@@ -1505,7 +1506,7 @@ Size2 LineEdit::get_minimum_size() const {
min_size.width = get_theme_constant("minimum_character_width") * em_space_size;
if (expand_to_text_length) {
- // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end.
+ // Add a space because some fonts are too exact, and because caret needs a bit more when at the end.
min_size.width = MAX(min_size.width, full_width + em_space_size);
}
@@ -1526,7 +1527,7 @@ Size2 LineEdit::get_minimum_size() const {
void LineEdit::deselect() {
selection.begin = 0;
selection.end = 0;
- selection.cursor_start = 0;
+ selection.start_column = 0;
selection.enabled = false;
selection.creating = false;
selection.doubleclick = false;
@@ -1551,13 +1552,13 @@ int LineEdit::get_max_length() const {
return max_length;
}
-void LineEdit::selection_fill_at_cursor() {
+void LineEdit::selection_fill_at_caret() {
if (!selecting_enabled) {
return;
}
- selection.begin = cursor_pos;
- selection.end = selection.cursor_start;
+ selection.begin = caret_column;
+ selection.end = selection.start_column;
if (selection.end < selection.begin) {
int aux = selection.end;
@@ -1714,82 +1715,82 @@ void LineEdit::menu_option(int p_option) {
} break;
case MENU_INSERT_LRM: {
if (editable) {
- append_at_cursor(String::chr(0x200E));
+ insert_text_at_caret(String::chr(0x200E));
}
} break;
case MENU_INSERT_RLM: {
if (editable) {
- append_at_cursor(String::chr(0x200F));
+ insert_text_at_caret(String::chr(0x200F));
}
} break;
case MENU_INSERT_LRE: {
if (editable) {
- append_at_cursor(String::chr(0x202A));
+ insert_text_at_caret(String::chr(0x202A));
}
} break;
case MENU_INSERT_RLE: {
if (editable) {
- append_at_cursor(String::chr(0x202B));
+ insert_text_at_caret(String::chr(0x202B));
}
} break;
case MENU_INSERT_LRO: {
if (editable) {
- append_at_cursor(String::chr(0x202D));
+ insert_text_at_caret(String::chr(0x202D));
}
} break;
case MENU_INSERT_RLO: {
if (editable) {
- append_at_cursor(String::chr(0x202E));
+ insert_text_at_caret(String::chr(0x202E));
}
} break;
case MENU_INSERT_PDF: {
if (editable) {
- append_at_cursor(String::chr(0x202C));
+ insert_text_at_caret(String::chr(0x202C));
}
} break;
case MENU_INSERT_ALM: {
if (editable) {
- append_at_cursor(String::chr(0x061C));
+ insert_text_at_caret(String::chr(0x061C));
}
} break;
case MENU_INSERT_LRI: {
if (editable) {
- append_at_cursor(String::chr(0x2066));
+ insert_text_at_caret(String::chr(0x2066));
}
} break;
case MENU_INSERT_RLI: {
if (editable) {
- append_at_cursor(String::chr(0x2067));
+ insert_text_at_caret(String::chr(0x2067));
}
} break;
case MENU_INSERT_FSI: {
if (editable) {
- append_at_cursor(String::chr(0x2068));
+ insert_text_at_caret(String::chr(0x2068));
}
} break;
case MENU_INSERT_PDI: {
if (editable) {
- append_at_cursor(String::chr(0x2069));
+ insert_text_at_caret(String::chr(0x2069));
}
} break;
case MENU_INSERT_ZWJ: {
if (editable) {
- append_at_cursor(String::chr(0x200D));
+ insert_text_at_caret(String::chr(0x200D));
}
} break;
case MENU_INSERT_ZWNJ: {
if (editable) {
- append_at_cursor(String::chr(0x200C));
+ insert_text_at_caret(String::chr(0x200C));
}
} break;
case MENU_INSERT_WJ: {
if (editable) {
- append_at_cursor(String::chr(0x2060));
+ insert_text_at_caret(String::chr(0x2060));
}
} break;
case MENU_INSERT_SHY: {
if (editable) {
- append_at_cursor(String::chr(0x00AD));
+ insert_text_at_caret(String::chr(0x00AD));
}
}
}
@@ -1809,18 +1810,18 @@ PopupMenu *LineEdit::get_menu() const {
void LineEdit::_editor_settings_changed() {
#ifdef TOOLS_ENABLED
- cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
- cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
+ set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
#endif
}
-void LineEdit::set_expand_to_text_length(bool p_enabled) {
+void LineEdit::set_expand_to_text_length_enabled(bool p_enabled) {
expand_to_text_length = p_enabled;
minimum_size_changed();
- set_cursor_position(cursor_pos);
+ set_caret_column(caret_column);
}
-bool LineEdit::get_expand_to_text_length() const {
+bool LineEdit::is_expand_to_text_length_enabled() const {
return expand_to_text_length;
}
@@ -1905,7 +1906,7 @@ void LineEdit::_shape() {
t = secret_character.repeat(text.length() + ime_text.length());
} else {
if (ime_text.length() > 0) {
- t = text.substr(0, cursor_pos) + ime_text + text.substr(cursor_pos, text.length());
+ t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length());
} else {
t = text;
}
@@ -1970,7 +1971,7 @@ void LineEdit::_clear_undo_stack() {
void LineEdit::_create_undo_state() {
TextOperation op;
op.text = text;
- op.cursor_pos = cursor_pos;
+ op.caret_column = caret_column;
op.scroll_offset = scroll_offset;
undo_stack.push_back(op);
}
@@ -2115,23 +2116,23 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder);
ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha);
ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha);
- ClassDB::bind_method(D_METHOD("set_cursor_position", "position"), &LineEdit::set_cursor_position);
- ClassDB::bind_method(D_METHOD("get_cursor_position"), &LineEdit::get_cursor_position);
+ ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column);
+ ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column);
ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset);
- ClassDB::bind_method(D_METHOD("set_expand_to_text_length", "enabled"), &LineEdit::set_expand_to_text_length);
- ClassDB::bind_method(D_METHOD("get_expand_to_text_length"), &LineEdit::get_expand_to_text_length);
- ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enabled"), &LineEdit::cursor_set_blink_enabled);
- ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &LineEdit::cursor_get_blink_enabled);
- ClassDB::bind_method(D_METHOD("set_mid_grapheme_caret_enabled", "enabled"), &LineEdit::set_mid_grapheme_caret_enabled);
- ClassDB::bind_method(D_METHOD("get_mid_grapheme_caret_enabled"), &LineEdit::get_mid_grapheme_caret_enabled);
- ClassDB::bind_method(D_METHOD("cursor_set_force_displayed", "enabled"), &LineEdit::cursor_set_force_displayed);
- ClassDB::bind_method(D_METHOD("cursor_get_force_displayed"), &LineEdit::cursor_get_force_displayed);
- ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &LineEdit::cursor_set_blink_speed);
- ClassDB::bind_method(D_METHOD("cursor_get_blink_speed"), &LineEdit::cursor_get_blink_speed);
+ ClassDB::bind_method(D_METHOD("set_expand_to_text_length_enabled", "enabled"), &LineEdit::set_expand_to_text_length_enabled);
+ ClassDB::bind_method(D_METHOD("is_expand_to_text_length_enabled"), &LineEdit::is_expand_to_text_length_enabled);
+ ClassDB::bind_method(D_METHOD("set_caret_blink_enabled", "enabled"), &LineEdit::set_caret_blink_enabled);
+ ClassDB::bind_method(D_METHOD("is_caret_blink_enabled"), &LineEdit::is_caret_blink_enabled);
+ ClassDB::bind_method(D_METHOD("set_caret_mid_grapheme_enabled", "enabled"), &LineEdit::set_caret_mid_grapheme_enabled);
+ ClassDB::bind_method(D_METHOD("is_caret_mid_grapheme_enabled"), &LineEdit::is_caret_mid_grapheme_enabled);
+ ClassDB::bind_method(D_METHOD("set_caret_force_displayed", "enabled"), &LineEdit::set_caret_force_displayed);
+ ClassDB::bind_method(D_METHOD("is_caret_force_displayed"), &LineEdit::is_caret_force_displayed);
+ ClassDB::bind_method(D_METHOD("set_caret_blink_speed", "blink_speed"), &LineEdit::set_caret_blink_speed);
+ ClassDB::bind_method(D_METHOD("get_caret_blink_speed"), &LineEdit::get_caret_blink_speed);
ClassDB::bind_method(D_METHOD("set_max_length", "chars"), &LineEdit::set_max_length);
ClassDB::bind_method(D_METHOD("get_max_length"), &LineEdit::get_max_length);
- ClassDB::bind_method(D_METHOD("append_at_cursor", "text"), &LineEdit::append_at_cursor);
- ClassDB::bind_method(D_METHOD("delete_char_at_cursor"), &LineEdit::delete_char);
+ ClassDB::bind_method(D_METHOD("insert_text_at_caret", "text"), &LineEdit::insert_text_at_caret);
+ ClassDB::bind_method(D_METHOD("delete_char_at_caret"), &LineEdit::delete_char);
ClassDB::bind_method(D_METHOD("delete_text", "from_column", "to_column"), &LineEdit::delete_text);
ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &LineEdit::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable);
@@ -2199,7 +2200,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
@@ -2216,11 +2217,11 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
ADD_GROUP("Caret", "caret_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "cursor_set_force_displayed", "cursor_get_force_displayed");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column"), "set_caret_column", "get_caret_column");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled");
}
LineEdit::LineEdit() {
@@ -2236,7 +2237,7 @@ LineEdit::LineEdit() {
add_child(caret_blink_timer);
caret_blink_timer->set_wait_time(0.65);
caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret));
- cursor_set_blink_enabled(false);
+ set_caret_blink_enabled(false);
menu = memnew(PopupMenu);
add_child(menu);
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index ef36377f2e..f4f0ff0629 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -103,9 +103,9 @@ private:
PopupMenu *menu_dir = nullptr;
PopupMenu *menu_ctl = nullptr;
- bool mid_grapheme_caret_enabled = false;
+ bool caret_mid_grapheme_enabled = false;
- int cursor_pos = 0;
+ int caret_column = 0;
int scroll_offset = 0;
int max_length = 0; // 0 for no maximum.
@@ -131,7 +131,7 @@ private:
struct Selection {
int begin = 0;
int end = 0;
- int cursor_start = 0;
+ int start_column = 0;
bool enabled = false;
bool creating = false;
bool doubleclick = false;
@@ -140,7 +140,7 @@ private:
} selection;
struct TextOperation {
- int cursor_pos = 0;
+ int caret_column = 0;
int scroll_offset = 0;
int cached_width = 0;
String text;
@@ -175,12 +175,12 @@ private:
void shift_selection_check_pre(bool);
void shift_selection_check_post(bool);
- void selection_fill_at_cursor();
+ void selection_fill_at_caret();
void set_scroll_offset(int p_pos);
int get_scroll_offset() const;
- void set_cursor_at_pixel_pos(int p_x);
- Vector2i get_cursor_pixel_pos();
+ void set_caret_at_pixel_pos(int p_x);
+ Vector2i get_caret_pixel_pos();
void _reset_caret_blink_timer();
void _toggle_draw_caret();
@@ -191,10 +191,10 @@ private:
void _editor_settings_changed();
void _swap_current_input_direction();
- void _move_cursor_left(bool p_select, bool p_move_by_word = false);
- void _move_cursor_right(bool p_select, bool p_move_by_word = false);
- void _move_cursor_start(bool p_select);
- void _move_cursor_end(bool p_select);
+ void _move_caret_left(bool p_select, bool p_move_by_word = false);
+ void _move_caret_right(bool p_select, bool p_move_by_word = false);
+ void _move_caret_start(bool p_select);
+ void _move_caret_end(bool p_select);
void _backspace(bool p_word = false, bool p_all_to_left = false);
void _delete(bool p_word = false, bool p_all_to_right = false);
@@ -259,26 +259,26 @@ public:
void set_placeholder_alpha(float p_alpha);
float get_placeholder_alpha() const;
- void set_cursor_position(int p_pos);
- int get_cursor_position() const;
+ void set_caret_column(int p_column);
+ int get_caret_column() const;
void set_max_length(int p_max_length);
int get_max_length() const;
- void append_at_cursor(String p_text);
+ void insert_text_at_caret(String p_text);
void clear();
- void set_mid_grapheme_caret_enabled(const bool p_enabled);
- bool get_mid_grapheme_caret_enabled() const;
+ void set_caret_mid_grapheme_enabled(const bool p_enabled);
+ bool is_caret_mid_grapheme_enabled() const;
- bool cursor_get_blink_enabled() const;
- void cursor_set_blink_enabled(const bool p_enabled);
+ bool is_caret_blink_enabled() const;
+ void set_caret_blink_enabled(const bool p_enabled);
- float cursor_get_blink_speed() const;
- void cursor_set_blink_speed(const float p_speed);
+ float get_caret_blink_speed() const;
+ void set_caret_blink_speed(const float p_speed);
- bool cursor_get_force_displayed() const;
- void cursor_set_force_displayed(const bool p_enabled);
+ void set_caret_force_displayed(const bool p_enabled);
+ bool is_caret_force_displayed() const;
void copy_text();
void cut_text();
@@ -297,8 +297,8 @@ public:
virtual Size2 get_minimum_size() const override;
- void set_expand_to_text_length(bool p_enabled);
- bool get_expand_to_text_length() const;
+ void set_expand_to_text_length_enabled(bool p_enabled);
+ bool is_expand_to_text_length_enabled() const;
void set_clear_button_enabled(bool p_enabled);
bool is_clear_button_enabled() const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index f54ab004c6..4f508423b3 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1973,7 +1973,7 @@ void TextEdit::backspace_at_cursor() {
}
}
- cursor_set_line(prev_line, true, true);
+ cursor_set_line(prev_line, false, true);
cursor_set_column(prev_column);
}
@@ -2207,7 +2207,7 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) {
if (!p_split_current_line) {
if (p_above) {
if (cursor.line > 0) {
- cursor_set_line(cursor.line - 1);
+ cursor_set_line(cursor.line - 1, false);
cursor_set_column(text[cursor.line].length());
} else {
cursor_set_column(0);
@@ -2223,7 +2223,7 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) {
if (first_line) {
cursor_set_line(0);
} else if (brace_indent) {
- cursor_set_line(cursor.line - 1);
+ cursor_set_line(cursor.line - 1, false);
cursor_set_column(text[cursor.line].length());
}
end_complex_operation();
@@ -2573,7 +2573,7 @@ void TextEdit::_backspace(bool p_word, bool p_all_to_left) {
_remove_text(line, column, cursor.line, cursor.column);
- cursor_set_line(line);
+ cursor_set_line(line, false);
cursor_set_column(column);
} else {
// One character.
@@ -2640,7 +2640,7 @@ void TextEdit::_delete_selection() {
selection.active = false;
update();
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
- cursor_set_line(selection.from_line, true, false);
+ cursor_set_line(selection.from_line, false, false);
cursor_set_column(selection.from_column);
update();
}
@@ -3261,7 +3261,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
accept_event();
return;
}
- if (k->is_action("ui_accept", true) || k->is_action("ui_text_completion_accept", true)) {
+ if (k->is_action("ui_text_completion_accept", true)) {
_confirm_completion();
accept_event();
return;
@@ -3851,7 +3851,7 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) {
int new_column, new_line;
_insert_text(cursor.line, cursor.column, p_text, &new_line, &new_column);
_update_scrollbars();
- cursor_set_line(new_line);
+ cursor_set_line(new_line, false);
cursor_set_column(new_column);
update();
@@ -4425,7 +4425,7 @@ int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const {
void TextEdit::insert_text_at_cursor(const String &p_text) {
if (selection.active) {
- cursor_set_line(selection.from_line);
+ cursor_set_line(selection.from_line, false);
cursor_set_column(selection.from_column);
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
@@ -5042,7 +5042,7 @@ void TextEdit::cut() {
DisplayServer::get_singleton()->clipboard_set(clipboard);
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
- cursor_set_line(selection.from_line); // Set afterwards else it causes the view to be offset.
+ cursor_set_line(selection.from_line, false); // Set afterwards else it causes the view to be offset.
cursor_set_column(selection.from_column);
selection.active = false;
@@ -5078,7 +5078,7 @@ void TextEdit::paste() {
selection.active = false;
selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
- cursor_set_line(selection.from_line);
+ cursor_set_line(selection.from_line, false);
cursor_set_column(selection.from_column);
} else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) {
@@ -5817,11 +5817,11 @@ void TextEdit::undo() {
_update_scrollbars();
if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) {
- cursor_set_line(undo_stack_pos->get().to_line);
+ cursor_set_line(undo_stack_pos->get().to_line, false);
cursor_set_column(undo_stack_pos->get().to_column);
_cancel_code_hint();
} else {
- cursor_set_line(undo_stack_pos->get().from_line);
+ cursor_set_line(undo_stack_pos->get().from_line, false);
cursor_set_column(undo_stack_pos->get().from_column);
}
update();
@@ -5856,7 +5856,7 @@ void TextEdit::redo() {
}
_update_scrollbars();
- cursor_set_line(undo_stack_pos->get().to_line);
+ cursor_set_line(undo_stack_pos->get().to_line, false);
cursor_set_column(undo_stack_pos->get().to_column);
undo_stack_pos = undo_stack_pos->next();
update();
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index f05b43377f..b9f4a7a741 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -438,7 +438,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_selected_color", "LineEdit", Color(0, 0, 0));
theme->set_color("font_uneditable_color", "LineEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
theme->set_color("font_outline_color", "LineEdit", Color(1, 1, 1));
- theme->set_color("cursor_color", "LineEdit", control_font_hover_color);
+ theme->set_color("caret_color", "LineEdit", control_font_hover_color);
theme->set_color("selection_color", "LineEdit", control_selection_color);
theme->set_color("clear_button_color", "LineEdit", control_font_color);
theme->set_color("clear_button_color_pressed", "LineEdit", control_font_pressed_color);
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 195ce070a7..bb47eebe9b 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -289,7 +289,7 @@ void ParticlesMaterial::_update_shader() {
code += "}\n";
code += "\n";
- code += "void compute() {\n";
+ code += "void start() {\n";
code += " uint base_number = NUMBER;\n";
code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n";
code += " float angle_rand = rand_from_seed(alt_seed);\n";
@@ -305,97 +305,94 @@ void ParticlesMaterial::_update_shader() {
code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
}
- code += " float tv = 0.0;\n";
- code += " if (RESTART) {\n";
-
if (tex_parameters[PARAM_ANGLE].is_valid()) {
- code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n";
+ code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n";
} else {
- code += " float tex_angle = 0.0;\n";
+ code += " float tex_angle = 0.0;\n";
}
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) {
- code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n";
+ code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_offset = 0.0;\n";
+ code += " float tex_anim_offset = 0.0;\n";
}
- code += " float spread_rad = spread * degree_to_rad;\n";
+ code += " float spread_rad = spread * degree_to_rad;\n";
- code += " if (RESTART_VELOCITY) {\n";
+ code += " if (RESTART_VELOCITY) {\n";
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
+ code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_velocity = 0.0;\n";
+ code += " float tex_linear_velocity = 0.0;\n";
}
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
- code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
- code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
- code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
+ code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
+ code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
+ code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
} else {
//initiate velocity spread in 3D
- code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
- code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
- code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
- code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
- code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
- code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
- code += " vec3 direction_nrm = normalize(direction);\n";
- code += " // rotate spread to direction\n";
- code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n";
- code += " if (length(binormal) < 0.0001) {\n";
- code += " // direction is parallel to Y. Choose Z as the binormal.\n";
- code += " binormal = vec3(0.0, 0.0, 1.0);\n";
- code += " }\n";
- code += " binormal = normalize(binormal);\n";
- code += " vec3 normal = cross(binormal, direction_nrm);\n";
- code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n";
- code += " VELOCITY = spread_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
+ code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
+ code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
+ code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
+ code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
+ code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
+ code += " vec3 direction_nrm = normalize(direction);\n";
+ code += " // rotate spread to direction\n";
+ code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n";
+ code += " if (length(binormal) < 0.0001) {\n";
+ code += " // direction is parallel to Y. Choose Z as the binormal.\n";
+ code += " binormal = vec3(0.0, 0.0, 1.0);\n";
+ code += " }\n";
+ code += " binormal = normalize(binormal);\n";
+ code += " vec3 normal = cross(binormal, direction_nrm);\n";
+ code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n";
+ code += " VELOCITY = spread_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
}
- code += " }\n";
+ code += " }\n";
- code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
- code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
- code += " CUSTOM.y = 0.0;\n"; // phase
- code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
- code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1)
+ code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
+ code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
+ code += " CUSTOM.y = 0.0;\n"; // phase
+ code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
+ code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1)
- code += " if (RESTART_POSITION) {\n";
+ code += " if (RESTART_POSITION) {\n";
switch (emission_shape) {
case EMISSION_SHAPE_POINT: {
//do none, identity (will later be multiplied by emission transform)
- code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n";
+ code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n";
} break;
case EMISSION_SHAPE_SPHERE: {
- code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
- code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
- code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
- code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
+ code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
+ code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
+ code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
+ code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
} break;
case EMISSION_SHAPE_BOX: {
- code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
+ code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
- code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
+ code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " mat2 rotm;";
- code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
- code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
- code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n";
+ code += " mat2 rotm;";
+ code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
+ code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
+ code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n";
} else {
- code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
- code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
- code += " vec3 tangent = normalize(cross(v0, normal));\n";
- code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
- code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
+ code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
+ code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
+ code += " vec3 tangent = normalize(cross(v0, normal));\n";
+ code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
+ code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
}
}
} break;
@@ -404,134 +401,144 @@ void ParticlesMaterial::_update_shader() {
}
}
- code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
- code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
+ code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
+ code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " VELOCITY.z = 0.0;\n";
- code += " TRANSFORM[3].z = 0.0;\n";
+ code += " VELOCITY.z = 0.0;\n";
+ code += " TRANSFORM[3].z = 0.0;\n";
}
- code += " }\n";
+ code += " }\n";
+ code += "}\n\n";
- code += " } else {\n";
+ code += "void process() {\n";
+ code += " uint base_number = NUMBER;\n";
+ code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n";
+ code += " float angle_rand = rand_from_seed(alt_seed);\n";
+ code += " float scale_rand = rand_from_seed(alt_seed);\n";
+ code += " float hue_rot_rand = rand_from_seed(alt_seed);\n";
+ code += " float anim_offset_rand = rand_from_seed(alt_seed);\n";
+ code += " float pi = 3.14159;\n";
+ code += " float degree_to_rad = pi / 180.0;\n";
+ code += "\n";
- code += " CUSTOM.y += DELTA / LIFETIME;\n";
- code += " tv = CUSTOM.y / CUSTOM.w;\n";
+ code += " CUSTOM.y += DELTA / LIFETIME;\n";
+ code += " float tv = CUSTOM.y / CUSTOM.w;\n";
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_velocity = 0.0;\n";
+ code += " float tex_linear_velocity = 0.0;\n";
}
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
- code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_orbit_velocity = 0.0;\n";
+ code += " float tex_orbit_velocity = 0.0;\n";
}
}
if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
- code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_angular_velocity = 0.0;\n";
+ code += " float tex_angular_velocity = 0.0;\n";
}
if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
- code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_accel = 0.0;\n";
+ code += " float tex_linear_accel = 0.0;\n";
}
if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
- code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_radial_accel = 0.0;\n";
+ code += " float tex_radial_accel = 0.0;\n";
}
if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
- code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_tangent_accel = 0.0;\n";
+ code += " float tex_tangent_accel = 0.0;\n";
}
if (tex_parameters[PARAM_DAMPING].is_valid()) {
- code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_damping = 0.0;\n";
+ code += " float tex_damping = 0.0;\n";
}
if (tex_parameters[PARAM_ANGLE].is_valid()) {
- code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_angle = 0.0;\n";
+ code += " float tex_angle = 0.0;\n";
}
if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) {
- code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_speed = 0.0;\n";
+ code += " float tex_anim_speed = 0.0;\n";
}
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) {
- code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_offset = 0.0;\n";
+ code += " float tex_anim_offset = 0.0;\n";
}
- code += " vec3 force = gravity;\n";
- code += " vec3 pos = TRANSFORM[3].xyz;\n";
+ code += " vec3 force = gravity;\n";
+ code += " vec3 pos = TRANSFORM[3].xyz;\n";
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " pos.z = 0.0;\n";
- }
- code += " // apply linear acceleration\n";
- code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n";
- code += " // apply radial acceleration\n";
- code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n";
- code += " vec3 diff = pos - org;\n";
- code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n";
- code += " // apply tangential acceleration;\n";
+ code += " pos.z = 0.0;\n";
+ }
+ code += " // apply linear acceleration\n";
+ code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n";
+ code += " // apply radial acceleration\n";
+ code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n";
+ code += " vec3 diff = pos - org;\n";
+ code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n";
+ code += " // apply tangential acceleration;\n";
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
+ code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
} else {
- code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n";
- code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
+ code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n";
+ code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
}
if (attractor_interaction_enabled) {
- code += " force += ATTRACTOR_FORCE;\n\n";
+ code += " force += ATTRACTOR_FORCE;\n\n";
}
- code += " // apply attractor forces\n";
- code += " VELOCITY += force * DELTA;\n";
- code += " // orbit velocity\n";
+ code += " // apply attractor forces\n";
+ code += " VELOCITY += force * DELTA;\n";
+ code += " // orbit velocity\n";
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n";
- code += " if (orbit_amount != 0.0) {\n";
- code += " float ang = orbit_amount * DELTA * pi * 2.0;\n";
- code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n";
- code += " TRANSFORM[3].xy -= diff.xy;\n";
- code += " TRANSFORM[3].xy += rot * diff.xy;\n";
- code += " }\n";
+ code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n";
+ code += " if (orbit_amount != 0.0) {\n";
+ code += " float ang = orbit_amount * DELTA * pi * 2.0;\n";
+ code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n";
+ code += " TRANSFORM[3].xy -= diff.xy;\n";
+ code += " TRANSFORM[3].xy += rot * diff.xy;\n";
+ code += " }\n";
}
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n";
- }
- code += " if (damping + tex_damping > 0.0) {\n";
- code += " float v = length(VELOCITY);\n";
- code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n";
- code += " v -= damp * DELTA;\n";
- code += " if (v < 0.0) {\n";
- code += " VELOCITY = vec3(0.0);\n";
- code += " } else {\n";
- code += " VELOCITY = normalize(VELOCITY) * v;\n";
- code += " }\n";
+ code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n";
+ }
+ code += " if (damping + tex_damping > 0.0) {\n";
+ code += " float v = length(VELOCITY);\n";
+ code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n";
+ code += " v -= damp * DELTA;\n";
+ code += " if (v < 0.0) {\n";
+ code += " VELOCITY = vec3(0.0);\n";
+ code += " } else {\n";
+ code += " VELOCITY = normalize(VELOCITY) * v;\n";
code += " }\n";
- code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
- code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n";
- code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
- code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle
code += " }\n";
+ code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
+ code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n";
+ code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
+ code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle
+
// apply color
// apply hue rotation
if (tex_parameters[PARAM_SCALE].is_valid()) {
@@ -659,7 +666,7 @@ void ParticlesMaterial::_update_shader() {
code += " }";
}
- code += " if (CUSTOM.y > CUSTOM.w) {";
+ code += " if (CUSTOM.y > CUSTOM.w) {\n";
code += " ACTIVE = false;\n";
code += " }\n";
code += "}\n";
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index b2efecb1cb..f50ee9c4c8 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -194,7 +194,7 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() {
code += "uniform float sun_angle_max = 1.74;\n";
code += "uniform float sun_curve : hint_range(0, 1) = 0.05;\n\n";
code += "const float PI = 3.1415926535897932384626433833;\n\n";
- code += "void fragment() {\n";
+ code += "void sky() {\n";
code += "\tfloat v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0));\n";
code += "\tfloat c = (1.0 - v_angle / (PI * 0.5));\n";
code += "\tvec3 sky = mix(sky_horizon_color.rgb, sky_top_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / sky_curve), 0.0, 1.0));\n";
@@ -301,7 +301,7 @@ PanoramaSkyMaterial::PanoramaSkyMaterial() {
String code = "shader_type sky;\n\n";
code += "uniform sampler2D source_panorama : filter_linear;\n";
- code += "void fragment() {\n";
+ code += "void sky() {\n";
code += "\tCOLOR = texture(source_panorama, SKY_COORDS).rgb;\n";
code += "}";
@@ -521,7 +521,7 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() {
code += "\treturn fract(p.x * p.y * p.z * (p.x + p.y + p.z));\n";
code += "}\n\n";
- code += "void fragment() {\n";
+ code += "void sky() {\n";
code += "\tif (LIGHT0_ENABLED) {\n";
code += "\t\tfloat zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 );\n";
code += "\t\tfloat sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY;\n";
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index e1e24ddab2..d09cf4b971 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -961,7 +961,8 @@ static const char *type_string[VisualShader::TYPE_MAX] = {
"light",
"emit",
"process",
- "end"
+ "end",
+ "sky",
};
bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
@@ -1476,7 +1477,7 @@ void VisualShader::_update_shader() const {
global_code += "render_mode " + render_mode + ";\n\n";
}
- static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end" };
+ static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end", "sky" };
String global_expressions;
Set<String> used_uniform_names;
@@ -1667,6 +1668,7 @@ void VisualShader::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_EMIT);
BIND_ENUM_CONSTANT(TYPE_PROCESS);
BIND_ENUM_CONSTANT(TYPE_END);
+ BIND_ENUM_CONSTANT(TYPE_SKY);
BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_CONSTANT(NODE_ID_INVALID);
@@ -1698,7 +1700,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview", "MODELVIEW_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
@@ -1721,10 +1722,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
@@ -1750,7 +1749,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
@@ -1759,6 +1757,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
+
// Canvas Item, Vertex
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
@@ -1766,12 +1765,12 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
-
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas", "CANVAS_MATRIX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" },
+
// Canvas Item, Fragment
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
@@ -1789,6 +1788,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" },
+
// Canvas Item, Light
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
@@ -1856,36 +1856,36 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- // Sky, Fragment
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Sky, Sky
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -2449,9 +2449,9 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- // Sky, Fragment
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
+ // Sky, Sky
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 54a5c19049..e2e1b473ed 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -54,6 +54,7 @@ public:
TYPE_EMIT,
TYPE_PROCESS,
TYPE_END,
+ TYPE_SKY,
TYPE_MAX
};
diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp
index 0e9f7a6cf2..f067771d58 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -348,7 +348,7 @@ World3D::World3D() {
navigation_map = NavigationServer3D::get_singleton()->map_create();
NavigationServer3D::get_singleton()->map_set_active(navigation_map, true);
NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.3));
- NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 5.0)); // Five meters, depends a lot on the agent's radius
+ NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.3));
#ifdef _3D_DISABLED
indexer = nullptr;
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 892802c103..7575ccd5c3 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -190,10 +190,6 @@ SceneStringNames::SceneStringNames() {
_default = StaticCString::create("default");
- for (int i = 0; i < MAX_MATERIALS; i++) {
- mesh_materials[i] = "material/" + itos(i);
- }
-
_window_group = StaticCString::create("_window_group");
_window_input = StaticCString::create("_window_input");
window_input = StaticCString::create("window_input");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 655e49c6f9..a5b489eddc 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -216,10 +216,6 @@ public:
StringName use_in_baked_light;
StringName use_dynamic_gi;
#endif
- enum {
- MAX_MATERIALS = 32
- };
- StringName mesh_materials[MAX_MATERIALS];
};
#endif // SCENE_STRING_NAMES_H
diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp
index d0636047b7..0a91931354 100644
--- a/servers/physics_2d/body_2d_sw.cpp
+++ b/servers/physics_2d/body_2d_sw.cpp
@@ -658,8 +658,6 @@ Body2DSW::Body2DSW() :
omit_force_integration = false;
applied_torque = 0;
island_step = 0;
- island_next = nullptr;
- island_list_next = nullptr;
_set_static(false);
first_time_kinematic = false;
linear_damp = -1;
diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h
index 60d55ab8bd..7ea4ac697c 100644
--- a/servers/physics_2d/body_2d_sw.h
+++ b/servers/physics_2d/body_2d_sw.h
@@ -125,8 +125,6 @@ class Body2DSW : public CollisionObject2DSW {
ForceIntegrationCallback *fi_callback;
uint64_t island_step;
- Body2DSW *island_next;
- Body2DSW *island_list_next;
_FORCE_INLINE_ void _compute_area_gravity_and_dampenings(const Area2DSW *p_area);
@@ -175,12 +173,6 @@ public:
_FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
_FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step = p_step; }
- _FORCE_INLINE_ Body2DSW *get_island_next() const { return island_next; }
- _FORCE_INLINE_ void set_island_next(Body2DSW *p_next) { island_next = p_next; }
-
- _FORCE_INLINE_ Body2DSW *get_island_list_next() const { return island_list_next; }
- _FORCE_INLINE_ void set_island_list_next(Body2DSW *p_next) { island_list_next = p_next; }
-
_FORCE_INLINE_ void add_constraint(Constraint2DSW *p_constraint, int p_pos) { constraint_list.push_back({ p_constraint, p_pos }); }
_FORCE_INLINE_ void remove_constraint(Constraint2DSW *p_constraint, int p_pos) { constraint_list.erase({ p_constraint, p_pos }); }
const List<Pair<Constraint2DSW *, int>> &get_constraint_list() const { return constraint_list; }
diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.cpp b/servers/physics_2d/broad_phase_2d_hash_grid.cpp
index 6cfe6908d1..35447c5389 100644
--- a/servers/physics_2d/broad_phase_2d_hash_grid.cpp
+++ b/servers/physics_2d/broad_phase_2d_hash_grid.cpp
@@ -35,6 +35,12 @@
#define LARGE_ELEMENT_FI 1.01239812
void BroadPhase2DHashGrid::_pair_attempt(Element *p_elem, Element *p_with) {
+ if (p_elem->owner == p_with->owner) {
+ return;
+ }
+ if (!_test_collision_mask(p_elem->collision_mask, p_elem->collision_layer, p_with->collision_mask, p_with->collision_layer)) {
+ return;
+ }
Map<Element *, PairData *>::Element *E = p_elem->paired.find(p_with);
ERR_FAIL_COND(p_elem->_static && p_with->_static);
@@ -49,6 +55,12 @@ void BroadPhase2DHashGrid::_pair_attempt(Element *p_elem, Element *p_with) {
}
void BroadPhase2DHashGrid::_unpair_attempt(Element *p_elem, Element *p_with) {
+ if (p_elem->owner == p_with->owner) {
+ return;
+ }
+ if (!_test_collision_mask(p_elem->collision_mask, p_elem->collision_layer, p_with->collision_mask, p_with->collision_layer)) {
+ return;
+ }
Map<Element *, PairData *>::Element *E = p_elem->paired.find(p_with);
ERR_FAIL_COND(!E); //this should really be paired..
@@ -74,24 +86,22 @@ void BroadPhase2DHashGrid::_check_motion(Element *p_elem) {
bool physical_collision = p_elem->aabb.intersects(E->key()->aabb);
bool logical_collision = p_elem->owner->test_collision_mask(E->key()->owner);
- if (physical_collision) {
- if (!E->get()->colliding || (logical_collision && !E->get()->ud && pair_callback)) {
+ if (physical_collision && logical_collision) {
+ if (!E->get()->colliding && pair_callback) {
E->get()->ud = pair_callback(p_elem->owner, p_elem->subindex, E->key()->owner, E->key()->subindex, pair_userdata);
- } else if (E->get()->colliding && !logical_collision && E->get()->ud && unpair_callback) {
- unpair_callback(p_elem->owner, p_elem->subindex, E->key()->owner, E->key()->subindex, E->get()->ud, unpair_userdata);
- E->get()->ud = nullptr;
}
E->get()->colliding = true;
- } else { // No physcial_collision
+ } else { // No collision
if (E->get()->colliding && unpair_callback) {
unpair_callback(p_elem->owner, p_elem->subindex, E->key()->owner, E->key()->subindex, E->get()->ud, unpair_userdata);
+ E->get()->ud = nullptr;
}
E->get()->colliding = false;
}
}
}
-void BroadPhase2DHashGrid::_enter_grid(Element *p_elem, const Rect2 &p_rect, bool p_static) {
+void BroadPhase2DHashGrid::_enter_grid(Element *p_elem, const Rect2 &p_rect, bool p_static, bool p_force_enter) {
Vector2 sz = (p_rect.size / cell_size * LARGE_ELEMENT_FI); //use magic number to avoid floating point issues
if (sz.width * sz.height > large_object_min_surface) {
//large object, do not use grid, must check against all elements
@@ -99,9 +109,6 @@ void BroadPhase2DHashGrid::_enter_grid(Element *p_elem, const Rect2 &p_rect, boo
if (E->key() == p_elem->self) {
continue; // do not pair against itself
}
- if (E->get().owner == p_elem->owner) {
- continue;
- }
if (E->get()._static && p_static) {
continue;
}
@@ -133,7 +140,7 @@ void BroadPhase2DHashGrid::_enter_grid(Element *p_elem, const Rect2 &p_rect, boo
pb = pb->next;
}
- bool entered = false;
+ bool entered = p_force_enter;
if (!pb) {
//does not exist, create!
@@ -155,17 +162,11 @@ void BroadPhase2DHashGrid::_enter_grid(Element *p_elem, const Rect2 &p_rect, boo
if (entered) {
for (Map<Element *, RC>::Element *E = pb->object_set.front(); E; E = E->next()) {
- if (E->key()->owner == p_elem->owner) {
- continue;
- }
_pair_attempt(p_elem, E->key());
}
if (!p_static) {
for (Map<Element *, RC>::Element *E = pb->static_object_set.front(); E; E = E->next()) {
- if (E->key()->owner == p_elem->owner) {
- continue;
- }
_pair_attempt(p_elem, E->key());
}
}
@@ -179,18 +180,14 @@ void BroadPhase2DHashGrid::_enter_grid(Element *p_elem, const Rect2 &p_rect, boo
if (E->key() == p_elem) {
continue; // do not pair against itself
}
- if (E->key()->owner == p_elem->owner) {
- continue;
- }
if (E->key()->_static && p_static) {
continue;
}
-
_pair_attempt(E->key(), p_elem);
}
}
-void BroadPhase2DHashGrid::_exit_grid(Element *p_elem, const Rect2 &p_rect, bool p_static) {
+void BroadPhase2DHashGrid::_exit_grid(Element *p_elem, const Rect2 &p_rect, bool p_static, bool p_force_exit) {
Vector2 sz = (p_rect.size / cell_size * LARGE_ELEMENT_FI);
if (sz.width * sz.height > large_object_min_surface) {
//unpair all elements, instead of checking all, just check what is already paired, so we at least save from checking static vs static
@@ -229,7 +226,7 @@ void BroadPhase2DHashGrid::_exit_grid(Element *p_elem, const Rect2 &p_rect, bool
ERR_CONTINUE(!pb); //should exist!!
- bool exited = false;
+ bool exited = p_force_exit;
if (p_static) {
if (pb->static_object_set[p_elem].dec() == 0) {
@@ -245,17 +242,11 @@ void BroadPhase2DHashGrid::_exit_grid(Element *p_elem, const Rect2 &p_rect, bool
if (exited) {
for (Map<Element *, RC>::Element *E = pb->object_set.front(); E; E = E->next()) {
- if (E->key()->owner == p_elem->owner) {
- continue;
- }
_unpair_attempt(p_elem, E->key());
}
if (!p_static) {
for (Map<Element *, RC>::Element *E = pb->static_object_set.front(); E; E = E->next()) {
- if (E->key()->owner == p_elem->owner) {
- continue;
- }
_unpair_attempt(p_elem, E->key());
}
}
@@ -288,9 +279,6 @@ void BroadPhase2DHashGrid::_exit_grid(Element *p_elem, const Rect2 &p_rect, bool
if (E->key() == p_elem) {
continue; // do not pair against itself
}
- if (E->key()->owner == p_elem->owner) {
- continue;
- }
if (E->key()->_static && p_static) {
continue;
}
@@ -306,6 +294,8 @@ BroadPhase2DHashGrid::ID BroadPhase2DHashGrid::create(CollisionObject2DSW *p_obj
Element e;
e.owner = p_object;
e._static = false;
+ e.collision_mask = p_object->get_collision_mask();
+ e.collision_layer = p_object->get_collision_layer();
e.subindex = p_subindex;
e.self = current;
e.pass = 0;
@@ -319,13 +309,26 @@ void BroadPhase2DHashGrid::move(ID p_id, const Rect2 &p_aabb) {
ERR_FAIL_COND(!E);
Element &e = E->get();
+ bool layer_changed = e.collision_mask != e.owner->get_collision_mask() || e.collision_layer != e.owner->get_collision_layer();
- if (p_aabb != e.aabb) {
+ if (p_aabb != e.aabb || layer_changed) {
+ uint32_t old_mask = e.collision_mask;
+ uint32_t old_layer = e.collision_layer;
if (p_aabb != Rect2()) {
- _enter_grid(&e, p_aabb, e._static);
+ e.collision_mask = e.owner->get_collision_mask();
+ e.collision_layer = e.owner->get_collision_layer();
+
+ _enter_grid(&e, p_aabb, e._static, layer_changed);
}
if (e.aabb != Rect2()) {
- _exit_grid(&e, e.aabb, e._static);
+ // Need _exit_grid to remove from cells based on the old layer values.
+ e.collision_mask = old_mask;
+ e.collision_layer = old_layer;
+
+ _exit_grid(&e, e.aabb, e._static, layer_changed);
+
+ e.collision_mask = e.owner->get_collision_mask();
+ e.collision_layer = e.owner->get_collision_layer();
}
e.aabb = p_aabb;
}
@@ -344,13 +347,13 @@ void BroadPhase2DHashGrid::set_static(ID p_id, bool p_static) {
}
if (e.aabb != Rect2()) {
- _exit_grid(&e, e.aabb, e._static);
+ _exit_grid(&e, e.aabb, e._static, false);
}
e._static = p_static;
if (e.aabb != Rect2()) {
- _enter_grid(&e, e.aabb, e._static);
+ _enter_grid(&e, e.aabb, e._static, false);
_check_motion(&e);
}
}
@@ -362,7 +365,7 @@ void BroadPhase2DHashGrid::remove(ID p_id) {
Element &e = E->get();
if (e.aabb != Rect2()) {
- _exit_grid(&e, e.aabb, e._static);
+ _exit_grid(&e, e.aabb, e._static, false);
}
element_map.erase(p_id);
diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.h b/servers/physics_2d/broad_phase_2d_hash_grid.h
index eb7c8879ac..bb7c03b989 100644
--- a/servers/physics_2d/broad_phase_2d_hash_grid.h
+++ b/servers/physics_2d/broad_phase_2d_hash_grid.h
@@ -51,6 +51,9 @@ class BroadPhase2DHashGrid : public BroadPhase2DSW {
CollisionObject2DSW *owner;
bool _static;
Rect2 aabb;
+ // Owner's collision_mask/layer, used to detect changes in layers.
+ uint32_t collision_mask;
+ uint32_t collision_layer;
int subindex;
uint64_t pass;
Map<Element *, PairData *> paired;
@@ -115,8 +118,12 @@ class BroadPhase2DHashGrid : public BroadPhase2DSW {
UnpairCallback unpair_callback;
void *unpair_userdata;
- void _enter_grid(Element *p_elem, const Rect2 &p_rect, bool p_static);
- void _exit_grid(Element *p_elem, const Rect2 &p_rect, bool p_static);
+ static _FORCE_INLINE_ bool _test_collision_mask(uint32_t p_mask1, uint32_t p_layer1, uint32_t p_mask2, uint32_t p_layer2) {
+ return p_mask1 & p_layer2 || p_mask2 & p_layer1;
+ }
+
+ void _enter_grid(Element *p_elem, const Rect2 &p_rect, bool p_static, bool p_force_enter);
+ void _exit_grid(Element *p_elem, const Rect2 &p_rect, bool p_static, bool p_force_exit);
template <bool use_aabb, bool use_segment>
_FORCE_INLINE_ void _cull(const Point2i p_cell, const Rect2 &p_aabb, const Point2 &p_from, const Point2 &p_to, CollisionObject2DSW **p_results, int p_max_results, int *p_result_indices, int &index);
diff --git a/servers/physics_2d/constraint_2d_sw.h b/servers/physics_2d/constraint_2d_sw.h
index 49ae4dd848..b724deb48e 100644
--- a/servers/physics_2d/constraint_2d_sw.h
+++ b/servers/physics_2d/constraint_2d_sw.h
@@ -37,8 +37,6 @@ class Constraint2DSW {
Body2DSW **_body_ptr;
int _body_count;
uint64_t island_step;
- Constraint2DSW *island_next;
- Constraint2DSW *island_list_next;
bool disabled_collisions_between_bodies;
RID self;
@@ -58,12 +56,6 @@ public:
_FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
_FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step = p_step; }
- _FORCE_INLINE_ Constraint2DSW *get_island_next() const { return island_next; }
- _FORCE_INLINE_ void set_island_next(Constraint2DSW *p_next) { island_next = p_next; }
-
- _FORCE_INLINE_ Constraint2DSW *get_island_list_next() const { return island_list_next; }
- _FORCE_INLINE_ void set_island_list_next(Constraint2DSW *p_next) { island_list_next = p_next; }
-
_FORCE_INLINE_ Body2DSW **get_body_ptr() const { return _body_ptr; }
_FORCE_INLINE_ int get_body_count() const { return _body_count; }
diff --git a/servers/physics_2d/joints_2d_sw.cpp b/servers/physics_2d/joints_2d_sw.cpp
index c7b556deba..20d4b9aa1a 100644
--- a/servers/physics_2d/joints_2d_sw.cpp
+++ b/servers/physics_2d/joints_2d_sw.cpp
@@ -97,8 +97,13 @@ normal_relative_velocity(Body2DSW *a, Body2DSW *b, Vector2 rA, Vector2 rB, Vecto
}
bool PinJoint2DSW::setup(real_t p_step) {
+ if ((A->get_mode() <= PhysicsServer2D::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer2D::BODY_MODE_KINEMATIC)) {
+ return false;
+ }
+
Space2DSW *space = A->get_space();
ERR_FAIL_COND_V(!space, false);
+
rA = A->get_transform().basis_xform(anchor_A);
rB = B ? B->get_transform().basis_xform(anchor_B) : anchor_B;
@@ -257,6 +262,10 @@ mult_k(const Vector2 &vr, const Vector2 &k1, const Vector2 &k2) {
}
bool GrooveJoint2DSW::setup(real_t p_step) {
+ if ((A->get_mode() <= PhysicsServer2D::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer2D::BODY_MODE_KINEMATIC)) {
+ return false;
+ }
+
// calculate endpoints in worldspace
Vector2 ta = A->get_transform().xform(A_groove_1);
Vector2 tb = A->get_transform().xform(A_groove_2);
@@ -342,6 +351,10 @@ GrooveJoint2DSW::GrooveJoint2DSW(const Vector2 &p_a_groove1, const Vector2 &p_a_
//////////////////////////////////////////////
bool DampedSpringJoint2DSW::setup(real_t p_step) {
+ if ((A->get_mode() <= PhysicsServer2D::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer2D::BODY_MODE_KINEMATIC)) {
+ return false;
+ }
+
rA = A->get_transform().basis_xform(anchor_A);
rB = B->get_transform().basis_xform(anchor_B);
diff --git a/servers/physics_2d/step_2d_sw.cpp b/servers/physics_2d/step_2d_sw.cpp
index 6613d19729..406d750776 100644
--- a/servers/physics_2d/step_2d_sw.cpp
+++ b/servers/physics_2d/step_2d_sw.cpp
@@ -31,19 +31,23 @@
#include "step_2d_sw.h"
#include "core/os/os.h"
-void Step2DSW::_populate_island(Body2DSW *p_body, Body2DSW **p_island, Constraint2DSW **p_constraint_island) {
+#define BODY_ISLAND_COUNT_RESERVE 128
+#define BODY_ISLAND_SIZE_RESERVE 512
+#define ISLAND_COUNT_RESERVE 128
+#define ISLAND_SIZE_RESERVE 512
+
+void Step2DSW::_populate_island(Body2DSW *p_body, LocalVector<Body2DSW *> &p_body_island, LocalVector<Constraint2DSW *> &p_constraint_island) {
p_body->set_island_step(_step);
- p_body->set_island_next(*p_island);
- *p_island = p_body;
+ p_body_island.push_back(p_body);
- for (const List<Pair<Constraint2DSW *, int>>::Element *E = p_body->get_constraint_list().front(); E; E = E->next()) {
+ // Faster with reversed iterations.
+ for (const List<Pair<Constraint2DSW *, int>>::Element *E = p_body->get_constraint_list().back(); E; E = E->prev()) {
Constraint2DSW *c = (Constraint2DSW *)E->get().first;
if (c->get_island_step() == _step) {
continue; //already processed
}
c->set_island_step(_step);
- c->set_island_next(*p_constraint_island);
- *p_constraint_island = c;
+ p_constraint_island.push_back(c);
for (int i = 0; i < c->get_body_count(); i++) {
if (i == E->get().second) {
@@ -53,78 +57,62 @@ void Step2DSW::_populate_island(Body2DSW *p_body, Body2DSW **p_island, Constrain
if (b->get_island_step() == _step || b->get_mode() == PhysicsServer2D::BODY_MODE_STATIC || b->get_mode() == PhysicsServer2D::BODY_MODE_KINEMATIC) {
continue; //no go
}
- _populate_island(c->get_body_ptr()[i], p_island, p_constraint_island);
+ _populate_island(c->get_body_ptr()[i], p_body_island, p_constraint_island);
}
}
}
-bool Step2DSW::_setup_island(Constraint2DSW *p_island, real_t p_delta) {
- Constraint2DSW *ci = p_island;
- Constraint2DSW *prev_ci = nullptr;
- bool removed_root = false;
- while (ci) {
- bool process = ci->setup(p_delta);
-
- if (!process) {
- //remove from island if process fails
- if (prev_ci) {
- prev_ci->set_island_next(ci->get_island_next());
- } else {
- removed_root = true;
- prev_ci = ci;
- }
- } else {
- prev_ci = ci;
+void Step2DSW::_setup_island(LocalVector<Constraint2DSW *> &p_constraint_island, real_t p_delta) {
+ uint32_t constraint_count = p_constraint_island.size();
+ uint32_t valid_constraint_count = 0;
+ for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
+ Constraint2DSW *constraint = p_constraint_island[constraint_index];
+ if (p_constraint_island[constraint_index]->setup(p_delta)) {
+ // Keep this constraint for solving.
+ p_constraint_island[valid_constraint_count++] = constraint;
}
- ci = ci->get_island_next();
}
-
- return removed_root;
+ p_constraint_island.resize(valid_constraint_count);
}
-void Step2DSW::_solve_island(Constraint2DSW *p_island, int p_iterations, real_t p_delta) {
+void Step2DSW::_solve_island(LocalVector<Constraint2DSW *> &p_constraint_island, int p_iterations, real_t p_delta) {
for (int i = 0; i < p_iterations; i++) {
- Constraint2DSW *ci = p_island;
- while (ci) {
- ci->solve(p_delta);
- ci = ci->get_island_next();
+ uint32_t constraint_count = p_constraint_island.size();
+ for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
+ p_constraint_island[constraint_index]->solve(p_delta);
}
}
}
-void Step2DSW::_check_suspend(Body2DSW *p_island, real_t p_delta) {
+void Step2DSW::_check_suspend(const LocalVector<Body2DSW *> &p_body_island, real_t p_delta) {
bool can_sleep = true;
- Body2DSW *b = p_island;
- while (b) {
- if (b->get_mode() == PhysicsServer2D::BODY_MODE_STATIC || b->get_mode() == PhysicsServer2D::BODY_MODE_KINEMATIC) {
- b = b->get_island_next();
- continue; //ignore for static
+ uint32_t body_count = p_body_island.size();
+ for (uint32_t body_index = 0; body_index < body_count; ++body_index) {
+ Body2DSW *body = p_body_island[body_index];
+
+ if (body->get_mode() == PhysicsServer2D::BODY_MODE_STATIC || body->get_mode() == PhysicsServer2D::BODY_MODE_KINEMATIC) {
+ continue; // Ignore for static.
}
- if (!b->sleep_test(p_delta)) {
+ if (!body->sleep_test(p_delta)) {
can_sleep = false;
}
-
- b = b->get_island_next();
}
- //put all to sleep or wake up everyoen
+ // Put all to sleep or wake up everyone.
+ for (uint32_t body_index = 0; body_index < body_count; ++body_index) {
+ Body2DSW *body = p_body_island[body_index];
- b = p_island;
- while (b) {
- if (b->get_mode() == PhysicsServer2D::BODY_MODE_STATIC || b->get_mode() == PhysicsServer2D::BODY_MODE_KINEMATIC) {
- b = b->get_island_next();
- continue; //ignore for static
+ if (body->get_mode() == PhysicsServer2D::BODY_MODE_STATIC || body->get_mode() == PhysicsServer2D::BODY_MODE_KINEMATIC) {
+ continue; // Ignore for static.
}
- bool active = b->is_active();
+ bool active = body->is_active();
if (active == can_sleep) {
- b->set_active(!can_sleep);
+ body->set_active(!can_sleep);
}
-
- b = b->get_island_next();
}
}
@@ -159,33 +147,43 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) {
/* GENERATE CONSTRAINT ISLANDS */
- Body2DSW *island_list = nullptr;
- Constraint2DSW *constraint_island_list = nullptr;
b = body_list->first();
- int island_count = 0;
+ uint32_t body_island_count = 0;
+ uint32_t island_count = 0;
while (b) {
Body2DSW *body = b->self();
if (body->get_island_step() != _step) {
- Body2DSW *island = nullptr;
- Constraint2DSW *constraint_island = nullptr;
- _populate_island(body, &island, &constraint_island);
+ ++body_island_count;
+ if (body_islands.size() < body_island_count) {
+ body_islands.resize(body_island_count);
+ }
+ LocalVector<Body2DSW *> &body_island = body_islands[body_island_count - 1];
+ body_island.clear();
+ body_island.reserve(BODY_ISLAND_SIZE_RESERVE);
- island->set_island_list_next(island_list);
- island_list = island;
+ ++island_count;
+ if (constraint_islands.size() < island_count) {
+ constraint_islands.resize(island_count);
+ }
+ LocalVector<Constraint2DSW *> &constraint_island = constraint_islands[island_count - 1];
+ constraint_island.clear();
+ constraint_island.reserve(ISLAND_SIZE_RESERVE);
- if (constraint_island) {
- constraint_island->set_island_list_next(constraint_island_list);
- constraint_island_list = constraint_island;
- island_count++;
+ _populate_island(body, body_island, constraint_island);
+
+ body_islands.push_back(body_island);
+
+ if (constraint_island.is_empty()) {
+ --island_count;
}
}
b = b->next();
}
- p_space->set_island_count(island_count);
+ p_space->set_island_count((int)island_count);
const SelfList<Area2DSW>::List &aml = p_space->get_moved_area_list();
@@ -196,9 +194,13 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) {
continue;
}
c->set_island_step(_step);
- c->set_island_next(nullptr);
- c->set_island_list_next(constraint_island_list);
- constraint_island_list = c;
+ ++island_count;
+ if (constraint_islands.size() < island_count) {
+ constraint_islands.resize(island_count);
+ }
+ LocalVector<Constraint2DSW *> &constraint_island = constraint_islands[island_count - 1];
+ constraint_island.clear();
+ constraint_island.push_back(c);
}
p_space->area_remove_from_moved_list((SelfList<Area2DSW> *)aml.first()); //faster to remove here
}
@@ -211,39 +213,8 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) {
/* SETUP CONSTRAINT ISLANDS */
- {
- Constraint2DSW *ci = constraint_island_list;
- Constraint2DSW *prev_ci = nullptr;
- while (ci) {
- if (_setup_island(ci, p_delta)) {
- //removed the root from the island graph because it is not to be processed
-
- Constraint2DSW *next = ci->get_island_next();
-
- if (next) {
- //root from list being deleted no longer exists, replace by next
- next->set_island_list_next(ci->get_island_list_next());
- if (prev_ci) {
- prev_ci->set_island_list_next(next);
- } else {
- constraint_island_list = next;
- }
- prev_ci = next;
- } else {
- //list is empty, just skip
- if (prev_ci) {
- prev_ci->set_island_list_next(ci->get_island_list_next());
-
- } else {
- constraint_island_list = ci->get_island_list_next();
- }
- }
- } else {
- prev_ci = ci;
- }
-
- ci = ci->get_island_list_next();
- }
+ for (uint32_t island_index = 0; island_index < island_count; ++island_index) {
+ _setup_island(constraint_islands[island_index], p_delta);
}
{ //profile
@@ -254,13 +225,8 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) {
/* SOLVE CONSTRAINT ISLANDS */
- {
- Constraint2DSW *ci = constraint_island_list;
- while (ci) {
- //iterating each island separatedly improves cache efficiency
- _solve_island(ci, p_iterations, p_delta);
- ci = ci->get_island_list_next();
- }
+ for (uint32_t island_index = 0; island_index < island_count; ++island_index) {
+ _solve_island(constraint_islands[island_index], p_iterations, p_delta);
}
{ //profile
@@ -280,12 +246,8 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) {
/* SLEEP / WAKE UP ISLANDS */
- {
- Body2DSW *bi = island_list;
- while (bi) {
- _check_suspend(bi, p_delta);
- bi = bi->get_island_list_next();
- }
+ for (uint32_t island_index = 0; island_index < body_island_count; ++island_index) {
+ _check_suspend(body_islands[island_index], p_delta);
}
{ //profile
@@ -301,4 +263,7 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) {
Step2DSW::Step2DSW() {
_step = 1;
+
+ body_islands.reserve(BODY_ISLAND_COUNT_RESERVE);
+ constraint_islands.reserve(ISLAND_COUNT_RESERVE);
}
diff --git a/servers/physics_2d/step_2d_sw.h b/servers/physics_2d/step_2d_sw.h
index 83b9130608..5af4a36f52 100644
--- a/servers/physics_2d/step_2d_sw.h
+++ b/servers/physics_2d/step_2d_sw.h
@@ -33,13 +33,18 @@
#include "space_2d_sw.h"
+#include "core/templates/local_vector.h"
+
class Step2DSW {
uint64_t _step;
- void _populate_island(Body2DSW *p_body, Body2DSW **p_island, Constraint2DSW **p_constraint_island);
- bool _setup_island(Constraint2DSW *p_island, real_t p_delta);
- void _solve_island(Constraint2DSW *p_island, int p_iterations, real_t p_delta);
- void _check_suspend(Body2DSW *p_island, real_t p_delta);
+ LocalVector<LocalVector<Body2DSW *>> body_islands;
+ LocalVector<LocalVector<Constraint2DSW *>> constraint_islands;
+
+ void _populate_island(Body2DSW *p_body, LocalVector<Body2DSW *> &p_body_island, LocalVector<Constraint2DSW *> &p_constraint_island);
+ void _setup_island(LocalVector<Constraint2DSW *> &p_constraint_island, real_t p_delta);
+ void _solve_island(LocalVector<Constraint2DSW *> &p_constraint_island, int p_iterations, real_t p_delta);
+ void _check_suspend(const LocalVector<Body2DSW *> &p_body_island, real_t p_delta);
public:
void step(Space2DSW *p_space, real_t p_delta, int p_iterations);
diff --git a/servers/physics_3d/body_3d_sw.cpp b/servers/physics_3d/body_3d_sw.cpp
index 64ba0cb09d..cc414b7f30 100644
--- a/servers/physics_3d/body_3d_sw.cpp
+++ b/servers/physics_3d/body_3d_sw.cpp
@@ -761,8 +761,6 @@ Body3DSW::Body3DSW() :
omit_force_integration = false;
//applied_torque=0;
island_step = 0;
- island_next = nullptr;
- island_list_next = nullptr;
first_time_kinematic = false;
first_integration = false;
_set_static(false);
diff --git a/servers/physics_3d/body_3d_sw.h b/servers/physics_3d/body_3d_sw.h
index e87ff2364b..5790f43019 100644
--- a/servers/physics_3d/body_3d_sw.h
+++ b/servers/physics_3d/body_3d_sw.h
@@ -135,8 +135,6 @@ class Body3DSW : public CollisionObject3DSW {
ForceIntegrationCallback *fi_callback;
uint64_t island_step;
- Body3DSW *island_next;
- Body3DSW *island_list_next;
_FORCE_INLINE_ void _compute_area_gravity_and_dampenings(const Area3DSW *p_area);
@@ -189,12 +187,6 @@ public:
_FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
_FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step = p_step; }
- _FORCE_INLINE_ Body3DSW *get_island_next() const { return island_next; }
- _FORCE_INLINE_ void set_island_next(Body3DSW *p_next) { island_next = p_next; }
-
- _FORCE_INLINE_ Body3DSW *get_island_list_next() const { return island_list_next; }
- _FORCE_INLINE_ void set_island_list_next(Body3DSW *p_next) { island_list_next = p_next; }
-
_FORCE_INLINE_ void add_constraint(Constraint3DSW *p_constraint, int p_pos) { constraint_map[p_constraint] = p_pos; }
_FORCE_INLINE_ void remove_constraint(Constraint3DSW *p_constraint) { constraint_map.erase(p_constraint); }
const Map<Constraint3DSW *, int> &get_constraint_map() const { return constraint_map; }
diff --git a/servers/physics_3d/body_pair_3d_sw.cpp b/servers/physics_3d/body_pair_3d_sw.cpp
index ef4dca7dcf..28c854466f 100644
--- a/servers/physics_3d/body_pair_3d_sw.cpp
+++ b/servers/physics_3d/body_pair_3d_sw.cpp
@@ -281,6 +281,8 @@ bool BodyPair3DSW::setup(real_t p_step) {
real_t inv_dt = 1.0 / p_step;
+ bool do_process = false;
+
for (int i = 0; i < contact_count; i++) {
Contact &c = contacts[i];
c.active = false;
@@ -323,6 +325,7 @@ bool BodyPair3DSW::setup(real_t p_step) {
}
c.active = true;
+ do_process = true;
// Precompute normal mass, tangent mass, and bias.
Vector3 inertia_A = A->get_inv_inertia_tensor().xform(c.rA.cross(c.normal));
@@ -350,7 +353,7 @@ bool BodyPair3DSW::setup(real_t p_step) {
}
}
- return true;
+ return do_process;
}
void BodyPair3DSW::solve(real_t p_step) {
@@ -594,6 +597,8 @@ bool BodySoftBodyPair3DSW::setup(real_t p_step) {
real_t inv_dt = 1.0 / p_step;
+ bool do_process = false;
+
uint32_t contact_count = contacts.size();
for (uint32_t contact_index = 0; contact_index < contact_count; ++contact_index) {
Contact &c = contacts[contact_index];
@@ -614,6 +619,7 @@ bool BodySoftBodyPair3DSW::setup(real_t p_step) {
}
c.active = true;
+ do_process = true;
#ifdef DEBUG_ENABLED
@@ -661,7 +667,7 @@ bool BodySoftBodyPair3DSW::setup(real_t p_step) {
}
}
- return true;
+ return do_process;
}
void BodySoftBodyPair3DSW::solve(real_t p_step) {
diff --git a/servers/physics_3d/constraint_3d_sw.h b/servers/physics_3d/constraint_3d_sw.h
index 2571335c43..16a31e167d 100644
--- a/servers/physics_3d/constraint_3d_sw.h
+++ b/servers/physics_3d/constraint_3d_sw.h
@@ -37,8 +37,6 @@ class Constraint3DSW {
Body3DSW **_body_ptr;
int _body_count;
uint64_t island_step;
- Constraint3DSW *island_next;
- Constraint3DSW *island_list_next;
int priority;
bool disabled_collisions_between_bodies;
@@ -60,12 +58,6 @@ public:
_FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
_FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step = p_step; }
- _FORCE_INLINE_ Constraint3DSW *get_island_next() const { return island_next; }
- _FORCE_INLINE_ void set_island_next(Constraint3DSW *p_next) { island_next = p_next; }
-
- _FORCE_INLINE_ Constraint3DSW *get_island_list_next() const { return island_list_next; }
- _FORCE_INLINE_ void set_island_list_next(Constraint3DSW *p_next) { island_list_next = p_next; }
-
_FORCE_INLINE_ Body3DSW **get_body_ptr() const { return _body_ptr; }
_FORCE_INLINE_ int get_body_count() const { return _body_count; }
diff --git a/servers/physics_3d/joints/cone_twist_joint_3d_sw.cpp b/servers/physics_3d/joints/cone_twist_joint_3d_sw.cpp
index 9c4493f4a2..167f797bfe 100644
--- a/servers/physics_3d/joints/cone_twist_joint_3d_sw.cpp
+++ b/servers/physics_3d/joints/cone_twist_joint_3d_sw.cpp
@@ -109,6 +109,10 @@ ConeTwistJoint3DSW::ConeTwistJoint3DSW(Body3DSW *rbA, Body3DSW *rbB, const Trans
}
bool ConeTwistJoint3DSW::setup(real_t p_timestep) {
+ if ((A->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC)) {
+ return false;
+ }
+
m_appliedImpulse = real_t(0.);
//set bias, sign, clear accumulator
diff --git a/servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp b/servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp
index 13b389251f..a86e8b4e76 100644
--- a/servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp
+++ b/servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp
@@ -303,6 +303,10 @@ bool Generic6DOFJoint3DSW::testAngularLimitMotor(int axis_index) {
}
bool Generic6DOFJoint3DSW::setup(real_t p_timestep) {
+ if ((A->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC)) {
+ return false;
+ }
+
// Clear accumulated impulses for the next simulation step
m_linearLimits.m_accumulatedImpulse = Vector3(real_t(0.), real_t(0.), real_t(0.));
int i;
diff --git a/servers/physics_3d/joints/hinge_joint_3d_sw.cpp b/servers/physics_3d/joints/hinge_joint_3d_sw.cpp
index 2b9f0038b4..90b82f4680 100644
--- a/servers/physics_3d/joints/hinge_joint_3d_sw.cpp
+++ b/servers/physics_3d/joints/hinge_joint_3d_sw.cpp
@@ -155,6 +155,10 @@ HingeJoint3DSW::HingeJoint3DSW(Body3DSW *rbA, Body3DSW *rbB, const Vector3 &pivo
}
bool HingeJoint3DSW::setup(real_t p_step) {
+ if ((A->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC)) {
+ return false;
+ }
+
m_appliedImpulse = real_t(0.);
if (!m_angularOnly) {
diff --git a/servers/physics_3d/joints/pin_joint_3d_sw.cpp b/servers/physics_3d/joints/pin_joint_3d_sw.cpp
index 9f708ce151..75d87992d1 100644
--- a/servers/physics_3d/joints/pin_joint_3d_sw.cpp
+++ b/servers/physics_3d/joints/pin_joint_3d_sw.cpp
@@ -50,6 +50,10 @@ subject to the following restrictions:
#include "pin_joint_3d_sw.h"
bool PinJoint3DSW::setup(real_t p_step) {
+ if ((A->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC)) {
+ return false;
+ }
+
m_appliedImpulse = real_t(0.);
Vector3 normal(0, 0, 0);
diff --git a/servers/physics_3d/joints/slider_joint_3d_sw.cpp b/servers/physics_3d/joints/slider_joint_3d_sw.cpp
index 0adc471797..2e1ee8e770 100644
--- a/servers/physics_3d/joints/slider_joint_3d_sw.cpp
+++ b/servers/physics_3d/joints/slider_joint_3d_sw.cpp
@@ -127,6 +127,10 @@ SliderJoint3DSW::SliderJoint3DSW(Body3DSW *rbA, Body3DSW *rbB, const Transform &
//-----------------------------------------------------------------------------
bool SliderJoint3DSW::setup(real_t p_step) {
+ if ((A->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer3D::BODY_MODE_KINEMATIC)) {
+ return false;
+ }
+
//calculate transforms
m_calculatedTransformA = A->get_transform() * m_frameInA;
m_calculatedTransformB = B->get_transform() * m_frameInB;
diff --git a/servers/physics_3d/step_3d_sw.cpp b/servers/physics_3d/step_3d_sw.cpp
index 2133a38670..06f3227eab 100644
--- a/servers/physics_3d/step_3d_sw.cpp
+++ b/servers/physics_3d/step_3d_sw.cpp
@@ -33,19 +33,23 @@
#include "core/os/os.h"
-void Step3DSW::_populate_island(Body3DSW *p_body, Body3DSW **p_island, Constraint3DSW **p_constraint_island) {
+#define BODY_ISLAND_COUNT_RESERVE 128
+#define BODY_ISLAND_SIZE_RESERVE 512
+#define ISLAND_COUNT_RESERVE 128
+#define ISLAND_SIZE_RESERVE 512
+
+void Step3DSW::_populate_island(Body3DSW *p_body, LocalVector<Body3DSW *> &p_body_island, LocalVector<Constraint3DSW *> &p_constraint_island) {
p_body->set_island_step(_step);
- p_body->set_island_next(*p_island);
- *p_island = p_body;
+ p_body_island.push_back(p_body);
- for (Map<Constraint3DSW *, int>::Element *E = p_body->get_constraint_map().front(); E; E = E->next()) {
+ // Faster with reversed iterations.
+ for (Map<Constraint3DSW *, int>::Element *E = p_body->get_constraint_map().back(); E; E = E->prev()) {
Constraint3DSW *c = (Constraint3DSW *)E->key();
if (c->get_island_step() == _step) {
continue; //already processed
}
c->set_island_step(_step);
- c->set_island_next(*p_constraint_island);
- *p_constraint_island = c;
+ p_constraint_island.push_back(c);
for (int i = 0; i < c->get_body_count(); i++) {
if (i == E->get()) {
@@ -55,87 +59,79 @@ void Step3DSW::_populate_island(Body3DSW *p_body, Body3DSW **p_island, Constrain
if (b->get_island_step() == _step || b->get_mode() == PhysicsServer3D::BODY_MODE_STATIC || b->get_mode() == PhysicsServer3D::BODY_MODE_KINEMATIC) {
continue; //no go
}
- _populate_island(c->get_body_ptr()[i], p_island, p_constraint_island);
+ _populate_island(c->get_body_ptr()[i], p_body_island, p_constraint_island);
}
}
}
-void Step3DSW::_setup_island(Constraint3DSW *p_island, real_t p_delta) {
- Constraint3DSW *ci = p_island;
- while (ci) {
- ci->setup(p_delta);
- //todo remove from island if process fails
- ci = ci->get_island_next();
+void Step3DSW::_setup_island(LocalVector<Constraint3DSW *> &p_constraint_island, real_t p_delta) {
+ uint32_t constraint_count = p_constraint_island.size();
+ uint32_t valid_constraint_count = 0;
+ for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
+ Constraint3DSW *constraint = p_constraint_island[constraint_index];
+ if (p_constraint_island[constraint_index]->setup(p_delta)) {
+ // Keep this constraint for solving.
+ p_constraint_island[valid_constraint_count++] = constraint;
+ }
}
+ p_constraint_island.resize(valid_constraint_count);
}
-void Step3DSW::_solve_island(Constraint3DSW *p_island, int p_iterations, real_t p_delta) {
- int at_priority = 1;
+void Step3DSW::_solve_island(LocalVector<Constraint3DSW *> &p_constraint_island, int p_iterations, real_t p_delta) {
+ int current_priority = 1;
- while (p_island) {
+ uint32_t constraint_count = p_constraint_island.size();
+ while (constraint_count > 0) {
for (int i = 0; i < p_iterations; i++) {
- Constraint3DSW *ci = p_island;
- while (ci) {
- ci->solve(p_delta);
- ci = ci->get_island_next();
+ // Go through all iterations.
+ for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
+ p_constraint_island[constraint_index]->solve(p_delta);
}
}
- at_priority++;
-
- {
- Constraint3DSW *ci = p_island;
- Constraint3DSW *prev = nullptr;
- while (ci) {
- if (ci->get_priority() < at_priority) {
- if (prev) {
- prev->set_island_next(ci->get_island_next()); //remove
- } else {
- p_island = ci->get_island_next();
- }
- } else {
- prev = ci;
- }
-
- ci = ci->get_island_next();
+ // Check priority to keep only higher priority constraints.
+ uint32_t priority_constraint_count = 0;
+ ++current_priority;
+ for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
+ Constraint3DSW *constraint = p_constraint_island[constraint_index];
+ if (constraint->get_priority() >= current_priority) {
+ // Keep this constraint for the next iteration.
+ p_constraint_island[priority_constraint_count++] = constraint;
}
}
+ constraint_count = priority_constraint_count;
}
}
-void Step3DSW::_check_suspend(Body3DSW *p_island, real_t p_delta) {
+void Step3DSW::_check_suspend(const LocalVector<Body3DSW *> &p_body_island, real_t p_delta) {
bool can_sleep = true;
- Body3DSW *b = p_island;
- while (b) {
- if (b->get_mode() == PhysicsServer3D::BODY_MODE_STATIC || b->get_mode() == PhysicsServer3D::BODY_MODE_KINEMATIC) {
- b = b->get_island_next();
- continue; //ignore for static
+ uint32_t body_count = p_body_island.size();
+ for (uint32_t body_index = 0; body_index < body_count; ++body_index) {
+ Body3DSW *body = p_body_island[body_index];
+
+ if (body->get_mode() == PhysicsServer3D::BODY_MODE_STATIC || body->get_mode() == PhysicsServer3D::BODY_MODE_KINEMATIC) {
+ continue; // Ignore for static.
}
- if (!b->sleep_test(p_delta)) {
+ if (!body->sleep_test(p_delta)) {
can_sleep = false;
}
-
- b = b->get_island_next();
}
- //put all to sleep or wake up everyoen
+ // Put all to sleep or wake up everyone.
+ for (uint32_t body_index = 0; body_index < body_count; ++body_index) {
+ Body3DSW *body = p_body_island[body_index];
- b = p_island;
- while (b) {
- if (b->get_mode() == PhysicsServer3D::BODY_MODE_STATIC || b->get_mode() == PhysicsServer3D::BODY_MODE_KINEMATIC) {
- b = b->get_island_next();
- continue; //ignore for static
+ if (body->get_mode() == PhysicsServer3D::BODY_MODE_STATIC || body->get_mode() == PhysicsServer3D::BODY_MODE_KINEMATIC) {
+ continue; // Ignore for static.
}
- bool active = b->is_active();
+ bool active = body->is_active();
if (active == can_sleep) {
- b->set_active(!can_sleep);
+ body->set_active(!can_sleep);
}
-
- b = b->get_island_next();
}
}
@@ -181,33 +177,43 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) {
/* GENERATE CONSTRAINT ISLANDS */
- Body3DSW *island_list = nullptr;
- Constraint3DSW *constraint_island_list = nullptr;
b = body_list->first();
- int island_count = 0;
+ uint32_t body_island_count = 0;
+ uint32_t island_count = 0;
while (b) {
Body3DSW *body = b->self();
if (body->get_island_step() != _step) {
- Body3DSW *island = nullptr;
- Constraint3DSW *constraint_island = nullptr;
- _populate_island(body, &island, &constraint_island);
+ ++body_island_count;
+ if (body_islands.size() < body_island_count) {
+ body_islands.resize(body_island_count);
+ }
+ LocalVector<Body3DSW *> &body_island = body_islands[body_island_count - 1];
+ body_island.clear();
+ body_island.reserve(BODY_ISLAND_SIZE_RESERVE);
- island->set_island_list_next(island_list);
- island_list = island;
+ ++island_count;
+ if (constraint_islands.size() < island_count) {
+ constraint_islands.resize(island_count);
+ }
+ LocalVector<Constraint3DSW *> &constraint_island = constraint_islands[island_count - 1];
+ constraint_island.clear();
+ constraint_island.reserve(ISLAND_SIZE_RESERVE);
- if (constraint_island) {
- constraint_island->set_island_list_next(constraint_island_list);
- constraint_island_list = constraint_island;
- island_count++;
+ _populate_island(body, body_island, constraint_island);
+
+ body_islands.push_back(body_island);
+
+ if (constraint_island.is_empty()) {
+ --island_count;
}
}
b = b->next();
}
- p_space->set_island_count(island_count);
+ p_space->set_island_count((int)island_count);
const SelfList<Area3DSW>::List &aml = p_space->get_moved_area_list();
@@ -218,9 +224,13 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) {
continue;
}
c->set_island_step(_step);
- c->set_island_next(nullptr);
- c->set_island_list_next(constraint_island_list);
- constraint_island_list = c;
+ ++island_count;
+ if (constraint_islands.size() < island_count) {
+ constraint_islands.resize(island_count);
+ }
+ LocalVector<Constraint3DSW *> &constraint_island = constraint_islands[island_count - 1];
+ constraint_island.clear();
+ constraint_island.push_back(c);
}
p_space->area_remove_from_moved_list((SelfList<Area3DSW> *)aml.first()); //faster to remove here
}
@@ -233,9 +243,13 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) {
continue;
}
c->set_island_step(_step);
- c->set_island_next(nullptr);
- c->set_island_list_next(constraint_island_list);
- constraint_island_list = c;
+ ++island_count;
+ if (constraint_islands.size() < island_count) {
+ constraint_islands.resize(island_count);
+ }
+ LocalVector<Constraint3DSW *> &constraint_island = constraint_islands[island_count - 1];
+ constraint_island.clear();
+ constraint_island.push_back(c);
}
sb = sb->next();
}
@@ -248,12 +262,8 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) {
/* SETUP CONSTRAINT ISLANDS */
- {
- Constraint3DSW *ci = constraint_island_list;
- while (ci) {
- _setup_island(ci, p_delta);
- ci = ci->get_island_list_next();
- }
+ for (uint32_t island_index = 0; island_index < island_count; ++island_index) {
+ _setup_island(constraint_islands[island_index], p_delta);
}
{ //profile
@@ -264,13 +274,10 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) {
/* SOLVE CONSTRAINT ISLANDS */
- {
- Constraint3DSW *ci = constraint_island_list;
- while (ci) {
- //iterating each island separatedly improves cache efficiency
- _solve_island(ci, p_iterations, p_delta);
- ci = ci->get_island_list_next();
- }
+ for (uint32_t island_index = 0; island_index < island_count; ++island_index) {
+ // Warning: _solve_island modifies the constraint islands for optimization purpose,
+ // their content is not reliable after these calls and shouldn't be used anymore.
+ _solve_island(constraint_islands[island_index], p_iterations, p_delta);
}
{ //profile
@@ -290,12 +297,8 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) {
/* SLEEP / WAKE UP ISLANDS */
- {
- Body3DSW *bi = island_list;
- while (bi) {
- _check_suspend(bi, p_delta);
- bi = bi->get_island_list_next();
- }
+ for (uint32_t island_index = 0; island_index < body_island_count; ++island_index) {
+ _check_suspend(body_islands[island_index], p_delta);
}
/* UPDATE SOFT BODY CONSTRAINTS */
@@ -319,4 +322,7 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) {
Step3DSW::Step3DSW() {
_step = 1;
+
+ body_islands.reserve(BODY_ISLAND_COUNT_RESERVE);
+ constraint_islands.reserve(ISLAND_COUNT_RESERVE);
}
diff --git a/servers/physics_3d/step_3d_sw.h b/servers/physics_3d/step_3d_sw.h
index 55c48ec0eb..f406c35c3a 100644
--- a/servers/physics_3d/step_3d_sw.h
+++ b/servers/physics_3d/step_3d_sw.h
@@ -33,13 +33,18 @@
#include "space_3d_sw.h"
+#include "core/templates/local_vector.h"
+
class Step3DSW {
uint64_t _step;
- void _populate_island(Body3DSW *p_body, Body3DSW **p_island, Constraint3DSW **p_constraint_island);
- void _setup_island(Constraint3DSW *p_island, real_t p_delta);
- void _solve_island(Constraint3DSW *p_island, int p_iterations, real_t p_delta);
- void _check_suspend(Body3DSW *p_island, real_t p_delta);
+ LocalVector<LocalVector<Body3DSW *>> body_islands;
+ LocalVector<LocalVector<Constraint3DSW *>> constraint_islands;
+
+ void _populate_island(Body3DSW *p_body, LocalVector<Body3DSW *> &p_body_island, LocalVector<Constraint3DSW *> &p_constraint_island);
+ void _setup_island(LocalVector<Constraint3DSW *> &p_constraint_island, real_t p_delta);
+ void _solve_island(LocalVector<Constraint3DSW *> &p_constraint_island, int p_iterations, real_t p_delta);
+ void _check_suspend(const LocalVector<Body3DSW *> &p_body_island, real_t p_delta);
public:
void step(Space3DSW *p_space, real_t p_delta, int p_iterations);
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 45f6384b5e..15982b4b29 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -77,6 +77,9 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
int depth_drawi = DEPTH_DRAW_OPAQUE;
ShaderCompilerRD::IdentifierActions actions;
+ actions.entry_point_stages["vertex"] = ShaderCompilerRD::STAGE_VERTEX;
+ actions.entry_point_stages["fragment"] = ShaderCompilerRD::STAGE_FRAGMENT;
+ actions.entry_point_stages["light"] = ShaderCompilerRD::STAGE_FRAGMENT;
actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_mode, BLEND_MODE_ADD);
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
@@ -148,7 +151,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
print_line("\n**fragment_code:\n" + gen_code.fragment);
print_line("\n**light_code:\n" + gen_code.light);
#endif
- shader_singleton->shader.version_set_code(version, gen_code.uniforms, gen_code.vertex_global, gen_code.vertex, gen_code.fragment_global, gen_code.light, gen_code.fragment, gen_code.defines);
+ shader_singleton->shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompilerRD::STAGE_VERTEX], gen_code.stage_globals[ShaderCompilerRD::STAGE_FRAGMENT], gen_code.defines);
ERR_FAIL_COND(!shader_singleton->shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 7d6e2fa8e4..3c76c91a67 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -2012,6 +2012,9 @@ void RendererCanvasRenderRD::ShaderData::set_code(const String &p_code) {
uses_screen_texture = false;
ShaderCompilerRD::IdentifierActions actions;
+ actions.entry_point_stages["vertex"] = ShaderCompilerRD::STAGE_VERTEX;
+ actions.entry_point_stages["fragment"] = ShaderCompilerRD::STAGE_FRAGMENT;
+ actions.entry_point_stages["light"] = ShaderCompilerRD::STAGE_FRAGMENT;
actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_mode, BLEND_MODE_ADD);
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
@@ -2048,7 +2051,7 @@ void RendererCanvasRenderRD::ShaderData::set_code(const String &p_code) {
print_line("\n**fragment_code:\n" + gen_code.fragment);
print_line("\n**light_code:\n" + gen_code.light);
#endif
- canvas_singleton->shader.canvas_shader.version_set_code(version, gen_code.uniforms, gen_code.vertex_global, gen_code.vertex, gen_code.fragment_global, gen_code.light, gen_code.fragment, gen_code.defines);
+ canvas_singleton->shader.canvas_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompilerRD::STAGE_VERTEX], gen_code.stage_globals[ShaderCompilerRD::STAGE_FRAGMENT], gen_code.defines);
ERR_FAIL_COND(!canvas_singleton->shader.canvas_shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
index 769335ac16..54c6e81110 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
@@ -50,6 +50,7 @@ void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) {
ShaderCompilerRD::GeneratedCode gen_code;
ShaderCompilerRD::IdentifierActions actions;
+ actions.entry_point_stages["sky"] = ShaderCompilerRD::STAGE_FRAGMENT;
uses_time = false;
uses_half_res = false;
@@ -110,7 +111,7 @@ void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) {
print_line("\n**light_code:\n" + gen_code.light);
#endif
- scene_singleton->sky.sky_shader.shader.version_set_code(version, gen_code.uniforms, gen_code.vertex_global, gen_code.vertex, gen_code.fragment_global, gen_code.light, gen_code.fragment, gen_code.defines);
+ scene_singleton->sky.sky_shader.shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompilerRD::STAGE_VERTEX], gen_code.stage_globals[ShaderCompilerRD::STAGE_FRAGMENT], gen_code.defines);
ERR_FAIL_COND(!scene_singleton->sky.sky_shader.shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
@@ -759,7 +760,7 @@ void RendererSceneSkyRD::init(RendererStorageRD *p_storage) {
sky_shader.default_shader = storage->shader_allocate();
storage->shader_initialize(sky_shader.default_shader);
- storage->shader_set_code(sky_shader.default_shader, "shader_type sky; void fragment() { COLOR = vec3(0.0); } \n");
+ storage->shader_set_code(sky_shader.default_shader, "shader_type sky; void sky() { COLOR = vec3(0.0); } \n");
sky_shader.default_material = storage->material_allocate();
storage->material_initialize(sky_shader.default_material);
@@ -840,7 +841,7 @@ void RendererSceneSkyRD::init(RendererStorageRD *p_storage) {
sky_scene_state.fog_shader = storage->shader_allocate();
storage->shader_initialize(sky_scene_state.fog_shader);
- storage->shader_set_code(sky_scene_state.fog_shader, "shader_type sky; uniform vec4 clear_color; void fragment() { COLOR = clear_color.rgb; } \n");
+ storage->shader_set_code(sky_scene_state.fog_shader, "shader_type sky; uniform vec4 clear_color; void sky() { COLOR = clear_color.rgb; } \n");
sky_scene_state.fog_material = storage->material_allocate();
storage->material_initialize(sky_scene_state.fog_material);
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index b984f850a0..189c5782f4 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -4781,6 +4781,8 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) {
ShaderCompilerRD::GeneratedCode gen_code;
ShaderCompilerRD::IdentifierActions actions;
+ actions.entry_point_stages["start"] = ShaderCompilerRD::STAGE_COMPUTE;
+ actions.entry_point_stages["process"] = ShaderCompilerRD::STAGE_COMPUTE;
/*
uses_time = false;
@@ -4801,7 +4803,7 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) {
version = base_singleton->particles_shader.shader.version_create();
}
- base_singleton->particles_shader.shader.version_set_compute_code(version, gen_code.uniforms, gen_code.compute_global, gen_code.compute, gen_code.defines);
+ base_singleton->particles_shader.shader.version_set_compute_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompilerRD::STAGE_COMPUTE], gen_code.defines);
ERR_FAIL_COND(!base_singleton->particles_shader.shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
@@ -8824,7 +8826,6 @@ RendererStorageRD::RendererStorageRD() {
sdf_versions.push_back(""); //one only
giprobe_sdf_shader.initialize(sdf_versions);
giprobe_sdf_shader_version = giprobe_sdf_shader.version_create();
- giprobe_sdf_shader.version_set_compute_code(giprobe_sdf_shader_version, "", "", "", Vector<String>());
giprobe_sdf_shader_version_shader = giprobe_sdf_shader.version_get_shader(giprobe_sdf_shader_version, 0);
giprobe_sdf_shader_pipeline = RD::get_singleton()->compute_pipeline_create(giprobe_sdf_shader_version_shader);
}
@@ -8913,7 +8914,7 @@ RendererStorageRD::RendererStorageRD() {
// default material and shader for particles shader
particles_shader.default_shader = shader_allocate();
shader_initialize(particles_shader.default_shader);
- shader_set_code(particles_shader.default_shader, "shader_type particles; void compute() { COLOR = vec4(1.0); } \n");
+ shader_set_code(particles_shader.default_shader, "shader_type particles; void process() { COLOR = vec4(1.0); } \n");
particles_shader.default_material = material_allocate();
material_initialize(particles_shader.default_material);
material_set_shader(particles_shader.default_material, particles_shader.default_shader);
diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.cpp b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
index 8135d388e1..24ac85bb35 100644
--- a/servers/rendering/renderer_rd/shader_compiler_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
@@ -535,9 +535,9 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
struct_code += "}";
struct_code += ";\n";
- r_gen_code.vertex_global += struct_code;
- r_gen_code.fragment_global += struct_code;
- r_gen_code.compute_global += struct_code;
+ for (int j = 0; j < STAGE_MAX; j++) {
+ r_gen_code.stage_globals[j] += struct_code;
+ }
}
int max_texture_uniforms = 0;
@@ -590,9 +590,9 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
ucode += " " + _mkid(E->key());
ucode += ";\n";
if (SL::is_sampler_type(E->get().type)) {
- r_gen_code.vertex_global += ucode;
- r_gen_code.fragment_global += ucode;
- r_gen_code.compute_global += ucode;
+ for (int j = 0; j < STAGE_MAX; j++) {
+ r_gen_code.stage_globals[j] += ucode;
+ }
GeneratedCode::Texture texture;
texture.name = E->key();
@@ -608,7 +608,6 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
r_gen_code.texture_uniforms.write[E->get().texture_order] = texture;
} else {
if (!uses_uniforms) {
- r_gen_code.defines.push_back(String("#define USE_MATERIAL_UNIFORMS\n"));
uses_uniforms = true;
}
uniform_defines.write[E->get().order] = ucode;
@@ -707,9 +706,10 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
vcode += "]";
}
vcode += ";\n";
- r_gen_code.vertex_global += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode;
- r_gen_code.fragment_global += "layout(location=" + itos(index) + ") " + interp_mode + "in " + vcode;
- r_gen_code.compute_global += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode;
+
+ r_gen_code.stage_globals[STAGE_VERTEX] += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode;
+ r_gen_code.stage_globals[STAGE_FRAGMENT] += "layout(location=" + itos(index) + ") " + interp_mode + "in " + vcode;
+
index++;
}
@@ -725,7 +725,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
gcode += ";\n";
}
gcode += "} frag_to_light;\n";
- r_gen_code.fragment_global += gcode;
+ r_gen_code.stage_globals[STAGE_FRAGMENT] += gcode;
}
for (int i = 0; i < pnode->vconstants.size(); i++) {
@@ -747,9 +747,9 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
gcode += "=";
gcode += _dump_node_code(cnode.initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
gcode += ";\n";
- r_gen_code.vertex_global += gcode;
- r_gen_code.fragment_global += gcode;
- r_gen_code.compute_global += gcode;
+ for (int j = 0; j < STAGE_MAX; j++) {
+ r_gen_code.stage_globals[j] += gcode;
+ }
}
Map<StringName, String> function_code;
@@ -765,9 +765,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
//place functions in actual code
- Set<StringName> added_vtx;
- Set<StringName> added_fragment; //share for light
- Set<StringName> added_compute; //share for light
+ Set<StringName> added_funcs_per_stage[STAGE_MAX];
for (int i = 0; i < pnode->functions.size(); i++) {
SL::FunctionNode *fnode = pnode->functions[i].function;
@@ -776,24 +774,10 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
current_func_name = fnode->name;
- if (fnode->name == vertex_name) {
- _dump_function_deps(pnode, fnode->name, function_code, r_gen_code.vertex_global, added_vtx);
- r_gen_code.vertex = function_code[vertex_name];
- }
-
- if (fnode->name == fragment_name) {
- _dump_function_deps(pnode, fnode->name, function_code, r_gen_code.fragment_global, added_fragment);
- r_gen_code.fragment = function_code[fragment_name];
- }
-
- if (fnode->name == light_name) {
- _dump_function_deps(pnode, fnode->name, function_code, r_gen_code.fragment_global, added_fragment);
- r_gen_code.light = function_code[light_name];
- }
-
- if (fnode->name == compute_name) {
- _dump_function_deps(pnode, fnode->name, function_code, r_gen_code.compute_global, added_compute);
- r_gen_code.compute = function_code[compute_name];
+ if (p_actions.entry_point_stages.has(fnode->name)) {
+ Stage stage = p_actions.entry_point_stages[fnode->name];
+ _dump_function_deps(pnode, fnode->name, function_code, r_gen_code.stage_globals[stage], added_funcs_per_stage[stage]);
+ r_gen_code.code[fnode->name] = function_code[fnode->name];
}
function = nullptr;
@@ -858,7 +842,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
SL::VariableNode *vnode = (SL::VariableNode *)p_node;
bool use_fragment_varying = false;
- if (current_func_name != vertex_name) {
+ if (!(p_actions.entry_point_stages.has(current_func_name) && p_actions.entry_point_stages[current_func_name] == STAGE_VERTEX)) {
if (p_assigning) {
if (shader->varyings.has(vnode->name)) {
use_fragment_varying = true;
@@ -921,10 +905,10 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
}
if (vnode->name == time_name) {
- if (current_func_name == vertex_name) {
+ if (p_actions.entry_point_stages.has(current_func_name) && p_actions.entry_point_stages[current_func_name] == STAGE_VERTEX) {
r_gen_code.uses_vertex_time = true;
}
- if (current_func_name == fragment_name || current_func_name == light_name) {
+ if (p_actions.entry_point_stages.has(current_func_name) && p_actions.entry_point_stages[current_func_name] == STAGE_FRAGMENT) {
r_gen_code.uses_fragment_time = true;
}
}
@@ -1003,7 +987,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
SL::ArrayNode *anode = (SL::ArrayNode *)p_node;
bool use_fragment_varying = false;
- if (current_func_name != vertex_name) {
+ if (!(p_actions.entry_point_stages.has(current_func_name) && p_actions.entry_point_stages[current_func_name] == STAGE_VERTEX)) {
if (anode->assign_expression != nullptr) {
use_fragment_varying = true;
} else {
@@ -1059,10 +1043,10 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
}
if (anode->name == time_name) {
- if (current_func_name == vertex_name) {
+ if (p_actions.entry_point_stages.has(current_func_name) && p_actions.entry_point_stages[current_func_name] == STAGE_VERTEX) {
r_gen_code.uses_vertex_time = true;
}
- if (current_func_name == fragment_name || current_func_name == light_name) {
+ if (p_actions.entry_point_stages.has(current_func_name) && p_actions.entry_point_stages[current_func_name] == STAGE_FRAGMENT) {
r_gen_code.uses_fragment_time = true;
}
}
@@ -1309,7 +1293,7 @@ ShaderLanguage::DataType ShaderCompilerRD::_get_variable_type(const StringName &
}
Error ShaderCompilerRD::compile(RS::ShaderMode p_mode, const String &p_code, IdentifierActions *p_actions, const String &p_path, GeneratedCode &r_gen_code) {
- Error err = parser.compile(p_code, ShaderTypes::get_singleton()->get_functions(p_mode), ShaderTypes::get_singleton()->get_modes(p_mode), ShaderTypes::get_singleton()->get_types(), _get_variable_type);
+ Error err = parser.compile(p_code, ShaderTypes::get_singleton()->get_functions(p_mode), ShaderTypes::get_singleton()->get_modes(p_mode), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_variable_type);
if (err != OK) {
Vector<String> shader = p_code.split("\n");
@@ -1322,13 +1306,10 @@ Error ShaderCompilerRD::compile(RS::ShaderMode p_mode, const String &p_code, Ide
}
r_gen_code.defines.clear();
- r_gen_code.vertex = String();
- r_gen_code.vertex_global = String();
- r_gen_code.fragment = String();
- r_gen_code.fragment_global = String();
- r_gen_code.compute = String();
- r_gen_code.compute_global = String();
- r_gen_code.light = String();
+ r_gen_code.code.clear();
+ for (int i = 0; i < STAGE_MAX; i++) {
+ r_gen_code.stage_globals[i] = String();
+ }
r_gen_code.uses_fragment_time = false;
r_gen_code.uses_vertex_time = false;
r_gen_code.uses_global_textures = false;
@@ -1348,10 +1329,6 @@ Error ShaderCompilerRD::compile(RS::ShaderMode p_mode, const String &p_code, Ide
void ShaderCompilerRD::initialize(DefaultIdentifierActions p_actions) {
actions = p_actions;
- vertex_name = "vertex";
- fragment_name = "fragment";
- compute_name = "compute";
- light_name = "light";
time_name = "TIME";
List<String> func_list;
diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.h b/servers/rendering/renderer_rd/shader_compiler_rd.h
index 6575829e73..2da127ffa3 100644
--- a/servers/rendering/renderer_rd/shader_compiler_rd.h
+++ b/servers/rendering/renderer_rd/shader_compiler_rd.h
@@ -38,7 +38,16 @@
class ShaderCompilerRD {
public:
+ enum Stage {
+ STAGE_VERTEX,
+ STAGE_FRAGMENT,
+ STAGE_COMPUTE,
+ STAGE_MAX
+ };
+
struct IdentifierActions {
+ Map<StringName, Stage> entry_point_stages;
+
Map<StringName, Pair<int *, int>> render_mode_values;
Map<StringName, bool *> render_mode_flags;
Map<StringName, bool *> usage_flag_pointers;
@@ -63,13 +72,9 @@ public:
Vector<uint32_t> uniform_offsets;
uint32_t uniform_total_size;
String uniforms;
- String vertex_global;
- String vertex;
- String fragment_global;
- String fragment;
- String light;
- String compute_global;
- String compute;
+ String stage_globals[STAGE_MAX];
+
+ Map<String, String> code;
bool uses_global_textures;
bool uses_fragment_time;
@@ -103,10 +108,6 @@ private:
const ShaderLanguage::ShaderNode *shader;
const ShaderLanguage::FunctionNode *function;
StringName current_func_name;
- StringName vertex_name;
- StringName fragment_name;
- StringName light_name;
- StringName compute_name;
StringName time_name;
Set<StringName> texture_functions;
diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp
index e4a39ff813..f7242a2b17 100644
--- a/servers/rendering/renderer_rd/shader_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_rd.cpp
@@ -30,146 +30,83 @@
#include "shader_rd.h"
-#include "core/string/string_builder.h"
#include "renderer_compositor_rd.h"
#include "servers/rendering/rendering_device.h"
-void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) {
- name = p_name;
- //split vertex and shader code (thank you, shader compiler programmers from you know what company).
- if (p_vertex_code) {
- String defines_tag = "\nVERSION_DEFINES";
- String globals_tag = "\nVERTEX_SHADER_GLOBALS";
- String material_tag = "\nMATERIAL_UNIFORMS";
- String code_tag = "\nVERTEX_SHADER_CODE";
- String code = p_vertex_code;
-
- int cpos = code.find(defines_tag);
- if (cpos != -1) {
- vertex_codev = code.substr(0, cpos).ascii();
- code = code.substr(cpos + defines_tag.length(), code.length());
- }
-
- cpos = code.find(material_tag);
-
- if (cpos == -1) {
- vertex_code0 = code.ascii();
- } else {
- vertex_code0 = code.substr(0, cpos).ascii();
- code = code.substr(cpos + material_tag.length(), code.length());
-
- cpos = code.find(globals_tag);
-
- if (cpos == -1) {
- vertex_code1 = code.ascii();
- } else {
- vertex_code1 = code.substr(0, cpos).ascii();
- String code2 = code.substr(cpos + globals_tag.length(), code.length());
-
- cpos = code2.find(code_tag);
- if (cpos == -1) {
- vertex_code2 = code2.ascii();
- } else {
- vertex_code2 = code2.substr(0, cpos).ascii();
- vertex_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii();
+void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) {
+ Vector<String> lines = String(p_code).split("\n");
+
+ String text;
+
+ for (int i = 0; i < lines.size(); i++) {
+ String l = lines[i];
+ bool push_chunk = false;
+
+ StageTemplate::Chunk chunk;
+
+ if (l.begins_with("#VERSION_DEFINES")) {
+ chunk.type = StageTemplate::Chunk::TYPE_VERSION_DEFINES;
+ push_chunk = true;
+ } else if (l.begins_with("#GLOBALS")) {
+ switch (p_stage_type) {
+ case STAGE_TYPE_VERTEX:
+ chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS;
+ break;
+ case STAGE_TYPE_FRAGMENT:
+ chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS;
+ break;
+ case STAGE_TYPE_COMPUTE:
+ chunk.type = StageTemplate::Chunk::TYPE_COMPUTE_GLOBALS;
+ break;
+ default: {
}
}
- }
- }
- if (p_fragment_code) {
- String defines_tag = "\nVERSION_DEFINES";
- String globals_tag = "\nFRAGMENT_SHADER_GLOBALS";
- String material_tag = "\nMATERIAL_UNIFORMS";
- String code_tag = "\nFRAGMENT_SHADER_CODE";
- String light_code_tag = "\nLIGHT_SHADER_CODE";
- String code = p_fragment_code;
-
- int cpos = code.find(defines_tag);
- if (cpos != -1) {
- fragment_codev = code.substr(0, cpos).ascii();
- code = code.substr(cpos + defines_tag.length(), code.length());
+ push_chunk = true;
+ } else if (l.begins_with("#MATERIAL_UNIFORMS")) {
+ chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS;
+ push_chunk = true;
+ } else if (l.begins_with("#CODE")) {
+ chunk.type = StageTemplate::Chunk::TYPE_CODE;
+ push_chunk = true;
+ chunk.code = l.replace_first("#CODE", String()).replace(":", "").strip_edges().to_upper();
+ } else {
+ text += l + "\n";
}
- cpos = code.find(material_tag);
- if (cpos == -1) {
- fragment_code0 = code.ascii();
- } else {
- fragment_code0 = code.substr(0, cpos).ascii();
- //print_line("CODE0:\n"+String(fragment_code0.get_data()));
- code = code.substr(cpos + material_tag.length(), code.length());
- cpos = code.find(globals_tag);
-
- if (cpos == -1) {
- fragment_code1 = code.ascii();
- } else {
- fragment_code1 = code.substr(0, cpos).ascii();
- //print_line("CODE1:\n"+String(fragment_code1.get_data()));
-
- String code2 = code.substr(cpos + globals_tag.length(), code.length());
- cpos = code2.find(light_code_tag);
-
- if (cpos == -1) {
- fragment_code2 = code2.ascii();
- } else {
- fragment_code2 = code2.substr(0, cpos).ascii();
- //print_line("CODE2:\n"+String(fragment_code2.get_data()));
-
- String code3 = code2.substr(cpos + light_code_tag.length(), code2.length());
-
- cpos = code3.find(code_tag);
- if (cpos == -1) {
- fragment_code3 = code3.ascii();
- } else {
- fragment_code3 = code3.substr(0, cpos).ascii();
- //print_line("CODE3:\n"+String(fragment_code3.get_data()));
- fragment_code4 = code3.substr(cpos + code_tag.length(), code3.length()).ascii();
- //print_line("CODE4:\n"+String(fragment_code4.get_data()));
- }
- }
+ if (push_chunk) {
+ if (text != String()) {
+ StageTemplate::Chunk text_chunk;
+ text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
+ text_chunk.text = text.utf8();
+ stage_templates[p_stage_type].chunks.push_back(text_chunk);
+ text = String();
}
+ stage_templates[p_stage_type].chunks.push_back(chunk);
}
}
+ if (text != String()) {
+ StageTemplate::Chunk text_chunk;
+ text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
+ text_chunk.text = text.utf8();
+ stage_templates[p_stage_type].chunks.push_back(text_chunk);
+ text = String();
+ }
+}
+
+void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) {
+ name = p_name;
if (p_compute_code) {
+ _add_stage(p_compute_code, STAGE_TYPE_COMPUTE);
is_compute = true;
-
- String defines_tag = "\nVERSION_DEFINES";
- String globals_tag = "\nCOMPUTE_SHADER_GLOBALS";
- String material_tag = "\nMATERIAL_UNIFORMS";
- String code_tag = "\nCOMPUTE_SHADER_CODE";
- String code = p_compute_code;
-
- int cpos = code.find(defines_tag);
- if (cpos != -1) {
- compute_codev = code.substr(0, cpos).ascii();
- code = code.substr(cpos + defines_tag.length(), code.length());
+ } else {
+ is_compute = false;
+ if (p_vertex_code) {
+ _add_stage(p_vertex_code, STAGE_TYPE_VERTEX);
}
-
- cpos = code.find(material_tag);
-
- if (cpos == -1) {
- compute_code0 = code.ascii();
- } else {
- compute_code0 = code.substr(0, cpos).ascii();
- code = code.substr(cpos + material_tag.length(), code.length());
-
- cpos = code.find(globals_tag);
-
- if (cpos == -1) {
- compute_code1 = code.ascii();
- } else {
- compute_code1 = code.substr(0, cpos).ascii();
- String code2 = code.substr(cpos + globals_tag.length(), code.length());
-
- cpos = code2.find(code_tag);
- if (cpos == -1) {
- compute_code2 = code2.ascii();
- } else {
- compute_code2 = code2.substr(0, cpos).ascii();
- compute_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii();
- }
- }
+ if (p_fragment_code) {
+ _add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT);
}
}
}
@@ -198,6 +135,49 @@ void ShaderRD::_clear_version(Version *p_version) {
}
}
+void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template) {
+ for (uint32_t i = 0; i < p_template.chunks.size(); i++) {
+ const StageTemplate::Chunk &chunk = p_template.chunks[i];
+ switch (chunk.type) {
+ case StageTemplate::Chunk::TYPE_VERSION_DEFINES: {
+ builder.append("\n"); //make sure defines begin at newline
+ builder.append(general_defines.get_data());
+ builder.append(variant_defines[p_variant].get_data());
+ for (int j = 0; j < p_version->custom_defines.size(); j++) {
+ builder.append(p_version->custom_defines[j].get_data());
+ }
+ builder.append("\n"); //make sure defines begin at newline
+ if (p_version->uniforms.size()) {
+ builder.append("#define MATERIAL_UNIFORMS_USED\n");
+ }
+ for (Map<StringName, CharString>::Element *E = p_version->code_sections.front(); E; E = E->next()) {
+ builder.append(String("#define ") + String(E->key()) + "_CODE_USED\n");
+ }
+ } break;
+ case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: {
+ builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
+ } break;
+ case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: {
+ builder.append(p_version->vertex_globals.get_data()); // vertex globals
+ } break;
+ case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: {
+ builder.append(p_version->fragment_globals.get_data()); // fragment globals
+ } break;
+ case StageTemplate::Chunk::TYPE_COMPUTE_GLOBALS: {
+ builder.append(p_version->compute_globals.get_data()); // compute globals
+ } break;
+ case StageTemplate::Chunk::TYPE_CODE: {
+ if (p_version->code_sections.has(chunk.code)) {
+ builder.append(p_version->code_sections[chunk.code].get_data());
+ }
+ } break;
+ case StageTemplate::Chunk::TYPE_TEXT: {
+ builder.append(chunk.text.get_data());
+ } break;
+ }
+ }
+}
+
void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
if (!variants_enabled[p_variant]) {
return; //variant is disabled, return
@@ -214,29 +194,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
//vertex stage
StringBuilder builder;
-
- builder.append(vertex_codev.get_data()); // version info (if exists)
- builder.append("\n"); //make sure defines begin at newline
- builder.append(general_defines.get_data());
- builder.append(variant_defines[p_variant].get_data());
-
- for (int j = 0; j < p_version->custom_defines.size(); j++) {
- builder.append(p_version->custom_defines[j].get_data());
- }
-
- builder.append(vertex_code0.get_data()); //first part of vertex
-
- builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
-
- builder.append(vertex_code1.get_data()); //second part of vertex
-
- builder.append(p_version->vertex_globals.get_data()); // vertex globals
-
- builder.append(vertex_code2.get_data()); //third part of vertex
-
- builder.append(p_version->vertex_code.get_data()); // code
-
- builder.append(vertex_code3.get_data()); //fourth of vertex
+ _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX]);
current_source = builder.as_string();
RD::ShaderStageData stage;
@@ -254,33 +212,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
current_stage = RD::SHADER_STAGE_FRAGMENT;
StringBuilder builder;
-
- builder.append(fragment_codev.get_data()); // version info (if exists)
- builder.append("\n"); //make sure defines begin at newline
-
- builder.append(general_defines.get_data());
- builder.append(variant_defines[p_variant].get_data());
- for (int j = 0; j < p_version->custom_defines.size(); j++) {
- builder.append(p_version->custom_defines[j].get_data());
- }
-
- builder.append(fragment_code0.get_data()); //first part of fragment
-
- builder.append(p_version->uniforms.get_data()); //uniforms (same for fragment and fragment)
-
- builder.append(fragment_code1.get_data()); //first part of fragment
-
- builder.append(p_version->fragment_globals.get_data()); // fragment globals
-
- builder.append(fragment_code2.get_data()); //third part of fragment
-
- builder.append(p_version->fragment_light.get_data()); // fragment light
-
- builder.append(fragment_code3.get_data()); //fourth part of fragment
-
- builder.append(p_version->fragment_code.get_data()); // fragment code
-
- builder.append(fragment_code4.get_data()); //fourth part of fragment
+ _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT]);
current_source = builder.as_string();
RD::ShaderStageData stage;
@@ -298,32 +230,10 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
current_stage = RD::SHADER_STAGE_COMPUTE;
StringBuilder builder;
-
- builder.append(compute_codev.get_data()); // version info (if exists)
- builder.append("\n"); //make sure defines begin at newline
- builder.append(base_compute_defines.get_data());
- builder.append(general_defines.get_data());
- builder.append(variant_defines[p_variant].get_data());
-
- for (int j = 0; j < p_version->custom_defines.size(); j++) {
- builder.append(p_version->custom_defines[j].get_data());
- }
-
- builder.append(compute_code0.get_data()); //first part of compute
-
- builder.append(p_version->uniforms.get_data()); //uniforms (same for compute and fragment)
-
- builder.append(compute_code1.get_data()); //second part of compute
-
- builder.append(p_version->compute_globals.get_data()); // compute globals
-
- builder.append(compute_code2.get_data()); //third part of compute
-
- builder.append(p_version->compute_code.get_data()); // code
-
- builder.append(compute_code3.get_data()); //fourth of compute
+ _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_COMPUTE]);
current_source = builder.as_string();
+
RD::ShaderStageData stage;
stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error);
if (stage.spir_v.size() == 0) {
@@ -364,29 +274,7 @@ RS::ShaderNativeSourceCode ShaderRD::version_get_native_source_code(RID p_versio
//vertex stage
StringBuilder builder;
-
- builder.append(vertex_codev.get_data()); // version info (if exists)
- builder.append("\n"); //make sure defines begin at newline
- builder.append(general_defines.get_data());
- builder.append(variant_defines[i].get_data());
-
- for (int j = 0; j < version->custom_defines.size(); j++) {
- builder.append(version->custom_defines[j].get_data());
- }
-
- builder.append(vertex_code0.get_data()); //first part of vertex
-
- builder.append(version->uniforms.get_data()); //uniforms (same for vertex and fragment)
-
- builder.append(vertex_code1.get_data()); //second part of vertex
-
- builder.append(version->vertex_globals.get_data()); // vertex globals
-
- builder.append(vertex_code2.get_data()); //third part of vertex
-
- builder.append(version->vertex_code.get_data()); // code
-
- builder.append(vertex_code3.get_data()); //fourth of vertex
+ _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX]);
RS::ShaderNativeSourceCode::Version::Stage stage;
stage.name = "vertex";
@@ -399,32 +287,7 @@ RS::ShaderNativeSourceCode ShaderRD::version_get_native_source_code(RID p_versio
//fragment stage
StringBuilder builder;
-
- builder.append(fragment_codev.get_data()); // version info (if exists)
- builder.append("\n"); //make sure defines begin at newline
- builder.append(general_defines.get_data());
- builder.append(variant_defines[i].get_data());
- for (int j = 0; j < version->custom_defines.size(); j++) {
- builder.append(version->custom_defines[j].get_data());
- }
-
- builder.append(fragment_code0.get_data()); //first part of fragment
-
- builder.append(version->uniforms.get_data()); //uniforms (same for fragment and fragment)
-
- builder.append(fragment_code1.get_data()); //first part of fragment
-
- builder.append(version->fragment_globals.get_data()); // fragment globals
-
- builder.append(fragment_code2.get_data()); //third part of fragment
-
- builder.append(version->fragment_light.get_data()); // fragment light
-
- builder.append(fragment_code3.get_data()); //fourth part of fragment
-
- builder.append(version->fragment_code.get_data()); // fragment code
-
- builder.append(fragment_code4.get_data()); //fourth part of fragment
+ _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT]);
RS::ShaderNativeSourceCode::Version::Stage stage;
stage.name = "fragment";
@@ -437,30 +300,7 @@ RS::ShaderNativeSourceCode ShaderRD::version_get_native_source_code(RID p_versio
//compute stage
StringBuilder builder;
-
- builder.append(compute_codev.get_data()); // version info (if exists)
- builder.append("\n"); //make sure defines begin at newline
- builder.append(base_compute_defines.get_data());
- builder.append(general_defines.get_data());
- builder.append(variant_defines[i].get_data());
-
- for (int j = 0; j < version->custom_defines.size(); j++) {
- builder.append(version->custom_defines[j].get_data());
- }
-
- builder.append(compute_code0.get_data()); //first part of compute
-
- builder.append(version->uniforms.get_data()); //uniforms (same for compute and fragment)
-
- builder.append(compute_code1.get_data()); //second part of compute
-
- builder.append(version->compute_globals.get_data()); // compute globals
-
- builder.append(compute_code2.get_data()); //third part of compute
-
- builder.append(version->compute_code.get_data()); // code
-
- builder.append(compute_code3.get_data()); //fourth of compute
+ _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_COMPUTE]);
RS::ShaderNativeSourceCode::Version::Stage stage;
stage.name = "compute";
@@ -518,17 +358,18 @@ void ShaderRD::_compile_version(Version *p_version) {
p_version->valid = true;
}
-void ShaderRD::version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector<String> &p_custom_defines) {
+void ShaderRD::version_set_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines) {
ERR_FAIL_COND(is_compute);
Version *version = version_owner.getornull(p_version);
ERR_FAIL_COND(!version);
version->vertex_globals = p_vertex_globals.utf8();
- version->vertex_code = p_vertex_code.utf8();
- version->fragment_light = p_fragment_light.utf8();
version->fragment_globals = p_fragment_globals.utf8();
- version->fragment_code = p_fragment_code.utf8();
version->uniforms = p_uniforms.utf8();
+ version->code_sections.clear();
+ for (Map<String, String>::Element *E = p_code.front(); E; E = E->next()) {
+ version->code_sections[StringName(E->key().to_upper())] = E->get().utf8();
+ }
version->custom_defines.clear();
for (int i = 0; i < p_custom_defines.size(); i++) {
@@ -542,15 +383,20 @@ void ShaderRD::version_set_code(RID p_version, const String &p_uniforms, const S
}
}
-void ShaderRD::version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector<String> &p_custom_defines) {
+void ShaderRD::version_set_compute_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_compute_globals, const Vector<String> &p_custom_defines) {
ERR_FAIL_COND(!is_compute);
Version *version = version_owner.getornull(p_version);
ERR_FAIL_COND(!version);
+
version->compute_globals = p_compute_globals.utf8();
- version->compute_code = p_compute_code.utf8();
version->uniforms = p_uniforms.utf8();
+ version->code_sections.clear();
+ for (Map<String, String>::Element *E = p_code.front(); E; E = E->next()) {
+ version->code_sections[StringName(E->key().to_upper())] = E->get().utf8();
+ }
+
version->custom_defines.clear();
for (int i = 0; i < p_custom_defines.size(); i++) {
version->custom_defines.push_back(p_custom_defines[i].utf8());
diff --git a/servers/rendering/renderer_rd/shader_rd.h b/servers/rendering/renderer_rd/shader_rd.h
index e0f4dcf2d0..f20d539621 100644
--- a/servers/rendering/renderer_rd/shader_rd.h
+++ b/servers/rendering/renderer_rd/shader_rd.h
@@ -32,7 +32,9 @@
#define SHADER_RD_H
#include "core/os/mutex.h"
+#include "core/string/string_builder.h"
#include "core/templates/hash_map.h"
+#include "core/templates/local_vector.h"
#include "core/templates/map.h"
#include "core/templates/rid_owner.h"
#include "core/variant/variant.h"
@@ -52,12 +54,9 @@ class ShaderRD {
struct Version {
CharString uniforms;
CharString vertex_globals;
- CharString vertex_code;
CharString compute_globals;
- CharString compute_code;
- CharString fragment_light;
CharString fragment_globals;
- CharString fragment_code;
+ Map<StringName, CharString> code_sections;
Vector<CharString> custom_defines;
RID *variants; //same size as version defines
@@ -76,31 +75,44 @@ class ShaderRD {
RID_Owner<Version> version_owner;
- CharString fragment_codev; //for version and extensions
- CharString fragment_code0;
- CharString fragment_code1;
- CharString fragment_code2;
- CharString fragment_code3;
- CharString fragment_code4;
-
- CharString vertex_codev; //for version and extensions
- CharString vertex_code0;
- CharString vertex_code1;
- CharString vertex_code2;
- CharString vertex_code3;
+ struct StageTemplate {
+ struct Chunk {
+ enum Type {
+ TYPE_VERSION_DEFINES,
+ TYPE_MATERIAL_UNIFORMS,
+ TYPE_VERTEX_GLOBALS,
+ TYPE_FRAGMENT_GLOBALS,
+ TYPE_COMPUTE_GLOBALS,
+ TYPE_CODE,
+ TYPE_TEXT
+ };
+
+ Type type;
+ StringName code;
+ CharString text;
+ };
+ LocalVector<Chunk> chunks;
+ };
bool is_compute = false;
- CharString compute_codev; //for version and extensions
- CharString compute_code0;
- CharString compute_code1;
- CharString compute_code2;
- CharString compute_code3;
-
const char *name;
CharString base_compute_defines;
+ enum StageType {
+ STAGE_TYPE_VERTEX,
+ STAGE_TYPE_FRAGMENT,
+ STAGE_TYPE_COMPUTE,
+ STAGE_TYPE_MAX,
+ };
+
+ StageTemplate stage_templates[STAGE_TYPE_MAX];
+
+ void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template);
+
+ void _add_stage(const char *p_code, StageType p_stage_type);
+
protected:
ShaderRD();
void setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name);
@@ -108,8 +120,8 @@ protected:
public:
RID version_create();
- void version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector<String> &p_custom_defines);
- void version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector<String> &p_custom_defines);
+ void version_set_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines);
+ void version_set_compute_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_compute_globals, const Vector<String> &p_custom_defines);
_FORCE_INLINE_ RID version_get_shader(RID p_version, int p_variant) {
ERR_FAIL_INDEX_V(p_variant, variant_defines.size(), RID());
diff --git a/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl b/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl
index 63f086a83d..b70e0b6bd5 100644
--- a/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl
+++ b/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#define BLOCK_SIZE 8
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 3b39edc70e..8b97ec119f 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#ifdef USE_ATTRIBUTES
layout(location = 0) in vec2 vertex_attrib;
@@ -26,17 +26,15 @@ layout(location = 3) out vec2 pixel_size_interp;
#endif
-#ifdef USE_MATERIAL_UNIFORMS
+#ifdef MATERIAL_UNIFORMS_USED
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
- /* clang-format off */
-MATERIAL_UNIFORMS
- /* clang-format on */
+
+#MATERIAL_UNIFORMS
+
} material;
#endif
-/* clang-format off */
-VERTEX_SHADER_GLOBALS
-/* clang-format on */
+#GLOBALS
void main() {
vec4 instance_custom = vec4(0.0);
@@ -132,9 +130,7 @@ void main() {
float point_size = 1.0;
#endif
{
- /* clang-format off */
-VERTEX_SHADER_CODE
- /* clang-format on */
+#CODE : VERTEX
}
#ifdef USE_NINEPATCH
@@ -212,7 +208,7 @@ VERTEX_SHADER_CODE
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "canvas_uniforms_inc.glsl"
@@ -228,11 +224,11 @@ layout(location = 3) in vec2 pixel_size_interp;
layout(location = 0) out vec4 frag_color;
-#ifdef USE_MATERIAL_UNIFORMS
+#ifdef MATERIAL_UNIFORMS_USED
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
- /* clang-format off */
-MATERIAL_UNIFORMS
- /* clang-format on */
+
+#MATERIAL_UNIFORMS
+
} material;
#endif
@@ -260,11 +256,9 @@ vec2 sdf_to_screen_uv(vec2 p_sdf) {
return p_sdf * canvas_data.sdf_to_screen;
}
-/* clang-format off */
-FRAGMENT_SHADER_GLOBALS
-/* clang-format on */
+#GLOBALS
-#ifdef LIGHT_SHADER_CODE_USED
+#ifdef LIGHT_CODE_USED
vec4 light_compute(
vec3 light_vertex,
@@ -278,9 +272,9 @@ vec4 light_compute(
vec2 uv,
vec4 color, bool is_directional) {
vec4 light = vec4(0.0);
- /* clang-format off */
-LIGHT_SHADER_CODE
- /* clang-format on */
+
+#CODE : LIGHT
+
return light;
}
@@ -356,7 +350,7 @@ vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 lig
//float distance = length(shadow_pos);
vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
-#ifdef LIGHT_SHADER_CODE_USED
+#ifdef LIGHT_CODE_USED
,
vec3 shadow_modulate
#endif
@@ -395,7 +389,7 @@ vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
}
vec4 shadow_color = unpackUnorm4x8(light_array.data[light_base].shadow_color);
-#ifdef LIGHT_SHADER_CODE_USED
+#ifdef LIGHT_CODE_USED
shadow_color.rgb *= shadow_modulate;
#endif
@@ -504,11 +498,7 @@ void main() {
normal_used = true;
#endif
- /* clang-format off */
-
-FRAGMENT_SHADER_CODE
-
- /* clang-format on */
+#CODE : FRAGMENT
#if defined(NORMAL_MAP_USED)
normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_map_depth);
@@ -543,7 +533,7 @@ FRAGMENT_SHADER_CODE
vec2 direction = light_array.data[light_base].position;
vec4 light_color = light_array.data[light_base].color;
-#ifdef LIGHT_SHADER_CODE_USED
+#ifdef LIGHT_CODE_USED
vec4 shadow_modulate = vec4(1.0);
light_color = light_compute(light_vertex, vec3(direction, light_array.data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, true);
@@ -561,7 +551,7 @@ FRAGMENT_SHADER_CODE
vec4 shadow_uv = vec4(shadow_pos.x, light_array.data[light_base].shadow_y_ofs, shadow_pos.y * light_array.data[light_base].shadow_zfar_inv, 1.0);
light_color = light_shadow_compute(light_base, light_color, shadow_uv
-#ifdef LIGHT_SHADER_CODE_USED
+#ifdef LIGHT_CODE_USED
,
shadow_modulate.rgb
#endif
@@ -599,7 +589,7 @@ FRAGMENT_SHADER_CODE
vec4 light_color = textureLod(sampler2D(atlas_texture, texture_sampler), tex_uv_atlas, 0.0);
vec4 light_base_color = light_array.data[light_base].color;
-#ifdef LIGHT_SHADER_CODE_USED
+#ifdef LIGHT_CODE_USED
vec4 shadow_modulate = vec4(1.0);
vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
@@ -657,7 +647,7 @@ FRAGMENT_SHADER_CODE
vec4 shadow_uv = vec4(tex_ofs, light_array.data[light_base].shadow_y_ofs, distance, 1.0);
light_color = light_shadow_compute(light_base, light_color, shadow_uv
-#ifdef LIGHT_SHADER_CODE_USED
+#ifdef LIGHT_CODE_USED
,
shadow_modulate.rgb
#endif
diff --git a/servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl b/servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl
index 5c25235c58..9f89f4b3b7 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) in highp vec3 vertex;
@@ -32,7 +32,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(push_constant, binding = 0, std430) uniform Constants {
mat4 projection;
diff --git a/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl b/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl
index 302ad03b41..65a554e839 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/cluster_debug.glsl b/servers/rendering/renderer_rd/shaders/cluster_debug.glsl
index 70a875192c..40da2c6e5c 100644
--- a/servers/rendering/renderer_rd/shaders/cluster_debug.glsl
+++ b/servers/rendering/renderer_rd/shaders/cluster_debug.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/cluster_render.glsl b/servers/rendering/renderer_rd/shaders/cluster_render.glsl
index ca92d2104e..da7d189281 100644
--- a/servers/rendering/renderer_rd/shaders/cluster_render.glsl
+++ b/servers/rendering/renderer_rd/shaders/cluster_render.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) in vec3 vertex_attrib;
@@ -63,7 +63,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic) && defined(has_GL_KHR_shader_subgroup_vote)
diff --git a/servers/rendering/renderer_rd/shaders/cluster_store.glsl b/servers/rendering/renderer_rd/shaders/cluster_store.glsl
index 5be0893c4f..b0606efa94 100644
--- a/servers/rendering/renderer_rd/shaders/cluster_store.glsl
+++ b/servers/rendering/renderer_rd/shaders/cluster_store.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/copy.glsl b/servers/rendering/renderer_rd/shaders/copy.glsl
index cdd35dfb3f..4110a95ddb 100644
--- a/servers/rendering/renderer_rd/shaders/copy.glsl
+++ b/servers/rendering/renderer_rd/shaders/copy.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/copy_to_fb.glsl b/servers/rendering/renderer_rd/shaders/copy_to_fb.glsl
index 9751e13b4e..8c68e2dc2f 100644
--- a/servers/rendering/renderer_rd/shaders/copy_to_fb.glsl
+++ b/servers/rendering/renderer_rd/shaders/copy_to_fb.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
@@ -37,7 +37,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(push_constant, binding = 1, std430) uniform Params {
vec4 section;
diff --git a/servers/rendering/renderer_rd/shaders/cube_to_dp.glsl b/servers/rendering/renderer_rd/shaders/cube_to_dp.glsl
index c3ac0bee57..dfbce29119 100644
--- a/servers/rendering/renderer_rd/shaders/cube_to_dp.glsl
+++ b/servers/rendering/renderer_rd/shaders/cube_to_dp.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(push_constant, binding = 1, std430) uniform Params {
float z_far;
@@ -26,7 +26,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) in vec2 uv_interp;
diff --git a/servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl b/servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl
index 7f269b7af3..9fa84657d1 100644
--- a/servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl
+++ b/servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl
@@ -22,7 +22,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#define BLOCK_SIZE 8
diff --git a/servers/rendering/renderer_rd/shaders/cubemap_filter.glsl b/servers/rendering/renderer_rd/shaders/cubemap_filter.glsl
index 987545fb76..2a774b0eb4 100644
--- a/servers/rendering/renderer_rd/shaders/cubemap_filter.glsl
+++ b/servers/rendering/renderer_rd/shaders/cubemap_filter.glsl
@@ -22,7 +22,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#define GROUP_SIZE 64
diff --git a/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl b/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl
index 5cbb00baa4..ce7c03c1d4 100644
--- a/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl
+++ b/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#define GROUP_SIZE 8
diff --git a/servers/rendering/renderer_rd/shaders/gi.glsl b/servers/rendering/renderer_rd/shaders/gi.glsl
index 92a5682572..bfd5c4c88d 100644
--- a/servers/rendering/renderer_rd/shaders/gi.glsl
+++ b/servers/rendering/renderer_rd/shaders/gi.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/giprobe.glsl b/servers/rendering/renderer_rd/shaders/giprobe.glsl
index b931461b31..49a493cdc7 100644
--- a/servers/rendering/renderer_rd/shaders/giprobe.glsl
+++ b/servers/rendering/renderer_rd/shaders/giprobe.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#ifdef MODE_DYNAMIC
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/giprobe_debug.glsl b/servers/rendering/renderer_rd/shaders/giprobe_debug.glsl
index 515cc35507..7d4d72967a 100644
--- a/servers/rendering/renderer_rd/shaders/giprobe_debug.glsl
+++ b/servers/rendering/renderer_rd/shaders/giprobe_debug.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
struct CellData {
uint position; // xyz 10 bits
@@ -172,7 +172,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) in vec4 color_interp;
layout(location = 0) out vec4 frag_color;
diff --git a/servers/rendering/renderer_rd/shaders/giprobe_sdf.glsl b/servers/rendering/renderer_rd/shaders/giprobe_sdf.glsl
index 5b3dec0ee7..e20b3f680d 100644
--- a/servers/rendering/renderer_rd/shaders/giprobe_sdf.glsl
+++ b/servers/rendering/renderer_rd/shaders/giprobe_sdf.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
diff --git a/servers/rendering/renderer_rd/shaders/giprobe_write.glsl b/servers/rendering/renderer_rd/shaders/giprobe_write.glsl
index 56b3b7ccb4..5dc2d08a3b 100644
--- a/servers/rendering/renderer_rd/shaders/giprobe_write.glsl
+++ b/servers/rendering/renderer_rd/shaders/giprobe_write.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl b/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl
index 8a11c35b78..466442b67a 100644
--- a/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl
+++ b/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#define BLOCK_SIZE 8
diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl
index cb6d8dc7f6..3712220dc4 100644
--- a/servers/rendering/renderer_rd/shaders/particles.glsl
+++ b/servers/rendering/renderer_rd/shaders/particles.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
@@ -146,11 +146,11 @@ layout(set = 2, binding = 1) uniform texture2D height_field_texture;
/* SET 3: MATERIAL */
-#ifdef USE_MATERIAL_UNIFORMS
+#ifdef MATERIAL_UNIFORMS_USED
layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
- /* clang-format off */
-MATERIAL_UNIFORMS
- /* clang-format on */
+
+#MATERIAL_UNIFORMS
+
} material;
#endif
@@ -196,11 +196,7 @@ bool emit_subparticle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom
return true;
}
-/* clang-format off */
-
-COMPUTE_SHADER_GLOBALS
-
-/* clang-format on */
+#GLOBALS
void main() {
uint particle = gl_GlobalInvocationID.x;
@@ -256,6 +252,115 @@ void main() {
/* Process physics if active */
+ if (params.sub_emitter_mode) {
+ if (!PARTICLE.is_active) {
+ int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
+
+ if (src_index >= 0) {
+ PARTICLE.is_active = true;
+ restart = true;
+
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
+ PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
+ } else {
+ PARTICLE.xform[3] = vec4(0, 0, 0, 1);
+ restart_position = true;
+ }
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
+ PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
+ PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
+ PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
+ } else {
+ PARTICLE.xform[0] = vec4(1, 0, 0, 0);
+ PARTICLE.xform[1] = vec4(0, 1, 0, 0);
+ PARTICLE.xform[2] = vec4(0, 0, 1, 0);
+ restart_rotation_scale = true;
+ }
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
+ PARTICLE.velocity = src_particles.data[src_index].velocity;
+ } else {
+ PARTICLE.velocity = vec3(0);
+ restart_velocity = true;
+ }
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
+ PARTICLE.color = src_particles.data[src_index].color;
+ } else {
+ PARTICLE.color = vec4(1);
+ restart_color = true;
+ }
+
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
+ PARTICLE.custom = src_particles.data[src_index].custom;
+ } else {
+ PARTICLE.custom = vec4(0);
+ restart_custom = true;
+ }
+ }
+ }
+
+ } else if (FRAME.emitting) {
+ float restart_phase = float(index) / float(params.total_particles);
+
+ if (FRAME.randomness > 0.0) {
+ uint seed = FRAME.cycle;
+ if (restart_phase >= FRAME.system_phase) {
+ seed -= uint(1);
+ }
+ seed *= uint(params.total_particles);
+ seed += uint(index);
+ float random = float(hash(seed) % uint(65536)) / 65536.0;
+ restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
+ }
+
+ restart_phase *= (1.0 - FRAME.explosiveness);
+
+ if (FRAME.system_phase > FRAME.prev_system_phase) {
+ // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
+
+ if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
+ restart = true;
+ if (params.use_fractional_delta) {
+ local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
+ }
+ }
+
+ } else if (FRAME.delta > 0.0) {
+ if (restart_phase >= FRAME.prev_system_phase) {
+ restart = true;
+ if (params.use_fractional_delta) {
+ local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
+ }
+
+ } else if (restart_phase < FRAME.system_phase) {
+ restart = true;
+ if (params.use_fractional_delta) {
+ local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
+ }
+ }
+ }
+
+ uint current_cycle = FRAME.cycle;
+
+ if (FRAME.system_phase < restart_phase) {
+ current_cycle -= uint(1);
+ }
+
+ uint particle_number = current_cycle * uint(params.total_particles) + particle;
+
+ if (restart) {
+ PARTICLE.is_active = FRAME.emitting;
+ restart_position = true;
+ restart_rotation_scale = true;
+ restart_velocity = true;
+ restart_color = true;
+ restart_custom = true;
+ }
+ }
+
+ if (restart && PARTICLE.is_active) {
+#CODE : START
+ }
+
if (PARTICLE.is_active) {
for (uint i = 0; i < FRAME.attractor_count; i++) {
vec3 dir;
@@ -434,116 +539,7 @@ void main() {
}
}
- if (params.sub_emitter_mode) {
- if (!PARTICLE.is_active) {
- int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
-
- if (src_index >= 0) {
- PARTICLE.is_active = true;
- restart = true;
-
- if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
- PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
- } else {
- PARTICLE.xform[3] = vec4(0, 0, 0, 1);
- restart_position = true;
- }
- if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
- PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
- PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
- PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
- } else {
- PARTICLE.xform[0] = vec4(1, 0, 0, 0);
- PARTICLE.xform[1] = vec4(0, 1, 0, 0);
- PARTICLE.xform[2] = vec4(0, 0, 1, 0);
- restart_rotation_scale = true;
- }
- if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
- PARTICLE.velocity = src_particles.data[src_index].velocity;
- } else {
- PARTICLE.velocity = vec3(0);
- restart_velocity = true;
- }
- if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
- PARTICLE.color = src_particles.data[src_index].color;
- } else {
- PARTICLE.color = vec4(1);
- restart_color = true;
- }
-
- if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
- PARTICLE.custom = src_particles.data[src_index].custom;
- } else {
- PARTICLE.custom = vec4(0);
- restart_custom = true;
- }
- }
- }
-
- } else if (FRAME.emitting) {
- float restart_phase = float(index) / float(params.total_particles);
-
- if (FRAME.randomness > 0.0) {
- uint seed = FRAME.cycle;
- if (restart_phase >= FRAME.system_phase) {
- seed -= uint(1);
- }
- seed *= uint(params.total_particles);
- seed += uint(index);
- float random = float(hash(seed) % uint(65536)) / 65536.0;
- restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
- }
-
- restart_phase *= (1.0 - FRAME.explosiveness);
-
- if (FRAME.system_phase > FRAME.prev_system_phase) {
- // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
-
- if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
- restart = true;
- if (params.use_fractional_delta) {
- local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
- }
- }
-
- } else if (FRAME.delta > 0.0) {
- if (restart_phase >= FRAME.prev_system_phase) {
- restart = true;
- if (params.use_fractional_delta) {
- local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
- }
-
- } else if (restart_phase < FRAME.system_phase) {
- restart = true;
- if (params.use_fractional_delta) {
- local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
- }
- }
- }
-
- uint current_cycle = FRAME.cycle;
-
- if (FRAME.system_phase < restart_phase) {
- current_cycle -= uint(1);
- }
-
- uint particle_number = current_cycle * uint(params.total_particles) + particle;
-
- if (restart) {
- PARTICLE.is_active = FRAME.emitting;
- restart_position = true;
- restart_rotation_scale = true;
- restart_velocity = true;
- restart_color = true;
- restart_custom = true;
- }
- }
-
if (PARTICLE.is_active) {
- /* clang-format off */
-
-COMPUTE_SHADER_CODE
-
- /* clang-format on */
+#CODE : PROCESS
}
}
diff --git a/servers/rendering/renderer_rd/shaders/particles_copy.glsl b/servers/rendering/renderer_rd/shaders/particles_copy.glsl
index 6c782b6045..80adb49619 100644
--- a/servers/rendering/renderer_rd/shaders/particles_copy.glsl
+++ b/servers/rendering/renderer_rd/shaders/particles_copy.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/resolve.glsl b/servers/rendering/renderer_rd/shaders/resolve.glsl
index e83c4ca93b..2286a26485 100644
--- a/servers/rendering/renderer_rd/shaders/resolve.glsl
+++ b/servers/rendering/renderer_rd/shaders/resolve.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/roughness_limiter.glsl b/servers/rendering/renderer_rd/shaders/roughness_limiter.glsl
index 464895928a..7b964675ca 100644
--- a/servers/rendering/renderer_rd/shaders/roughness_limiter.glsl
+++ b/servers/rendering/renderer_rd/shaders/roughness_limiter.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
index 76edec1cb6..fce17c47e8 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "scene_forward_clustered_inc.glsl"
@@ -81,11 +81,11 @@ layout(location = 5) out vec3 tangent_interp;
layout(location = 6) out vec3 binormal_interp;
#endif
-#ifdef USE_MATERIAL_UNIFORMS
+#ifdef MATERIAL_UNIFORMS_USED
layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms{
- /* clang-format off */
-MATERIAL_UNIFORMS
- /* clang-format on */
+
+#MATERIAL_UNIFORMS
+
} material;
#endif
@@ -99,11 +99,7 @@ layout(location = 8) out float dp_clip;
layout(location = 9) out flat uint instance_index;
-/* clang-format off */
-
-VERTEX_SHADER_GLOBALS
-
-/* clang-format on */
+#GLOBALS
void main() {
vec4 instance_custom = vec4(0.0);
@@ -230,11 +226,7 @@ void main() {
mat3 modelview_normal = mat3(scene_data.inv_camera_matrix) * world_normal_matrix;
{
- /* clang-format off */
-
-VERTEX_SHADER_CODE
-
- /* clang-format on */
+#CODE : VERTEX
}
// using local coordinates (default)
@@ -325,7 +317,7 @@ VERTEX_SHADER_CODE
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "scene_forward_clustered_inc.glsl"
@@ -372,19 +364,15 @@ layout(location = 9) in flat uint instance_index;
#define LIGHT_TRANSMITTANCE_USED
#endif
-#ifdef USE_MATERIAL_UNIFORMS
+#ifdef MATERIAL_UNIFORMS_USED
layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms{
- /* clang-format off */
-MATERIAL_UNIFORMS
- /* clang-format on */
-} material;
-#endif
-/* clang-format off */
+#MATERIAL_UNIFORMS
-FRAGMENT_SHADER_GLOBALS
+} material;
+#endif
-/* clang-format on */
+#GLOBALS
#ifdef MODE_RENDER_DEPTH
@@ -581,18 +569,14 @@ void light_compute(vec3 N, vec3 L, vec3 V, vec3 light_color, float attenuation,
#endif
inout vec3 diffuse_light, inout vec3 specular_light) {
-#if defined(USE_LIGHT_SHADER_CODE)
+#if defined(LIGHT_CODE_USED)
// light is written by the light shader
vec3 normal = N;
vec3 light = L;
vec3 view = V;
- /* clang-format off */
-
-LIGHT_SHADER_CODE
-
- /* clang-format on */
+#CODE : LIGHT
#else
@@ -794,7 +778,7 @@ LIGHT_SHADER_CODE
alpha = min(alpha, clamp(1.0 - attenuation), 0.0, 1.0));
#endif
-#endif //defined(USE_LIGHT_SHADER_CODE)
+#endif //defined(LIGHT_CODE_USED)
}
#ifndef USE_NO_SHADOWS
@@ -1925,11 +1909,7 @@ void main() {
#endif // ALPHA_ANTIALIASING_EDGE_USED
{
- /* clang-format off */
-
-FRAGMENT_SHADER_CODE
-
- /* clang-format on */
+#CODE : FRAGMENT
}
#ifdef LIGHT_TRANSMITTANCE_USED
diff --git a/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl
index 06dc4b13de..78e0a85341 100644
--- a/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl
+++ b/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/screen_space_reflection_filter.glsl b/servers/rendering/renderer_rd/shaders/screen_space_reflection_filter.glsl
index a5afe74cb2..62d1cffb0a 100644
--- a/servers/rendering/renderer_rd/shaders/screen_space_reflection_filter.glsl
+++ b/servers/rendering/renderer_rd/shaders/screen_space_reflection_filter.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/screen_space_reflection_scale.glsl b/servers/rendering/renderer_rd/shaders/screen_space_reflection_scale.glsl
index 218605a962..7e06516d90 100644
--- a/servers/rendering/renderer_rd/shaders/screen_space_reflection_scale.glsl
+++ b/servers/rendering/renderer_rd/shaders/screen_space_reflection_scale.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl b/servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl
index e4c3f3a84b..8b58796962 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl
+++ b/servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl b/servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl
index 08da283dad..0eacbc5363 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl
+++ b/servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#define MAX_CASCADES 8
@@ -153,7 +153,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) out vec4 frag_color;
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl b/servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl
index dc7238abed..99db35bb34 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl
+++ b/servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_fields.glsl b/servers/rendering/renderer_rd/shaders/sdfgi_fields.glsl
deleted file mode 100644
index 69d8824d8a..0000000000
--- a/servers/rendering/renderer_rd/shaders/sdfgi_fields.glsl
+++ /dev/null
@@ -1,182 +0,0 @@
-/* clang-format off */
-[compute]
-
-#version 450
-
-VERSION_DEFINES
-
-layout(local_size_x = OCT_RES, local_size_y = OCT_RES, local_size_z = 1) in;
-
-/* clang-format on */
-
-#define MAX_CASCADES 8
-
-layout(rgba16f, set = 0, binding = 1) uniform restrict image2DArray irradiance_texture;
-layout(rg16f, set = 0, binding = 2) uniform restrict image2DArray depth_texture;
-
-layout(rgba32ui, set = 0, binding = 3) uniform restrict uimage2DArray irradiance_history_texture;
-layout(rg32ui, set = 0, binding = 4) uniform restrict uimage2DArray depth_history_texture;
-
-struct CascadeData {
- vec3 offset; //offset of (0,0,0) in world coordinates
- float to_cell; // 1/bounds * grid_size
-};
-
-layout(set = 0, binding = 5, std140) uniform Cascades {
- CascadeData data[MAX_CASCADES];
-}
-cascades;
-
-#define DEPTH_HISTORY_BITS 24
-#define IRRADIANCE_HISTORY_BITS 16
-
-layout(push_constant, binding = 0, std430) uniform Params {
- vec3 grid_size;
- uint max_cascades;
-
- uint probe_axis_size;
- uint cascade;
- uint history_size;
- uint pad0;
-
- ivec3 scroll; //scroll in probes
- uint pad1;
-}
-params;
-
-void main() {
- ivec2 local = ivec2(gl_LocalInvocationID.xy);
- ivec2 probe = ivec2(gl_WorkGroupID.xy);
-
- ivec3 probe_cell;
- probe_cell.x = probe.x % int(params.probe_axis_size);
- probe_cell.y = probe.y;
- probe_cell.z = probe.x / int(params.probe_axis_size);
-
-#ifdef MODE_SCROLL_BEGIN
-
- ivec3 read_cell = probe_cell - params.scroll;
-
- uint src_layer = (params.history_size + 1) * params.cascade;
- uint dst_layer = (params.history_size + 1) * params.max_cascades;
-
- for (uint i = 0; i <= params.history_size; i++) {
- ivec3 write_pos = ivec3(probe * OCT_RES + local, int(i));
-
- if (any(lessThan(read_pos, ivec3(0))) || any(greaterThanEqual(read_pos, ivec3(params.probe_axis_size)))) {
- // nowhere to read from for scrolling, try finding the value from upper probes
-
-#ifdef MODE_IRRADIANCE
- imageStore(irradiance_history_texture, write_pos, uvec4(0));
-#endif
-#ifdef MODE_DEPTH
- imageStore(depth_history_texture, write_pos, uvec4(0));
-#endif
- } else {
- ivec3 read_pos;
- read_pos.xy = read_cell.xy;
- read_pos.x += read_cell.z * params.probe_axis_size;
- read_pos.xy = read_pos.xy * OCT_RES + local;
- read_pos.z = int(i);
-
-#ifdef MODE_IRRADIANCE
- uvec4 value = imageLoad(irradiance_history_texture, read_pos);
- imageStore(irradiance_history_texture, write_pos, value);
-#endif
-#ifdef MODE_DEPTH
- uvec2 value = imageLoad(depth_history_texture, read_pos);
- imageStore(depth_history_texture, write_pos, value);
-#endif
- }
- }
-
-#endif // MODE_SCROLL_BEGIN
-
-#ifdef MODE_SCROLL_END
-
- uint src_layer = (params.history_size + 1) * params.max_cascades;
- uint dst_layer = (params.history_size + 1) * params.cascade;
-
- for (uint i = 0; i <= params.history_size; i++) {
- ivec3 pos = ivec3(probe * OCT_RES + local, int(i));
-
-#ifdef MODE_IRRADIANCE
- uvec4 value = imageLoad(irradiance_history_texture, read_pos);
- imageStore(irradiance_history_texture, write_pos, value);
-#endif
-#ifdef MODE_DEPTH
- uvec2 value = imageLoad(depth_history_texture, read_pos);
- imageStore(depth_history_texture, write_pos, value);
-#endif
- }
-
-#endif //MODE_SCROLL_END
-
-#ifdef MODE_STORE
-
- uint src_layer = (params.history_size + 1) * params.cascade + params.history_size;
- ivec3 read_pos = ivec3(probe * OCT_RES + local, int(src_layer));
-
- ivec3 write_pos = ivec3(probe * (OCT_RES + 2) + ivec2(1), int(params.cascade));
-
- ivec3 copy_to[4] = ivec3[](write_pos, ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2));
-
-#ifdef MODE_IRRADIANCE
- uvec4 average = imageLoad(irradiance_history_texture, read_pos);
- vec4 light_accum = vec4(average / params.history_size) / float(1 << IRRADIANCE_HISTORY_BITS);
-
-#endif
-#ifdef MODE_DEPTH
- uvec2 value = imageLoad(depth_history_texture, read_pos);
- vec2 depth_accum = vec4(average / params.history_size) / float(1 << IRRADIANCE_HISTORY_BITS);
-
- float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
- float max_depth = length(params.grid_size / cascades.data[params.max_cascades - 1].to_cell);
- max_depth /= probe_cell_size;
-
- depth_value = (vec2(average / params.history_size) / float(1 << DEPTH_HISTORY_BITS)) * vec2(max_depth, max_depth * max_depth);
-
-#endif
-
- /* Fill the border if required */
-
- if (local == ivec2(0, 0)) {
- copy_to[1] = texture_pos + ivec3(OCT_RES - 1, -1, 0);
- copy_to[2] = texture_pos + ivec3(-1, OCT_RES - 1, 0);
- copy_to[3] = texture_pos + ivec3(OCT_RES, OCT_RES, 0);
- } else if (local == ivec2(OCT_RES - 1, 0)) {
- copy_to[1] = texture_pos + ivec3(0, -1, 0);
- copy_to[2] = texture_pos + ivec3(OCT_RES, OCT_RES - 1, 0);
- copy_to[3] = texture_pos + ivec3(-1, OCT_RES, 0);
- } else if (local == ivec2(0, OCT_RES - 1)) {
- copy_to[1] = texture_pos + ivec3(-1, 0, 0);
- copy_to[2] = texture_pos + ivec3(OCT_RES - 1, OCT_RES, 0);
- copy_to[3] = texture_pos + ivec3(OCT_RES, -1, 0);
- } else if (local == ivec2(OCT_RES - 1, OCT_RES - 1)) {
- copy_to[1] = texture_pos + ivec3(0, OCT_RES, 0);
- copy_to[2] = texture_pos + ivec3(OCT_RES, 0, 0);
- copy_to[3] = texture_pos + ivec3(-1, -1, 0);
- } else if (local.y == 0) {
- copy_to[1] = texture_pos + ivec3(OCT_RES - local.x - 1, local.y - 1, 0);
- } else if (local.x == 0) {
- copy_to[1] = texture_pos + ivec3(local.x - 1, OCT_RES - local.y - 1, 0);
- } else if (local.y == OCT_RES - 1) {
- copy_to[1] = texture_pos + ivec3(OCT_RES - local.x - 1, local.y + 1, 0);
- } else if (local.x == OCT_RES - 1) {
- copy_to[1] = texture_pos + ivec3(local.x + 1, OCT_RES - local.y - 1, 0);
- }
-
- for (int i = 0; i < 4; i++) {
- if (copy_to[i] == ivec3(-2, -2, -2)) {
- continue;
- }
-#ifdef MODE_IRRADIANCE
- imageStore(irradiance_texture, copy_to[i], light_accum);
-#endif
-#ifdef MODE_DEPTH
- imageStore(depth_texture, copy_to[i], vec4(depth_value, 0.0, 0.0));
-#endif
- }
-
-#endif // MODE_STORE
-}
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl b/servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl
index 007e4c113a..bc376e9522 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl
+++ b/servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl b/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl
index 916c60ac89..aa4ded146f 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl
+++ b/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#ifdef MODE_JUMPFLOOD_OPTIMIZED
#define GROUP_SIZE 8
diff --git a/servers/rendering/renderer_rd/shaders/skeleton.glsl b/servers/rendering/renderer_rd/shaders/skeleton.glsl
index 680d1045cd..669ffc961d 100644
--- a/servers/rendering/renderer_rd/shaders/skeleton.glsl
+++ b/servers/rendering/renderer_rd/shaders/skeleton.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/sky.glsl b/servers/rendering/renderer_rd/shaders/sky.glsl
index 6c985e1f5c..9924da37d5 100644
--- a/servers/rendering/renderer_rd/shaders/sky.glsl
+++ b/servers/rendering/renderer_rd/shaders/sky.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
@@ -24,7 +24,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#define M_PI 3.14159265359
@@ -88,13 +88,9 @@ layout(set = 0, binding = 3, std140) uniform DirectionalLights {
directional_lights;
-#ifdef USE_MATERIAL_UNIFORMS
+#ifdef MATERIAL_UNIFORMS_USED
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
- /* clang-format off */
-
-MATERIAL_UNIFORMS
-
- /* clang-format on */
+#MATERIAL_UNIFORMS
} material;
#endif
@@ -127,11 +123,7 @@ layout(set = 3, binding = 0) uniform texture3D volumetric_fog_texture;
#define AT_QUARTER_RES_PASS false
#endif
-/* clang-format off */
-
-FRAGMENT_SHADER_GLOBALS
-
-/* clang-format on */
+#GLOBALS
layout(location = 0) out vec4 frag_color;
@@ -202,22 +194,10 @@ void main() {
#endif
#endif
-// unused, just here to make our compiler happy, make sure we don't execute any light code the user adds in..
-#ifndef REALLYINCLUDETHIS
- {
- /* clang-format off */
-
-LIGHT_SHADER_CODE
-
- /* clang-format on */
- }
-#endif
{
- /* clang-format off */
-FRAGMENT_SHADER_CODE
+#CODE : SKY
- /* clang-format on */
}
frag_color.rgb = color * params.position_multiplier.w;
diff --git a/servers/rendering/renderer_rd/shaders/sort.glsl b/servers/rendering/renderer_rd/shaders/sort.glsl
index e5ebb9c64b..307e60dc21 100644
--- a/servers/rendering/renderer_rd/shaders/sort.glsl
+++ b/servers/rendering/renderer_rd/shaders/sort.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
// Original version here:
// https://github.com/GPUOpen-LibrariesAndSDKs/GPUParticles11/blob/master/gpuparticles11/src/Shaders
diff --git a/servers/rendering/renderer_rd/shaders/specular_merge.glsl b/servers/rendering/renderer_rd/shaders/specular_merge.glsl
index 0b8f406213..3579c35cce 100644
--- a/servers/rendering/renderer_rd/shaders/specular_merge.glsl
+++ b/servers/rendering/renderer_rd/shaders/specular_merge.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
@@ -17,7 +17,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) in vec2 uv_interp;
diff --git a/servers/rendering/renderer_rd/shaders/ssao.glsl b/servers/rendering/renderer_rd/shaders/ssao.glsl
index 231f8f91ec..6e945edfcd 100644
--- a/servers/rendering/renderer_rd/shaders/ssao.glsl
+++ b/servers/rendering/renderer_rd/shaders/ssao.glsl
@@ -21,7 +21,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#define SSAO_ADAPTIVE_TAP_BASE_COUNT 5
diff --git a/servers/rendering/renderer_rd/shaders/ssao_blur.glsl b/servers/rendering/renderer_rd/shaders/ssao_blur.glsl
index 510a777048..d9cd2b4e85 100644
--- a/servers/rendering/renderer_rd/shaders/ssao_blur.glsl
+++ b/servers/rendering/renderer_rd/shaders/ssao_blur.glsl
@@ -21,7 +21,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/ssao_downsample.glsl b/servers/rendering/renderer_rd/shaders/ssao_downsample.glsl
index cb2d31f70d..ee0db6a6f0 100644
--- a/servers/rendering/renderer_rd/shaders/ssao_downsample.glsl
+++ b/servers/rendering/renderer_rd/shaders/ssao_downsample.glsl
@@ -21,7 +21,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl b/servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl
index 6aa7624261..687fe1e6e2 100644
--- a/servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl
+++ b/servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl
@@ -21,7 +21,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/ssao_interleave.glsl b/servers/rendering/renderer_rd/shaders/ssao_interleave.glsl
index 4fdf334aa5..0907423d5d 100644
--- a/servers/rendering/renderer_rd/shaders/ssao_interleave.glsl
+++ b/servers/rendering/renderer_rd/shaders/ssao_interleave.glsl
@@ -20,7 +20,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl b/servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl
index 88a953562f..9367b641c2 100644
--- a/servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl
+++ b/servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
diff --git a/servers/rendering/renderer_rd/shaders/tonemap.glsl b/servers/rendering/renderer_rd/shaders/tonemap.glsl
index 7de91fd541..86b4da6b08 100644
--- a/servers/rendering/renderer_rd/shaders/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/tonemap.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
@@ -16,7 +16,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
layout(location = 0) in vec2 uv_interp;
diff --git a/servers/rendering/renderer_rd/shaders/volumetric_fog.glsl b/servers/rendering/renderer_rd/shaders/volumetric_fog.glsl
index ce8a459b24..cace607667 100644
--- a/servers/rendering/renderer_rd/shaders/volumetric_fog.glsl
+++ b/servers/rendering/renderer_rd/shaders/volumetric_fog.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
/* Do not use subgroups here, seems there is not much advantage and causes glitches
#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic)
diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h
index b546001843..551d4f4240 100644
--- a/servers/rendering/renderer_scene.h
+++ b/servers/rendering/renderer_scene.h
@@ -69,7 +69,7 @@ public:
virtual void instance_set_transform(RID p_instance, const Transform &p_transform) = 0;
virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0;
virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) = 0;
- virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material) = 0;
+ virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) = 0;
virtual void instance_set_visible(RID p_instance, bool p_visible) = 0;
virtual void instance_set_custom_aabb(RID p_instance, AABB p_aabb) = 0;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index e8155e4025..2c865186b4 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -752,7 +752,7 @@ void RendererSceneCull::instance_set_blend_shape_weight(RID p_instance, int p_sh
}
}
-void RendererSceneCull::instance_set_surface_material(RID p_instance, int p_surface, RID p_material) {
+void RendererSceneCull::instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) {
Instance *instance = instance_owner.getornull(p_instance);
ERR_FAIL_COND(!instance);
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 32f4334288..d7d59665ec 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -840,7 +840,7 @@ public:
virtual void instance_set_transform(RID p_instance, const Transform &p_transform);
virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id);
virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight);
- virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material);
+ virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material);
virtual void instance_set_visible(RID p_instance, bool p_visible);
virtual void instance_set_custom_aabb(RID p_instance, AABB p_aabb);
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index e82d5cc3f8..683a22fd9a 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -703,7 +703,7 @@ public:
FUNC2(instance_set_transform, RID, const Transform &)
FUNC2(instance_attach_object_instance_id, RID, ObjectID)
FUNC3(instance_set_blend_shape_weight, RID, int, float)
- FUNC3(instance_set_surface_material, RID, int, RID)
+ FUNC3(instance_set_surface_override_material, RID, int, RID)
FUNC2(instance_set_visible, RID, bool)
FUNC2(instance_set_custom_aabb, RID, AABB)
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 4ae0eda232..f5228f9747 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -3109,20 +3109,20 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St
}
switch (p_varying.stage) {
case ShaderNode::Varying::STAGE_UNKNOWN: // first assign
- if (current_function == String("vertex")) {
+ if (current_function == varying_function_names.vertex) {
p_varying.stage = ShaderNode::Varying::STAGE_VERTEX;
- } else if (current_function == String("fragment")) {
+ } else if (current_function == varying_function_names.fragment) {
p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT;
}
break;
case ShaderNode::Varying::STAGE_VERTEX:
- if (current_function == String("fragment")) {
+ if (current_function == varying_function_names.fragment) {
*r_message = RTR("Varyings which assigned in 'vertex' function may not be reassigned in 'fragment' or 'light'.");
return false;
}
break;
case ShaderNode::Varying::STAGE_FRAGMENT:
- if (current_function == String("vertex")) {
+ if (current_function == varying_function_names.vertex) {
*r_message = RTR("Varyings which assigned in 'fragment' function may not be reassigned in 'vertex' or 'light'.");
return false;
}
@@ -3139,25 +3139,25 @@ bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, Str
*r_message = RTR("Varying must be assigned before using!");
return false;
case ShaderNode::Varying::STAGE_VERTEX:
- if (current_function == String("fragment")) {
+ if (current_function == varying_function_names.fragment) {
p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT;
- } else if (current_function == String("light")) {
+ } else if (current_function == varying_function_names.light) {
p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT;
}
break;
case ShaderNode::Varying::STAGE_FRAGMENT:
- if (current_function == String("light")) {
+ if (current_function == varying_function_names.light) {
p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT;
}
break;
case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT:
- if (current_function == String("light")) {
+ if (current_function == varying_function_names.light) {
*r_message = RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'");
return false;
}
break;
case ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT:
- if (current_function == String("fragment")) {
+ if (current_function == varying_function_names.fragment) {
*r_message = RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'");
return false;
}
@@ -5847,7 +5847,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
//check return type
BlockNode *b = p_block;
- if (b && b->parent_function && (b->parent_function->name == "vertex" || b->parent_function->name == "fragment" || b->parent_function->name == "light")) {
+ if (b && b->parent_function && p_function_info.main_function) {
_set_error(vformat("Using 'return' in '%s' processor function results in undefined behavior!", b->parent_function->name));
return ERR_PARSE_ERROR;
}
@@ -7246,26 +7246,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
bool ShaderLanguage::has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name) {
- if (p_functions.has("vertex")) {
- if (p_functions["vertex"].built_ins.has(p_name)) {
- return true;
- }
- }
- if (p_functions.has("fragment")) {
- if (p_functions["fragment"].built_ins.has(p_name)) {
- return true;
- }
- }
- if (p_functions.has("light")) {
- if (p_functions["light"].built_ins.has(p_name)) {
- return true;
- }
- }
- if (p_functions.has("compute")) {
- if (p_functions["compute"].built_ins.has(p_name)) {
+ for (Map<StringName, ShaderLanguage::FunctionInfo>::Element *E = p_functions.front(); E; E = E->next()) {
+ if (E->get().built_ins.has(p_name)) {
return true;
}
}
+
return false;
}
@@ -7399,11 +7385,12 @@ String ShaderLanguage::get_shader_type(const String &p_code) {
return String();
}
-Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) {
+Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) {
clear();
code = p_code;
global_var_get_type_func = p_global_variable_type_func;
+ varying_function_names = p_varying_function_names;
nodes = nullptr;
@@ -7416,10 +7403,11 @@ Error ShaderLanguage::compile(const String &p_code, const Map<StringName, Functi
return OK;
}
-Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) {
+Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) {
clear();
code = p_code;
+ varying_function_names = p_varying_function_names;
nodes = nullptr;
global_var_get_type_func = p_global_variable_type_func;
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 14594b039c..03327f9677 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -331,6 +331,17 @@ public:
MAX_INSTANCE_UNIFORM_INDICES = 16
};
+ struct VaryingFunctionNames {
+ StringName fragment;
+ StringName vertex;
+ StringName light;
+ VaryingFunctionNames() {
+ fragment = "fragment";
+ vertex = "vertex";
+ light = "light";
+ }
+ };
+
struct Node {
Node *next = nullptr;
@@ -769,7 +780,8 @@ public:
Map<StringName, BuiltInInfo> built_ins;
Map<StringName, StageFunctionInfo> stage_functions;
- bool can_discard;
+ bool can_discard = false;
+ bool main_function = false;
};
static bool has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name);
@@ -796,6 +808,8 @@ private:
StringName current_function;
bool last_const = false;
+ VaryingFunctionNames varying_function_names;
+
TkPos _get_tkpos() {
TkPos tkp;
tkp.char_idx = char_idx;
@@ -898,8 +912,8 @@ public:
void clear();
static String get_shader_type(const String &p_code);
- Error compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func);
- Error complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint);
+ Error compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func);
+ Error complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint);
String get_error_text();
int get_error_line();
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index e99b8504bb..460fd5fc97 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -74,6 +74,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM2"] = ShaderLanguage::TYPE_VEC4;
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM3"] = ShaderLanguage::TYPE_VEC4;
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].can_discard = false;
+ shader_modes[RS::SHADER_SPATIAL].functions["vertex"].main_function = true;
//builtins
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["WORLD_MATRIX"] = ShaderLanguage::TYPE_MAT4;
@@ -139,6 +140,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["RADIANCE"] = ShaderLanguage::TYPE_VEC4;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["IRRADIANCE"] = ShaderLanguage::TYPE_VEC4;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].can_discard = true;
+ shader_modes[RS::SHADER_SPATIAL].functions["fragment"].main_function = true;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ALPHA_SCISSOR_THRESHOLD"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ALPHA_HASH_SCALE"] = ShaderLanguage::TYPE_FLOAT;
@@ -171,6 +173,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["light"].can_discard = true;
+ shader_modes[RS::SHADER_SPATIAL].functions["light"].main_function = true;
//order used puts first enum mode (default) first
shader_modes[RS::SHADER_SPATIAL].modes.push_back("blend_mix");
@@ -236,6 +239,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["AT_LIGHT_PASS"] = constt(ShaderLanguage::TYPE_BOOL);
shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["TEXTURE_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].can_discard = false;
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].main_function = true;
shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC2;
shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SHADOW_VERTEX"] = ShaderLanguage::TYPE_VEC2;
@@ -257,6 +261,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["AT_LIGHT_PASS"] = constt(ShaderLanguage::TYPE_BOOL);
shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SCREEN_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D);
shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].can_discard = true;
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].main_function = true;
{
ShaderLanguage::StageFunctionInfo func;
@@ -294,6 +299,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["TEXTURE_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].can_discard = true;
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].main_function = true;
shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("skip_vertex_transform");
@@ -310,34 +316,50 @@ ShaderTypes::ShaderTypes() {
/************ PARTICLES **************************/
shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4;
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3;
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["MASS"] = ShaderLanguage::TYPE_FLOAT;
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["ACTIVE"] = ShaderLanguage::TYPE_BOOL;
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["CUSTOM"] = ShaderLanguage::TYPE_VEC4;
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_POSITION"] = constt(ShaderLanguage::TYPE_UINT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_ROT_SCALE"] = constt(ShaderLanguage::TYPE_UINT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_VELOCITY"] = constt(ShaderLanguage::TYPE_UINT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_COLOR"] = constt(ShaderLanguage::TYPE_UINT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_CUSTOM"] = constt(ShaderLanguage::TYPE_UINT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_ROT_SCALE"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["COLLIDED"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["COLLISION_NORMAL"] = constt(ShaderLanguage::TYPE_VEC3);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["COLLISION_DEPTH"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["ATTRACTOR_FORCE"] = constt(ShaderLanguage::TYPE_VEC3);
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].can_discard = false;
+
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3;
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["MASS"] = ShaderLanguage::TYPE_FLOAT;
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["ACTIVE"] = ShaderLanguage::TYPE_BOOL;
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["CUSTOM"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_ROT_SCALE"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["start"].main_function = true;
+
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3;
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["MASS"] = ShaderLanguage::TYPE_FLOAT;
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["ACTIVE"] = ShaderLanguage::TYPE_BOOL;
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["RESTART"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["CUSTOM"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_POSITION"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_ROT_SCALE"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_VELOCITY"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_COLOR"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_CUSTOM"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLLIDED"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLLISION_NORMAL"] = constt(ShaderLanguage::TYPE_VEC3);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLLISION_DEPTH"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["ATTRACTOR_FORCE"] = constt(ShaderLanguage::TYPE_VEC3);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].main_function = true;
{
ShaderLanguage::StageFunctionInfo emit_vertex_func;
@@ -347,7 +369,7 @@ ShaderTypes::ShaderTypes() {
emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("custom", ShaderLanguage::TYPE_VEC4));
emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("flags", ShaderLanguage::TYPE_UINT));
emit_vertex_func.return_type = ShaderLanguage::TYPE_BOOL; //whether it could emit
- shader_modes[RS::SHADER_PARTICLES].functions["compute"].stage_functions["emit_subparticle"] = emit_vertex_func;
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].stage_functions["emit_subparticle"] = emit_vertex_func;
}
shader_modes[RS::SHADER_PARTICLES].modes.push_back("collision_use_scale");
@@ -384,14 +406,15 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT3_COLOR"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT3_SIZE"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC3;
- shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT;
- shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["EYEDIR"] = constt(ShaderLanguage::TYPE_VEC3);
- shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2);
- shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["SKY_COORDS"] = constt(ShaderLanguage::TYPE_VEC2);
- shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["HALF_RES_COLOR"] = constt(ShaderLanguage::TYPE_VEC4);
- shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["QUARTER_RES_COLOR"] = constt(ShaderLanguage::TYPE_VEC4);
- shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["FOG"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_SKY].functions["sky"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC3;
+ shader_modes[RS::SHADER_SKY].functions["sky"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT;
+ shader_modes[RS::SHADER_SKY].functions["sky"].built_ins["EYEDIR"] = constt(ShaderLanguage::TYPE_VEC3);
+ shader_modes[RS::SHADER_SKY].functions["sky"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2);
+ shader_modes[RS::SHADER_SKY].functions["sky"].built_ins["SKY_COORDS"] = constt(ShaderLanguage::TYPE_VEC2);
+ shader_modes[RS::SHADER_SKY].functions["sky"].built_ins["HALF_RES_COLOR"] = constt(ShaderLanguage::TYPE_VEC4);
+ shader_modes[RS::SHADER_SKY].functions["sky"].built_ins["QUARTER_RES_COLOR"] = constt(ShaderLanguage::TYPE_VEC4);
+ shader_modes[RS::SHADER_SKY].functions["sky"].built_ins["FOG"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[RS::SHADER_SKY].functions["sky"].main_function = true;
shader_modes[RS::SHADER_SKY].modes.push_back("use_half_res_pass");
shader_modes[RS::SHADER_SKY].modes.push_back("use_quarter_res_pass");
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 809343114c..f8644b5ecb 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -1706,7 +1706,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("instance_set_transform", "instance", "transform"), &RenderingServer::instance_set_transform);
ClassDB::bind_method(D_METHOD("instance_attach_object_instance_id", "instance", "id"), &RenderingServer::instance_attach_object_instance_id);
ClassDB::bind_method(D_METHOD("instance_set_blend_shape_weight", "instance", "shape", "weight"), &RenderingServer::instance_set_blend_shape_weight);
- ClassDB::bind_method(D_METHOD("instance_set_surface_material", "instance", "surface", "material"), &RenderingServer::instance_set_surface_material);
+ ClassDB::bind_method(D_METHOD("instance_set_surface_override_material", "instance", "surface", "material"), &RenderingServer::instance_set_surface_override_material);
ClassDB::bind_method(D_METHOD("instance_set_visible", "instance", "visible"), &RenderingServer::instance_set_visible);
// ClassDB::bind_method(D_METHOD("instance_set_use_lightmap", "instance", "lightmap_instance", "lightmap"), &RenderingServer::instance_set_use_lightmap);
ClassDB::bind_method(D_METHOD("instance_set_custom_aabb", "instance", "aabb"), &RenderingServer::instance_set_custom_aabb);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 6a8bb83ec1..694fae7fde 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1124,7 +1124,7 @@ public:
virtual void instance_set_transform(RID p_instance, const Transform &p_transform) = 0;
virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0;
virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) = 0;
- virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material) = 0;
+ virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) = 0;
virtual void instance_set_visible(RID p_instance, bool p_visible) = 0;
virtual void instance_set_custom_aabb(RID p_instance, AABB aabb) = 0;
diff --git a/tests/test_shader_lang.cpp b/tests/test_shader_lang.cpp
index a023f35506..2169350c02 100644
--- a/tests/test_shader_lang.cpp
+++ b/tests/test_shader_lang.cpp
@@ -344,7 +344,7 @@ MainLoop *test() {
Set<String> types;
types.insert("spatial");
- Error err = sl.compile(code, dt, rm, types, nullptr);
+ Error err = sl.compile(code, dt, rm, ShaderLanguage::VaryingFunctionNames(), types, nullptr);
if (err) {
print_line("Error at line: " + rtos(sl.get_error_line()) + ": " + sl.get_error_text());
diff --git a/thirdparty/README.md b/thirdparty/README.md
index cacc0275dd..33ce2423d9 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -89,17 +89,19 @@ will limit its functionality to IPv4 only.
## etcpak
- Upstream: https://github.com/wolfpld/etcpak
-- Version: git (403d38b3f1cb347c196d845d0a05e44a00d17169, 2021)
+- Version: git (f27daea656ff77671580f838a889e33049430ebd, 2021)
- License: BSD-3-Clause
-Important: Some Godot-made changes, see `patches` folders.
-
Files extracted from upstream source:
-- All `.cpp` and `.hpp` files in the root folder except `Application.cpp`.
-- `lz4` folder.
+- Only the files relevant for compression (i.e. `Process*.cpp` and their deps):
+ ```
+ Dither.{cpp,hpp} ForceInline.hpp Math.hpp ProcessCommon.hpp ProcessRGB.{cpp,hpp}
+ ProcessDxtc.{cpp,hpp} Tables.{cpp,hpp} Vector.hpp
+ ```
- `AUTHORS.txt` and `LICENSE.txt`
+
## fonts
- `NotoSans*.ttf`, `NotoNaskhArabicUI_Regular.ttf`:
@@ -342,7 +344,7 @@ File extracted from upstream release tarball:
## meshoptimizer
- Upstream: https://github.com/zeux/meshoptimizer
-- Version: git (e3f53f66e7a35b9b8764bee478589d79e34fa698, 2021)
+- Version: 0.16 (95893c0566646434dd675b708d293fcb2d526d08, 2021)
- License: MIT
Files extracted from upstream repository:
diff --git a/thirdparty/etcpak/Bitmap.cpp b/thirdparty/etcpak/Bitmap.cpp
deleted file mode 100644
index ef318318ac..0000000000
--- a/thirdparty/etcpak/Bitmap.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include <png.h>
-#include "lz4/lz4.h"
-
-#include "Bitmap.hpp"
-#include "Debug.hpp"
-
-Bitmap::Bitmap( const char* fn, unsigned int lines, bool bgr )
- : m_block( nullptr )
- , m_lines( lines )
- , m_alpha( true )
- , m_sema( 0 )
-{
- FILE* f = fopen( fn, "rb" );
- assert( f );
-
- char buf[4];
- fread( buf, 1, 4, f );
- if( memcmp( buf, "raw4", 4 ) == 0 )
- {
- uint8_t a;
- fread( &a, 1, 1, f );
- m_alpha = a == 1;
- uint32_t d;
- fread( &d, 1, 4, f );
- m_size.x = d;
- fread( &d, 1, 4, f );
- m_size.y = d;
- DBGPRINT( "Raw bitmap " << fn << " " << m_size.x << "x" << m_size.y );
-
- assert( m_size.x % 4 == 0 );
- assert( m_size.y % 4 == 0 );
-
- int32_t csize;
- fread( &csize, 1, 4, f );
- char* cbuf = new char[csize];
- fread( cbuf, 1, csize, f );
- fclose( f );
-
- m_block = m_data = new uint32_t[m_size.x*m_size.y];
- m_linesLeft = m_size.y / 4;
-
- LZ4_decompress_fast( cbuf, (char*)m_data, m_size.x*m_size.y*4 );
- delete[] cbuf;
-
- for( int i=0; i<m_size.y/4; i++ )
- {
- m_sema.unlock();
- }
- }
- else
- {
- fseek( f, 0, SEEK_SET );
-
- unsigned int sig_read = 0;
- int bit_depth, color_type, interlace_type;
-
- png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
- png_infop info_ptr = png_create_info_struct( png_ptr );
- setjmp( png_jmpbuf( png_ptr ) );
-
- png_init_io( png_ptr, f );
- png_set_sig_bytes( png_ptr, sig_read );
-
- png_uint_32 w, h;
-
- png_read_info( png_ptr, info_ptr );
- png_get_IHDR( png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, &interlace_type, NULL, NULL );
-
- m_size = v2i( w, h );
-
- png_set_strip_16( png_ptr );
- if( color_type == PNG_COLOR_TYPE_PALETTE )
- {
- png_set_palette_to_rgb( png_ptr );
- }
- else if( color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 )
- {
- png_set_expand_gray_1_2_4_to_8( png_ptr );
- }
- if( png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) )
- {
- png_set_tRNS_to_alpha( png_ptr );
- }
- if( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
- {
- png_set_gray_to_rgb(png_ptr);
- }
- if( bgr )
- {
- png_set_bgr(png_ptr);
- }
-
- switch( color_type )
- {
- case PNG_COLOR_TYPE_PALETTE:
- if( !png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) )
- {
- png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER );
- m_alpha = false;
- }
- break;
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- png_set_gray_to_rgb( png_ptr );
- break;
- case PNG_COLOR_TYPE_RGB:
- png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER );
- m_alpha = false;
- break;
- default:
- break;
- }
-
- DBGPRINT( "Bitmap " << fn << " " << w << "x" << h );
-
- assert( w % 4 == 0 );
- assert( h % 4 == 0 );
-
- m_block = m_data = new uint32_t[w*h];
- m_linesLeft = h / 4;
-
- m_load = std::async( std::launch::async, [this, f, png_ptr, info_ptr]() mutable
- {
- auto ptr = m_data;
- unsigned int lines = 0;
- for( int i=0; i<m_size.y / 4; i++ )
- {
- for( int j=0; j<4; j++ )
- {
- png_read_rows( png_ptr, (png_bytepp)&ptr, NULL, 1 );
- ptr += m_size.x;
- }
- lines++;
- if( lines >= m_lines )
- {
- lines = 0;
- m_sema.unlock();
- }
- }
-
- if( lines != 0 )
- {
- m_sema.unlock();
- }
-
- png_read_end( png_ptr, info_ptr );
- png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
- fclose( f );
- } );
- }
-}
-
-Bitmap::Bitmap( const v2i& size )
- : m_data( new uint32_t[size.x*size.y] )
- , m_block( nullptr )
- , m_lines( 1 )
- , m_linesLeft( size.y / 4 )
- , m_size( size )
- , m_sema( 0 )
-{
-}
-
-Bitmap::Bitmap( const Bitmap& src, unsigned int lines )
- : m_lines( lines )
- , m_alpha( src.Alpha() )
- , m_sema( 0 )
-{
-}
-
-Bitmap::~Bitmap()
-{
- delete[] m_data;
-}
-
-void Bitmap::Write( const char* fn )
-{
- FILE* f = fopen( fn, "wb" );
- assert( f );
-
- png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
- png_infop info_ptr = png_create_info_struct( png_ptr );
- setjmp( png_jmpbuf( png_ptr ) );
- png_init_io( png_ptr, f );
-
- png_set_IHDR( png_ptr, info_ptr, m_size.x, m_size.y, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );
-
- png_write_info( png_ptr, info_ptr );
-
- uint32_t* ptr = m_data;
- for( int i=0; i<m_size.y; i++ )
- {
- png_write_rows( png_ptr, (png_bytepp)(&ptr), 1 );
- ptr += m_size.x;
- }
-
- png_write_end( png_ptr, info_ptr );
- png_destroy_write_struct( &png_ptr, &info_ptr );
-
- fclose( f );
-}
-
-const uint32_t* Bitmap::NextBlock( unsigned int& lines, bool& done )
-{
- std::lock_guard<std::mutex> lock( m_lock );
- lines = std::min( m_lines, m_linesLeft );
- auto ret = m_block;
- m_sema.lock();
- m_block += m_size.x * 4 * lines;
- m_linesLeft -= lines;
- done = m_linesLeft == 0;
- return ret;
-}
diff --git a/thirdparty/etcpak/Bitmap.hpp b/thirdparty/etcpak/Bitmap.hpp
deleted file mode 100644
index fae8c936ed..0000000000
--- a/thirdparty/etcpak/Bitmap.hpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef __DARKRL__BITMAP_HPP__
-#define __DARKRL__BITMAP_HPP__
-
-#include <future>
-#include <memory>
-#include <mutex>
-#include <stdint.h>
-
-#include "Semaphore.hpp"
-#include "Vector.hpp"
-
-enum class Channels
-{
- RGB,
- Alpha
-};
-
-class Bitmap
-{
-public:
- Bitmap( const char* fn, unsigned int lines, bool bgr );
- Bitmap( const v2i& size );
- virtual ~Bitmap();
-
- void Write( const char* fn );
-
- uint32_t* Data() { if( m_load.valid() ) m_load.wait(); return m_data; }
- const uint32_t* Data() const { if( m_load.valid() ) m_load.wait(); return m_data; }
- const v2i& Size() const { return m_size; }
- bool Alpha() const { return m_alpha; }
-
- const uint32_t* NextBlock( unsigned int& lines, bool& done );
-
-protected:
- Bitmap( const Bitmap& src, unsigned int lines );
-
- uint32_t* m_data;
- uint32_t* m_block;
- unsigned int m_lines;
- unsigned int m_linesLeft;
- v2i m_size;
- bool m_alpha;
- Semaphore m_sema;
- std::mutex m_lock;
- std::future<void> m_load;
-};
-
-typedef std::shared_ptr<Bitmap> BitmapPtr;
-
-#endif
diff --git a/thirdparty/etcpak/BitmapDownsampled.cpp b/thirdparty/etcpak/BitmapDownsampled.cpp
deleted file mode 100644
index 0eb0d81185..0000000000
--- a/thirdparty/etcpak/BitmapDownsampled.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <string.h>
-#include <utility>
-
-#include "BitmapDownsampled.hpp"
-#include "Debug.hpp"
-
-BitmapDownsampled::BitmapDownsampled( const Bitmap& bmp, unsigned int lines )
- : Bitmap( bmp, lines )
-{
- m_size.x = std::max( 1, bmp.Size().x / 2 );
- m_size.y = std::max( 1, bmp.Size().y / 2 );
-
- int w = std::max( m_size.x, 4 );
- int h = std::max( m_size.y, 4 );
-
- DBGPRINT( "Subbitmap " << m_size.x << "x" << m_size.y );
-
- m_block = m_data = new uint32_t[w*h];
-
- if( m_size.x < w || m_size.y < h )
- {
- memset( m_data, 0, w*h*sizeof( uint32_t ) );
- m_linesLeft = h / 4;
- unsigned int lines = 0;
- for( int i=0; i<h/4; i++ )
- {
- for( int j=0; j<4; j++ )
- {
- lines++;
- if( lines > m_lines )
- {
- lines = 0;
- m_sema.unlock();
- }
- }
- }
- if( lines != 0 )
- {
- m_sema.unlock();
- }
- }
- else
- {
- m_linesLeft = h / 4;
- m_load = std::async( std::launch::async, [this, &bmp, w, h]() mutable
- {
- auto ptr = m_data;
- auto src1 = bmp.Data();
- auto src2 = src1 + bmp.Size().x;
- unsigned int lines = 0;
- for( int i=0; i<h/4; i++ )
- {
- for( int j=0; j<4; j++ )
- {
- for( int k=0; k<m_size.x; k++ )
- {
- int r = ( ( *src1 & 0x000000FF ) + ( *(src1+1) & 0x000000FF ) + ( *src2 & 0x000000FF ) + ( *(src2+1) & 0x000000FF ) ) / 4;
- int g = ( ( ( *src1 & 0x0000FF00 ) + ( *(src1+1) & 0x0000FF00 ) + ( *src2 & 0x0000FF00 ) + ( *(src2+1) & 0x0000FF00 ) ) / 4 ) & 0x0000FF00;
- int b = ( ( ( *src1 & 0x00FF0000 ) + ( *(src1+1) & 0x00FF0000 ) + ( *src2 & 0x00FF0000 ) + ( *(src2+1) & 0x00FF0000 ) ) / 4 ) & 0x00FF0000;
- int a = ( ( ( ( ( *src1 & 0xFF000000 ) >> 8 ) + ( ( *(src1+1) & 0xFF000000 ) >> 8 ) + ( ( *src2 & 0xFF000000 ) >> 8 ) + ( ( *(src2+1) & 0xFF000000 ) >> 8 ) ) / 4 ) & 0x00FF0000 ) << 8;
- *ptr++ = r | g | b | a;
- src1 += 2;
- src2 += 2;
- }
- src1 += m_size.x * 2;
- src2 += m_size.x * 2;
- }
- lines++;
- if( lines >= m_lines )
- {
- lines = 0;
- m_sema.unlock();
- }
- }
-
- if( lines != 0 )
- {
- m_sema.unlock();
- }
- } );
- }
-}
-
-BitmapDownsampled::~BitmapDownsampled()
-{
-}
diff --git a/thirdparty/etcpak/BitmapDownsampled.hpp b/thirdparty/etcpak/BitmapDownsampled.hpp
deleted file mode 100644
index b7313808df..0000000000
--- a/thirdparty/etcpak/BitmapDownsampled.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __DARKRL__BITMAPDOWNSAMPLED_HPP__
-#define __DARKRL__BITMAPDOWNSAMPLED_HPP__
-
-#include "Bitmap.hpp"
-
-class BitmapDownsampled : public Bitmap
-{
-public:
- BitmapDownsampled( const Bitmap& bmp, unsigned int lines );
- ~BitmapDownsampled();
-};
-
-#endif
diff --git a/thirdparty/etcpak/BlockData.cpp b/thirdparty/etcpak/BlockData.cpp
deleted file mode 100644
index 4906e69492..0000000000
--- a/thirdparty/etcpak/BlockData.cpp
+++ /dev/null
@@ -1,1296 +0,0 @@
-#include <assert.h>
-#include <string.h>
-
-#include "BlockData.hpp"
-#include "ColorSpace.hpp"
-#include "Debug.hpp"
-#include "MipMap.hpp"
-#include "mmap.hpp"
-#include "ProcessRGB.hpp"
-#include "ProcessDxtc.hpp"
-#include "Tables.hpp"
-#include "TaskDispatch.hpp"
-
-#ifdef __ARM_NEON
-# include <arm_neon.h>
-#endif
-
-#if defined __SSE4_1__ || defined __AVX2__ || defined _MSC_VER
-# ifdef _MSC_VER
-# include <intrin.h>
-# include <Windows.h>
-# define _bswap(x) _byteswap_ulong(x)
-# define _bswap64(x) _byteswap_uint64(x)
-# else
-# include <x86intrin.h>
-# endif
-#endif
-
-#ifndef _bswap
-# define _bswap(x) __builtin_bswap32(x)
-# define _bswap64(x) __builtin_bswap64(x)
-#endif
-
-static uint8_t table59T58H[8] = { 3,6,11,16,23,32,41,64 };
-
-BlockData::BlockData( const char* fn )
- : m_file( fopen( fn, "rb" ) )
-{
- assert( m_file );
- fseek( m_file, 0, SEEK_END );
- m_maplen = ftell( m_file );
- fseek( m_file, 0, SEEK_SET );
- m_data = (uint8_t*)mmap( nullptr, m_maplen, PROT_READ, MAP_SHARED, fileno( m_file ), 0 );
-
- auto data32 = (uint32_t*)m_data;
- if( *data32 == 0x03525650 )
- {
- // PVR
- switch( *(data32+2) )
- {
- case 6:
- m_type = Etc1;
- break;
- case 7:
- m_type = Dxt1;
- break;
- case 11:
- m_type = Dxt5;
- break;
- case 22:
- m_type = Etc2_RGB;
- break;
- case 23:
- m_type = Etc2_RGBA;
- break;
- default:
- assert( false );
- break;
- }
-
- m_size.y = *(data32+6);
- m_size.x = *(data32+7);
- m_dataOffset = 52 + *(data32+12);
- }
- else if( *data32 == 0x58544BAB )
- {
- // KTX
- switch( *(data32+7) )
- {
- case 0x9274:
- m_type = Etc2_RGB;
- break;
- case 0x9278:
- m_type = Etc2_RGBA;
- break;
- default:
- assert( false );
- break;
- }
-
- m_size.x = *(data32+9);
- m_size.y = *(data32+10);
- m_dataOffset = sizeof( uint32_t ) * 17 + *(data32+15);
- }
- else
- {
- assert( false );
- }
-}
-
-static uint8_t* OpenForWriting( const char* fn, size_t len, const v2i& size, FILE** f, int levels, BlockData::Type type )
-{
- *f = fopen( fn, "wb+" );
- assert( *f );
- fseek( *f, len - 1, SEEK_SET );
- const char zero = 0;
- fwrite( &zero, 1, 1, *f );
- fseek( *f, 0, SEEK_SET );
-
- auto ret = (uint8_t*)mmap( nullptr, len, PROT_WRITE, MAP_SHARED, fileno( *f ), 0 );
- auto dst = (uint32_t*)ret;
-
- *dst++ = 0x03525650; // version
- *dst++ = 0; // flags
- switch( type ) // pixelformat[0]
- {
- case BlockData::Etc1:
- *dst++ = 6;
- break;
- case BlockData::Etc2_RGB:
- *dst++ = 22;
- break;
- case BlockData::Etc2_RGBA:
- *dst++ = 23;
- break;
- case BlockData::Dxt1:
- *dst++ = 7;
- break;
- case BlockData::Dxt5:
- *dst++ = 11;
- break;
- default:
- assert( false );
- break;
- }
- *dst++ = 0; // pixelformat[1]
- *dst++ = 0; // colourspace
- *dst++ = 0; // channel type
- *dst++ = size.y; // height
- *dst++ = size.x; // width
- *dst++ = 1; // depth
- *dst++ = 1; // num surfs
- *dst++ = 1; // num faces
- *dst++ = levels; // mipmap count
- *dst++ = 0; // metadata size
-
- return ret;
-}
-
-static int AdjustSizeForMipmaps( const v2i& size, int levels )
-{
- int len = 0;
- v2i current = size;
- for( int i=1; i<levels; i++ )
- {
- assert( current.x != 1 || current.y != 1 );
- current.x = std::max( 1, current.x / 2 );
- current.y = std::max( 1, current.y / 2 );
- len += std::max( 4, current.x ) * std::max( 4, current.y ) / 2;
- }
- assert( current.x == 1 && current.y == 1 );
- return len;
-}
-
-BlockData::BlockData( const char* fn, const v2i& size, bool mipmap, Type type )
- : m_size( size )
- , m_dataOffset( 52 )
- , m_maplen( m_size.x*m_size.y/2 )
- , m_type( type )
-{
- assert( m_size.x%4 == 0 && m_size.y%4 == 0 );
-
- uint32_t cnt = m_size.x * m_size.y / 16;
- DBGPRINT( cnt << " blocks" );
-
- int levels = 1;
-
- if( mipmap )
- {
- levels = NumberOfMipLevels( size );
- DBGPRINT( "Number of mipmaps: " << levels );
- m_maplen += AdjustSizeForMipmaps( size, levels );
- }
-
- if( type == Etc2_RGBA || type == Dxt5 ) m_maplen *= 2;
-
- m_maplen += m_dataOffset;
- m_data = OpenForWriting( fn, m_maplen, m_size, &m_file, levels, type );
-}
-
-BlockData::BlockData( const v2i& size, bool mipmap, Type type )
- : m_size( size )
- , m_dataOffset( 52 )
- , m_file( nullptr )
- , m_maplen( m_size.x*m_size.y/2 )
- , m_type( type )
-{
- assert( m_size.x%4 == 0 && m_size.y%4 == 0 );
- if( mipmap )
- {
- const int levels = NumberOfMipLevels( size );
- m_maplen += AdjustSizeForMipmaps( size, levels );
- }
-
- if( type == Etc2_RGBA || type == Dxt5 ) m_maplen *= 2;
-
- m_maplen += m_dataOffset;
- m_data = new uint8_t[m_maplen];
-}
-
-BlockData::~BlockData()
-{
- if( m_file )
- {
- munmap( m_data, m_maplen );
- fclose( m_file );
- }
- else
- {
- delete[] m_data;
- }
-}
-
-void BlockData::Process( const uint32_t* src, uint32_t blocks, size_t offset, size_t width, Channels type, bool dither )
-{
- auto dst = ((uint64_t*)( m_data + m_dataOffset )) + offset;
-
- if( type == Channels::Alpha )
- {
- if( m_type != Etc1 )
- {
- CompressEtc2Alpha( src, dst, blocks, width );
- }
- else
- {
- CompressEtc1Alpha( src, dst, blocks, width );
- }
- }
- else
- {
- switch( m_type )
- {
- case Etc1:
- if( dither )
- {
- CompressEtc1RgbDither( src, dst, blocks, width );
- }
- else
- {
- CompressEtc1Rgb( src, dst, blocks, width );
- }
- break;
- case Etc2_RGB:
- CompressEtc2Rgb( src, dst, blocks, width );
- break;
- case Dxt1:
- if( dither )
- {
- CompressDxt1Dither( src, dst, blocks, width );
- }
- else
- {
- CompressDxt1( src, dst, blocks, width );
- }
- break;
- default:
- assert( false );
- break;
- }
- }
-}
-
-void BlockData::ProcessRGBA( const uint32_t* src, uint32_t blocks, size_t offset, size_t width )
-{
- auto dst = ((uint64_t*)( m_data + m_dataOffset )) + offset * 2;
-
- switch( m_type )
- {
- case Etc2_RGBA:
- CompressEtc2Rgba( src, dst, blocks, width );
- break;
- case Dxt5:
- CompressDxt5( src, dst, blocks, width );
- break;
- default:
- assert( false );
- break;
- }
-}
-
-namespace
-{
-
-static etcpak_force_inline int32_t expand6(uint32_t value)
-{
- return (value << 2) | (value >> 4);
-}
-
-static etcpak_force_inline int32_t expand7(uint32_t value)
-{
- return (value << 1) | (value >> 6);
-}
-
-static etcpak_force_inline void DecodeT( uint64_t block, uint32_t* dst, uint32_t w )
-{
- const auto r0 = ( block >> 24 ) & 0x1B;
- const auto rh0 = ( r0 >> 3 ) & 0x3;
- const auto rl0 = r0 & 0x3;
- const auto g0 = ( block >> 20 ) & 0xF;
- const auto b0 = ( block >> 16 ) & 0xF;
-
- const auto r1 = ( block >> 12 ) & 0xF;
- const auto g1 = ( block >> 8 ) & 0xF;
- const auto b1 = ( block >> 4 ) & 0xF;
-
- const auto cr0 = ( ( rh0 << 6 ) | ( rl0 << 4 ) | ( rh0 << 2 ) | rl0);
- const auto cg0 = ( g0 << 4 ) | g0;
- const auto cb0 = ( b0 << 4 ) | b0;
-
- const auto cr1 = ( r1 << 4 ) | r1;
- const auto cg1 = ( g1 << 4 ) | g1;
- const auto cb1 = ( b1 << 4 ) | b1;
-
- const auto codeword_hi = ( block >> 2 ) & 0x3;
- const auto codeword_lo = block & 0x1;
- const auto codeword = ( codeword_hi << 1 ) | codeword_lo;
-
- const auto c2r = clampu8( cr1 + table59T58H[codeword] );
- const auto c2g = clampu8( cg1 + table59T58H[codeword] );
- const auto c2b = clampu8( cb1 + table59T58H[codeword] );
-
- const auto c3r = clampu8( cr1 - table59T58H[codeword] );
- const auto c3g = clampu8( cg1 - table59T58H[codeword] );
- const auto c3b = clampu8( cb1 - table59T58H[codeword] );
-
- const uint32_t col_tab[4] = {
- uint32_t(cr0 | ( cg0 << 8 ) | ( cb0 << 16 ) | 0xFF000000),
- uint32_t(c2r | ( c2g << 8 ) | ( c2b << 16 ) | 0xFF000000),
- uint32_t(cr1 | ( cg1 << 8 ) | ( cb1 << 16 ) | 0xFF000000),
- uint32_t(c3r | ( c3g << 8 ) | ( c3b << 16 ) | 0xFF000000)
- };
-
- const uint32_t indexes = ( block >> 32 ) & 0xFFFFFFFF;
- for( uint8_t j = 0; j < 4; j++ )
- {
- for( uint8_t i = 0; i < 4; i++ )
- {
- //2bit indices distributed on two lane 16bit numbers
- const uint8_t index = ( ( ( indexes >> ( j + i * 4 + 16 ) ) & 0x1 ) << 1) | ( ( indexes >> ( j + i * 4 ) ) & 0x1);
- dst[j * w + i] = col_tab[index];
- }
- }
-}
-
-static etcpak_force_inline void DecodeTAlpha( uint64_t block, uint64_t alpha, uint32_t* dst, uint32_t w )
-{
- const auto r0 = ( block >> 24 ) & 0x1B;
- const auto rh0 = ( r0 >> 3 ) & 0x3;
- const auto rl0 = r0 & 0x3;
- const auto g0 = ( block >> 20 ) & 0xF;
- const auto b0 = ( block >> 16 ) & 0xF;
-
- const auto r1 = ( block >> 12 ) & 0xF;
- const auto g1 = ( block >> 8 ) & 0xF;
- const auto b1 = ( block >> 4 ) & 0xF;
-
- const auto cr0 = ( ( rh0 << 6 ) | ( rl0 << 4 ) | ( rh0 << 2 ) | rl0);
- const auto cg0 = ( g0 << 4 ) | g0;
- const auto cb0 = ( b0 << 4 ) | b0;
-
- const auto cr1 = ( r1 << 4 ) | r1;
- const auto cg1 = ( g1 << 4 ) | g1;
- const auto cb1 = ( b1 << 4 ) | b1;
-
- const auto codeword_hi = ( block >> 2 ) & 0x3;
- const auto codeword_lo = block & 0x1;
- const auto codeword = (codeword_hi << 1) | codeword_lo;
-
- const int32_t base = alpha >> 56;
- const int32_t mul = ( alpha >> 52 ) & 0xF;
- const auto tbl = g_alpha[( alpha >> 48 ) & 0xF];
-
- const auto c2r = clampu8( cr1 + table59T58H[codeword] );
- const auto c2g = clampu8( cg1 + table59T58H[codeword] );
- const auto c2b = clampu8( cb1 + table59T58H[codeword] );
-
- const auto c3r = clampu8( cr1 - table59T58H[codeword] );
- const auto c3g = clampu8( cg1 - table59T58H[codeword] );
- const auto c3b = clampu8( cb1 - table59T58H[codeword] );
-
- const uint32_t col_tab[4] = {
- uint32_t(cr0 | ( cg0 << 8 ) | ( cb0 << 16 )),
- uint32_t(c2r | ( c2g << 8 ) | ( c2b << 16 )),
- uint32_t(cr1 | ( cg1 << 8 ) | ( cb1 << 16 )),
- uint32_t(c3r | ( c3g << 8 ) | ( c3b << 16 ))
- };
-
- const uint32_t indexes = ( block >> 32 ) & 0xFFFFFFFF;
- for( uint8_t j = 0; j < 4; j++ )
- {
- for( uint8_t i = 0; i < 4; i++ )
- {
- //2bit indices distributed on two lane 16bit numbers
- const uint8_t index = ( ( ( indexes >> ( j + i * 4 + 16 ) ) & 0x1 ) << 1 ) | ( ( indexes >> ( j + i * 4 ) ) & 0x1 );
- const auto amod = tbl[( alpha >> ( 45 - j * 3 - i * 12 ) ) & 0x7];
- const uint32_t a = clampu8( base + amod * mul );
- dst[j * w + i] = col_tab[index] | ( a << 24 );
- }
- }
-}
-
-static etcpak_force_inline void DecodeH( uint64_t block, uint32_t* dst, uint32_t w )
-{
- const uint32_t indexes = ( block >> 32 ) & 0xFFFFFFFF;
-
- const auto r0444 = ( block >> 27 ) & 0xF;
- const auto g0444 = ( ( block >> 20 ) & 0x1 ) | ( ( ( block >> 24 ) & 0x7 ) << 1 );
- const auto b0444 = ( ( block >> 15 ) & 0x7 ) | ( ( ( block >> 19 ) & 0x1 ) << 3 );
-
- const auto r1444 = ( block >> 11 ) & 0xF;
- const auto g1444 = ( block >> 7 ) & 0xF;
- const auto b1444 = ( block >> 3 ) & 0xF;
-
- const auto r0 = ( r0444 << 4 ) | r0444;
- const auto g0 = ( g0444 << 4 ) | g0444;
- const auto b0 = ( b0444 << 4 ) | b0444;
-
- const auto r1 = ( r1444 << 4 ) | r1444;
- const auto g1 = ( g1444 << 4 ) | g1444;
- const auto b1 = ( b1444 << 4 ) | b1444;
-
- const auto codeword_hi = ( ( block & 0x1 ) << 1 ) | ( ( block & 0x4 ) );
- const auto c0 = ( r0444 << 8 ) | ( g0444 << 4 ) | ( b0444 << 0 );
- const auto c1 = ( block >> 3 ) & ( ( 1 << 12 ) - 1 );
- const auto codeword_lo = ( c0 >= c1 ) ? 1 : 0;
- const auto codeword = codeword_hi | codeword_lo;
-
- const uint32_t col_tab[] = {
- uint32_t(clampu8( r0 + table59T58H[codeword] ) | ( clampu8( g0 + table59T58H[codeword] ) << 8 ) | ( clampu8( b0 + table59T58H[codeword] ) << 16 )),
- uint32_t(clampu8( r0 - table59T58H[codeword] ) | ( clampu8( g0 - table59T58H[codeword] ) << 8 ) | ( clampu8( b0 - table59T58H[codeword] ) << 16 )),
- uint32_t(clampu8( r1 + table59T58H[codeword] ) | ( clampu8( g1 + table59T58H[codeword] ) << 8 ) | ( clampu8( b1 + table59T58H[codeword] ) << 16 )),
- uint32_t(clampu8( r1 - table59T58H[codeword] ) | ( clampu8( g1 - table59T58H[codeword] ) << 8 ) | ( clampu8( b1 - table59T58H[codeword] ) << 16 ))
- };
-
- for( uint8_t j = 0; j < 4; j++ )
- {
- for( uint8_t i = 0; i < 4; i++ )
- {
- const uint8_t index = ( ( ( indexes >> ( j + i * 4 + 16 ) ) & 0x1 ) << 1 ) | ( ( indexes >> ( j + i * 4 ) ) & 0x1 );
- dst[j * w + i] = col_tab[index] | 0xFF000000;
- }
- }
-}
-
-static etcpak_force_inline void DecodeHAlpha( uint64_t block, uint64_t alpha, uint32_t* dst, uint32_t w )
-{
- const uint32_t indexes = ( block >> 32 ) & 0xFFFFFFFF;
-
- const auto r0444 = ( block >> 27 ) & 0xF;
- const auto g0444 = ( ( block >> 20 ) & 0x1 ) | ( ( ( block >> 24 ) & 0x7 ) << 1 );
- const auto b0444 = ( ( block >> 15 ) & 0x7 ) | ( ( ( block >> 19 ) & 0x1 ) << 3 );
-
- const auto r1444 = ( block >> 11 ) & 0xF;
- const auto g1444 = ( block >> 7 ) & 0xF;
- const auto b1444 = ( block >> 3 ) & 0xF;
-
- const auto r0 = ( r0444 << 4 ) | r0444;
- const auto g0 = ( g0444 << 4 ) | g0444;
- const auto b0 = ( b0444 << 4 ) | b0444;
-
- const auto r1 = ( r1444 << 4 ) | r1444;
- const auto g1 = ( g1444 << 4 ) | g1444;
- const auto b1 = ( b1444 << 4 ) | b1444;
-
- const auto codeword_hi = ( ( block & 0x1 ) << 1 ) | ( ( block & 0x4 ) );
- const auto c0 = ( r0444 << 8 ) | ( g0444 << 4 ) | ( b0444 << 0 );
- const auto c1 = ( block >> 3 ) & ( ( 1 << 12 ) - 1 );
- const auto codeword_lo = ( c0 >= c1 ) ? 1 : 0;
- const auto codeword = codeword_hi | codeword_lo;
-
- const int32_t base = alpha >> 56;
- const int32_t mul = ( alpha >> 52 ) & 0xF;
- const auto tbl = g_alpha[(alpha >> 48) & 0xF];
-
- const uint32_t col_tab[] = {
- uint32_t(clampu8( r0 + table59T58H[codeword] ) | ( clampu8( g0 + table59T58H[codeword] ) << 8 ) | ( clampu8( b0 + table59T58H[codeword] ) << 16 )),
- uint32_t(clampu8( r0 - table59T58H[codeword] ) | ( clampu8( g0 - table59T58H[codeword] ) << 8 ) | ( clampu8( b0 - table59T58H[codeword] ) << 16 )),
- uint32_t(clampu8( r1 + table59T58H[codeword] ) | ( clampu8( g1 + table59T58H[codeword] ) << 8 ) | ( clampu8( b1 + table59T58H[codeword] ) << 16 )),
- uint32_t(clampu8( r1 - table59T58H[codeword] ) | ( clampu8( g1 - table59T58H[codeword] ) << 8 ) | ( clampu8( b1 - table59T58H[codeword] ) << 16 ))
- };
-
- for( uint8_t j = 0; j < 4; j++ )
- {
- for( uint8_t i = 0; i < 4; i++ )
- {
- const uint8_t index = ( ( ( indexes >> ( j + i * 4 + 16 ) ) & 0x1 ) << 1 ) | ( ( indexes >> ( j + i * 4 ) ) & 0x1 );
- const auto amod = tbl[( alpha >> ( 45 - j * 3 - i * 12) ) & 0x7];
- const uint32_t a = clampu8( base + amod * mul );
- dst[j * w + i] = col_tab[index] | ( a << 24 );
- }
- }
-}
-
-static etcpak_force_inline void DecodePlanar( uint64_t block, uint32_t* dst, uint32_t w )
-{
- const auto bv = expand6((block >> ( 0 + 32)) & 0x3F);
- const auto gv = expand7((block >> ( 6 + 32)) & 0x7F);
- const auto rv = expand6((block >> (13 + 32)) & 0x3F);
-
- const auto bh = expand6((block >> (19 + 32)) & 0x3F);
- const auto gh = expand7((block >> (25 + 32)) & 0x7F);
-
- const auto rh0 = (block >> (32 - 32)) & 0x01;
- const auto rh1 = ((block >> (34 - 32)) & 0x1F) << 1;
- const auto rh = expand6(rh0 | rh1);
-
- const auto bo0 = (block >> (39 - 32)) & 0x07;
- const auto bo1 = ((block >> (43 - 32)) & 0x3) << 3;
- const auto bo2 = ((block >> (48 - 32)) & 0x1) << 5;
- const auto bo = expand6(bo0 | bo1 | bo2);
- const auto go0 = (block >> (49 - 32)) & 0x3F;
- const auto go1 = ((block >> (56 - 32)) & 0x01) << 6;
- const auto go = expand7(go0 | go1);
- const auto ro = expand6((block >> (57 - 32)) & 0x3F);
-
-#ifdef __ARM_NEON
- uint64_t init = uint64_t(uint16_t(rh-ro)) | ( uint64_t(uint16_t(gh-go)) << 16 ) | ( uint64_t(uint16_t(bh-bo)) << 32 );
- int16x8_t chco = vreinterpretq_s16_u64( vdupq_n_u64( init ) );
- init = uint64_t(uint16_t( (rv-ro) - 4 * (rh-ro) )) | ( uint64_t(uint16_t( (gv-go) - 4 * (gh-go) )) << 16 ) | ( uint64_t(uint16_t( (bv-bo) - 4 * (bh-bo) )) << 32 );
- int16x8_t cvco = vreinterpretq_s16_u64( vdupq_n_u64( init ) );
- init = uint64_t(4*ro+2) | ( uint64_t(4*go+2) << 16 ) | ( uint64_t(4*bo+2) << 32 ) | ( uint64_t(0xFFF) << 48 );
- int16x8_t col = vreinterpretq_s16_u64( vdupq_n_u64( init ) );
-
- for( int j=0; j<4; j++ )
- {
- for( int i=0; i<4; i++ )
- {
- uint8x8_t c = vqshrun_n_s16( col, 2 );
- vst1_lane_u32( dst+j*w+i, vreinterpret_u32_u8( c ), 0 );
- col = vaddq_s16( col, chco );
- }
- col = vaddq_s16( col, cvco );
- }
-#elif defined __AVX2__
- const auto R0 = 4*ro+2;
- const auto G0 = 4*go+2;
- const auto B0 = 4*bo+2;
- const auto RHO = rh-ro;
- const auto GHO = gh-go;
- const auto BHO = bh-bo;
-
- __m256i cvco = _mm256_setr_epi16( rv - ro, gv - go, bv - bo, 0, rv - ro, gv - go, bv - bo, 0, rv - ro, gv - go, bv - bo, 0, rv - ro, gv - go, bv - bo, 0 );
- __m256i col = _mm256_setr_epi16( R0, G0, B0, 0xFFF, R0+RHO, G0+GHO, B0+BHO, 0xFFF, R0+2*RHO, G0+2*GHO, B0+2*BHO, 0xFFF, R0+3*RHO, G0+3*GHO, B0+3*BHO, 0xFFF );
-
- for( int j=0; j<4; j++ )
- {
- __m256i c = _mm256_srai_epi16( col, 2 );
- __m128i s = _mm_packus_epi16( _mm256_castsi256_si128( c ), _mm256_extracti128_si256( c, 1 ) );
- _mm_storeu_si128( (__m128i*)(dst+j*w), s );
- col = _mm256_add_epi16( col, cvco );
- }
-#elif defined __SSE4_1__
- __m128i chco = _mm_setr_epi16( rh - ro, gh - go, bh - bo, 0, 0, 0, 0, 0 );
- __m128i cvco = _mm_setr_epi16( (rv - ro) - 4 * (rh - ro), (gv - go) - 4 * (gh - go), (bv - bo) - 4 * (bh - bo), 0, 0, 0, 0, 0 );
- __m128i col = _mm_setr_epi16( 4*ro+2, 4*go+2, 4*bo+2, 0xFFF, 0, 0, 0, 0 );
-
- for( int j=0; j<4; j++ )
- {
- for( int i=0; i<4; i++ )
- {
- __m128i c = _mm_srai_epi16( col, 2 );
- __m128i s = _mm_packus_epi16( c, c );
- dst[j*w+i] = _mm_cvtsi128_si32( s );
- col = _mm_add_epi16( col, chco );
- }
- col = _mm_add_epi16( col, cvco );
- }
-#else
- for( int j=0; j<4; j++ )
- {
- for( int i=0; i<4; i++ )
- {
- const uint32_t r = (i * (rh - ro) + j * (rv - ro) + 4 * ro + 2) >> 2;
- const uint32_t g = (i * (gh - go) + j * (gv - go) + 4 * go + 2) >> 2;
- const uint32_t b = (i * (bh - bo) + j * (bv - bo) + 4 * bo + 2) >> 2;
- if( ( ( r | g | b ) & ~0xFF ) == 0 )
- {
- dst[j*w+i] = r | ( g << 8 ) | ( b << 16 ) | 0xFF000000;
- }
- else
- {
- const auto rc = clampu8( r );
- const auto gc = clampu8( g );
- const auto bc = clampu8( b );
- dst[j*w+i] = rc | ( gc << 8 ) | ( bc << 16 ) | 0xFF000000;
- }
- }
- }
-#endif
-}
-
-static etcpak_force_inline void DecodePlanarAlpha( uint64_t block, uint64_t alpha, uint32_t* dst, uint32_t w )
-{
- const auto bv = expand6((block >> ( 0 + 32)) & 0x3F);
- const auto gv = expand7((block >> ( 6 + 32)) & 0x7F);
- const auto rv = expand6((block >> (13 + 32)) & 0x3F);
-
- const auto bh = expand6((block >> (19 + 32)) & 0x3F);
- const auto gh = expand7((block >> (25 + 32)) & 0x7F);
-
- const auto rh0 = (block >> (32 - 32)) & 0x01;
- const auto rh1 = ((block >> (34 - 32)) & 0x1F) << 1;
- const auto rh = expand6(rh0 | rh1);
-
- const auto bo0 = (block >> (39 - 32)) & 0x07;
- const auto bo1 = ((block >> (43 - 32)) & 0x3) << 3;
- const auto bo2 = ((block >> (48 - 32)) & 0x1) << 5;
- const auto bo = expand6(bo0 | bo1 | bo2);
- const auto go0 = (block >> (49 - 32)) & 0x3F;
- const auto go1 = ((block >> (56 - 32)) & 0x01) << 6;
- const auto go = expand7(go0 | go1);
- const auto ro = expand6((block >> (57 - 32)) & 0x3F);
-
- const int32_t base = alpha >> 56;
- const int32_t mul = ( alpha >> 52 ) & 0xF;
- const auto tbl = g_alpha[( alpha >> 48 ) & 0xF];
-
-#ifdef __ARM_NEON
- uint64_t init = uint64_t(uint16_t(rh-ro)) | ( uint64_t(uint16_t(gh-go)) << 16 ) | ( uint64_t(uint16_t(bh-bo)) << 32 );
- int16x8_t chco = vreinterpretq_s16_u64( vdupq_n_u64( init ) );
- init = uint64_t(uint16_t( (rv-ro) - 4 * (rh-ro) )) | ( uint64_t(uint16_t( (gv-go) - 4 * (gh-go) )) << 16 ) | ( uint64_t(uint16_t( (bv-bo) - 4 * (bh-bo) )) << 32 );
- int16x8_t cvco = vreinterpretq_s16_u64( vdupq_n_u64( init ) );
- init = uint64_t(4*ro+2) | ( uint64_t(4*go+2) << 16 ) | ( uint64_t(4*bo+2) << 32 );
- int16x8_t col = vreinterpretq_s16_u64( vdupq_n_u64( init ) );
-
- for( int j=0; j<4; j++ )
- {
- for( int i=0; i<4; i++ )
- {
- const auto amod = tbl[(alpha >> ( 45 - j*3 - i*12 )) & 0x7];
- const uint32_t a = clampu8( base + amod * mul );
- uint8x8_t c = vqshrun_n_s16( col, 2 );
- dst[j*w+i] = vget_lane_u32( vreinterpret_u32_u8( c ), 0 ) | ( a << 24 );
- col = vaddq_s16( col, chco );
- }
- col = vaddq_s16( col, cvco );
- }
-#elif defined __SSE4_1__
- __m128i chco = _mm_setr_epi16( rh - ro, gh - go, bh - bo, 0, 0, 0, 0, 0 );
- __m128i cvco = _mm_setr_epi16( (rv - ro) - 4 * (rh - ro), (gv - go) - 4 * (gh - go), (bv - bo) - 4 * (bh - bo), 0, 0, 0, 0, 0 );
- __m128i col = _mm_setr_epi16( 4*ro+2, 4*go+2, 4*bo+2, 0, 0, 0, 0, 0 );
-
- for( int j=0; j<4; j++ )
- {
- for( int i=0; i<4; i++ )
- {
- const auto amod = tbl[(alpha >> ( 45 - j*3 - i*12 )) & 0x7];
- const uint32_t a = clampu8( base + amod * mul );
- __m128i c = _mm_srai_epi16( col, 2 );
- __m128i s = _mm_packus_epi16( c, c );
- dst[j*w+i] = _mm_cvtsi128_si32( s ) | ( a << 24 );
- col = _mm_add_epi16( col, chco );
- }
- col = _mm_add_epi16( col, cvco );
- }
-#else
- for (auto j = 0; j < 4; j++)
- {
- for (auto i = 0; i < 4; i++)
- {
- const uint32_t r = (i * (rh - ro) + j * (rv - ro) + 4 * ro + 2) >> 2;
- const uint32_t g = (i * (gh - go) + j * (gv - go) + 4 * go + 2) >> 2;
- const uint32_t b = (i * (bh - bo) + j * (bv - bo) + 4 * bo + 2) >> 2;
- const auto amod = tbl[(alpha >> ( 45 - j*3 - i*12 )) & 0x7];
- const uint32_t a = clampu8( base + amod * mul );
- if( ( ( r | g | b ) & ~0xFF ) == 0 )
- {
- dst[j*w+i] = r | ( g << 8 ) | ( b << 16 ) | ( a << 24 );
- }
- else
- {
- const auto rc = clampu8( r );
- const auto gc = clampu8( g );
- const auto bc = clampu8( b );
- dst[j*w+i] = rc | ( gc << 8 ) | ( bc << 16 ) | ( a << 24 );
- }
- }
- }
-#endif
-}
-
-}
-
-BitmapPtr BlockData::Decode()
-{
- switch( m_type )
- {
- case Etc1:
- case Etc2_RGB:
- return DecodeRGB();
- case Etc2_RGBA:
- return DecodeRGBA();
- case Dxt1:
- return DecodeDxt1();
- case Dxt5:
- return DecodeDxt5();
- default:
- assert( false );
- return nullptr;
- }
-}
-
-static etcpak_force_inline uint64_t ConvertByteOrder( uint64_t d )
-{
- uint32_t word[2];
- memcpy( word, &d, 8 );
- word[0] = _bswap( word[0] );
- word[1] = _bswap( word[1] );
- memcpy( &d, word, 8 );
- return d;
-}
-
-static etcpak_force_inline void DecodeRGBPart( uint64_t d, uint32_t* dst, uint32_t w )
-{
- d = ConvertByteOrder( d );
-
- uint32_t br[2], bg[2], bb[2];
-
- if( d & 0x2 )
- {
- int32_t dr, dg, db;
-
- uint32_t r0 = ( d & 0xF8000000 ) >> 27;
- uint32_t g0 = ( d & 0x00F80000 ) >> 19;
- uint32_t b0 = ( d & 0x0000F800 ) >> 11;
-
- dr = ( int32_t(d) << 5 ) >> 29;
- dg = ( int32_t(d) << 13 ) >> 29;
- db = ( int32_t(d) << 21 ) >> 29;
-
- int32_t r1 = int32_t(r0) + dr;
- int32_t g1 = int32_t(g0) + dg;
- int32_t b1 = int32_t(b0) + db;
-
- // T mode
- if ( (r1 < 0) || (r1 > 31) )
- {
- DecodeT( d, dst, w );
- return;
- }
-
- // H mode
- if ((g1 < 0) || (g1 > 31))
- {
- DecodeH( d, dst, w );
- return;
- }
-
- // P mode
- if( (b1 < 0) || (b1 > 31) )
- {
- DecodePlanar( d, dst, w );
- return;
- }
-
- br[0] = ( r0 << 3 ) | ( r0 >> 2 );
- br[1] = ( r1 << 3 ) | ( r1 >> 2 );
- bg[0] = ( g0 << 3 ) | ( g0 >> 2 );
- bg[1] = ( g1 << 3 ) | ( g1 >> 2 );
- bb[0] = ( b0 << 3 ) | ( b0 >> 2 );
- bb[1] = ( b1 << 3 ) | ( b1 >> 2 );
- }
- else
- {
- br[0] = ( ( d & 0xF0000000 ) >> 24 ) | ( ( d & 0xF0000000 ) >> 28 );
- br[1] = ( ( d & 0x0F000000 ) >> 20 ) | ( ( d & 0x0F000000 ) >> 24 );
- bg[0] = ( ( d & 0x00F00000 ) >> 16 ) | ( ( d & 0x00F00000 ) >> 20 );
- bg[1] = ( ( d & 0x000F0000 ) >> 12 ) | ( ( d & 0x000F0000 ) >> 16 );
- bb[0] = ( ( d & 0x0000F000 ) >> 8 ) | ( ( d & 0x0000F000 ) >> 12 );
- bb[1] = ( ( d & 0x00000F00 ) >> 4 ) | ( ( d & 0x00000F00 ) >> 8 );
- }
-
- unsigned int tcw[2];
- tcw[0] = ( d & 0xE0 ) >> 5;
- tcw[1] = ( d & 0x1C ) >> 2;
-
- uint32_t b1 = ( d >> 32 ) & 0xFFFF;
- uint32_t b2 = ( d >> 48 );
-
- b1 = ( b1 | ( b1 << 8 ) ) & 0x00FF00FF;
- b1 = ( b1 | ( b1 << 4 ) ) & 0x0F0F0F0F;
- b1 = ( b1 | ( b1 << 2 ) ) & 0x33333333;
- b1 = ( b1 | ( b1 << 1 ) ) & 0x55555555;
-
- b2 = ( b2 | ( b2 << 8 ) ) & 0x00FF00FF;
- b2 = ( b2 | ( b2 << 4 ) ) & 0x0F0F0F0F;
- b2 = ( b2 | ( b2 << 2 ) ) & 0x33333333;
- b2 = ( b2 | ( b2 << 1 ) ) & 0x55555555;
-
- uint32_t idx = b1 | ( b2 << 1 );
-
- if( d & 0x1 )
- {
- for( int i=0; i<4; i++ )
- {
- for( int j=0; j<4; j++ )
- {
- const auto mod = g_table[tcw[j/2]][idx & 0x3];
- const auto r = br[j/2] + mod;
- const auto g = bg[j/2] + mod;
- const auto b = bb[j/2] + mod;
- if( ( ( r | g | b ) & ~0xFF ) == 0 )
- {
- dst[j*w+i] = r | ( g << 8 ) | ( b << 16 ) | 0xFF000000;
- }
- else
- {
- const auto rc = clampu8( r );
- const auto gc = clampu8( g );
- const auto bc = clampu8( b );
- dst[j*w+i] = rc | ( gc << 8 ) | ( bc << 16 ) | 0xFF000000;
- }
- idx >>= 2;
- }
- }
- }
- else
- {
- for( int i=0; i<4; i++ )
- {
- const auto tbl = g_table[tcw[i/2]];
- const auto cr = br[i/2];
- const auto cg = bg[i/2];
- const auto cb = bb[i/2];
-
- for( int j=0; j<4; j++ )
- {
- const auto mod = tbl[idx & 0x3];
- const auto r = cr + mod;
- const auto g = cg + mod;
- const auto b = cb + mod;
- if( ( ( r | g | b ) & ~0xFF ) == 0 )
- {
- dst[j*w+i] = r | ( g << 8 ) | ( b << 16 ) | 0xFF000000;
- }
- else
- {
- const auto rc = clampu8( r );
- const auto gc = clampu8( g );
- const auto bc = clampu8( b );
- dst[j*w+i] = rc | ( gc << 8 ) | ( bc << 16 ) | 0xFF000000;
- }
- idx >>= 2;
- }
- }
- }
-}
-
-static etcpak_force_inline void DecodeRGBAPart( uint64_t d, uint64_t alpha, uint32_t* dst, uint32_t w )
-{
- d = ConvertByteOrder( d );
- alpha = _bswap64( alpha );
-
- uint32_t br[2], bg[2], bb[2];
-
- if( d & 0x2 )
- {
- int32_t dr, dg, db;
-
- uint32_t r0 = ( d & 0xF8000000 ) >> 27;
- uint32_t g0 = ( d & 0x00F80000 ) >> 19;
- uint32_t b0 = ( d & 0x0000F800 ) >> 11;
-
- dr = ( int32_t(d) << 5 ) >> 29;
- dg = ( int32_t(d) << 13 ) >> 29;
- db = ( int32_t(d) << 21 ) >> 29;
-
- int32_t r1 = int32_t(r0) + dr;
- int32_t g1 = int32_t(g0) + dg;
- int32_t b1 = int32_t(b0) + db;
-
- // T mode
- if ( (r1 < 0) || (r1 > 31) )
- {
- DecodeTAlpha( d, alpha, dst, w );
- return;
- }
-
- // H mode
- if ( (g1 < 0) || (g1 > 31) )
- {
- DecodeHAlpha( d, alpha, dst, w );
- return;
- }
-
- // P mode
- if ( (b1 < 0) || (b1 > 31) )
- {
- DecodePlanarAlpha( d, alpha, dst, w );
- return;
- }
-
- br[0] = ( r0 << 3 ) | ( r0 >> 2 );
- br[1] = ( r1 << 3 ) | ( r1 >> 2 );
- bg[0] = ( g0 << 3 ) | ( g0 >> 2 );
- bg[1] = ( g1 << 3 ) | ( g1 >> 2 );
- bb[0] = ( b0 << 3 ) | ( b0 >> 2 );
- bb[1] = ( b1 << 3 ) | ( b1 >> 2 );
- }
- else
- {
- br[0] = ( ( d & 0xF0000000 ) >> 24 ) | ( ( d & 0xF0000000 ) >> 28 );
- br[1] = ( ( d & 0x0F000000 ) >> 20 ) | ( ( d & 0x0F000000 ) >> 24 );
- bg[0] = ( ( d & 0x00F00000 ) >> 16 ) | ( ( d & 0x00F00000 ) >> 20 );
- bg[1] = ( ( d & 0x000F0000 ) >> 12 ) | ( ( d & 0x000F0000 ) >> 16 );
- bb[0] = ( ( d & 0x0000F000 ) >> 8 ) | ( ( d & 0x0000F000 ) >> 12 );
- bb[1] = ( ( d & 0x00000F00 ) >> 4 ) | ( ( d & 0x00000F00 ) >> 8 );
- }
-
- unsigned int tcw[2];
- tcw[0] = ( d & 0xE0 ) >> 5;
- tcw[1] = ( d & 0x1C ) >> 2;
-
- uint32_t b1 = ( d >> 32 ) & 0xFFFF;
- uint32_t b2 = ( d >> 48 );
-
- b1 = ( b1 | ( b1 << 8 ) ) & 0x00FF00FF;
- b1 = ( b1 | ( b1 << 4 ) ) & 0x0F0F0F0F;
- b1 = ( b1 | ( b1 << 2 ) ) & 0x33333333;
- b1 = ( b1 | ( b1 << 1 ) ) & 0x55555555;
-
- b2 = ( b2 | ( b2 << 8 ) ) & 0x00FF00FF;
- b2 = ( b2 | ( b2 << 4 ) ) & 0x0F0F0F0F;
- b2 = ( b2 | ( b2 << 2 ) ) & 0x33333333;
- b2 = ( b2 | ( b2 << 1 ) ) & 0x55555555;
-
- uint32_t idx = b1 | ( b2 << 1 );
-
- const int32_t base = alpha >> 56;
- const int32_t mul = ( alpha >> 52 ) & 0xF;
- const auto atbl = g_alpha[( alpha >> 48 ) & 0xF];
-
- if( d & 0x1 )
- {
- for( int i=0; i<4; i++ )
- {
- for( int j=0; j<4; j++ )
- {
- const auto mod = g_table[tcw[j/2]][idx & 0x3];
- const auto r = br[j/2] + mod;
- const auto g = bg[j/2] + mod;
- const auto b = bb[j/2] + mod;
- const auto amod = atbl[(alpha >> ( 45 - j*3 - i*12 )) & 0x7];
- const uint32_t a = clampu8( base + amod * mul );
- if( ( ( r | g | b ) & ~0xFF ) == 0 )
- {
- dst[j*w+i] = r | ( g << 8 ) | ( b << 16 ) | ( a << 24 );
- }
- else
- {
- const auto rc = clampu8( r );
- const auto gc = clampu8( g );
- const auto bc = clampu8( b );
- dst[j*w+i] = rc | ( gc << 8 ) | ( bc << 16 ) | ( a << 24 );
- }
- idx >>= 2;
- }
- }
- }
- else
- {
- for( int i=0; i<4; i++ )
- {
- const auto tbl = g_table[tcw[i/2]];
- const auto cr = br[i/2];
- const auto cg = bg[i/2];
- const auto cb = bb[i/2];
-
- for( int j=0; j<4; j++ )
- {
- const auto mod = tbl[idx & 0x3];
- const auto r = cr + mod;
- const auto g = cg + mod;
- const auto b = cb + mod;
- const auto amod = atbl[(alpha >> ( 45 - j*3 - i*12 )) & 0x7];
- const uint32_t a = clampu8( base + amod * mul );
- if( ( ( r | g | b ) & ~0xFF ) == 0 )
- {
- dst[j*w+i] = r | ( g << 8 ) | ( b << 16 ) | ( a << 24 );
- }
- else
- {
- const auto rc = clampu8( r );
- const auto gc = clampu8( g );
- const auto bc = clampu8( b );
- dst[j*w+i] = rc | ( gc << 8 ) | ( bc << 16 ) | ( a << 24 );
- }
- idx >>= 2;
- }
- }
- }
-}
-
-BitmapPtr BlockData::DecodeRGB()
-{
- auto ret = std::make_shared<Bitmap>( m_size );
-
- const uint64_t* src = (const uint64_t*)( m_data + m_dataOffset );
- uint32_t* dst = ret->Data();
-
- for( int y=0; y<m_size.y/4; y++ )
- {
- for( int x=0; x<m_size.x/4; x++ )
- {
- uint64_t d = *src++;
- DecodeRGBPart( d, dst, m_size.x );
- dst += 4;
- }
- dst += m_size.x*3;
- }
-
- return ret;
-}
-
-BitmapPtr BlockData::DecodeRGBA()
-{
- auto ret = std::make_shared<Bitmap>( m_size );
-
- const uint64_t* src = (const uint64_t*)( m_data + m_dataOffset );
- uint32_t* dst = ret->Data();
-
- for( int y=0; y<m_size.y/4; y++ )
- {
- for( int x=0; x<m_size.x/4; x++ )
- {
- uint64_t a = *src++;
- uint64_t d = *src++;
- DecodeRGBAPart( d, a, dst, m_size.x );
- dst += 4;
- }
- dst += m_size.x*3;
- }
-
- return ret;
-}
-
-static etcpak_force_inline void DecodeDxt1Part( uint64_t d, uint32_t* dst, uint32_t w )
-{
- uint8_t* in = (uint8_t*)&d;
- uint16_t c0, c1;
- uint32_t idx;
- memcpy( &c0, in, 2 );
- memcpy( &c1, in+2, 2 );
- memcpy( &idx, in+4, 4 );
-
- uint8_t r0 = ( ( c0 & 0xF800 ) >> 8 ) | ( ( c0 & 0xF800 ) >> 13 );
- uint8_t g0 = ( ( c0 & 0x07E0 ) >> 3 ) | ( ( c0 & 0x07E0 ) >> 9 );
- uint8_t b0 = ( ( c0 & 0x001F ) << 3 ) | ( ( c0 & 0x001F ) >> 2 );
-
- uint8_t r1 = ( ( c1 & 0xF800 ) >> 8 ) | ( ( c1 & 0xF800 ) >> 13 );
- uint8_t g1 = ( ( c1 & 0x07E0 ) >> 3 ) | ( ( c1 & 0x07E0 ) >> 9 );
- uint8_t b1 = ( ( c1 & 0x001F ) << 3 ) | ( ( c1 & 0x001F ) >> 2 );
-
- uint32_t dict[4];
-
- dict[0] = 0xFF000000 | ( b0 << 16 ) | ( g0 << 8 ) | r0;
- dict[1] = 0xFF000000 | ( b1 << 16 ) | ( g1 << 8 ) | r1;
-
- uint32_t r, g, b;
- if( c0 > c1 )
- {
- r = (2*r0+r1)/3;
- g = (2*g0+g1)/3;
- b = (2*b0+b1)/3;
- dict[2] = 0xFF000000 | ( b << 16 ) | ( g << 8 ) | r;
- r = (2*r1+r0)/3;
- g = (2*g1+g0)/3;
- b = (2*b1+b0)/3;
- dict[3] = 0xFF000000 | ( b << 16 ) | ( g << 8 ) | r;
- }
- else
- {
- r = (int(r0)+r1)/2;
- g = (int(g0)+g1)/2;
- b = (int(b0)+b1)/2;
- dict[2] = 0xFF000000 | ( b << 16 ) | ( g << 8 ) | r;
- dict[3] = 0xFF000000;
- }
-
- memcpy( dst+0, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+1, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+2, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+3, dict + (idx & 0x3), 4 );
- idx >>= 2;
- dst += w;
-
- memcpy( dst+0, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+1, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+2, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+3, dict + (idx & 0x3), 4 );
- idx >>= 2;
- dst += w;
-
- memcpy( dst+0, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+1, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+2, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+3, dict + (idx & 0x3), 4 );
- idx >>= 2;
- dst += w;
-
- memcpy( dst+0, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+1, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+2, dict + (idx & 0x3), 4 );
- idx >>= 2;
- memcpy( dst+3, dict + (idx & 0x3), 4 );
-}
-
-static etcpak_force_inline void DecodeDxt5Part( uint64_t a, uint64_t d, uint32_t* dst, uint32_t w )
-{
- uint8_t* ain = (uint8_t*)&a;
- uint8_t a0, a1;
- uint64_t aidx = 0;
- memcpy( &a0, ain, 1 );
- memcpy( &a1, ain+1, 1 );
- memcpy( &aidx, ain+2, 6 );
-
- uint8_t* in = (uint8_t*)&d;
- uint16_t c0, c1;
- uint32_t idx;
- memcpy( &c0, in, 2 );
- memcpy( &c1, in+2, 2 );
- memcpy( &idx, in+4, 4 );
-
- uint32_t adict[8];
- adict[0] = a0 << 24;
- adict[1] = a1 << 24;
- if( a0 > a1 )
- {
- adict[2] = ( (6*a0+1*a1)/7 ) << 24;
- adict[3] = ( (5*a0+2*a1)/7 ) << 24;
- adict[4] = ( (4*a0+3*a1)/7 ) << 24;
- adict[5] = ( (3*a0+4*a1)/7 ) << 24;
- adict[6] = ( (2*a0+5*a1)/7 ) << 24;
- adict[7] = ( (1*a0+6*a1)/7 ) << 24;
- }
- else
- {
- adict[2] = ( (4*a0+1*a1)/5 ) << 24;
- adict[3] = ( (3*a0+2*a1)/5 ) << 24;
- adict[4] = ( (2*a0+3*a1)/5 ) << 24;
- adict[5] = ( (1*a0+4*a1)/5 ) << 24;
- adict[6] = 0;
- adict[7] = 0xFF000000;
- }
-
- uint8_t r0 = ( ( c0 & 0xF800 ) >> 8 ) | ( ( c0 & 0xF800 ) >> 13 );
- uint8_t g0 = ( ( c0 & 0x07E0 ) >> 3 ) | ( ( c0 & 0x07E0 ) >> 9 );
- uint8_t b0 = ( ( c0 & 0x001F ) << 3 ) | ( ( c0 & 0x001F ) >> 2 );
-
- uint8_t r1 = ( ( c1 & 0xF800 ) >> 8 ) | ( ( c1 & 0xF800 ) >> 13 );
- uint8_t g1 = ( ( c1 & 0x07E0 ) >> 3 ) | ( ( c1 & 0x07E0 ) >> 9 );
- uint8_t b1 = ( ( c1 & 0x001F ) << 3 ) | ( ( c1 & 0x001F ) >> 2 );
-
- uint32_t dict[4];
-
- dict[0] = ( b0 << 16 ) | ( g0 << 8 ) | r0;
- dict[1] = ( b1 << 16 ) | ( g1 << 8 ) | r1;
-
- uint32_t r, g, b;
- if( c0 > c1 )
- {
- r = (2*r0+r1)/3;
- g = (2*g0+g1)/3;
- b = (2*b0+b1)/3;
- dict[2] = ( b << 16 ) | ( g << 8 ) | r;
- r = (2*r1+r0)/3;
- g = (2*g1+g0)/3;
- b = (2*b1+b0)/3;
- dict[3] = ( b << 16 ) | ( g << 8 ) | r;
- }
- else
- {
- r = (int(r0)+r1)/2;
- g = (int(g0)+g1)/2;
- b = (int(b0)+b1)/2;
- dict[2] = ( b << 16 ) | ( g << 8 ) | r;
- dict[3] = 0;
- }
-
- dst[0] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[1] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[2] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[3] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst += w;
-
- dst[0] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[1] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[2] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[3] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst += w;
-
- dst[0] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[1] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[2] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[3] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst += w;
-
- dst[0] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[1] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[2] = dict[idx & 0x3] | adict[aidx & 0x7];
- idx >>= 2;
- aidx >>= 3;
- dst[3] = dict[idx & 0x3] | adict[aidx & 0x7];
-}
-
-BitmapPtr BlockData::DecodeDxt1()
-{
- auto ret = std::make_shared<Bitmap>( m_size );
-
- const uint64_t* src = (const uint64_t*)( m_data + m_dataOffset );
- uint32_t* dst = ret->Data();
-
- for( int y=0; y<m_size.y/4; y++ )
- {
- for( int x=0; x<m_size.x/4; x++ )
- {
- uint64_t d = *src++;
- DecodeDxt1Part( d, dst, m_size.x );
- dst += 4;
- }
- dst += m_size.x*3;
- }
-
- return ret;
-}
-
-BitmapPtr BlockData::DecodeDxt5()
-{
- auto ret = std::make_shared<Bitmap>( m_size );
-
- const uint64_t* src = (const uint64_t*)( m_data + m_dataOffset );
- uint32_t* dst = ret->Data();
-
- for( int y=0; y<m_size.y/4; y++ )
- {
- for( int x=0; x<m_size.x/4; x++ )
- {
- uint64_t a = *src++;
- uint64_t d = *src++;
- DecodeDxt5Part( a, d, dst, m_size.x );
- dst += 4;
- }
- dst += m_size.x*3;
- }
-
- return ret;
-}
diff --git a/thirdparty/etcpak/BlockData.hpp b/thirdparty/etcpak/BlockData.hpp
deleted file mode 100644
index 209e35b4e6..0000000000
--- a/thirdparty/etcpak/BlockData.hpp
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef __BLOCKDATA_HPP__
-#define __BLOCKDATA_HPP__
-
-#include <condition_variable>
-#include <future>
-#include <memory>
-#include <mutex>
-#include <stdint.h>
-#include <stdio.h>
-#include <vector>
-
-#include "Bitmap.hpp"
-#include "ForceInline.hpp"
-#include "Vector.hpp"
-
-class BlockData
-{
-public:
- enum Type
- {
- Etc1,
- Etc2_RGB,
- Etc2_RGBA,
- Dxt1,
- Dxt5
- };
-
- BlockData( const char* fn );
- BlockData( const char* fn, const v2i& size, bool mipmap, Type type );
- BlockData( const v2i& size, bool mipmap, Type type );
- ~BlockData();
-
- BitmapPtr Decode();
-
- void Process( const uint32_t* src, uint32_t blocks, size_t offset, size_t width, Channels type, bool dither );
- void ProcessRGBA( const uint32_t* src, uint32_t blocks, size_t offset, size_t width );
-
- const v2i& Size() const { return m_size; }
-
-private:
- etcpak_no_inline BitmapPtr DecodeRGB();
- etcpak_no_inline BitmapPtr DecodeRGBA();
- etcpak_no_inline BitmapPtr DecodeDxt1();
- etcpak_no_inline BitmapPtr DecodeDxt5();
-
- uint8_t* m_data;
- v2i m_size;
- size_t m_dataOffset;
- FILE* m_file;
- size_t m_maplen;
- Type m_type;
-};
-
-typedef std::shared_ptr<BlockData> BlockDataPtr;
-
-#endif
diff --git a/thirdparty/etcpak/ColorSpace.cpp b/thirdparty/etcpak/ColorSpace.cpp
deleted file mode 100644
index 0411541066..0000000000
--- a/thirdparty/etcpak/ColorSpace.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-#include <math.h>
-#include <stdint.h>
-
-#include "Math.hpp"
-#include "ColorSpace.hpp"
-
-namespace Color
-{
-
- static const XYZ white( v3b( 255, 255, 255 ) );
- static const v3f rwhite( 1.f / white.x, 1.f / white.y, 1.f / white.z );
-
-
- XYZ::XYZ( float _x, float _y, float _z )
- : x( _x )
- , y( _y )
- , z( _z )
- {
- }
-
- XYZ::XYZ( const v3b& rgb )
- {
- const float r = rgb.x / 255.f;
- const float g = rgb.y / 255.f;
- const float b = rgb.z / 255.f;
-
- const float rl = sRGB2linear( r );
- const float gl = sRGB2linear( g );
- const float bl = sRGB2linear( b );
-
- x = 0.4124f * rl + 0.3576f * gl + 0.1805f * bl;
- y = 0.2126f * rl + 0.7152f * gl + 0.0722f * bl;
- z = 0.0193f * rl + 0.1192f * gl + 0.9505f * bl;
- }
-
- static float revlab( float t )
- {
- const float p1 = 6.f/29.f;
- const float p2 = 4.f/29.f;
-
- if( t > p1 )
- {
- return t*t*t;
- }
- else
- {
- return 3 * sq( p1 ) * ( t - p2 );
- }
- }
-
- XYZ::XYZ( const Lab& lab )
- {
- y = white.y * revlab( 1.f/116.f * ( lab.L + 16 ) );
- x = white.x * revlab( 1.f/116.f * ( lab.L + 16 ) + 1.f/500.f * lab.a );
- z = white.z * revlab( 1.f/116.f * ( lab.L + 16 ) - 1.f/200.f * lab.b );
- }
-
- v3i XYZ::RGB() const
- {
- const float rl = 3.2406f * x - 1.5372f * y - 0.4986f * z;
- const float gl = -0.9689f * x + 1.8758f * y + 0.0415f * z;
- const float bl = 0.0557f * x - 0.2040f * y + 1.0570f * z;
-
- const float r = linear2sRGB( rl );
- const float g = linear2sRGB( gl );
- const float b = linear2sRGB( bl );
-
- return v3i( clampu8( int32_t( r * 255 ) ), clampu8( int32_t( g * 255 ) ), clampu8( int32_t( b * 255 ) ) );
- }
-
-
- Lab::Lab()
- : L( 0 )
- , a( 0 )
- , b( 0 )
- {
- }
-
- Lab::Lab( float L, float a, float b )
- : L( L )
- , a( a )
- , b( b )
- {
- }
-
- static float labfunc( float t )
- {
- const float p1 = (6.f/29.f)*(6.f/29.f)*(6.f/29.f);
- const float p2 = (1.f/3.f)*(29.f/6.f)*(29.f/6.f);
- const float p3 = (4.f/29.f);
-
- if( t > p1 )
- {
- return pow( t, 1.f/3.f );
- }
- else
- {
- return p2 * t + p3;
- }
- }
-
- Lab::Lab( const XYZ& xyz )
- {
- L = 116 * labfunc( xyz.y * rwhite.y ) - 16;
- a = 500 * ( labfunc( xyz.x * rwhite.x ) - labfunc( xyz.y * rwhite.y ) );
- b = 200 * ( labfunc( xyz.y * rwhite.y ) - labfunc( xyz.z * rwhite.z ) );
- }
-
- Lab::Lab( const v3b& rgb )
- {
- new(this) Lab( XYZ( rgb ) );
- }
-
-}
diff --git a/thirdparty/etcpak/ColorSpace.hpp b/thirdparty/etcpak/ColorSpace.hpp
deleted file mode 100644
index c9d0a9cf3f..0000000000
--- a/thirdparty/etcpak/ColorSpace.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef __DARKRL__COLORSPACE_HPP__
-#define __DARKRL__COLORSPACE_HPP__
-
-#include "Vector.hpp"
-
-namespace Color
-{
-
- class Lab;
-
- class XYZ
- {
- public:
- XYZ( float x, float y, float z );
- XYZ( const v3b& rgb );
- XYZ( const Lab& lab );
-
- v3i RGB() const;
-
- float x, y, z;
- };
-
- class Lab
- {
- public:
- Lab();
- Lab( float L, float a, float b );
- Lab( const XYZ& xyz );
- Lab( const v3b& rgb );
-
- float L, a, b;
- };
-
-}
-
-#endif
diff --git a/thirdparty/etcpak/DataProvider.cpp b/thirdparty/etcpak/DataProvider.cpp
deleted file mode 100644
index 6bd4b105ed..0000000000
--- a/thirdparty/etcpak/DataProvider.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#include <assert.h>
-#include <utility>
-
-#include "BitmapDownsampled.hpp"
-#include "DataProvider.hpp"
-#include "MipMap.hpp"
-
-DataProvider::DataProvider( const char* fn, bool mipmap, bool bgr )
- : m_offset( 0 )
- , m_mipmap( mipmap )
- , m_done( false )
- , m_lines( 32 )
-{
- m_bmp.emplace_back( new Bitmap( fn, m_lines, bgr ) );
- m_current = m_bmp[0].get();
-}
-
-DataProvider::~DataProvider()
-{
-}
-
-unsigned int DataProvider::NumberOfParts() const
-{
- unsigned int parts = ( ( m_bmp[0]->Size().y / 4 ) + m_lines - 1 ) / m_lines;
-
- if( m_mipmap )
- {
- v2i current = m_bmp[0]->Size();
- int levels = NumberOfMipLevels( current );
- unsigned int lines = m_lines;
- for( int i=1; i<levels; i++ )
- {
- assert( current.x != 1 || current.y != 1 );
- current.x = std::max( 1, current.x / 2 );
- current.y = std::max( 1, current.y / 2 );
- lines *= 2;
- parts += ( ( std::max( 4, current.y ) / 4 ) + lines - 1 ) / lines;
- }
- assert( current.x == 1 && current.y == 1 );
- }
-
- return parts;
-}
-
-DataPart DataProvider::NextPart()
-{
- assert( !m_done );
-
- unsigned int lines = m_lines;
- bool done;
-
- const auto ptr = m_current->NextBlock( lines, done );
- DataPart ret = {
- ptr,
- std::max<unsigned int>( 4, m_current->Size().x ),
- lines,
- m_offset
- };
-
- m_offset += m_current->Size().x / 4 * lines;
-
- if( done )
- {
- if( m_mipmap && ( m_current->Size().x != 1 || m_current->Size().y != 1 ) )
- {
- m_lines *= 2;
- m_bmp.emplace_back( new BitmapDownsampled( *m_current, m_lines ) );
- m_current = m_bmp[m_bmp.size()-1].get();
- }
- else
- {
- m_done = true;
- }
- }
-
- return ret;
-}
diff --git a/thirdparty/etcpak/DataProvider.hpp b/thirdparty/etcpak/DataProvider.hpp
deleted file mode 100644
index e773801ed6..0000000000
--- a/thirdparty/etcpak/DataProvider.hpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef __DATAPROVIDER_HPP__
-#define __DATAPROVIDER_HPP__
-
-#include <memory>
-#include <stdint.h>
-#include <vector>
-
-#include "Bitmap.hpp"
-
-struct DataPart
-{
- const uint32_t* src;
- unsigned int width;
- unsigned int lines;
- unsigned int offset;
-};
-
-class DataProvider
-{
-public:
- DataProvider( const char* fn, bool mipmap, bool bgr );
- ~DataProvider();
-
- unsigned int NumberOfParts() const;
-
- DataPart NextPart();
-
- bool Alpha() const { return m_bmp[0]->Alpha(); }
- const v2i& Size() const { return m_bmp[0]->Size(); }
- const Bitmap& ImageData() const { return *m_bmp[0]; }
-
-private:
- std::vector<std::unique_ptr<Bitmap>> m_bmp;
- Bitmap* m_current;
- unsigned int m_offset;
- unsigned int m_lines;
- bool m_mipmap;
- bool m_done;
-};
-
-#endif
diff --git a/thirdparty/etcpak/Debug.cpp b/thirdparty/etcpak/Debug.cpp
deleted file mode 100644
index 72dc4e0526..0000000000
--- a/thirdparty/etcpak/Debug.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <algorithm>
-#include <vector>
-#include "Debug.hpp"
-
-static std::vector<DebugLog::Callback*> s_callbacks;
-
-void DebugLog::Message( const char* msg )
-{
- for( auto it = s_callbacks.begin(); it != s_callbacks.end(); ++it )
- {
- (*it)->OnDebugMessage( msg );
- }
-}
-
-void DebugLog::AddCallback( Callback* c )
-{
- const auto it = std::find( s_callbacks.begin(), s_callbacks.end(), c );
- if( it == s_callbacks.end() )
- {
- s_callbacks.push_back( c );
- }
-}
-
-void DebugLog::RemoveCallback( Callback* c )
-{
- const auto it = std::find( s_callbacks.begin(), s_callbacks.end(), c );
- if( it != s_callbacks.end() )
- {
- s_callbacks.erase( it );
- }
-}
diff --git a/thirdparty/etcpak/Debug.hpp b/thirdparty/etcpak/Debug.hpp
deleted file mode 100644
index 524eaa7baf..0000000000
--- a/thirdparty/etcpak/Debug.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef __DARKRL__DEBUG_HPP__
-#define __DARKRL__DEBUG_HPP__
-
-#ifdef DEBUG
-# include <sstream>
-# define DBGPRINT(msg) { std::stringstream __buf; __buf << msg; DebugLog::Message( __buf.str().c_str() ); }
-#else
-# define DBGPRINT(msg) ((void)0)
-#endif
-
-class DebugLog
-{
-public:
- struct Callback
- {
- virtual void OnDebugMessage( const char* msg ) = 0;
- };
-
- static void Message( const char* msg );
- static void AddCallback( Callback* c );
- static void RemoveCallback( Callback* c );
-
-private:
- DebugLog() {}
-};
-
-#endif
diff --git a/thirdparty/etcpak/Error.cpp b/thirdparty/etcpak/Error.cpp
deleted file mode 100644
index 014ecdab66..0000000000
--- a/thirdparty/etcpak/Error.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include <stdint.h>
-
-#include "Error.hpp"
-#include "Math.hpp"
-
-float CalcMSE3( const Bitmap& bmp, const Bitmap& out )
-{
- float err = 0;
-
- const uint32_t* p1 = bmp.Data();
- const uint32_t* p2 = out.Data();
- size_t cnt = bmp.Size().x * bmp.Size().y;
-
- for( size_t i=0; i<cnt; i++ )
- {
- uint32_t c1 = *p1++;
- uint32_t c2 = *p2++;
-
- err += sq( ( c1 & 0x000000FF ) - ( c2 & 0x000000FF ) );
- err += sq( ( ( c1 & 0x0000FF00 ) >> 8 ) - ( ( c2 & 0x0000FF00 ) >> 8 ) );
- err += sq( ( ( c1 & 0x00FF0000 ) >> 16 ) - ( ( c2 & 0x00FF0000 ) >> 16 ) );
- }
-
- err /= cnt * 3;
-
- return err;
-}
-
-float CalcMSE1( const Bitmap& bmp, const Bitmap& out )
-{
- float err = 0;
-
- const uint32_t* p1 = bmp.Data();
- const uint32_t* p2 = out.Data();
- size_t cnt = bmp.Size().x * bmp.Size().y;
-
- for( size_t i=0; i<cnt; i++ )
- {
- uint32_t c1 = *p1++;
- uint32_t c2 = *p2++;
-
- err += sq( ( c1 >> 24 ) - ( c2 & 0xFF ) );
- }
-
- err /= cnt;
-
- return err;
-}
diff --git a/thirdparty/etcpak/Error.hpp b/thirdparty/etcpak/Error.hpp
deleted file mode 100644
index 9817754b74..0000000000
--- a/thirdparty/etcpak/Error.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef __ERROR_HPP__
-#define __ERROR_HPP__
-
-#include "Bitmap.hpp"
-
-float CalcMSE3( const Bitmap& bmp, const Bitmap& out );
-float CalcMSE1( const Bitmap& bmp, const Bitmap& out );
-
-#endif
diff --git a/thirdparty/etcpak/MipMap.hpp b/thirdparty/etcpak/MipMap.hpp
deleted file mode 100644
index d3b4bc9e7c..0000000000
--- a/thirdparty/etcpak/MipMap.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __MIPMAP_HPP__
-#define __MIPMAP_HPP__
-
-#include "Vector.hpp"
-
-inline int NumberOfMipLevels( const v2i& size )
-{
- return (int)floor( log2( std::max( size.x, size.y ) ) ) + 1;
-}
-
-#endif
diff --git a/thirdparty/etcpak/Semaphore.hpp b/thirdparty/etcpak/Semaphore.hpp
deleted file mode 100644
index 9e42dbb9e0..0000000000
--- a/thirdparty/etcpak/Semaphore.hpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef __DARKRL__SEMAPHORE_HPP__
-#define __DARKRL__SEMAPHORE_HPP__
-
-#include <condition_variable>
-#include <mutex>
-
-class Semaphore
-{
-public:
- Semaphore( int count ) : m_count( count ) {}
-
- void lock()
- {
- std::unique_lock<std::mutex> lock( m_mutex );
- m_cv.wait( lock, [this](){ return m_count != 0; } );
- m_count--;
- }
-
- void unlock()
- {
- std::lock_guard<std::mutex> lock( m_mutex );
- m_count++;
- m_cv.notify_one();
- }
-
- bool try_lock()
- {
- std::lock_guard<std::mutex> lock( m_mutex );
- if( m_count == 0 )
- {
- return false;
- }
- else
- {
- m_count--;
- return true;
- }
- }
-
-private:
- std::mutex m_mutex;
- std::condition_variable m_cv;
- unsigned int m_count;
-};
-
-#endif
diff --git a/thirdparty/etcpak/System.cpp b/thirdparty/etcpak/System.cpp
deleted file mode 100644
index a09b289cb2..0000000000
--- a/thirdparty/etcpak/System.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <algorithm>
-#ifdef _WIN32
-# include <windows.h>
-#else
-# include <pthread.h>
-# include <unistd.h>
-#endif
-
-#include "System.hpp"
-
-unsigned int System::CPUCores()
-{
- static unsigned int cores = 0;
- if( cores == 0 )
- {
- int tmp;
-#ifdef _WIN32
- SYSTEM_INFO info;
- GetSystemInfo( &info );
- tmp = (int)info.dwNumberOfProcessors;
-#else
-# ifndef _SC_NPROCESSORS_ONLN
-# ifdef _SC_NPROC_ONLN
-# define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
-# elif defined _SC_CRAY_NCPU
-# define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
-# endif
-# endif
- tmp = (int)(long)sysconf( _SC_NPROCESSORS_ONLN );
-#endif
- cores = (unsigned int)std::max( tmp, 1 );
- }
- return cores;
-}
-
-void System::SetThreadName( std::thread& thread, const char* name )
-{
-#ifdef _MSC_VER
- const DWORD MS_VC_EXCEPTION=0x406D1388;
-
-# pragma pack( push, 8 )
- struct THREADNAME_INFO
- {
- DWORD dwType;
- LPCSTR szName;
- DWORD dwThreadID;
- DWORD dwFlags;
- };
-# pragma pack(pop)
-
- DWORD ThreadId = GetThreadId( static_cast<HANDLE>( thread.native_handle() ) );
- THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = name;
- info.dwThreadID = ThreadId;
- info.dwFlags = 0;
-
- __try
- {
- RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- }
-#elif !defined(__APPLE__)
- pthread_setname_np( thread.native_handle(), name );
-#endif
-}
diff --git a/thirdparty/etcpak/System.hpp b/thirdparty/etcpak/System.hpp
deleted file mode 100644
index 1a09bb15e1..0000000000
--- a/thirdparty/etcpak/System.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef __DARKRL__SYSTEM_HPP__
-#define __DARKRL__SYSTEM_HPP__
-
-#include <thread>
-
-class System
-{
-public:
- System() = delete;
-
- static unsigned int CPUCores();
- static void SetThreadName( std::thread& thread, const char* name );
-};
-
-#endif
diff --git a/thirdparty/etcpak/TaskDispatch.cpp b/thirdparty/etcpak/TaskDispatch.cpp
deleted file mode 100644
index 7287da4de2..0000000000
--- a/thirdparty/etcpak/TaskDispatch.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-#include <assert.h>
-#include <stdio.h>
-
-#include "Debug.hpp"
-#include "System.hpp"
-#include "TaskDispatch.hpp"
-
-static TaskDispatch* s_instance = nullptr;
-
-TaskDispatch::TaskDispatch( size_t workers )
- : m_exit( false )
- , m_jobs( 0 )
-{
- assert( !s_instance );
- s_instance = this;
-
- assert( workers >= 1 );
- workers--;
-
- m_workers.reserve( workers );
- for( size_t i=0; i<workers; i++ )
- {
- char tmp[16];
- sprintf( tmp, "Worker %zu", i );
-#ifdef __APPLE__
- auto worker = std::thread( [this, tmp]{
- pthread_setname_np( tmp );
- Worker();
- } );
-#else
- auto worker = std::thread( [this]{ Worker(); } );
-#endif
- System::SetThreadName( worker, tmp );
- m_workers.emplace_back( std::move( worker ) );
- }
-
- DBGPRINT( "Task dispatcher with " << m_workers.size() + 1 << " workers" );
-}
-
-TaskDispatch::~TaskDispatch()
-{
- m_exit = true;
- m_queueLock.lock();
- m_cvWork.notify_all();
- m_queueLock.unlock();
-
- for( auto& worker : m_workers )
- {
- worker.join();
- }
-
- assert( s_instance );
- s_instance = nullptr;
-}
-
-void TaskDispatch::Queue( const std::function<void(void)>& f )
-{
- std::unique_lock<std::mutex> lock( s_instance->m_queueLock );
- s_instance->m_queue.emplace_back( f );
- const auto size = s_instance->m_queue.size();
- lock.unlock();
- if( size > 1 )
- {
- s_instance->m_cvWork.notify_one();
- }
-}
-
-void TaskDispatch::Queue( std::function<void(void)>&& f )
-{
- std::unique_lock<std::mutex> lock( s_instance->m_queueLock );
- s_instance->m_queue.emplace_back( std::move( f ) );
- const auto size = s_instance->m_queue.size();
- lock.unlock();
- if( size > 1 )
- {
- s_instance->m_cvWork.notify_one();
- }
-}
-
-void TaskDispatch::Sync()
-{
- std::unique_lock<std::mutex> lock( s_instance->m_queueLock );
- while( !s_instance->m_queue.empty() )
- {
- auto f = s_instance->m_queue.back();
- s_instance->m_queue.pop_back();
- lock.unlock();
- f();
- lock.lock();
- }
- s_instance->m_cvJobs.wait( lock, []{ return s_instance->m_jobs == 0; } );
-}
-
-void TaskDispatch::Worker()
-{
- for(;;)
- {
- std::unique_lock<std::mutex> lock( m_queueLock );
- m_cvWork.wait( lock, [this]{ return !m_queue.empty() || m_exit; } );
- if( m_exit ) return;
- auto f = m_queue.back();
- m_queue.pop_back();
- m_jobs++;
- lock.unlock();
- f();
- lock.lock();
- m_jobs--;
- bool notify = m_jobs == 0 && m_queue.empty();
- lock.unlock();
- if( notify )
- {
- m_cvJobs.notify_all();
- }
- }
-}
diff --git a/thirdparty/etcpak/TaskDispatch.hpp b/thirdparty/etcpak/TaskDispatch.hpp
deleted file mode 100644
index b513de4c0c..0000000000
--- a/thirdparty/etcpak/TaskDispatch.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef __DARKRL__TASKDISPATCH_HPP__
-#define __DARKRL__TASKDISPATCH_HPP__
-
-#include <atomic>
-#include <condition_variable>
-#include <functional>
-#include <mutex>
-#include <thread>
-#include <vector>
-
-class TaskDispatch
-{
-public:
- TaskDispatch( size_t workers );
- ~TaskDispatch();
-
- static void Queue( const std::function<void(void)>& f );
- static void Queue( std::function<void(void)>&& f );
-
- static void Sync();
-
-private:
- void Worker();
-
- std::vector<std::function<void(void)>> m_queue;
- std::mutex m_queueLock;
- std::condition_variable m_cvWork, m_cvJobs;
- std::atomic<bool> m_exit;
- size_t m_jobs;
-
- std::vector<std::thread> m_workers;
-};
-
-#endif
diff --git a/thirdparty/etcpak/Timing.cpp b/thirdparty/etcpak/Timing.cpp
deleted file mode 100644
index 2af851f9a9..0000000000
--- a/thirdparty/etcpak/Timing.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <chrono>
-
-#include "Timing.hpp"
-
-uint64_t GetTime()
-{
- return std::chrono::time_point_cast<std::chrono::microseconds>( std::chrono::high_resolution_clock::now() ).time_since_epoch().count();
-}
diff --git a/thirdparty/etcpak/Timing.hpp b/thirdparty/etcpak/Timing.hpp
deleted file mode 100644
index 3767e20f24..0000000000
--- a/thirdparty/etcpak/Timing.hpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __DARKRL__TIMING_HPP__
-#define __DARKRL__TIMING_HPP__
-
-#include <stdint.h>
-
-uint64_t GetTime();
-
-#endif
diff --git a/thirdparty/etcpak/lz4/lz4.c b/thirdparty/etcpak/lz4/lz4.c
deleted file mode 100644
index 08cf6b5cd7..0000000000
--- a/thirdparty/etcpak/lz4/lz4.c
+++ /dev/null
@@ -1,1516 +0,0 @@
-/*
- LZ4 - Fast LZ compression algorithm
- Copyright (C) 2011-2015, Yann Collet.
-
- BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following disclaimer
- in the documentation and/or other materials provided with the
- distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- You can contact the author at :
- - LZ4 source repository : https://github.com/Cyan4973/lz4
- - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
-*/
-
-
-/**************************************
-* Tuning parameters
-**************************************/
-/*
- * HEAPMODE :
- * Select how default compression functions will allocate memory for their hash table,
- * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
- */
-#define HEAPMODE 0
-
-/*
- * ACCELERATION_DEFAULT :
- * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
- */
-#define ACCELERATION_DEFAULT 1
-
-
-/**************************************
-* CPU Feature Detection
-**************************************/
-/*
- * LZ4_FORCE_SW_BITCOUNT
- * Define this parameter if your target system or compiler does not support hardware bit count
- */
-#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */
-# define LZ4_FORCE_SW_BITCOUNT
-#endif
-
-
-/**************************************
-* Includes
-**************************************/
-#include "lz4.h"
-
-
-/**************************************
-* Compiler Options
-**************************************/
-#ifdef _MSC_VER /* Visual Studio */
-# define FORCE_INLINE static __forceinline
-# include <intrin.h>
-# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
-# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
-#else
-# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */
-# if defined(__GNUC__) || defined(__clang__)
-# define FORCE_INLINE static inline __attribute__((always_inline))
-# else
-# define FORCE_INLINE static inline
-# endif
-# else
-# define FORCE_INLINE static
-# endif /* __STDC_VERSION__ */
-#endif /* _MSC_VER */
-
-/* LZ4_GCC_VERSION is defined into lz4.h */
-#if (LZ4_GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__)
-# define expect(expr,value) (__builtin_expect ((expr),(value)) )
-#else
-# define expect(expr,value) (expr)
-#endif
-
-#define likely(expr) expect((expr) != 0, 1)
-#define unlikely(expr) expect((expr) != 0, 0)
-
-
-/**************************************
-* Memory routines
-**************************************/
-#include <stdlib.h> /* malloc, calloc, free */
-#define ALLOCATOR(n,s) calloc(n,s)
-#define FREEMEM free
-#include <string.h> /* memset, memcpy */
-#define MEM_INIT memset
-
-
-/**************************************
-* Basic Types
-**************************************/
-#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */
-# include <stdint.h>
- typedef uint8_t BYTE;
- typedef uint16_t U16;
- typedef uint32_t U32;
- typedef int32_t S32;
- typedef uint64_t U64;
-#else
- typedef unsigned char BYTE;
- typedef unsigned short U16;
- typedef unsigned int U32;
- typedef signed int S32;
- typedef unsigned long long U64;
-#endif
-
-
-/**************************************
-* Reading and writing into memory
-**************************************/
-#define STEPSIZE sizeof(size_t)
-
-static unsigned LZ4_64bits(void) { return sizeof(void*)==8; }
-
-static unsigned LZ4_isLittleEndian(void)
-{
- const union { U32 i; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
- return one.c[0];
-}
-
-
-static U16 LZ4_read16(const void* memPtr)
-{
- U16 val16;
- memcpy(&val16, memPtr, 2);
- return val16;
-}
-
-static U16 LZ4_readLE16(const void* memPtr)
-{
- if (LZ4_isLittleEndian())
- {
- return LZ4_read16(memPtr);
- }
- else
- {
- const BYTE* p = (const BYTE*)memPtr;
- return (U16)((U16)p[0] + (p[1]<<8));
- }
-}
-
-static void LZ4_writeLE16(void* memPtr, U16 value)
-{
- if (LZ4_isLittleEndian())
- {
- memcpy(memPtr, &value, 2);
- }
- else
- {
- BYTE* p = (BYTE*)memPtr;
- p[0] = (BYTE) value;
- p[1] = (BYTE)(value>>8);
- }
-}
-
-static U32 LZ4_read32(const void* memPtr)
-{
- U32 val32;
- memcpy(&val32, memPtr, 4);
- return val32;
-}
-
-static U64 LZ4_read64(const void* memPtr)
-{
- U64 val64;
- memcpy(&val64, memPtr, 8);
- return val64;
-}
-
-static size_t LZ4_read_ARCH(const void* p)
-{
- if (LZ4_64bits())
- return (size_t)LZ4_read64(p);
- else
- return (size_t)LZ4_read32(p);
-}
-
-
-static void LZ4_copy4(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 4); }
-
-static void LZ4_copy8(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 8); }
-
-/* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */
-static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
-{
- BYTE* d = (BYTE*)dstPtr;
- const BYTE* s = (const BYTE*)srcPtr;
- BYTE* e = (BYTE*)dstEnd;
- do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
-}
-
-
-/**************************************
-* Common Constants
-**************************************/
-#define MINMATCH 4
-
-#define COPYLENGTH 8
-#define LASTLITERALS 5
-#define MFLIMIT (COPYLENGTH+MINMATCH)
-static const int LZ4_minLength = (MFLIMIT+1);
-
-#define KB *(1 <<10)
-#define MB *(1 <<20)
-#define GB *(1U<<30)
-
-#define MAXD_LOG 16
-#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
-
-#define ML_BITS 4
-#define ML_MASK ((1U<<ML_BITS)-1)
-#define RUN_BITS (8-ML_BITS)
-#define RUN_MASK ((1U<<RUN_BITS)-1)
-
-
-/**************************************
-* Common Utils
-**************************************/
-#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
-
-
-/**************************************
-* Common functions
-**************************************/
-static unsigned LZ4_NbCommonBytes (register size_t val)
-{
- if (LZ4_isLittleEndian())
- {
- if (LZ4_64bits())
- {
-# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r = 0;
- _BitScanForward64( &r, (U64)val );
- return (int)(r>>3);
-# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_ctzll((U64)val) >> 3);
-# else
- static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
-# endif
- }
- else /* 32 bits */
- {
-# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r;
- _BitScanForward( &r, (U32)val );
- return (int)(r>>3);
-# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_ctz((U32)val) >> 3);
-# else
- static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
-# endif
- }
- }
- else /* Big Endian CPU */
- {
- if (LZ4_64bits())
- {
-# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r = 0;
- _BitScanReverse64( &r, val );
- return (unsigned)(r>>3);
-# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_clzll((U64)val) >> 3);
-# else
- unsigned r;
- if (!(val>>32)) { r=4; } else { r=0; val>>=32; }
- if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
- r += (!val);
- return r;
-# endif
- }
- else /* 32 bits */
- {
-# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r = 0;
- _BitScanReverse( &r, (unsigned long)val );
- return (unsigned)(r>>3);
-# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_clz((U32)val) >> 3);
-# else
- unsigned r;
- if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
- r += (!val);
- return r;
-# endif
- }
- }
-}
-
-static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
-{
- const BYTE* const pStart = pIn;
-
- while (likely(pIn<pInLimit-(STEPSIZE-1)))
- {
- size_t diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
- if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
- pIn += LZ4_NbCommonBytes(diff);
- return (unsigned)(pIn - pStart);
- }
-
- if (LZ4_64bits()) if ((pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
- if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
- if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
- return (unsigned)(pIn - pStart);
-}
-
-
-#ifndef LZ4_COMMONDEFS_ONLY
-/**************************************
-* Local Constants
-**************************************/
-#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
-#define HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
-#define HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
-
-static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
-static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
-
-
-/**************************************
-* Local Structures and types
-**************************************/
-typedef struct {
- U32 hashTable[HASH_SIZE_U32];
- U32 currentOffset;
- U32 initCheck;
- const BYTE* dictionary;
- BYTE* bufferStart; /* obsolete, used for slideInputBuffer */
- U32 dictSize;
-} LZ4_stream_t_internal;
-
-typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
-typedef enum { byPtr, byU32, byU16 } tableType_t;
-
-typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
-typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
-
-typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
-typedef enum { full = 0, partial = 1 } earlyEnd_directive;
-
-
-/**************************************
-* Local Utils
-**************************************/
-int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
-int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
-int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
-
-
-
-/********************************
-* Compression functions
-********************************/
-
-static U32 LZ4_hashSequence(U32 sequence, tableType_t const tableType)
-{
- if (tableType == byU16)
- return (((sequence) * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
- else
- return (((sequence) * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
-}
-
-static const U64 prime5bytes = 889523592379ULL;
-static U32 LZ4_hashSequence64(size_t sequence, tableType_t const tableType)
-{
- const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
- const U32 hashMask = (1<<hashLog) - 1;
- return ((sequence * prime5bytes) >> (40 - hashLog)) & hashMask;
-}
-
-static U32 LZ4_hashSequenceT(size_t sequence, tableType_t const tableType)
-{
- if (LZ4_64bits())
- return LZ4_hashSequence64(sequence, tableType);
- return LZ4_hashSequence((U32)sequence, tableType);
-}
-
-static U32 LZ4_hashPosition(const void* p, tableType_t tableType) { return LZ4_hashSequenceT(LZ4_read_ARCH(p), tableType); }
-
-static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
-{
- switch (tableType)
- {
- case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
- case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
- case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
- }
-}
-
-static void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
-{
- U32 h = LZ4_hashPosition(p, tableType);
- LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
-}
-
-static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase)
-{
- if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; }
- if (tableType == byU32) { U32* hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
- { U16* hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
-}
-
-static const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
-{
- U32 h = LZ4_hashPosition(p, tableType);
- return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
-}
-
-FORCE_INLINE int LZ4_compress_generic(
- void* const ctx,
- const char* const source,
- char* const dest,
- const int inputSize,
- const int maxOutputSize,
- const limitedOutput_directive outputLimited,
- const tableType_t tableType,
- const dict_directive dict,
- const dictIssue_directive dictIssue,
- const U32 acceleration)
-{
- LZ4_stream_t_internal* const dictPtr = (LZ4_stream_t_internal*)ctx;
-
- const BYTE* ip = (const BYTE*) source;
- const BYTE* base;
- const BYTE* lowLimit;
- const BYTE* const lowRefLimit = ip - dictPtr->dictSize;
- const BYTE* const dictionary = dictPtr->dictionary;
- const BYTE* const dictEnd = dictionary + dictPtr->dictSize;
- const size_t dictDelta = dictEnd - (const BYTE*)source;
- const BYTE* anchor = (const BYTE*) source;
- const BYTE* const iend = ip + inputSize;
- const BYTE* const mflimit = iend - MFLIMIT;
- const BYTE* const matchlimit = iend - LASTLITERALS;
-
- BYTE* op = (BYTE*) dest;
- BYTE* const olimit = op + maxOutputSize;
-
- U32 forwardH;
- size_t refDelta=0;
-
- /* Init conditions */
- if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
- switch(dict)
- {
- case noDict:
- default:
- base = (const BYTE*)source;
- lowLimit = (const BYTE*)source;
- break;
- case withPrefix64k:
- base = (const BYTE*)source - dictPtr->currentOffset;
- lowLimit = (const BYTE*)source - dictPtr->dictSize;
- break;
- case usingExtDict:
- base = (const BYTE*)source - dictPtr->currentOffset;
- lowLimit = (const BYTE*)source;
- break;
- }
- if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
- if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
-
- /* First Byte */
- LZ4_putPosition(ip, ctx, tableType, base);
- ip++; forwardH = LZ4_hashPosition(ip, tableType);
-
- /* Main Loop */
- for ( ; ; )
- {
- const BYTE* match;
- BYTE* token;
- {
- const BYTE* forwardIp = ip;
- unsigned step = 1;
- unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
-
- /* Find a match */
- do {
- U32 h = forwardH;
- ip = forwardIp;
- forwardIp += step;
- step = (searchMatchNb++ >> LZ4_skipTrigger);
-
- if (unlikely(forwardIp > mflimit)) goto _last_literals;
-
- match = LZ4_getPositionOnHash(h, ctx, tableType, base);
- if (dict==usingExtDict)
- {
- if (match<(const BYTE*)source)
- {
- refDelta = dictDelta;
- lowLimit = dictionary;
- }
- else
- {
- refDelta = 0;
- lowLimit = (const BYTE*)source;
- }
- }
- forwardH = LZ4_hashPosition(forwardIp, tableType);
- LZ4_putPositionOnHash(ip, h, ctx, tableType, base);
-
- } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0)
- || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
- || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) );
- }
-
- /* Catch up */
- while ((ip>anchor) && (match+refDelta > lowLimit) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; }
-
- {
- /* Encode Literal length */
- unsigned litLength = (unsigned)(ip - anchor);
- token = op++;
- if ((outputLimited) && (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
- return 0; /* Check output limit */
- if (litLength>=RUN_MASK)
- {
- int len = (int)litLength-RUN_MASK;
- *token=(RUN_MASK<<ML_BITS);
- for(; len >= 255 ; len-=255) *op++ = 255;
- *op++ = (BYTE)len;
- }
- else *token = (BYTE)(litLength<<ML_BITS);
-
- /* Copy Literals */
- LZ4_wildCopy(op, anchor, op+litLength);
- op+=litLength;
- }
-
-_next_match:
- /* Encode Offset */
- LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
-
- /* Encode MatchLength */
- {
- unsigned matchLength;
-
- if ((dict==usingExtDict) && (lowLimit==dictionary))
- {
- const BYTE* limit;
- match += refDelta;
- limit = ip + (dictEnd-match);
- if (limit > matchlimit) limit = matchlimit;
- matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
- ip += MINMATCH + matchLength;
- if (ip==limit)
- {
- unsigned more = LZ4_count(ip, (const BYTE*)source, matchlimit);
- matchLength += more;
- ip += more;
- }
- }
- else
- {
- matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
- ip += MINMATCH + matchLength;
- }
-
- if ((outputLimited) && (unlikely(op + (1 + LASTLITERALS) + (matchLength>>8) > olimit)))
- return 0; /* Check output limit */
- if (matchLength>=ML_MASK)
- {
- *token += ML_MASK;
- matchLength -= ML_MASK;
- for (; matchLength >= 510 ; matchLength-=510) { *op++ = 255; *op++ = 255; }
- if (matchLength >= 255) { matchLength-=255; *op++ = 255; }
- *op++ = (BYTE)matchLength;
- }
- else *token += (BYTE)(matchLength);
- }
-
- anchor = ip;
-
- /* Test end of chunk */
- if (ip > mflimit) break;
-
- /* Fill table */
- LZ4_putPosition(ip-2, ctx, tableType, base);
-
- /* Test next position */
- match = LZ4_getPosition(ip, ctx, tableType, base);
- if (dict==usingExtDict)
- {
- if (match<(const BYTE*)source)
- {
- refDelta = dictDelta;
- lowLimit = dictionary;
- }
- else
- {
- refDelta = 0;
- lowLimit = (const BYTE*)source;
- }
- }
- LZ4_putPosition(ip, ctx, tableType, base);
- if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1)
- && (match+MAX_DISTANCE>=ip)
- && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) )
- { token=op++; *token=0; goto _next_match; }
-
- /* Prepare next loop */
- forwardH = LZ4_hashPosition(++ip, tableType);
- }
-
-_last_literals:
- /* Encode Last Literals */
- {
- const size_t lastRun = (size_t)(iend - anchor);
- if ((outputLimited) && ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize))
- return 0; /* Check output limit */
- if (lastRun >= RUN_MASK)
- {
- size_t accumulator = lastRun - RUN_MASK;
- *op++ = RUN_MASK << ML_BITS;
- for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
- *op++ = (BYTE) accumulator;
- }
- else
- {
- *op++ = (BYTE)(lastRun<<ML_BITS);
- }
- memcpy(op, anchor, lastRun);
- op += lastRun;
- }
-
- /* End */
- return (int) (((char*)op)-dest);
-}
-
-
-int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
- LZ4_resetStream((LZ4_stream_t*)state);
- if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
-
- if (maxOutputSize >= LZ4_compressBound(inputSize))
- {
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
- }
- else
- {
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
- }
-}
-
-
-int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
-#if (HEAPMODE)
- void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
-#else
- LZ4_stream_t ctx;
- void* ctxPtr = &ctx;
-#endif
-
- int result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
-
-#if (HEAPMODE)
- FREEMEM(ctxPtr);
-#endif
- return result;
-}
-
-
-int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize)
-{
- return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1);
-}
-
-
-/* hidden debug function */
-/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */
-int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
- LZ4_stream_t ctx;
-
- LZ4_resetStream(&ctx);
-
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
-}
-
-
-/********************************
-* destSize variant
-********************************/
-
-static int LZ4_compress_destSize_generic(
- void* const ctx,
- const char* const src,
- char* const dst,
- int* const srcSizePtr,
- const int targetDstSize,
- const tableType_t tableType)
-{
- const BYTE* ip = (const BYTE*) src;
- const BYTE* base = (const BYTE*) src;
- const BYTE* lowLimit = (const BYTE*) src;
- const BYTE* anchor = ip;
- const BYTE* const iend = ip + *srcSizePtr;
- const BYTE* const mflimit = iend - MFLIMIT;
- const BYTE* const matchlimit = iend - LASTLITERALS;
-
- BYTE* op = (BYTE*) dst;
- BYTE* const oend = op + targetDstSize;
- BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */;
- BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */);
- BYTE* const oMaxSeq = oMaxLit - 1 /* token */;
-
- U32 forwardH;
-
-
- /* Init conditions */
- if (targetDstSize < 1) return 0; /* Impossible to store anything */
- if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
- if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
- if (*srcSizePtr<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
-
- /* First Byte */
- *srcSizePtr = 0;
- LZ4_putPosition(ip, ctx, tableType, base);
- ip++; forwardH = LZ4_hashPosition(ip, tableType);
-
- /* Main Loop */
- for ( ; ; )
- {
- const BYTE* match;
- BYTE* token;
- {
- const BYTE* forwardIp = ip;
- unsigned step = 1;
- unsigned searchMatchNb = 1 << LZ4_skipTrigger;
-
- /* Find a match */
- do {
- U32 h = forwardH;
- ip = forwardIp;
- forwardIp += step;
- step = (searchMatchNb++ >> LZ4_skipTrigger);
-
- if (unlikely(forwardIp > mflimit))
- goto _last_literals;
-
- match = LZ4_getPositionOnHash(h, ctx, tableType, base);
- forwardH = LZ4_hashPosition(forwardIp, tableType);
- LZ4_putPositionOnHash(ip, h, ctx, tableType, base);
-
- } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
- || (LZ4_read32(match) != LZ4_read32(ip)) );
- }
-
- /* Catch up */
- while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
-
- {
- /* Encode Literal length */
- unsigned litLength = (unsigned)(ip - anchor);
- token = op++;
- if (op + ((litLength+240)/255) + litLength > oMaxLit)
- {
- /* Not enough space for a last match */
- op--;
- goto _last_literals;
- }
- if (litLength>=RUN_MASK)
- {
- unsigned len = litLength - RUN_MASK;
- *token=(RUN_MASK<<ML_BITS);
- for(; len >= 255 ; len-=255) *op++ = 255;
- *op++ = (BYTE)len;
- }
- else *token = (BYTE)(litLength<<ML_BITS);
-
- /* Copy Literals */
- LZ4_wildCopy(op, anchor, op+litLength);
- op += litLength;
- }
-
-_next_match:
- /* Encode Offset */
- LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
-
- /* Encode MatchLength */
- {
- size_t matchLength;
-
- matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
-
- if (op + ((matchLength+240)/255) > oMaxMatch)
- {
- /* Match description too long : reduce it */
- matchLength = (15-1) + (oMaxMatch-op) * 255;
- }
- //printf("offset %5i, matchLength%5i \n", (int)(ip-match), matchLength + MINMATCH);
- ip += MINMATCH + matchLength;
-
- if (matchLength>=ML_MASK)
- {
- *token += ML_MASK;
- matchLength -= ML_MASK;
- while (matchLength >= 255) { matchLength-=255; *op++ = 255; }
- *op++ = (BYTE)matchLength;
- }
- else *token += (BYTE)(matchLength);
- }
-
- anchor = ip;
-
- /* Test end of block */
- if (ip > mflimit) break;
- if (op > oMaxSeq) break;
-
- /* Fill table */
- LZ4_putPosition(ip-2, ctx, tableType, base);
-
- /* Test next position */
- match = LZ4_getPosition(ip, ctx, tableType, base);
- LZ4_putPosition(ip, ctx, tableType, base);
- if ( (match+MAX_DISTANCE>=ip)
- && (LZ4_read32(match)==LZ4_read32(ip)) )
- { token=op++; *token=0; goto _next_match; }
-
- /* Prepare next loop */
- forwardH = LZ4_hashPosition(++ip, tableType);
- }
-
-_last_literals:
- /* Encode Last Literals */
- {
- size_t lastRunSize = (size_t)(iend - anchor);
- if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend)
- {
- /* adapt lastRunSize to fill 'dst' */
- lastRunSize = (oend-op) - 1;
- lastRunSize -= (lastRunSize+240)/255;
- }
- ip = anchor + lastRunSize;
-
- if (lastRunSize >= RUN_MASK)
- {
- size_t accumulator = lastRunSize - RUN_MASK;
- *op++ = RUN_MASK << ML_BITS;
- for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
- *op++ = (BYTE) accumulator;
- }
- else
- {
- *op++ = (BYTE)(lastRunSize<<ML_BITS);
- }
- memcpy(op, anchor, lastRunSize);
- op += lastRunSize;
- }
-
- /* End */
- *srcSizePtr = (int) (((const char*)ip)-src);
- return (int) (((char*)op)-dst);
-}
-
-
-static int LZ4_compress_destSize_extState (void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)
-{
- LZ4_resetStream((LZ4_stream_t*)state);
-
- if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) /* compression success is guaranteed */
- {
- return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
- }
- else
- {
- if (*srcSizePtr < LZ4_64Klimit)
- return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16);
- else
- return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, LZ4_64bits() ? byU32 : byPtr);
- }
-}
-
-
-int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
-{
-#if (HEAPMODE)
- void* ctx = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
-#else
- LZ4_stream_t ctxBody;
- void* ctx = &ctxBody;
-#endif
-
- int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
-
-#if (HEAPMODE)
- FREEMEM(ctx);
-#endif
- return result;
-}
-
-
-
-/********************************
-* Streaming functions
-********************************/
-
-LZ4_stream_t* LZ4_createStream(void)
-{
- LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64);
- LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */
- LZ4_resetStream(lz4s);
- return lz4s;
-}
-
-void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
-{
- MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
-}
-
-int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
-{
- FREEMEM(LZ4_stream);
- return (0);
-}
-
-
-#define HASH_UNIT sizeof(size_t)
-int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
-{
- LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict;
- const BYTE* p = (const BYTE*)dictionary;
- const BYTE* const dictEnd = p + dictSize;
- const BYTE* base;
-
- if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */
- LZ4_resetStream(LZ4_dict);
-
- if (dictSize < (int)HASH_UNIT)
- {
- dict->dictionary = NULL;
- dict->dictSize = 0;
- return 0;
- }
-
- if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
- dict->currentOffset += 64 KB;
- base = p - dict->currentOffset;
- dict->dictionary = p;
- dict->dictSize = (U32)(dictEnd - p);
- dict->currentOffset += dict->dictSize;
-
- while (p <= dictEnd-HASH_UNIT)
- {
- LZ4_putPosition(p, dict->hashTable, byU32, base);
- p+=3;
- }
-
- return dict->dictSize;
-}
-
-
-static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src)
-{
- if ((LZ4_dict->currentOffset > 0x80000000) ||
- ((size_t)LZ4_dict->currentOffset > (size_t)src)) /* address space overflow */
- {
- /* rescale hash table */
- U32 delta = LZ4_dict->currentOffset - 64 KB;
- const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
- int i;
- for (i=0; i<HASH_SIZE_U32; i++)
- {
- if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;
- else LZ4_dict->hashTable[i] -= delta;
- }
- LZ4_dict->currentOffset = 64 KB;
- if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;
- LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
- }
-}
-
-
-int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
- LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_stream;
- const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
-
- const BYTE* smallest = (const BYTE*) source;
- if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */
- if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd;
- LZ4_renormDictT(streamPtr, smallest);
- if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
-
- /* Check overlapping input/dictionary space */
- {
- const BYTE* sourceEnd = (const BYTE*) source + inputSize;
- if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd))
- {
- streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
- if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;
- if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
- streamPtr->dictionary = dictEnd - streamPtr->dictSize;
- }
- }
-
- /* prefix mode : source data follows dictionary */
- if (dictEnd == (const BYTE*)source)
- {
- int result;
- if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
- result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration);
- else
- result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration);
- streamPtr->dictSize += (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
- return result;
- }
-
- /* external dictionary mode */
- {
- int result;
- if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
- result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration);
- else
- result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration);
- streamPtr->dictionary = (const BYTE*)source;
- streamPtr->dictSize = (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
- return result;
- }
-}
-
-
-/* Hidden debug function, to force external dictionary mode */
-int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize)
-{
- LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_dict;
- int result;
- const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
-
- const BYTE* smallest = dictEnd;
- if (smallest > (const BYTE*) source) smallest = (const BYTE*) source;
- LZ4_renormDictT((LZ4_stream_t_internal*)LZ4_dict, smallest);
-
- result = LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
-
- streamPtr->dictionary = (const BYTE*)source;
- streamPtr->dictSize = (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
-
- return result;
-}
-
-
-int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
-{
- LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict;
- const BYTE* previousDictEnd = dict->dictionary + dict->dictSize;
-
- if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */
- if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize;
-
- memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
-
- dict->dictionary = (const BYTE*)safeBuffer;
- dict->dictSize = (U32)dictSize;
-
- return dictSize;
-}
-
-
-
-/*******************************
-* Decompression functions
-*******************************/
-/*
- * This generic decompression function cover all use cases.
- * It shall be instantiated several times, using different sets of directives
- * Note that it is essential this generic function is really inlined,
- * in order to remove useless branches during compilation optimization.
- */
-FORCE_INLINE int LZ4_decompress_generic(
- const char* const source,
- char* const dest,
- int inputSize,
- int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
-
- int endOnInput, /* endOnOutputSize, endOnInputSize */
- int partialDecoding, /* full, partial */
- int targetOutputSize, /* only used if partialDecoding==partial */
- int dict, /* noDict, withPrefix64k, usingExtDict */
- const BYTE* const lowPrefix, /* == dest if dict == noDict */
- const BYTE* const dictStart, /* only if dict==usingExtDict */
- const size_t dictSize /* note : = 0 if noDict */
- )
-{
- /* Local Variables */
- const BYTE* ip = (const BYTE*) source;
- const BYTE* const iend = ip + inputSize;
-
- BYTE* op = (BYTE*) dest;
- BYTE* const oend = op + outputSize;
- BYTE* cpy;
- BYTE* oexit = op + targetOutputSize;
- const BYTE* const lowLimit = lowPrefix - dictSize;
-
- const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
- const size_t dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4};
- const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3};
-
- const int safeDecode = (endOnInput==endOnInputSize);
- const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
-
-
- /* Special cases */
- if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */
- if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
- if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
-
-
- /* Main Loop */
- while (1)
- {
- unsigned token;
- size_t length;
- const BYTE* match;
-
- /* get literal length */
- token = *ip++;
- if ((length=(token>>ML_BITS)) == RUN_MASK)
- {
- unsigned s;
- do
- {
- s = *ip++;
- length += s;
- }
- while (likely((endOnInput)?ip<iend-RUN_MASK:1) && (s==255));
- if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)(op))) goto _output_error; /* overflow detection */
- if ((safeDecode) && unlikely((size_t)(ip+length)<(size_t)(ip))) goto _output_error; /* overflow detection */
- }
-
- /* copy literals */
- cpy = op+length;
- if (((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
- || ((!endOnInput) && (cpy>oend-COPYLENGTH)))
- {
- if (partialDecoding)
- {
- if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
- if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
- }
- else
- {
- if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
- if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
- }
- memcpy(op, ip, length);
- ip += length;
- op += length;
- break; /* Necessarily EOF, due to parsing restrictions */
- }
- LZ4_wildCopy(op, ip, cpy);
- ip += length; op = cpy;
-
- /* get offset */
- match = cpy - LZ4_readLE16(ip); ip+=2;
- if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside destination buffer */
-
- /* get matchlength */
- length = token & ML_MASK;
- if (length == ML_MASK)
- {
- unsigned s;
- do
- {
- if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
- s = *ip++;
- length += s;
- } while (s==255);
- if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)op)) goto _output_error; /* overflow detection */
- }
- length += MINMATCH;
-
- /* check external dictionary */
- if ((dict==usingExtDict) && (match < lowPrefix))
- {
- if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
-
- if (length <= (size_t)(lowPrefix-match))
- {
- /* match can be copied as a single segment from external dictionary */
- match = dictEnd - (lowPrefix-match);
- memmove(op, match, length); op += length;
- }
- else
- {
- /* match encompass external dictionary and current segment */
- size_t copySize = (size_t)(lowPrefix-match);
- memcpy(op, dictEnd - copySize, copySize);
- op += copySize;
- copySize = length - copySize;
- if (copySize > (size_t)(op-lowPrefix)) /* overlap within current segment */
- {
- BYTE* const endOfMatch = op + copySize;
- const BYTE* copyFrom = lowPrefix;
- while (op < endOfMatch) *op++ = *copyFrom++;
- }
- else
- {
- memcpy(op, lowPrefix, copySize);
- op += copySize;
- }
- }
- continue;
- }
-
- /* copy repeated sequence */
- cpy = op + length;
- if (unlikely((op-match)<8))
- {
- const size_t dec64 = dec64table[op-match];
- op[0] = match[0];
- op[1] = match[1];
- op[2] = match[2];
- op[3] = match[3];
- match += dec32table[op-match];
- LZ4_copy4(op+4, match);
- op += 8; match -= dec64;
- } else { LZ4_copy8(op, match); op+=8; match+=8; }
-
- if (unlikely(cpy>oend-12))
- {
- if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals */
- if (op < oend-8)
- {
- LZ4_wildCopy(op, match, oend-8);
- match += (oend-8) - op;
- op = oend-8;
- }
- while (op<cpy) *op++ = *match++;
- }
- else
- LZ4_wildCopy(op, match, cpy);
- op=cpy; /* correction */
- }
-
- /* end of decoding */
- if (endOnInput)
- return (int) (((char*)op)-dest); /* Nb of output bytes decoded */
- else
- return (int) (((const char*)ip)-source); /* Nb of input bytes read */
-
- /* Overflow error detected */
-_output_error:
- return (int) (-(((const char*)ip)-source))-1;
-}
-
-
-int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
-}
-
-int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0);
-}
-
-int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
-{
- return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB);
-}
-
-
-/* streaming decompression functions */
-
-typedef struct
-{
- const BYTE* externalDict;
- size_t extDictSize;
- const BYTE* prefixEnd;
- size_t prefixSize;
-} LZ4_streamDecode_t_internal;
-
-/*
- * If you prefer dynamic allocation methods,
- * LZ4_createStreamDecode()
- * provides a pointer (void*) towards an initialized LZ4_streamDecode_t structure.
- */
-LZ4_streamDecode_t* LZ4_createStreamDecode(void)
-{
- LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOCATOR(1, sizeof(LZ4_streamDecode_t));
- return lz4s;
-}
-
-int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
-{
- FREEMEM(LZ4_stream);
- return 0;
-}
-
-/*
- * LZ4_setStreamDecode
- * Use this function to instruct where to find the dictionary
- * This function is not necessary if previous data is still available where it was decoded.
- * Loading a size of 0 is allowed (same effect as no dictionary).
- * Return : 1 if OK, 0 if error
- */
-int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
-{
- LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode;
- lz4sd->prefixSize = (size_t) dictSize;
- lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
- lz4sd->externalDict = NULL;
- lz4sd->extDictSize = 0;
- return 1;
-}
-
-/*
-*_continue() :
- These decoding functions allow decompression of multiple blocks in "streaming" mode.
- Previously decoded blocks must still be available at the memory position where they were decoded.
- If it's not possible, save the relevant part of decoded data into a safe buffer,
- and indicate where it stands using LZ4_setStreamDecode()
-*/
-int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
-{
- LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode;
- int result;
-
- if (lz4sd->prefixEnd == (BYTE*)dest)
- {
- result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0,
- usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize += result;
- lz4sd->prefixEnd += result;
- }
- else
- {
- lz4sd->extDictSize = lz4sd->prefixSize;
- lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
- result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0,
- usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize = result;
- lz4sd->prefixEnd = (BYTE*)dest + result;
- }
-
- return result;
-}
-
-int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize)
-{
- LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode;
- int result;
-
- if (lz4sd->prefixEnd == (BYTE*)dest)
- {
- result = LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0,
- usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize += originalSize;
- lz4sd->prefixEnd += originalSize;
- }
- else
- {
- lz4sd->extDictSize = lz4sd->prefixSize;
- lz4sd->externalDict = (BYTE*)dest - lz4sd->extDictSize;
- result = LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0,
- usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize = originalSize;
- lz4sd->prefixEnd = (BYTE*)dest + originalSize;
- }
-
- return result;
-}
-
-
-/*
-Advanced decoding functions :
-*_usingDict() :
- These decoding functions work the same as "_continue" ones,
- the dictionary must be explicitly provided within parameters
-*/
-
-FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize)
-{
- if (dictSize==0)
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0);
- if (dictStart+dictSize == dest)
- {
- if (dictSize >= (int)(64 KB - 1))
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0);
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0);
- }
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
-}
-
-int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
-{
- return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize);
-}
-
-int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
-{
- return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize);
-}
-
-/* debug function */
-int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
-}
-
-
-/***************************************************
-* Obsolete Functions
-***************************************************/
-/* obsolete compression functions */
-int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); }
-int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); }
-int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); }
-int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); }
-int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); }
-int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); }
-
-/*
-These function names are deprecated and should no longer be used.
-They are only provided here for compatibility with older user programs.
-- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
-- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
-*/
-int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }
-int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }
-
-
-/* Obsolete Streaming functions */
-
-int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }
-
-static void LZ4_init(LZ4_stream_t_internal* lz4ds, BYTE* base)
-{
- MEM_INIT(lz4ds, 0, LZ4_STREAMSIZE);
- lz4ds->bufferStart = base;
-}
-
-int LZ4_resetStreamState(void* state, char* inputBuffer)
-{
- if ((((size_t)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */
- LZ4_init((LZ4_stream_t_internal*)state, (BYTE*)inputBuffer);
- return 0;
-}
-
-void* LZ4_create (char* inputBuffer)
-{
- void* lz4ds = ALLOCATOR(8, LZ4_STREAMSIZE_U64);
- LZ4_init ((LZ4_stream_t_internal*)lz4ds, (BYTE*)inputBuffer);
- return lz4ds;
-}
-
-char* LZ4_slideInputBuffer (void* LZ4_Data)
-{
- LZ4_stream_t_internal* ctx = (LZ4_stream_t_internal*)LZ4_Data;
- int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB);
- return (char*)(ctx->bufferStart + dictSize);
-}
-
-/* Obsolete streaming decompression functions */
-
-int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
-}
-
-int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
-{
- return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
-}
-
-#endif /* LZ4_COMMONDEFS_ONLY */
-
diff --git a/thirdparty/etcpak/lz4/lz4.h b/thirdparty/etcpak/lz4/lz4.h
deleted file mode 100644
index 3e74002256..0000000000
--- a/thirdparty/etcpak/lz4/lz4.h
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- LZ4 - Fast LZ compression algorithm
- Header File
- Copyright (C) 2011-2015, Yann Collet.
-
- BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following disclaimer
- in the documentation and/or other materials provided with the
- distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- You can contact the author at :
- - LZ4 source repository : https://github.com/Cyan4973/lz4
- - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
-*/
-#pragma once
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-/*
- * lz4.h provides block compression functions, and gives full buffer control to programmer.
- * If you need to generate inter-operable compressed data (respecting LZ4 frame specification),
- * and can let the library handle its own memory, please use lz4frame.h instead.
-*/
-
-/**************************************
-* Version
-**************************************/
-#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
-#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
-#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */
-#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
-int LZ4_versionNumber (void);
-
-/**************************************
-* Tuning parameter
-**************************************/
-/*
- * LZ4_MEMORY_USAGE :
- * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
- * Increasing memory usage improves compression ratio
- * Reduced memory usage can improve speed, due to cache effect
- * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
- */
-#define LZ4_MEMORY_USAGE 14
-
-
-/**************************************
-* Simple Functions
-**************************************/
-
-int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
-int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
-
-/*
-LZ4_compress_default() :
- Compresses 'sourceSize' bytes from buffer 'source'
- into already allocated 'dest' buffer of size 'maxDestSize'.
- Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
- It also runs faster, so it's a recommended setting.
- If the function cannot compress 'source' into a more limited 'dest' budget,
- compression stops *immediately*, and the function result is zero.
- As a consequence, 'dest' content is not valid.
- This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
- sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
- maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
- return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
- or 0 if compression fails
-
-LZ4_decompress_safe() :
- compressedSize : is the precise full size of the compressed block.
- maxDecompressedSize : is the size of destination buffer, which must be already allocated.
- return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
- If destination buffer is not large enough, decoding will stop and output an error code (<0).
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- This function is protected against buffer overflow exploits, including malicious data packets.
- It never writes outside output buffer, nor reads outside input buffer.
-*/
-
-
-/**************************************
-* Advanced Functions
-**************************************/
-#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
-#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
-
-/*
-LZ4_compressBound() :
- Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
- This function is primarily useful for memory allocation purposes (destination buffer size).
- Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
- Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
- inputSize : max supported value is LZ4_MAX_INPUT_SIZE
- return : maximum output size in a "worst case" scenario
- or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
-*/
-int LZ4_compressBound(int inputSize);
-
-/*
-LZ4_compress_fast() :
- Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
- The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
- It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
- An acceleration value of "1" is the same as regular LZ4_compress_default()
- Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
-*/
-int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
-
-
-/*
-LZ4_compress_fast_extState() :
- Same compression function, just using an externally allocated memory space to store compression state.
- Use LZ4_sizeofState() to know how much memory must be allocated,
- and allocate it on 8-bytes boundaries (using malloc() typically).
- Then, provide it as 'void* state' to compression function.
-*/
-int LZ4_sizeofState(void);
-int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
-
-
-/*
-LZ4_compress_destSize() :
- Reverse the logic, by compressing as much data as possible from 'source' buffer
- into already allocated buffer 'dest' of size 'targetDestSize'.
- This function either compresses the entire 'source' content into 'dest' if it's large enough,
- or fill 'dest' buffer completely with as much data as possible from 'source'.
- *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
- New value is necessarily <= old value.
- return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
- or 0 if compression fails
-*/
-int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
-
-
-/*
-LZ4_decompress_fast() :
- originalSize : is the original and therefore uncompressed size
- return : the number of bytes read from the source buffer (in other words, the compressed size)
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
- note : This function fully respect memory boundaries for properly formed compressed data.
- It is a bit faster than LZ4_decompress_safe().
- However, it does not provide any protection against intentionally modified data stream (malicious input).
- Use this function in trusted environment only (data to decode comes from a trusted source).
-*/
-int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
-
-/*
-LZ4_decompress_safe_partial() :
- This function decompress a compressed block of size 'compressedSize' at position 'source'
- into destination buffer 'dest' of size 'maxDecompressedSize'.
- The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
- reducing decompression time.
- return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
- Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
- Always control how many bytes were decoded.
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
-*/
-int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
-
-
-/***********************************************
-* Streaming Compression Functions
-***********************************************/
-#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
-#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
-/*
- * LZ4_stream_t
- * information structure to track an LZ4 stream.
- * important : init this structure content before first use !
- * note : only allocated directly the structure if you are statically linking LZ4
- * If you are using liblz4 as a DLL, please use below construction methods instead.
- */
-typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t;
-
-/*
- * LZ4_resetStream
- * Use this function to init an allocated LZ4_stream_t structure
- */
-void LZ4_resetStream (LZ4_stream_t* streamPtr);
-
-/*
- * LZ4_createStream will allocate and initialize an LZ4_stream_t structure
- * LZ4_freeStream releases its memory.
- * In the context of a DLL (liblz4), please use these methods rather than the static struct.
- * They are more future proof, in case of a change of LZ4_stream_t size.
- */
-LZ4_stream_t* LZ4_createStream(void);
-int LZ4_freeStream (LZ4_stream_t* streamPtr);
-
-/*
- * LZ4_loadDict
- * Use this function to load a static dictionary into LZ4_stream.
- * Any previous data will be forgotten, only 'dictionary' will remain in memory.
- * Loading a size of 0 is allowed.
- * Return : dictionary size, in bytes (necessarily <= 64 KB)
- */
-int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
-
-/*
- * LZ4_compress_fast_continue
- * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
- * Important : Previous data blocks are assumed to still be present and unmodified !
- * 'dst' buffer must be already allocated.
- * If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
- * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
- */
-int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
-
-/*
- * LZ4_saveDict
- * If previously compressed data block is not guaranteed to remain available at its memory location
- * save it into a safer place (char* safeBuffer)
- * Note : you don't need to call LZ4_loadDict() afterwards,
- * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue()
- * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error
- */
-int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
-
-
-/************************************************
-* Streaming Decompression Functions
-************************************************/
-
-#define LZ4_STREAMDECODESIZE_U64 4
-#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
-typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t;
-/*
- * LZ4_streamDecode_t
- * information structure to track an LZ4 stream.
- * init this structure content using LZ4_setStreamDecode or memset() before first use !
- *
- * In the context of a DLL (liblz4) please prefer usage of construction methods below.
- * They are more future proof, in case of a change of LZ4_streamDecode_t size in the future.
- * LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure
- * LZ4_freeStreamDecode releases its memory.
- */
-LZ4_streamDecode_t* LZ4_createStreamDecode(void);
-int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
-
-/*
- * LZ4_setStreamDecode
- * Use this function to instruct where to find the dictionary.
- * Setting a size of 0 is allowed (same effect as reset).
- * Return : 1 if OK, 0 if error
- */
-int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
-
-/*
-*_continue() :
- These decoding functions allow decompression of multiple blocks in "streaming" mode.
- Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
- In the case of a ring buffers, decoding buffer must be either :
- - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
- In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
- - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
- maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
- In which case, encoding and decoding buffers do not need to be synchronized,
- and encoding ring buffer can have any size, including small ones ( < 64 KB).
- - _At least_ 64 KB + 8 bytes + maxBlockSize.
- In which case, encoding and decoding buffers do not need to be synchronized,
- and encoding ring buffer can have any size, including larger than decoding buffer.
- Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
- and indicate where it is saved using LZ4_setStreamDecode()
-*/
-int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
-int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
-
-
-/*
-Advanced decoding functions :
-*_usingDict() :
- These decoding functions work the same as
- a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
- They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
-*/
-int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
-int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
-
-
-
-/**************************************
-* Obsolete Functions
-**************************************/
-/* Deprecate Warnings */
-/* Should these warnings messages be a problem,
- it is generally possible to disable them,
- with -Wno-deprecated-declarations for gcc
- or _CRT_SECURE_NO_WARNINGS in Visual for example.
- You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */
-#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK
-# define LZ4_DEPRECATE_WARNING_DEFBLOCK
-# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-# if (LZ4_GCC_VERSION >= 405) || defined(__clang__)
-# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
-# elif (LZ4_GCC_VERSION >= 301)
-# define LZ4_DEPRECATED(message) __attribute__((deprecated))
-# elif defined(_MSC_VER)
-# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
-# else
-# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
-# define LZ4_DEPRECATED(message)
-# endif
-#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */
-
-/* Obsolete compression functions */
-/* These functions are planned to start generate warnings by r131 approximately */
-int LZ4_compress (const char* source, char* dest, int sourceSize);
-int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
-int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
-int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
-int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
-int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
-
-/* Obsolete decompression functions */
-/* These function names are completely deprecated and must no longer be used.
- They are only provided here for compatibility with older programs.
- - LZ4_uncompress is the same as LZ4_decompress_fast
- - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
- These function prototypes are now disabled; uncomment them only if you really need them.
- It is highly recommended to stop using these prototypes and migrate to maintained ones */
-/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
-/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
-
-/* Obsolete streaming functions; use new streaming interface whenever possible */
-LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
-LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
-LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer);
-LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state);
-
-/* Obsolete streaming decoding functions */
-LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
-LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
-
-
-#if defined (__cplusplus)
-}
-#endif
diff --git a/thirdparty/etcpak/mmap.cpp b/thirdparty/etcpak/mmap.cpp
deleted file mode 100644
index c2460ee9e4..0000000000
--- a/thirdparty/etcpak/mmap.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "mmap.hpp"
-
-#ifdef _WIN32
-# include <io.h>
-# include <windows.h>
-
-void* mmap( void* addr, size_t length, int prot, int flags, int fd, off_t offset )
-{
- HANDLE hnd;
- void* map = nullptr;
-
- switch( prot )
- {
- case PROT_READ:
- if( hnd = CreateFileMapping( HANDLE( _get_osfhandle( fd ) ), nullptr, PAGE_READONLY, 0, DWORD( length ), nullptr ) )
- {
- map = MapViewOfFile( hnd, FILE_MAP_READ, 0, 0, length );
- CloseHandle( hnd );
- }
- break;
- case PROT_WRITE:
- if( hnd = CreateFileMapping( HANDLE( _get_osfhandle( fd ) ), nullptr, PAGE_READWRITE, 0, DWORD( length ), nullptr ) )
- {
- map = MapViewOfFile( hnd, FILE_MAP_WRITE, 0, 0, length );
- CloseHandle( hnd );
- }
- break;
- }
-
- return map ? (char*)map + offset : (void*)-1;
-}
-
-int munmap( void* addr, size_t length )
-{
- return UnmapViewOfFile( addr ) != 0 ? 0 : -1;
-}
-
-#endif
diff --git a/thirdparty/etcpak/mmap.hpp b/thirdparty/etcpak/mmap.hpp
deleted file mode 100644
index e4cfe7759c..0000000000
--- a/thirdparty/etcpak/mmap.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef __MMAP_HPP__
-#define __MMAP_HPP__
-
-#ifndef _WIN32
-# include <sys/mman.h>
-#else
-# include <string.h>
-# include <sys/types.h>
-
-# define PROT_READ 1
-# define PROT_WRITE 2
-# define MAP_SHARED 0
-
-void* mmap( void* addr, size_t length, int prot, int flags, int fd, off_t offset );
-int munmap( void* addr, size_t length );
-
-#endif
-
-#endif
diff --git a/thirdparty/etcpak/patches/libpng-unbundle.patch b/thirdparty/etcpak/patches/libpng-unbundle.patch
deleted file mode 100644
index e3c07412c6..0000000000
--- a/thirdparty/etcpak/patches/libpng-unbundle.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/thirdparty/etcpak/Bitmap.cpp b/thirdparty/etcpak/Bitmap.cpp
-index 6aa36f5caa..ef318318ac 100644
---- a/thirdparty/etcpak/Bitmap.cpp
-+++ b/thirdparty/etcpak/Bitmap.cpp
-@@ -3,7 +3,7 @@
- #include <string.h>
- #include <assert.h>
-
--#include "libpng/png.h"
-+#include <png.h>
- #include "lz4/lz4.h"
-
- #include "Bitmap.hpp"
diff --git a/thirdparty/etcpak/patches/llvm-c++11-narrowing-errors.patch b/thirdparty/etcpak/patches/llvm-c++11-narrowing-errors.patch
deleted file mode 100644
index ab0d1e63a2..0000000000
--- a/thirdparty/etcpak/patches/llvm-c++11-narrowing-errors.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-diff --git a/thirdparty/etcpak/BlockData.cpp b/thirdparty/etcpak/BlockData.cpp
-index bd738085f3..395b55246b 100644
---- a/thirdparty/etcpak/BlockData.cpp
-+++ b/thirdparty/etcpak/BlockData.cpp
-@@ -334,10 +334,10 @@ static etcpak_force_inline void DecodeT( uint64_t block, uint32_t* dst, uint32_t
- const auto c3b = clampu8( cb1 - table59T58H[codeword] );
-
- const uint32_t col_tab[4] = {
-- cr0 | ( cg0 << 8 ) | ( cb0 << 16 ) | 0xFF000000,
-- c2r | ( c2g << 8 ) | ( c2b << 16 ) | 0xFF000000,
-- cr1 | ( cg1 << 8 ) | ( cb1 << 16 ) | 0xFF000000,
-- c3r | ( c3g << 8 ) | ( c3b << 16 ) | 0xFF000000
-+ uint32_t(cr0 | ( cg0 << 8 ) | ( cb0 << 16 ) | 0xFF000000),
-+ uint32_t(c2r | ( c2g << 8 ) | ( c2b << 16 ) | 0xFF000000),
-+ uint32_t(cr1 | ( cg1 << 8 ) | ( cb1 << 16 ) | 0xFF000000),
-+ uint32_t(c3r | ( c3g << 8 ) | ( c3b << 16 ) | 0xFF000000)
- };
-
- const uint32_t indexes = ( block >> 32 ) & 0xFFFFFFFF;
-@@ -389,10 +389,10 @@ static etcpak_force_inline void DecodeTAlpha( uint64_t block, uint64_t alpha, ui
- const auto c3b = clampu8( cb1 - table59T58H[codeword] );
-
- const uint32_t col_tab[4] = {
-- cr0 | ( cg0 << 8 ) | ( cb0 << 16 ),
-- c2r | ( c2g << 8 ) | ( c2b << 16 ),
-- cr1 | ( cg1 << 8 ) | ( cb1 << 16 ),
-- c3r | ( c3g << 8 ) | ( c3b << 16 )
-+ uint32_t(cr0 | ( cg0 << 8 ) | ( cb0 << 16 )),
-+ uint32_t(c2r | ( c2g << 8 ) | ( c2b << 16 )),
-+ uint32_t(cr1 | ( cg1 << 8 ) | ( cb1 << 16 )),
-+ uint32_t(c3r | ( c3g << 8 ) | ( c3b << 16 ))
- };
-
- const uint32_t indexes = ( block >> 32 ) & 0xFFFFFFFF;
-@@ -436,10 +436,10 @@ static etcpak_force_inline void DecodeH( uint64_t block, uint32_t* dst, uint32_t
- const auto codeword = codeword_hi | codeword_lo;
-
- const uint32_t col_tab[] = {
-- clampu8( r0 + table59T58H[codeword] ) | ( clampu8( g0 + table59T58H[codeword] ) << 8 ) | ( clampu8( b0 + table59T58H[codeword] ) << 16 ),
-- clampu8( r0 - table59T58H[codeword] ) | ( clampu8( g0 - table59T58H[codeword] ) << 8 ) | ( clampu8( b0 - table59T58H[codeword] ) << 16 ),
-- clampu8( r1 + table59T58H[codeword] ) | ( clampu8( g1 + table59T58H[codeword] ) << 8 ) | ( clampu8( b1 + table59T58H[codeword] ) << 16 ),
-- clampu8( r1 - table59T58H[codeword] ) | ( clampu8( g1 - table59T58H[codeword] ) << 8 ) | ( clampu8( b1 - table59T58H[codeword] ) << 16 )
-+ uint32_t(clampu8( r0 + table59T58H[codeword] ) | ( clampu8( g0 + table59T58H[codeword] ) << 8 ) | ( clampu8( b0 + table59T58H[codeword] ) << 16 )),
-+ uint32_t(clampu8( r0 - table59T58H[codeword] ) | ( clampu8( g0 - table59T58H[codeword] ) << 8 ) | ( clampu8( b0 - table59T58H[codeword] ) << 16 )),
-+ uint32_t(clampu8( r1 + table59T58H[codeword] ) | ( clampu8( g1 + table59T58H[codeword] ) << 8 ) | ( clampu8( b1 + table59T58H[codeword] ) << 16 )),
-+ uint32_t(clampu8( r1 - table59T58H[codeword] ) | ( clampu8( g1 - table59T58H[codeword] ) << 8 ) | ( clampu8( b1 - table59T58H[codeword] ) << 16 ))
- };
-
- for( uint8_t j = 0; j < 4; j++ )
-@@ -483,10 +483,10 @@ static etcpak_force_inline void DecodeHAlpha( uint64_t block, uint64_t alpha, ui
- const auto tbl = g_alpha[(alpha >> 48) & 0xF];
-
- const uint32_t col_tab[] = {
-- clampu8( r0 + table59T58H[codeword] ) | ( clampu8( g0 + table59T58H[codeword] ) << 8 ) | ( clampu8( b0 + table59T58H[codeword] ) << 16 ),
-- clampu8( r0 - table59T58H[codeword] ) | ( clampu8( g0 - table59T58H[codeword] ) << 8 ) | ( clampu8( b0 - table59T58H[codeword] ) << 16 ),
-- clampu8( r1 + table59T58H[codeword] ) | ( clampu8( g1 + table59T58H[codeword] ) << 8 ) | ( clampu8( b1 + table59T58H[codeword] ) << 16 ),
-- clampu8( r1 - table59T58H[codeword] ) | ( clampu8( g1 - table59T58H[codeword] ) << 8 ) | ( clampu8( b1 - table59T58H[codeword] ) << 16 )
-+ uint32_t(clampu8( r0 + table59T58H[codeword] ) | ( clampu8( g0 + table59T58H[codeword] ) << 8 ) | ( clampu8( b0 + table59T58H[codeword] ) << 16 )),
-+ uint32_t(clampu8( r0 - table59T58H[codeword] ) | ( clampu8( g0 - table59T58H[codeword] ) << 8 ) | ( clampu8( b0 - table59T58H[codeword] ) << 16 )),
-+ uint32_t(clampu8( r1 + table59T58H[codeword] ) | ( clampu8( g1 + table59T58H[codeword] ) << 8 ) | ( clampu8( b1 + table59T58H[codeword] ) << 16 )),
-+ uint32_t(clampu8( r1 - table59T58H[codeword] ) | ( clampu8( g1 - table59T58H[codeword] ) << 8 ) | ( clampu8( b1 - table59T58H[codeword] ) << 16 ))
- };
-
- for( uint8_t j = 0; j < 4; j++ )
diff --git a/thirdparty/etcpak/patches/windows-mingw-fixes.patch b/thirdparty/etcpak/patches/windows-mingw-fixes.patch
deleted file mode 100644
index 1da60e4a4f..0000000000
--- a/thirdparty/etcpak/patches/windows-mingw-fixes.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-diff --git a/thirdparty/etcpak/BlockData.cpp b/thirdparty/etcpak/BlockData.cpp
-index a2cd032c5b..bd738085f3 100644
---- a/thirdparty/etcpak/BlockData.cpp
-+++ b/thirdparty/etcpak/BlockData.cpp
-@@ -15,7 +15,7 @@
- # include <arm_neon.h>
- #endif
-
--#ifdef __SSE4_1__
-+#if defined __SSE4_1__ || defined __AVX2__ || defined _MSC_VER
- # ifdef _MSC_VER
- # include <intrin.h>
- # include <Windows.h>
-@@ -24,12 +24,6 @@
- # else
- # include <x86intrin.h>
- # endif
--#else
--# ifndef _MSC_VER
--# include <byteswap.h>
--# define _bswap(x) bswap_32(x)
--# define _bswap64(x) bswap_64(x)
--# endif
- #endif
-
- #ifndef _bswap
-diff --git a/thirdparty/etcpak/ProcessRGB.cpp b/thirdparty/etcpak/ProcessRGB.cpp
-index 220d5c55e2..9dc5a78b67 100644
---- a/thirdparty/etcpak/ProcessRGB.cpp
-+++ b/thirdparty/etcpak/ProcessRGB.cpp
-@@ -1,5 +1,6 @@
- #include <array>
- #include <string.h>
-+#include <limits>
-
- #ifdef __ARM_NEON
- # include <arm_neon.h>
-@@ -21,12 +22,6 @@
- # else
- # include <x86intrin.h>
- # endif
--#else
--# ifndef _MSC_VER
--# include <byteswap.h>
--# define _bswap(x) bswap_32(x)
--# define _bswap64(x) bswap_64(x)
--# endif
- #endif
-
- #ifndef _bswap
-diff --git a/thirdparty/etcpak/System.cpp b/thirdparty/etcpak/System.cpp
-index 1383d0ecd0..a09b289cb2 100644
---- a/thirdparty/etcpak/System.cpp
-+++ b/thirdparty/etcpak/System.cpp
-@@ -35,7 +35,7 @@ unsigned int System::CPUCores()
-
- void System::SetThreadName( std::thread& thread, const char* name )
- {
--#ifdef _WIN32
-+#ifdef _MSC_VER
- const DWORD MS_VC_EXCEPTION=0x406D1388;
-
- # pragma pack( push, 8 )
diff --git a/thirdparty/meshoptimizer/LICENSE.md b/thirdparty/meshoptimizer/LICENSE.md
index 4fcd766d22..3c52415f62 100644
--- a/thirdparty/meshoptimizer/LICENSE.md
+++ b/thirdparty/meshoptimizer/LICENSE.md
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2016-2020 Arseny Kapoulkine
+Copyright (c) 2016-2021 Arseny Kapoulkine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/meshoptimizer/clusterizer.cpp b/thirdparty/meshoptimizer/clusterizer.cpp
index f7d88c5136..f8aad7b49c 100644
--- a/thirdparty/meshoptimizer/clusterizer.cpp
+++ b/thirdparty/meshoptimizer/clusterizer.cpp
@@ -2,6 +2,7 @@
#include "meshoptimizer.h"
#include <assert.h>
+#include <float.h>
#include <math.h>
#include <string.h>
@@ -12,6 +13,68 @@
namespace meshopt
{
+// This must be <= 255 since index 0xff is used internally to indice a vertex that doesn't belong to a meshlet
+const size_t kMeshletMaxVertices = 255;
+
+// A reasonable limit is around 2*max_vertices or less
+const size_t kMeshletMaxTriangles = 512;
+
+struct TriangleAdjacency2
+{
+ unsigned int* counts;
+ unsigned int* offsets;
+ unsigned int* data;
+};
+
+static void buildTriangleAdjacency(TriangleAdjacency2& adjacency, const unsigned int* indices, size_t index_count, size_t vertex_count, meshopt_Allocator& allocator)
+{
+ size_t face_count = index_count / 3;
+
+ // allocate arrays
+ adjacency.counts = allocator.allocate<unsigned int>(vertex_count);
+ adjacency.offsets = allocator.allocate<unsigned int>(vertex_count);
+ adjacency.data = allocator.allocate<unsigned int>(index_count);
+
+ // fill triangle counts
+ memset(adjacency.counts, 0, vertex_count * sizeof(unsigned int));
+
+ for (size_t i = 0; i < index_count; ++i)
+ {
+ assert(indices[i] < vertex_count);
+
+ adjacency.counts[indices[i]]++;
+ }
+
+ // fill offset table
+ unsigned int offset = 0;
+
+ for (size_t i = 0; i < vertex_count; ++i)
+ {
+ adjacency.offsets[i] = offset;
+ offset += adjacency.counts[i];
+ }
+
+ assert(offset == index_count);
+
+ // fill triangle data
+ for (size_t i = 0; i < face_count; ++i)
+ {
+ unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2];
+
+ adjacency.data[adjacency.offsets[a]++] = unsigned(i);
+ adjacency.data[adjacency.offsets[b]++] = unsigned(i);
+ adjacency.data[adjacency.offsets[c]++] = unsigned(i);
+ }
+
+ // fix offsets that have been disturbed by the previous pass
+ for (size_t i = 0; i < vertex_count; ++i)
+ {
+ assert(adjacency.offsets[i] >= adjacency.counts[i]);
+
+ adjacency.offsets[i] -= adjacency.counts[i];
+ }
+}
+
static void computeBoundingSphere(float result[4], const float points[][3], size_t count)
{
assert(count > 0);
@@ -82,13 +145,310 @@ static void computeBoundingSphere(float result[4], const float points[][3], size
result[3] = radius;
}
+struct Cone
+{
+ float px, py, pz;
+ float nx, ny, nz;
+};
+
+static float getMeshletScore(float distance2, float spread, float cone_weight, float expected_radius)
+{
+ float cone = 1.f - spread * cone_weight;
+ float cone_clamped = cone < 1e-3f ? 1e-3f : cone;
+
+ return (1 + sqrtf(distance2) / expected_radius * (1 - cone_weight)) * cone_clamped;
+}
+
+static Cone getMeshletCone(const Cone& acc, unsigned int triangle_count)
+{
+ Cone result = acc;
+
+ float center_scale = triangle_count == 0 ? 0.f : 1.f / float(triangle_count);
+
+ result.px *= center_scale;
+ result.py *= center_scale;
+ result.pz *= center_scale;
+
+ float axis_length = result.nx * result.nx + result.ny * result.ny + result.nz * result.nz;
+ float axis_scale = axis_length == 0.f ? 0.f : 1.f / sqrtf(axis_length);
+
+ result.nx *= axis_scale;
+ result.ny *= axis_scale;
+ result.nz *= axis_scale;
+
+ return result;
+}
+
+static float computeTriangleCones(Cone* triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
+{
+ (void)vertex_count;
+
+ size_t vertex_stride_float = vertex_positions_stride / sizeof(float);
+ size_t face_count = index_count / 3;
+
+ float mesh_area = 0;
+
+ for (size_t i = 0; i < face_count; ++i)
+ {
+ unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2];
+ assert(a < vertex_count && b < vertex_count && c < vertex_count);
+
+ const float* p0 = vertex_positions + vertex_stride_float * a;
+ const float* p1 = vertex_positions + vertex_stride_float * b;
+ const float* p2 = vertex_positions + vertex_stride_float * c;
+
+ float p10[3] = {p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]};
+ float p20[3] = {p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]};
+
+ float normalx = p10[1] * p20[2] - p10[2] * p20[1];
+ float normaly = p10[2] * p20[0] - p10[0] * p20[2];
+ float normalz = p10[0] * p20[1] - p10[1] * p20[0];
+
+ float area = sqrtf(normalx * normalx + normaly * normaly + normalz * normalz);
+ float invarea = (area == 0.f) ? 0.f : 1.f / area;
+
+ triangles[i].px = (p0[0] + p1[0] + p2[0]) / 3.f;
+ triangles[i].py = (p0[1] + p1[1] + p2[1]) / 3.f;
+ triangles[i].pz = (p0[2] + p1[2] + p2[2]) / 3.f;
+
+ triangles[i].nx = normalx * invarea;
+ triangles[i].ny = normaly * invarea;
+ triangles[i].nz = normalz * invarea;
+
+ mesh_area += area;
+ }
+
+ return mesh_area;
+}
+
+static void finishMeshlet(meshopt_Meshlet& meshlet, unsigned char* meshlet_triangles)
+{
+ size_t offset = meshlet.triangle_offset + meshlet.triangle_count * 3;
+
+ // fill 4b padding with 0
+ while (offset & 3)
+ meshlet_triangles[offset++] = 0;
+}
+
+static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, unsigned char* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t meshlet_offset, size_t max_vertices, size_t max_triangles)
+{
+ unsigned char& av = used[a];
+ unsigned char& bv = used[b];
+ unsigned char& cv = used[c];
+
+ bool result = false;
+
+ unsigned int used_extra = (av == 0xff) + (bv == 0xff) + (cv == 0xff);
+
+ if (meshlet.vertex_count + used_extra > max_vertices || meshlet.triangle_count >= max_triangles)
+ {
+ meshlets[meshlet_offset] = meshlet;
+
+ for (size_t j = 0; j < meshlet.vertex_count; ++j)
+ used[meshlet_vertices[meshlet.vertex_offset + j]] = 0xff;
+
+ finishMeshlet(meshlet, meshlet_triangles);
+
+ meshlet.vertex_offset += meshlet.vertex_count;
+ meshlet.triangle_offset += (meshlet.triangle_count * 3 + 3) & ~3; // 4b padding
+ meshlet.vertex_count = 0;
+ meshlet.triangle_count = 0;
+
+ result = true;
+ }
+
+ if (av == 0xff)
+ {
+ av = (unsigned char)meshlet.vertex_count;
+ meshlet_vertices[meshlet.vertex_offset + meshlet.vertex_count++] = a;
+ }
+
+ if (bv == 0xff)
+ {
+ bv = (unsigned char)meshlet.vertex_count;
+ meshlet_vertices[meshlet.vertex_offset + meshlet.vertex_count++] = b;
+ }
+
+ if (cv == 0xff)
+ {
+ cv = (unsigned char)meshlet.vertex_count;
+ meshlet_vertices[meshlet.vertex_offset + meshlet.vertex_count++] = c;
+ }
+
+ meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 0] = av;
+ meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 1] = bv;
+ meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 2] = cv;
+ meshlet.triangle_count++;
+
+ return result;
+}
+
+struct KDNode
+{
+ union
+ {
+ float split;
+ unsigned int index;
+ };
+
+ // leaves: axis = 3, children = number of extra points after this one (0 if 'index' is the only point)
+ // branches: axis != 3, left subtree = skip 1, right subtree = skip 1+children
+ unsigned int axis : 2;
+ unsigned int children : 30;
+};
+
+static size_t kdtreePartition(unsigned int* indices, size_t count, const float* points, size_t stride, unsigned int axis, float pivot)
+{
+ size_t m = 0;
+
+ // invariant: elements in range [0, m) are < pivot, elements in range [m, i) are >= pivot
+ for (size_t i = 0; i < count; ++i)
+ {
+ float v = points[indices[i] * stride + axis];
+
+ // swap(m, i) unconditionally
+ unsigned int t = indices[m];
+ indices[m] = indices[i];
+ indices[i] = t;
+
+ // when v >= pivot, we swap i with m without advancing it, preserving invariants
+ m += v < pivot;
+ }
+
+ return m;
+}
+
+static size_t kdtreeBuildLeaf(size_t offset, KDNode* nodes, size_t node_count, unsigned int* indices, size_t count)
+{
+ assert(offset + count <= node_count);
+ (void)node_count;
+
+ KDNode& result = nodes[offset];
+
+ result.index = indices[0];
+ result.axis = 3;
+ result.children = unsigned(count - 1);
+
+ // all remaining points are stored in nodes immediately following the leaf
+ for (size_t i = 1; i < count; ++i)
+ {
+ KDNode& tail = nodes[offset + i];
+
+ tail.index = indices[i];
+ tail.axis = 3;
+ tail.children = ~0u >> 2; // bogus value to prevent misuse
+ }
+
+ return offset + count;
+}
+
+static size_t kdtreeBuild(size_t offset, KDNode* nodes, size_t node_count, const float* points, size_t stride, unsigned int* indices, size_t count, size_t leaf_size)
+{
+ assert(count > 0);
+ assert(offset < node_count);
+
+ if (count <= leaf_size)
+ return kdtreeBuildLeaf(offset, nodes, node_count, indices, count);
+
+ float mean[3] = {};
+ float vars[3] = {};
+ float runc = 1, runs = 1;
+
+ // gather statistics on the points in the subtree using Welford's algorithm
+ for (size_t i = 0; i < count; ++i, runc += 1.f, runs = 1.f / runc)
+ {
+ const float* point = points + indices[i] * stride;
+
+ for (int k = 0; k < 3; ++k)
+ {
+ float delta = point[k] - mean[k];
+ mean[k] += delta * runs;
+ vars[k] += delta * (point[k] - mean[k]);
+ }
+ }
+
+ // split axis is one where the variance is largest
+ unsigned int axis = vars[0] >= vars[1] && vars[0] >= vars[2] ? 0 : vars[1] >= vars[2] ? 1
+ : 2;
+
+ float split = mean[axis];
+ size_t middle = kdtreePartition(indices, count, points, stride, axis, split);
+
+ // when the partition is degenerate simply consolidate the points into a single node
+ if (middle <= leaf_size / 2 || middle >= count - leaf_size / 2)
+ return kdtreeBuildLeaf(offset, nodes, node_count, indices, count);
+
+ KDNode& result = nodes[offset];
+
+ result.split = split;
+ result.axis = axis;
+
+ // left subtree is right after our node
+ size_t next_offset = kdtreeBuild(offset + 1, nodes, node_count, points, stride, indices, middle, leaf_size);
+
+ // distance to the right subtree is represented explicitly
+ result.children = unsigned(next_offset - offset - 1);
+
+ return kdtreeBuild(next_offset, nodes, node_count, points, stride, indices + middle, count - middle, leaf_size);
+}
+
+static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points, size_t stride, const unsigned char* emitted_flags, const float* position, unsigned int& result, float& limit)
+{
+ const KDNode& node = nodes[root];
+
+ if (node.axis == 3)
+ {
+ // leaf
+ for (unsigned int i = 0; i <= node.children; ++i)
+ {
+ unsigned int index = nodes[root + i].index;
+
+ if (emitted_flags[index])
+ continue;
+
+ const float* point = points + index * stride;
+
+ float distance2 =
+ (point[0] - position[0]) * (point[0] - position[0]) +
+ (point[1] - position[1]) * (point[1] - position[1]) +
+ (point[2] - position[2]) * (point[2] - position[2]);
+ float distance = sqrtf(distance2);
+
+ if (distance < limit)
+ {
+ result = index;
+ limit = distance;
+ }
+ }
+ }
+ else
+ {
+ // branch; we order recursion to process the node that search position is in first
+ float delta = position[node.axis] - node.split;
+ unsigned int first = (delta <= 0) ? 0 : node.children;
+ unsigned int second = first ^ node.children;
+
+ kdtreeNearest(nodes, root + 1 + first, points, stride, emitted_flags, position, result, limit);
+
+ // only process the other node if it can have a match based on closest distance so far
+ if (fabsf(delta) <= limit)
+ kdtreeNearest(nodes, root + 1 + second, points, stride, emitted_flags, position, result, limit);
+ }
+}
+
} // namespace meshopt
size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles)
{
+ using namespace meshopt;
+
assert(index_count % 3 == 0);
- assert(max_vertices >= 3);
- assert(max_triangles >= 1);
+ assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
+ assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles);
+ assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
+
+ (void)kMeshletMaxVertices;
+ (void)kMeshletMaxTriangles;
// meshlet construction is limited by max vertices and max triangles per meshlet
// the worst case is that the input is an unindexed stream since this equally stresses both limits
@@ -100,77 +460,226 @@ size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_
return meshlet_limit_vertices > meshlet_limit_triangles ? meshlet_limit_vertices : meshlet_limit_triangles;
}
-size_t meshopt_buildMeshlets(meshopt_Meshlet* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles)
+size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight)
{
+ using namespace meshopt;
+
assert(index_count % 3 == 0);
- assert(max_vertices >= 3);
- assert(max_triangles >= 1);
+ assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
+ assert(vertex_positions_stride % sizeof(float) == 0);
+
+ assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
+ assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles);
+ assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
meshopt_Allocator allocator;
- meshopt_Meshlet meshlet;
- memset(&meshlet, 0, sizeof(meshlet));
+ TriangleAdjacency2 adjacency = {};
+ buildTriangleAdjacency(adjacency, indices, index_count, vertex_count, allocator);
+
+ unsigned int* live_triangles = allocator.allocate<unsigned int>(vertex_count);
+ memcpy(live_triangles, adjacency.counts, vertex_count * sizeof(unsigned int));
+
+ size_t face_count = index_count / 3;
+
+ unsigned char* emitted_flags = allocator.allocate<unsigned char>(face_count);
+ memset(emitted_flags, 0, face_count);
+
+ // for each triangle, precompute centroid & normal to use for scoring
+ Cone* triangles = allocator.allocate<Cone>(face_count);
+ float mesh_area = computeTriangleCones(triangles, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride);
+
+ // assuming each meshlet is a square patch, expected radius is sqrt(expected area)
+ float triangle_area_avg = face_count == 0 ? 0.f : mesh_area / float(face_count) * 0.5f;
+ float meshlet_expected_radius = sqrtf(triangle_area_avg * max_triangles) * 0.5f;
+
+ // build a kd-tree for nearest neighbor lookup
+ unsigned int* kdindices = allocator.allocate<unsigned int>(face_count);
+ for (size_t i = 0; i < face_count; ++i)
+ kdindices[i] = unsigned(i);
- assert(max_vertices <= sizeof(meshlet.vertices) / sizeof(meshlet.vertices[0]));
- assert(max_triangles <= sizeof(meshlet.indices) / 3);
+ KDNode* nodes = allocator.allocate<KDNode>(face_count * 2);
+ kdtreeBuild(0, nodes, face_count * 2, &triangles[0].px, sizeof(Cone) / sizeof(float), kdindices, face_count, /* leaf_size= */ 8);
// index of the vertex in the meshlet, 0xff if the vertex isn't used
unsigned char* used = allocator.allocate<unsigned char>(vertex_count);
memset(used, -1, vertex_count);
- size_t offset = 0;
+ meshopt_Meshlet meshlet = {};
+ size_t meshlet_offset = 0;
- for (size_t i = 0; i < index_count; i += 3)
- {
- unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2];
- assert(a < vertex_count && b < vertex_count && c < vertex_count);
+ Cone meshlet_cone_acc = {};
- unsigned char& av = used[a];
- unsigned char& bv = used[b];
- unsigned char& cv = used[c];
+ for (;;)
+ {
+ unsigned int best_triangle = ~0u;
+ unsigned int best_extra = 5;
+ float best_score = FLT_MAX;
- unsigned int used_extra = (av == 0xff) + (bv == 0xff) + (cv == 0xff);
+ Cone meshlet_cone = getMeshletCone(meshlet_cone_acc, meshlet.triangle_count);
- if (meshlet.vertex_count + used_extra > max_vertices || meshlet.triangle_count >= max_triangles)
+ for (size_t i = 0; i < meshlet.vertex_count; ++i)
{
- destination[offset++] = meshlet;
+ unsigned int index = meshlet_vertices[meshlet.vertex_offset + i];
+
+ unsigned int* neighbours = &adjacency.data[0] + adjacency.offsets[index];
+ size_t neighbours_size = adjacency.counts[index];
+
+ for (size_t j = 0; j < neighbours_size; ++j)
+ {
+ unsigned int triangle = neighbours[j];
+ assert(!emitted_flags[triangle]);
+
+ unsigned int a = indices[triangle * 3 + 0], b = indices[triangle * 3 + 1], c = indices[triangle * 3 + 2];
+ assert(a < vertex_count && b < vertex_count && c < vertex_count);
+
+ unsigned int extra = (used[a] == 0xff) + (used[b] == 0xff) + (used[c] == 0xff);
+
+ // triangles that don't add new vertices to meshlets are max. priority
+ if (extra != 0)
+ {
+ // artificially increase the priority of dangling triangles as they're expensive to add to new meshlets
+ if (live_triangles[a] == 1 || live_triangles[b] == 1 || live_triangles[c] == 1)
+ extra = 0;
+
+ extra++;
+ }
+
+ // since topology-based priority is always more important than the score, we can skip scoring in some cases
+ if (extra > best_extra)
+ continue;
+
+ const Cone& tri_cone = triangles[triangle];
+
+ float distance2 =
+ (tri_cone.px - meshlet_cone.px) * (tri_cone.px - meshlet_cone.px) +
+ (tri_cone.py - meshlet_cone.py) * (tri_cone.py - meshlet_cone.py) +
+ (tri_cone.pz - meshlet_cone.pz) * (tri_cone.pz - meshlet_cone.pz);
- for (size_t j = 0; j < meshlet.vertex_count; ++j)
- used[meshlet.vertices[j]] = 0xff;
+ float spread = tri_cone.nx * meshlet_cone.nx + tri_cone.ny * meshlet_cone.ny + tri_cone.nz * meshlet_cone.nz;
- memset(&meshlet, 0, sizeof(meshlet));
+ float score = getMeshletScore(distance2, spread, cone_weight, meshlet_expected_radius);
+
+ // note that topology-based priority is always more important than the score
+ // this helps maintain reasonable effectiveness of meshlet data and reduces scoring cost
+ if (extra < best_extra || score < best_score)
+ {
+ best_triangle = triangle;
+ best_extra = extra;
+ best_score = score;
+ }
+ }
}
- if (av == 0xff)
+ if (best_triangle == ~0u)
{
- av = meshlet.vertex_count;
- meshlet.vertices[meshlet.vertex_count++] = a;
+ float position[3] = {meshlet_cone.px, meshlet_cone.py, meshlet_cone.pz};
+ unsigned int index = ~0u;
+ float limit = FLT_MAX;
+
+ kdtreeNearest(nodes, 0, &triangles[0].px, sizeof(Cone) / sizeof(float), emitted_flags, position, index, limit);
+
+ best_triangle = index;
}
- if (bv == 0xff)
+ if (best_triangle == ~0u)
+ break;
+
+ unsigned int a = indices[best_triangle * 3 + 0], b = indices[best_triangle * 3 + 1], c = indices[best_triangle * 3 + 2];
+ assert(a < vertex_count && b < vertex_count && c < vertex_count);
+
+ // add meshlet to the output; when the current meshlet is full we reset the accumulated bounds
+ if (appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles))
{
- bv = meshlet.vertex_count;
- meshlet.vertices[meshlet.vertex_count++] = b;
+ meshlet_offset++;
+ memset(&meshlet_cone_acc, 0, sizeof(meshlet_cone_acc));
}
- if (cv == 0xff)
+ live_triangles[a]--;
+ live_triangles[b]--;
+ live_triangles[c]--;
+
+ // remove emitted triangle from adjacency data
+ // this makes sure that we spend less time traversing these lists on subsequent iterations
+ for (size_t k = 0; k < 3; ++k)
{
- cv = meshlet.vertex_count;
- meshlet.vertices[meshlet.vertex_count++] = c;
+ unsigned int index = indices[best_triangle * 3 + k];
+
+ unsigned int* neighbours = &adjacency.data[0] + adjacency.offsets[index];
+ size_t neighbours_size = adjacency.counts[index];
+
+ for (size_t i = 0; i < neighbours_size; ++i)
+ {
+ unsigned int tri = neighbours[i];
+
+ if (tri == best_triangle)
+ {
+ neighbours[i] = neighbours[neighbours_size - 1];
+ adjacency.counts[index]--;
+ break;
+ }
+ }
}
- meshlet.indices[meshlet.triangle_count][0] = av;
- meshlet.indices[meshlet.triangle_count][1] = bv;
- meshlet.indices[meshlet.triangle_count][2] = cv;
- meshlet.triangle_count++;
+ // update aggregated meshlet cone data for scoring subsequent triangles
+ meshlet_cone_acc.px += triangles[best_triangle].px;
+ meshlet_cone_acc.py += triangles[best_triangle].py;
+ meshlet_cone_acc.pz += triangles[best_triangle].pz;
+ meshlet_cone_acc.nx += triangles[best_triangle].nx;
+ meshlet_cone_acc.ny += triangles[best_triangle].ny;
+ meshlet_cone_acc.nz += triangles[best_triangle].nz;
+
+ emitted_flags[best_triangle] = 1;
+ }
+
+ if (meshlet.triangle_count)
+ {
+ finishMeshlet(meshlet, meshlet_triangles);
+
+ meshlets[meshlet_offset++] = meshlet;
+ }
+
+ assert(meshlet_offset <= meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles));
+ return meshlet_offset;
+}
+
+size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles)
+{
+ using namespace meshopt;
+
+ assert(index_count % 3 == 0);
+
+ assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
+ assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles);
+ assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
+
+ meshopt_Allocator allocator;
+
+ // index of the vertex in the meshlet, 0xff if the vertex isn't used
+ unsigned char* used = allocator.allocate<unsigned char>(vertex_count);
+ memset(used, -1, vertex_count);
+
+ meshopt_Meshlet meshlet = {};
+ size_t meshlet_offset = 0;
+
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+ unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2];
+ assert(a < vertex_count && b < vertex_count && c < vertex_count);
+
+ // appends triangle to the meshlet and writes previous meshlet to the output if full
+ meshlet_offset += appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles);
}
if (meshlet.triangle_count)
- destination[offset++] = meshlet;
+ {
+ finishMeshlet(meshlet, meshlet_triangles);
- assert(offset <= meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles));
+ meshlets[meshlet_offset++] = meshlet;
+ }
- return offset;
+ assert(meshlet_offset <= meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles));
+ return meshlet_offset;
}
meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
@@ -178,18 +687,17 @@ meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t
using namespace meshopt;
assert(index_count % 3 == 0);
+ assert(index_count / 3 <= kMeshletMaxTriangles);
assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
assert(vertex_positions_stride % sizeof(float) == 0);
- assert(index_count / 3 <= 256);
-
(void)vertex_count;
size_t vertex_stride_float = vertex_positions_stride / sizeof(float);
// compute triangle normals and gather triangle corners
- float normals[256][3];
- float corners[256][3][3];
+ float normals[kMeshletMaxTriangles][3];
+ float corners[kMeshletMaxTriangles][3][3];
size_t triangles = 0;
for (size_t i = 0; i < index_count; i += 3)
@@ -327,25 +835,23 @@ meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t
return bounds;
}
-meshopt_Bounds meshopt_computeMeshletBounds(const meshopt_Meshlet* meshlet, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
+meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices, const unsigned char* meshlet_triangles, size_t triangle_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
{
+ using namespace meshopt;
+
+ assert(triangle_count <= kMeshletMaxTriangles);
assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
assert(vertex_positions_stride % sizeof(float) == 0);
- unsigned int indices[sizeof(meshlet->indices) / sizeof(meshlet->indices[0][0])];
+ unsigned int indices[kMeshletMaxTriangles * 3];
- for (size_t i = 0; i < meshlet->triangle_count; ++i)
+ for (size_t i = 0; i < triangle_count * 3; ++i)
{
- unsigned int a = meshlet->vertices[meshlet->indices[i][0]];
- unsigned int b = meshlet->vertices[meshlet->indices[i][1]];
- unsigned int c = meshlet->vertices[meshlet->indices[i][2]];
-
- assert(a < vertex_count && b < vertex_count && c < vertex_count);
+ unsigned int index = meshlet_vertices[meshlet_triangles[i]];
+ assert(index < vertex_count);
- indices[i * 3 + 0] = a;
- indices[i * 3 + 1] = b;
- indices[i * 3 + 2] = c;
+ indices[i] = index;
}
- return meshopt_computeClusterBounds(indices, meshlet->triangle_count * 3, vertex_positions, vertex_count, vertex_positions_stride);
+ return meshopt_computeClusterBounds(indices, triangle_count * 3, vertex_positions, vertex_count, vertex_positions_stride);
}
diff --git a/thirdparty/meshoptimizer/indexgenerator.cpp b/thirdparty/meshoptimizer/indexgenerator.cpp
index aa4a30efa4..f60db0dc4f 100644
--- a/thirdparty/meshoptimizer/indexgenerator.cpp
+++ b/thirdparty/meshoptimizer/indexgenerator.cpp
@@ -4,6 +4,8 @@
#include <assert.h>
#include <string.h>
+// This work is based on:
+// John McDonald, Mark Kilgard. Crack-Free Point-Normal Triangles using Adjacent Edge Normals. 2010
namespace meshopt
{
@@ -83,10 +85,49 @@ struct VertexStreamHasher
}
};
+struct EdgeHasher
+{
+ const unsigned int* remap;
+
+ size_t hash(unsigned long long edge) const
+ {
+ unsigned int e0 = unsigned(edge >> 32);
+ unsigned int e1 = unsigned(edge);
+
+ unsigned int h1 = remap[e0];
+ unsigned int h2 = remap[e1];
+
+ const unsigned int m = 0x5bd1e995;
+
+ // MurmurHash64B finalizer
+ h1 ^= h2 >> 18;
+ h1 *= m;
+ h2 ^= h1 >> 22;
+ h2 *= m;
+ h1 ^= h2 >> 17;
+ h1 *= m;
+ h2 ^= h1 >> 19;
+ h2 *= m;
+
+ return h2;
+ }
+
+ bool equal(unsigned long long lhs, unsigned long long rhs) const
+ {
+ unsigned int l0 = unsigned(lhs >> 32);
+ unsigned int l1 = unsigned(lhs);
+
+ unsigned int r0 = unsigned(rhs >> 32);
+ unsigned int r1 = unsigned(rhs);
+
+ return remap[l0] == remap[r0] && remap[l1] == remap[r1];
+ }
+};
+
static size_t hashBuckets(size_t count)
{
size_t buckets = 1;
- while (buckets < count)
+ while (buckets < count + count / 4)
buckets *= 2;
return buckets;
@@ -119,6 +160,26 @@ static T* hashLookup(T* table, size_t buckets, const Hash& hash, const T& key, c
return 0;
}
+static void buildPositionRemap(unsigned int* remap, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, meshopt_Allocator& allocator)
+{
+ VertexHasher vertex_hasher = {reinterpret_cast<const unsigned char*>(vertex_positions), 3 * sizeof(float), vertex_positions_stride};
+
+ size_t vertex_table_size = hashBuckets(vertex_count);
+ unsigned int* vertex_table = allocator.allocate<unsigned int>(vertex_table_size);
+ memset(vertex_table, -1, vertex_table_size * sizeof(unsigned int));
+
+ for (size_t i = 0; i < vertex_count; ++i)
+ {
+ unsigned int index = unsigned(i);
+ unsigned int* entry = hashLookup(vertex_table, vertex_table_size, vertex_hasher, index, ~0u);
+
+ if (*entry == ~0u)
+ *entry = index;
+
+ remap[index] = *entry;
+ }
+}
+
} // namespace meshopt
size_t meshopt_generateVertexRemap(unsigned int* destination, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size)
@@ -345,3 +406,146 @@ void meshopt_generateShadowIndexBufferMulti(unsigned int* destination, const uns
destination[i] = remap[index];
}
}
+
+void meshopt_generateAdjacencyIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
+{
+ using namespace meshopt;
+
+ assert(index_count % 3 == 0);
+ assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
+ assert(vertex_positions_stride % sizeof(float) == 0);
+
+ meshopt_Allocator allocator;
+
+ static const int next[4] = {1, 2, 0, 1};
+
+ // build position remap: for each vertex, which other (canonical) vertex does it map to?
+ unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
+ buildPositionRemap(remap, vertex_positions, vertex_count, vertex_positions_stride, allocator);
+
+ // build edge set; this stores all triangle edges but we can look these up by any other wedge
+ EdgeHasher edge_hasher = {remap};
+
+ size_t edge_table_size = hashBuckets(index_count);
+ unsigned long long* edge_table = allocator.allocate<unsigned long long>(edge_table_size);
+ unsigned int* edge_vertex_table = allocator.allocate<unsigned int>(edge_table_size);
+
+ memset(edge_table, -1, edge_table_size * sizeof(unsigned long long));
+ memset(edge_vertex_table, -1, edge_table_size * sizeof(unsigned int));
+
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+ for (int e = 0; e < 3; ++e)
+ {
+ unsigned int i0 = indices[i + e];
+ unsigned int i1 = indices[i + next[e]];
+ unsigned int i2 = indices[i + next[e + 1]];
+ assert(i0 < vertex_count && i1 < vertex_count && i2 < vertex_count);
+
+ unsigned long long edge = ((unsigned long long)i0 << 32) | i1;
+ unsigned long long* entry = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);
+
+ if (*entry == ~0ull)
+ {
+ *entry = edge;
+
+ // store vertex opposite to the edge
+ edge_vertex_table[entry - edge_table] = i2;
+ }
+ }
+ }
+
+ // build resulting index buffer: 6 indices for each input triangle
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+ unsigned int patch[6];
+
+ for (int e = 0; e < 3; ++e)
+ {
+ unsigned int i0 = indices[i + e];
+ unsigned int i1 = indices[i + next[e]];
+ assert(i0 < vertex_count && i1 < vertex_count);
+
+ // note: this refers to the opposite edge!
+ unsigned long long edge = ((unsigned long long)i1 << 32) | i0;
+ unsigned long long* oppe = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);
+
+ patch[e * 2 + 0] = i0;
+ patch[e * 2 + 1] = (*oppe == ~0ull) ? i0 : edge_vertex_table[oppe - edge_table];
+ }
+
+ memcpy(destination + i * 2, patch, sizeof(patch));
+ }
+}
+
+void meshopt_generateTessellationIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
+{
+ using namespace meshopt;
+
+ assert(index_count % 3 == 0);
+ assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
+ assert(vertex_positions_stride % sizeof(float) == 0);
+
+ meshopt_Allocator allocator;
+
+ static const int next[3] = {1, 2, 0};
+
+ // build position remap: for each vertex, which other (canonical) vertex does it map to?
+ unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
+ buildPositionRemap(remap, vertex_positions, vertex_count, vertex_positions_stride, allocator);
+
+ // build edge set; this stores all triangle edges but we can look these up by any other wedge
+ EdgeHasher edge_hasher = {remap};
+
+ size_t edge_table_size = hashBuckets(index_count);
+ unsigned long long* edge_table = allocator.allocate<unsigned long long>(edge_table_size);
+ memset(edge_table, -1, edge_table_size * sizeof(unsigned long long));
+
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+ for (int e = 0; e < 3; ++e)
+ {
+ unsigned int i0 = indices[i + e];
+ unsigned int i1 = indices[i + next[e]];
+ assert(i0 < vertex_count && i1 < vertex_count);
+
+ unsigned long long edge = ((unsigned long long)i0 << 32) | i1;
+ unsigned long long* entry = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);
+
+ if (*entry == ~0ull)
+ *entry = edge;
+ }
+ }
+
+ // build resulting index buffer: 12 indices for each input triangle
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+ unsigned int patch[12];
+
+ for (int e = 0; e < 3; ++e)
+ {
+ unsigned int i0 = indices[i + e];
+ unsigned int i1 = indices[i + next[e]];
+ assert(i0 < vertex_count && i1 < vertex_count);
+
+ // note: this refers to the opposite edge!
+ unsigned long long edge = ((unsigned long long)i1 << 32) | i0;
+ unsigned long long oppe = *hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);
+
+ // use the same edge if opposite edge doesn't exist (border)
+ oppe = (oppe == ~0ull) ? edge : oppe;
+
+ // triangle index (0, 1, 2)
+ patch[e] = i0;
+
+ // opposite edge (3, 4; 5, 6; 7, 8)
+ patch[3 + e * 2 + 0] = unsigned(oppe);
+ patch[3 + e * 2 + 1] = unsigned(oppe >> 32);
+
+ // dominant vertex (9, 10, 11)
+ patch[9 + e] = remap[i0];
+ }
+
+ memcpy(destination + i * 4, patch, sizeof(patch));
+ }
+}
diff --git a/thirdparty/meshoptimizer/meshoptimizer.h b/thirdparty/meshoptimizer/meshoptimizer.h
index 1714000384..fe8d349731 100644
--- a/thirdparty/meshoptimizer/meshoptimizer.h
+++ b/thirdparty/meshoptimizer/meshoptimizer.h
@@ -1,7 +1,7 @@
/**
- * meshoptimizer - version 0.15
+ * meshoptimizer - version 0.16
*
- * Copyright (C) 2016-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Copyright (C) 2016-2021, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at https://github.com/zeux/meshoptimizer
*
* This library is distributed under the MIT License. See notice at the end of this file.
@@ -12,7 +12,7 @@
#include <stddef.h>
/* Version macro; major * 1000 + minor * 10 + patch */
-#define MESHOPTIMIZER_VERSION 150 /* 0.15 */
+#define MESHOPTIMIZER_VERSION 160 /* 0.16 */
/* If no API is defined, assume default */
#ifndef MESHOPTIMIZER_API
@@ -98,6 +98,35 @@ MESHOPTIMIZER_API void meshopt_generateShadowIndexBuffer(unsigned int* destinati
MESHOPTIMIZER_API void meshopt_generateShadowIndexBufferMulti(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, const struct meshopt_Stream* streams, size_t stream_count);
/**
+ * Generate index buffer that can be used as a geometry shader input with triangle adjacency topology
+ * Each triangle is converted into a 6-vertex patch with the following layout:
+ * - 0, 2, 4: original triangle vertices
+ * - 1, 3, 5: vertices adjacent to edges 02, 24 and 40
+ * The resulting patch can be rendered with geometry shaders using e.g. VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY.
+ * This can be used to implement algorithms like silhouette detection/expansion and other forms of GS-driven rendering.
+ *
+ * destination must contain enough space for the resulting index buffer (index_count*2 elements)
+ * vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
+ */
+MESHOPTIMIZER_EXPERIMENTAL void meshopt_generateAdjacencyIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
+
+/**
+ * Generate index buffer that can be used for PN-AEN tessellation with crack-free displacement
+ * Each triangle is converted into a 12-vertex patch with the following layout:
+ * - 0, 1, 2: original triangle vertices
+ * - 3, 4: opposing edge for edge 0, 1
+ * - 5, 6: opposing edge for edge 1, 2
+ * - 7, 8: opposing edge for edge 2, 0
+ * - 9, 10, 11: dominant vertices for corners 0, 1, 2
+ * The resulting patch can be rendered with hardware tessellation using PN-AEN and displacement mapping.
+ * See "Tessellation on Any Budget" (John McDonald, GDC 2011) for implementation details.
+ *
+ * destination must contain enough space for the resulting index buffer (index_count*4 elements)
+ * vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
+ */
+MESHOPTIMIZER_EXPERIMENTAL void meshopt_generateTessellationIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
+
+/**
* Vertex transform cache optimizer
* Reorders indices to reduce the number of GPU vertex shader invocations
* If index buffer contains multiple ranges for multiple draw calls, this functions needs to be called on each range individually.
@@ -373,22 +402,31 @@ MESHOPTIMIZER_API struct meshopt_VertexFetchStatistics meshopt_analyzeVertexFetc
struct meshopt_Meshlet
{
- unsigned int vertices[64];
- unsigned char indices[126][3];
- unsigned char triangle_count;
- unsigned char vertex_count;
+ /* offsets within meshlet_vertices and meshlet_triangles arrays with meshlet data */
+ unsigned int vertex_offset;
+ unsigned int triangle_offset;
+
+ /* number of vertices and triangles used in the meshlet; data is stored in consecutive range defined by offset and count */
+ unsigned int vertex_count;
+ unsigned int triangle_count;
};
/**
* Experimental: Meshlet builder
* Splits the mesh into a set of meshlets where each meshlet has a micro index buffer indexing into meshlet vertices that refer to the original vertex buffer
* The resulting data can be used to render meshes using NVidia programmable mesh shading pipeline, or in other cluster-based renderers.
- * For maximum efficiency the index buffer being converted has to be optimized for vertex cache first.
+ * When using buildMeshlets, vertex positions need to be provided to minimize the size of the resulting clusters.
+ * When using buildMeshletsScan, for maximum efficiency the index buffer being converted has to be optimized for vertex cache first.
*
- * destination must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound
- * max_vertices and max_triangles can't exceed limits statically declared in meshopt_Meshlet (max_vertices <= 64, max_triangles <= 126)
+ * meshlets must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound
+ * meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices
+ * meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3
+ * vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
+ * max_vertices and max_triangles must not exceed implementation limits (max_vertices <= 255 - not 256!, max_triangles <= 512)
+ * cone_weight should be set to 0 when cone culling is not used, and a value between 0 and 1 otherwise to balance between cluster size and cone culling efficiency
*/
-MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshlets(struct meshopt_Meshlet* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles);
+MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshlets(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight);
+MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshletsScan(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles);
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles);
struct meshopt_Bounds
@@ -426,10 +464,10 @@ struct meshopt_Bounds
* to do frustum/occlusion culling, the formula that doesn't use the apex may be preferable.
*
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
- * index_count should be less than or equal to 256*3 (the function assumes clusters of limited size)
+ * index_count/3 should be less than or equal to 512 (the function assumes clusters of limited size)
*/
MESHOPTIMIZER_EXPERIMENTAL struct meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
-MESHOPTIMIZER_EXPERIMENTAL struct meshopt_Bounds meshopt_computeMeshletBounds(const struct meshopt_Meshlet* meshlet, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
+MESHOPTIMIZER_EXPERIMENTAL struct meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices, const unsigned char* meshlet_triangles, size_t triangle_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
/**
* Experimental: Spatial sorter
@@ -513,6 +551,10 @@ inline void meshopt_generateShadowIndexBuffer(T* destination, const T* indices,
template <typename T>
inline void meshopt_generateShadowIndexBufferMulti(T* destination, const T* indices, size_t index_count, size_t vertex_count, const meshopt_Stream* streams, size_t stream_count);
template <typename T>
+inline void meshopt_generateAdjacencyIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
+template <typename T>
+inline void meshopt_generateTessellationIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
+template <typename T>
inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count);
template <typename T>
inline void meshopt_optimizeVertexCacheStrip(T* destination, const T* indices, size_t index_count, size_t vertex_count);
@@ -547,7 +589,9 @@ inline meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const T* indices, size
template <typename T>
inline meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const T* indices, size_t index_count, size_t vertex_count, size_t vertex_size);
template <typename T>
-inline size_t meshopt_buildMeshlets(meshopt_Meshlet* destination, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles);
+inline size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight);
+template <typename T>
+inline size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles);
template <typename T>
inline meshopt_Bounds meshopt_computeClusterBounds(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
template <typename T>
@@ -762,6 +806,24 @@ inline void meshopt_generateShadowIndexBufferMulti(T* destination, const T* indi
}
template <typename T>
+inline void meshopt_generateAdjacencyIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
+{
+ meshopt_IndexAdapter<T> in(0, indices, index_count);
+ meshopt_IndexAdapter<T> out(destination, 0, index_count * 2);
+
+ meshopt_generateAdjacencyIndexBuffer(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);
+}
+
+template <typename T>
+inline void meshopt_generateTessellationIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
+{
+ meshopt_IndexAdapter<T> in(0, indices, index_count);
+ meshopt_IndexAdapter<T> out(destination, 0, index_count * 4);
+
+ meshopt_generateTessellationIndexBuffer(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);
+}
+
+template <typename T>
inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count)
{
meshopt_IndexAdapter<T> in(0, indices, index_count);
@@ -908,11 +970,19 @@ inline meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const T* indices
}
template <typename T>
-inline size_t meshopt_buildMeshlets(meshopt_Meshlet* destination, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles)
+inline size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight)
+{
+ meshopt_IndexAdapter<T> in(0, indices, index_count);
+
+ return meshopt_buildMeshlets(meshlets, meshlet_vertices, meshlet_triangles, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, max_triangles, cone_weight);
+}
+
+template <typename T>
+inline size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles)
{
meshopt_IndexAdapter<T> in(0, indices, index_count);
- return meshopt_buildMeshlets(destination, in.data, index_count, vertex_count, max_vertices, max_triangles);
+ return meshopt_buildMeshletsScan(meshlets, meshlet_vertices, meshlet_triangles, in.data, index_count, vertex_count, max_vertices, max_triangles);
}
template <typename T>
@@ -934,7 +1004,7 @@ inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_
#endif
/**
- * Copyright (c) 2016-2020 Arseny Kapoulkine
+ * Copyright (c) 2016-2021 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
index 942db14461..b2cb589462 100644
--- a/thirdparty/meshoptimizer/simplifier.cpp
+++ b/thirdparty/meshoptimizer/simplifier.cpp
@@ -131,7 +131,7 @@ struct PositionHasher
static size_t hashBuckets2(size_t count)
{
size_t buckets = 1;
- while (buckets < count)
+ while (buckets < count + count / 4)
buckets *= 2;
return buckets;
diff --git a/thirdparty/meshoptimizer/vertexcodec.cpp b/thirdparty/meshoptimizer/vertexcodec.cpp
index 2cbfaac367..5f3ec204ab 100644
--- a/thirdparty/meshoptimizer/vertexcodec.cpp
+++ b/thirdparty/meshoptimizer/vertexcodec.cpp
@@ -710,18 +710,12 @@ static v128_t decodeShuffleMask(unsigned char mask0, unsigned char mask1)
SIMD_TARGET
static void wasmMoveMask(v128_t mask, unsigned char& mask0, unsigned char& mask1)
{
- v128_t mask_0 = wasm_v32x4_shuffle(mask, mask, 0, 2, 1, 3);
-
- uint64_t mask_1a = wasm_i64x2_extract_lane(mask_0, 0) & 0x0804020108040201ull;
- uint64_t mask_1b = wasm_i64x2_extract_lane(mask_0, 1) & 0x8040201080402010ull;
+ // magic constant found using z3 SMT assuming mask has 8 groups of 0xff or 0x00
+ const uint64_t magic = 0x000103070f1f3f80ull;
// TODO: This can use v8x16_bitmask in the future
- uint64_t mask_2 = mask_1a | mask_1b;
- uint64_t mask_4 = mask_2 | (mask_2 >> 16);
- uint64_t mask_8 = mask_4 | (mask_4 >> 8);
-
- mask0 = uint8_t(mask_8);
- mask1 = uint8_t(mask_8 >> 32);
+ mask0 = uint8_t((wasm_i64x2_extract_lane(mask, 0) * magic) >> 56);
+ mask1 = uint8_t((wasm_i64x2_extract_lane(mask, 1) * magic) >> 56);
}
SIMD_TARGET