Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig9
-rw-r--r--CMakeLists.txt11
-rw-r--r--GNUmakefile3
-rw-r--r--build_files/build_environment/CMakeLists.txt1
-rw-r--r--build_files/build_environment/cmake/boost.cmake9
-rw-r--r--build_files/build_environment/cmake/brotli.cmake38
-rw-r--r--build_files/build_environment/cmake/download.cmake1
-rw-r--r--build_files/build_environment/cmake/freetype.cmake19
-rw-r--r--build_files/build_environment/cmake/harvest.cmake2
-rw-r--r--build_files/build_environment/cmake/python_site_packages.cmake2
-rw-r--r--build_files/build_environment/cmake/versions.cmake40
-rwxr-xr-xbuild_files/build_environment/install_deps.sh40
-rw-r--r--build_files/build_environment/windows/build_deps.cmd53
-rw-r--r--build_files/cmake/Modules/FindBrotli.cmake83
-rw-r--r--build_files/cmake/Modules/FindPythonLibsUnix.cmake2
-rw-r--r--build_files/cmake/clang_array_check.py9
-rw-r--r--build_files/cmake/macros.cmake26
-rw-r--r--build_files/cmake/platform/platform_apple.cmake17
-rw-r--r--build_files/cmake/platform/platform_unix.cmake35
-rw-r--r--build_files/cmake/platform/platform_win32.cmake18
-rw-r--r--build_files/windows/autodetect_msvc.cmd3
-rw-r--r--build_files/windows/check_libraries.cmd1
-rw-r--r--build_files/windows/configure_msbuild.cmd6
-rw-r--r--build_files/windows/configure_ninja.cmd12
-rw-r--r--build_files/windows/detect_msvc2017.cmd3
-rw-r--r--build_files/windows/find_dependencies.cmd29
-rw-r--r--build_files/windows/format.cmd12
-rw-r--r--build_files/windows/icons.cmd16
-rw-r--r--build_files/windows/icons_geom.cmd16
-rw-r--r--build_files/windows/parse_arguments.cmd8
-rw-r--r--build_files/windows/show_help.cmd6
-rw-r--r--build_files/windows/svn_fix.cmd1
-rw-r--r--build_files/windows/update_sources.cmd9
-rw-r--r--doc/doxygen/Doxyfile2
-rw-r--r--doc/python_api/rst/info_api_reference.rst10
-rw-r--r--doc/python_api/rst/info_best_practice.rst6
-rw-r--r--doc/python_api/rst/info_gotcha.rst48
-rw-r--r--doc/python_api/rst/info_tips_and_tricks.rst6
-rw-r--r--doc/python_api/sphinx_doc_gen.py18
-rw-r--r--doc/python_api/static/css/version_switch.css127
-rw-r--r--doc/python_api/static/js/version_switch.js323
-rw-r--r--doc/python_api/templates/versions.html17
-rw-r--r--extern/CMakeLists.txt2
-rw-r--r--intern/cycles/blender/addon/properties.py30
-rw-r--r--intern/cycles/blender/addon/ui.py42
-rw-r--r--intern/cycles/blender/curves.cpp90
-rw-r--r--intern/cycles/blender/geometry.cpp8
-rw-r--r--intern/cycles/blender/object.cpp4
-rw-r--r--intern/cycles/blender/output_driver.cpp2
-rw-r--r--intern/cycles/blender/shader.cpp3
-rw-r--r--intern/cycles/blender/sync.cpp1
-rw-r--r--intern/cycles/bvh/embree.cpp56
-rw-r--r--intern/cycles/bvh/params.h4
-rw-r--r--intern/cycles/cmake/external_libs.cmake4
-rw-r--r--intern/cycles/device/hip/device_impl.cpp4
-rw-r--r--intern/cycles/device/metal/bvh.h5
-rw-r--r--intern/cycles/device/metal/bvh.mm217
-rw-r--r--intern/cycles/device/metal/kernel.h2
-rw-r--r--intern/cycles/device/metal/kernel.mm36
-rw-r--r--intern/cycles/device/optix/device_impl.cpp2
-rw-r--r--intern/cycles/integrator/denoiser.cpp38
-rw-r--r--intern/cycles/integrator/denoiser_oidn.cpp21
-rw-r--r--intern/cycles/integrator/denoiser_oidn.h1
-rw-r--r--intern/cycles/integrator/pass_accessor.cpp7
-rw-r--r--intern/cycles/integrator/path_trace.cpp7
-rw-r--r--intern/cycles/integrator/shader_eval.cpp2
-rw-r--r--intern/cycles/kernel/bvh/bvh.h38
-rw-r--r--intern/cycles/kernel/bvh/embree.h35
-rw-r--r--intern/cycles/kernel/bvh/local.h8
-rw-r--r--intern/cycles/kernel/bvh/metal.h3
-rw-r--r--intern/cycles/kernel/bvh/shadow_all.h3
-rw-r--r--intern/cycles/kernel/bvh/traversal.h72
-rw-r--r--intern/cycles/kernel/bvh/util.h81
-rw-r--r--intern/cycles/kernel/bvh/volume.h6
-rw-r--r--intern/cycles/kernel/bvh/volume_all.h6
-rw-r--r--intern/cycles/kernel/device/metal/kernel.metal200
-rw-r--r--intern/cycles/kernel/device/optix/kernel.cu45
-rw-r--r--intern/cycles/kernel/film/read.h15
-rw-r--r--intern/cycles/kernel/geom/curve.h12
-rw-r--r--intern/cycles/kernel/geom/motion_triangle_intersect.h112
-rw-r--r--intern/cycles/kernel/geom/motion_triangle_shader.h10
-rw-r--r--intern/cycles/kernel/geom/point.h46
-rw-r--r--intern/cycles/kernel/geom/shader_data.h54
-rw-r--r--intern/cycles/kernel/geom/triangle_intersect.h114
-rw-r--r--intern/cycles/kernel/integrator/intersect_closest.h6
-rw-r--r--intern/cycles/kernel/integrator/intersect_shadow.h5
-rw-r--r--intern/cycles/kernel/integrator/intersect_volume_stack.h13
-rw-r--r--intern/cycles/kernel/integrator/shade_light.h7
-rw-r--r--intern/cycles/kernel/integrator/shade_shadow.h7
-rw-r--r--intern/cycles/kernel/integrator/shade_surface.h44
-rw-r--r--intern/cycles/kernel/integrator/shade_volume.h30
-rw-r--r--intern/cycles/kernel/integrator/shadow_state_template.h1
-rw-r--r--intern/cycles/kernel/integrator/subsurface.h5
-rw-r--r--intern/cycles/kernel/integrator/subsurface_disk.h4
-rw-r--r--intern/cycles/kernel/integrator/subsurface_random_walk.h24
-rw-r--r--intern/cycles/kernel/light/light.h90
-rw-r--r--intern/cycles/kernel/light/sample.h10
-rw-r--r--intern/cycles/kernel/osl/services.cpp14
-rw-r--r--intern/cycles/kernel/osl/services.h2
-rw-r--r--intern/cycles/kernel/osl/shaders/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/osl/shaders/node_point_info.osl26
-rw-r--r--intern/cycles/kernel/svm/ao.h6
-rw-r--r--intern/cycles/kernel/svm/attribute.h12
-rw-r--r--intern/cycles/kernel/svm/bevel.h21
-rw-r--r--intern/cycles/kernel/svm/geometry.h31
-rw-r--r--intern/cycles/kernel/svm/svm.h11
-rw-r--r--intern/cycles/kernel/svm/types.h9
-rw-r--r--intern/cycles/kernel/types.h32
-rw-r--r--intern/cycles/scene/colorspace.cpp89
-rw-r--r--intern/cycles/scene/colorspace.h6
-rw-r--r--intern/cycles/scene/constant_fold.cpp10
-rw-r--r--intern/cycles/scene/geometry.cpp1
-rw-r--r--intern/cycles/scene/image.cpp16
-rw-r--r--intern/cycles/scene/scene.cpp1
-rw-r--r--intern/cycles/scene/scene.h3
-rw-r--r--intern/cycles/scene/shader_nodes.cpp73
-rw-r--r--intern/cycles/scene/shader_nodes.h15
-rw-r--r--intern/cycles/util/math.h2
-rw-r--r--intern/ghost/CMakeLists.txt12
-rw-r--r--intern/ghost/GHOST_C-api.h11
-rw-r--r--intern/ghost/GHOST_ISystem.h11
-rw-r--r--intern/ghost/GHOST_Types.h7
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp4
-rw-r--r--intern/ghost/intern/GHOST_DisplayManagerCocoa.mm5
-rw-r--r--intern/ghost/intern/GHOST_DisplayManagerX11.cpp8
-rw-r--r--intern/ghost/intern/GHOST_ImeWin32.cpp8
-rw-r--r--intern/ghost/intern/GHOST_ImeWin32.h5
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_SystemNULL.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp2
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp20
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h11
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp7
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.h2
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp34
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp11
-rw-r--r--intern/ghost/intern/GHOST_XrContext.h1
-rw-r--r--intern/ghost/test/CMakeLists.txt2
-rw-r--r--intern/guardedalloc/MEM_guardedalloc.h10
-rw-r--r--intern/iksolver/extern/IK_solver.h1
-rw-r--r--intern/iksolver/intern/IK_Math.h1
-rw-r--r--intern/iksolver/intern/IK_QSegment.h1
-rw-r--r--intern/iksolver/intern/IK_QTask.h1
-rw-r--r--intern/locale/CMakeLists.txt1
-rw-r--r--intern/quadriflow/quadriflow_capi.cpp34
-rw-r--r--intern/quadriflow/quadriflow_capi.hpp34
-rw-r--r--intern/sky/source/sky_model.cpp54
-rw-r--r--intern/sky/source/sky_model_data.h54
-rw-r--r--intern/sky/source/sky_nishita.cpp4
-rw-r--r--make.bat11
m---------release/datafiles/locale0
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/modules/bl_i18n_utils/settings.py3
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils_spell_check.py6
-rw-r--r--release/scripts/modules/bpy_extras/io_utils.py2
-rw-r--r--release/scripts/modules/console/__init__.py4
-rw-r--r--release/scripts/modules/console/complete_calltip.py4
-rw-r--r--release/scripts/modules/console/complete_import.py4
-rw-r--r--release/scripts/modules/console/complete_namespace.py4
-rw-r--r--release/scripts/modules/console/intellisense.py4
-rw-r--r--release/scripts/modules/console_python.py2
-rw-r--r--release/scripts/modules/rna_manual_reference.py3
-rw-r--r--release/scripts/modules/rna_xml.py32
-rw-r--r--release/scripts/presets/keyconfig/Blender.py3
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py74
-rw-r--r--release/scripts/startup/bl_operators/object_align.py1
-rw-r--r--release/scripts/startup/bl_operators/vertexpaint_dirt.py5
-rw-r--r--release/scripts/startup/bl_operators/wm.py68
-rw-r--r--release/scripts/startup/bl_ui/__init__.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_data_armature.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_curves.py (renamed from release/scripts/startup/bl_ui/properties_data_hair.py)48
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py13
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py30
-rw-r--r--release/scripts/startup/bl_ui/properties_output.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py1
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py4
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py10
-rw-r--r--release/scripts/startup/bl_ui/space_graph.py1
-rw-r--r--release/scripts/startup/bl_ui/space_nla.py3
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py2
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py3
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py10
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py3
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py6
-rw-r--r--release/scripts/startup/nodeitems_builtins.py8
-rw-r--r--release/scripts/templates_py/operator_modal_timer.py2
-rw-r--r--source/CMakeLists.txt2
-rw-r--r--source/blender/CMakeLists.txt2
-rw-r--r--source/blender/blendthumb/CMakeLists.txt1
-rw-r--r--source/blender/blenfont/BLF_api.h2
-rw-r--r--source/blender/blenfont/CMakeLists.txt6
-rw-r--r--source/blender/blenfont/intern/blf_default.c6
-rw-r--r--source/blender/blenfont/intern/blf_font.c65
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c49
-rw-r--r--source/blender/blenfont/intern/blf_internal.h20
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h6
-rw-r--r--source/blender/blenfont/intern/blf_thumbs.c7
-rw-r--r--source/blender/blenkernel/BKE_DerivedMesh.h73
-rw-r--r--source/blender/blenkernel/BKE_action.h1
-rw-r--r--source/blender/blenkernel/BKE_armature.h32
-rw-r--r--source/blender/blenkernel/BKE_attribute.h4
-rw-r--r--source/blender/blenkernel/BKE_attribute_math.hh82
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_blendfile_link_append.h2
-rw-r--r--source/blender/blenkernel/BKE_camera.h2
-rw-r--r--source/blender/blenkernel/BKE_collection.h11
-rw-r--r--source/blender/blenkernel/BKE_constraint.h12
-rw-r--r--source/blender/blenkernel/BKE_crazyspace.h27
-rw-r--r--source/blender/blenkernel/BKE_curve_to_mesh.hh2
-rw-r--r--source/blender/blenkernel/BKE_curves.h68
-rw-r--r--source/blender/blenkernel/BKE_customdata.h11
-rw-r--r--source/blender/blenkernel/BKE_duplilist.h2
-rw-r--r--source/blender/blenkernel/BKE_editmesh.h11
-rw-r--r--source/blender/blenkernel/BKE_hair.h63
-rw-r--r--source/blender/blenkernel/BKE_idtype.h4
-rw-r--r--source/blender/blenkernel/BKE_image.h40
-rw-r--r--source/blender/blenkernel/BKE_image_partial_update.hh298
-rw-r--r--source/blender/blenkernel/BKE_layer.h8
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h14
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h23
-rw-r--r--source/blender/blenkernel/BKE_lib_query.h3
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h76
-rw-r--r--source/blender/blenkernel/BKE_main.h6
-rw-r--r--source/blender/blenkernel/BKE_mesh.h3
-rw-r--r--source/blender/blenkernel/BKE_mesh_fair.h5
-rw-r--r--source/blender/blenkernel/BKE_modifier.h9
-rw-r--r--source/blender/blenkernel/BKE_node.h125
-rw-r--r--source/blender/blenkernel/BKE_node_tree_update.h6
-rw-r--r--source/blender/blenkernel/BKE_object.h25
-rw-r--r--source/blender/blenkernel/BKE_paint.h5
-rw-r--r--source/blender/blenkernel/BKE_particle.h7
-rw-r--r--source/blender/blenkernel/BKE_screen.h6
-rw-r--r--source/blender/blenkernel/BKE_shrinkwrap.h2
-rw-r--r--source/blender/blenkernel/BKE_subdiv_eval.h2
-rw-r--r--source/blender/blenkernel/CMakeLists.txt15
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc286
-rw-r--r--source/blender/blenkernel/intern/action.c19
-rw-r--r--source/blender/blenkernel/intern/anim_data.c8
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c4
-rw-r--r--source/blender/blenkernel/intern/armature.c221
-rw-r--r--source/blender/blenkernel/intern/armature_pose.cc2
-rw-r--r--source/blender/blenkernel/intern/armature_update.c60
-rw-r--r--source/blender/blenkernel/intern/asset_catalog.cc2
-rw-r--r--source/blender/blenkernel/intern/attribute.c29
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc17
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh3
-rw-r--r--source/blender/blenkernel/intern/blendfile.c32
-rw-r--r--source/blender/blenkernel/intern/blendfile_link_append.c83
-rw-r--r--source/blender/blenkernel/intern/brush.c23
-rw-r--r--source/blender/blenkernel/intern/camera.c2
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c48
-rw-r--r--source/blender/blenkernel/intern/collection.c17
-rw-r--r--source/blender/blenkernel/intern/constraint.c47
-rw-r--r--source/blender/blenkernel/intern/crazyspace.c98
-rw-r--r--source/blender/blenkernel/intern/curve.cc122
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc4
-rw-r--r--source/blender/blenkernel/intern/curves.cc473
-rw-r--r--source/blender/blenkernel/intern/customdata.cc46
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c7
-rw-r--r--source/blender/blenkernel/intern/editmesh.c39
-rw-r--r--source/blender/blenkernel/intern/effect.c21
-rw-r--r--source/blender/blenkernel/intern/fcurve_driver.c20
-rw-r--r--source/blender/blenkernel/intern/hair.cc435
-rw-r--r--source/blender/blenkernel/intern/idprop_serialize.cc28
-rw-r--r--source/blender/blenkernel/intern/idtype.c13
-rw-r--r--source/blender/blenkernel/intern/image.c34
-rw-r--r--source/blender/blenkernel/intern/image_gpu.cc192
-rw-r--r--source/blender/blenkernel/intern/image_partial_update.cc599
-rw-r--r--source/blender/blenkernel/intern/image_partial_update_test.cc393
-rw-r--r--source/blender/blenkernel/intern/layer.c44
-rw-r--r--source/blender/blenkernel/intern/lib_id.c119
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c43
-rw-r--r--source/blender/blenkernel/intern/lib_id_remapper.cc175
-rw-r--r--source/blender/blenkernel/intern/lib_id_remapper_test.cc83
-rw-r--r--source/blender/blenkernel/intern/lib_override.c457
-rw-r--r--source/blender/blenkernel/intern/lib_override_proxy_conversion.c176
-rw-r--r--source/blender/blenkernel/intern/lib_query.c8
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c127
-rw-r--r--source/blender/blenkernel/intern/lib_remap_test.cc4
-rw-r--r--source/blender/blenkernel/intern/main.c6
-rw-r--r--source/blender/blenkernel/intern/material.c31
-rw-r--r--source/blender/blenkernel/intern/mball.c9
-rw-r--r--source/blender/blenkernel/intern/mesh.cc32
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc7
-rw-r--r--source/blender/blenkernel/intern/mesh_fair.cc5
-rw-r--r--source/blender/blenkernel/intern/mesh_iterators.c18
-rw-r--r--source/blender/blenkernel/intern/mesh_merge.c13
-rw-r--r--source/blender/blenkernel/intern/mesh_mirror.c6
-rw-r--r--source/blender/blenkernel/intern/mesh_normals.cc58
-rw-r--r--source/blender/blenkernel/intern/mesh_validate.c11
-rw-r--r--source/blender/blenkernel/intern/modifier.c40
-rw-r--r--source/blender/blenkernel/intern/node.cc25
-rw-r--r--source/blender/blenkernel/intern/node_tree_update.cc7
-rw-r--r--source/blender/blenkernel/intern/object.cc377
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc2
-rw-r--r--source/blender/blenkernel/intern/object_update.c64
-rw-r--r--source/blender/blenkernel/intern/ocean.c6
-rw-r--r--source/blender/blenkernel/intern/particle.c2
-rw-r--r--source/blender/blenkernel/intern/particle_system.c7
-rw-r--r--source/blender/blenkernel/intern/pbvh.c24
-rw-r--r--source/blender/blenkernel/intern/pointcache.c30
-rw-r--r--source/blender/blenkernel/intern/scene.c2
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc72
-rw-r--r--source/blender/blenkernel/intern/subdiv_modifier.c2
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c171
-rw-r--r--source/blender/blenkernel/intern/texture.c6
-rw-r--r--source/blender/blenkernel/intern/type_conversions.cc64
-rw-r--r--source/blender/blenkernel/intern/vfontdata_freetype.c8
-rw-r--r--source/blender/blenkernel/intern/writeavi.c4
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c12
-rw-r--r--source/blender/blenlib/BLI_enumerable_thread_specific.hh2
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh14
-rw-r--r--source/blender/blenlib/BLI_math.h2
-rw-r--r--source/blender/blenlib/BLI_math_base.h2
-rw-r--r--source/blender/blenlib/BLI_math_color.h2
-rw-r--r--source/blender/blenlib/BLI_math_color_blend.h2
-rw-r--r--source/blender/blenlib/BLI_math_geom.h2
-rw-r--r--source/blender/blenlib/BLI_math_inline.h2
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h17
-rw-r--r--source/blender/blenlib/BLI_math_rotation.h2
-rw-r--r--source/blender/blenlib/BLI_math_vector.h2
-rw-r--r--source/blender/blenlib/BLI_math_vector.hh5
-rw-r--r--source/blender/blenlib/BLI_mempool.h2
-rw-r--r--source/blender/blenlib/BLI_path_util.h22
-rw-r--r--source/blender/blenlib/BLI_string_ref.hh27
-rw-r--r--source/blender/blenlib/BLI_task.hh14
-rw-r--r--source/blender/blenlib/BLI_vector_set.hh4
-rw-r--r--source/blender/blenlib/CMakeLists.txt8
-rw-r--r--source/blender/blenlib/intern/BLI_dynstr.c2
-rw-r--r--source/blender/blenlib/intern/BLI_linklist.c1
-rw-r--r--source/blender/blenlib/intern/bitmap_draw_2d.c2
-rw-r--r--source/blender/blenlib/intern/hash_md5.c2
-rw-r--r--source/blender/blenlib/intern/list_sort_impl.h2
-rw-r--r--source/blender/blenlib/intern/math_base.c2
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c2
-rw-r--r--source/blender/blenlib/intern/math_color.c2
-rw-r--r--source/blender/blenlib/intern/math_color_blend_inline.c2
-rw-r--r--source/blender/blenlib/intern/math_color_inline.c2
-rw-r--r--source/blender/blenlib/intern/math_geom.c6
-rw-r--r--source/blender/blenlib/intern/math_geom_inline.c2
-rw-r--r--source/blender/blenlib/intern/math_interp.c4
-rw-r--r--source/blender/blenlib/intern/math_matrix.c23
-rw-r--r--source/blender/blenlib/intern/math_rotation.c2
-rw-r--r--source/blender/blenlib/intern/math_vector.c2
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c2
-rw-r--r--source/blender/blenlib/intern/path_util.c24
-rw-r--r--source/blender/blenlib/intern/polyfill_2d.c2
-rw-r--r--source/blender/blenlib/intern/scanfill.c1
-rw-r--r--source/blender/blenlib/intern/voronoi_2d.c2
-rw-r--r--source/blender/blenlib/intern/winstuff.c2
-rw-r--r--source/blender/blenlib/intern/winstuff_dir.c1
-rw-r--r--source/blender/blenlib/tests/BLI_task_test.cc14
-rw-r--r--source/blender/blenlib/tests/BLI_vector_set_test.cc10
-rw-r--r--source/blender/blenloader/BLO_readfile.h2
-rw-r--r--source/blender/blenloader/BLO_undofile.h4
-rw-r--r--source/blender/blenloader/CMakeLists.txt4
-rw-r--r--source/blender/blenloader/intern/readblenentry.c5
-rw-r--r--source/blender/blenloader/intern/readfile.c23
-rw-r--r--source/blender/blenloader/intern/readfile.h10
-rw-r--r--source/blender/blenloader/intern/undofile.c1
-rw-r--r--source/blender/blenloader/intern/versioning_260.c1
-rw-r--r--source/blender/blenloader/intern/versioning_280.c8
-rw-r--r--source/blender/blenloader/intern/versioning_290.c12
-rw-r--r--source/blender/blenloader/intern/versioning_300.c73
-rw-r--r--source/blender/blenloader/intern/versioning_cycles.c2
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c7
-rw-r--r--source/blender/blenloader/intern/writefile.c2
-rw-r--r--source/blender/blentranslation/BLT_translation.h28
-rw-r--r--source/blender/bmesh/CMakeLists.txt6
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.cc (renamed from source/blender/bmesh/intern/bmesh_mesh_convert.c)281
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_private.h8
-rw-r--r--source/blender/bmesh/intern/bmesh_query.h2
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c144
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.cc4
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_collapse.c4
-rw-r--r--source/blender/compositor/CMakeLists.txt18
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc8
-rw-r--r--source/blender/compositor/nodes/COM_AntiAliasingNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_AntiAliasingNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CombineXYZNode.cc55
-rw-r--r--source/blender/compositor/nodes/COM_CombineXYZNode.h36
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.cc5
-rw-r--r--source/blender/compositor/nodes/COM_FilterNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_SeparateXYZNode.cc63
-rw-r--r--source/blender/compositor/nodes/COM_SeparateXYZNode.h36
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.cc14
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc41
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc68
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc54
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h5
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc61
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_from_ids.cc22
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_from_ids.h5
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_eval.cc8
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc23
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc54
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node.cc7
-rw-r--r--source/blender/depsgraph/intern/node/deg_node.h2
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.cc2
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.h20
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.h3
-rw-r--r--source/blender/draw/CMakeLists.txt26
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c28
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_depth_of_field.c7
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h8
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c7
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders.c8
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c2
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl8
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl9
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl3
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_vert.glsl3
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_lib.glsl10
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_vert.glsl3
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c2
-rw-r--r--source/blender/draw/engines/image/image_batches.hh106
-rw-r--r--source/blender/draw/engines/image/image_drawing_mode.hh518
-rw-r--r--source/blender/draw/engines/image/image_drawing_mode_image_space.hh147
-rw-r--r--source/blender/draw/engines/image/image_engine.cc73
-rw-r--r--source/blender/draw/engines/image/image_instance_data.hh119
-rw-r--r--source/blender/draw/engines/image/image_partial_updater.hh78
-rw-r--r--source/blender/draw/engines/image/image_private.hh140
-rw-r--r--source/blender/draw/engines/image/image_shader.cc43
-rw-r--r--source/blender/draw/engines/image/image_shader_params.hh57
-rw-r--r--source/blender/draw/engines/image/image_space.hh98
-rw-r--r--source/blender/draw/engines/image/image_space_image.hh38
-rw-r--r--source/blender/draw/engines/image/image_space_node.hh55
-rw-r--r--source/blender/draw/engines/image/image_texture_info.hh78
-rw-r--r--source/blender/draw/engines/image/image_wrappers.hh49
-rw-r--r--source/blender/draw/engines/image/shaders/engine_image_frag.glsl94
-rw-r--r--source/blender/draw/engines/image/shaders/engine_image_vert.glsl34
-rw-r--r--source/blender/draw/engines/image/shaders/image_engine_color_frag.glsl38
-rw-r--r--source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl11
-rw-r--r--source/blender/draw/engines/image/shaders/image_engine_depth_frag.glsl16
-rw-r--r--source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl11
-rw-r--r--source/blender/draw/engines/image/shaders/infos/engine_image_info.hh30
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.c198
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_mesh.c6
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_uv.c16
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_fade.c1
-rw-r--r--source/blender/draw/engines/overlay/overlay_shader.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_volume.c1
-rw-r--r--source/blender/draw/engines/overlay/overlay_wireframe.c9
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl2
-rw-r--r--source/blender/draw/engines/select/select_draw_utils.c2
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_composite_info.hh41
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh64
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh (renamed from source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_info.hh)15
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh55
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh11
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh9
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh149
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_shadow_info.hh98
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh10
-rw-r--r--source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh114
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl12
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl11
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl48
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl12
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl31
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl8
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl39
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl4
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl5
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl9
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl12
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl10
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl11
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl20
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_info.hh136
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl15
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl22
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl17
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl49
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl36
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl14
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl15
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl7
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl32
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl14
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl8
-rw-r--r--source/blender/draw/engines/workbench/workbench_effect_cavity.c4
-rw-r--r--source/blender/draw/engines/workbench/workbench_effect_dof.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_effect_outline.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c41
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c5
-rw-r--r--source/blender/draw/engines/workbench/workbench_opaque.c14
-rw-r--r--source/blender/draw/engines/workbench/workbench_private.h1
-rw-r--r--source/blender/draw/engines/workbench/workbench_shader.c562
-rw-r--r--source/blender/draw/engines/workbench/workbench_shader.cc398
-rw-r--r--source/blender/draw/engines/workbench/workbench_shader_shared.h3
-rw-r--r--source/blender/draw/engines/workbench/workbench_transparent.c14
-rw-r--r--source/blender/draw/engines/workbench/workbench_volume.c8
-rw-r--r--source/blender/draw/intern/DRW_gpu_wrapper.hh84
-rw-r--r--source/blender/draw/intern/DRW_render.h67
-rw-r--r--source/blender/draw/intern/draw_cache.c42
-rw-r--r--source/blender/draw/intern/draw_cache.h16
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h12
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc7
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_render_data.c14
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h46
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curves.cc (renamed from source/blender/draw/intern/draw_cache_impl_hair.cc)190
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c165
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_subdivision.cc15
-rw-r--r--source/blender/draw/intern/draw_common.c2
-rw-r--r--source/blender/draw/intern/draw_common.h2
-rw-r--r--source/blender/draw/intern/draw_hair.c4
-rw-r--r--source/blender/draw/intern/draw_instance_data.c2
-rw-r--r--source/blender/draw/intern/draw_manager.c24
-rw-r--r--source/blender/draw/intern/draw_manager.h30
-rw-r--r--source/blender/draw/intern/draw_manager_data.c110
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c18
-rw-r--r--source/blender/draw/intern/draw_manager_shader.c20
-rw-r--r--source/blender/draw/intern/draw_shader.c7
-rw-r--r--source/blender/draw/intern/draw_shader_shared.h19
-rw-r--r--source/blender/draw/intern/draw_subdivision.h2
-rw-r--r--source/blender/draw/intern/draw_view_data.h6
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh.h3
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc19
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc13
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc2
-rw-r--r--source/blender/draw/intern/shaders/common_fxaa_lib.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl9
-rw-r--r--source/blender/draw/intern/shaders/common_hair_refine_comp.glsl5
-rw-r--r--source/blender/draw/intern/shaders/common_pointcloud_lib.glsl13
-rw-r--r--source/blender/draw/intern/shaders/common_smaa_lib.glsl6
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_lib.glsl4
-rw-r--r--source/blender/draw/intern/shaders/common_view_clipping_lib.glsl33
-rw-r--r--source/blender/draw/intern/shaders/common_view_lib.glsl130
-rw-r--r--source/blender/draw/intern/shaders/draw_hair_refine_info.hh42
-rw-r--r--source/blender/draw/intern/shaders/draw_object_infos_info.hh1
-rw-r--r--source/blender/draw/intern/shaders/draw_view_info.hh89
-rw-r--r--source/blender/draw/tests/shaders_test.cc8
-rw-r--r--source/blender/editors/animation/CMakeLists.txt4
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c40
-rw-r--r--source/blender/editors/animation/anim_filter.c14
-rw-r--r--source/blender/editors/animation/anim_ops.c2
-rw-r--r--source/blender/editors/animation/keyframes_draw.c2
-rw-r--r--source/blender/editors/animation/keyframes_edit.c55
-rw-r--r--source/blender/editors/armature/CMakeLists.txt4
-rw-r--r--source/blender/editors/armature/armature_add.c25
-rw-r--r--source/blender/editors/armature/armature_edit.c2
-rw-r--r--source/blender/editors/armature/armature_intern.h7
-rw-r--r--source/blender/editors/armature/armature_naming.c2
-rw-r--r--source/blender/editors/armature/armature_relations.c2
-rw-r--r--source/blender/editors/armature/armature_select.c83
-rw-r--r--source/blender/editors/armature/armature_skinning.c4
-rw-r--r--source/blender/editors/armature/meshlaplacian.c7
-rw-r--r--source/blender/editors/armature/meshlaplacian.h3
-rw-r--r--source/blender/editors/armature/pose_edit.c36
-rw-r--r--source/blender/editors/armature/pose_group.c6
-rw-r--r--source/blender/editors/armature/pose_select.c4
-rw-r--r--source/blender/editors/asset/CMakeLists.txt1
-rw-r--r--source/blender/editors/asset/ED_asset_catalog.h2
-rw-r--r--source/blender/editors/asset/ED_asset_mark_clear.h2
-rw-r--r--source/blender/editors/asset/ED_asset_type.h5
-rw-r--r--source/blender/editors/asset/intern/asset_list.cc4
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc10
-rw-r--r--source/blender/editors/asset/intern/asset_type.cc2
-rw-r--r--source/blender/editors/curve/CMakeLists.txt4
-rw-r--r--source/blender/editors/curve/editcurve.c11
-rw-r--r--source/blender/editors/curve/editcurve_add.c6
-rw-r--r--source/blender/editors/geometry/CMakeLists.txt2
-rw-r--r--source/blender/editors/geometry/geometry_attributes.cc207
-rw-r--r--source/blender/editors/geometry/geometry_intern.hh5
-rw-r--r--source/blender/editors/geometry/geometry_ops.cc3
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c29
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt4
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c27
-rw-r--r--source/blender/editors/gpencil/gpencil_edit_curve.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_ops_versioning.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_weight_paint.c2
-rw-r--r--source/blender/editors/include/ED_anim_api.h2
-rw-r--r--source/blender/editors/include/ED_armature.h14
-rw-r--r--source/blender/editors/include/ED_image.h2
-rw-r--r--source/blender/editors/include/ED_keyframes_edit.h19
-rw-r--r--source/blender/editors/include/ED_keyframing.h1
-rw-r--r--source/blender/editors/include/ED_mesh.h2
-rw-r--r--source/blender/editors/include/ED_node.h13
-rw-r--r--source/blender/editors/include/ED_transverts.h15
-rw-r--r--source/blender/editors/include/ED_util.h8
-rw-r--r--source/blender/editors/include/ED_view3d.h22
-rw-r--r--source/blender/editors/include/UI_icons.h8
-rw-r--r--source/blender/editors/include/UI_interface.h2
-rw-r--r--source/blender/editors/include/UI_interface.hh2
-rw-r--r--source/blender/editors/include/UI_interface_icons.h13
-rw-r--r--source/blender/editors/include/UI_tree_view.hh2
-rw-r--r--source/blender/editors/include/UI_view2d.h4
-rw-r--r--source/blender/editors/interface/interface.c7
-rw-r--r--source/blender/editors/interface/interface_context_menu.c14
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c17
-rw-r--r--source/blender/editors/interface/interface_eyedropper_color.c40
-rw-r--r--source/blender/editors/interface/interface_eyedropper_colorband.c34
-rw-r--r--source/blender/editors/interface/interface_eyedropper_datablock.c30
-rw-r--r--source/blender/editors/interface/interface_eyedropper_depth.c30
-rw-r--r--source/blender/editors/interface/interface_eyedropper_gpencil_color.c8
-rw-r--r--source/blender/editors/interface/interface_eyedropper_intern.h4
-rw-r--r--source/blender/editors/interface/interface_handlers.c9
-rw-r--r--source/blender/editors/interface/interface_icons.c22
-rw-r--r--source/blender/editors/interface/interface_ops.c11
-rw-r--r--source/blender/editors/interface/interface_style.c18
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.cc3
-rw-r--r--source/blender/editors/interface/interface_templates.c94
-rw-r--r--source/blender/editors/io/CMakeLists.txt4
-rw-r--r--source/blender/editors/io/io_usd.c55
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt4
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c2
-rw-r--r--source/blender/editors/mesh/editmesh_select.c35
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c45
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c12
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c5
-rw-r--r--source/blender/editors/metaball/CMakeLists.txt1
-rw-r--r--source/blender/editors/metaball/mball_edit.c14
-rw-r--r--source/blender/editors/object/CMakeLists.txt6
-rw-r--r--source/blender/editors/object/object_add.c60
-rw-r--r--source/blender/editors/object/object_bake.c2
-rw-r--r--source/blender/editors/object/object_bake_api.c24
-rw-r--r--source/blender/editors/object/object_constraint.c15
-rw-r--r--source/blender/editors/object/object_data_transfer.c17
-rw-r--r--source/blender/editors/object/object_edit.c10
-rw-r--r--source/blender/editors/object/object_intern.h3
-rw-r--r--source/blender/editors/object/object_modifier.c16
-rw-r--r--source/blender/editors/object/object_ops.c3
-rw-r--r--source/blender/editors/object/object_relations.c63
-rw-r--r--source/blender/editors/object/object_shapekey.c3
-rw-r--r--source/blender/editors/object/object_transform.c6
-rw-r--r--source/blender/editors/object/object_vgroup.c42
-rw-r--r--source/blender/editors/physics/CMakeLists.txt4
-rw-r--r--source/blender/editors/physics/particle_edit.c4
-rw-r--r--source/blender/editors/render/CMakeLists.txt5
-rw-r--r--source/blender/editors/render/render_internal.cc16
-rw-r--r--source/blender/editors/render/render_opengl.cc11
-rw-r--r--source/blender/editors/render/render_preview.cc12
-rw-r--r--source/blender/editors/render/render_shading.cc2
-rw-r--r--source/blender/editors/render/render_update.cc2
-rw-r--r--source/blender/editors/scene/CMakeLists.txt3
-rw-r--r--source/blender/editors/screen/CMakeLists.txt4
-rw-r--r--source/blender/editors/screen/screen_edit.c2
-rw-r--r--source/blender/editors/screen/screen_ops.c23
-rw-r--r--source/blender/editors/screen/screendump.c2
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt5
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_hide.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c7
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c72
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c131
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_brush_types.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h11
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_ops.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c45
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_uv.c2
-rw-r--r--source/blender/editors/space_action/action_edit.c2
-rw-r--r--source/blender/editors/space_action/space_action.c18
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt6
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c16
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c68
-rw-r--r--source/blender/editors/space_clip/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_clip/clip_ops.c5
-rw-r--r--source/blender/editors/space_clip/space_clip.c18
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c2
-rw-r--r--source/blender/editors/space_file/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_file/file_draw.c3
-rw-r--r--source/blender/editors/space_file/file_intern.h4
-rw-r--r--source/blender/editors/space_file/filelist.c17
-rw-r--r--source/blender/editors/space_file/fsmenu.c9
-rw-r--r--source/blender/editors/space_file/space_file.c3
-rw-r--r--source/blender/editors/space_graph/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_graph/graph_draw.c21
-rw-r--r--source/blender/editors/space_graph/graph_edit.c99
-rw-r--r--source/blender/editors/space_graph/graph_intern.h10
-rw-r--r--source/blender/editors/space_graph/graph_ops.c3
-rw-r--r--source/blender/editors/space_graph/graph_slider_ops.c2
-rw-r--r--source/blender/editors/space_graph/space_graph.c18
-rw-r--r--source/blender/editors/space_image/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_image/image_draw.c2
-rw-r--r--source/blender/editors/space_image/image_ops.c2
-rw-r--r--source/blender/editors/space_image/image_undo.c2
-rw-r--r--source/blender/editors/space_image/space_image.c25
-rw-r--r--source/blender/editors/space_info/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_info/info_stats.cc2
-rw-r--r--source/blender/editors/space_nla/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_nla/nla_draw.c2
-rw-r--r--source/blender/editors/space_nla/space_nla.c16
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_node/drawnode.cc43
-rw-r--r--source/blender/editors/space_node/node_add.cc4
-rw-r--r--source/blender/editors/space_node/node_context_path.cc4
-rw-r--r--source/blender/editors/space_node/node_draw.cc36
-rw-r--r--source/blender/editors/space_node/node_edit.cc26
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc8
-rw-r--r--source/blender/editors/space_node/node_gizmo.cc4
-rw-r--r--source/blender/editors/space_node/node_group.cc18
-rw-r--r--source/blender/editors/space_node/node_intern.hh100
-rw-r--r--source/blender/editors/space_node/node_ops.cc22
-rw-r--r--source/blender/editors/space_node/node_relationships.cc131
-rw-r--r--source/blender/editors/space_node/node_select.cc160
-rw-r--r--source/blender/editors/space_node/node_templates.cc13
-rw-r--r--source/blender/editors/space_node/node_view.cc8
-rw-r--r--source/blender/editors/space_node/space_node.cc55
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt8
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.cc52
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.cc61
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.hh39
-rw-r--r--source/blender/editors/space_outliner/outliner_select.cc36
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc116
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.cc237
-rw-r--r--source/blender/editors/space_outliner/space_outliner.cc65
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.hh2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.cc19
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_id.cc2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_rna.cc268
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_rna.hh86
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_seq.cc111
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_seq.hh60
-rw-r--r--source/blender/editors/space_sequencer/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c131
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_proxy.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_scopes.c2
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c16
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc27
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc5
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_intern.hh2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc22
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc2
-rw-r--r--source/blender/editors/space_text/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_text/space_text.c15
-rw-r--r--source/blender/editors/space_text/text_draw.c33
-rw-r--r--source/blender/editors/space_text/text_ops.c2
-rw-r--r--source/blender/editors/space_userpref/userpref_ops.c11
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt15
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c79
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c12
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c4189
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c128
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h146
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate.c1593
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate.h282
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_dolly.c337
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_fly.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_move.c188
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_ndof.c661
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_roll.c292
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_rotate.c452
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_smoothview.c406
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_walk.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_zoom.c598
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_zoom_border.c221
-rw-r--r--source/blender/editors/space_view3d/view3d_ops.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c168
-rw-r--r--source/blender/editors/space_view3d/view3d_snap.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c428
-rw-r--r--source/blender/editors/transform/CMakeLists.txt4
-rw-r--r--source/blender/editors/transform/transform.h4
-rw-r--r--source/blender/editors/transform/transform_convert.h6
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c78
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c2
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c33
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c2
-rw-r--r--source/blender/editors/transform/transform_snap_object.c28
-rw-r--r--source/blender/editors/undo/CMakeLists.txt4
-rw-r--r--source/blender/editors/util/CMakeLists.txt4
-rw-r--r--source/blender/editors/util/ed_transverts.c17
-rw-r--r--source/blender/editors/util/ed_util.c27
-rw-r--r--source/blender/editors/util/ed_util_ops.cc63
-rw-r--r--source/blender/editors/uvedit/CMakeLists.txt4
-rw-r--r--source/blender/freestyle/CMakeLists.txt4
-rw-r--r--source/blender/freestyle/intern/geometry/matrix_util.cpp9
-rw-r--r--source/blender/freestyle/intern/geometry/matrix_util.h9
-rw-r--r--source/blender/freestyle/intern/geometry/normal_cycle.cpp9
-rw-r--r--source/blender/freestyle/intern/geometry/normal_cycle.h9
-rw-r--r--source/blender/freestyle/intern/winged_edge/Curvature.cpp8
-rw-r--r--source/blender/freestyle/intern/winged_edge/Curvature.h10
-rw-r--r--source/blender/functions/FN_field.hh5
-rw-r--r--source/blender/functions/intern/cpp_types.cc2
-rw-r--r--source/blender/functions/intern/field.cc13
-rw-r--r--source/blender/functions/intern/multi_function_procedure_optimization.cc2
-rw-r--r--source/blender/geometry/CMakeLists.txt6
-rw-r--r--source/blender/geometry/GEO_mesh_merge_by_distance.hh55
-rw-r--r--source/blender/geometry/GEO_point_merge_by_distance.hh38
-rw-r--r--source/blender/geometry/intern/mesh_merge_by_distance.cc1729
-rw-r--r--source/blender/geometry/intern/point_merge_by_distance.cc183
-rw-r--r--source/blender/geometry/intern/realize_instances.cc2
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c3
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h4
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c58
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c85
-rw-r--r--source/blender/gpu/CMakeLists.txt91
-rw-r--r--source/blender/gpu/GPU_debug.h2
-rw-r--r--source/blender/gpu/GPU_select.h31
-rw-r--r--source/blender/gpu/GPU_shader.h8
-rw-r--r--source/blender/gpu/GPU_shader_shared.h9
-rw-r--r--source/blender/gpu/GPU_shader_shared_utils.h (renamed from source/blender/gpu/intern/gpu_shader_shared_utils.h)30
-rw-r--r--source/blender/gpu/GPU_state.h13
-rw-r--r--source/blender/gpu/GPU_texture.h1
-rw-r--r--source/blender/gpu/GPU_viewport.h4
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c6
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc2
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer_private.hh5
-rw-r--r--source/blender/gpu/intern/gpu_material.c2
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c9
-rw-r--r--source/blender/gpu/intern/gpu_select.c88
-rw-r--r--source/blender/gpu/intern/gpu_select_pick.c206
-rw-r--r--source/blender/gpu/intern/gpu_select_private.h7
-rw-r--r--source/blender/gpu/intern/gpu_select_sample_query.cc49
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc111
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder_stubs.cc4
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c22
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.cc211
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.hh316
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc288
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency_private.h31
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.hh18
-rw-r--r--source/blender/gpu/intern/gpu_shader_log.cc108
-rw-r--r--source/blender/gpu/intern/gpu_shader_private.hh2
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh18
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c5
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc13
-rw-r--r--source/blender/gpu/opengl/gl_compute.cc2
-rw-r--r--source/blender/gpu/opengl/gl_context.hh2
-rw-r--r--source/blender/gpu/opengl/gl_debug.cc10
-rw-r--r--source/blender/gpu/opengl/gl_debug_layer.cc4
-rw-r--r--source/blender/gpu/opengl/gl_framebuffer.cc7
-rw-r--r--source/blender/gpu/opengl/gl_framebuffer.hh2
-rw-r--r--source/blender/gpu/opengl/gl_shader.cc325
-rw-r--r--source/blender/gpu/opengl/gl_shader.hh9
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc65
-rw-r--r--source/blender/gpu/opengl/gl_shader_log.cc9
-rw-r--r--source/blender/gpu/opengl/gl_state.hh13
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl101
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl8
-rw-r--r--source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_text_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl1
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh10
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh8
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh10
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh6
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh10
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh6
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh10
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh3
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh12
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh6
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh4
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_text_info.hh2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh1
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl11
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl11
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl5
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl13
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl2
-rw-r--r--source/blender/ikplugin/BIK_api.h1
-rw-r--r--source/blender/ikplugin/intern/ikplugin_api.c1
-rw-r--r--source/blender/ikplugin/intern/ikplugin_api.h1
-rw-r--r--source/blender/ikplugin/intern/iksolver_plugin.c1
-rw-r--r--source/blender/ikplugin/intern/iksolver_plugin.h1
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.cpp1
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.h1
-rw-r--r--source/blender/imbuf/IMB_imbuf.h6
-rw-r--r--source/blender/imbuf/intern/anim_movie.c176
-rw-r--r--source/blender/imbuf/intern/cineon/dpxlib.c2
-rw-r--r--source/blender/imbuf/intern/cineon/dpxlib.h2
-rw-r--r--source/blender/imbuf/intern/dds/FlipDXT.cpp7
-rw-r--r--source/blender/imbuf/intern/divers.c3
-rw-r--r--source/blender/imbuf/intern/filter.c1
-rw-r--r--source/blender/imbuf/intern/indexer.c142
-rw-r--r--source/blender/imbuf/intern/radiance_hdr.c7
-rw-r--r--source/blender/imbuf/intern/readimage.c1
-rw-r--r--source/blender/imbuf/intern/rectop.c1
-rw-r--r--source/blender/imbuf/intern/rotate.c1
-rw-r--r--source/blender/imbuf/intern/scaling.c1
-rw-r--r--source/blender/imbuf/intern/util.c1
-rw-r--r--source/blender/imbuf/intern/util_gpu.c1
-rw-r--r--source/blender/imbuf/intern/writeimage.c1
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc65
-rw-r--r--source/blender/io/collada/CMakeLists.txt4
-rw-r--r--source/blender/io/usd/CMakeLists.txt2
-rw-r--r--source/blender/io/usd/intern/usd_reader_camera.cc2
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.cc39
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h2
-rw-r--r--source/blender/io/usd/intern/usd_writer_material.cc767
-rw-r--r--source/blender/io/usd/intern/usd_writer_material.h57
-rw-r--r--source/blender/io/usd/intern/usd_writer_mesh.cc4
-rw-r--r--source/blender/io/usd/usd.h4
-rw-r--r--source/blender/io/wavefront_obj/CMakeLists.txt6
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc416
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh96
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_io.hh163
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc74
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh41
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc4
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc6
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh4
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_exporter.cc136
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc91
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh2
-rw-r--r--source/blender/makesdna/DNA_ID.h27
-rw-r--r--source/blender/makesdna/DNA_ID_enums.h2
-rw-r--r--source/blender/makesdna/DNA_action_types.h5
-rw-r--r--source/blender/makesdna/DNA_armature_types.h2
-rw-r--r--source/blender/makesdna/DNA_brush_enums.h6
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h4
-rw-r--r--source/blender/makesdna/DNA_curves_defaults.h (renamed from source/blender/makesdna/DNA_hair_defaults.h)4
-rw-r--r--source/blender/makesdna/DNA_curves_types.h108
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h18
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h3
-rw-r--r--source/blender/makesdna/DNA_hair_types.h85
-rw-r--r--source/blender/makesdna/DNA_image_types.h20
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h3
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h4
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h7
-rw-r--r--source/blender/makesdna/DNA_node_types.h68
-rw-r--r--source/blender/makesdna/DNA_object_types.h35
-rw-r--r--source/blender/makesdna/DNA_outliner_types.h2
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h11
-rw-r--r--source/blender/makesdna/DNA_space_types.h7
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h7
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h4
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c14
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c86
-rw-r--r--source/blender/makesdna/intern/dna_rename_defs.h1
-rw-r--r--source/blender/makesdna/intern/makesdna.c16
-rw-r--r--source/blender/makesrna/RNA_access.h17
-rw-r--r--source/blender/makesrna/RNA_enum_types.h4
-rw-r--r--source/blender/makesrna/RNA_types.h4
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt7
-rw-r--r--source/blender/makesrna/intern/makesrna.c6
-rw-r--r--source/blender/makesrna/intern/rna_ID.c67
-rw-r--r--source/blender/makesrna/intern/rna_access.c8
-rw-r--r--source/blender/makesrna/intern/rna_action.c4
-rw-r--r--source/blender/makesrna/intern/rna_armature.c11
-rw-r--r--source/blender/makesrna/intern/rna_asset.c2
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c73
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c8
-rw-r--r--source/blender/makesrna/intern/rna_curves.c296
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c2
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c20
-rw-r--r--source/blender/makesrna/intern/rna_hair.c242
-rw-r--r--source/blender/makesrna/intern/rna_image.c2
-rw-r--r--source/blender/makesrna/intern/rna_image_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_internal.h16
-rw-r--r--source/blender/makesrna/intern/rna_layer.c21
-rw-r--r--source/blender/makesrna/intern/rna_main.c21
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c54
-rw-r--r--source/blender/makesrna/intern/rna_material.c8
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c5
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c167
-rw-r--r--source/blender/makesrna/intern/rna_object.c25
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c80
-rw-r--r--source/blender/makesrna/intern/rna_particle.c13
-rw-r--r--source/blender/makesrna/intern/rna_pose.c4
-rw-r--r--source/blender/makesrna/intern/rna_rna.c4
-rw-r--r--source/blender/makesrna/intern/rna_scene.c25
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c25
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c5
-rw-r--r--source/blender/makesrna/intern/rna_space.c12
-rw-r--r--source/blender/makesrna/intern/rna_texture_api.c7
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c15
-rw-r--r--source/blender/makesrna/intern/rna_wm.c5
-rw-r--r--source/blender/modifiers/CMakeLists.txt7
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c1
-rw-r--r--source/blender/modifiers/intern/MOD_array.c1
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c1
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.cc1
-rw-r--r--source/blender/modifiers/intern/MOD_build.c1
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c1
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c1
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c5
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c1
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c1
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c1
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c7
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c1
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c1
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c1
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c1
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_lattice.c1
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc1
-rw-r--r--source/blender/modifiers/intern/MOD_mesh_to_volume.cc1
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c1
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c3
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.cc5
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c4
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c1
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc363
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc7
-rw-r--r--source/blender/modifiers/intern/MOD_none.c1
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c3
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c1
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c1
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c1
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c1
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c30
-rw-r--r--source/blender/modifiers/intern/MOD_shapekey.c1
-rw-r--r--source/blender/modifiers/intern/MOD_shrinkwrap.c1
-rw-r--r--source/blender/modifiers/intern/MOD_simpledeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c1
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c1
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c8
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c1
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c5
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c17
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c1
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c4
-rw-r--r--source/blender/modifiers/intern/MOD_volume_displace.cc3
-rw-r--r--source/blender/modifiers/intern/MOD_volume_to_mesh.cc1
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c1
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvg_util.c18
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weld.cc1842
-rw-r--r--source/blender/modifiers/intern/MOD_wireframe.c1
-rw-r--r--source/blender/nodes/CMakeLists.txt4
-rw-r--r--source/blender/nodes/NOD_composite.h71
-rw-r--r--source/blender/nodes/NOD_geometry.h5
-rw-r--r--source/blender/nodes/NOD_shader.h22
-rw-r--r--source/blender/nodes/NOD_static_types.h8
-rw-r--r--source/blender/nodes/NOD_texture.h16
-rw-r--r--source/blender/nodes/composite/CMakeLists.txt9
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_blur.cc1
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehblur.cc1
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_common.cc1
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_denoise.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc71
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.cc1
-rw-r--r--source/blender/nodes/function/CMakeLists.txt4
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc51
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt9
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc391
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc1365
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc114
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc84
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_id.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc66
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc110
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc485
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc138
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc72
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc63
-rw-r--r--source/blender/nodes/intern/node_exec.cc59
-rw-r--r--source/blender/nodes/intern/node_exec.h20
-rw-r--r--source/blender/nodes/intern/node_socket_declarations.cc3
-rw-r--r--source/blender/nodes/shader/CMakeLists.txt5
-rw-r--r--source/blender/nodes/shader/node_shader_util.hh5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bump.cc11
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_common.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_hair_info.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc11
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_point_info.cc54
-rw-r--r--source/blender/nodes/texture/CMakeLists.txt3
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c57
-rw-r--r--source/blender/nodes/texture/node_texture_util.h12
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_common.c1
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_output.c8
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_proc.c2
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_texture.c2
-rw-r--r--source/blender/python/intern/bpy_interface.c2
-rw-r--r--source/blender/python/intern/bpy_interface_run.c2
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c2
-rw-r--r--source/blender/python/intern/bpy_rna_operator.c2
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c2
-rw-r--r--source/blender/render/CMakeLists.txt4
-rw-r--r--source/blender/render/RE_bake.h2
-rw-r--r--source/blender/render/RE_pipeline.h10
-rw-r--r--source/blender/render/RE_texture.h5
-rw-r--r--source/blender/render/RE_texture_margin.h6
-rw-r--r--source/blender/render/intern/bake.c6
-rw-r--r--source/blender/render/intern/initrender.c8
-rw-r--r--source/blender/render/intern/multires_bake.c34
-rw-r--r--source/blender/render/intern/pipeline.c2
-rw-r--r--source/blender/render/intern/texture_common.h38
-rw-r--r--source/blender/render/intern/texture_image.c283
-rw-r--r--source/blender/render/intern/texture_margin.cc164
-rw-r--r--source/blender/render/intern/texture_pointdensity.c30
-rw-r--r--source/blender/render/intern/texture_procedural.c108
-rw-r--r--source/blender/sequencer/SEQ_add.h9
-rw-r--r--source/blender/sequencer/SEQ_proxy.h3
-rw-r--r--source/blender/sequencer/SEQ_utils.h2
-rw-r--r--source/blender/sequencer/intern/clipboard.c8
-rw-r--r--source/blender/sequencer/intern/effects.c8
-rw-r--r--source/blender/sequencer/intern/iterator.c8
-rw-r--r--source/blender/sequencer/intern/multiview.c8
-rw-r--r--source/blender/sequencer/intern/proxy.c19
-rw-r--r--source/blender/sequencer/intern/proxy_job.c8
-rw-r--r--source/blender/sequencer/intern/render.c19
-rw-r--r--source/blender/sequencer/intern/sequencer.c18
-rw-r--r--source/blender/sequencer/intern/sequencer.h3
-rw-r--r--source/blender/sequencer/intern/sound.c17
-rw-r--r--source/blender/sequencer/intern/strip_add.c56
-rw-r--r--source/blender/sequencer/intern/strip_edit.c8
-rw-r--r--source/blender/sequencer/intern/strip_relations.c8
-rw-r--r--source/blender/sequencer/intern/strip_select.c8
-rw-r--r--source/blender/sequencer/intern/strip_time.c8
-rw-r--r--source/blender/sequencer/intern/strip_transform.c8
-rw-r--r--source/blender/sequencer/intern/utils.c8
-rw-r--r--source/blender/sequencer/intern/utils.h2
-rw-r--r--source/blender/shader_fx/CMakeLists.txt4
-rw-r--r--source/blender/windowmanager/CMakeLists.txt4
-rw-r--r--source/blender/windowmanager/WM_api.h7
-rw-r--r--source/blender/windowmanager/WM_types.h4
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c4
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c195
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c49
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c152
-rw-r--r--source/blender/windowmanager/intern/wm_files.c30
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c8
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c2
-rw-r--r--source/blender/windowmanager/intern/wm_window.c11
-rw-r--r--source/blender/windowmanager/wm_event_system.h1
-rw-r--r--source/blender/windowmanager/wm_event_types.h173
-rw-r--r--source/creator/CMakeLists.txt19
-rw-r--r--source/creator/blender_launcher_win32.c21
-rw-r--r--source/creator/creator_args.c21
m---------source/tools0
-rw-r--r--tests/python/CMakeLists.txt15
-rw-r--r--tests/python/bl_keymap_validate.py2
-rw-r--r--tests/python/bl_rigging_symmetrize.py244
-rw-r--r--tests/python/collada/animation/test_animation_simple.py10
-rw-r--r--tests/python/collada/mesh/test_mesh_simple.py10
-rw-r--r--tests/python/cycles_render_tests.py15
-rwxr-xr-xtests/python/modules/global_report.py2
-rwxr-xr-xtests/python/modules/render_report.py8
1212 files changed, 31236 insertions, 18604 deletions
diff --git a/.editorconfig b/.editorconfig
index 691643adebe..41ec3a1dc3d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -42,3 +42,12 @@ insert_final_newline = true
indent_style = space
indent_size = 3
max_line_length = 120
+
+# Makefile
+[{Makefile,GNUmakefile}]
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = tab
+indent_size = 4
+max_line_length = 120 \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 25cdd122a7f..21a398e31a4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -157,8 +157,9 @@ option(WITH_BLENDER "Build blender (disable to build only the blender player)" O
mark_as_advanced(WITH_BLENDER)
if(APPLE)
- # Currently this causes a build error linking, disable.
- set(WITH_BLENDER_THUMBNAILER OFF)
+ # In future, can be used with `quicklookthumbnailing/qlthumbnailreply` to create file
+ # thumbnails for say Finder. Turn it off for now.
+ option(WITH_BLENDER_THUMBNAILER "Build \"blender-thumbnailer\" thumbnail extraction utility" OFF)
elseif(WIN32)
option(WITH_BLENDER_THUMBNAILER "Build \"BlendThumb.dll\" helper for Windows explorer integration" ON)
else()
@@ -273,11 +274,13 @@ endif()
if(UNIX AND NOT APPLE)
option(WITH_SYSTEM_GLEW "Use GLEW OpenGL wrapper library provided by the operating system" OFF)
- option(WITH_SYSTEM_GLES "Use OpenGL ES library provided by the operating system" ON)
+ option(WITH_SYSTEM_GLEW "Use GLEW OpenGL wrapper library provided by the operating system" OFF)
+ option(WITH_SYSTEM_FREETYPE "Use the freetype library provided by the operating system" OFF)
else()
# not an option for other OS's
set(WITH_SYSTEM_GLEW OFF)
set(WITH_SYSTEM_GLES OFF)
+ set(WITH_SYSTEM_FREETYPE OFF)
endif()
@@ -683,7 +686,7 @@ if(WIN32 OR XCODE)
option(IDE_GROUP_PROJECTS_IN_FOLDERS "Organize the projects according to source folder structure." ON)
mark_as_advanced(IDE_GROUP_PROJECTS_IN_FOLDERS)
- if (IDE_GROUP_PROJECTS_IN_FOLDERS)
+ if(IDE_GROUP_PROJECTS_IN_FOLDERS)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
endif()
diff --git a/GNUmakefile b/GNUmakefile
index e4703973384..feca1236e72 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -1,6 +1,3 @@
-# -*- mode: gnumakefile; tab-width: 4; indent-tabs-mode: t; -*-
-# vim: tabstop=4
-#
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt
index af1653de59a..0d305e0dd1f 100644
--- a/build_files/build_environment/CMakeLists.txt
+++ b/build_files/build_environment/CMakeLists.txt
@@ -63,6 +63,7 @@ include(cmake/jpeg.cmake)
include(cmake/blosc.cmake)
include(cmake/pthreads.cmake)
include(cmake/openexr.cmake)
+include(cmake/brotli.cmake)
include(cmake/freetype.cmake)
include(cmake/freeglut.cmake)
include(cmake/glew.cmake)
diff --git a/build_files/build_environment/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake
index 5170a3a123e..f2944a41af8 100644
--- a/build_files/build_environment/cmake/boost.cmake
+++ b/build_files/build_environment/cmake/boost.cmake
@@ -25,8 +25,13 @@ else()
endif()
if(WIN32)
- set(BOOST_TOOLSET toolset=msvc-14.1)
- set(BOOST_COMPILER_STRING -vc141)
+ if(MSVC_VERSION GREATER_EQUAL 1920) # 2019
+ set(BOOST_TOOLSET toolset=msvc-14.2)
+ set(BOOST_COMPILER_STRING -vc142)
+ else() # 2017
+ set(BOOST_TOOLSET toolset=msvc-14.1)
+ set(BOOST_COMPILER_STRING -vc141)
+ endif()
set(BOOST_CONFIGURE_COMMAND bootstrap.bat)
set(BOOST_BUILD_COMMAND b2)
diff --git a/build_files/build_environment/cmake/brotli.cmake b/build_files/build_environment/cmake/brotli.cmake
new file mode 100644
index 00000000000..3264fa2516f
--- /dev/null
+++ b/build_files/build_environment/cmake/brotli.cmake
@@ -0,0 +1,38 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(BROTLI_EXTRA_ARGS
+)
+
+ExternalProject_Add(external_brotli
+ URL file://${PACKAGE_DIR}/${BROTLI_FILE}
+ DOWNLOAD_DIR ${DOWNLOAD_DIR}
+ URL_HASH ${BROTLI_HASH_TYPE}=${BROTLI_HASH}
+ PREFIX ${BUILD_DIR}/brotli
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/brotli ${DEFAULT_CMAKE_FLAGS} ${BROTLI_EXTRA_ARGS}
+ INSTALL_DIR ${LIBDIR}/brotli
+)
+
+if(BUILD_MODE STREQUAL Release AND WIN32)
+ ExternalProject_Add_Step(external_brotli after_install
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/brotli/include ${HARVEST_TARGET}/brotli/include
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/brotli/lib/brotlidec-static${LIBEXT} ${HARVEST_TARGET}/brotli/lib/brotlidec-static${LIBEXT}
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/brotli/lib/brotlicommon-static${LIBEXT} ${HARVEST_TARGET}/brotli/lib/brotlicommon-static${LIBEXT}
+ DEPENDEES install
+ )
+endif()
diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake
index 2a84202ea02..d3a4d767c08 100644
--- a/build_files/build_environment/cmake/download.cmake
+++ b/build_files/build_environment/cmake/download.cmake
@@ -94,3 +94,4 @@ download_source(POTRACE)
download_source(HARU)
download_source(ZSTD)
download_source(FLEX)
+download_source(BROTLI)
diff --git a/build_files/build_environment/cmake/freetype.cmake b/build_files/build_environment/cmake/freetype.cmake
index 49a83cb3377..c6663c287b1 100644
--- a/build_files/build_environment/cmake/freetype.cmake
+++ b/build_files/build_environment/cmake/freetype.cmake
@@ -19,13 +19,13 @@
set(FREETYPE_EXTRA_ARGS
-DCMAKE_RELEASE_POSTFIX:STRING=2ST
-DCMAKE_DEBUG_POSTFIX:STRING=2ST_d
- -DWITH_BZip2=OFF
- -DWITH_HarfBuzz=OFF
- -DFT_WITH_HARFBUZZ=OFF
- -DFT_WITH_BZIP2=OFF
- -DCMAKE_DISABLE_FIND_PACKAGE_HarfBuzz=TRUE
- -DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE
- -DCMAKE_DISABLE_FIND_PACKAGE_BrotliDec=TRUE)
+ -DFT_DISABLE_BZIP2=ON
+ -DFT_DISABLE_HARFBUZZ=ON
+ -DFT_DISABLE_PNG=ON
+ -DFT_REQUIRE_BROTLI=ON
+ -DPC_BROTLIDEC_INCLUDEDIR=${LIBDIR}/brotli/include
+ -DPC_BROTLIDEC_LIBDIR=${LIBDIR}/brotli/lib
+ )
ExternalProject_Add(external_freetype
URL file://${PACKAGE_DIR}/${FREETYPE_FILE}
@@ -36,6 +36,11 @@ ExternalProject_Add(external_freetype
INSTALL_DIR ${LIBDIR}/freetype
)
+add_dependencies(
+ external_freetype
+ external_brotli
+)
+
if(BUILD_MODE STREQUAL Release AND WIN32)
ExternalProject_Add_Step(external_freetype after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/freetype ${HARVEST_TARGET}/freetype
diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake
index 4d06430ea49..0e68e720b44 100644
--- a/build_files/build_environment/cmake/harvest.cmake
+++ b/build_files/build_environment/cmake/harvest.cmake
@@ -79,6 +79,8 @@ endfunction()
harvest(alembic/include alembic/include "*.h")
harvest(alembic/lib/libAlembic.a alembic/lib/libAlembic.a)
harvest(alembic/bin alembic/bin "*")
+harvest(brotli/include brotli/include "*.h")
+harvest(brotli/lib brotli/lib "*.a")
harvest(boost/include boost/include "*")
harvest(boost/lib boost/lib "*.a")
harvest(ffmpeg/include ffmpeg/include "*.h")
diff --git a/build_files/build_environment/cmake/python_site_packages.cmake b/build_files/build_environment/cmake/python_site_packages.cmake
index a8918fdb784..e58c3d90b4d 100644
--- a/build_files/build_environment/cmake/python_site_packages.cmake
+++ b/build_files/build_environment/cmake/python_site_packages.cmake
@@ -31,7 +31,7 @@ ExternalProject_Add(external_python_site_packages
CONFIGURE_COMMAND ${PIP_CONFIGURE_COMMAND}
BUILD_COMMAND ""
PREFIX ${BUILD_DIR}/site_packages
- INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all:
+ INSTALL_COMMAND ${PYTHON_BINARY} -m pip install --no-cache-dir ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all:
)
if(USE_PIP_NUMPY)
diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake
index c758dbd265e..28a27023818 100644
--- a/build_files/build_environment/cmake/versions.cmake
+++ b/build_files/build_environment/cmake/versions.cmake
@@ -83,9 +83,9 @@ else()
set(OPENEXR_VERSION_POSTFIX)
endif()
-set(FREETYPE_VERSION 2.10.2)
+set(FREETYPE_VERSION 2.11.1)
set(FREETYPE_URI http://prdownloads.sourceforge.net/freetype/freetype-${FREETYPE_VERSION}.tar.gz)
-set(FREETYPE_HASH b1cb620e4c875cd4d1bfa04945400945)
+set(FREETYPE_HASH bd4e3b007474319909a6b79d50908e85)
set(FREETYPE_HASH_TYPE MD5)
set(FREETYPE_FILE freetype-${FREETYPE_VERSION}.tar.gz)
@@ -189,11 +189,11 @@ set(OSL_HASH 1abd7ce40481771a9fa937f19595d2f2)
set(OSL_HASH_TYPE MD5)
set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz)
-set(PYTHON_VERSION 3.9.7)
-set(PYTHON_SHORT_VERSION 3.9)
-set(PYTHON_SHORT_VERSION_NO_DOTS 39)
+set(PYTHON_VERSION 3.10.2)
+set(PYTHON_SHORT_VERSION 3.10)
+set(PYTHON_SHORT_VERSION_NO_DOTS 310)
set(PYTHON_URI https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz)
-set(PYTHON_HASH fddb060b483bc01850a3f412eea1d954)
+set(PYTHON_HASH 14e8c22458ed7779a1957b26cde01db9)
set(PYTHON_HASH_TYPE MD5)
set(PYTHON_FILE Python-${PYTHON_VERSION}.tar.xz)
@@ -215,18 +215,20 @@ set(NANOVDB_HASH e7b9e863ec2f3b04ead171dec2322807)
set(NANOVDB_HASH_TYPE MD5)
set(NANOVDB_FILE nano-vdb-${NANOVDB_GIT_UID}.tar.gz)
-set(IDNA_VERSION 3.2)
-set(CHARSET_NORMALIZER_VERSION 2.0.6)
-set(URLLIB3_VERSION 1.26.7)
+set(IDNA_VERSION 3.3)
+set(CHARSET_NORMALIZER_VERSION 2.0.10)
+set(URLLIB3_VERSION 1.26.8)
set(CERTIFI_VERSION 2021.10.8)
-set(REQUESTS_VERSION 2.26.0)
-set(CYTHON_VERSION 0.29.24)
-set(ZSTANDARD_VERSION 0.15.2 )
-
-set(NUMPY_VERSION 1.21.2)
-set(NUMPY_SHORT_VERSION 1.21)
+set(REQUESTS_VERSION 2.27.1)
+set(CYTHON_VERSION 0.29.26)
+# The version of the zstd library used to build the Python package should match ZSTD_VERSION defined below.
+# At this time of writing, 0.17.0 was already released, but built against zstd 1.5.1, while we use 1.5.0.
+set(ZSTANDARD_VERSION 0.16.0)
+
+set(NUMPY_VERSION 1.22.0)
+set(NUMPY_SHORT_VERSION 1.22)
set(NUMPY_URI https://github.com/numpy/numpy/releases/download/v${NUMPY_VERSION}/numpy-${NUMPY_VERSION}.zip)
-set(NUMPY_HASH 5638d5dae3ca387be562912312db842e)
+set(NUMPY_HASH 252de134862a27bd66705d29622edbfe)
set(NUMPY_HASH_TYPE MD5)
set(NUMPY_FILE numpy-${NUMPY_VERSION}.zip)
@@ -500,3 +502,9 @@ set(ZSTD_FILE zstd-${ZSTD_VERSION}.tar.gz)
set(SSE2NEON_GIT https://github.com/DLTcollab/sse2neon.git)
set(SSE2NEON_GIT_HASH fe5ff00bb8d19b327714a3c290f3e2ce81ba3525)
+
+set(BROTLI_VERSION v1.0.9)
+set(BROTLI_URI https://github.com/google/brotli/archive/refs/tags/${BROTLI_VERSION}.tar.gz)
+set(BROTLI_HASH f9e8d81d0405ba66d181529af42a3354f838c939095ff99930da6aa9cdf6fe46)
+set(BROTLI_HASH_TYPE SHA256)
+set(BROTLI_FILE brotli-${BROTLI_VERSION}.tar.gz)
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index ce7a251bfba..75c0b3c0009 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -379,27 +379,27 @@ USE_CXX11=true
CLANG_FORMAT_VERSION_MIN="6.0"
CLANG_FORMAT_VERSION_MEX="10.0"
-PYTHON_VERSION="3.9.7"
-PYTHON_VERSION_SHORT="3.9"
-PYTHON_VERSION_MIN="3.7"
-PYTHON_VERSION_MEX="3.11"
+PYTHON_VERSION="3.10.2"
+PYTHON_VERSION_SHORT="3.10"
+PYTHON_VERSION_MIN="3.9"
+PYTHON_VERSION_MEX="3.12"
PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT
PYTHON_FORCE_BUILD=false
PYTHON_FORCE_REBUILD=false
PYTHON_SKIP=false
# Additional Python modules.
-PYTHON_IDNA_VERSION="3.2"
+PYTHON_IDNA_VERSION="3.3"
PYTHON_IDNA_VERSION_MIN="2.0"
PYTHON_IDNA_VERSION_MEX="4.0"
PYTHON_IDNA_NAME="idna"
-PYTHON_CHARSET_NORMALIZER_VERSION="2.0.6"
+PYTHON_CHARSET_NORMALIZER_VERSION="2.0.10"
PYTHON_CHARSET_NORMALIZER_VERSION_MIN="2.0.6"
PYTHON_CHARSET_NORMALIZER_VERSION_MEX="2.1.0" # requests uses `charset_normalizer~=2.0.0`
PYTHON_CHARSET_NORMALIZER_NAME="charset-normalizer"
-PYTHON_URLLIB3_VERSION="1.26.7"
+PYTHON_URLLIB3_VERSION="1.26.8"
PYTHON_URLLIB3_VERSION_MIN="1.0"
PYTHON_URLLIB3_VERSION_MEX="2.0"
PYTHON_URLLIB3_NAME="urllib3"
@@ -409,17 +409,17 @@ PYTHON_CERTIFI_VERSION_MIN="2021.0"
PYTHON_CERTIFI_VERSION_MEX="2023.0"
PYTHON_CERTIFI_NAME="certifi"
-PYTHON_REQUESTS_VERSION="2.23.0"
+PYTHON_REQUESTS_VERSION="2.27.1"
PYTHON_REQUESTS_VERSION_MIN="2.0"
PYTHON_REQUESTS_VERSION_MEX="3.0"
PYTHON_REQUESTS_NAME="requests"
-PYTHON_ZSTANDARD_VERSION="0.15.2"
+PYTHON_ZSTANDARD_VERSION="0.16.0"
PYTHON_ZSTANDARD_VERSION_MIN="0.15.2"
-PYTHON_ZSTANDARD_VERSION_MEX="0.16.0"
+PYTHON_ZSTANDARD_VERSION_MEX="0.20.0"
PYTHON_ZSTANDARD_NAME="zstandard"
-PYTHON_NUMPY_VERSION="1.21.2"
+PYTHON_NUMPY_VERSION="1.22.0"
PYTHON_NUMPY_VERSION_MIN="1.14"
PYTHON_NUMPY_VERSION_MEX="2.0"
PYTHON_NUMPY_NAME="numpy"
@@ -499,7 +499,7 @@ LLVM_FORCE_REBUILD=false
LLVM_SKIP=false
# OSL needs to be compiled for now!
-OSL_VERSION="1.11.14.1"
+OSL_VERSION="1.11.17.0"
OSL_VERSION_SHORT="1.11"
OSL_VERSION_MIN="1.11"
OSL_VERSION_MEX="2.0"
@@ -4036,14 +4036,14 @@ install_DEB() {
INFO "Forced Python building, as requested..."
_do_compile_python=true
else
- check_package_version_ge_lt_DEB python3-dev $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX
+ check_package_version_ge_lt_DEB python${PYTHON_VERSION_SHORT}-dev $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX
if [ $? -eq 0 ]; then
- PYTHON_VERSION_INSTALLED=$(echo `get_package_version_DEB python3-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
-
- install_packages_DEB python3-dev
+ install_packages_DEB python${PYTHON_VERSION_SHORT}-dev
clean_Python
PRINT ""
+ PYTHON_VERSION_INSTALLED=$(echo `get_package_version_DEB python${PYTHON_VERSION_SHORT}-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
+
for module in "${PYTHON_MODULES_PACKAGES[@]}"
do
module=($module)
@@ -4681,11 +4681,11 @@ install_RPM() {
else
check_package_version_ge_lt_RPM python3-devel $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX
if [ $? -eq 0 ]; then
- PYTHON_VERSION_INSTALLED=$(echo `get_package_version_RPM python3-devel` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
-
install_packages_RPM python3-devel
clean_Python
+ PYTHON_VERSION_INSTALLED=$(echo `get_package_version_RPM python3-devel` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
+
for module in "${PYTHON_MODULES_PACKAGES[@]}"
do
module=($module)
@@ -5224,12 +5224,12 @@ install_ARCH() {
else
check_package_version_ge_lt_ARCH python $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX
if [ $? -eq 0 ]; then
- PYTHON_VERSION_INSTALLED=$(echo `get_package_version_ARCH python` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
-
install_packages_ARCH python
clean_Python
PRINT ""
+ PYTHON_VERSION_INSTALLED=$(echo `get_package_version_ARCH python` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
+
for module in "${PYTHON_MODULES_PACKAGES[@]}"
do
module=($module)
diff --git a/build_files/build_environment/windows/build_deps.cmd b/build_files/build_environment/windows/build_deps.cmd
index 5174af8e20d..c0e13ac3a55 100644
--- a/build_files/build_environment/windows/build_deps.cmd
+++ b/build_files/build_environment/windows/build_deps.cmd
@@ -1,19 +1,5 @@
@echo off
if NOT "%1" == "" (
- if "%1" == "2013" (
- echo "Building for VS2013"
- set VSVER=12.0
- set VSVER_SHORT=12
- set BuildDir=VS12
- goto par2
- )
- if "%1" == "2015" (
- echo "Building for VS2015"
- set VSVER=14.0
- set VSVER_SHORT=14
- set BuildDir=VS14
- goto par2
- )
if "%1" == "2017" (
echo "Building for VS2017"
set VSVER=15.0
@@ -21,44 +7,33 @@ if NOT "%1" == "" (
set BuildDir=VS15
goto par2
)
+ if "%1" == "2019" (
+ echo "Building for VS2019"
+ set VSVER=15.0
+ set VSVER_SHORT=15
+ set BuildDir=VS15
+ goto par2
+ )
)
:usage
-Echo Usage build_deps 2013/2015/2017 x64/x86
+Echo Usage build_deps 2017/2019 x64
goto exit
:par2
if NOT "%2" == "" (
- if "%2" == "x86" (
- echo "Building for x86"
- set HARVESTROOT=Windows_vc
- set ARCH=86
- if "%1" == "2013" (
- set CMAKE_BUILDER=Visual Studio 12 2013
- )
- if "%1" == "2015" (
- set CMAKE_BUILDER=Visual Studio 14 2015
- )
- if "%1" == "2017" (
- set CMAKE_BUILDER=Visual Studio 15 2017
- )
-
- goto start
- )
if "%2" == "x64" (
echo "Building for x64"
set HARVESTROOT=Win64_vc
set ARCH=64
- if "%1" == "2013" (
- set CMAKE_BUILDER=Visual Studio 12 2013 Win64
- )
- if "%1" == "2015" (
- set CMAKE_BUILDER=Visual Studio 14 2015 Win64
+ if "%1" == "2019" (
+ set CMAKE_BUILDER=Visual Studio 16 2019
+ set CMAKE_BUILD_ARCH=-A x64
)
if "%1" == "2017" (
set CMAKE_BUILDER=Visual Studio 15 2017 Win64
+ set CMAKE_BUILD_ARCH=
)
-
goto start
)
)
@@ -120,7 +95,7 @@ set path=%BUILD_DIR%\downloads\mingw\mingw64\msys\1.0\bin\;%BUILD_DIR%\downloads
mkdir %STAGING%\%BuildDir%%ARCH%R
cd %Staging%\%BuildDir%%ARCH%R
echo %DATE% %TIME% : Start > %StatusFile%
-cmake -G "%CMAKE_BUILDER%" -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DBUILD_MODE=Release -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/
+cmake -G "%CMAKE_BUILDER%" %CMAKE_BUILD_ARCH% -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DBUILD_MODE=Release -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/
echo %DATE% %TIME% : Release Configuration done >> %StatusFile%
if "%dobuild%" == "1" (
msbuild /m "ll.vcxproj" /p:Configuration=Release /fl /flp:logfile=BlenderDeps_llvm.log;Verbosity=normal
@@ -133,7 +108,7 @@ if "%NODEBUG%" == "1" goto exit
cd %BUILD_DIR%
mkdir %STAGING%\%BuildDir%%ARCH%D
cd %Staging%\%BuildDir%%ARCH%D
-cmake -G "%CMAKE_BUILDER%" -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DCMAKE_BUILD_TYPE=Debug -DBUILD_MODE=Debug -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/ %CMAKE_DEBUG_OPTIONS%
+cmake -G "%CMAKE_BUILDER%" %CMAKE_BUILD_ARCH% -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DCMAKE_BUILD_TYPE=Debug -DBUILD_MODE=Debug -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/ %CMAKE_DEBUG_OPTIONS%
echo %DATE% %TIME% : Debug Configuration done >> %StatusFile%
if "%dobuild%" == "1" (
msbuild /m "ll.vcxproj" /p:Configuration=Debug /fl /flp:logfile=BlenderDeps_llvm.log;;Verbosity=normal
diff --git a/build_files/cmake/Modules/FindBrotli.cmake b/build_files/cmake/Modules/FindBrotli.cmake
new file mode 100644
index 00000000000..d1e40b1966a
--- /dev/null
+++ b/build_files/cmake/Modules/FindBrotli.cmake
@@ -0,0 +1,83 @@
+# - Find Brotli library (compression for freetype/woff2).
+# This module defines
+# BROTLI_INCLUDE_DIRS, where to find Brotli headers, Set when
+# BROTLI_INCLUDE_DIR is found.
+# BROTLI_LIBRARIES, libraries to link against to use Brotli.
+# BROTLI_ROOT_DIR, The base directory to search for Brotli.
+# This can also be an environment variable.
+# BROTLI_FOUND, If false, do not try to use Brotli.
+#
+
+#=============================================================================
+# Copyright 2022 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD 3-Clause License,
+# see accompanying file BSD-3-Clause-license.txt for details.
+#=============================================================================
+
+# If BROTLI_ROOT_DIR was defined in the environment, use it.
+IF(NOT BROTLI_ROOT_DIR AND NOT $ENV{BROTLI_ROOT_DIR} STREQUAL "")
+ SET(BROTLI_ROOT_DIR $ENV{BROTLI_ROOT_DIR})
+ENDIF()
+
+SET(_BROTLI_SEARCH_DIRS
+ ${BROTLI_ROOT_DIR}
+)
+
+FIND_PATH(BROTLI_INCLUDE_DIR
+ NAMES
+ brotli/decode.h
+ HINTS
+ ${_BROTLI_SEARCH_DIRS}
+ PATH_SUFFIXES
+ include
+ DOC "Brotli header files"
+)
+
+FIND_LIBRARY(BROTLI_LIBRARY_COMMON
+ NAMES
+ # Some builds use a special `-static` postfix in their static libraries names.
+ brotlicommon-static
+ brotlicommon
+ HINTS
+ ${_BROTLI_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib lib/static
+ DOC "Brotli static common library"
+)
+FIND_LIBRARY(BROTLI_LIBRARY_DEC
+ NAMES
+ # Some builds use a special `-static` postfix in their static libraries names.
+ brotlidec-static
+ brotlidec
+ HINTS
+ ${_BROTLI_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib lib/static
+ DOC "Brotli static decode library"
+)
+
+
+IF(${BROTLI_LIBRARY_COMMON_NOTFOUND} or ${BROTLI_LIBRARY_DEC_NOTFOUND})
+ set(BROTLI_FOUND FALSE)
+ELSE()
+ # handle the QUIETLY and REQUIRED arguments and set BROTLI_FOUND to TRUE if
+ # all listed variables are TRUE
+ INCLUDE(FindPackageHandleStandardArgs)
+ FIND_PACKAGE_HANDLE_STANDARD_ARGS(Brotli DEFAULT_MSG BROTLI_LIBRARY_COMMON BROTLI_LIBRARY_DEC BROTLI_INCLUDE_DIR)
+
+ IF(BROTLI_FOUND)
+ get_filename_component(BROTLI_LIBRARY_DIR ${BROTLI_LIBRARY_COMMON} DIRECTORY)
+ SET(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR})
+ SET(BROTLI_LIBRARIES ${BROTLI_LIBRARY_DEC} ${BROTLI_LIBRARY_COMMON})
+ ENDIF()
+ENDIF()
+
+MARK_AS_ADVANCED(
+ BROTLI_INCLUDE_DIR
+ BROTLI_LIBRARY_COMMON
+ BROTLI_LIBRARY_DEC
+ BROTLI_LIBRARY_DIR
+)
+
+UNSET(_BROTLI_SEARCH_DIRS)
diff --git a/build_files/cmake/Modules/FindPythonLibsUnix.cmake b/build_files/cmake/Modules/FindPythonLibsUnix.cmake
index a6fb463432b..e0fe8dd00cb 100644
--- a/build_files/cmake/Modules/FindPythonLibsUnix.cmake
+++ b/build_files/cmake/Modules/FindPythonLibsUnix.cmake
@@ -34,7 +34,7 @@ IF(NOT PYTHON_ROOT_DIR AND NOT $ENV{PYTHON_ROOT_DIR} STREQUAL "")
SET(PYTHON_ROOT_DIR $ENV{PYTHON_ROOT_DIR})
ENDIF()
-SET(PYTHON_VERSION 3.9 CACHE STRING "Python Version (major and minor only)")
+SET(PYTHON_VERSION 3.10 CACHE STRING "Python Version (major and minor only)")
MARK_AS_ADVANCED(PYTHON_VERSION)
diff --git a/build_files/cmake/clang_array_check.py b/build_files/cmake/clang_array_check.py
index 63f86b5aab7..a3a0ea63347 100644
--- a/build_files/cmake/clang_array_check.py
+++ b/build_files/cmake/clang_array_check.py
@@ -1,11 +1,4 @@
-# ---
-# * Licensed under the Apache License, Version 2.0 (the "License");
-# * you may not use this file except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# * http://www.apache.org/licenses/LICENSE-2.0
-# ---
-# by Campbell Barton
+# Apache License, Version 2.0
"""
Invocation:
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index e0219cd1121..94d5fe1e31b 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -1197,21 +1197,21 @@ endfunction()
macro(openmp_delayload
projectname
)
- if(MSVC)
- if(WITH_OPENMP)
- if(MSVC_CLANG)
- set(OPENMP_DLL_NAME "libomp")
- elseif(MSVC_VERSION EQUAL 1800)
- set(OPENMP_DLL_NAME "vcomp120")
- else()
- set(OPENMP_DLL_NAME "vcomp140")
- endif()
- set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib")
- set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /DELAYLOAD:${OPENMP_DLL_NAME}d.dll delayimp.lib")
- set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib")
- set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib")
+ if(MSVC)
+ if(WITH_OPENMP)
+ if(MSVC_CLANG)
+ set(OPENMP_DLL_NAME "libomp")
+ elseif(MSVC_VERSION EQUAL 1800)
+ set(OPENMP_DLL_NAME "vcomp120")
+ else()
+ set(OPENMP_DLL_NAME "vcomp140")
endif()
+ set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib")
+ set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /DELAYLOAD:${OPENMP_DLL_NAME}d.dll delayimp.lib")
+ set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib")
+ set_property(TARGET ${projectname} APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /DELAYLOAD:${OPENMP_DLL_NAME}.dll delayimp.lib")
endif()
+ endif()
endmacro()
macro(set_and_warn_dependency
diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake
index 447645b6806..379370a989a 100644
--- a/build_files/cmake/platform/platform_apple.cmake
+++ b/build_files/cmake/platform/platform_apple.cmake
@@ -128,25 +128,20 @@ if(WITH_CODEC_SNDFILE)
endif()
if(WITH_PYTHON)
- # we use precompiled libraries for py 3.9 and up by default
- set(PYTHON_VERSION 3.9)
+ # Use precompiled libraries by default.
+ set(PYTHON_VERSION 3.10)
if(NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK)
- # normally cached but not since we include them with blender
+ # Normally cached but not since we include them with blender.
set(PYTHON_INCLUDE_DIR "${LIBDIR}/python/include/python${PYTHON_VERSION}")
set(PYTHON_EXECUTABLE "${LIBDIR}/python/bin/python${PYTHON_VERSION}")
set(PYTHON_LIBRARY ${LIBDIR}/python/lib/libpython${PYTHON_VERSION}.a)
set(PYTHON_LIBPATH "${LIBDIR}/python/lib/python${PYTHON_VERSION}")
- # set(PYTHON_LINKFLAGS "-u _PyMac_Error") # won't build with this enabled
else()
- # module must be compiled against Python framework
+ # Module must be compiled against Python framework.
set(_py_framework "/Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}")
-
set(PYTHON_INCLUDE_DIR "${_py_framework}/include/python${PYTHON_VERSION}")
set(PYTHON_EXECUTABLE "${_py_framework}/bin/python${PYTHON_VERSION}")
set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}")
- # set(PYTHON_LIBRARY python${PYTHON_VERSION})
- # set(PYTHON_LINKFLAGS "-u _PyMac_Error -framework Python") # won't build with this enabled
-
unset(_py_framework)
endif()
@@ -166,7 +161,11 @@ if(WITH_FFTW3)
find_package(Fftw3)
endif()
+# FreeType compiled with Brotli compression for woff2.
find_package(Freetype REQUIRED)
+list(APPEND FREETYPE_LIBRARIES
+ ${LIBDIR}/brotli/lib/libbrotlicommon-static.a
+ ${LIBDIR}/brotli/lib/libbrotlidec-static.a)
if(WITH_IMAGE_OPENEXR)
find_package(OpenEXR)
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index b6da737af71..6a896709cc2 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -48,6 +48,9 @@ if(NOT DEFINED LIBDIR)
unset(LIBDIR_CENTOS7_ABI)
endif()
+# Support restoring this value once pre-compiled libraries have been handled.
+set(WITH_STATIC_LIBS_INIT ${WITH_STATIC_LIBS})
+
if(EXISTS ${LIBDIR})
message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}")
@@ -100,7 +103,22 @@ find_package_wrapper(JPEG REQUIRED)
find_package_wrapper(PNG REQUIRED)
find_package_wrapper(ZLIB REQUIRED)
find_package_wrapper(Zstd REQUIRED)
-find_package_wrapper(Freetype REQUIRED)
+
+if(NOT WITH_SYSTEM_FREETYPE)
+ # FreeType compiled with Brotli compression for woff2.
+ find_package_wrapper(Freetype REQUIRED)
+ if(EXISTS ${LIBDIR})
+ find_package_wrapper(Brotli REQUIRED)
+
+ # NOTE: This is done on WIN32 & APPLE but fails on some Linux systems.
+ # See: https://devtalk.blender.org/t/22536
+ # So `BROTLI_LIBRARIES` need to be added directly after `FREETYPE_LIBRARIES`.
+ #
+ # list(APPEND FREETYPE_LIBRARIES
+ # ${BROTLI_LIBRARIES}
+ # )
+ endif()
+endif()
if(WITH_PYTHON)
# No way to set py35, remove for now.
@@ -536,6 +554,21 @@ add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
#
# Keep last, so indirectly linked libraries don't override our own pre-compiled libs.
+if(EXISTS ${LIBDIR})
+ # Clear the prefix path as it causes the `LIBDIR` to override system locations.
+ unset(CMAKE_PREFIX_PATH)
+
+ # Since the pre-compiled `LIBDIR` directories have been handled, don't prefer static libraries.
+ set(WITH_STATIC_LIBS ${WITH_STATIC_LIBS_INIT})
+endif()
+
+if(WITH_SYSTEM_FREETYPE)
+ find_package_wrapper(Freetype)
+ if(NOT FREETYPE_FOUND)
+ message(FATAL_ERROR "Failed finding system FreeType version!")
+ endif()
+endif()
+
if(WITH_LZO AND WITH_SYSTEM_LZO)
find_package_wrapper(LZO)
if(NOT LZO_FOUND)
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake
index 851dafc34fb..220b67bc1e3 100644
--- a/build_files/cmake/platform/platform_win32.cmake
+++ b/build_files/cmake/platform/platform_win32.cmake
@@ -55,6 +55,10 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang")
message(WARNING "stripped pdb not supported with clang, disabling..")
set(WITH_WINDOWS_STRIPPED_PDB OFF)
endif()
+else()
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.28.29921) # MSVC 2019 16.9.16
+ message(FATAL_ERROR "Compiler is unsupported, MSVC 2019 16.9.16 or newer is required for building blender.")
+ endif()
endif()
if(NOT WITH_PYTHON_MODULE)
@@ -265,12 +269,6 @@ if(NOT DEFINED LIBDIR)
elseif(MSVC_VERSION GREATER 1919)
message(STATUS "Visual Studio 2019 detected.")
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
- elseif(MSVC_VERSION GREATER 1909)
- message(STATUS "Visual Studio 2017 detected.")
- set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
- elseif(MSVC_VERSION EQUAL 1900)
- message(STATUS "Visual Studio 2015 detected.")
- set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
endif()
else()
message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}")
@@ -347,7 +345,11 @@ set(FREETYPE_INCLUDE_DIRS
${LIBDIR}/freetype/include
${LIBDIR}/freetype/include/freetype2
)
-set(FREETYPE_LIBRARY ${LIBDIR}/freetype/lib/freetype2ST.lib)
+set(FREETYPE_LIBRARIES
+ ${LIBDIR}/freetype/lib/freetype2ST.lib
+ ${LIBDIR}/brotli/lib/brotlidec-static.lib
+ ${LIBDIR}/brotli/lib/brotlicommon-static.lib
+)
windows_find_package(freetype REQUIRED)
if(WITH_FFTW3)
@@ -461,7 +463,7 @@ if(WITH_JACK)
endif()
if(WITH_PYTHON)
- set(PYTHON_VERSION 3.9) # CACHE STRING)
+ set(PYTHON_VERSION 3.10) # CACHE STRING)
string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION})
set(PYTHON_LIBRARY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.lib)
diff --git a/build_files/windows/autodetect_msvc.cmd b/build_files/windows/autodetect_msvc.cmd
index a4ab8929040..fd0c19764e9 100644
--- a/build_files/windows/autodetect_msvc.cmd
+++ b/build_files/windows/autodetect_msvc.cmd
@@ -3,9 +3,6 @@ echo No explicit msvc version requested, autodetecting version.
call "%~dp0\detect_msvc2019.cmd"
if %ERRORLEVEL% EQU 0 goto DetectionComplete
-call "%~dp0\detect_msvc2017.cmd"
-if %ERRORLEVEL% EQU 0 goto DetectionComplete
-
call "%~dp0\detect_msvc2022.cmd"
if %ERRORLEVEL% EQU 0 goto DetectionComplete
diff --git a/build_files/windows/check_libraries.cmd b/build_files/windows/check_libraries.cmd
index c495ee6eee9..e8c04fb3258 100644
--- a/build_files/windows/check_libraries.cmd
+++ b/build_files/windows/check_libraries.cmd
@@ -1,4 +1,3 @@
-if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
diff --git a/build_files/windows/configure_msbuild.cmd b/build_files/windows/configure_msbuild.cmd
index a9267e8cfbb..9fbdef25641 100644
--- a/build_files/windows/configure_msbuild.cmd
+++ b/build_files/windows/configure_msbuild.cmd
@@ -19,12 +19,6 @@ if "%WITH_PYDEBUG%"=="1" (
set PYDEBUG_CMAKE_ARGS=-DWINDOWS_PYTHON_DEBUG=On
)
-if "%BUILD_VS_YEAR%"=="2017" (
- set BUILD_GENERATOR_POST=%WINDOWS_ARCH%
-) else (
- set BUILD_PLATFORM_SELECT=-A %MSBUILD_PLATFORM%
-)
-
set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -G "Visual Studio %BUILD_VS_VER% %BUILD_VS_YEAR%%BUILD_GENERATOR_POST%" %BUILD_PLATFORM_SELECT% %TESTS_CMAKE_ARGS% %CLANG_CMAKE_ARGS% %ASAN_CMAKE_ARGS% %PYDEBUG_CMAKE_ARGS%
if NOT EXIST %BUILD_DIR%\nul (
diff --git a/build_files/windows/configure_ninja.cmd b/build_files/windows/configure_ninja.cmd
index 90085feb2bd..684673b7611 100644
--- a/build_files/windows/configure_ninja.cmd
+++ b/build_files/windows/configure_ninja.cmd
@@ -37,15 +37,9 @@ set LLVM_DIR=
:DetectionComplete
set CC=%LLVM_DIR%\bin\clang-cl
set CXX=%LLVM_DIR%\bin\clang-cl
- if "%BUILD_VS_YEAR%" == "2019" (
- rem build and tested against 2019 16.2
- set CFLAGS=-m64 -fmsc-version=1922
- set CXXFLAGS=-m64 -fmsc-version=1922
- ) else (
- rem build and tested against 2017 15.7
- set CFLAGS=-m64 -fmsc-version=1914
- set CXXFLAGS=-m64 -fmsc-version=1914
- )
+ rem build and tested against 2019 16.2
+ set CFLAGS=-m64 -fmsc-version=1922
+ set CXXFLAGS=-m64 -fmsc-version=1922
)
if "%WITH_ASAN%"=="1" (
diff --git a/build_files/windows/detect_msvc2017.cmd b/build_files/windows/detect_msvc2017.cmd
deleted file mode 100644
index 5f760275f78..00000000000
--- a/build_files/windows/detect_msvc2017.cmd
+++ /dev/null
@@ -1,3 +0,0 @@
-set BUILD_VS_VER=15
-set BUILD_VS_YEAR=2017
-call "%~dp0\detect_msvc_vswhere.cmd"
diff --git a/build_files/windows/find_dependencies.cmd b/build_files/windows/find_dependencies.cmd
index 9fa3b156a4f..fec2bd2e752 100644
--- a/build_files/windows/find_dependencies.cmd
+++ b/build_files/windows/find_dependencies.cmd
@@ -3,7 +3,32 @@ for %%X in (svn.exe) do (set SVN=%%~$PATH:X)
for %%X in (cmake.exe) do (set CMAKE=%%~$PATH:X)
for %%X in (ctest.exe) do (set CTEST=%%~$PATH:X)
for %%X in (git.exe) do (set GIT=%%~$PATH:X)
+REM For python, default on 39 but if that does not exist also check
+REM the 310,311 and 312 folders to see if those are there, it checks
+REM this far ahead to ensure good lib folder compatiblity in the future.
set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe
+if EXIST %PYTHON% (
+ goto detect_python_done
+)
+set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\310\bin\python.exe
+if EXIST %PYTHON% (
+ goto detect_python_done
+)
+set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\311\bin\python.exe
+if EXIST %PYTHON% (
+ goto detect_python_done
+)
+set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\312\bin\python.exe
+if EXIST %PYTHON% (
+ goto detect_python_done
+)
+
+if NOT EXIST %PYTHON% (
+ echo Warning: Python not found, there is likely an issue with the library folder
+ set PYTHON=""
+)
+
+:detect_python_done
if NOT "%verbose%" == "" (
echo svn : "%SVN%"
echo cmake : "%CMAKE%"
@@ -11,7 +36,3 @@ if NOT "%verbose%" == "" (
echo git : "%GIT%"
echo python : "%PYTHON%"
)
-if "%CMAKE%" == "" (
- echo Cmake not found in path, required for building, exiting...
- exit /b 1
-)
diff --git a/build_files/windows/format.cmd b/build_files/windows/format.cmd
index d5003c9f8d8..95440cb1818 100644
--- a/build_files/windows/format.cmd
+++ b/build_files/windows/format.cmd
@@ -9,17 +9,11 @@ exit /b 1
:detect_done
echo found clang-format in %CF_PATH%
-if EXIST %PYTHON% (
- set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe
- goto detect_python_done
+if NOT EXIST %PYTHON% (
+ echo python not found, required for this operation
+ exit /b 1
)
-echo python not found in lib folder
-exit /b 1
-
-:detect_python_done
-echo found python (%PYTHON%)
-
set FORMAT_PATHS=%BLENDER_DIR%\source\tools\utils_maintenance\clang_format_paths.py
REM The formatting script expects clang-format to be in the current PATH.
diff --git a/build_files/windows/icons.cmd b/build_files/windows/icons.cmd
index d51b27d8953..9390ccf827c 100644
--- a/build_files/windows/icons.cmd
+++ b/build_files/windows/icons.cmd
@@ -1,18 +1,8 @@
-if EXIST "%PYTHON%" (
- goto detect_python_done
+if NOT EXIST %PYTHON% (
+ echo python not found, required for this operation
+ exit /b 1
)
-set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe
-if EXIST %PYTHON% (
- goto detect_python_done
-)
-
-echo python not found at %PYTHON%
-exit /b 1
-
-:detect_python_done
-echo found python (%PYTHON%)
-
call "%~dp0\find_inkscape.cmd"
if EXIST "%INKSCAPE_BIN%" (
diff --git a/build_files/windows/icons_geom.cmd b/build_files/windows/icons_geom.cmd
index 18312daf35b..4cb3bf5b4df 100644
--- a/build_files/windows/icons_geom.cmd
+++ b/build_files/windows/icons_geom.cmd
@@ -1,18 +1,8 @@
-if EXIST %PYTHON% (
- goto detect_python_done
+if NOT EXIST %PYTHON% (
+ echo python not found, required for this operation
+ exit /b 1
)
-set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe
-if EXIST %PYTHON% (
- goto detect_python_done
-)
-
-echo python not found at %PYTHON%
-exit /b 1
-
-:detect_python_done
-echo found python (%PYTHON%)
-
call "%~dp0\find_blender.cmd"
if EXIST "%BLENDER_BIN%" (
diff --git a/build_files/windows/parse_arguments.cmd b/build_files/windows/parse_arguments.cmd
index dcef46c2c9a..eaf4a85f7ac 100644
--- a/build_files/windows/parse_arguments.cmd
+++ b/build_files/windows/parse_arguments.cmd
@@ -50,14 +50,6 @@ if NOT "%1" == "" (
goto ERR
) else if "%1" == "x64" (
set BUILD_ARCH=x64
- ) else if "%1" == "2017" (
- set BUILD_VS_YEAR=2017
- ) else if "%1" == "2017pre" (
- set BUILD_VS_YEAR=2017
- set VSWHERE_ARGS=-prerelease
- ) else if "%1" == "2017b" (
- set BUILD_VS_YEAR=2017
- set VSWHERE_ARGS=-products Microsoft.VisualStudio.Product.BuildTools
) else if "%1" == "2019" (
set BUILD_VS_YEAR=2019
) else if "%1" == "2019pre" (
diff --git a/build_files/windows/show_help.cmd b/build_files/windows/show_help.cmd
index d914ecab2b8..00d5bd7a9a8 100644
--- a/build_files/windows/show_help.cmd
+++ b/build_files/windows/show_help.cmd
@@ -24,12 +24,12 @@ echo - nobuildinfo ^(disable buildinfo^)
echo - debug ^(Build an unoptimized debuggable build^)
echo - packagename [newname] ^(override default cpack package name^)
echo - builddir [newdir] ^(override default build folder^)
-echo - 2017 ^(build with visual studio 2017^)
-echo - 2017pre ^(build with visual studio 2017 pre-release^)
-echo - 2017b ^(build with visual studio 2017 Build Tools^)
echo - 2019 ^(build with visual studio 2019^)
echo - 2019pre ^(build with visual studio 2019 pre-release^)
echo - 2019b ^(build with visual studio 2019 Build Tools^)
+echo - 2022 ^(build with visual studio 2022^)
+echo - 2022pre ^(build with visual studio 2022 pre-release^)
+echo - 2022b ^(build with visual studio 2022 Build Tools^)
echo.
echo Documentation Targets ^(Not associated with building^)
diff --git a/build_files/windows/svn_fix.cmd b/build_files/windows/svn_fix.cmd
index a9dcdf36847..9ab48de10f1 100644
--- a/build_files/windows/svn_fix.cmd
+++ b/build_files/windows/svn_fix.cmd
@@ -1,4 +1,3 @@
-if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
diff --git a/build_files/windows/update_sources.cmd b/build_files/windows/update_sources.cmd
index f8fbd383090..f99ce43f40f 100644
--- a/build_files/windows/update_sources.cmd
+++ b/build_files/windows/update_sources.cmd
@@ -1,10 +1,7 @@
-if EXIST %PYTHON% (
- goto detect_python_done
+if NOT EXIST %PYTHON% (
+ echo python not found, required for this operation
+ exit /b 1
)
-
-echo python not found in lib folder
-exit /b 1
-
:detect_python_done
REM Use -B to avoid writing __pycache__ in lib directory and causing update conflicts.
diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile
index 89954d8a155..2f004491c89 100644
--- a/doc/doxygen/Doxyfile
+++ b/doc/doxygen/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = Blender
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = V3.1
+PROJECT_NUMBER = V3.2
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/doc/python_api/rst/info_api_reference.rst b/doc/python_api/rst/info_api_reference.rst
index 19d09aee66c..70d201016f0 100644
--- a/doc/python_api/rst/info_api_reference.rst
+++ b/doc/python_api/rst/info_api_reference.rst
@@ -22,7 +22,7 @@ Data Access
===========
The most common case for using the reference API is to find out how to access data in the blend-file.
-Before going any further its best to be aware of ID data-blocks in Blender since you will often find properties
+Before going any further it's best to be aware of ID data-blocks in Blender since you will often find properties
relative to them.
@@ -55,9 +55,9 @@ Start by collecting the information where the data is located.
First find this setting in the interface ``Properties editor -> Object -> Transform -> Location``.
From the button context menu select *Online Python Reference*, this will link you to:
:class:`bpy.types.Object.location`.
-Being an API reference, this link often gives little more information then the tooltip, though some of the pages
+Being an API reference, this link often gives little more information than the tooltip, though some of the pages
include examples (normally at the top of the page).
-But you now know that you have to use ``.location`` and that its an array of three floats.
+But you now know that you have to use ``.location`` and that it's an array of three floats.
So the next step is to find out where to access objects, go down to the bottom of the page to the references section,
for objects there are many references, but one of the most common places to access objects is via the context.
@@ -154,7 +154,7 @@ The tooltip includes :class:`bpy.types.SubsurfModifier.levels` but you want the
Note that the text copied won't include the ``bpy.data.collection["name"].`` component since its assumed that
you won't be doing collection look-ups on every access and typically you'll want to use the context rather
-then access each :class:`bpy.types.ID` instance by name.
+than access each :class:`bpy.types.ID` instance by name.
Type in the ID path into a Python console :mod:`bpy.context.active_object`.
Include the trailing dot and don't execute the code, yet.
@@ -252,6 +252,6 @@ Each entry can be selected, then copied :kbd:`Ctrl-C`, usually to paste in the t
.. note::
Not all operators get registered for display,
- zooming the view for example isn't so useful to repeat so its excluded from the output.
+ zooming the view for example isn't so useful to repeat so it's excluded from the output.
To display *every* operator that runs see :ref:`Show All Operators <info_show_all_operators>`.
diff --git a/doc/python_api/rst/info_best_practice.rst b/doc/python_api/rst/info_best_practice.rst
index 2607d047997..e88adcc0d70 100644
--- a/doc/python_api/rst/info_best_practice.rst
+++ b/doc/python_api/rst/info_best_practice.rst
@@ -229,7 +229,7 @@ removing the last items first, which is faster (as explained above):
This example shows a fast way of removing items,
-for use in cases where you can alter the list order without breaking the scripts functionality.
+for use in cases where you can alter the list order without breaking the script's functionality.
This works by swapping two list items, so the item you remove is always last:
.. code-block:: python
@@ -278,7 +278,7 @@ Here are three ways of joining multiple strings into one string for writing.
This also applies to any area of your code that involves a lot of string joining:
String concatenation
- This is the slowest option, do **not** use if you can avoid it, especially when writing data in a loop.
+ This is the slowest option, do **not** use this if you can avoid it, especially when writing data in a loop.
>>> file.write(str1 + " " + str2 + " " + str3 + "\n")
@@ -288,7 +288,7 @@ String formatting
>>> file.write("%s %s %s\n" % (str1, str2, str3))
String joining
- Use to join a list of strings (the list may be temporary). In the following example, the strings are joined with
+ Use this to join a list of strings (the list may be temporary). In the following example, the strings are joined with
a space " " in between, other examples are "" or ", ".
>>> file.write(" ".join((str1, str2, str3, "\n")))
diff --git a/doc/python_api/rst/info_gotcha.rst b/doc/python_api/rst/info_gotcha.rst
index 7361f3e9ade..bef76a5e479 100644
--- a/doc/python_api/rst/info_gotcha.rst
+++ b/doc/python_api/rst/info_gotcha.rst
@@ -12,7 +12,7 @@ that can be troublesome and avoid practices that are known to cause instability.
Using Operators
===============
-Blender's operators are tools for users to access, that can access with Python too which is very useful.
+Blender's operators are tools for users to access, that can be accessed with Python too which is very useful.
Still operators have limitations that can make them cumbersome to script.
The main limits are:
@@ -20,13 +20,13 @@ The main limits are:
- Can't pass data such as objects, meshes or materials to operate on (operators use the context instead).
- The return value from calling an operator is the success (if it finished or was canceled),
in some cases it would be more logical from an API perspective to return the result of the operation.
-- Operators poll function can fail where an API function would raise an exception giving details on exactly why.
+- Operators' poll function can fail where an API function would raise an exception giving details on exactly why.
Why does an operator's poll fail?
---------------------------------
-When calling an operator gives an error like this:
+When calling an operator it gives an error like this:
>>> bpy.ops.action.clean(threshold=0.001)
RuntimeError: Operator bpy.ops.action.clean.poll() failed, context is incorrect
@@ -49,9 +49,9 @@ you should be able to find the poll function with no knowledge of C.
.. note::
Blender does have the functionality for poll functions to describe why they fail,
- but its currently not used much, if you're interested to help improve the API
+ but it's currently not used much, if you're interested to help improve the API
feel free to add calls to :class:`bpy.types.Operator.poll_message_set` (``CTX_wm_operator_poll_msg_set`` in C)
- where its not obvious why poll fails, e.g:
+ where it's not obvious why poll fails, e.g:
>>> bpy.ops.gpencil.draw()
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into
@@ -107,7 +107,7 @@ In this case you need to call :class:`bpy.types.ViewLayer.update` after modifyin
Now all dependent data (child objects, modifiers, drivers, etc.)
-has been recalculated and is available to the script within active view layer.
+have been recalculated and are available to the script within the active view layer.
Can I redraw during script execution?
@@ -116,13 +116,13 @@ Can I redraw during script execution?
The official answer to this is no, or... *"You don't want to do that"*.
To give some background on the topic:
-While a script executes Blender waits for it to finish and is effectively locked until its done,
+While a script executes, Blender waits for it to finish and is effectively locked until it's done;
while in this state Blender won't redraw or respond to user input.
Normally this is not such a problem because scripts distributed with Blender
tend not to run for an extended period of time,
nevertheless scripts *can* take a long time to complete and it would be nice to see progress in the viewport.
-When tools lock Blender in a loop redraw are highly discouraged
+Tools that lock Blender in a loop redraw are highly discouraged
since they conflict with Blender's ability to run multiple operators
at once and update different parts of the interface as the tool runs.
@@ -130,7 +130,7 @@ So the solution here is to write a **modal** operator, which is an operator that
See the modal operator template in the text editor.
Modal operators execute on user input or setup their own timers to run frequently,
they can handle the events or pass through to be handled by the keymap or other modal operators.
-Examples of a modal operators are Transform, Painting, Fly Navigation and File Select.
+Examples of modal operators are Transform, Painting, Fly Navigation and File Select.
Writing modal operators takes more effort than a simple ``for`` loop
that contains draw calls but is more flexible and integrates better with Blender's design.
@@ -240,7 +240,7 @@ Editing
Editing is where the three data types vary most.
- Polygons are very limited for editing,
- changing materials and options like smooth works but for anything else
+ changing materials and options like smooth works, but for anything else
they are too inflexible and are only intended for storage.
- Tessfaces should not be used for editing geometry because doing so will cause existing n-gons to be tessellated.
- BMesh-faces are by far the best way to manipulate geometry.
@@ -256,7 +256,7 @@ the choice mostly depends on whether the target format supports n-gons or not.
- Tessfaces work well for exporting to formats which don't support n-gons,
in fact this is the only place where their use is encouraged.
- BMesh-Faces can work for exporting too but may not be necessary if polygons can be used
- since using BMesh gives some overhead because its not the native storage format in Object-Mode.
+ since using BMesh gives some overhead because it's not the native storage format in Object-Mode.
Edit Bones, Pose Bones, Bone... Bones
@@ -348,7 +348,7 @@ Armature Mode Switching
While writing scripts that deal with armatures you may find you have to switch between modes,
when doing so take care when switching out of Edit-Mode not to keep references
to the edit bones or their head/tail vectors.
-Further access to these will crash Blender so its important the script
+Further access to these will crash Blender so it's important that the script
clearly separates sections of the code which operate in different modes.
This is mainly an issue with Edit-Mode since pose data can be manipulated without having to be in Pose-Mode,
@@ -386,11 +386,11 @@ Or with name assignment:
Data names may not match the assigned values if they exceed the maximum length, are already used or an empty string.
-Its better practice not to reference objects by names at all,
+It's better practice not to reference objects by names at all,
once created you can store the data in a list, dictionary, on a class, etc;
there is rarely a reason to have to keep searching for the same data by name.
-If you do need to use name references, its best to use a dictionary to maintain
+If you do need to use name references, it's best to use a dictionary to maintain
a mapping between the names of the imported assets and the newly created data,
this way you don't run this risk of referencing existing data from the blend-file, or worse modifying it.
@@ -414,11 +414,11 @@ Library Collisions
Blender keeps data names unique (:class:`bpy.types.ID.name`) so you can't name two objects,
meshes, scenes, etc., the same by accident.
However, when linking in library data from another blend-file naming collisions can occur,
-so its best to avoid referencing data by name at all.
+so it's best to avoid referencing data by name at all.
-This can be tricky at times and not even Blender handles this correctly in some case
+This can be tricky at times and not even Blender handles this correctly in some cases
(when selecting the modifier object for e.g. you can't select between multiple objects with the same name),
-but its still good to try avoiding these problems in this area.
+but it's still good to try avoiding these problems in this area.
If you need to select between local and library data, there is a feature in ``bpy.data`` members to allow for this.
.. code-block:: python
@@ -467,11 +467,11 @@ writing a script in ``latin1`` or ``iso-8859-15``.
See `PEP 263 <https://www.python.org/dev/peps/pep-0263/>`__.
However, this complicates matters for Blender's Python API because ``.blend`` files don't have an explicit encoding.
-To avoid the problem for Python integration and script authors we have decided all strings in blend-files
+To avoid the problem for Python integration and script authors we have decided that all strings in blend-files
**must** be ``UTF-8``, ``ASCII`` compatible.
-This means assigning strings with different encodings to an object names for instance will raise an error.
+This means assigning strings with different encodings to an object name, for instance, will raise an error.
-Paths are an exception to this rule since the existence of non-UTF-8 paths on user's file system cannot be ignored.
+Paths are an exception to this rule since the existence of non-UTF-8 paths on the user's file system cannot be ignored.
This means seemingly harmless expressions can raise errors, e.g:
>>> print(bpy.data.filepath)
@@ -505,7 +505,7 @@ to keep it short about encoding problems -- here are some suggestions:
.. note::
Sometimes it's preferable to avoid string encoding issues by using bytes instead of Python strings,
- when reading some input its less trouble to read it as binary data
+ when reading some input it's less trouble to read it as binary data
though you will still need to decide how to treat any strings you want to use with Blender,
some importers do this.
@@ -679,7 +679,7 @@ Undo/Redo
---------
For safety, you should assume that undo and redo always invalidates all :class:`bpy.types.ID`
-instances (Object, Scene, Mesh, Light, etc.), as weel obviously as all of their sub-data.
+instances (Object, Scene, Mesh, Light, etc.), as well obviously as all of their sub-data.
This example shows how you can tell undo changes the memory locations:
@@ -716,7 +716,7 @@ Tools in Blender are not allowed to modify library data.
But Python does not enforce this restriction.
This can be useful in some cases, using a script to adjust material values for example.
-But its also possible to use a script to make library data point to newly created local data,
+But it's also possible to use a script to make library data point to newly created local data,
which is not supported since a call to undo will remove the local data
but leave the library referencing it and likely crash.
@@ -743,7 +743,7 @@ will re-allocate objects data,
any references to a meshes vertices/polygons/UVs, armatures bones,
curves points, etc. cannot be accessed after switching mode.
-Only the reference to the data its self can be re-accessed, the following example will crash.
+Only the reference to the data itself can be re-accessed, the following example will crash.
.. code-block:: python
diff --git a/doc/python_api/rst/info_tips_and_tricks.rst b/doc/python_api/rst/info_tips_and_tricks.rst
index d16dcc077df..691cfb50b4f 100644
--- a/doc/python_api/rst/info_tips_and_tricks.rst
+++ b/doc/python_api/rst/info_tips_and_tricks.rst
@@ -81,7 +81,7 @@ but reference an external file rather than including it directly.
Executing External Scripts
--------------------------
-This is the equivalent to running the script directly, referencing a scripts path from a two line code block.
+This is the equivalent to running the script directly, referencing a script's path from a two line code block.
.. code-block:: python
@@ -124,7 +124,7 @@ small script which is often useful for testing different settings quickly.
The other issue with this is the script has to be in Python's module search path.
While this is not best practice -- for testing purposes you can extend the search path,
-this following example adds the current blend-files directory to the search path
+this following example adds the current blend-file's directory to the search path
and then loads the script as a module.
.. code-block:: python
@@ -302,7 +302,7 @@ Python Safety (Build Option)
----------------------------
Since it's possible to access data which has been removed (see :doc:`Gotchas <info_gotcha>`),
-can make it hard to track down the cause of crashes.
+it can be hard to track down the cause of crashes.
To raise Python exceptions on accessing freed data (rather than crashing),
enable the CMake build option ``WITH_PYTHON_SAFETY``.
This enables data tracking which makes data access about two times slower
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index 0ae3b24578b..2133f5fcb9f 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -417,7 +417,8 @@ MODULE_GROUPING = {
BLENDER_REVISION = str(bpy.app.build_hash, 'utf_8')
# '2.83.0 Beta' or '2.83.0' or '2.83.1'
-BLENDER_VERSION_DOTS = bpy.app.version_string
+BLENDER_VERSION_STRING = bpy.app.version_string
+BLENDER_VERSION_DOTS = "%d.%d" % (bpy.app.version[0], bpy.app.version[1])
if BLENDER_REVISION != "Unknown":
# SHA1 Git hash
@@ -1724,11 +1725,11 @@ def write_sphinx_conf_py(basepath):
fw("import sys, os\n\n")
fw("extensions = ['sphinx.ext.intersphinx']\n\n")
fw("intersphinx_mapping = {'blender_manual': ('https://docs.blender.org/manual/en/dev/', None)}\n\n")
- fw("project = 'Blender %s Python API'\n" % BLENDER_VERSION_DOTS)
+ fw("project = 'Blender %s Python API'\n" % BLENDER_VERSION_STRING)
fw("master_doc = 'index'\n")
fw("copyright = u'Blender Foundation'\n")
- fw("version = '%s'\n" % BLENDER_VERSION_HASH)
- fw("release = '%s'\n" % BLENDER_VERSION_HASH)
+ fw("version = '%s'\n" % BLENDER_VERSION_DOTS)
+ fw("release = '%s'\n" % BLENDER_VERSION_DOTS)
# Quiet file not in table-of-contents warnings.
fw("exclude_patterns = [\n")
@@ -1749,6 +1750,7 @@ except ModuleNotFoundError:
fw("if html_theme == 'sphinx_rtd_theme':\n")
fw(" html_theme_options = {\n")
+ fw(" 'display_version': False,\n")
# fw(" 'analytics_id': '',\n")
# fw(" 'collapse_navigation': True,\n")
fw(" 'sticky_navigation': False,\n")
@@ -1765,10 +1767,15 @@ except ModuleNotFoundError:
fw("html_show_search_summary = True\n")
fw("html_split_index = True\n")
fw("html_static_path = ['static']\n")
+ fw("templates_path = ['templates']\n")
+ fw("html_context = {'commit': '%s'}\n" % BLENDER_VERSION_HASH)
fw("html_extra_path = ['static/favicon.ico', 'static/blender_logo.svg']\n")
fw("html_favicon = 'static/favicon.ico'\n")
fw("html_logo = 'static/blender_logo.svg'\n")
fw("html_last_updated_fmt = '%m/%d/%Y'\n\n")
+ fw("if html_theme == 'sphinx_rtd_theme':\n")
+ fw(" html_css_files = ['css/version_switch.css']\n")
+ fw(" html_js_files = ['js/version_switch.js']\n")
# needed for latex, pdf gen
fw("latex_elements = {\n")
@@ -2125,6 +2132,9 @@ def copy_theme_assets(basepath):
shutil.copytree(os.path.join(SCRIPT_DIR, "static"),
os.path.join(basepath, "static"),
copy_function=shutil.copy)
+ shutil.copytree(os.path.join(SCRIPT_DIR, "templates"),
+ os.path.join(basepath, "templates"),
+ copy_function=shutil.copy)
def rna2sphinx(basepath):
diff --git a/doc/python_api/static/css/version_switch.css b/doc/python_api/static/css/version_switch.css
new file mode 100644
index 00000000000..360ff2eea0e
--- /dev/null
+++ b/doc/python_api/static/css/version_switch.css
@@ -0,0 +1,127 @@
+/* Override RTD theme */
+.rst-versions {
+ border-top: 0px;
+ overflow: visible;
+}
+.version-btn.vdeact {
+ cursor: default;
+ color: dimgray;
+}
+
+.version-btn.vdeact::after {
+ content: "";
+}
+#versionwrap {
+ display: flex;
+ padding-top: 2px;
+ font-size: 90%;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+.version-btn {
+ display: inline-block;
+ background-color: #272525;
+ width: 140px;
+ text-align: center;
+ padding: 3px 10px;
+ margin: 0px 5px 4px;
+ vertical-align: middle;
+ color: #27AE60;
+ border: solid 1px #444444;
+ border-radius: 3px;
+ cursor: pointer;
+ z-index: 400;
+ transition: border-color 0.4s;
+}
+.version-btn::after {
+ content:"\f0d8";
+ display: inline;
+ font: normal normal normal 16px/1 FontAwesome;
+ color: #8d8c8c;
+ vertical-align: top;
+ padding-left: 0.5em;
+}
+.version-btn-open::after {
+ color: gray;
+}
+.version-btn:hover, .version-btn:focus {
+ border-color: #525252;
+}
+.version-btn-open {
+ color: gray;
+ border: solid 1px gray;
+}
+.version-btn.wait {
+ cursor: wait;
+}
+.version-btn.disabled {
+ cursor: not-allowed;
+ color: dimgray;
+}
+.version-dialog {
+ display: none;
+ position: absolute;
+ bottom: 28px;
+ width: 140px;
+ margin: 0 5px;
+ padding-bottom: 4px;
+ background-color: #0003;
+ border-radius: 3px;
+ box-shadow: 0 0 6px #000C;
+ z-index: 999;
+ max-height: calc(100vh - 30px);
+ overflow-y: auto;
+ cursor: default;
+}
+.version-title {
+ padding: 5px;
+ color: black;
+ text-align: center;
+ font-size: 102%;
+ background-color: #27ae60;
+ border-bottom: solid 1.5px #444;
+}
+.version-list {
+ margin-bottom: 4px;
+ text-align: center;
+ background-color: #000C;
+ border: solid 1px gray;
+ border-radius: 0px 0px 3px 3px;
+}
+.version-list a, .version-list span, .version-list li {
+ position: relative;
+ display: block;
+ font-size: 98%;
+ line-height: 1.15;
+ width: 100%;
+ margin: 0;
+ padding: 4px 0px;
+ color: #404040;
+}
+.version-list li {
+ background-color: #ede9e9;
+ color: #404040;
+ padding: 1px;
+}
+.version-list li:hover, .version-list li a:focus {
+ background-color: #b9cfda;
+}
+.version-list li.selected, .version-list li.selected:hover {
+ background-color: #8d8c8c;
+}
+.version-list li.selected span {
+ cursor: default;
+ outline-color: red;
+}
+.version-arrow {
+ position: absolute;
+ width: 8px;
+ height: 8px;
+ left: 50%;
+ bottom: 4px;
+ margin-left: -4px;
+ transform: rotate(225deg);
+ background: #ede9e9;
+ border: 1px solid gray;
+ border-width: 1px 0 0 1px;
+}
diff --git a/doc/python_api/static/js/version_switch.js b/doc/python_api/static/js/version_switch.js
new file mode 100644
index 00000000000..d69b17c31bb
--- /dev/null
+++ b/doc/python_api/static/js/version_switch.js
@@ -0,0 +1,323 @@
+(function() { // switch: v1.2
+"use strict";
+
+var versionsFileUrl = "https://docs.blender.org/PROD/versions.json"
+
+var all_versions;
+
+var Popover = function() {
+ function Popover(id)
+ {
+ this.isOpen = false;
+ this.type = (id === "version-popover");
+ this.$btn = $('#' + id);
+ this.$dialog = this.$btn.next();
+ this.$list = this.$dialog.children("ul");
+ this.sel = null;
+ this.beforeInit();
+ }
+
+ Popover.prototype = {
+ beforeInit : function() {
+ var that = this;
+ this.$btn.on("click", function(e) {
+ that.init();
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ this.$btn.on("keydown", function(e) {
+ if (that.btnKeyFilter(e)) {
+ that.init();
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
+ },
+ init : function() {
+ this.$btn.off("click");
+ this.$btn.off("keydown");
+
+ if (all_versions === undefined) {
+ this.$btn.addClass("wait");
+ this.loadVL(this);
+ }
+ else {
+ this.afterLoad();
+ }
+ },
+ loadVL : function(that) {
+ $.getJSON(versionsFileUrl, function(data) {
+ all_versions = data;
+ that.afterLoad();
+ return true;
+ }).fail(function() {
+ console.log("Version Switch Error: versions.json could not be loaded.");
+ that.$btn.addClass("disabled");
+ return false;
+ });
+ },
+ afterLoad : function() {
+ var release = DOCUMENTATION_OPTIONS.VERSION;
+ const m = release.match(/\d\.\d+/g);
+ if (m) {
+ release = m[0];
+ }
+
+ this.warnOld(release, all_versions);
+
+ var version = this.getNamed(release);
+ var list = this.buildList(version);
+
+ this.$list.children(":first-child").remove();
+ this.$list.append(list);
+ var that = this;
+ this.$list.on("keydown", function(e) {
+ that.keyMove(e);
+ });
+
+ this.$btn.removeClass("wait");
+ this.btnOpenHandler();
+ this.$btn.on("mousedown", function(e) {
+ that.btnOpenHandler();
+ e.preventDefault()
+ });
+ this.$btn.on("keydown", function(e) {
+ if (that.btnKeyFilter(e)) {
+ that.btnOpenHandler();
+ }
+ });
+ },
+ warnOld : function(release, all_versions) {
+ // Note this is effectively disabled now, two issues must fixed:
+ // * versions.js does not contain a current entry, because that leads to
+ // duplicate version numbers in the menu. These need to be deduplicated.
+ // * It only shows the warning after opening the menu to switch version
+ // when versions.js is loaded. This is too late to be useful.
+ var current = all_versions.current
+ if (!current)
+ {
+ // console.log("Version Switch Error: no 'current' in version.json.");
+ return;
+ }
+ const m = current.match(/\d\.\d+/g);
+ if (m) {
+ current = parseFloat(m[0]);
+ }
+ if (release < current) {
+ var currentURL = window.location.pathname.replace(release, current);
+ var warning = $('<div class="admonition warning"> ' +
+ '<p class="first admonition-title">Note</p> ' +
+ '<p class="last"> ' +
+ 'You are not using the most up to date version of the documentation. ' +
+ '<a href="#"></a> is the newest version.' +
+ '</p>' +
+ '</div>');
+
+ warning.find('a').attr('href', currentURL).text(current);
+
+ var body = $("div.body");
+ if (!body.length) {
+ body = $("div.document");
+ }
+ body.prepend(warning);
+ }
+ },
+ buildList : function(v) {
+ var url = new URL(window.location.href);
+ let pathSplit = [ "", "api", v ];
+ if (url.pathname.startsWith("/api/")) {
+ pathSplit.push(url.pathname.split('/').slice(3).join('/'));
+ }
+ else {
+ pathSplit.push(url.pathname.substring(1));
+ }
+ if (this.type) {
+ var dyn = all_versions;
+ var cur = v;
+ }
+ var buf = [];
+ var that = this;
+ $.each(dyn, function(ix, title) {
+ buf.push("<li");
+ if (ix === cur) {
+ buf.push(
+ ' class="selected" tabindex="-1" role="presentation"><span tabindex="-1" role="menuitem" aria-current="page">' +
+ title + '</spanp></li>');
+ }
+ else {
+ pathSplit[2 + that.type] = ix;
+ var href = new URL(url);
+ href.pathname = pathSplit.join('/');
+ buf.push(' tabindex="-1" role="presentation"><a href ="' + href + '" tabindex="-1">' +
+ title + '</a></li>');
+ }
+ });
+ return buf.join('');
+ },
+ getNamed : function(v) {
+ $.each(all_versions, function(ix, title) {
+ if (ix === "master" || ix === "latest") {
+ var m = title.match(/\d\.\d[\w\d\.]*/)[0];
+ if (parseFloat(m) == v) {
+ v = ix;
+ return false;
+ }
+ }
+ });
+ return v;
+ },
+ dialogToggle : function(speed) {
+ var wasClose = !this.isOpen;
+ var that = this;
+ if (!this.isOpen) {
+ this.$btn.addClass("version-btn-open");
+ this.$btn.attr("aria-pressed", true);
+ this.$dialog.attr("aria-hidden", false);
+ this.$dialog.fadeIn(speed, function() {
+ that.$btn.parent().on("focusout", function(e) {
+ that.focusoutHandler();
+ e.stopImmediatePropagation();
+ })
+ that.$btn.parent().on("mouseleave", function(e) {
+ that.mouseoutHandler();
+ e.stopImmediatePropagation();
+ });
+ });
+ this.isOpen = true;
+ }
+ else {
+ this.$btn.removeClass("version-btn-open");
+ this.$btn.attr("aria-pressed", false);
+ this.$dialog.attr("aria-hidden", true);
+ this.$btn.parent().off("focusout");
+ this.$btn.parent().off("mouseleave");
+ this.$dialog.fadeOut(speed, function() {
+ if (this.$sel) {
+ this.$sel.attr("tabindex", -1);
+ }
+ that.$btn.attr("tabindex", 0);
+ if (document.activeElement !== null && document.activeElement !== document &&
+ document.activeElement !== document.body) {
+ that.$btn.focus();
+ }
+ });
+ this.isOpen = false;
+ }
+
+ if (wasClose) {
+ if (this.$sel) {
+ this.$sel.attr("tabindex", -1);
+ }
+ if (document.activeElement !== null && document.activeElement !== document &&
+ document.activeElement !== document.body) {
+ var $nw = this.listEnter();
+ $nw.attr("tabindex", 0);
+ $nw.focus();
+ this.$sel = $nw;
+ }
+ }
+ },
+ btnOpenHandler : function() {
+ this.dialogToggle(300);
+ },
+ focusoutHandler : function() {
+ var list = this.$list;
+ var that = this;
+ setTimeout(function() {
+ if (list.find(":focus").length === 0) {
+ that.dialogToggle(200);
+ }
+ }, 200);
+ },
+ mouseoutHandler : function() {
+ this.dialogToggle(200);
+ },
+ btnKeyFilter : function(e) {
+ if (e.ctrlKey || e.shiftKey) {
+ return false;
+ }
+ if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
+ e.key === "ArrowDown" || e.key === "ArrowUp") {
+ return true;
+ }
+ return false;
+ },
+ keyMove : function(e) {
+ if (e.ctrlKey || e.shiftKey) {
+ return true;
+ }
+ var p = true;
+ var $nw = $(e.target);
+ switch (e.key) {
+ case "ArrowUp":
+ $nw = this.listPrev($nw);
+ break;
+ case "ArrowDown":
+ $nw = this.listNext($nw);
+ break;
+ case "Home":
+ $nw = this.listFirst();
+ break;
+ case "End":
+ $nw = this.listLast();
+ break;
+ case "Escape":
+ $nw = this.listExit();
+ break;
+ case "ArrowLeft":
+ $nw = this.listExit();
+ break;
+ case "ArrowRight":
+ $nw = this.listExit();
+ break;
+ default:
+ p = false;
+ }
+ if (p) {
+ $nw.attr("tabindex", 0);
+ $nw.focus();
+ if (this.$sel) {
+ this.$sel.attr("tabindex", -1);
+ }
+ this.$sel = $nw;
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ },
+ listPrev : function($nw) {
+ if ($nw.parent().prev().length !== 0) {
+ return $nw.parent().prev().children(":first-child");
+ }
+ else {
+ return this.listLast();
+ }
+ },
+ listNext : function($nw) {
+ if ($nw.parent().next().length !== 0) {
+ return $nw.parent().next().children(":first-child");
+ }
+ else {
+ return this.listFirst();
+ }
+ },
+ listFirst : function() {
+ return this.$list.children(":first-child").children(":first-child");
+ },
+ listLast : function() {
+ return this.$list.children(":last-child").children(":first-child");
+ },
+ listExit : function() {
+ this.mouseoutHandler();
+ return this.$btn;
+ },
+ listEnter : function() {
+ return this.$list.children(":first-child").children(":first-child");
+ }
+ };
+ return Popover
+}();
+
+$(document).ready(function() {
+ var lng_popover = new Popover("version-popover");
+});
+})();
diff --git a/doc/python_api/templates/versions.html b/doc/python_api/templates/versions.html
new file mode 100644
index 00000000000..64b47185ba7
--- /dev/null
+++ b/doc/python_api/templates/versions.html
@@ -0,0 +1,17 @@
+<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="document versions">
+ <ul id="versionwrap" role="presentation">
+ <li role="presentation">
+ <span id="version-popover" class="version-btn" tabindex="0" role="button" aria-label="versions selector" aria-haspopup="true" aria-controls="version-vsnlist" aria-disabled="true">
+ {{ release }}
+ </span>
+ <div class="version-dialog" aria-hidden="true">
+ <div class="version-arrow" aria-hidden="true"></div>
+ <div class="version-title">Versions</div>
+ <ul id="version-vsnlist" class="version-list" role="menu" aria-labelledby="version-popover" aria-hidden="true">
+ <li role="presentation">Loading...</li>
+ </ul>
+ </div>
+ </li>
+ </ul>
+</div>
+ \ No newline at end of file
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index 1fdc8e60167..be76e8b0db1 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -113,6 +113,6 @@ if(WITH_MOD_FLUID)
add_subdirectory(mantaflow)
endif()
-if (WITH_COMPOSITOR)
+if(WITH_COMPOSITOR)
add_subdirectory(smaa_areatex)
endif()
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index f669adb9f37..1afb321da3d 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -74,7 +74,7 @@ enum_panorama_types = (
"Similar to most fisheye modern lens, takes sensor dimensions into consideration"),
('MIRRORBALL', "Mirror Ball", "Uses the mirror ball mapping"),
('FISHEYE_LENS_POLYNOMIAL', "Fisheye Lens Polynomial",
- "Defines the lens projection as polynomial to allow real world camera lenses to be mimicked."),
+ "Defines the lens projection as polynomial to allow real world camera lenses to be mimicked"),
)
enum_curve_shape = (
@@ -667,6 +667,11 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
description="Use special type BVH optimized for hair (uses more ram but renders faster)",
default=True,
)
+ debug_use_compact_bvh: BoolProperty(
+ name="Use Compact BVH",
+ description="Use compact BVH structure (uses less ram but renders slower)",
+ default=True,
+ )
debug_bvh_time_steps: IntProperty(
name="BVH Time Steps",
description="Split BVH primitives by this number of time steps to speed up render time in cost of memory",
@@ -896,27 +901,27 @@ class CyclesCameraSettings(bpy.types.PropertyGroup):
fisheye_polynomial_k0: FloatProperty(
name="Fisheye Polynomial K0",
- description="Coefficient K0 of the lens polinomial",
+ description="Coefficient K0 of the lens polynomial",
default=camera.default_fisheye_polynomial[0], precision=6, step=0.1, subtype='ANGLE',
)
fisheye_polynomial_k1: FloatProperty(
name="Fisheye Polynomial K1",
- description="Coefficient K1 of the lens polinomial",
+ description="Coefficient K1 of the lens polynomial",
default=camera.default_fisheye_polynomial[1], precision=6, step=0.1, subtype='ANGLE',
)
fisheye_polynomial_k2: FloatProperty(
name="Fisheye Polynomial K2",
- description="Coefficient K2 of the lens polinomial",
+ description="Coefficient K2 of the lens polynomial",
default=camera.default_fisheye_polynomial[2], precision=6, step=0.1, subtype='ANGLE',
)
fisheye_polynomial_k3: FloatProperty(
name="Fisheye Polynomial K3",
- description="Coefficient K3 of the lens polinomial",
+ description="Coefficient K3 of the lens polynomial",
default=camera.default_fisheye_polynomial[3], precision=6, step=0.1, subtype='ANGLE',
)
fisheye_polynomial_k4: FloatProperty(
name="Fisheye Polynomial K4",
- description="Coefficient K4 of the lens polinomial",
+ description="Coefficient K4 of the lens polynomial",
default=camera.default_fisheye_polynomial[4], precision=6, step=0.1, subtype='ANGLE',
)
@@ -1447,6 +1452,19 @@ class CyclesPreferences(bpy.types.AddonPreferences):
num += 1
return num
+ def has_multi_device(self):
+ import _cycles
+ compute_device_type = self.get_compute_device_type()
+ device_list = _cycles.available_devices(compute_device_type)
+ for device in device_list:
+ if device[1] == compute_device_type:
+ continue
+ for dev in self.devices:
+ if dev.use and dev.id == device[2]:
+ return True
+
+ return False
+
def has_active_device(self):
return self.get_num_gpu_devices() > 0
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 5b600692152..e4b2fef87c3 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -118,6 +118,12 @@ def use_optix(context):
return (get_device_type(context) == 'OPTIX' and cscene.device == 'GPU')
+def use_multi_device(context):
+ cscene = context.scene.cycles
+ if cscene.device != 'GPU':
+ return False
+ return context.preferences.addons[__package__].preferences.has_multi_device()
+
def show_device_active(context):
cscene = context.scene.cycles
@@ -661,6 +667,10 @@ class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Pa
bl_label = "Acceleration Structure"
bl_parent_id = "CYCLES_RENDER_PT_performance"
+ @classmethod
+ def poll(cls, context):
+ return not use_optix(context) or use_multi_device(context)
+
def draw(self, context):
import _cycles
@@ -673,21 +683,33 @@ class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Pa
col = layout.column()
- use_embree = False
+ use_embree = _cycles.with_embree
+
if use_cpu(context):
- use_embree = _cycles.with_embree
- if not use_embree:
+ col.prop(cscene, "debug_use_spatial_splits")
+ if use_embree:
+ col.prop(cscene, "debug_use_compact_bvh")
+ else:
+ sub = col.column()
+ sub.active = not cscene.debug_use_spatial_splits
+ sub.prop(cscene, "debug_bvh_time_steps")
+
+ col.prop(cscene, "debug_use_hair_bvh")
+
sub = col.column(align=True)
sub.label(text="Cycles built without Embree support")
sub.label(text="CPU raytracing performance will be poor")
+ else:
+ col.prop(cscene, "debug_use_spatial_splits")
+ sub = col.column()
+ sub.active = not cscene.debug_use_spatial_splits
+ sub.prop(cscene, "debug_bvh_time_steps")
- col.prop(cscene, "debug_use_spatial_splits")
- sub = col.column()
- sub.active = not use_embree
- sub.prop(cscene, "debug_use_hair_bvh")
- sub = col.column()
- sub.active = not cscene.debug_use_spatial_splits and not use_embree
- sub.prop(cscene, "debug_bvh_time_steps")
+ col.prop(cscene, "debug_use_hair_bvh")
+
+ # CPU is used in addition to a GPU
+ if use_multi_device(context) and use_embree:
+ col.prop(cscene, "debug_use_compact_bvh")
class CYCLES_RENDER_PT_performance_final_render(CyclesButtonsPanel, Panel):
diff --git a/intern/cycles/blender/curves.cpp b/intern/cycles/blender/curves.cpp
index 65a02d041cc..102ddf5ee32 100644
--- a/intern/cycles/blender/curves.cpp
+++ b/intern/cycles/blender/curves.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <optional>
+
#include "blender/sync.h"
#include "blender/util.h"
@@ -624,15 +626,36 @@ void BlenderSync::sync_particle_hair(
}
}
-#ifdef WITH_HAIR_NODES
-static float4 hair_point_as_float4(BL::HairPoint b_point)
+#ifdef WITH_NEW_CURVES_TYPE
+
+static std::optional<BL::FloatAttribute> find_curves_radius_attribute(BL::Curves b_curves)
{
- float4 mP = float3_to_float4(get_float3(b_point.co()));
- mP.w = b_point.radius();
+ for (BL::Attribute &b_attribute : b_curves.attributes) {
+ if (b_attribute.name() != "radius") {
+ continue;
+ }
+ if (b_attribute.domain() != BL::Attribute::domain_POINT) {
+ continue;
+ }
+ if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) {
+ continue;
+ }
+ return BL::FloatAttribute{b_attribute};
+ }
+ return std::nullopt;
+}
+
+static float4 hair_point_as_float4(BL::Curves b_curves,
+ std::optional<BL::FloatAttribute> b_attr_radius,
+ const int index)
+{
+ float4 mP = float3_to_float4(get_float3(b_curves.position_data[index].vector()));
+ mP.w = b_attr_radius ? b_attr_radius->data[index].value() : 0.0f;
return mP;
}
-static float4 interpolate_hair_points(BL::Hair b_hair,
+static float4 interpolate_hair_points(BL::Curves b_curves,
+ std::optional<BL::FloatAttribute> b_attr_radius,
const int first_point_index,
const int num_points,
const float step)
@@ -641,12 +664,12 @@ static float4 interpolate_hair_points(BL::Hair b_hair,
const int point_a = clamp((int)curve_t, 0, num_points - 1);
const int point_b = min(point_a + 1, num_points - 1);
const float t = curve_t - (float)point_a;
- return lerp(hair_point_as_float4(b_hair.points[first_point_index + point_a]),
- hair_point_as_float4(b_hair.points[first_point_index + point_b]),
+ return lerp(hair_point_as_float4(b_curves, b_attr_radius, first_point_index + point_a),
+ hair_point_as_float4(b_curves, b_attr_radius, first_point_index + point_b),
t);
}
-static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
+static void export_hair_curves(Scene *scene, Hair *hair, BL::Curves b_curves)
{
/* TODO: optimize so we can straight memcpy arrays from Blender? */
@@ -666,17 +689,19 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
}
/* Reserve memory. */
- const int num_keys = b_hair.points.length();
- const int num_curves = b_hair.curves.length();
+ const int num_keys = b_curves.points.length();
+ const int num_curves = b_curves.curves.length();
hair->reserve_curves(num_curves, num_keys);
+ std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves);
+
/* Export curves and points. */
vector<float> points_length;
- for (BL::HairCurve &b_curve : b_hair.curves) {
- const int first_point_index = b_curve.first_point_index();
- const int num_points = b_curve.num_points();
+ for (int i = 0; i < num_curves; i++) {
+ const int first_point_index = b_curves.curve_offset_data[i].value();
+ const int num_points = b_curves.curve_offset_data[i + 1].value() - first_point_index;
float3 prev_co = zero_float3();
float length = 0.0f;
@@ -687,10 +712,9 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Position and radius. */
for (int i = 0; i < num_points; i++) {
- BL::HairPoint b_point = b_hair.points[first_point_index + i];
-
- const float3 co = get_float3(b_point.co());
- const float radius = b_point.radius();
+ const float3 co = get_float3(b_curves.position_data[first_point_index + i].vector());
+ const float radius = b_attr_radius ? b_attr_radius->data[first_point_index + i].value() :
+ 0.0f;
hair->add_curve_key(co, radius);
if (attr_intercept) {
@@ -715,7 +739,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Random number per curve. */
if (attr_random != NULL) {
- attr_random->add(hash_uint2_to_float(b_curve.index(), 0));
+ attr_random->add(hash_uint2_to_float(i, 0));
}
/* Curve. */
@@ -724,7 +748,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
}
}
-static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_step)
+static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motion_step)
{
/* Find or add attribute. */
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
@@ -737,14 +761,17 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
/* Export motion keys. */
const int num_keys = hair->get_curve_keys().size();
+ const int num_curves = b_curves.curves.length();
float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
bool have_motion = false;
int num_motion_keys = 0;
int curve_index = 0;
- for (BL::HairCurve &b_curve : b_hair.curves) {
- const int first_point_index = b_curve.first_point_index();
- const int num_points = b_curve.num_points();
+ std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves);
+
+ for (int i = 0; i < num_curves; i++) {
+ const int first_point_index = b_curves.curve_offset_data[i].value();
+ const int num_points = b_curves.curve_offset_data[i + 1].value() - first_point_index;
Hair::Curve curve = hair->get_curve(curve_index);
curve_index++;
@@ -755,7 +782,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
int point_index = first_point_index + i;
if (point_index < num_keys) {
- mP[num_motion_keys] = hair_point_as_float4(b_hair.points[point_index]);
+ mP[num_motion_keys] = hair_point_as_float4(b_curves, b_attr_radius, point_index);
num_motion_keys++;
if (!have_motion) {
@@ -774,7 +801,8 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
const float step_size = curve.num_keys > 1 ? 1.0f / (curve.num_keys - 1) : 0.0f;
for (int i = 0; i < curve.num_keys; i++) {
const float step = i * step_size;
- mP[num_motion_keys] = interpolate_hair_points(b_hair, first_point_index, num_points, step);
+ mP[num_motion_keys] = interpolate_hair_points(
+ b_curves, b_attr_radius, first_point_index, num_points, step);
num_motion_keys++;
}
have_motion = true;
@@ -791,12 +819,12 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step)
{
/* Convert Blender hair to Cycles curves. */
- BL::Hair b_hair(b_ob_info.object_data);
+ BL::Curves b_curves(b_ob_info.object_data);
if (motion) {
- export_hair_curves_motion(hair, b_hair, motion_step);
+ export_hair_curves_motion(hair, b_curves, motion_step);
}
else {
- export_hair_curves(scene, hair, b_hair);
+ export_hair_curves(scene, hair, b_curves);
}
}
#else
@@ -819,8 +847,8 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, H
new_hair.set_used_shaders(used_shaders);
if (view_layer.use_hair) {
-#ifdef WITH_HAIR_NODES
- if (b_ob_info.object_data.is_a(&RNA_Hair)) {
+#ifdef WITH_NEW_CURVES_TYPE
+ if (b_ob_info.object_data.is_a(&RNA_Curves)) {
/* Hair object. */
sync_hair(&new_hair, b_ob_info, false);
}
@@ -873,8 +901,8 @@ void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph,
/* Export deformed coordinates. */
if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
-#ifdef WITH_HAIR_NODES
- if (b_ob_info.object_data.is_a(&RNA_Hair)) {
+#ifdef WITH_NEW_CURVES_TYPE
+ if (b_ob_info.object_data.is_a(&RNA_Curves)) {
/* Hair object. */
sync_hair(hair, b_ob_info, true, motion_step);
return;
diff --git a/intern/cycles/blender/geometry.cpp b/intern/cycles/blender/geometry.cpp
index 78c803b7adb..a9b61f2578f 100644
--- a/intern/cycles/blender/geometry.cpp
+++ b/intern/cycles/blender/geometry.cpp
@@ -32,8 +32,8 @@ CCL_NAMESPACE_BEGIN
static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair)
{
-#ifdef WITH_HAIR_NODES
- if (b_ob_info.object_data.is_a(&RNA_Hair) || use_particle_hair) {
+#ifdef WITH_NEW_CURVES_TYPE
+ if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) {
#else
if (use_particle_hair) {
#endif
@@ -231,8 +231,8 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
if (progress.get_cancel())
return;
-#ifdef WITH_HAIR_NODES
- if (b_ob_info.object_data.is_a(&RNA_Hair) || use_particle_hair) {
+#ifdef WITH_NEW_CURVES_TYPE
+ if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) {
#else
if (use_particle_hair) {
#endif
diff --git a/intern/cycles/blender/object.cpp b/intern/cycles/blender/object.cpp
index 65a04a39660..22acc09c538 100644
--- a/intern/cycles/blender/object.cpp
+++ b/intern/cycles/blender/object.cpp
@@ -72,7 +72,7 @@ bool BlenderSync::object_is_geometry(BObjectInfo &b_ob_info)
BL::Object::type_enum type = b_ob_info.iter_object.type();
- if (type == BL::Object::type_VOLUME || type == BL::Object::type_HAIR ||
+ if (type == BL::Object::type_VOLUME || type == BL::Object::type_CURVES ||
type == BL::Object::type_POINTCLOUD) {
/* Will be exported attached to mesh. */
return true;
@@ -97,7 +97,7 @@ bool BlenderSync::object_can_have_geometry(BL::Object &b_ob)
case BL::Object::type_SURFACE:
case BL::Object::type_META:
case BL::Object::type_FONT:
- case BL::Object::type_HAIR:
+ case BL::Object::type_CURVES:
case BL::Object::type_POINTCLOUD:
case BL::Object::type_VOLUME:
return true;
diff --git a/intern/cycles/blender/output_driver.cpp b/intern/cycles/blender/output_driver.cpp
index d5cc0c60bae..f35b48493cb 100644
--- a/intern/cycles/blender/output_driver.cpp
+++ b/intern/cycles/blender/output_driver.cpp
@@ -51,8 +51,6 @@ bool BlenderOutputDriver::read_render_tile(const Tile &tile)
BL::RenderLayer b_rlay = *b_single_rlay;
- vector<float> pixels(static_cast<size_t>(tile.size.x) * tile.size.y * 4);
-
/* Copy each pass.
* TODO:copy only the required ones for better performance? */
for (BL::RenderPass &b_pass : b_rlay.passes) {
diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp
index 5604c2989fd..39e49ac3478 100644
--- a/intern/cycles/blender/shader.cpp
+++ b/intern/cycles/blender/shader.cpp
@@ -689,6 +689,9 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeHairInfo)) {
node = graph->create_node<HairInfoNode>();
}
+ else if (b_node.is_a(&RNA_ShaderNodePointInfo)) {
+ node = graph->create_node<PointInfoNode>();
+ }
else if (b_node.is_a(&RNA_ShaderNodeVolumeInfo)) {
node = graph->create_node<VolumeInfoNode>();
}
diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp
index 588e057b9ad..7e6f1535d66 100644
--- a/intern/cycles/blender/sync.cpp
+++ b/intern/cycles/blender/sync.cpp
@@ -787,6 +787,7 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
params.bvh_type = BVH_TYPE_DYNAMIC;
params.use_bvh_spatial_split = RNA_boolean_get(&cscene, "debug_use_spatial_splits");
+ params.use_bvh_compact_structure = RNA_boolean_get(&cscene, "debug_use_compact_bvh");
params.use_bvh_unaligned_nodes = RNA_boolean_get(&cscene, "debug_use_hair_bvh");
params.num_bvh_time_steps = RNA_int_get(&cscene, "debug_bvh_time_steps");
diff --git a/intern/cycles/bvh/embree.cpp b/intern/cycles/bvh/embree.cpp
index 618dd9438d5..616b6273e6a 100644
--- a/intern/cycles/bvh/embree.cpp
+++ b/intern/cycles/bvh/embree.cpp
@@ -66,6 +66,26 @@ static_assert(Object::MAX_MOTION_STEPS == Geometry::MAX_MOTION_STEPS,
* as well as filtering for volume objects happen here.
* Cycles' own BVH does that directly inside the traversal calls.
*/
+static void rtc_filter_intersection_func(const RTCFilterFunctionNArguments *args)
+{
+ /* Current implementation in Cycles assumes only single-ray intersection queries. */
+ assert(args->N == 1);
+
+ RTCHit *hit = (RTCHit *)args->hit;
+ CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt;
+ const KernelGlobalsCPU *kg = ctx->kg;
+ const Ray *cray = ctx->ray;
+
+ if (kernel_embree_is_self_intersection(kg, hit, cray)) {
+ *args->valid = 0;
+ }
+}
+
+/* This gets called by Embree at every valid ray/object intersection.
+ * Things like recording subsurface or shadow hits for later evaluation
+ * as well as filtering for volume objects happen here.
+ * Cycles' own BVH does that directly inside the traversal calls.
+ */
static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
{
/* Current implementation in Cycles assumes only single-ray intersection queries. */
@@ -75,12 +95,16 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
RTCHit *hit = (RTCHit *)args->hit;
CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt;
const KernelGlobalsCPU *kg = ctx->kg;
+ const Ray *cray = ctx->ray;
switch (ctx->type) {
case CCLIntersectContext::RAY_SHADOW_ALL: {
Intersection current_isect;
kernel_embree_convert_hit(kg, ray, hit, &current_isect);
-
+ if (intersection_skip_self_shadow(cray->self, current_isect.object, current_isect.prim)) {
+ *args->valid = 0;
+ return;
+ }
/* If no transparent shadows or max number of hits exceeded, all light is blocked. */
const int flags = intersection_get_shader_flags(kg, current_isect.prim, current_isect.type);
if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->num_hits >= ctx->max_hits) {
@@ -160,6 +184,10 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
break;
}
}
+ if (intersection_skip_self_local(cray->self, current_isect.prim)) {
+ *args->valid = 0;
+ return;
+ }
/* No intersection information requested, just return a hit. */
if (ctx->max_hits == 0) {
@@ -225,6 +253,11 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
if (ctx->num_hits < ctx->max_hits) {
Intersection current_isect;
kernel_embree_convert_hit(kg, ray, hit, &current_isect);
+ if (intersection_skip_self(cray->self, current_isect.object, current_isect.prim)) {
+ *args->valid = 0;
+ return;
+ }
+
Intersection *isect = &ctx->isect_s[ctx->num_hits];
++ctx->num_hits;
*isect = current_isect;
@@ -236,12 +269,15 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
}
/* This tells Embree to continue tracing. */
*args->valid = 0;
- break;
}
+ break;
}
case CCLIntersectContext::RAY_REGULAR:
default:
- /* Nothing to do here. */
+ if (kernel_embree_is_self_intersection(kg, hit, cray)) {
+ *args->valid = 0;
+ return;
+ }
break;
}
}
@@ -257,6 +293,14 @@ static void rtc_filter_func_backface_cull(const RTCFilterFunctionNArguments *arg
*args->valid = 0;
return;
}
+
+ CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt;
+ const KernelGlobalsCPU *kg = ctx->kg;
+ const Ray *cray = ctx->ray;
+
+ if (kernel_embree_is_self_intersection(kg, hit, cray)) {
+ *args->valid = 0;
+ }
}
static void rtc_filter_occluded_func_backface_cull(const RTCFilterFunctionNArguments *args)
@@ -355,10 +399,12 @@ void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_)
}
const bool dynamic = params.bvh_type == BVH_TYPE_DYNAMIC;
+ const bool compact = params.use_compact_structure;
scene = rtcNewScene(rtc_device);
const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) |
- RTC_SCENE_FLAG_COMPACT | RTC_SCENE_FLAG_ROBUST;
+ (compact ? RTC_SCENE_FLAG_COMPACT : RTC_SCENE_FLAG_NONE) |
+ RTC_SCENE_FLAG_ROBUST;
rtcSetSceneFlags(scene, scene_flags);
build_quality = dynamic ? RTC_BUILD_QUALITY_LOW :
(params.use_spatial_split ? RTC_BUILD_QUALITY_HIGH :
@@ -503,6 +549,7 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
+ rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_intersection_func);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
rtcCommitGeometry(geom_id);
@@ -765,6 +812,7 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
if (hair->curve_shape == CURVE_RIBBON) {
+ rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_intersection_func);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
}
else {
diff --git a/intern/cycles/bvh/params.h b/intern/cycles/bvh/params.h
index 16edf2e88e4..61fa5484ce0 100644
--- a/intern/cycles/bvh/params.h
+++ b/intern/cycles/bvh/params.h
@@ -97,6 +97,9 @@ class BVHParams {
*/
bool use_unaligned_nodes;
+ /* Use compact acceleration structure (Embree)*/
+ bool use_compact_structure;
+
/* Split time range to this number of steps and create leaf node for each
* of this time steps.
*
@@ -139,6 +142,7 @@ class BVHParams {
top_level = false;
bvh_layout = BVH_LAYOUT_BVH2;
+ use_compact_structure = true;
use_unaligned_nodes = false;
num_motion_curve_steps = 0;
diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake
index f46d18a4926..8d9631e5b44 100644
--- a/intern/cycles/cmake/external_libs.cmake
+++ b/intern/cycles/cmake/external_libs.cmake
@@ -559,10 +559,10 @@ if(WITH_CYCLES_DEVICE_METAL)
find_library(METAL_LIBRARY Metal)
# This file was added in the 12.0 SDK, use it as a way to detect the version.
- if (METAL_LIBRARY AND NOT EXISTS "${METAL_LIBRARY}/Headers/MTLFunctionStitching.h")
+ if(METAL_LIBRARY AND NOT EXISTS "${METAL_LIBRARY}/Headers/MTLFunctionStitching.h")
message(STATUS "Metal version too old, must be SDK 12.0 or newer, disabling WITH_CYCLES_DEVICE_METAL")
set(WITH_CYCLES_DEVICE_METAL OFF)
- elseif (NOT METAL_LIBRARY)
+ elseif(NOT METAL_LIBRARY)
message(STATUS "Metal not found, disabling WITH_CYCLES_DEVICE_METAL")
set(WITH_CYCLES_DEVICE_METAL OFF)
else()
diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp
index 4f1cbabc89b..85ed3dc5b55 100644
--- a/intern/cycles/device/hip/device_impl.cpp
+++ b/intern/cycles/device/hip/device_impl.cpp
@@ -905,8 +905,8 @@ void HIPDevice::tex_alloc(device_texture &mem)
address_mode = hipAddressModeClamp;
break;
case EXTENSION_CLIP:
- // TODO : (Arya) setting this to Mode Clamp instead of Mode Border because it's unsupported
- // in hip
+ /* TODO(@arya): setting this to Mode Clamp instead of Mode Border
+ * because it's unsupported in HIP. */
address_mode = hipAddressModeClamp;
break;
default:
diff --git a/intern/cycles/device/metal/bvh.h b/intern/cycles/device/metal/bvh.h
index cbc5ca7d2c3..58d71e3928f 100644
--- a/intern/cycles/device/metal/bvh.h
+++ b/intern/cycles/device/metal/bvh.h
@@ -58,6 +58,11 @@ class BVHMetal : public BVH {
id<MTLCommandQueue> queue,
Geometry *const geom,
bool refit);
+ bool build_BLAS_pointcloud(Progress &progress,
+ id<MTLDevice> device,
+ id<MTLCommandQueue> queue,
+ Geometry *const geom,
+ bool refit);
bool build_TLAS(Progress &progress, id<MTLDevice> device, id<MTLCommandQueue> queue, bool refit);
};
diff --git a/intern/cycles/device/metal/bvh.mm b/intern/cycles/device/metal/bvh.mm
index 1953102cb41..8b252f1a5ec 100644
--- a/intern/cycles/device/metal/bvh.mm
+++ b/intern/cycles/device/metal/bvh.mm
@@ -19,6 +19,7 @@
# include "scene/hair.h"
# include "scene/mesh.h"
# include "scene/object.h"
+# include "scene/pointcloud.h"
# include "util/progress.h"
@@ -475,6 +476,220 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
return false;
}
+bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
+ id<MTLDevice> device,
+ id<MTLCommandQueue> queue,
+ Geometry *const geom,
+ bool refit)
+{
+ if (@available(macos 12.0, *)) {
+ /* Build BLAS for point cloud */
+ PointCloud *pointcloud = static_cast<PointCloud *>(geom);
+ if (pointcloud->num_points() == 0) {
+ return false;
+ }
+
+ /*------------------------------------------------*/
+ BVH_status("Building pointcloud BLAS | %7d points | %s",
+ (int)pointcloud->num_points(),
+ geom->name.c_str());
+ /*------------------------------------------------*/
+
+ const size_t num_points = pointcloud->get_points().size();
+ const float3 *points = pointcloud->get_points().data();
+ const float *radius = pointcloud->get_radius().data();
+
+ const bool use_fast_trace_bvh = (params.bvh_type == BVH_TYPE_STATIC);
+
+ size_t num_motion_steps = 1;
+ Attribute *motion_keys = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+ if (motion_blur && pointcloud->get_use_motion_blur() && motion_keys) {
+ num_motion_steps = pointcloud->get_motion_steps();
+ }
+
+ const size_t num_aabbs = num_motion_steps;
+
+ MTLResourceOptions storage_mode;
+ if (device.hasUnifiedMemory) {
+ storage_mode = MTLResourceStorageModeShared;
+ }
+ else {
+ storage_mode = MTLResourceStorageModeManaged;
+ }
+
+ /* Allocate a GPU buffer for the AABB data and populate it */
+ id<MTLBuffer> aabbBuf = [device
+ newBufferWithLength:num_aabbs * sizeof(MTLAxisAlignedBoundingBox)
+ options:storage_mode];
+ MTLAxisAlignedBoundingBox *aabb_data = (MTLAxisAlignedBoundingBox *)[aabbBuf contents];
+
+ /* Get AABBs for each motion step */
+ size_t center_step = (num_motion_steps - 1) / 2;
+ for (size_t step = 0; step < num_motion_steps; ++step) {
+ /* The center step for motion vertices is not stored in the attribute */
+ if (step != center_step) {
+ size_t attr_offset = (step > center_step) ? step - 1 : step;
+ points = motion_keys->data_float3() + attr_offset * num_points;
+ }
+
+ for (size_t j = 0; j < num_points; ++j) {
+ const PointCloud::Point point = pointcloud->get_point(j);
+ BoundBox bounds = BoundBox::empty;
+ point.bounds_grow(points, radius, bounds);
+
+ const size_t index = step * num_points + j;
+ aabb_data[index].min = (MTLPackedFloat3 &)bounds.min;
+ aabb_data[index].max = (MTLPackedFloat3 &)bounds.max;
+ }
+ }
+
+ if (storage_mode == MTLResourceStorageModeManaged) {
+ [aabbBuf didModifyRange:NSMakeRange(0, aabbBuf.length)];
+ }
+
+# if 0
+ for (size_t i=0; i<num_aabbs && i < 400; i++) {
+ MTLAxisAlignedBoundingBox& bb = aabb_data[i];
+ printf(" %d: %.1f,%.1f,%.1f -- %.1f,%.1f,%.1f\n", int(i), bb.min.x, bb.min.y, bb.min.z, bb.max.x, bb.max.y, bb.max.z);
+ }
+# endif
+
+ MTLAccelerationStructureGeometryDescriptor *geomDesc;
+ if (motion_blur) {
+ std::vector<MTLMotionKeyframeData *> aabb_ptrs;
+ aabb_ptrs.reserve(num_motion_steps);
+ for (size_t step = 0; step < num_motion_steps; ++step) {
+ MTLMotionKeyframeData *k = [MTLMotionKeyframeData data];
+ k.buffer = aabbBuf;
+ k.offset = step * num_points * sizeof(MTLAxisAlignedBoundingBox);
+ aabb_ptrs.push_back(k);
+ }
+
+ MTLAccelerationStructureMotionBoundingBoxGeometryDescriptor *geomDescMotion =
+ [MTLAccelerationStructureMotionBoundingBoxGeometryDescriptor descriptor];
+ geomDescMotion.boundingBoxBuffers = [NSArray arrayWithObjects:aabb_ptrs.data()
+ count:aabb_ptrs.size()];
+ geomDescMotion.boundingBoxCount = num_points;
+ geomDescMotion.boundingBoxStride = sizeof(aabb_data[0]);
+ geomDescMotion.intersectionFunctionTableOffset = 2;
+
+ /* Force a single any-hit call, so shadow record-all behavior works correctly */
+ /* (Match optix behavior: unsigned int build_flags =
+ * OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL;) */
+ geomDescMotion.allowDuplicateIntersectionFunctionInvocation = false;
+ geomDescMotion.opaque = true;
+ geomDesc = geomDescMotion;
+ }
+ else {
+ MTLAccelerationStructureBoundingBoxGeometryDescriptor *geomDescNoMotion =
+ [MTLAccelerationStructureBoundingBoxGeometryDescriptor descriptor];
+ geomDescNoMotion.boundingBoxBuffer = aabbBuf;
+ geomDescNoMotion.boundingBoxBufferOffset = 0;
+ geomDescNoMotion.boundingBoxCount = int(num_aabbs);
+ geomDescNoMotion.boundingBoxStride = sizeof(aabb_data[0]);
+ geomDescNoMotion.intersectionFunctionTableOffset = 2;
+
+ /* Force a single any-hit call, so shadow record-all behavior works correctly */
+ /* (Match optix behavior: unsigned int build_flags =
+ * OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL;) */
+ geomDescNoMotion.allowDuplicateIntersectionFunctionInvocation = false;
+ geomDescNoMotion.opaque = true;
+ geomDesc = geomDescNoMotion;
+ }
+
+ MTLPrimitiveAccelerationStructureDescriptor *accelDesc =
+ [MTLPrimitiveAccelerationStructureDescriptor descriptor];
+ accelDesc.geometryDescriptors = @[ geomDesc ];
+
+ if (motion_blur) {
+ accelDesc.motionStartTime = 0.0f;
+ accelDesc.motionEndTime = 1.0f;
+ accelDesc.motionStartBorderMode = MTLMotionBorderModeVanish;
+ accelDesc.motionEndBorderMode = MTLMotionBorderModeVanish;
+ accelDesc.motionKeyframeCount = num_motion_steps;
+ }
+
+ if (!use_fast_trace_bvh) {
+ accelDesc.usage |= (MTLAccelerationStructureUsageRefit |
+ MTLAccelerationStructureUsagePreferFastBuild);
+ }
+
+ MTLAccelerationStructureSizes accelSizes = [device
+ accelerationStructureSizesWithDescriptor:accelDesc];
+ id<MTLAccelerationStructure> accel_uncompressed = [device
+ newAccelerationStructureWithSize:accelSizes.accelerationStructureSize];
+ id<MTLBuffer> scratchBuf = [device newBufferWithLength:accelSizes.buildScratchBufferSize
+ options:MTLResourceStorageModePrivate];
+ id<MTLBuffer> sizeBuf = [device newBufferWithLength:8 options:MTLResourceStorageModeShared];
+ id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
+ id<MTLAccelerationStructureCommandEncoder> accelEnc =
+ [accelCommands accelerationStructureCommandEncoder];
+ if (refit) {
+ [accelEnc refitAccelerationStructure:accel_struct
+ descriptor:accelDesc
+ destination:accel_uncompressed
+ scratchBuffer:scratchBuf
+ scratchBufferOffset:0];
+ }
+ else {
+ [accelEnc buildAccelerationStructure:accel_uncompressed
+ descriptor:accelDesc
+ scratchBuffer:scratchBuf
+ scratchBufferOffset:0];
+ }
+ if (use_fast_trace_bvh) {
+ [accelEnc writeCompactedAccelerationStructureSize:accel_uncompressed
+ toBuffer:sizeBuf
+ offset:0
+ sizeDataType:MTLDataTypeULong];
+ }
+ [accelEnc endEncoding];
+ [accelCommands addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) {
+ /* free temp resources */
+ [scratchBuf release];
+ [aabbBuf release];
+
+ if (use_fast_trace_bvh) {
+ /* Compact the accel structure */
+ uint64_t compressed_size = *(uint64_t *)sizeBuf.contents;
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
+ id<MTLAccelerationStructureCommandEncoder> accelEnc =
+ [accelCommands accelerationStructureCommandEncoder];
+ id<MTLAccelerationStructure> accel = [device
+ newAccelerationStructureWithSize:compressed_size];
+ [accelEnc copyAndCompactAccelerationStructure:accel_uncompressed
+ toAccelerationStructure:accel];
+ [accelEnc endEncoding];
+ [accelCommands addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) {
+ uint64_t allocated_size = [accel allocatedSize];
+ stats.mem_alloc(allocated_size);
+ accel_struct = accel;
+ [accel_uncompressed release];
+ accel_struct_building = false;
+ }];
+ [accelCommands commit];
+ });
+ }
+ else {
+ /* set our acceleration structure to the uncompressed structure */
+ accel_struct = accel_uncompressed;
+
+ uint64_t allocated_size = [accel_struct allocatedSize];
+ stats.mem_alloc(allocated_size);
+ accel_struct_building = false;
+ }
+ [sizeBuf release];
+ }];
+
+ accel_struct_building = true;
+ [accelCommands commit];
+ return true;
+ }
+ return false;
+}
+
bool BVHMetal::build_BLAS(Progress &progress,
id<MTLDevice> device,
id<MTLCommandQueue> queue,
@@ -491,6 +706,8 @@ bool BVHMetal::build_BLAS(Progress &progress,
return build_BLAS_mesh(progress, device, queue, geom, refit);
case Geometry::HAIR:
return build_BLAS_hair(progress, device, queue, geom, refit);
+ case Geometry::POINTCLOUD:
+ return build_BLAS_pointcloud(progress, device, queue, geom, refit);
default:
return false;
}
diff --git a/intern/cycles/device/metal/kernel.h b/intern/cycles/device/metal/kernel.h
index 4874af1bfa6..a4bfb30436d 100644
--- a/intern/cycles/device/metal/kernel.h
+++ b/intern/cycles/device/metal/kernel.h
@@ -36,6 +36,8 @@ enum {
METALRT_FUNC_CURVE_RIBBON_SHADOW,
METALRT_FUNC_CURVE_ALL,
METALRT_FUNC_CURVE_ALL_SHADOW,
+ METALRT_FUNC_POINT,
+ METALRT_FUNC_POINT_SHADOW,
METALRT_FUNC_NUM
};
diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm
index f948a8a0a0f..e9bd1cea5df 100644
--- a/intern/cycles/device/metal/kernel.mm
+++ b/intern/cycles/device/metal/kernel.mm
@@ -358,6 +358,8 @@ bool MetalDeviceKernels::load(MetalDevice *device, int kernel_type)
"__intersection__curve_ribbon_shadow",
"__intersection__curve_all",
"__intersection__curve_all_shadow",
+ "__intersection__point",
+ "__intersection__point_shadow",
};
assert(sizeof(function_names) / sizeof(function_names[0]) == METALRT_FUNC_NUM);
@@ -400,37 +402,51 @@ bool MetalDeviceKernels::load(MetalDevice *device, int kernel_type)
NSArray *function_list = nil;
if (device->use_metalrt) {
- id<MTLFunction> box_intersect_default = nil;
- id<MTLFunction> box_intersect_shadow = nil;
+ id<MTLFunction> curve_intersect_default = nil;
+ id<MTLFunction> curve_intersect_shadow = nil;
+ id<MTLFunction> point_intersect_default = nil;
+ id<MTLFunction> point_intersect_shadow = nil;
if (device->kernel_features & KERNEL_FEATURE_HAIR) {
/* Add curve intersection programs. */
if (device->kernel_features & KERNEL_FEATURE_HAIR_THICK) {
/* Slower programs for thick hair since that also slows down ribbons.
* Ideally this should not be needed. */
- box_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL];
- box_intersect_shadow = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL_SHADOW];
+ curve_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL];
+ curve_intersect_shadow =
+ rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL_SHADOW];
}
else {
- box_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON];
- box_intersect_shadow =
+ curve_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON];
+ curve_intersect_shadow =
rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON_SHADOW];
}
}
+ if (device->kernel_features & KERNEL_FEATURE_POINTCLOUD) {
+ point_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_POINT];
+ point_intersect_shadow = rt_intersection_funcs[kernel_type][METALRT_FUNC_POINT_SHADOW];
+ }
table_functions[METALRT_TABLE_DEFAULT] = [NSArray
arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_TRI],
- box_intersect_default ?
- box_intersect_default :
+ curve_intersect_default ?
+ curve_intersect_default :
+ rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_BOX],
+ point_intersect_default ?
+ point_intersect_default :
rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_BOX],
nil];
table_functions[METALRT_TABLE_SHADOW] = [NSArray
arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_TRI],
- box_intersect_shadow ?
- box_intersect_shadow :
+ curve_intersect_shadow ?
+ curve_intersect_shadow :
+ rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_BOX],
+ point_intersect_shadow ?
+ point_intersect_shadow :
rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_BOX],
nil];
table_functions[METALRT_TABLE_LOCAL] = [NSArray
arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_TRI],
rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_BOX],
+ rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_BOX],
nil];
NSMutableSet *unique_functions = [NSMutableSet
diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp
index 009661b2dec..cb6c36d5ea6 100644
--- a/intern/cycles/device/optix/device_impl.cpp
+++ b/intern/cycles/device/optix/device_impl.cpp
@@ -226,7 +226,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
pipeline_options.usesMotionBlur = false;
pipeline_options.traversableGraphFlags =
OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING;
- pipeline_options.numPayloadValues = 6;
+ pipeline_options.numPayloadValues = 8;
pipeline_options.numAttributeValues = 2; /* u, v */
pipeline_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE;
pipeline_options.pipelineLaunchParamsVariableName = "__params"; /* See globals.h */
diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp
index 2a5f99f358b..28cdeeb630a 100644
--- a/intern/cycles/integrator/denoiser.cpp
+++ b/intern/cycles/integrator/denoiser.cpp
@@ -125,20 +125,41 @@ static Device *find_best_device(Device *device, DenoiserType type)
return best_device;
}
+static DeviceInfo find_best_denoiser_device_info(const vector<DeviceInfo> &device_infos,
+ DenoiserType denoiser_type)
+{
+ for (const DeviceInfo &device_info : device_infos) {
+ if ((device_info.denoisers & denoiser_type) == 0) {
+ continue;
+ }
+
+ /* TODO(sergey): Use one of the already configured devices, so that OptiX denoising can happen
+ * on a physical CUDA device which is already used for rendering. */
+
+ /* TODO(sergey): Choose fastest device for denoising. */
+
+ return device_info;
+ }
+
+ DeviceInfo none_device;
+ none_device.type = DEVICE_NONE;
+ return none_device;
+}
+
static unique_ptr<Device> create_denoiser_device(Device *path_trace_device,
- const uint device_type_mask)
+ const uint device_type_mask,
+ DenoiserType denoiser_type)
{
const vector<DeviceInfo> device_infos = Device::available_devices(device_type_mask);
if (device_infos.empty()) {
return nullptr;
}
- /* TODO(sergey): Use one of the already configured devices, so that OptiX denoising can happen on
- * a physical CUDA device which is already used for rendering. */
-
- /* TODO(sergey): Choose fastest device for denoising. */
-
- const DeviceInfo denoiser_device_info = device_infos.front();
+ const DeviceInfo denoiser_device_info = find_best_denoiser_device_info(device_infos,
+ denoiser_type);
+ if (denoiser_device_info.type == DEVICE_NONE) {
+ return nullptr;
+ }
unique_ptr<Device> denoiser_device(
Device::create(denoiser_device_info, path_trace_device->stats, path_trace_device->profiler));
@@ -186,7 +207,8 @@ Device *Denoiser::ensure_denoiser_device(Progress *progress)
device_creation_attempted_ = true;
const uint device_type_mask = get_device_type_mask();
- local_denoiser_device_ = create_denoiser_device(path_trace_device_, device_type_mask);
+ local_denoiser_device_ = create_denoiser_device(
+ path_trace_device_, device_type_mask, params_.type);
denoiser_device_ = local_denoiser_device_.get();
return denoiser_device_;
diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp
index a08aec513fc..4676e69c4fb 100644
--- a/intern/cycles/integrator/denoiser_oidn.cpp
+++ b/intern/cycles/integrator/denoiser_oidn.cpp
@@ -37,8 +37,6 @@ OIDNDenoiser::OIDNDenoiser(Device *path_trace_device, const DenoiseParams &param
: Denoiser(path_trace_device, params)
{
DCHECK_EQ(params.type, DENOISER_OPENIMAGEDENOISE);
-
- DCHECK(openimagedenoise_supported()) << "OpenImageDenoiser is not supported on this platform.";
}
#ifdef WITH_OPENIMAGEDENOISE
@@ -585,6 +583,9 @@ bool OIDNDenoiser::denoise_buffer(const BufferParams &buffer_params,
const int num_samples,
bool allow_inplace_modification)
{
+ DCHECK(openimagedenoise_supported())
+ << "OpenImageDenoiser is not supported on this platform or build.";
+
#ifdef WITH_OPENIMAGEDENOISE
thread_scoped_lock lock(mutex_);
@@ -635,4 +636,20 @@ uint OIDNDenoiser::get_device_type_mask() const
return DEVICE_MASK_CPU;
}
+Device *OIDNDenoiser::ensure_denoiser_device(Progress *progress)
+{
+#ifndef WITH_OPENIMAGEDENOISE
+ path_trace_device_->set_error("Build without OpenImageDenoiser");
+ return nullptr;
+#else
+ if (!openimagedenoise_supported()) {
+ path_trace_device_->set_error(
+ "OpenImageDenoiser is not supported on this CPU: missing SSE 4.1 support");
+ return nullptr;
+ }
+
+ return Denoiser::ensure_denoiser_device(progress);
+#endif
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/integrator/denoiser_oidn.h b/intern/cycles/integrator/denoiser_oidn.h
index a0ec3e26b9c..2b815be973e 100644
--- a/intern/cycles/integrator/denoiser_oidn.h
+++ b/intern/cycles/integrator/denoiser_oidn.h
@@ -38,6 +38,7 @@ class OIDNDenoiser : public Denoiser {
protected:
virtual uint get_device_type_mask() const override;
+ virtual Device *ensure_denoiser_device(Progress *progress) override;
/* We only perform one denoising at a time, since OpenImageDenoise itself is multithreaded.
* Use this mutex whenever images are passed to the OIDN and needs to be denoised. */
diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp
index 4479442df56..9fa5aab9ea9 100644
--- a/intern/cycles/integrator/pass_accessor.cpp
+++ b/intern/cycles/integrator/pass_accessor.cpp
@@ -141,6 +141,7 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
const PassType type = pass_access_info_.type;
const PassMode mode = pass_access_info_.mode;
const PassInfo pass_info = Pass::get_info(type, pass_access_info_.include_albedo);
+ int num_written_components = pass_info.num_components;
if (pass_info.num_components == 1) {
/* Single channel passes. */
@@ -188,8 +189,10 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
else if ((pass_info.divide_type != PASS_NONE || pass_info.direct_type != PASS_NONE ||
pass_info.indirect_type != PASS_NONE) &&
mode != PassMode::DENOISED) {
- /* RGB lighting passes that need to divide out color and/or sum direct and indirect. */
+ /* RGB lighting passes that need to divide out color and/or sum direct and indirect.
+ * These can also optionally write alpha like the combined pass. */
get_pass_light_path(render_buffers, buffer_params, destination);
+ num_written_components = 4;
}
else {
/* Passes that need no special computation, or denoised passes that already
@@ -215,7 +218,7 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
}
}
- pad_pixels(buffer_params, destination, pass_info.num_components);
+ pad_pixels(buffer_params, destination, num_written_components);
return true;
}
diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp
index 0b55d1078a8..fd697836f52 100644
--- a/intern/cycles/integrator/path_trace.cpp
+++ b/intern/cycles/integrator/path_trace.cpp
@@ -820,8 +820,15 @@ void PathTrace::tile_buffer_read()
return;
}
+ /* Read buffers back from device. */
+ tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
+ path_trace_work->copy_render_buffers_from_device();
+ });
+
+ /* Read (subset of) passes from output driver. */
PathTraceTile tile(*this);
if (output_driver_->read_render_tile(tile)) {
+ /* Copy buffers to device again. */
tbb::parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) {
path_trace_work->copy_render_buffers_to_device();
});
diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp
index 95a1adeb016..0edd3810c39 100644
--- a/intern/cycles/integrator/shader_eval.cpp
+++ b/intern/cycles/integrator/shader_eval.cpp
@@ -157,7 +157,7 @@ bool ShaderEval::eval_gpu(Device *device,
queue->init_execution();
/* Execute work on GPU in chunk, so we can cancel.
- * TODO : query appropriate size from device.*/
+ * TODO: query appropriate size from device. */
const int32_t chunk_size = 65536;
device_ptr d_input = input.device_pointer;
diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h
index 67804fb1d0d..1797bf60720 100644
--- a/intern/cycles/kernel/bvh/bvh.h
+++ b/intern/cycles/kernel/bvh/bvh.h
@@ -173,15 +173,16 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
uint p3 = 0;
uint p4 = visibility;
uint p5 = PRIMITIVE_NONE;
+ uint p6 = ((uint64_t)ray) & 0xFFFFFFFF;
+ uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF;
uint ray_mask = visibility & 0xFF;
- uint ray_flags = OPTIX_RAY_FLAG_NONE;
+ uint ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
- ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
}
else if (visibility & PATH_RAY_SHADOW_OPAQUE) {
- ray_flags = OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
+ ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
}
optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0,
@@ -200,7 +201,9 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
p2,
p3,
p4,
- p5);
+ p5,
+ p6,
+ p7);
isect->t = __uint_as_float(p0);
isect->u = __uint_as_float(p1);
@@ -242,6 +245,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
}
MetalRTIntersectionPayload payload;
+ payload.self = ray->self;
payload.u = 0.0f;
payload.v = 0.0f;
payload.visibility = visibility;
@@ -309,6 +313,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_REGULAR);
IntersectContext rtc_ctx(&ctx);
RTCRayHit ray_hit;
+ ctx.ray = ray;
kernel_embree_setup_rayhit(*ray, ray_hit, visibility);
rtcIntersect1(kernel_data.bvh.scene, &rtc_ctx.context, &ray_hit);
if (ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID &&
@@ -356,6 +361,9 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
uint p2 = pointer_pack_to_uint_0(local_isect);
uint p3 = pointer_pack_to_uint_1(local_isect);
uint p4 = local_object;
+ uint p6 = ((uint64_t)ray) & 0xFFFFFFFF;
+ uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF;
+
/* Is set to zero on miss or if ray is aborted, so can be used as return value. */
uint p5 = max_hits;
@@ -379,7 +387,9 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
p2,
p3,
p4,
- p5);
+ p5,
+ p6,
+ p7);
return p5;
# elif defined(__METALRT__)
@@ -417,6 +427,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
}
MetalRTIntersectionLocalPayload payload;
+ payload.self = ray->self;
payload.local_object = local_object;
payload.max_hits = max_hits;
payload.local_isect.num_hits = 0;
@@ -460,6 +471,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
kg, has_bvh ? CCLIntersectContext::RAY_SSS : CCLIntersectContext::RAY_LOCAL);
ctx.lcg_state = lcg_state;
ctx.max_hits = max_hits;
+ ctx.ray = ray;
ctx.local_isect = local_isect;
if (local_isect) {
local_isect->num_hits = 0;
@@ -532,6 +544,8 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
uint p3 = max_hits;
uint p4 = visibility;
uint p5 = false;
+ uint p6 = ((uint64_t)ray) & 0xFFFFFFFF;
+ uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF;
uint ray_mask = visibility & 0xFF;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
@@ -555,7 +569,9 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
p2,
p3,
p4,
- p5);
+ p5,
+ p6,
+ p7);
*num_recorded_hits = uint16_unpack_from_uint_0(p2);
*throughput = __uint_as_float(p1);
@@ -588,6 +604,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
}
MetalRTIntersectionShadowPayload payload;
+ payload.self = ray->self;
payload.visibility = visibility;
payload.max_hits = max_hits;
payload.num_hits = 0;
@@ -634,6 +651,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
Intersection *isect_array = (Intersection *)state->shadow_isect;
ctx.isect_s = isect_array;
ctx.max_hits = max_hits;
+ ctx.ray = ray;
IntersectContext rtc_ctx(&ctx);
RTCRay rtc_ray;
kernel_embree_setup_ray(*ray, rtc_ray, visibility);
@@ -685,6 +703,8 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
uint p3 = 0;
uint p4 = visibility;
uint p5 = PRIMITIVE_NONE;
+ uint p6 = ((uint64_t)ray) & 0xFFFFFFFF;
+ uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF;
uint ray_mask = visibility & 0xFF;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
@@ -708,7 +728,9 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
p2,
p3,
p4,
- p5);
+ p5,
+ p6,
+ p7);
isect->t = __uint_as_float(p0);
isect->u = __uint_as_float(p1);
@@ -744,6 +766,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
}
MetalRTIntersectionPayload payload;
+ payload.self = ray->self;
payload.visibility = visibility;
typename metalrt_intersector_type::result_type intersection;
@@ -820,6 +843,7 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg,
ctx.isect_s = isect;
ctx.max_hits = max_hits;
ctx.num_hits = 0;
+ ctx.ray = ray;
IntersectContext rtc_ctx(&ctx);
RTCRay rtc_ray;
kernel_embree_setup_ray(*ray, rtc_ray, visibility);
diff --git a/intern/cycles/kernel/bvh/embree.h b/intern/cycles/kernel/bvh/embree.h
index 9edd4f90a7e..19c4b9f6f3d 100644
--- a/intern/cycles/kernel/bvh/embree.h
+++ b/intern/cycles/kernel/bvh/embree.h
@@ -22,6 +22,8 @@
#include "kernel/device/cpu/compat.h"
#include "kernel/device/cpu/globals.h"
+#include "kernel/bvh/util.h"
+
#include "util/vector.h"
CCL_NAMESPACE_BEGIN
@@ -38,6 +40,9 @@ struct CCLIntersectContext {
KernelGlobals kg;
RayType type;
+ /* For avoiding self intersections */
+ const Ray *ray;
+
/* for shadow rays */
Intersection *isect_s;
uint max_hits;
@@ -56,6 +61,7 @@ struct CCLIntersectContext {
{
kg = kg_;
type = type_;
+ ray = NULL;
max_hits = 1;
num_hits = 0;
num_recorded_hits = 0;
@@ -102,7 +108,34 @@ ccl_device_inline void kernel_embree_setup_rayhit(const Ray &ray,
{
kernel_embree_setup_ray(ray, rayhit.ray, visibility);
rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
- rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID;
+ rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
+}
+
+ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg,
+ const RTCHit *hit,
+ const Ray *ray)
+{
+ bool status = false;
+ if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) {
+ const int oID = hit->instID[0] / 2;
+ if ((ray->self.object == oID) || (ray->self.light_object == oID)) {
+ RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
+ rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0]));
+ const int pID = hit->primID +
+ (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
+ status = intersection_skip_self_shadow(ray->self, oID, pID);
+ }
+ }
+ else {
+ const int oID = hit->geomID / 2;
+ if ((ray->self.object == oID) || (ray->self.light_object == oID)) {
+ const int pID = hit->primID + (intptr_t)rtcGetGeometryUserData(
+ rtcGetGeometry(kernel_data.bvh.scene, hit->geomID));
+ status = intersection_skip_self_shadow(ray->self, oID, pID);
+ }
+ }
+
+ return status;
}
ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg,
diff --git a/intern/cycles/kernel/bvh/local.h b/intern/cycles/kernel/bvh/local.h
index 4d0e6aac901..4ef6deef98d 100644
--- a/intern/cycles/kernel/bvh/local.h
+++ b/intern/cycles/kernel/bvh/local.h
@@ -157,7 +157,11 @@ ccl_device_inline
}
}
+ /* Skip self intersection. */
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ if (intersection_skip_self_local(ray->self, prim)) {
+ continue;
+ }
if (triangle_intersect_local(kg,
local_isect,
@@ -188,7 +192,11 @@ ccl_device_inline
}
}
+ /* Skip self intersection. */
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ if (intersection_skip_self_local(ray->self, prim)) {
+ continue;
+ }
if (motion_triangle_intersect_local(kg,
local_isect,
diff --git a/intern/cycles/kernel/bvh/metal.h b/intern/cycles/kernel/bvh/metal.h
index 55456d15f50..5ab413d9314 100644
--- a/intern/cycles/kernel/bvh/metal.h
+++ b/intern/cycles/kernel/bvh/metal.h
@@ -15,6 +15,7 @@
*/
struct MetalRTIntersectionPayload {
+ RaySelfPrimitives self;
uint visibility;
float u, v;
int prim;
@@ -25,6 +26,7 @@ struct MetalRTIntersectionPayload {
};
struct MetalRTIntersectionLocalPayload {
+ RaySelfPrimitives self;
uint local_object;
uint lcg_state;
short max_hits;
@@ -34,6 +36,7 @@ struct MetalRTIntersectionLocalPayload {
};
struct MetalRTIntersectionShadowPayload {
+ RaySelfPrimitives self;
uint visibility;
#if defined(__METALRT_MOTION__)
float time;
diff --git a/intern/cycles/kernel/bvh/shadow_all.h b/intern/cycles/kernel/bvh/shadow_all.h
index 0fb86bfda77..59a7ba63045 100644
--- a/intern/cycles/kernel/bvh/shadow_all.h
+++ b/intern/cycles/kernel/bvh/shadow_all.h
@@ -160,6 +160,9 @@ ccl_device_inline
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ if (intersection_skip_self_shadow(ray->self, prim_object, prim)) {
+ continue;
+ }
switch (type & PRIMITIVE_ALL) {
case PRIMITIVE_TRIANGLE: {
diff --git a/intern/cycles/kernel/bvh/traversal.h b/intern/cycles/kernel/bvh/traversal.h
index dc2d1422df6..17cd357a069 100644
--- a/intern/cycles/kernel/bvh/traversal.h
+++ b/intern/cycles/kernel/bvh/traversal.h
@@ -133,35 +133,29 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
--stack_ptr;
/* primitive intersection */
- switch (type & PRIMITIVE_ALL) {
- case PRIMITIVE_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
-
- const int prim_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ for (; prim_addr < prim_addr2; prim_addr++) {
+ kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
+
+ const int prim_object = (object == OBJECT_NONE) ?
+ kernel_tex_fetch(__prim_object, prim_addr) :
+ object;
+ const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ if (intersection_skip_self_shadow(ray->self, prim_object, prim)) {
+ continue;
+ }
+ switch (type & PRIMITIVE_ALL) {
+ case PRIMITIVE_TRIANGLE: {
if (triangle_intersect(
kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr)) {
/* shadow ray early termination */
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
}
+ break;
}
- break;
- }
#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
-
- const int prim_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- const int prim = kernel_tex_fetch(__prim_index, prim_addr);
-
+ case PRIMITIVE_MOTION_TRIANGLE: {
if (motion_triangle_intersect(kg,
isect,
P,
@@ -176,28 +170,21 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
}
+ break;
}
- break;
- }
#endif /* BVH_FEATURE(BVH_MOTION) */
#if BVH_FEATURE(BVH_HAIR)
- case PRIMITIVE_CURVE_THICK:
- case PRIMITIVE_MOTION_CURVE_THICK:
- case PRIMITIVE_CURVE_RIBBON:
- case PRIMITIVE_MOTION_CURVE_RIBBON: {
- for (; prim_addr < prim_addr2; prim_addr++) {
+ case PRIMITIVE_CURVE_THICK:
+ case PRIMITIVE_MOTION_CURVE_THICK:
+ case PRIMITIVE_CURVE_RIBBON:
+ case PRIMITIVE_MOTION_CURVE_RIBBON: {
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr);
if (ray->time < prim_time.x || ray->time > prim_time.y) {
- continue;
+ break;
}
}
- const int prim_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- const int prim = kernel_tex_fetch(__prim_index, prim_addr);
-
const int curve_type = kernel_tex_fetch(__prim_type, prim_addr);
const bool hit = curve_intersect(
kg, isect, P, dir, isect->t, prim_object, prim, ray->time, curve_type);
@@ -206,26 +193,19 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
}
+ break;
}
- break;
- }
#endif /* BVH_FEATURE(BVH_HAIR) */
#if BVH_FEATURE(BVH_POINTCLOUD)
- case PRIMITIVE_POINT:
- case PRIMITIVE_MOTION_POINT: {
- for (; prim_addr < prim_addr2; prim_addr++) {
+ case PRIMITIVE_POINT:
+ case PRIMITIVE_MOTION_POINT: {
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr);
if (ray->time < prim_time.x || ray->time > prim_time.y) {
- continue;
+ break;
}
}
- const int prim_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- const int prim = kernel_tex_fetch(__prim_index, prim_addr);
-
const int point_type = kernel_tex_fetch(__prim_type, prim_addr);
const bool hit = point_intersect(
kg, isect, P, dir, isect->t, prim_object, prim, ray->time, point_type);
@@ -234,10 +214,10 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
}
+ break;
}
- break;
- }
#endif /* BVH_FEATURE(BVH_POINTCLOUD) */
+ }
}
}
else {
diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h
index bd79c6e19c6..39c3ecd78c0 100644
--- a/intern/cycles/kernel/bvh/util.h
+++ b/intern/cycles/kernel/bvh/util.h
@@ -21,54 +21,22 @@ CCL_NAMESPACE_BEGIN
/* Ray offset to avoid self intersection.
*
* This function should be used to compute a modified ray start position for
- * rays leaving from a surface. */
-
+ * rays leaving from a surface. This is from "A Fast and Robust Method for Avoiding
+ * Self-Intersection" see https://research.nvidia.com/publication/2019-03_A-Fast-and
+ */
ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
{
-#ifdef __INTERSECTION_REFINE__
- const float epsilon_f = 1e-5f;
- /* ideally this should match epsilon_f, but instancing and motion blur
- * precision makes it problematic */
- const float epsilon_test = 1.0f;
- const int epsilon_i = 32;
-
- float3 res;
-
- /* x component */
- if (fabsf(P.x) < epsilon_test) {
- res.x = P.x + Ng.x * epsilon_f;
- }
- else {
- uint ix = __float_as_uint(P.x);
- ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
- res.x = __uint_as_float(ix);
- }
-
- /* y component */
- if (fabsf(P.y) < epsilon_test) {
- res.y = P.y + Ng.y * epsilon_f;
- }
- else {
- uint iy = __float_as_uint(P.y);
- iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
- res.y = __uint_as_float(iy);
- }
-
- /* z component */
- if (fabsf(P.z) < epsilon_test) {
- res.z = P.z + Ng.z * epsilon_f;
- }
- else {
- uint iz = __float_as_uint(P.z);
- iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
- res.z = __uint_as_float(iz);
- }
-
- return res;
-#else
- const float epsilon_f = 1e-4f;
- return P + epsilon_f * Ng;
-#endif
+ const float int_scale = 256.0f;
+ int3 of_i = make_int3((int)(int_scale * Ng.x), (int)(int_scale * Ng.y), (int)(int_scale * Ng.z));
+
+ float3 p_i = make_float3(__int_as_float(__float_as_int(P.x) + ((P.x < 0) ? -of_i.x : of_i.x)),
+ __int_as_float(__float_as_int(P.y) + ((P.y < 0) ? -of_i.y : of_i.y)),
+ __int_as_float(__float_as_int(P.z) + ((P.z < 0) ? -of_i.z : of_i.z)));
+ const float origin = 1.0f / 32.0f;
+ const float float_scale = 1.0f / 65536.0f;
+ return make_float3(fabsf(P.x) < origin ? P.x + float_scale * Ng.x : p_i.x,
+ fabsf(P.y) < origin ? P.y + float_scale * Ng.y : p_i.y,
+ fabsf(P.z) < origin ? P.z + float_scale * Ng.z : p_i.z);
}
#if defined(__KERNEL_CPU__)
@@ -227,4 +195,25 @@ ccl_device_inline float intersection_curve_shadow_transparency(KernelGlobals kg,
return (1.0f - u) * f0 + u * f1;
}
+ccl_device_inline bool intersection_skip_self(ccl_private const RaySelfPrimitives &self,
+ const int object,
+ const int prim)
+{
+ return (self.prim == prim) && (self.object == object);
+}
+
+ccl_device_inline bool intersection_skip_self_shadow(ccl_private const RaySelfPrimitives &self,
+ const int object,
+ const int prim)
+{
+ return ((self.prim == prim) && (self.object == object)) ||
+ ((self.light_prim == prim) && (self.light_object == object));
+}
+
+ccl_device_inline bool intersection_skip_self_local(ccl_private const RaySelfPrimitives &self,
+ const int prim)
+{
+ return (self.prim == prim);
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/bvh/volume.h b/intern/cycles/kernel/bvh/volume.h
index c0746c8efc3..95bba4f071d 100644
--- a/intern/cycles/kernel/bvh/volume.h
+++ b/intern/cycles/kernel/bvh/volume.h
@@ -144,6 +144,9 @@ ccl_device_inline
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ if (intersection_skip_self(ray->self, prim_object, prim)) {
+ continue;
+ }
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
@@ -164,6 +167,9 @@ ccl_device_inline
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ if (intersection_skip_self(ray->self, prim_object, prim)) {
+ continue;
+ }
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
continue;
diff --git a/intern/cycles/kernel/bvh/volume_all.h b/intern/cycles/kernel/bvh/volume_all.h
index a88c9d95d46..9f53e987cf1 100644
--- a/intern/cycles/kernel/bvh/volume_all.h
+++ b/intern/cycles/kernel/bvh/volume_all.h
@@ -147,6 +147,9 @@ ccl_device_inline
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ if (intersection_skip_self(ray->self, prim_object, prim)) {
+ continue;
+ }
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
continue;
@@ -188,6 +191,9 @@ ccl_device_inline
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
+ if (intersection_skip_self(ray->self, prim_object, prim)) {
+ continue;
+ }
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
continue;
diff --git a/intern/cycles/kernel/device/metal/kernel.metal b/intern/cycles/kernel/device/metal/kernel.metal
index deb7dafe55e..6b77940660f 100644
--- a/intern/cycles/kernel/device/metal/kernel.metal
+++ b/intern/cycles/kernel/device/metal/kernel.metal
@@ -40,6 +40,27 @@ struct TriangleIntersectionResult
enum { METALRT_HIT_TRIANGLE, METALRT_HIT_BOUNDING_BOX };
+ccl_device_inline bool intersection_skip_self(ray_data const RaySelfPrimitives& self,
+ const int object,
+ const int prim)
+{
+ return (self.prim == prim) && (self.object == object);
+}
+
+ccl_device_inline bool intersection_skip_self_shadow(ray_data const RaySelfPrimitives& self,
+ const int object,
+ const int prim)
+{
+ return ((self.prim == prim) && (self.object == object)) ||
+ ((self.light_prim == prim) && (self.light_object == object));
+}
+
+ccl_device_inline bool intersection_skip_self_local(ray_data const RaySelfPrimitives& self,
+ const int prim)
+{
+ return (self.prim == prim);
+}
+
template<typename TReturn, uint intersection_type>
TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload,
@@ -53,8 +74,8 @@ TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal,
#ifdef __BVH_LOCAL__
uint prim = primitive_id + kernel_tex_fetch(__object_prim_offset, object);
- if (object != payload.local_object) {
- /* Only intersect with matching object */
+ if ((object != payload.local_object) || intersection_skip_self_local(payload.self, prim)) {
+ /* Only intersect with matching object and skip self-intersecton. */
result.accept = false;
result.continue_search = true;
return result;
@@ -166,6 +187,11 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal,
}
# endif
+ if (intersection_skip_self_shadow(payload.self, object, prim)) {
+ /* continue search */
+ return true;
+ }
+
float u = 0.0f, v = 0.0f;
int type = 0;
if (intersection_type == METALRT_HIT_TRIANGLE) {
@@ -322,21 +348,35 @@ inline TReturnType metalrt_visibility_test(constant KernelParamsMetal &launch_pa
}
# endif
-# ifdef __VISIBILITY_FLAG__
uint visibility = payload.visibility;
+# ifdef __VISIBILITY_FLAG__
if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) {
result.accept = false;
result.continue_search = true;
return result;
}
+# endif
/* Shadow ray early termination. */
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
- result.accept = true;
- result.continue_search = false;
- return result;
+ if (intersection_skip_self_shadow(payload.self, object, prim)) {
+ result.accept = false;
+ result.continue_search = true;
+ return result;
+ }
+ else {
+ result.accept = true;
+ result.continue_search = false;
+ return result;
+ }
+ }
+ else {
+ if (intersection_skip_self(payload.self, object, prim)) {
+ result.accept = false;
+ result.continue_search = true;
+ return result;
+ }
}
-# endif
result.accept = true;
result.continue_search = true;
@@ -576,6 +616,150 @@ __intersection__curve_all_shadow(constant KernelParamsMetal &launch_params_metal
return result;
}
-
#endif /* __HAIR__ */
+
+#ifdef __POINTCLOUD__
+ccl_device_inline
+void metalrt_intersection_point(constant KernelParamsMetal &launch_params_metal,
+ ray_data MetalKernelContext::MetalRTIntersectionPayload &payload,
+ const uint object,
+ const uint prim,
+ const uint type,
+ const float3 ray_origin,
+ const float3 ray_direction,
+ float time,
+ const float ray_tmax,
+ thread BoundingBoxIntersectionResult &result)
+{
+# ifdef __VISIBILITY_FLAG__
+ const uint visibility = payload.visibility;
+ if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) {
+ return;
+ }
+# endif
+
+ float3 P = ray_origin;
+ float3 dir = ray_direction;
+
+ /* The direction is not normalized by default, but the point intersection routine expects that */
+ float len;
+ dir = normalize_len(dir, &len);
+
+ Intersection isect;
+ isect.t = ray_tmax;
+ /* Transform maximum distance into object space. */
+ if (isect.t != FLT_MAX)
+ isect.t *= len;
+
+ MetalKernelContext context(launch_params_metal);
+ if (context.point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) {
+ result = metalrt_visibility_test<BoundingBoxIntersectionResult, METALRT_HIT_BOUNDING_BOX>(
+ launch_params_metal, payload, object, prim, isect.u);
+ if (result.accept) {
+ result.distance = isect.t / len;
+ payload.u = isect.u;
+ payload.v = isect.v;
+ payload.prim = prim;
+ payload.type = type;
+ }
+ }
+}
+
+ccl_device_inline
+void metalrt_intersection_point_shadow(constant KernelParamsMetal &launch_params_metal,
+ ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload,
+ const uint object,
+ const uint prim,
+ const uint type,
+ const float3 ray_origin,
+ const float3 ray_direction,
+ float time,
+ const float ray_tmax,
+ thread BoundingBoxIntersectionResult &result)
+{
+ const uint visibility = payload.visibility;
+
+ float3 P = ray_origin;
+ float3 dir = ray_direction;
+
+ /* The direction is not normalized by default, but the point intersection routine expects that */
+ float len;
+ dir = normalize_len(dir, &len);
+
+ Intersection isect;
+ isect.t = ray_tmax;
+ /* Transform maximum distance into object space */
+ if (isect.t != FLT_MAX)
+ isect.t *= len;
+
+ MetalKernelContext context(launch_params_metal);
+ if (context.point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) {
+ result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_BOUNDING_BOX>(
+ launch_params_metal, payload, object, prim, float2(isect.u, isect.v), ray_tmax);
+ result.accept = !result.continue_search;
+
+ if (result.accept) {
+ result.distance = isect.t / len;
+ }
+ }
+}
+
+[[intersection(bounding_box, triangle_data, METALRT_TAGS)]]
+BoundingBoxIntersectionResult
+__intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
+ ray_data MetalKernelContext::MetalRTIntersectionPayload &payload [[payload]],
+ const uint object [[user_instance_id]],
+ const uint primitive_id [[primitive_id]],
+ const float3 ray_origin [[origin]],
+ const float3 ray_direction [[direction]],
+ const float ray_tmax [[max_distance]])
+{
+ const uint prim = primitive_id + kernel_tex_fetch(__object_prim_offset, object);
+ const int type = kernel_tex_fetch(__objects, object).primitive_type;
+
+ BoundingBoxIntersectionResult result;
+ result.accept = false;
+ result.continue_search = true;
+ result.distance = ray_tmax;
+
+ metalrt_intersection_point(launch_params_metal, payload, object, prim, type, ray_origin, ray_direction,
+# if defined(__METALRT_MOTION__)
+ payload.time,
+# else
+ 0.0f,
+# endif
+ ray_tmax, result);
+
+ return result;
+}
+
+[[intersection(bounding_box, triangle_data, METALRT_TAGS)]]
+BoundingBoxIntersectionResult
+__intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
+ ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload [[payload]],
+ const uint object [[user_instance_id]],
+ const uint primitive_id [[primitive_id]],
+ const float3 ray_origin [[origin]],
+ const float3 ray_direction [[direction]],
+ const float ray_tmax [[max_distance]])
+{
+ const uint prim = primitive_id + kernel_tex_fetch(__object_prim_offset, object);
+ const int type = kernel_tex_fetch(__objects, object).primitive_type;
+
+ BoundingBoxIntersectionResult result;
+ result.accept = false;
+ result.continue_search = true;
+ result.distance = ray_tmax;
+
+ metalrt_intersection_point_shadow(launch_params_metal, payload, object, prim, type, ray_origin, ray_direction,
+# if defined(__METALRT_MOTION__)
+ payload.time,
+# else
+ 0.0f,
+# endif
+ ray_tmax, result);
+
+ return result;
+}
+#endif /* __POINTCLOUD__ */
#endif /* __METALRT__ */
diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu
index aa210b31a95..8e3d57bff8a 100644
--- a/intern/cycles/kernel/device/optix/kernel.cu
+++ b/intern/cycles/kernel/device/optix/kernel.cu
@@ -45,6 +45,11 @@ template<typename T> ccl_device_forceinline T *get_payload_ptr_2()
return pointer_unpack_from_uint<T>(optixGetPayload_2(), optixGetPayload_3());
}
+template<typename T> ccl_device_forceinline T *get_payload_ptr_6()
+{
+ return (T *)(((uint64_t)optixGetPayload_7() << 32) | optixGetPayload_6());
+}
+
ccl_device_forceinline int get_object_id()
{
#ifdef __OBJECT_MOTION__
@@ -111,6 +116,12 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
return optixIgnoreIntersection();
}
+ const int prim = optixGetPrimitiveIndex();
+ ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
+ if (intersection_skip_self_local(ray->self, prim)) {
+ return optixIgnoreIntersection();
+ }
+
const uint max_hits = optixGetPayload_5();
if (max_hits == 0) {
/* Special case for when no hit information is requested, just report that something was hit */
@@ -149,8 +160,6 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
local_isect->num_hits = 1;
}
- const int prim = optixGetPrimitiveIndex();
-
Intersection *isect = &local_isect->hits[hit];
isect->t = optixGetRayTmax();
isect->prim = prim;
@@ -185,6 +194,11 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
}
# endif
+ ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
+ if (intersection_skip_self_shadow(ray->self, object, prim)) {
+ return optixIgnoreIntersection();
+ }
+
float u = 0.0f, v = 0.0f;
int type = 0;
if (optixIsTriangleHit()) {
@@ -314,6 +328,12 @@ extern "C" __global__ void __anyhit__kernel_optix_volume_test()
if ((kernel_tex_fetch(__object_flag, object) & SD_OBJECT_HAS_VOLUME) == 0) {
return optixIgnoreIntersection();
}
+
+ const int prim = optixGetPrimitiveIndex();
+ ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
+ if (intersection_skip_self(ray->self, object, prim)) {
+ return optixIgnoreIntersection();
+ }
}
extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
@@ -330,18 +350,31 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
# endif
#endif
-#ifdef __VISIBILITY_FLAG__
const uint object = get_object_id();
const uint visibility = optixGetPayload_4();
+#ifdef __VISIBILITY_FLAG__
if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) {
return optixIgnoreIntersection();
}
+#endif
+
+ const int prim = optixGetPrimitiveIndex();
+ ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
- /* Shadow ray early termination. */
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
- return optixTerminateRay();
+ if (intersection_skip_self_shadow(ray->self, object, prim)) {
+ return optixIgnoreIntersection();
+ }
+ else {
+ /* Shadow ray early termination. */
+ return optixTerminateRay();
+ }
+ }
+ else {
+ if (intersection_skip_self(ray->self, object, prim)) {
+ return optixIgnoreIntersection();
+ }
}
-#endif
}
extern "C" __global__ void __closesthit__kernel_optix_hit()
diff --git a/intern/cycles/kernel/film/read.h b/intern/cycles/kernel/film/read.h
index 18a593a75b1..ba895fd8909 100644
--- a/intern/cycles/kernel/film/read.h
+++ b/intern/cycles/kernel/film/read.h
@@ -214,6 +214,21 @@ ccl_device_inline void film_get_pass_pixel_light_path(
pixel[0] = f.x;
pixel[1] = f.y;
pixel[2] = f.z;
+
+ /* Optional alpha channel. */
+ if (kfilm_convert->num_components >= 4) {
+ if (kfilm_convert->pass_combined != PASS_UNUSED) {
+ float scale, scale_exposure;
+ film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure);
+
+ ccl_global const float *in_combined = buffer + kfilm_convert->pass_combined;
+ const float alpha = in_combined[3] * scale;
+ pixel[3] = film_transparency_to_alpha(alpha);
+ }
+ else {
+ pixel[3] = 1.0f;
+ }
+ }
}
ccl_device_inline void film_get_pass_pixel_float3(ccl_global const KernelFilmConvert *ccl_restrict
diff --git a/intern/cycles/kernel/geom/curve.h b/intern/cycles/kernel/geom/curve.h
index 8a63f01643b..48ee8226e89 100644
--- a/intern/cycles/kernel/geom/curve.h
+++ b/intern/cycles/kernel/geom/curve.h
@@ -226,6 +226,18 @@ ccl_device float curve_thickness(KernelGlobals kg, ccl_private const ShaderData
return r * 2.0f;
}
+/* Curve random */
+
+ccl_device float curve_random(KernelGlobals kg, ccl_private const ShaderData *sd)
+{
+ if (sd->type & PRIMITIVE_CURVE) {
+ const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_CURVE_RANDOM);
+ return (desc.offset != ATTR_STD_NOT_FOUND) ? curve_attribute_float(kg, sd, desc, NULL, NULL) :
+ 0.0f;
+ }
+ return 0.0f;
+}
+
/* Curve location for motion pass, linear interpolation between keys and
* ignoring radius because we do the same for the motion keys */
diff --git a/intern/cycles/kernel/geom/motion_triangle_intersect.h b/intern/cycles/kernel/geom/motion_triangle_intersect.h
index cb6d210d90f..a11cb88385b 100644
--- a/intern/cycles/kernel/geom/motion_triangle_intersect.h
+++ b/intern/cycles/kernel/geom/motion_triangle_intersect.h
@@ -29,46 +29,19 @@
CCL_NAMESPACE_BEGIN
-/* Refine triangle intersection to more precise hit point. For rays that travel
- * far the precision is often not so good, this reintersects the primitive from
- * a closer distance.
+/**
+ * Use the barycentric coordinates to get the intersection location
*/
-
-ccl_device_inline float3 motion_triangle_refine(KernelGlobals kg,
- ccl_private ShaderData *sd,
- float3 P,
- float3 D,
- float t,
- const int isect_object,
- const int isect_prim,
- float3 verts[3])
+ccl_device_inline float3 motion_triangle_point_from_uv(KernelGlobals kg,
+ ccl_private ShaderData *sd,
+ const int isect_object,
+ const int isect_prim,
+ const float u,
+ const float v,
+ float3 verts[3])
{
-#ifdef __INTERSECTION_REFINE__
- if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- if (UNLIKELY(t == 0.0f)) {
- return P;
- }
- const Transform tfm = object_get_inverse_transform(kg, sd);
-
- P = transform_point(&tfm, P);
- D = transform_direction(&tfm, D * t);
- D = normalize_len(D, &t);
- }
-
- P = P + D * t;
-
- /* Compute refined intersection distance. */
- const float3 e1 = verts[0] - verts[2];
- const float3 e2 = verts[1] - verts[2];
- const float3 s1 = cross(D, e2);
-
- const float invdivisor = 1.0f / dot(s1, e1);
- const float3 d = P - verts[2];
- const float3 s2 = cross(d, e1);
- float rt = dot(e2, s2) * invdivisor;
-
- /* Compute refined position. */
- P = P + D * rt;
+ float w = 1.0f - u - v;
+ float3 P = u * verts[0] + v * verts[1] + w * verts[2];
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
const Transform tfm = object_get_transform(kg, sd);
@@ -76,71 +49,8 @@ ccl_device_inline float3 motion_triangle_refine(KernelGlobals kg,
}
return P;
-#else
- return P + D * t;
-#endif
}
-/* Same as above, except that t is assumed to be in object space
- * for instancing.
- */
-
-#ifdef __BVH_LOCAL__
-# if defined(__KERNEL_CUDA__) && (defined(i386) || defined(_M_IX86))
-ccl_device_noinline
-# else
-ccl_device_inline
-# endif
- float3
- motion_triangle_refine_local(KernelGlobals kg,
- ccl_private ShaderData *sd,
- float3 P,
- float3 D,
- float t,
- const int isect_object,
- const int isect_prim,
- float3 verts[3])
-{
-# if defined(__KERNEL_GPU_RAYTRACING__)
- /* t is always in world space with OptiX and MetalRT. */
- return motion_triangle_refine(kg, sd, P, D, t, isect_object, isect_prim, verts);
-# else
-# ifdef __INTERSECTION_REFINE__
- if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- const Transform tfm = object_get_inverse_transform(kg, sd);
-
- P = transform_point(&tfm, P);
- D = transform_direction(&tfm, D);
- D = normalize(D);
- }
-
- P = P + D * t;
-
- /* compute refined intersection distance */
- const float3 e1 = verts[0] - verts[2];
- const float3 e2 = verts[1] - verts[2];
- const float3 s1 = cross(D, e2);
-
- const float invdivisor = 1.0f / dot(s1, e1);
- const float3 d = P - verts[2];
- const float3 s2 = cross(d, e1);
- float rt = dot(e2, s2) * invdivisor;
-
- P = P + D * rt;
-
- if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- const Transform tfm = object_get_transform(kg, sd);
- P = transform_point(&tfm, P);
- }
-
- return P;
-# else /* __INTERSECTION_REFINE__ */
- return P + D * t;
-# endif /* __INTERSECTION_REFINE__ */
-# endif
-}
-#endif /* __BVH_LOCAL__ */
-
/* Ray intersection. We simply compute the vertex positions at the given ray
* time and do a ray intersection with the resulting triangle.
*/
diff --git a/intern/cycles/kernel/geom/motion_triangle_shader.h b/intern/cycles/kernel/geom/motion_triangle_shader.h
index fc7c181882e..15730c83969 100644
--- a/intern/cycles/kernel/geom/motion_triangle_shader.h
+++ b/intern/cycles/kernel/geom/motion_triangle_shader.h
@@ -68,15 +68,7 @@ ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg,
verts[1] = (1.0f - t) * verts[1] + t * next_verts[1];
verts[2] = (1.0f - t) * verts[2] + t * next_verts[2];
/* Compute refined position. */
-#ifdef __BVH_LOCAL__
- if (is_local) {
- sd->P = motion_triangle_refine_local(kg, sd, P, D, ray_t, isect_object, isect_prim, verts);
- }
- else
-#endif /* __BVH_LOCAL__*/
- {
- sd->P = motion_triangle_refine(kg, sd, P, D, ray_t, isect_object, isect_prim, verts);
- }
+ sd->P = motion_triangle_point_from_uv(kg, sd, isect_object, isect_prim, sd->u, sd->v, verts);
/* Compute face normal. */
float3 Ng;
if (sd->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
diff --git a/intern/cycles/kernel/geom/point.h b/intern/cycles/kernel/geom/point.h
index 52a1e77d71a..545b5c7fa43 100644
--- a/intern/cycles/kernel/geom/point.h
+++ b/intern/cycles/kernel/geom/point.h
@@ -81,7 +81,7 @@ ccl_device float3 point_attribute_float3(KernelGlobals kg,
# endif
if (desc.element == ATTR_ELEMENT_VERTEX) {
- return float4_to_float3(kernel_tex_fetch(__attributes_float4, desc.offset + sd->prim));
+ return kernel_tex_fetch(__attributes_float3, desc.offset + sd->prim);
}
else {
return make_float3(0.0f, 0.0f, 0.0f);
@@ -109,17 +109,59 @@ ccl_device float4 point_attribute_float4(KernelGlobals kg,
}
}
+/* Point position */
+
+ccl_device float3 point_position(KernelGlobals kg, ccl_private const ShaderData *sd)
+{
+ if (sd->type & PRIMITIVE_POINT) {
+ /* World space center. */
+ float3 P = (sd->type & PRIMITIVE_MOTION) ?
+ float4_to_float3(motion_point(kg, sd->object, sd->prim, sd->time)) :
+ float4_to_float3(kernel_tex_fetch(__points, sd->prim));
+
+ if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
+ object_position_transform(kg, sd, &P);
+ }
+
+ return P;
+ }
+
+ return zero_float3();
+}
+
/* Point radius */
ccl_device float point_radius(KernelGlobals kg, ccl_private const ShaderData *sd)
{
if (sd->type & PRIMITIVE_POINT) {
- return kernel_tex_fetch(__points, sd->prim).w;
+ /* World space radius. */
+ const float r = kernel_tex_fetch(__points, sd->prim).w;
+
+ if (sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED) {
+ return r;
+ }
+ else {
+ float3 dir = make_float3(r, r, r);
+ object_dir_transform(kg, sd, &dir);
+ return average(dir);
+ }
}
return 0.0f;
}
+/* Point random */
+
+ccl_device float point_random(KernelGlobals kg, ccl_private const ShaderData *sd)
+{
+ if (sd->type & PRIMITIVE_POINT) {
+ const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POINT_RANDOM);
+ return (desc.offset != ATTR_STD_NOT_FOUND) ? point_attribute_float(kg, sd, desc, NULL, NULL) :
+ 0.0f;
+ }
+ return 0.0f;
+}
+
/* Point location for motion pass, linear interpolation between keys and
* ignoring radius because we do the same for the motion keys */
diff --git a/intern/cycles/kernel/geom/shader_data.h b/intern/cycles/kernel/geom/shader_data.h
index f5055d8b285..fdf914d85e0 100644
--- a/intern/cycles/kernel/geom/shader_data.h
+++ b/intern/cycles/kernel/geom/shader_data.h
@@ -89,7 +89,7 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg,
sd->shader = kernel_tex_fetch(__tri_shader, sd->prim);
/* vectors */
- sd->P = triangle_refine(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim);
+ sd->P = triangle_point_from_uv(kg, sd, isect->object, isect->prim, isect->u, isect->v);
sd->Ng = Ng;
sd->N = Ng;
@@ -190,40 +190,46 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
#ifdef __OBJECT_MOTION__
shader_setup_object_transforms(kg, sd, time);
#endif
- }
- else if (lamp != LAMP_NONE) {
- sd->lamp = lamp;
- }
- /* transform into world space */
- if (object_space) {
- object_position_transform_auto(kg, sd, &sd->P);
- object_normal_transform_auto(kg, sd, &sd->Ng);
- sd->N = sd->Ng;
- object_dir_transform_auto(kg, sd, &sd->I);
- }
+ /* transform into world space */
+ if (object_space) {
+ object_position_transform_auto(kg, sd, &sd->P);
+ object_normal_transform_auto(kg, sd, &sd->Ng);
+ sd->N = sd->Ng;
+ object_dir_transform_auto(kg, sd, &sd->I);
+ }
- if (sd->type == PRIMITIVE_TRIANGLE) {
- /* smooth normal */
- if (sd->shader & SHADER_SMOOTH_NORMAL) {
- sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v);
+ if (sd->type == PRIMITIVE_TRIANGLE) {
+ /* smooth normal */
+ if (sd->shader & SHADER_SMOOTH_NORMAL) {
+ sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v);
- if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- object_normal_transform_auto(kg, sd, &sd->N);
+ if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
+ object_normal_transform_auto(kg, sd, &sd->N);
+ }
}
- }
- /* dPdu/dPdv */
+ /* dPdu/dPdv */
#ifdef __DPDU__
- triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv);
+ triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv);
- if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- object_dir_transform_auto(kg, sd, &sd->dPdu);
- object_dir_transform_auto(kg, sd, &sd->dPdv);
+ if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
+ object_dir_transform_auto(kg, sd, &sd->dPdu);
+ object_dir_transform_auto(kg, sd, &sd->dPdv);
+ }
+#endif
}
+ else {
+#ifdef __DPDU__
+ sd->dPdu = zero_float3();
+ sd->dPdv = zero_float3();
#endif
+ }
}
else {
+ if (lamp != LAMP_NONE) {
+ sd->lamp = lamp;
+ }
#ifdef __DPDU__
sd->dPdu = zero_float3();
sd->dPdv = zero_float3();
diff --git a/intern/cycles/kernel/geom/triangle_intersect.h b/intern/cycles/kernel/geom/triangle_intersect.h
index 0169b40bc34..8458cf020a0 100644
--- a/intern/cycles/kernel/geom/triangle_intersect.h
+++ b/intern/cycles/kernel/geom/triangle_intersect.h
@@ -142,116 +142,23 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg,
}
#endif /* __BVH_LOCAL__ */
-/* Refine triangle intersection to more precise hit point. For rays that travel
- * far the precision is often not so good, this reintersects the primitive from
- * a closer distance. */
-
-/* Reintersections uses the paper:
- *
- * Tomas Moeller
- * Fast, minimum storage ray/triangle intersection
- * http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
+/**
+ * Use the barycentric coordinates to get the intersection location
*/
-
-ccl_device_inline float3 triangle_refine(KernelGlobals kg,
- ccl_private ShaderData *sd,
- float3 P,
- float3 D,
- float t,
- const int isect_object,
- const int isect_prim)
+ccl_device_inline float3 triangle_point_from_uv(KernelGlobals kg,
+ ccl_private ShaderData *sd,
+ const int isect_object,
+ const int isect_prim,
+ const float u,
+ const float v)
{
-#ifdef __INTERSECTION_REFINE__
- if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- if (UNLIKELY(t == 0.0f)) {
- return P;
- }
- const Transform tfm = object_get_inverse_transform(kg, sd);
-
- P = transform_point(&tfm, P);
- D = transform_direction(&tfm, D * t);
- D = normalize_len(D, &t);
- }
-
- P = P + D * t;
-
const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w;
const packed_float3 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0),
tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1),
tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2);
- float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z);
- float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z);
- float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z);
- float3 qvec = cross(tvec, edge1);
- float3 pvec = cross(D, edge2);
- float det = dot(edge1, pvec);
- if (det != 0.0f) {
- /* If determinant is zero it means ray lies in the plane of
- * the triangle. It is possible in theory due to watertight
- * nature of triangle intersection. For such cases we simply
- * don't refine intersection hoping it'll go all fine.
- */
- float rt = dot(edge2, qvec) / det;
- P = P + D * rt;
- }
-
- if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- const Transform tfm = object_get_transform(kg, sd);
- P = transform_point(&tfm, P);
- }
-
- return P;
-#else
- return P + D * t;
-#endif
-}
-
-/* Same as above, except that t is assumed to be in object space for
- * instancing.
- */
-ccl_device_inline float3 triangle_refine_local(KernelGlobals kg,
- ccl_private ShaderData *sd,
- float3 P,
- float3 D,
- float t,
- const int isect_object,
- const int isect_prim)
-{
-#if defined(__KERNEL_GPU_RAYTRACING__)
- /* t is always in world space with OptiX and MetalRT. */
- return triangle_refine(kg, sd, P, D, t, isect_object, isect_prim);
-#else
- if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- const Transform tfm = object_get_inverse_transform(kg, sd);
-
- P = transform_point(&tfm, P);
- D = transform_direction(&tfm, D);
- D = normalize(D);
- }
+ float w = 1.0f - u - v;
- P = P + D * t;
-
-# ifdef __INTERSECTION_REFINE__
- const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w;
- const packed_float3 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0),
- tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1),
- tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2);
- float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z);
- float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z);
- float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z);
- float3 qvec = cross(tvec, edge1);
- float3 pvec = cross(D, edge2);
- float det = dot(edge1, pvec);
- if (det != 0.0f) {
- /* If determinant is zero it means ray lies in the plane of
- * the triangle. It is possible in theory due to watertight
- * nature of triangle intersection. For such cases we simply
- * don't refine intersection hoping it'll go all fine.
- */
- float rt = dot(edge2, qvec) / det;
- P = P + D * rt;
- }
-# endif /* __INTERSECTION_REFINE__ */
+ float3 P = u * tri_a + v * tri_b + w * tri_c;
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
const Transform tfm = object_get_transform(kg, sd);
@@ -259,7 +166,6 @@ ccl_device_inline float3 triangle_refine_local(KernelGlobals kg,
}
return P;
-#endif
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h
index df710dc1d82..4c5265189fa 100644
--- a/intern/cycles/kernel/integrator/intersect_closest.h
+++ b/intern/cycles/kernel/integrator/intersect_closest.h
@@ -328,6 +328,12 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg,
/* Scene Intersection. */
Intersection isect ccl_optional_struct_init;
+ isect.object = OBJECT_NONE;
+ isect.prim = PRIM_NONE;
+ ray.self.object = last_isect_object;
+ ray.self.prim = last_isect_prim;
+ ray.self.light_object = OBJECT_NONE;
+ ray.self.light_prim = PRIM_NONE;
bool hit = scene_intersect(kg, &ray, visibility, &isect);
/* TODO: remove this and do it in the various intersection functions instead. */
diff --git a/intern/cycles/kernel/integrator/intersect_shadow.h b/intern/cycles/kernel/integrator/intersect_shadow.h
index 90422445fad..1ba8724826b 100644
--- a/intern/cycles/kernel/integrator/intersect_shadow.h
+++ b/intern/cycles/kernel/integrator/intersect_shadow.h
@@ -156,7 +156,10 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt
/* Read ray from integrator state into local memory. */
Ray ray ccl_optional_struct_init;
integrator_state_read_shadow_ray(kg, state, &ray);
-
+ ray.self.object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, object);
+ ray.self.prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, prim);
+ ray.self.light_object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, object);
+ ray.self.light_prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, prim);
/* Compute visibility. */
const uint visibility = integrate_intersect_shadow_visibility(kg, state);
diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h
index 9fa5ff63ad2..ee3d82ebacb 100644
--- a/intern/cycles/kernel/integrator/intersect_volume_stack.h
+++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h
@@ -38,7 +38,10 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
Ray volume_ray ccl_optional_struct_init;
volume_ray.P = from_P;
volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t);
-
+ volume_ray.self.object = INTEGRATOR_STATE(state, isect, object);
+ volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim);
+ volume_ray.self.light_object = OBJECT_NONE;
+ volume_ray.self.light_prim = PRIM_NONE;
/* Store to avoid global fetches on every intersection step. */
const uint volume_stack_size = kernel_data.volume_stack_size;
@@ -68,7 +71,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
volume_stack_enter_exit(kg, state, stack_sd);
/* Move ray forward. */
- volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng);
+ volume_ray.P = stack_sd->P;
if (volume_ray.t != FLT_MAX) {
volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t);
}
@@ -91,6 +94,10 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
* fewest hits. */
volume_ray.D = make_float3(0.0f, 0.0f, 1.0f);
volume_ray.t = FLT_MAX;
+ volume_ray.self.object = OBJECT_NONE;
+ volume_ray.self.prim = PRIM_NONE;
+ volume_ray.self.light_object = OBJECT_NONE;
+ volume_ray.self.light_prim = PRIM_NONE;
int stack_index = 0, enclosed_index = 0;
@@ -203,7 +210,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
}
/* Move ray forward. */
- volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng);
+ volume_ray.P = stack_sd->P;
++step;
}
#endif
diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h
index 97ca430752c..0a82c9cadef 100644
--- a/intern/cycles/kernel/integrator/shade_light.h
+++ b/intern/cycles/kernel/integrator/shade_light.h
@@ -37,8 +37,9 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
/* Advance ray beyond light. */
/* TODO: can we make this more numerically robust to avoid reintersecting the
- * same light in some cases? */
- const float3 new_ray_P = ray_offset(ray_P + ray_D * isect.t, ray_D);
+ * same light in some cases? Ray should not intersect surface anymore as the
+ * object and prim ids will prevent self intersection. */
+ const float3 new_ray_P = ray_P + ray_D * isect.t;
INTEGRATOR_STATE_WRITE(state, ray, P) = new_ray_P;
INTEGRATOR_STATE_WRITE(state, ray, t) -= isect.t;
@@ -46,7 +47,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t);
ray_P -= ray_D * mis_ray_t;
isect.t += mis_ray_t;
- INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = mis_ray_t + isect.t;
+ INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = isect.t;
LightSample ls ccl_optional_struct_init;
const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls);
diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h
index a68fcaa7a64..3e8eba29ef7 100644
--- a/intern/cycles/kernel/integrator/shade_shadow.h
+++ b/intern/cycles/kernel/integrator/shade_shadow.h
@@ -83,7 +83,10 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg,
/* Setup shader data. */
Ray ray ccl_optional_struct_init;
integrator_state_read_shadow_ray(kg, state, &ray);
-
+ ray.self.object = OBJECT_NONE;
+ ray.self.prim = PRIM_NONE;
+ ray.self.light_object = OBJECT_NONE;
+ ray.self.light_prim = PRIM_NONE;
/* Modify ray position and length to match current segment. */
const float start_t = (hit == 0) ? 0.0f :
INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t);
@@ -149,7 +152,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg,
const float last_hit_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, num_recorded_hits - 1, t);
const float3 ray_P = INTEGRATOR_STATE(state, shadow_ray, P);
const float3 ray_D = INTEGRATOR_STATE(state, shadow_ray, D);
- INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_offset(ray_P + last_hit_t * ray_D, ray_D);
+ INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_P + last_hit_t * ray_D;
INTEGRATOR_STATE_WRITE(state, shadow_ray, t) -= last_hit_t;
}
diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h
index 3d5b65458c7..10d3cbf7f57 100644
--- a/intern/cycles/kernel/integrator/shade_surface.h
+++ b/intern/cycles/kernel/integrator/shade_surface.h
@@ -182,23 +182,35 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
/* Write shadow ray and associated state to global memory. */
integrator_state_write_shadow_ray(kg, shadow_state, &ray);
+ // Save memory by storing the light and object indices in the shadow_isect
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim;
/* Copy state from main path to shadow path. */
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
- shadow_flag |= PATH_RAY_SURFACE_PASS;
const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * bsdf_eval_sum(&bsdf_eval);
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
- const packed_float3 pass_diffuse_weight =
- (bounce == 0) ? packed_float3(bsdf_eval_pass_diffuse_weight(&bsdf_eval)) :
- INTEGRATOR_STATE(state, path, pass_diffuse_weight);
- const packed_float3 pass_glossy_weight = (bounce == 0) ?
- packed_float3(
- bsdf_eval_pass_glossy_weight(&bsdf_eval)) :
- INTEGRATOR_STATE(state, path, pass_glossy_weight);
+ packed_float3 pass_diffuse_weight;
+ packed_float3 pass_glossy_weight;
+
+ if (shadow_flag & PATH_RAY_ANY_PASS) {
+ /* Indirect bounce, use weights from earlier surface or volume bounce. */
+ pass_diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
+ pass_glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight);
+ }
+ else {
+ /* Direct light, use BSDFs at this bounce. */
+ shadow_flag |= PATH_RAY_SURFACE_PASS;
+ pass_diffuse_weight = packed_float3(bsdf_eval_pass_diffuse_weight(&bsdf_eval));
+ pass_glossy_weight = packed_float3(bsdf_eval_pass_glossy_weight(&bsdf_eval));
+ }
+
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = pass_glossy_weight;
}
@@ -266,13 +278,11 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
}
/* Setup ray. Note that clipping works through transparent bounces. */
- INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P,
- (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng);
+ INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in);
INTEGRATOR_STATE_WRITE(state, ray, t) = (label & LABEL_TRANSPARENT) ?
INTEGRATOR_STATE(state, ray, t) - sd->ray_length :
FLT_MAX;
-
#ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in);
@@ -316,7 +326,7 @@ ccl_device_forceinline bool integrate_surface_volume_only_bounce(IntegratorState
}
/* Setup ray position, direction stays unchanged. */
- INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, -sd->Ng);
+ INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
/* Clipping works through transparent. */
INTEGRATOR_STATE_WRITE(state, ray, t) -= sd->ray_length;
@@ -360,10 +370,14 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
}
Ray ray ccl_optional_struct_init;
- ray.P = ray_offset(sd->P, sd->Ng);
+ ray.P = sd->P;
ray.D = ao_D;
ray.t = kernel_data.integrator.ao_bounces_distance;
ray.time = sd->time;
+ ray.self.object = sd->object;
+ ray.self.prim = sd->prim;
+ ray.self.light_object = OBJECT_NONE;
+ ray.self.light_prim = PRIM_NONE;
ray.dP = differential_zero_compact();
ray.dD = differential_zero_compact();
@@ -375,6 +389,10 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
/* Write shadow ray and associated state to global memory. */
integrator_state_write_shadow_ray(kg, shadow_state, &ray);
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim;
/* Copy state from main path to shadow path. */
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h
index 712c22357b8..c59234553a7 100644
--- a/intern/cycles/kernel/integrator/shade_volume.h
+++ b/intern/cycles/kernel/integrator/shade_volume.h
@@ -791,22 +791,36 @@ ccl_device_forceinline void integrate_volume_direct_light(
/* Write shadow ray and associated state to global memory. */
integrator_state_write_shadow_ray(kg, shadow_state, &ray);
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object;
+ INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim;
/* Copy state from main path to shadow path. */
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
- shadow_flag |= PATH_RAY_VOLUME_PASS;
const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval);
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
- const packed_float3 pass_diffuse_weight = (bounce == 0) ?
- packed_float3(one_float3()) :
- INTEGRATOR_STATE(
- state, path, pass_diffuse_weight);
+ packed_float3 pass_diffuse_weight;
+ packed_float3 pass_glossy_weight;
+
+ if (shadow_flag & PATH_RAY_ANY_PASS) {
+ /* Indirect bounce, use weights from earlier surface or volume bounce. */
+ pass_diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
+ pass_glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight);
+ }
+ else {
+ /* Direct light, no diffuse/glossy distinction needed for volumes. */
+ shadow_flag |= PATH_RAY_VOLUME_PASS;
+ pass_diffuse_weight = packed_float3(one_float3());
+ pass_glossy_weight = packed_float3(zero_float3());
+ }
+
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight;
- INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = zero_float3();
+ INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = pass_glossy_weight;
}
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE(
@@ -873,11 +887,13 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in);
INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX;
-
# ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in);
# endif
+ // Save memory by storing last hit prim and object in isect
+ INTEGRATOR_STATE_WRITE(state, isect, prim) = sd->prim;
+ INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object;
/* Update throughput. */
const float3 throughput = INTEGRATOR_STATE(state, path, throughput);
diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h
index 625a429d3db..86fcabdcd82 100644
--- a/intern/cycles/kernel/integrator/shadow_state_template.h
+++ b/intern/cycles/kernel/integrator/shadow_state_template.h
@@ -61,6 +61,7 @@ KERNEL_STRUCT_MEMBER(shadow_ray, packed_float3, D, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING)
+KERNEL_STRUCT_MEMBER(shadow_ray, int, object, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_END(shadow_ray)
/*********************** Shadow Intersection result **************************/
diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h
index 59b0cd2596c..6c0f815afea 100644
--- a/intern/cycles/kernel/integrator/subsurface.h
+++ b/intern/cycles/kernel/integrator/subsurface.h
@@ -57,7 +57,6 @@ ccl_device int subsurface_bounce(KernelGlobals kg,
/* Pass along object info, reusing isect to save memory. */
INTEGRATOR_STATE_WRITE(state, subsurface, Ng) = sd->Ng;
- INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object;
uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA) |
((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? PATH_RAY_SUBSURFACE_DISK :
@@ -165,10 +164,8 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat
if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) {
float3 P = INTEGRATOR_STATE(state, ray, P);
- const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng);
- const float3 offset_P = ray_offset(P, -Ng);
- integrator_volume_stack_update_for_subsurface(kg, state, offset_P, ray.P);
+ integrator_volume_stack_update_for_subsurface(kg, state, P, ray.P);
}
}
# endif /* __VOLUME__ */
diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h
index cc6f5048cda..f5641d1fa5e 100644
--- a/intern/cycles/kernel/integrator/subsurface_disk.h
+++ b/intern/cycles/kernel/integrator/subsurface_disk.h
@@ -99,6 +99,10 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
ray.dP = ray_dP;
ray.dD = differential_zero_compact();
ray.time = time;
+ ray.self.object = OBJECT_NONE;
+ ray.self.prim = PRIM_NONE;
+ ray.self.light_object = OBJECT_NONE;
+ ray.self.light_prim = OBJECT_NONE;
/* Intersect with the same object. if multiple intersections are found it
* will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */
diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h
index 7a8b467e199..993c54d9050 100644
--- a/intern/cycles/kernel/integrator/subsurface_random_walk.h
+++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h
@@ -195,6 +195,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
const float time = INTEGRATOR_STATE(state, ray, time);
const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng);
const int object = INTEGRATOR_STATE(state, isect, object);
+ const int prim = INTEGRATOR_STATE(state, isect, prim);
/* Sample diffuse surface scatter into the object. */
float3 D;
@@ -205,12 +206,16 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
}
/* Setup ray. */
- ray.P = ray_offset(P, -Ng);
+ ray.P = P;
ray.D = D;
ray.t = FLT_MAX;
ray.time = time;
ray.dP = ray_dP;
ray.dD = differential_zero_compact();
+ ray.self.object = object;
+ ray.self.prim = prim;
+ ray.self.light_object = OBJECT_NONE;
+ ray.self.light_prim = PRIM_NONE;
#ifndef __KERNEL_GPU_RAYTRACING__
/* Compute or fetch object transforms. */
@@ -377,7 +382,15 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
* If yes, we will later use backwards guided sampling in order to have a decent
* chance of connecting to it.
* TODO: Maybe use less than 10 times the mean free path? */
- ray.t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t;
+ if (bounce == 0) {
+ ray.t = max(t, 10.0f / (min3(sigma_t)));
+ }
+ else {
+ ray.t = t;
+ /* After the first bounce the object can intersect the same surface again */
+ ray.self.object = OBJECT_NONE;
+ ray.self.prim = PRIM_NONE;
+ }
scene_intersect_local(kg, &ray, &ss_isect, object, NULL, 1);
hit = (ss_isect.num_hits > 0);
@@ -408,13 +421,6 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
if (hit) {
t = ray.t;
}
- else if (bounce == 0) {
- /* Restore original position if nothing was hit after the first bounce,
- * without the ray_offset() that was added to avoid self-intersection.
- * Otherwise if that offset is relatively large compared to the scattering
- * radius, we never go back up high enough to exit the surface. */
- ray.P = P;
- }
/* Advance to new scatter location. */
ray.P += t * ray.D;
diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h
index 6e445f862db..d05fe47cc2c 100644
--- a/intern/cycles/kernel/light/light.h
+++ b/intern/cycles/kernel/light/light.h
@@ -113,22 +113,30 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]);
if (type == LIGHT_SPOT) {
- ls->Ng = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
- float radius = klight->spot.radius;
+ const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
+ const float radius = klight->spot.radius;
+ const float3 dir = make_float3(
+ klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
+ /* disk oriented normal */
+ const float3 lightN = normalize(P - center);
+ ls->P = center;
if (radius > 0.0f)
- /* sphere light */
- ls->P += disk_light_sample(ls->Ng, randu, randv) * radius;
+ /* disk light */
+ ls->P += disk_light_sample(lightN, randu, randv) * radius;
+
+ const float invarea = klight->spot.invarea;
+ ls->pdf = invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
+ /* we set the light normal to the outgoing direction to support texturing */
+ ls->Ng = -ls->D;
- float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
- ls->pdf = invarea;
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
- ls->Ng, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
+ dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
}
@@ -137,32 +145,33 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
ls->u = uv.x;
ls->v = uv.y;
- ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
+ ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
}
else if (type == LIGHT_POINT) {
float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
float radius = klight->spot.radius;
+ /* disk oriented normal */
+ const float3 lightN = normalize(P - center);
ls->P = center;
- float pdf = 1.0;
if (radius > 0.0f) {
- ls->Ng = normalize(P - center);
- ls->P += disk_light_sample(ls->Ng, randu, randv) * radius;
- pdf = klight->spot.invarea;
- ls->D = normalize_len(ls->P - P, &ls->t);
- }
- else {
- ls->Ng = normalize(P - center);
+ ls->P += disk_light_sample(lightN, randu, randv) * radius;
}
+ ls->pdf = klight->spot.invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
- ls->pdf = pdf;
+ /* we set the light normal to the outgoing direction to support texturing */
+ ls->Ng = -ls->D;
+
ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea;
+ if (!in_volume_segment && ls->eval_fac == 0.0f) {
+ return false;
+ }
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
- ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
+ ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
}
else {
/* area light */
@@ -263,14 +272,16 @@ ccl_device bool lights_intersect(KernelGlobals kg,
if (type == LIGHT_SPOT) {
/* Spot/Disk light. */
+ const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t);
+ const float3 ray_P = ray->P - ray->D * mis_ray_t;
+
const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]);
- const float3 lightN = make_float3(
- klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
const float radius = klight->spot.radius;
if (radius == 0.0f) {
continue;
}
-
+ /* disk oriented normal */
+ const float3 lightN = normalize(ray_P - lightP);
/* One sided. */
if (dot(ray->D, lightN) >= 0.0f) {
continue;
@@ -292,9 +303,10 @@ ccl_device bool lights_intersect(KernelGlobals kg,
continue;
}
+ /* disk oriented normal */
+ const float3 lightN = normalize(ray_P - lightP);
float3 P;
- const float3 lsN = normalize(ray_P - lightP);
- if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lsN, radius, &P, &t)) {
+ if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lightN, radius, &P, &t)) {
continue;
}
}
@@ -418,8 +430,8 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
LightType type = (LightType)klight->type;
ls->type = type;
ls->shader = klight->shader_id;
- ls->object = PRIM_NONE;
- ls->prim = PRIM_NONE;
+ ls->object = isect->object;
+ ls->prim = isect->prim;
ls->lamp = lamp;
/* todo: missing texture coordinates */
ls->t = isect->t;
@@ -427,7 +439,12 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
ls->D = ray_D;
if (type == LIGHT_SPOT) {
- ls->Ng = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
+ const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
+ const float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
+ /* the normal of the oriented disk */
+ const float3 lightN = normalize(ray_P - center);
+ /* we set the light normal to the outgoing direction to support texturing*/
+ ls->Ng = -ls->D;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
@@ -435,7 +452,7 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
- ls->Ng, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
+ dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
if (ls->eval_fac == 0.0f) {
return false;
@@ -447,23 +464,32 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
/* compute pdf */
if (ls->t != FLT_MAX)
- ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
+ ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
+ else
+ ls->pdf = 0.f;
}
else if (type == LIGHT_POINT) {
- float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
+ const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
+ const float3 lighN = normalize(ray_P - center);
+
+ /* we set the light normal to the outgoing direction to support texturing*/
+ ls->Ng = -ls->D;
- ls->Ng = normalize(ray_P - center);
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
+ if (ls->eval_fac == 0.0f) {
+ return false;
+ }
+
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
/* compute pdf */
if (ls->t != FLT_MAX)
- ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
+ ls->pdf *= lamp_light_pdf(kg, lighN, -ls->D, ls->t);
else
ls->pdf = 0.f;
}
@@ -921,4 +947,4 @@ ccl_device_inline bool light_distribution_sample_new_position(KernelGlobals kg,
}
}
-CCL_NAMESPACE_END
+CCL_NAMESPACE_END \ No newline at end of file
diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h
index 7dbc783b1bb..521ad2f7066 100644
--- a/intern/cycles/kernel/light/sample.h
+++ b/intern/cycles/kernel/light/sample.h
@@ -198,7 +198,7 @@ ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg,
float NL = dot(sd->N, L);
bool transmit = (NL < 0.0f);
float3 Ng = (transmit ? -sd->Ng : sd->Ng);
- float3 P = ray_offset(sd->P, Ng);
+ float3 P = sd->P;
if ((sd->type & PRIMITIVE_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) {
const float offset_cutoff =
@@ -243,7 +243,7 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri
}
else {
/* other lights, avoid self-intersection */
- ray->D = ray_offset(ls->P, ls->Ng) - P;
+ ray->D = ls->P - P;
ray->D = normalize_len(ray->D, &ray->t);
}
}
@@ -257,6 +257,12 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri
ray->dP = differential_make_compact(sd->dP);
ray->dD = differential_zero_compact();
ray->time = sd->time;
+
+ /* Fill in intersection surface and light details. */
+ ray->self.prim = sd->prim;
+ ray->self.object = sd->object;
+ ray->self.light_prim = ls->prim;
+ ray->self.light_object = ls->object;
}
/* Create shadow ray towards light sample. */
diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp
index a79fc323a13..d79e7dfa8a5 100644
--- a/intern/cycles/kernel/osl/services.cpp
+++ b/intern/cycles/kernel/osl/services.cpp
@@ -116,6 +116,8 @@ ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal");
ustring OSLRenderServices::u_curve_random("geom:curve_random");
ustring OSLRenderServices::u_is_point("geom:is_point");
ustring OSLRenderServices::u_point_radius("geom:point_radius");
+ustring OSLRenderServices::u_point_position("geom:point_position");
+ustring OSLRenderServices::u_point_random("geom:point_random");
ustring OSLRenderServices::u_normal_map_normal("geom:normal_map_normal");
ustring OSLRenderServices::u_path_ray_length("path:ray_length");
ustring OSLRenderServices::u_path_ray_depth("path:ray_depth");
@@ -999,6 +1001,10 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg
float3 f = curve_tangent_normal(kg, sd);
return set_attribute_float3(f, type, derivatives, val);
}
+ else if (name == u_curve_random) {
+ float f = curve_random(kg, sd);
+ return set_attribute_float(f, type, derivatives, val);
+ }
/* point attributes */
else if (name == u_is_point) {
float f = (sd->type & PRIMITIVE_POINT) != 0;
@@ -1008,6 +1014,14 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg
float f = point_radius(kg, sd);
return set_attribute_float(f, type, derivatives, val);
}
+ else if (name == u_point_position) {
+ float3 f = point_position(kg, sd);
+ return set_attribute_float3(f, type, derivatives, val);
+ }
+ else if (name == u_point_random) {
+ float f = point_random(kg, sd);
+ return set_attribute_float(f, type, derivatives, val);
+ }
else if (name == u_normal_map_normal) {
if (sd->type & PRIMITIVE_TRIANGLE) {
float3 f = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v);
diff --git a/intern/cycles/kernel/osl/services.h b/intern/cycles/kernel/osl/services.h
index 9526c92b8fb..96c71297186 100644
--- a/intern/cycles/kernel/osl/services.h
+++ b/intern/cycles/kernel/osl/services.h
@@ -298,7 +298,9 @@ class OSLRenderServices : public OSL::RendererServices {
static ustring u_curve_tangent_normal;
static ustring u_curve_random;
static ustring u_is_point;
+ static ustring u_point_position;
static ustring u_point_radius;
+ static ustring u_point_random;
static ustring u_normal_map_normal;
static ustring u_path_ray_length;
static ustring u_path_ray_depth;
diff --git a/intern/cycles/kernel/osl/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt
index 4cafdb2a6d7..16a9b1cc012 100644
--- a/intern/cycles/kernel/osl/shaders/CMakeLists.txt
+++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt
@@ -49,6 +49,7 @@ set(SRC_OSL
node_glossy_bsdf.osl
node_gradient_texture.osl
node_hair_info.osl
+ node_point_info.osl
node_scatter_volume.osl
node_absorption_volume.osl
node_principled_volume.osl
diff --git a/intern/cycles/kernel/osl/shaders/node_point_info.osl b/intern/cycles/kernel/osl/shaders/node_point_info.osl
new file mode 100644
index 00000000000..58d8acbf269
--- /dev/null
+++ b/intern/cycles/kernel/osl/shaders/node_point_info.osl
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011-2022 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stdcycles.h"
+
+shader node_point_info(output point Position = point(0.0, 0.0, 0.0),
+ output float Radius = 0.0,
+ output float Random = 0.0)
+{
+ getattribute("geom:point_position", Position);
+ getattribute("geom:point_radius", Radius);
+ getattribute("geom:point_random", Random);
+}
diff --git a/intern/cycles/kernel/svm/ao.h b/intern/cycles/kernel/svm/ao.h
index 678f49c8ccd..dcb1a79717d 100644
--- a/intern/cycles/kernel/svm/ao.h
+++ b/intern/cycles/kernel/svm/ao.h
@@ -70,10 +70,14 @@ ccl_device float svm_ao(
/* Create ray. */
Ray ray;
- ray.P = ray_offset(sd->P, N);
+ ray.P = sd->P;
ray.D = D.x * T + D.y * B + D.z * N;
ray.t = max_dist;
ray.time = sd->time;
+ ray.self.object = sd->object;
+ ray.self.prim = sd->prim;
+ ray.self.light_object = OBJECT_NONE;
+ ray.self.light_prim = PRIM_NONE;
ray.dP = differential_zero_compact();
ray.dD = differential_zero_compact();
diff --git a/intern/cycles/kernel/svm/attribute.h b/intern/cycles/kernel/svm/attribute.h
index e9de0164c7a..17301028528 100644
--- a/intern/cycles/kernel/svm/attribute.h
+++ b/intern/cycles/kernel/svm/attribute.h
@@ -87,7 +87,9 @@ ccl_device_noinline void svm_node_attr(KernelGlobals kg,
if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
/* No generated attribute, fall back to object coordinates. */
float3 f = sd->P;
- object_inverse_position_transform(kg, sd, &f);
+ if (sd->object != OBJECT_NONE) {
+ object_inverse_position_transform(kg, sd, &f);
+ }
if (type == NODE_ATTR_OUTPUT_FLOAT) {
stack_store_float(stack, out_offset, average(f));
}
@@ -179,7 +181,9 @@ ccl_device_noinline void svm_node_attr_bump_dx(KernelGlobals kg,
if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
/* No generated attribute, fall back to object coordinates. */
float3 f = sd->P + sd->dP.dx;
- object_inverse_position_transform(kg, sd, &f);
+ if (sd->object != OBJECT_NONE) {
+ object_inverse_position_transform(kg, sd, &f);
+ }
if (type == NODE_ATTR_OUTPUT_FLOAT) {
stack_store_float(stack, out_offset, average(f));
}
@@ -275,7 +279,9 @@ ccl_device_noinline void svm_node_attr_bump_dy(KernelGlobals kg,
if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
/* No generated attribute, fall back to object coordinates. */
float3 f = sd->P + sd->dP.dy;
- object_inverse_position_transform(kg, sd, &f);
+ if (sd->object != OBJECT_NONE) {
+ object_inverse_position_transform(kg, sd, &f);
+ }
if (type == NODE_ATTR_OUTPUT_FLOAT) {
stack_store_float(stack, out_offset, average(f));
}
diff --git a/intern/cycles/kernel/svm/bevel.h b/intern/cycles/kernel/svm/bevel.h
index 46dfb6631da..98b663299da 100644
--- a/intern/cycles/kernel/svm/bevel.h
+++ b/intern/cycles/kernel/svm/bevel.h
@@ -196,6 +196,10 @@ ccl_device float3 svm_bevel(
ray.dP = differential_zero_compact();
ray.dD = differential_zero_compact();
ray.time = sd->time;
+ ray.self.object = OBJECT_NONE;
+ ray.self.prim = PRIM_NONE;
+ ray.self.light_object = OBJECT_NONE;
+ ray.self.light_prim = PRIM_NONE;
/* Intersect with the same object. if multiple intersections are found it
* will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */
@@ -207,15 +211,24 @@ ccl_device float3 svm_bevel(
/* Quickly retrieve P and Ng without setting up ShaderData. */
float3 hit_P;
if (sd->type == PRIMITIVE_TRIANGLE) {
- hit_P = triangle_refine_local(
- kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim);
+ hit_P = triangle_point_from_uv(kg,
+ sd,
+ isect.hits[hit].object,
+ isect.hits[hit].prim,
+ isect.hits[hit].u,
+ isect.hits[hit].v);
}
# ifdef __OBJECT_MOTION__
else if (sd->type == PRIMITIVE_MOTION_TRIANGLE) {
float3 verts[3];
motion_triangle_vertices(kg, sd->object, isect.hits[hit].prim, sd->time, verts);
- hit_P = motion_triangle_refine_local(
- kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim, verts);
+ hit_P = motion_triangle_point_from_uv(kg,
+ sd,
+ isect.hits[hit].object,
+ isect.hits[hit].prim,
+ isect.hits[hit].u,
+ isect.hits[hit].v,
+ verts);
}
# endif /* __OBJECT_MOTION__ */
diff --git a/intern/cycles/kernel/svm/geometry.h b/intern/cycles/kernel/svm/geometry.h
index 2bac58b0aa2..225348b1ac2 100644
--- a/intern/cycles/kernel/svm/geometry.h
+++ b/intern/cycles/kernel/svm/geometry.h
@@ -242,13 +242,6 @@ ccl_device_noinline void svm_node_hair_info(KernelGlobals kg,
stack_store_float(stack, out_offset, data);
break;
}
-# if 0
- case NODE_INFO_CURVE_FADE: {
- data = sd->curve_transparency;
- stack_store_float(stack, out_offset, data);
- break;
- }
-# endif
case NODE_INFO_CURVE_TANGENT_NORMAL: {
data3 = curve_tangent_normal(kg, sd);
stack_store_float3(stack, out_offset, data3);
@@ -258,4 +251,28 @@ ccl_device_noinline void svm_node_hair_info(KernelGlobals kg,
}
#endif
+#ifdef __POINTCLOUD__
+
+/* Point Info */
+
+ccl_device_noinline void svm_node_point_info(KernelGlobals kg,
+ ccl_private ShaderData *sd,
+ ccl_private float *stack,
+ uint type,
+ uint out_offset)
+{
+ switch (type) {
+ case NODE_INFO_POINT_POSITION:
+ stack_store_float3(stack, out_offset, point_position(kg, sd));
+ break;
+ case NODE_INFO_POINT_RADIUS:
+ stack_store_float(stack, out_offset, point_radius(kg, sd));
+ break;
+ case NODE_INFO_POINT_RANDOM:
+ break; /* handled as attribute */
+ }
+}
+
+#endif
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index b226bc66771..35d4c3f2055 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -454,13 +454,14 @@ ccl_device void svm_eval_nodes(KernelGlobals kg,
break;
#if defined(__HAIR__)
case NODE_HAIR_INFO:
- IF_KERNEL_NODES_FEATURE(HAIR)
- {
- svm_node_hair_info(kg, sd, stack, node.y, node.z);
- }
+ svm_node_hair_info(kg, sd, stack, node.y, node.z);
+ break;
+#endif
+#if defined(__POINTCLOUD__)
+ case NODE_POINT_INFO:
+ svm_node_point_info(kg, sd, stack, node.y, node.z);
break;
#endif
-
case NODE_TEXTURE_MAPPING:
offset = svm_node_texture_mapping(kg, sd, stack, node.y, node.z, offset);
break;
diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h
index dd1b1f9bc28..16e9fd8862a 100644
--- a/intern/cycles/kernel/svm/types.h
+++ b/intern/cycles/kernel/svm/types.h
@@ -81,6 +81,7 @@ typedef enum ShaderNodeType {
NODE_OBJECT_INFO,
NODE_PARTICLE_INFO,
NODE_HAIR_INFO,
+ NODE_POINT_INFO,
NODE_TEXTURE_MAPPING,
NODE_MAPPING,
NODE_MIN_MAX,
@@ -176,12 +177,16 @@ typedef enum NodeHairInfo {
NODE_INFO_CURVE_INTERCEPT,
NODE_INFO_CURVE_LENGTH,
NODE_INFO_CURVE_THICKNESS,
- /* Fade for minimum hair width transiency. */
- // NODE_INFO_CURVE_FADE,
NODE_INFO_CURVE_TANGENT_NORMAL,
NODE_INFO_CURVE_RANDOM,
} NodeHairInfo;
+typedef enum NodePointInfo {
+ NODE_INFO_POINT_POSITION,
+ NODE_INFO_POINT_RADIUS,
+ NODE_INFO_POINT_RANDOM,
+} NodePointInfo;
+
typedef enum NodeLightPath {
NODE_LP_camera = 0,
NODE_LP_shadow,
diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h
index 5d41abb53c4..d4cb22d4af4 100644
--- a/intern/cycles/kernel/types.h
+++ b/intern/cycles/kernel/types.h
@@ -512,12 +512,21 @@ typedef struct differential {
/* Ray */
+typedef struct RaySelfPrimitives {
+ int prim; /* Primitive the ray is starting from */
+ int object; /* Instance prim is a part of */
+ int light_prim; /* Light primitive */
+ int light_object; /* Light object */
+} RaySelfPrimitives;
+
typedef struct Ray {
float3 P; /* origin */
float3 D; /* direction */
float t; /* length of the ray */
float time; /* time (for motion blur) */
+ RaySelfPrimitives self;
+
#ifdef __RAY_DIFFERENTIALS__
float dP;
float dD;
@@ -1565,21 +1574,21 @@ enum KernelFeatureFlag : uint32_t {
KERNEL_FEATURE_NODE_BSDF = (1U << 0U),
KERNEL_FEATURE_NODE_EMISSION = (1U << 1U),
KERNEL_FEATURE_NODE_VOLUME = (1U << 2U),
- KERNEL_FEATURE_NODE_HAIR = (1U << 3U),
- KERNEL_FEATURE_NODE_BUMP = (1U << 4U),
- KERNEL_FEATURE_NODE_BUMP_STATE = (1U << 5U),
- KERNEL_FEATURE_NODE_VORONOI_EXTRA = (1U << 6U),
- KERNEL_FEATURE_NODE_RAYTRACE = (1U << 7U),
- KERNEL_FEATURE_NODE_AOV = (1U << 8U),
- KERNEL_FEATURE_NODE_LIGHT_PATH = (1U << 9U),
+ KERNEL_FEATURE_NODE_BUMP = (1U << 3U),
+ KERNEL_FEATURE_NODE_BUMP_STATE = (1U << 4U),
+ KERNEL_FEATURE_NODE_VORONOI_EXTRA = (1U << 5U),
+ KERNEL_FEATURE_NODE_RAYTRACE = (1U << 6U),
+ KERNEL_FEATURE_NODE_AOV = (1U << 7U),
+ KERNEL_FEATURE_NODE_LIGHT_PATH = (1U << 8U),
/* Use denoising kernels and output denoising passes. */
- KERNEL_FEATURE_DENOISING = (1U << 10U),
+ KERNEL_FEATURE_DENOISING = (1U << 9U),
/* Use path tracing kernels. */
- KERNEL_FEATURE_PATH_TRACING = (1U << 11U),
+ KERNEL_FEATURE_PATH_TRACING = (1U << 10U),
/* BVH/sampling kernel features. */
+ KERNEL_FEATURE_POINTCLOUD = (1U << 11U),
KERNEL_FEATURE_HAIR = (1U << 12U),
KERNEL_FEATURE_HAIR_THICK = (1U << 13U),
KERNEL_FEATURE_OBJECT_MOTION = (1U << 14U),
@@ -1616,9 +1625,6 @@ enum KernelFeatureFlag : uint32_t {
KERNEL_FEATURE_AO_PASS = (1U << 25U),
KERNEL_FEATURE_AO_ADDITIVE = (1U << 26U),
KERNEL_FEATURE_AO = (KERNEL_FEATURE_AO_PASS | KERNEL_FEATURE_AO_ADDITIVE),
-
- /* Point clouds. */
- KERNEL_FEATURE_POINTCLOUD = (1U << 27U),
};
/* Shader node feature mask, to specialize shader evaluation for kernels. */
@@ -1628,7 +1634,7 @@ enum KernelFeatureFlag : uint32_t {
KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW \
(KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \
- KERNEL_FEATURE_NODE_HAIR | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE | \
+ KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE | \
KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_SURFACE \
(KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW | KERNEL_FEATURE_NODE_RAYTRACE | \
diff --git a/intern/cycles/scene/colorspace.cpp b/intern/cycles/scene/colorspace.cpp
index c1a308fcbaa..f0b7eb724de 100644
--- a/intern/cycles/scene/colorspace.cpp
+++ b/intern/cycles/scene/colorspace.cpp
@@ -263,7 +263,9 @@ template<typename T> inline void cast_from_float4(T *data, float4 value)
/* Slower versions for other all data types, which needs to convert to float and back. */
template<typename T, bool compress_as_srgb = false>
-inline void processor_apply_pixels(const OCIO::Processor *processor, T *pixels, size_t num_pixels)
+inline void processor_apply_pixels_rgba(const OCIO::Processor *processor,
+ T *pixels,
+ size_t num_pixels)
{
/* TODO: implement faster version for when we know the conversion
* is a simple matrix transform between linear spaces. In that case
@@ -310,25 +312,79 @@ inline void processor_apply_pixels(const OCIO::Processor *processor, T *pixels,
}
}
}
+
+template<typename T, bool compress_as_srgb = false>
+inline void processor_apply_pixels_grayscale(const OCIO::Processor *processor,
+ T *pixels,
+ size_t num_pixels)
+{
+ OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
+
+ /* Process large images in chunks to keep temporary memory requirement down. */
+ const size_t chunk_size = std::min((size_t)(16 * 1024 * 1024), num_pixels);
+ vector<float> float_pixels(chunk_size * 3);
+
+ for (size_t j = 0; j < num_pixels; j += chunk_size) {
+ size_t width = std::min(chunk_size, num_pixels - j);
+
+ /* Convert to 3 channels, since that's the minimum required by OpenColorIO. */
+ {
+ const T *pixel = pixels + j;
+ float *fpixel = float_pixels.data();
+ for (size_t i = 0; i < width; i++, pixel++, fpixel += 3) {
+ const float f = util_image_cast_to_float<T>(*pixel);
+ fpixel[0] = f;
+ fpixel[1] = f;
+ fpixel[2] = f;
+ }
+ }
+
+ OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, 1, 3);
+ device_processor->apply(desc);
+
+ {
+ T *pixel = pixels + j;
+ const float *fpixel = float_pixels.data();
+ for (size_t i = 0; i < width; i++, pixel++, fpixel += 3) {
+ float f = average(make_float3(fpixel[0], fpixel[1], fpixel[2]));
+ if (compress_as_srgb) {
+ f = color_linear_to_srgb(f);
+ }
+ *pixel = util_image_cast_from_float<T>(f);
+ }
+ }
+ }
+}
+
#endif
template<typename T>
-void ColorSpaceManager::to_scene_linear(ustring colorspace,
- T *pixels,
- size_t num_pixels,
- bool compress_as_srgb)
+void ColorSpaceManager::to_scene_linear(
+ ustring colorspace, T *pixels, size_t num_pixels, bool is_rgba, bool compress_as_srgb)
{
#ifdef WITH_OCIO
const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);
if (processor) {
- if (compress_as_srgb) {
- /* Compress output as sRGB. */
- processor_apply_pixels<T, true>(processor, pixels, num_pixels);
+ if (is_rgba) {
+ if (compress_as_srgb) {
+ /* Compress output as sRGB. */
+ processor_apply_pixels_rgba<T, true>(processor, pixels, num_pixels);
+ }
+ else {
+ /* Write output as scene linear directly. */
+ processor_apply_pixels_rgba<T>(processor, pixels, num_pixels);
+ }
}
else {
- /* Write output as scene linear directly. */
- processor_apply_pixels<T>(processor, pixels, num_pixels);
+ if (compress_as_srgb) {
+ /* Compress output as sRGB. */
+ processor_apply_pixels_grayscale<T, true>(processor, pixels, num_pixels);
+ }
+ else {
+ /* Write output as scene linear directly. */
+ processor_apply_pixels_grayscale<T>(processor, pixels, num_pixels);
+ }
}
}
#else
@@ -348,6 +404,11 @@ void ColorSpaceManager::to_scene_linear(ColorSpaceProcessor *processor_,
if (processor) {
OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
+ if (channels == 1) {
+ float3 rgb = make_float3(pixel[0], pixel[0], pixel[0]);
+ device_processor->applyRGB(&rgb.x);
+ pixel[0] = average(rgb);
+ }
if (channels == 3) {
device_processor->applyRGB(pixel);
}
@@ -390,9 +451,9 @@ void ColorSpaceManager::free_memory()
}
/* Template instantiations so we don't have to inline functions. */
-template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, bool);
-template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, bool);
-template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, bool);
-template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, bool);
+template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, bool, bool);
+template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, bool, bool);
+template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, bool, bool);
+template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, bool, bool);
CCL_NAMESPACE_END
diff --git a/intern/cycles/scene/colorspace.h b/intern/cycles/scene/colorspace.h
index 7f7bc604f07..f02c1231a44 100644
--- a/intern/cycles/scene/colorspace.h
+++ b/intern/cycles/scene/colorspace.h
@@ -43,10 +43,8 @@ class ColorSpaceManager {
/* Convert pixels in the specified colorspace to scene linear color for
* rendering. Must be a colorspace returned from detect_known_colorspace. */
template<typename T>
- static void to_scene_linear(ustring colorspace,
- T *pixels,
- size_t num_pixels,
- bool compress_as_srgb);
+ static void to_scene_linear(
+ ustring colorspace, T *pixels, size_t num_pixels, bool is_rgba, bool compress_as_srgb);
/* Efficiently convert pixels to scene linear colorspace at render time,
* for OSL where the image texture cache contains original pixels. The
diff --git a/intern/cycles/scene/constant_fold.cpp b/intern/cycles/scene/constant_fold.cpp
index a5fb68bf229..e9fb3426b70 100644
--- a/intern/cycles/scene/constant_fold.cpp
+++ b/intern/cycles/scene/constant_fold.cpp
@@ -441,9 +441,13 @@ void ConstantFolder::fold_mapping(NodeMappingType type) const
if (is_zero(scale_in)) {
make_zero();
}
- else if ((is_zero(location_in) || type == NODE_MAPPING_TYPE_VECTOR ||
- type == NODE_MAPPING_TYPE_NORMAL) &&
- is_zero(rotation_in) && is_one(scale_in)) {
+ else if (
+ /* Can't constant fold since we always need to normalize the output. */
+ (type != NODE_MAPPING_TYPE_NORMAL) &&
+ /* Check all use values are zero, note location is not used by vector and normal types. */
+ (is_zero(location_in) || type == NODE_MAPPING_TYPE_VECTOR ||
+ type == NODE_MAPPING_TYPE_NORMAL) &&
+ is_zero(rotation_in) && is_one(scale_in)) {
try_bypass_or_make_constant(vector_in);
}
}
diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp
index 49d18d00dd7..90f1e1cb021 100644
--- a/intern/cycles/scene/geometry.cpp
+++ b/intern/cycles/scene/geometry.cpp
@@ -236,6 +236,7 @@ void Geometry::compute_bvh(
BVHParams bparams;
bparams.use_spatial_split = params->use_bvh_spatial_split;
+ bparams.use_compact_structure = params->use_bvh_compact_structure;
bparams.bvh_layout = bvh_layout;
bparams.use_unaligned_nodes = dscene->data.bvh.have_curves &&
params->use_bvh_unaligned_nodes;
diff --git a/intern/cycles/scene/image.cpp b/intern/cycles/scene/image.cpp
index 3595ca55a46..7aad46d253c 100644
--- a/intern/cycles/scene/image.cpp
+++ b/intern/cycles/scene/image.cpp
@@ -576,13 +576,13 @@ bool ImageManager::file_load_image(Image *img, int texture_limit)
pixels[i * 4 + 3] = one;
}
}
+ }
- if (img->metadata.colorspace != u_colorspace_raw &&
- img->metadata.colorspace != u_colorspace_srgb) {
- /* Convert to scene linear. */
- ColorSpaceManager::to_scene_linear(
- img->metadata.colorspace, pixels, num_pixels, img->metadata.compress_as_srgb);
- }
+ if (img->metadata.colorspace != u_colorspace_raw &&
+ img->metadata.colorspace != u_colorspace_srgb) {
+ /* Convert to scene linear. */
+ ColorSpaceManager::to_scene_linear(
+ img->metadata.colorspace, pixels, num_pixels, is_rgba, img->metadata.compress_as_srgb);
}
/* Make sure we don't have buggy values. */
@@ -891,6 +891,10 @@ void ImageManager::device_free(Device *device)
void ImageManager::collect_statistics(RenderStats *stats)
{
foreach (const Image *image, images) {
+ if (!image) {
+ /* Image may have been freed due to lack of users. */
+ continue;
+ }
stats->image.textures.add_entry(
NamedSizeEntry(image->loader->name(), image->mem->memory_size()));
}
diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp
index 1963ebbbb19..b5b8eee24a7 100644
--- a/intern/cycles/scene/scene.cpp
+++ b/intern/cycles/scene/scene.cpp
@@ -570,7 +570,6 @@ static void log_kernel_features(const uint features)
<< "\n";
VLOG(2) << "Use Emission " << string_from_bool(features & KERNEL_FEATURE_NODE_EMISSION) << "\n";
VLOG(2) << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_NODE_VOLUME) << "\n";
- VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_NODE_HAIR) << "\n";
VLOG(2) << "Use Bump " << string_from_bool(features & KERNEL_FEATURE_NODE_BUMP) << "\n";
VLOG(2) << "Use Voronoi " << string_from_bool(features & KERNEL_FEATURE_NODE_VORONOI_EXTRA)
<< "\n";
diff --git a/intern/cycles/scene/scene.h b/intern/cycles/scene/scene.h
index ec935b41be6..77268837070 100644
--- a/intern/cycles/scene/scene.h
+++ b/intern/cycles/scene/scene.h
@@ -160,6 +160,7 @@ class SceneParams {
BVHType bvh_type;
bool use_bvh_spatial_split;
+ bool use_bvh_compact_structure;
bool use_bvh_unaligned_nodes;
int num_bvh_time_steps;
int hair_subdivisions;
@@ -174,6 +175,7 @@ class SceneParams {
bvh_layout = BVH_LAYOUT_BVH2;
bvh_type = BVH_TYPE_DYNAMIC;
use_bvh_spatial_split = false;
+ use_bvh_compact_structure = true;
use_bvh_unaligned_nodes = true;
num_bvh_time_steps = 0;
hair_subdivisions = 3;
@@ -187,6 +189,7 @@ class SceneParams {
return !(shadingsystem == params.shadingsystem && bvh_layout == params.bvh_layout &&
bvh_type == params.bvh_type &&
use_bvh_spatial_split == params.use_bvh_spatial_split &&
+ use_bvh_compact_structure == params.use_bvh_compact_structure &&
use_bvh_unaligned_nodes == params.use_bvh_unaligned_nodes &&
num_bvh_time_steps == params.num_bvh_time_steps &&
hair_subdivisions == params.hair_subdivisions && hair_shape == params.hair_shape &&
diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp
index e8316ad41b4..34675be8e80 100644
--- a/intern/cycles/scene/shader_nodes.cpp
+++ b/intern/cycles/scene/shader_nodes.cpp
@@ -32,6 +32,7 @@
#include "util/color.h"
#include "util/foreach.h"
#include "util/log.h"
+#include "util/string.h"
#include "util/transform.h"
#include "kernel/tables.h"
@@ -462,8 +463,12 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
const ustring known_colorspace = metadata.colorspace;
if (handle.svm_slot() == -1) {
+ /* OIIO currently does not support <UVTILE> substitutions natively. Replace with a format they
+ * understand. */
+ std::string osl_filename = filename.string();
+ string_replace(osl_filename, "<UVTILE>", "<U>_<V>");
compiler.parameter_texture(
- "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace);
+ "filename", ustring(osl_filename), compress_as_srgb ? u_colorspace_raw : known_colorspace);
}
else {
compiler.parameter_texture("filename", handle.svm_slot());
@@ -472,7 +477,8 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) ||
alpha_type == IMAGE_ALPHA_CHANNEL_PACKED ||
alpha_type == IMAGE_ALPHA_IGNORE);
- const bool is_tiled = (filename.find("<UDIM>") != string::npos);
+ const bool is_tiled = (filename.find("<UDIM>") != string::npos ||
+ filename.find("<UVTILE>") != string::npos);
compiler.parameter(this, "projection");
compiler.parameter(this, "projection_blend");
@@ -4388,9 +4394,6 @@ NODE_DEFINE(HairInfoNode)
SOCKET_OUT_FLOAT(size, "Length");
SOCKET_OUT_FLOAT(thickness, "Thickness");
SOCKET_OUT_NORMAL(tangent_normal, "Tangent Normal");
-#if 0 /* Output for minimum hair width transparency - deactivated. */
- SOCKET_OUT_FLOAT(fade, "Fade");
-#endif
SOCKET_OUT_FLOAT(index, "Random");
return type;
@@ -4448,12 +4451,7 @@ void HairInfoNode::compile(SVMCompiler &compiler)
if (!out->links.empty()) {
compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_TANGENT_NORMAL, compiler.stack_assign(out));
}
-#if 0
- out = output("Fade");
- if(!out->links.empty()) {
- compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_FADE, compiler.stack_assign(out));
- }
-#endif
+
out = output("Random");
if (!out->links.empty()) {
int attr = compiler.attribute(ATTR_STD_CURVE_RANDOM);
@@ -4466,6 +4464,59 @@ void HairInfoNode::compile(OSLCompiler &compiler)
compiler.add(this, "node_hair_info");
}
+/* Point Info */
+
+NODE_DEFINE(PointInfoNode)
+{
+ NodeType *type = NodeType::add("point_info", create, NodeType::SHADER);
+
+ SOCKET_OUT_POINT(position, "Position");
+ SOCKET_OUT_FLOAT(radius, "Radius");
+ SOCKET_OUT_FLOAT(random, "Random");
+
+ return type;
+}
+
+PointInfoNode::PointInfoNode() : ShaderNode(get_node_type())
+{
+}
+
+void PointInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes)
+{
+ if (shader->has_surface_link()) {
+ if (!output("Random")->links.empty())
+ attributes->add(ATTR_STD_POINT_RANDOM);
+ }
+
+ ShaderNode::attributes(shader, attributes);
+}
+
+void PointInfoNode::compile(SVMCompiler &compiler)
+{
+ ShaderOutput *out;
+
+ out = output("Position");
+ if (!out->links.empty()) {
+ compiler.add_node(NODE_POINT_INFO, NODE_INFO_POINT_POSITION, compiler.stack_assign(out));
+ }
+
+ out = output("Radius");
+ if (!out->links.empty()) {
+ compiler.add_node(NODE_POINT_INFO, NODE_INFO_POINT_RADIUS, compiler.stack_assign(out));
+ }
+
+ out = output("Random");
+ if (!out->links.empty()) {
+ int attr = compiler.attribute(ATTR_STD_POINT_RANDOM);
+ compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT);
+ }
+}
+
+void PointInfoNode::compile(OSLCompiler &compiler)
+{
+ compiler.add(this, "node_point_info");
+}
+
/* Volume Info */
NODE_DEFINE(VolumeInfoNode)
diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h
index 0faefd3041f..a8d5bdcf157 100644
--- a/intern/cycles/scene/shader_nodes.h
+++ b/intern/cycles/scene/shader_nodes.h
@@ -1005,9 +1005,20 @@ class HairInfoNode : public ShaderNode {
{
return true;
}
- virtual int get_feature()
+};
+
+class PointInfoNode : public ShaderNode {
+ public:
+ SHADER_NODE_CLASS(PointInfoNode)
+
+ void attributes(Shader *shader, AttributeRequestSet *attributes);
+ bool has_attribute_dependency()
+ {
+ return true;
+ }
+ bool has_spatial_varying()
{
- return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_HAIR;
+ return true;
}
};
diff --git a/intern/cycles/util/math.h b/intern/cycles/util/math.h
index 18b60b70a4b..605a19aaef0 100644
--- a/intern/cycles/util/math.h
+++ b/intern/cycles/util/math.h
@@ -401,7 +401,7 @@ ccl_device_inline float fractf(float x)
return x - floorf(x);
}
-/* Adapted from godot-engine math_funcs.h. */
+/* Adapted from `godot-engine` math_funcs.h. */
ccl_device_inline float wrapf(float value, float max, float min)
{
float range = max - min;
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 84f156949aa..34d1ab1150e 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -291,7 +291,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
include(CheckSymbolExists)
set(CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE")
check_symbol_exists(memfd_create "sys/mman.h" HAVE_MEMFD_CREATE)
- if (HAVE_MEMFD_CREATE)
+ if(HAVE_MEMFD_CREATE)
add_definitions(-DHAVE_MEMFD_CREATE)
endif()
@@ -307,7 +307,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner)
pkg_check_modules(wayland-protocols wayland-protocols>=1.15)
- if (${wayland-protocols_FOUND})
+ if(${wayland-protocols_FOUND})
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
else()
find_path(WAYLAND_PROTOCOLS_DIR
@@ -316,7 +316,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
)
endif()
- if (NOT EXISTS ${WAYLAND_PROTOCOLS_DIR})
+ if(NOT EXISTS ${WAYLAND_PROTOCOLS_DIR})
message(FATAL_ERROR "path to wayland-protocols not found")
endif()
@@ -518,11 +518,11 @@ if(WITH_XR_OPENXR)
)
elseif(UNIX AND NOT APPLE)
list(APPEND XR_PLATFORM_DEFINES -DXR_OS_LINUX)
- if (WITH_GHOST_WAYLAND)
+ if(WITH_GHOST_WAYLAND)
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_WAYLAND)
endif()
- if (WITH_GHOST_X11)
- if (WITH_GL_EGL)
+ if(WITH_GHOST_X11)
+ if(WITH_GL_EGL)
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_EGL)
else()
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_XLIB)
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index 98094cc0669..38adf81f877 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -889,16 +889,11 @@ extern char *GHOST_getClipboard(bool selection);
extern void GHOST_putClipboard(const char *buffer, bool selection);
/**
- * Toggles console
- * \param action:
- * - 0: Hides
- * - 1: Shows
- * - 2: Toggles
- * - 3: Hides if it runs not from command line
- * - *: Does nothing
+ * Set the Console State
+ * \param action: console state
* \return current status (1 -visible, 0 - hidden)
*/
-extern int GHOST_toggleConsole(int action);
+extern int setConsoleWindowState(GHOST_TConsoleWindowState action);
/**
* Use native pixel size (MacBook pro 'retina'), if supported.
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index 05c6c9d907f..3edc7605d41 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -411,16 +411,11 @@ class GHOST_ISystem {
#endif
/**
- * Toggles console
- * \param action:
- * - 0: Hides.
- * - 1: Shows
- * - 2: Toggles
- * - 3: Hides if it runs not from command line
- * - *: Does nothing
+ * Set the Console State
+ * \param action: console state
* \return current status (1 -visible, 0 - hidden)
*/
- virtual int toggleConsole(int action) = 0;
+ virtual int setConsoleWindowState(GHOST_TConsoleWindowState action) = 0;
/***************************************************************************************
* Access to clipboard.
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index 7fe9300ec3f..4e190d09234 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -140,6 +140,13 @@ typedef enum {
// GHOST_kWindowStateUnModified,
} GHOST_TWindowState;
+typedef enum {
+ GHOST_kConsoleWindowStateHide = 0,
+ GHOST_kConsoleWindowStateShow,
+ GHOST_kConsoleWindowStateToggle,
+ GHOST_kConsoleWindowStateHideForNonConsoleLaunch
+} GHOST_TConsoleWindowState;
+
typedef enum { GHOST_kWindowOrderTop = 0, GHOST_kWindowOrderBottom } GHOST_TWindowOrder;
typedef enum {
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index a21c3a90c06..a2d1c143316 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -809,10 +809,10 @@ void GHOST_putClipboard(const char *buffer, bool selection)
system->putClipboard(buffer, selection);
}
-int GHOST_toggleConsole(int action)
+int setConsoleWindowState(GHOST_TConsoleWindowState action)
{
GHOST_ISystem *system = GHOST_ISystem::getSystem();
- return system->toggleConsole(action);
+ return system->setConsoleWindowState(action);
}
int GHOST_UseNativePixels(void)
diff --git a/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm b/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm
index 96898a15f62..35ea4c12818 100644
--- a/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm
+++ b/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm
@@ -13,9 +13,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- Damien Plisson 10/2009
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2009 Damien Plisson
*/
#include <Cocoa/Cocoa.h>
diff --git a/intern/ghost/intern/GHOST_DisplayManagerX11.cpp b/intern/ghost/intern/GHOST_DisplayManagerX11.cpp
index 2ebdf8b3f2a..59abf9e51e4 100644
--- a/intern/ghost/intern/GHOST_DisplayManagerX11.cpp
+++ b/intern/ghost/intern/GHOST_DisplayManagerX11.cpp
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- * Video mode switching
- * Copyright (C) 1997-2001 Id Software, Inc.
- * Ported from Quake 2 by Alex Fraser <alex@phatcore.com>
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 1997-2001 Id Software, Inc. Video mode switching.
+ * Ported from Quake 2 by Alex Fraser <alex@phatcore.com>.
*/
/** \file
diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp
index d1fc80adf56..8366cbba869 100644
--- a/intern/ghost/intern/GHOST_ImeWin32.cpp
+++ b/intern/ghost/intern/GHOST_ImeWin32.cpp
@@ -14,9 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
- * All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
@@ -96,7 +93,7 @@ bool GHOST_ImeWin32::IsEnglishMode()
!(conversion_modes_ & (IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE));
}
-bool GHOST_ImeWin32::IsImeKeyEvent(char ascii)
+bool GHOST_ImeWin32::IsImeKeyEvent(char ascii, GHOST_TKey key)
{
if (!(IsEnglishMode())) {
/* In Chinese, Japanese, Korean, all alpha keys are processed by IME. */
@@ -106,7 +103,8 @@ bool GHOST_ImeWin32::IsImeKeyEvent(char ascii)
if (IsLanguage(IMELANG_JAPANESE) && (ascii >= ' ' && ascii <= '~')) {
return true;
}
- else if (IsLanguage(IMELANG_CHINESE) && ascii && strchr("!\"$'(),.:;<>?[\\]^_`/", ascii)) {
+ else if (IsLanguage(IMELANG_CHINESE) && ascii && strchr("!\"$'(),.:;<>?[\\]^_`/", ascii) &&
+ !(key == GHOST_kKeyNumpadPeriod)) {
return true;
}
}
diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h
index ce0e4d64d53..d17740c42d3 100644
--- a/intern/ghost/intern/GHOST_ImeWin32.h
+++ b/intern/ghost/intern/GHOST_ImeWin32.h
@@ -14,9 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
- * All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
@@ -161,7 +158,7 @@ class GHOST_ImeWin32 {
bool IsEnglishMode();
/* Checks a key whether IME has to do handling. */
- bool IsImeKeyEvent(char ascii);
+ bool IsImeKeyEvent(char ascii, GHOST_TKey key);
/**
* Create the IME windows, and allocate required resources for them.
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h
index 5950da6813d..02216f9d1f8 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.h
+++ b/intern/ghost/intern/GHOST_SystemCocoa.h
@@ -244,7 +244,7 @@ class GHOST_SystemCocoa : public GHOST_System {
/**
* \see GHOST_ISystem
*/
- int toggleConsole(int action)
+ int setConsoleWindowState(GHOST_TConsoleWindowState action)
{
return 0;
}
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index b92c3e73a88..756c4f876ed 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -890,7 +890,7 @@ bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
bool anyProcessed = false;
NSEvent *event;
- // TODO : implement timer ??
+ /* TODO: implement timer? */
#if 0
do {
GHOST_TimerManager* timerMgr = getTimerManager();
diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h
index 5dbc42b53a2..53cd97e8e57 100644
--- a/intern/ghost/intern/GHOST_SystemNULL.h
+++ b/intern/ghost/intern/GHOST_SystemNULL.h
@@ -40,7 +40,7 @@ class GHOST_SystemNULL : public GHOST_System {
{
return false;
}
- int toggleConsole(int action)
+ int setConsoleWindowState(GHOST_TConsoleWindowState action)
{
return 0;
}
diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h
index 051bb6777b1..8706dd0daa7 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.h
+++ b/intern/ghost/intern/GHOST_SystemSDL.h
@@ -47,7 +47,7 @@ class GHOST_SystemSDL : public GHOST_System {
bool processEvents(bool waitForEvent);
- int toggleConsole(int action)
+ int setConsoleWindowState(GHOST_TConsoleWindowState action)
{
return 0;
}
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 38700845405..27b37d0d326 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -1473,7 +1473,7 @@ bool GHOST_SystemWayland::processEvents(bool waitForEvent)
return fired || (getEventManager()->getNumEvents() > 0);
}
-int GHOST_SystemWayland::toggleConsole(int /*action*/)
+int GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*action*/)
{
return 0;
}
diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h
index 9f02afb9d5a..b11c243aca8 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.h
+++ b/intern/ghost/intern/GHOST_SystemWayland.h
@@ -53,7 +53,7 @@ class GHOST_SystemWayland : public GHOST_System {
bool processEvents(bool waitForEvent) override;
- int toggleConsole(int action) override;
+ int setConsoleWindowState(GHOST_TConsoleWindowState action) override;
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override;
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 5251dd01b29..2f5395fc8d0 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -105,6 +105,8 @@
*/
#define BROKEN_PEEK_TOUCHPAD
+static bool isStartedFromCommandPrompt();
+
static void initRawInput()
{
#ifdef WITH_INPUT_NDOF
@@ -166,7 +168,10 @@ GHOST_SystemWin32::~GHOST_SystemWin32()
{
// Shutdown COM
OleUninitialize();
- toggleConsole(1);
+
+ if (isStartedFromCommandPrompt()) {
+ setConsoleWindowState(GHOST_kConsoleWindowStateShow);
+ }
}
uint64_t GHOST_SystemWin32::performanceCounterToMillis(__int64 perf_ticks) const
@@ -1220,7 +1225,7 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA
}
#ifdef WITH_INPUT_IME
- if (window->getImeInput()->IsImeKeyEvent(ascii)) {
+ if (window->getImeInput()->IsImeKeyEvent(ascii, key)) {
return NULL;
}
#endif /* WITH_INPUT_IME */
@@ -2216,31 +2221,30 @@ static bool isStartedFromCommandPrompt()
return false;
}
-int GHOST_SystemWin32::toggleConsole(int action)
+int GHOST_SystemWin32::setConsoleWindowState(GHOST_TConsoleWindowState action)
{
HWND wnd = GetConsoleWindow();
switch (action) {
- case 3: // startup: hide if not started from command prompt
- {
+ case GHOST_kConsoleWindowStateHideForNonConsoleLaunch: {
if (!isStartedFromCommandPrompt()) {
ShowWindow(wnd, SW_HIDE);
m_consoleStatus = 0;
}
break;
}
- case 0: // hide
+ case GHOST_kConsoleWindowStateHide:
ShowWindow(wnd, SW_HIDE);
m_consoleStatus = 0;
break;
- case 1: // show
+ case GHOST_kConsoleWindowStateShow:
ShowWindow(wnd, SW_SHOW);
if (!isStartedFromCommandPrompt()) {
DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
}
m_consoleStatus = 1;
break;
- case 2: // toggle
+ case GHOST_kConsoleWindowStateToggle:
ShowWindow(wnd, m_consoleStatus ? SW_HIDE : SW_SHOW);
m_consoleStatus = !m_consoleStatus;
if (m_consoleStatus && !isStartedFromCommandPrompt()) {
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 4794982dc65..4f0fd6e6801 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -436,16 +436,11 @@ class GHOST_SystemWin32 : public GHOST_System {
static LRESULT WINAPI s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
/**
- * Toggles console
- * \param action:
- * - 0 - Hides
- * - 1 - Shows
- * - 2 - Toggles
- * - 3 - Hides if it runs not from command line
- * - * - Does nothing
+ * Set the Console State
+ * \param action: console state
* \return current status (1 -visible, 0 - hidden)
*/
- int toggleConsole(int action);
+ int setConsoleWindowState(GHOST_TConsoleWindowState action);
/** The current state of the modifier keys. */
GHOST_ModifierKeys m_modifierKeys;
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 85504bd94fb..941642978c0 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -13,10 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- * Part of this code has been taken from Qt, under LGPL license
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * Part of this code has been taken from Qt, under LGPL license.
*/
/** \file
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index d4803f88fbb..76011d3522b 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -269,7 +269,7 @@ class GHOST_SystemX11 : public GHOST_System {
/**
* \see GHOST_ISystem
*/
- int toggleConsole(int /*action*/)
+ int setConsoleWindowState(GHOST_TConsoleWindowState /*action*/)
{
return 0;
}
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index b5d0fd8e6db..47d4ff77d17 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -71,6 +71,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_mousePresent(false),
m_inLiveResize(false),
m_system(system),
+ m_dropTarget(NULL),
+ m_hWnd(0),
m_hDC(0),
m_isDialog(dialog),
m_hasMouseCaptured(false),
@@ -78,6 +80,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_nPressedButtons(0),
m_customCursor(0),
m_wantAlphaBackground(alphaBackground),
+ m_Bar(NULL),
m_wintab(NULL),
m_lastPointerTabletData(GHOST_TABLET_DATA_NONE),
m_normal_state(GHOST_kWindowStateNormal),
@@ -129,8 +132,24 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_hDC = ::GetDC(m_hWnd);
if (!setDrawingContextType(type)) {
+ const char *title = "Blender - Unsupported Graphics Card Configuration";
+ const char *text =
+ "A graphics card and driver with support for OpenGL 3.3 or higher is "
+ "required.\n\nInstalling the latest driver for your graphics card might resolve the "
+ "issue.";
+ if (GetSystemMetrics(SM_CMONITORS) > 1) {
+ text =
+ "A graphics card and driver with support for OpenGL 3.3 or higher is "
+ "required.\n\nPlugging all monitors into your primary graphics card might resolve "
+ "this issue. Installing the latest driver for your graphics card could also help.";
+ }
+ MessageBox(m_hWnd, text, title, MB_OK | MB_ICONERROR);
+ ::ReleaseDC(m_hWnd, m_hDC);
::DestroyWindow(m_hWnd);
m_hWnd = NULL;
+ if (!parentwindow) {
+ exit(0);
+ }
return;
}
@@ -564,20 +583,13 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty
(m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
- if (context->initializeDrawingContext()) {
- return context;
- }
- else {
- MessageBox(m_hWnd,
- "A graphics card and driver with support for OpenGL 3.3 or higher is required.\n"
- "Installing the latest driver for your graphics card may resolve the issue.\n\n"
- "The program will now close.",
- "Blender - Unsupported Graphics Card or Driver",
- MB_OK | MB_ICONERROR);
+ if (context && !context->initializeDrawingContext()) {
delete context;
- exit(0);
+ context = nullptr;
}
+ return context;
+
#elif defined(WITH_GL_PROFILE_COMPAT)
// ask for 2.1 context, driver gives any GL version >= 2.1
// (hopefully the latest compatibility profile)
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index 15b40690d83..9fa93f0c4b4 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -86,6 +86,7 @@ void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info)
initApiLayers();
initExtensions();
if (isDebugMode()) {
+ printSDKVersion();
printAvailableAPILayersAndExtensionsInfo();
}
@@ -156,6 +157,16 @@ void GHOST_XrContext::storeInstanceProperties()
/** \name Debug Printing
* \{ */
+void GHOST_XrContext::printSDKVersion()
+{
+ const XrVersion sdk_version = XR_CURRENT_API_VERSION;
+
+ printf("OpenXR SDK Version: %u.%u.%u\n",
+ XR_VERSION_MAJOR(sdk_version),
+ XR_VERSION_MINOR(sdk_version),
+ XR_VERSION_PATCH(sdk_version));
+}
+
void GHOST_XrContext::printInstanceInfo()
{
assert(m_oxr->instance != XR_NULL_HANDLE);
diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h
index 479b50e1537..b00f017ff59 100644
--- a/intern/ghost/intern/GHOST_XrContext.h
+++ b/intern/ghost/intern/GHOST_XrContext.h
@@ -126,6 +126,7 @@ class GHOST_XrContext : public GHOST_IXrContext {
void storeInstanceProperties();
void initDebugMessenger();
+ void printSDKVersion();
void printInstanceInfo();
void printAvailableAPILayersAndExtensionsInfo();
void printExtensionsAndAPILayersToEnable();
diff --git a/intern/ghost/test/CMakeLists.txt b/intern/ghost/test/CMakeLists.txt
index 37bb00332dd..c564085c774 100644
--- a/intern/ghost/test/CMakeLists.txt
+++ b/intern/ghost/test/CMakeLists.txt
@@ -292,7 +292,7 @@ target_link_libraries(multitest_c
guardedalloc_lib
wcwidth_lib
${OPENGL_gl_LIBRARY}
- ${FREETYPE_LIBRARY}
+ ${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES}
${ZLIB_LIBRARIES}
${CMAKE_DL_LIBS}
${PLATFORM_LINKLIBS}
diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h
index 8a20323dcfc..dccb7a32139 100644
--- a/intern/guardedalloc/MEM_guardedalloc.h
+++ b/intern/guardedalloc/MEM_guardedalloc.h
@@ -39,8 +39,8 @@
* second intern/ module with MEM_ prefix, for use in c++.
*
* \subsection memdependencies Dependencies
- * - stdlib
- * - stdio
+ * - `stdlib`
+ * - `stdio`
*
* \subsection memdocs API Documentation
* See \ref MEM_guardedalloc.h
@@ -268,6 +268,12 @@ void MEM_use_guarded_allocator(void);
* Allocate new memory for and constructs an object of type #T.
* #MEM_delete should be used to delete the object. Just calling #MEM_freeN is not enough when #T
* is not a trivial type.
+ *
+ * Note that when no arguments are passed, C++ will do recursive member-wise value initialization.
+ * That is because C++ differentiates between creating an object with `T` (default initialization)
+ * and `T()` (value initialization), whereby this function does the latter. Value initialization
+ * rules are complex, but for C-style structs, memory will be zero-initialized. So this doesn't
+ * match a `malloc()`, but a `calloc()` call in this case. See https://stackoverflow.com/a/4982720.
*/
template<typename T, typename... Args>
inline T *MEM_new(const char *allocation_name, Args &&...args)
diff --git a/intern/iksolver/extern/IK_solver.h b/intern/iksolver/extern/IK_solver.h
index 8e7ea11bc18..302ed1545b4 100644
--- a/intern/iksolver/extern/IK_solver.h
+++ b/intern/iksolver/extern/IK_solver.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Laurence
*/
/** \file
diff --git a/intern/iksolver/intern/IK_Math.h b/intern/iksolver/intern/IK_Math.h
index be115364fb7..6a284df51f4 100644
--- a/intern/iksolver/intern/IK_Math.h
+++ b/intern/iksolver/intern/IK_Math.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Laurence
*/
/** \file
diff --git a/intern/iksolver/intern/IK_QSegment.h b/intern/iksolver/intern/IK_QSegment.h
index 17b56b1cfb3..4abb94ec4f7 100644
--- a/intern/iksolver/intern/IK_QSegment.h
+++ b/intern/iksolver/intern/IK_QSegment.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Laurence
*/
/** \file
diff --git a/intern/iksolver/intern/IK_QTask.h b/intern/iksolver/intern/IK_QTask.h
index faca6e2a036..c0f1672ef80 100644
--- a/intern/iksolver/intern/IK_QTask.h
+++ b/intern/iksolver/intern/IK_QTask.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Laurence
*/
/** \file
diff --git a/intern/locale/CMakeLists.txt b/intern/locale/CMakeLists.txt
index 732fa1e4d11..3e467302d8e 100644
--- a/intern/locale/CMakeLists.txt
+++ b/intern/locale/CMakeLists.txt
@@ -56,7 +56,6 @@ if(WITH_INTERNATIONAL)
list(APPEND LIB
${BOOST_LIBRARIES}
)
- add_definitions(-DWITH_INTERNATIONAL)
add_definitions(${BOOST_DEFINITIONS})
endif()
diff --git a/intern/quadriflow/quadriflow_capi.cpp b/intern/quadriflow/quadriflow_capi.cpp
index 086d5f7d296..e5e7bd3d947 100644
--- a/intern/quadriflow/quadriflow_capi.cpp
+++ b/intern/quadriflow/quadriflow_capi.cpp
@@ -1,20 +1,20 @@
-// Copyright 2019 Blender Foundation. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software Foundation,
-// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//
-// Author: Sebastian Parborg, Pablo Dobarro
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2019 Blender Foundation. All rights reserved.
+ */
#include <unordered_map>
diff --git a/intern/quadriflow/quadriflow_capi.hpp b/intern/quadriflow/quadriflow_capi.hpp
index 59af2826e15..927cc7c22a7 100644
--- a/intern/quadriflow/quadriflow_capi.hpp
+++ b/intern/quadriflow/quadriflow_capi.hpp
@@ -1,20 +1,20 @@
-// Copyright 2019 Blender Foundation. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software Foundation,
-// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//
-// Author: Sebastian Parborg, Pablo Dobarro
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2019 Blender Foundation. All rights reserved.
+ */
#ifndef QUADRIFLOW_CAPI_HPP
#define QUADRIFLOW_CAPI_HPP
diff --git a/intern/sky/source/sky_model.cpp b/intern/sky/source/sky_model.cpp
index b5bf415da26..946c230781f 100644
--- a/intern/sky/source/sky_model.cpp
+++ b/intern/sky/source/sky_model.cpp
@@ -1,32 +1,30 @@
/*
-This source is published under the following 3-clause BSD license.
-
-Copyright (c) 2012 - 2013, Lukas Hosek and Alexander Wilkie
-All rights reserved.
-
-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.
- * None of the names of the contributors may be used to endorse or promote
- products derived from this software without specific prior written
- permission.
-
-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 HOLDERS 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.
-*/
+ * 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.
+ * * None of the names of the contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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 HOLDERS 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.
+ *
+ * Copyright (c) 2012-2013, Lukas Hosek and Alexander Wilkie
+ * All rights reserved.
+ */
/* ============================================================================
diff --git a/intern/sky/source/sky_model_data.h b/intern/sky/source/sky_model_data.h
index 4f7bcb7cc48..4626a819d90 100644
--- a/intern/sky/source/sky_model_data.h
+++ b/intern/sky/source/sky_model_data.h
@@ -1,32 +1,30 @@
/*
-This source is published under the following 3-clause BSD license.
-
-Copyright (c) 2012 - 2013, Lukas Hosek and Alexander Wilkie
-All rights reserved.
-
-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.
- * None of the names of the contributors may be used to endorse or promote
- products derived from this software without specific prior written
- permission.
-
-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 HOLDERS 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.
-*/
+ * 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.
+ * * None of the names of the contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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 HOLDERS 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.
+ *
+ * Copyright (c) 2012-2013, Lukas Hosek and Alexander Wilkie
+ * All rights reserved.
+ */
/* ============================================================================
diff --git a/intern/sky/source/sky_nishita.cpp b/intern/sky/source/sky_nishita.cpp
index 615755390c7..236c965fb27 100644
--- a/intern/sky/source/sky_nishita.cpp
+++ b/intern/sky/source/sky_nishita.cpp
@@ -1,6 +1,4 @@
/*
- * Copyright 2011-2020 Blender Foundation
- *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -12,6 +10,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
+ *
+ * Copyright 2011-2020 Blender Foundation
*/
/** \file
diff --git a/make.bat b/make.bat
index d55b2cfd1b3..ff8059b0754 100644
--- a/make.bat
+++ b/make.bat
@@ -13,6 +13,9 @@ if errorlevel 1 goto EOF
call "%BLENDER_DIR%\build_files\windows\parse_arguments.cmd" %*
if errorlevel 1 goto EOF
+call "%BLENDER_DIR%\build_files\windows\find_dependencies.cmd"
+if errorlevel 1 goto EOF
+
REM if it is one of the convenience targets and BLENDER_BIN is set
REM skip compiler detection
if "%ICONS%%ICONS_GEOM%%DOC_PY%" == "1" (
@@ -21,9 +24,6 @@ if "%ICONS%%ICONS_GEOM%%DOC_PY%" == "1" (
)
)
-call "%BLENDER_DIR%\build_files\windows\find_dependencies.cmd"
-if errorlevel 1 goto EOF
-
if "%BUILD_SHOW_HASHES%" == "1" (
call "%BLENDER_DIR%\build_files\windows\show_hashes.cmd"
goto EOF
@@ -88,6 +88,11 @@ if "%DOC_PY%" == "1" (
goto EOF
)
+if "%CMAKE%" == "" (
+ echo Cmake not found in path, required for building, exiting...
+ exit /b 1
+)
+
echo Building blender with VS%BUILD_VS_YEAR% for %BUILD_ARCH% in %BUILD_DIR%
call "%BLENDER_DIR%\build_files\windows\check_libraries.cmd"
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 620b85f16d03a6aadd7cae56969c9c29b06b992
+Subproject 2d12637a69df7643484a8a3655b7eeb6faa170a
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 6afec05c3286cdea58ab269fb8dd1f5de011de4
+Subproject e1d44bf37501eb19a057777bd0b0ba448477353
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 7936dde9ece881d531b1a2ee6c45ddb56d30038
+Subproject 61e45814503f51963c91c51aaf764612e7c5dc7
diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py
index bdc345ee50a..d41db4e45b3 100644
--- a/release/scripts/modules/bl_i18n_utils/settings.py
+++ b/release/scripts/modules/bl_i18n_utils/settings.py
@@ -356,6 +356,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = {
"y",
"y = (Ax + B)",
# Sub-strings.
+ "and AMD Radeon Pro 21.Q4 driver or newer",
"available with",
"brown fox",
"can't save image while rendering",
@@ -378,6 +379,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = {
"image path can't be written to",
"in memory to enable editing!",
"insufficient content",
+ "into",
"jumps over",
"left",
"local",
@@ -387,6 +389,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = {
"performance impact!",
"right",
"the lazy dog",
+ "to the top level of the tree",
"unable to load movie clip",
"unable to load text",
"unable to open the file",
diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
index 62186655326..6a74c27b9c4 100644
--- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
+++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
@@ -76,10 +76,12 @@ class SpellChecker:
"tangency",
"vertices",
"wasn", # wasn't
+ "zig", "zag",
# Brands etc.
"htc",
"huawei",
+ "radeon",
"vive",
"xbox",
@@ -136,6 +138,7 @@ class SpellChecker:
"filename", "filenames",
"filepath", "filepaths",
"forcefield", "forcefields",
+ "framerange",
"fulldome", "fulldomes",
"fullscreen",
"gamepad",
@@ -498,6 +501,7 @@ class SpellChecker:
"framerate",
"gimbal",
"grayscale",
+ "icosahedron",
"icosphere",
"inpaint",
"kerning",
@@ -556,6 +560,7 @@ class SpellChecker:
"bspline",
"bweight",
"colorband",
+ "crazyspace",
"datablock", "datablocks",
"despeckle",
"depsgraph",
@@ -730,6 +735,7 @@ class SpellChecker:
"precisa",
"px",
"qmc",
+ "rdna",
"rdp",
"rgb", "rgba",
"rhs",
diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py
index a3b39853b3a..65d8ce67578 100644
--- a/release/scripts/modules/bpy_extras/io_utils.py
+++ b/release/scripts/modules/bpy_extras/io_utils.py
@@ -546,7 +546,7 @@ def unique_name(key, name, name_dict, name_max=-1, clean_func=None, sep="."):
:arg key: unique item this name belongs to, name_dict[key] will be reused
when available.
- This can be the object, mesh, material, etc instance its self.
+ This can be the object, mesh, material, etc instance itself.
:type key: any hashable object associated with the *name*.
:arg name: The name used to create a unique value in *name_dict*.
:type name: string
diff --git a/release/scripts/modules/console/__init__.py b/release/scripts/modules/console/__init__.py
index 97cb14822c4..731f5715818 100644
--- a/release/scripts/modules/console/__init__.py
+++ b/release/scripts/modules/console/__init__.py
@@ -1,5 +1,3 @@
-# Copyright (c) 2009 www.stani.be (GPL license)
-
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
@@ -18,6 +16,8 @@
#
# ##### END GPL LICENSE BLOCK #####
+# Copyright (c) 2009 www.stani.be
+
# <pep8 compliant>
"""Package for console specific modules."""
diff --git a/release/scripts/modules/console/complete_calltip.py b/release/scripts/modules/console/complete_calltip.py
index 60daa1d2045..c8aeaf2c98f 100644
--- a/release/scripts/modules/console/complete_calltip.py
+++ b/release/scripts/modules/console/complete_calltip.py
@@ -1,5 +1,3 @@
-# Copyright (c) 2009 www.stani.be (GPL license)
-
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
@@ -18,6 +16,8 @@
#
# ##### END GPL LICENSE BLOCK #####
+# Copyright (c) 2009 www.stani.be
+
# <pep8-80 compliant>
import inspect
diff --git a/release/scripts/modules/console/complete_import.py b/release/scripts/modules/console/complete_import.py
index 4bdd4eb188a..524989b3455 100644
--- a/release/scripts/modules/console/complete_import.py
+++ b/release/scripts/modules/console/complete_import.py
@@ -1,5 +1,3 @@
-# Copyright (c) 2009 Fernando Perez, www.stani.be (GPL license)
-
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
@@ -18,6 +16,8 @@
#
# ##### END GPL LICENSE BLOCK #####
+# Copyright (c) 2009 Fernando Perez, www.stani.be
+
# Original copyright (see docstring):
# ****************************************************************************
# Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
diff --git a/release/scripts/modules/console/complete_namespace.py b/release/scripts/modules/console/complete_namespace.py
index fa6323dcc66..68c06e641dc 100644
--- a/release/scripts/modules/console/complete_namespace.py
+++ b/release/scripts/modules/console/complete_namespace.py
@@ -1,5 +1,3 @@
-# Copyright (c) 2009 www.stani.be (GPL license)
-
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
@@ -18,6 +16,8 @@
#
# ##### END GPL LICENSE BLOCK #####
+# Copyright (c) 2009 www.stani.be
+
# <pep8-80 compliant>
"""Autocomplete with the standard library"""
diff --git a/release/scripts/modules/console/intellisense.py b/release/scripts/modules/console/intellisense.py
index 7e293ee0082..68ddb5ed12b 100644
--- a/release/scripts/modules/console/intellisense.py
+++ b/release/scripts/modules/console/intellisense.py
@@ -1,5 +1,3 @@
-# Copyright (c) 2009 www.stani.be (GPL license)
-
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
@@ -18,6 +16,8 @@
#
# ##### END GPL LICENSE BLOCK #####
+# Copyright (c) 2009 www.stani.be
+
# <pep8-80 compliant>
"""This module provides intellisense features such as:
diff --git a/release/scripts/modules/console_python.py b/release/scripts/modules/console_python.py
index 9e1b921774d..637fc9ed8b6 100644
--- a/release/scripts/modules/console_python.py
+++ b/release/scripts/modules/console_python.py
@@ -272,7 +272,7 @@ def autocomplete(context):
sc.select_end += ofs
except:
# unlikely, but this can happen with unicode errors for example.
- # or if the api attribute access its self causes an error.
+ # or if the api attribute access itself causes an error.
import traceback
scrollback_error = traceback.format_exc()
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index 2983a326358..f0da4bac974 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -677,7 +677,6 @@ url_manual_mapping = (
("bpy.ops.gpencil.stroke_merge_by_distance*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-merge-by-distance"),
("bpy.ops.node.collapse_hide_unused_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-collapse-hide-unused-toggle"),
("bpy.ops.object.anim_transforms_to_deltas*", "scene_layout/object/editing/apply.html#bpy-ops-object-anim-transforms-to-deltas"),
- ("bpy.ops.object.convert_proxy_to_override*", "files/linked_libraries/library_overrides.html#bpy-ops-object-convert-proxy-to-override"),
("bpy.ops.object.modifier_copy_to_selected*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy-to-selected"),
("bpy.ops.preferences.app_template_install*", "advanced/app_templates.html#bpy-ops-preferences-app-template-install"),
("bpy.types.actionposemarkers.active_index*", "animation/armatures/properties/pose_library.html#bpy-types-actionposemarkers-active-index"),
@@ -1860,6 +1859,7 @@ url_manual_mapping = (
("bpy.types.shadernodeemission*", "render/shader_nodes/shader/emission.html#bpy-types-shadernodeemission"),
("bpy.types.shadernodegeometry*", "render/shader_nodes/input/geometry.html#bpy-types-shadernodegeometry"),
("bpy.types.shadernodehairinfo*", "render/shader_nodes/input/hair_info.html#bpy-types-shadernodehairinfo"),
+ ("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"),
("bpy.types.shadernodemaprange*", "render/shader_nodes/converter/map_range.html#bpy-types-shadernodemaprange"),
("bpy.types.shadernodergbcurve*", "modeling/geometry_nodes/color/rgb_curves.html#bpy-types-shadernodergbcurve"),
("bpy.types.shadernodeseparate*", "render/shader_nodes/converter/combine_separate.html#bpy-types-shadernodeseparate"),
@@ -2139,7 +2139,6 @@ url_manual_mapping = (
("bpy.ops.object.origin_set*", "scene_layout/object/origin.html#bpy-ops-object-origin-set"),
("bpy.ops.object.parent_set*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-set"),
("bpy.ops.object.pointcloud*", "modeling/point_cloud.html#bpy-ops-object-pointcloud"),
- ("bpy.ops.object.proxy_make*", "files/linked_libraries/library_proxies.html#bpy-ops-object-proxy-make"),
("bpy.ops.object.select_all*", "scene_layout/object/selecting.html#bpy-ops-object-select-all"),
("bpy.ops.object.shade_flat*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-flat"),
("bpy.ops.pose.group_assign*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-assign"),
diff --git a/release/scripts/modules/rna_xml.py b/release/scripts/modules/rna_xml.py
index a36b262c883..aa8841c5efe 100644
--- a/release/scripts/modules/rna_xml.py
+++ b/release/scripts/modules/rna_xml.py
@@ -122,7 +122,7 @@ def rna2xml(
if issubclass(value_type, skip_classes):
return
- # XXX, fixme, pointcache has eternal nested pointer to its self.
+ # XXX, fixme, pointcache has eternal nested pointer to itself.
if value == parent:
return
@@ -298,7 +298,7 @@ def xml2rna(
del value_xml_split
tp_name = 'ARRAY'
-# print(" %s.%s (%s) --- %s" % (type(value).__name__, attr, tp_name, subvalue_type))
+ # print(" %s.%s (%s) --- %s" % (type(value).__name__, attr, tp_name, subvalue_type))
try:
setattr(value, attr, value_xml_coerce)
except ValueError:
@@ -340,7 +340,6 @@ def xml2rna(
else:
# print(elems)
-
if len(elems) == 1:
# sub node named by its type
child_xml_real, = elems
@@ -376,7 +375,6 @@ def _get_context_val(context, path):
def xml_file_run(context, filepath, rna_map):
-
import xml.dom.minidom
xml_nodes = xml.dom.minidom.parse(filepath)
@@ -391,27 +389,25 @@ def xml_file_run(context, filepath, rna_map):
value = _get_context_val(context, rna_path)
if value is not Ellipsis and value is not None:
- print(" loading XML: %r -> %r" % (filepath, rna_path))
+ # print(" loading XML: %r -> %r" % (filepath, rna_path))
xml2rna(xml_node, root_rna=value)
def xml_file_write(context, filepath, rna_map, *, skip_typemap=None):
-
- file = open(filepath, "w", encoding="utf-8")
- fw = file.write
-
- fw("<bpy>\n")
-
- for rna_path, _xml_tag in rna_map:
- # xml_tag is ignored, we get this from the rna
- value = _get_context_val(context, rna_path)
- rna2xml(fw,
+ with open(filepath, "w", encoding="utf-8") as file:
+ fw = file.write
+ fw("<bpy>\n")
+
+ for rna_path, _xml_tag in rna_map:
+ # xml_tag is ignored, we get this from the rna
+ value = _get_context_val(context, rna_path)
+ rna2xml(
+ fw=fw,
root_rna=value,
method='ATTR',
root_ident=" ",
ident_val=" ",
skip_typemap=skip_typemap,
- )
+ )
- fw("</bpy>\n")
- file.close()
+ fw("</bpy>\n")
diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py
index 1ac7626f926..417a3c39310 100644
--- a/release/scripts/presets/keyconfig/Blender.py
+++ b/release/scripts/presets/keyconfig/Blender.py
@@ -318,7 +318,8 @@ def load():
use_v3d_tab_menu=kc_prefs.use_v3d_tab_menu,
use_v3d_shade_ex_pie=kc_prefs.use_v3d_shade_ex_pie,
use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'),
- use_fallback_tool=(True if is_select_left else (kc_prefs.rmb_action == 'FALLBACK_TOOL')),
+ use_fallback_tool=True,
+ use_fallback_tool_rmb=(False if is_select_left else kc_prefs.rmb_action == 'FALLBACK_TOOL'),
use_alt_tool_or_cursor=(
(not use_mouse_emulate_3_button) and
(kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor)
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index a006cd787ea..5367d9b33f9 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -16,6 +16,16 @@
#
# ##### END GPL LICENSE BLOCK #####
+# ------------------------------------------------------------------------------
+# Developer Notes
+#
+# - This script should run without Blender (no references to the `bpy` module for example).
+# - All configuration must be passed into the `generate_keymaps` function (via `Params`).
+# - Supporting some combinations of options is becoming increasingly complex,
+# especially `Params.select_mouse` & `Params.use_fallback_tool_rmb`.
+# To ensure changes don't unintentionally break other configurations, see:
+# `source/tools/utils/blender_keyconfig_export_permutations.py --help`
+#
# ------------------------------------------------------------------------------
# Configurable Parameters
@@ -48,6 +58,8 @@ class Params:
"use_gizmo_drag",
# Use the fallback tool instead of tweak for RMB select.
"use_fallback_tool",
+ # Only set for RMB select.
+ "use_fallback_tool_rmb",
# Use pie menu for tab by default (swap 'Tab/Ctrl-Tab').
"use_v3d_tab_menu",
# Use extended pie menu for shading.
@@ -65,15 +77,16 @@ class Params:
"v3d_tilde_action",
# Alt-MMB axis switching 'RELATIVE' or 'ABSOLUTE' axis switching.
"v3d_alt_mmb_drag_action",
-
+ # File selector actions on single click.
"use_file_single_click",
+
# Convenience variables:
# (derived from other settings).
#
- # This case needs to be checked often,
- # Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHTMOUSE' else False)`.
- "use_fallback_tool_rmb",
- # Shorthand for: `('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value)`.
+ # The fallback tool is activated on the same button as selection.
+ # Shorthand for: `(True if (select_mouse == 'LEFT') else self.use_fallback_tool_rmb)`
+ "use_fallback_tool_select_mouse",
+ # Shorthand for: `('CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value)`.
"select_mouse_value_fallback",
# Shorthand for: `{"type": params.select_tweak, "value": 'ANY'}`.
"select_tweak_event",
@@ -103,6 +116,7 @@ class Params:
use_select_all_toggle=False,
use_gizmo_drag=True,
use_fallback_tool=False,
+ use_fallback_tool_rmb=False,
use_v3d_tab_menu=False,
use_v3d_shade_ex_pie=False,
use_v3d_mmb_pan=False,
@@ -146,7 +160,6 @@ class Params:
self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'}
self.cursor_tweak_event = None
- self.use_fallback_tool = use_fallback_tool
self.tool_modifier = {}
else:
# Left mouse select uses Click event for selection. This is a little
@@ -169,7 +182,6 @@ class Params:
self.cursor_set_event = {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True}
self.cursor_tweak_event = {"type": 'EVT_TWEAK_R', "value": 'ANY', "shift": True}
- self.use_fallback_tool = True
# Use the "tool" functionality for LMB select.
if use_alt_tool_or_cursor:
@@ -197,8 +209,11 @@ class Params:
self.use_file_single_click = use_file_single_click
+ self.use_fallback_tool = use_fallback_tool
+ self.use_fallback_tool_rmb = use_fallback_tool_rmb
+
# Convenience variables:
- self.use_fallback_tool_rmb = self.use_fallback_tool if select_mouse == 'RIGHT' else False
+ self.use_fallback_tool_select_mouse = True if (select_mouse == 'LEFT') else self.use_fallback_tool_rmb
self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value
self.select_tweak_event = {"type": self.select_tweak, "value": 'ANY'}
self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS'
@@ -1149,11 +1164,7 @@ def km_uv_editor(params):
items.extend([
# Selection modes.
*_template_items_uv_select_mode(params),
- *_template_uv_select(
- type=params.select_mouse,
- value=('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value),
- legacy=params.legacy,
- ),
+ *_template_uv_select(type=params.select_mouse, value=params.select_mouse_value_fallback, legacy=params.legacy),
("uv.mark_seam", {"type": 'E', "value": 'PRESS', "ctrl": True}, None),
("uv.select_loop",
{"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None),
@@ -6283,7 +6294,8 @@ def km_image_editor_tool_uv_select_box(params, *, fallback):
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"uv.select_box",
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
- **(params.select_tweak_event if fallback else params.tool_tweak_event))),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event))),
]},
)
@@ -6295,7 +6307,8 @@ def km_image_editor_tool_uv_select_circle(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"uv.select_circle",
- **(params.select_tweak_event if fallback else {"type": params.tool_mouse, "value": 'PRESS'}),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ {"type": params.tool_mouse, "value": 'PRESS'}),
properties=[("wait_for_input", False)])),
# No selection fallback since this operates on press.
]},
@@ -6310,7 +6323,8 @@ def km_image_editor_tool_uv_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"uv.select_lasso",
- **(params.select_tweak_event if fallback else params.tool_tweak_event))),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event))),
]},
)
@@ -6402,7 +6416,8 @@ def km_node_editor_tool_select_box(params, *, fallback):
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"node.select_box",
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
- **(params.select_tweak_event if fallback else params.tool_tweak_event),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event),
properties=[("tweak", True)],
)),
]},
@@ -6415,7 +6430,9 @@ def km_node_editor_tool_select_lasso(params, *, fallback):
{"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'},
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
- "node.select_lasso", **(params.select_tweak_event if fallback else params.tool_tweak_event),
+ "node.select_lasso",
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event),
properties=[("tweak", True)]))
]},
)
@@ -6430,7 +6447,7 @@ def km_node_editor_tool_select_circle(params, *, fallback):
"node.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
- type=params.select_tweak if fallback else params.tool_mouse,
+ type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
value='ANY' if fallback else 'PRESS',
properties=[("wait_for_input", False)])),
]},
@@ -6484,7 +6501,8 @@ def km_3d_view_tool_select_box(params, *, fallback):
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"view3d.select_box",
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
- **(params.select_tweak_event if fallback else params.tool_tweak_event))),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event))),
]},
)
@@ -6498,7 +6516,7 @@ def km_3d_view_tool_select_circle(params, *, fallback):
"view3d.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
- type=params.select_tweak if fallback else params.tool_mouse,
+ type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
value='ANY' if fallback else 'PRESS',
properties=[("wait_for_input", False)])),
]},
@@ -6512,7 +6530,8 @@ def km_3d_view_tool_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"view3d.select_lasso",
- **(params.select_tweak_event if fallback else params.tool_tweak_event))),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event))),
]}
)
@@ -7394,7 +7413,8 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback):
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"gpencil.select_box",
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
- **(params.select_tweak_event if fallback else params.tool_tweak_event))),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event))),
]},
)
@@ -7408,7 +7428,7 @@ def km_3d_view_tool_edit_gpencil_select_circle(params, *, fallback):
"gpencil.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
- type=params.select_tweak if fallback else params.tool_mouse,
+ type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
value='ANY' if fallback else 'PRESS',
properties=[("wait_for_input", False)])),
]},
@@ -7422,7 +7442,8 @@ def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"gpencil.select_lasso",
- **(params.select_tweak_event if fallback else params.tool_tweak_event))),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event))),
]}
)
@@ -7573,7 +7594,8 @@ def km_sequencer_editor_tool_generic_select_box(params, *, fallback):
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"sequencer.select_box",
- **(params.select_tweak_event if fallback else params.tool_tweak_event),
+ **(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
+ params.tool_tweak_event),
properties=[("tweak", params.select_mouse == 'LEFTMOUSE')])),
# RMB select can already set the frame, match the tweak tool.
diff --git a/release/scripts/startup/bl_operators/object_align.py b/release/scripts/startup/bl_operators/object_align.py
index 4a7700b03bb..9c7fdf495ee 100644
--- a/release/scripts/startup/bl_operators/object_align.py
+++ b/release/scripts/startup/bl_operators/object_align.py
@@ -1,5 +1,4 @@
# ##### BEGIN GPL LICENSE BLOCK #####
-
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/release/scripts/startup/bl_operators/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py
index 3f43571fee5..8b2a8b541be 100644
--- a/release/scripts/startup/bl_operators/vertexpaint_dirt.py
+++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py
@@ -1,7 +1,5 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
-# Script copyright (C) Campbell J Barton
-#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
@@ -17,7 +15,8 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENSE BLOCK *****
-# --------------------------------------------------------------------------
+#
+# Copyright Campbell Barton
# <pep8 compliant>
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 3a46bb7fb53..bb85ad8ca50 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -581,7 +581,7 @@ class WM_OT_context_cycle_enum(Operator):
# Have the info we need, advance to the next item.
#
- # When wrap's disabled we may set the value to its self,
+ # When wrap's disabled we may set the value to itself,
# this is done to ensure update callbacks run.
if self.reverse:
if orig_index == 0:
@@ -2961,93 +2961,75 @@ class WM_MT_splash_quick_setup(Menu):
bl_label = "Quick Setup"
def draw(self, context):
- wm = context.window_manager
- # prefs = context.preferences
-
layout = self.layout
-
layout.operator_context = 'EXEC_DEFAULT'
layout.label(text="Quick Setup")
- split = layout.split(factor=0.25)
+ split = layout.split(factor=0.14) # Left margin.
split.label()
- split = split.split(factor=2.0 / 3.0)
+ split = split.split(factor=0.73) # Content width.
col = split.column()
+ col.use_property_split = True
+ col.use_property_decorate = False
+
+ # Languages.
if bpy.app.build_options.international:
- sub = col.split(factor=0.35)
- row = sub.row()
- row.alignment = 'RIGHT'
- row.label(text="Language")
prefs = context.preferences
- sub.prop(prefs.view, "language", text="")
+ col.prop(prefs.view, "language")
+ col.separator()
- col.separator()
+ # Shortcuts.
+ wm = context.window_manager
+ kc = wm.keyconfigs.active
+ kc_prefs = kc.preferences
- sub = col.split(factor=0.35)
- row = sub.row()
- row.alignment = 'RIGHT'
- row.label(text="Shortcuts")
- text = bpy.path.display_name(wm.keyconfigs.active.name)
+ sub = col.column(heading="Shortcuts")
+ text = bpy.path.display_name(kc.name)
if not text:
text = "Blender"
sub.menu("USERPREF_MT_keyconfigs", text=text)
- kc = wm.keyconfigs.active
- kc_prefs = kc.preferences
has_select_mouse = hasattr(kc_prefs, "select_mouse")
if has_select_mouse:
- sub = col.split(factor=0.35)
- row = sub.row()
- row.alignment = 'RIGHT'
- row.label(text="Select With")
- sub.row().prop(kc_prefs, "select_mouse", expand=True)
- has_select_mouse = True
+ col.row().prop(kc_prefs, "select_mouse", text="Select With", expand=True)
has_spacebar_action = hasattr(kc_prefs, "spacebar_action")
if has_spacebar_action:
- sub = col.split(factor=0.35)
- row = sub.row()
- row.alignment = 'RIGHT'
- row.label(text="Spacebar")
- sub.row().prop(kc_prefs, "spacebar_action", expand=True)
- has_select_mouse = True
+ col.row().prop(kc_prefs, "spacebar_action", text="Spacebar")
col.separator()
- sub = col.split(factor=0.35)
- row = sub.row()
- row.alignment = 'RIGHT'
- row.label(text="Theme")
+ # Themes.
+ sub = col.column(heading="Theme")
label = bpy.types.USERPREF_MT_interface_theme_presets.bl_label
if label == "Presets":
label = "Blender Dark"
sub.menu("USERPREF_MT_interface_theme_presets", text=label)
- # Keep height constant
+ # Keep height constant.
if not has_select_mouse:
col.label()
if not has_spacebar_action:
col.label()
- layout.label()
+ layout.separator(factor=2.0)
- row = layout.row()
+ # Save settings buttons.
+ sub = layout.row()
- sub = row.row()
old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version()
if bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version:
- sub.operator("preferences.copy_prev", text=iface_("Load %d.%d Settings", "Operator") % old_version)
+ sub.operator("preferences.copy_prev", text="Load %d.%d Settings" % old_version)
sub.operator("wm.save_userpref", text="Save New Settings")
else:
sub.label()
sub.label()
sub.operator("wm.save_userpref", text="Next")
- layout.separator()
- layout.separator()
+ layout.separator(factor=2.4)
class WM_MT_splash(Menu):
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index 1fb40ad8bc8..7c254596683 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -33,9 +33,9 @@ _modules = [
"properties_data_bone",
"properties_data_camera",
"properties_data_curve",
+ "properties_data_curves",
"properties_data_empty",
"properties_data_gpencil",
- "properties_data_hair",
"properties_data_light",
"properties_data_lattice",
"properties_data_mesh",
diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py
index 22f3d1a9c50..7fb8791f856 100644
--- a/release/scripts/startup/bl_ui/properties_data_armature.py
+++ b/release/scripts/startup/bl_ui/properties_data_armature.py
@@ -149,7 +149,6 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel):
col.operator("pose.group_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
split = layout.split()
- split.active = (ob.proxy is None)
col = split.column()
col.prop(group, "color_set")
diff --git a/release/scripts/startup/bl_ui/properties_data_hair.py b/release/scripts/startup/bl_ui/properties_data_curves.py
index 7f95fad9a9e..0b4bf0283ed 100644
--- a/release/scripts/startup/bl_ui/properties_data_hair.py
+++ b/release/scripts/startup/bl_ui/properties_data_curves.py
@@ -30,10 +30,10 @@ class DataButtonsPanel:
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
- return hasattr(context, 'hair') and context.hair and (engine in cls.COMPAT_ENGINES)
+ return hasattr(context, 'curves') and context.curves and (engine in cls.COMPAT_ENGINES)
-class DATA_PT_context_hair(DataButtonsPanel, Panel):
+class DATA_PT_context_curves(DataButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@@ -42,21 +42,21 @@ class DATA_PT_context_hair(DataButtonsPanel, Panel):
layout = self.layout
ob = context.object
- hair = context.hair
+ curves = context.curves
space = context.space_data
if ob:
layout.template_ID(ob, "data")
- elif hair:
+ elif curves:
layout.template_ID(space, "pin_id")
-class HAIR_MT_add_attribute(Menu):
+class CURVES_MT_add_attribute(Menu):
bl_label = "Add Attribute"
@staticmethod
- def add_standard_attribute(layout, hair, name, data_type, domain):
- exists = hair.attributes.get(name) is not None
+ def add_standard_attribute(layout, curves, name, data_type, domain):
+ exists = curves.attributes.get(name) is not None
col = layout.column()
col.enabled = not exists
@@ -69,10 +69,10 @@ class HAIR_MT_add_attribute(Menu):
def draw(self, context):
layout = self.layout
- hair = context.hair
+ curves = context.curves
- self.add_standard_attribute(layout, hair, 'Radius', 'FLOAT', 'POINT')
- self.add_standard_attribute(layout, hair, 'Color', 'FLOAT_COLOR', 'POINT')
+ self.add_standard_attribute(layout, curves, 'radius', 'FLOAT', 'POINT')
+ self.add_standard_attribute(layout, curves, 'color', 'FLOAT_COLOR', 'POINT')
layout.separator()
@@ -80,7 +80,7 @@ class HAIR_MT_add_attribute(Menu):
layout.operator("geometry.attribute_add", text="Custom...")
-class HAIR_UL_attributes(UIList):
+class CURVES_UL_attributes(UIList):
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
domain = attribute.bl_rna.properties['domain'].enum_items[attribute.domain]
@@ -96,44 +96,44 @@ class HAIR_UL_attributes(UIList):
sub.label(text=data_type.name)
-class DATA_PT_hair_attributes(DataButtonsPanel, Panel):
+class DATA_PT_CURVES_attributes(DataButtonsPanel, Panel):
bl_label = "Attributes"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
def draw(self, context):
- hair = context.hair
+ curves = context.curves
layout = self.layout
row = layout.row()
col = row.column()
col.template_list(
- "HAIR_UL_attributes",
+ "CURVES_UL_attributes",
"attributes",
- hair,
+ curves,
"attributes",
- hair.attributes,
+ curves.attributes,
"active_index",
rows=3,
)
col = row.column(align=True)
- col.menu("HAIR_MT_add_attribute", icon='ADD', text="")
+ col.menu("CURVES_MT_add_attribute", icon='ADD', text="")
col.operator("geometry.attribute_remove", icon='REMOVE', text="")
-class DATA_PT_custom_props_hair(DataButtonsPanel, PropertyPanel, Panel):
+class DATA_PT_custom_props_curves(DataButtonsPanel, PropertyPanel, Panel):
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
_context_path = "object.data"
- _property_type = bpy.types.Hair if hasattr(bpy.types, "Hair") else None
+ _property_type = bpy.types.Curves if hasattr(bpy.types, "Curves") else None
classes = (
- DATA_PT_context_hair,
- DATA_PT_hair_attributes,
- DATA_PT_custom_props_hair,
- HAIR_MT_add_attribute,
- HAIR_UL_attributes,
+ DATA_PT_context_curves,
+ DATA_PT_CURVES_attributes,
+ DATA_PT_custom_props_curves,
+ CURVES_MT_add_attribute,
+ CURVES_UL_attributes,
)
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 49f196640d8..11e2cd84903 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -82,6 +82,14 @@ class MESH_MT_shape_key_context_menu(Menu):
layout.operator("object.shape_key_move", icon='TRIA_UP_BAR', text="Move to Top").type = 'TOP'
layout.operator("object.shape_key_move", icon='TRIA_DOWN_BAR', text="Move to Bottom").type = 'BOTTOM'
+class MESH_MT_attribute_context_menu(Menu):
+ bl_label = "Attribute Specials"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("geometry.attribute_convert")
+
class MESH_UL_vgroups(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data_, _active_propname, _index):
@@ -615,6 +623,10 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
col.operator("geometry.attribute_add", icon='ADD', text="")
col.operator("geometry.attribute_remove", icon='REMOVE', text="")
+ col.separator()
+
+ col.menu("MESH_MT_attribute_context_menu", icon='DOWNARROW_HLT', text="")
+
self.draw_attribute_warnings(context, layout)
def draw_attribute_warnings(self, context, layout):
@@ -652,6 +664,7 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
classes = (
MESH_MT_vertex_group_context_menu,
MESH_MT_shape_key_context_menu,
+ MESH_MT_attribute_context_menu,
MESH_UL_vgroups,
MESH_UL_fmaps,
MESH_UL_shape_keys,
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index 81a641a20cf..fbd4ed3225a 100644
--- a/release/scripts/startup/bl_ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -184,24 +184,18 @@ class OBJECT_PT_collections(ObjectButtonsPanel, Panel):
row.operator("object.collection_add", text="Add to Collection")
row.operator("object.collection_add", text="", icon='ADD')
- obj_name = obj.name
- for collection in bpy.data.collections:
- # XXX this is slow and stupid!, we need 2 checks, one that's fast
- # and another that we can be sure its not a name collision
- # from linked library data
- collection_objects = collection.objects
- if obj_name in collection.objects and obj in collection_objects[:]:
- col = layout.column(align=True)
-
- col.context_pointer_set("collection", collection)
-
- row = col.box().row()
- row.prop(collection, "name", text="")
- row.operator("object.collection_remove", text="", icon='X', emboss=False)
- row.menu("COLLECTION_MT_context_menu", icon='DOWNARROW_HLT', text="")
-
- row = col.box().row()
- row.prop(collection, "instance_offset", text="")
+ for collection in obj.users_collection:
+ col = layout.column(align=True)
+
+ col.context_pointer_set("collection", collection)
+
+ row = col.box().row()
+ row.prop(collection, "name", text="")
+ row.operator("object.collection_remove", text="", icon='X', emboss=False)
+ row.menu("COLLECTION_MT_context_menu", icon='DOWNARROW_HLT', text="")
+
+ row = col.box().row()
+ row.prop(collection, "instance_offset", text="")
class OBJECT_PT_display(ObjectButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_output.py b/release/scripts/startup/bl_ui/properties_output.py
index d96a53f6ab8..ff98cbbaa75 100644
--- a/release/scripts/startup/bl_ui/properties_output.py
+++ b/release/scripts/startup/bl_ui/properties_output.py
@@ -1,5 +1,4 @@
# ##### BEGIN GPL LICENSE BLOCK #####
-
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 3abaa490d02..7f76431cc9a 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -1,5 +1,4 @@
# ##### BEGIN GPL LICENSE BLOCK #####
-
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index 18dfa4da6c6..99edbe647e2 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -127,8 +127,8 @@ class DopesheetFilterPopoverBase:
flow.prop(dopesheet, "show_lattices", text="Lattices")
if bpy.data.metaballs:
flow.prop(dopesheet, "show_metaballs", text="Metaballs")
- if hasattr(bpy.data, "hairs") and bpy.data.hairs:
- flow.prop(dopesheet, "show_hairs", text="Hairs")
+ if hasattr(bpy.data, "hair_curves") and bpy.data.hair_curves:
+ flow.prop(dopesheet, "show_hair_curves", text="Hair Curves")
if hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds:
flow.prop(dopesheet, "show_pointclouds", text="Point Clouds")
if bpy.data.volumes:
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index 731a220848e..50005a8f7f0 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -761,6 +761,15 @@ class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="")
col.separator()
col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="")
+ col.menu("ASSETBROWSER_MT_metadata_preview_menu", icon='DOWNARROW_HLT', text="")
+
+
+class ASSETBROWSER_MT_metadata_preview_menu(bpy.types.Menu):
+ bl_label = "Preview"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator("ed.lib_id_generate_preview_from_object", text="Render Active Object")
class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
@@ -840,6 +849,7 @@ classes = (
ASSETBROWSER_MT_view,
ASSETBROWSER_MT_select,
ASSETBROWSER_MT_edit,
+ ASSETBROWSER_MT_metadata_preview_menu,
ASSETBROWSER_PT_metadata,
ASSETBROWSER_PT_metadata_preview,
ASSETBROWSER_PT_metadata_tags,
diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py
index a9f2b9e9a36..db0020b7846 100644
--- a/release/scripts/startup/bl_ui/space_graph.py
+++ b/release/scripts/startup/bl_ui/space_graph.py
@@ -335,6 +335,7 @@ class GRAPH_MT_key_snap(Menu):
layout.operator("graph.snap", text="Selection to Nearest Second").type = 'NEAREST_SECOND'
layout.operator("graph.snap", text="Selection to Nearest Marker").type = 'NEAREST_MARKER'
layout.operator("graph.snap", text="Flatten Handles").type = 'HORIZONTAL'
+ layout.operator("graph.equalize_handles", text="Equalize Handles").side = 'BOTH'
layout.separator()
layout.operator("graph.frame_jump", text="Cursor to Selection")
layout.operator("graph.snap_cursor_value", text="Cursor Value to Selection")
diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py
index 2db2b11a5f9..78c312fd03d 100644
--- a/release/scripts/startup/bl_ui/space_nla.py
+++ b/release/scripts/startup/bl_ui/space_nla.py
@@ -291,6 +291,9 @@ class NLA_MT_context_menu(Menu):
layout.separator()
+ props = layout.operator("wm.call_panel", text="Rename...")
+ props.name = "TOPBAR_PT_name"
+ props.keep_open = False
layout.operator("nla.duplicate", text="Duplicate").linked = False
layout.operator("nla.duplicate", text="Linked Duplicate").linked = True
diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index d85538a37e0..6cc80c088e0 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -448,7 +448,7 @@ class OUTLINER_PT_filter(Panel):
if (
bpy.data.curves or
bpy.data.metaballs or
- (hasattr(bpy.data, "hairs") and bpy.data.hairs) or
+ (hasattr(bpy.data, "hair_curves") and bpy.data.hair_curves) or
(hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds) or
bpy.data.volumes or
bpy.data.lightprobes or
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 6035170f9df..9c77529229b 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -2028,6 +2028,9 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel):
layout.active = not strip.mute
col = layout.column(align=True)
+ col.prop(strip.transform, "filter", text="Filter")
+
+ col = layout.column(align=True)
col.prop(strip.transform, "offset_x", text="Position X")
col.prop(strip.transform, "offset_y", text="Y")
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 99abc60db6f..ce854155b88 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -481,7 +481,7 @@ class TOPBAR_MT_file_export(Menu):
bl_owner_use_filter = False
def draw(self, _context):
- self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj) - New")
+ self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj)")
if bpy.app.build_options.collada:
self.layout.operator("wm.collada_export",
text="Collada (Default) (.dae)")
@@ -832,6 +832,14 @@ class TOPBAR_PT_name(Panel):
row = row_with_icon(layout, 'NODE')
row.prop(item, "label", text="")
found = True
+ elif space_type == 'NLA_EDITOR':
+ layout.label(text="NLA Strip Name")
+ item = next(
+ (strip for strip in context.selected_nla_strips if strip.active), None)
+ if item:
+ row = row_with_icon(layout, 'NLA')
+ row.prop(item, "name", text="")
+ found = True
else:
if mode == 'POSE' or (mode == 'WEIGHT_PAINT' and context.pose_object):
layout.label(text="Bone Name")
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 0548486c786..26b4229690f 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -2295,7 +2295,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
def draw(self, context):
self._draw_items(
context, (
- ({"property": "use_new_hair_type"}, "T68981"),
+ ({"property": "use_new_curves_type"}, "T68981"),
({"property": "use_new_point_cloud_type"}, "T75717"),
({"property": "use_full_frame_compositor"}, "T88150"),
),
@@ -2316,7 +2316,6 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
context, (
({"property": "use_undo_legacy"}, "T60695"),
({"property": "override_auto_resync"}, "T83811"),
- ({"property": "proxy_to_override_auto_conversion"}, "T91671"),
({"property": "use_cycles_debug"}, None),
({"property": "use_geometry_nodes_legacy"}, "T91274"),
({"property": "show_asset_debug_info"}, None),
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 5eca606216e..ea7a1885369 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2148,8 +2148,8 @@ class VIEW3D_MT_add(Menu):
layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE')
layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META')
layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT')
- if context.preferences.experimental.use_new_hair_type:
- layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR')
+ if context.preferences.experimental.use_new_curves_type:
+ layout.operator("object.hair_curves_add", text="Hair Curves", icon='OUTLINER_OB_CURVES')
if context.preferences.experimental.use_new_point_cloud_type:
layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD')
layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME')
@@ -2228,8 +2228,6 @@ class VIEW3D_MT_object_relations(Menu):
layout.operator("object.make_override_library", text="Make Library Override...")
- layout.operator("object.convert_proxy_to_override")
-
layout.operator("object.make_dupli_face")
layout.separator()
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 7d8179265da..4b48f5f0680 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -142,6 +142,8 @@ def mesh_node_items(context):
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeDualMesh")
+ yield NodeItem("GeometryNodeExtrudeMesh")
+ yield NodeItem("GeometryNodeFlipFaces")
yield NodeItem("GeometryNodeMeshBoolean")
yield NodeItem("GeometryNodeMeshToCurve")
yield NodeItem("GeometryNodeMeshToPoints")
@@ -149,6 +151,7 @@ def mesh_node_items(context):
yield NodeItem("GeometryNodeSubdivideMesh")
yield NodeItem("GeometryNodeSubdivisionSurface")
yield NodeItem("GeometryNodeTriangulate")
+ yield NodeItem("GeometryNodeScaleElements")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputMeshEdgeAngle")
yield NodeItem("GeometryNodeInputMeshEdgeNeighbors")
@@ -178,6 +181,7 @@ def geometry_node_items(context):
yield NodeItem("GeometryNodeConvexHull")
yield NodeItem("GeometryNodeDeleteGeometry")
yield NodeItem("GeometryNodeGeometryToInstance")
+ yield NodeItem("GeometryNodeMergeByDistance")
yield NodeItem("GeometryNodeProximity")
yield NodeItem("GeometryNodeJoinGeometry")
yield NodeItem("GeometryNodeRaycast")
@@ -387,6 +391,7 @@ shader_node_categories = [
NodeItem("ShaderNodeAmbientOcclusion"),
NodeItem("ShaderNodeObjectInfo"),
NodeItem("ShaderNodeHairInfo"),
+ NodeItem("ShaderNodePointInfo"),
NodeItem("ShaderNodeVolumeInfo"),
NodeItem("ShaderNodeParticleInfo"),
NodeItem("ShaderNodeCameraData"),
@@ -542,6 +547,8 @@ compositor_node_categories = [
NodeItem("CompositorNodeCombYUVA"),
NodeItem("CompositorNodeSepYCCA"),
NodeItem("CompositorNodeCombYCCA"),
+ NodeItem("CompositorNodeSeparateXYZ"),
+ NodeItem("CompositorNodeCombineXYZ"),
NodeItem("CompositorNodeSwitchView"),
NodeItem("CompositorNodeConvertColorSpace"),
]),
@@ -704,6 +711,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeCurvePrimitiveCircle"),
NodeItem("GeometryNodeCurveStar"),
NodeItem("GeometryNodeCurveSpiral"),
+ NodeItem("GeometryNodeCurveArc"),
NodeItem("GeometryNodeCurveQuadraticBezier"),
NodeItem("GeometryNodeCurvePrimitiveQuadrilateral"),
NodeItem("GeometryNodeCurvePrimitiveBezierSegment"),
diff --git a/release/scripts/templates_py/operator_modal_timer.py b/release/scripts/templates_py/operator_modal_timer.py
index 11530f18829..19c003712ad 100644
--- a/release/scripts/templates_py/operator_modal_timer.py
+++ b/release/scripts/templates_py/operator_modal_timer.py
@@ -2,7 +2,7 @@ import bpy
class ModalTimerOperator(bpy.types.Operator):
- """Operator which runs its self from a timer"""
+ """Operator which runs itself from a timer"""
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 6c2cbb5df33..73e16c38cb7 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -27,7 +27,7 @@ if(WITH_CLANG_TIDY AND NOT MSVC)
message(WARNING "Currently Clang-Tidy might fail with GCC toolchain, switch to Clang toolchain if that happens")
if(COMMAND target_precompile_headers)
message(STATUS "Clang-Tidy and GCC precompiled headers are incompatible, disabling precompiled headers")
- set(CMAKE_DISABLE_PRECOMPILE_HEADERS On)
+ set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON)
endif()
endif()
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index c6112344208..1fcde431d2d 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -48,7 +48,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_modifier_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h
- ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_hair_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curves_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h
diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt
index e2d278255ba..d64e942069d 100644
--- a/source/blender/blendthumb/CMakeLists.txt
+++ b/source/blender/blendthumb/CMakeLists.txt
@@ -65,6 +65,7 @@ else()
)
add_executable(blender-thumbnailer ${SRC} ${SRC_CMD})
+ setup_platform_linker_flags(blender-thumbnailer)
target_link_libraries(blender-thumbnailer bf_blenlib)
target_link_libraries(blender-thumbnailer ${PTHREADS_LIBRARIES})
endif()
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 169107b19cb..638c5b727a6 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -309,7 +309,7 @@ void BLF_thumb_preview(const char *filename,
/* blf_default.c */
void BLF_default_dpi(int dpi);
-void BLF_default_size(int size);
+void BLF_default_size(float size);
void BLF_default_set(int fontid);
/**
* Get default font ID so we can pass it to other functions.
diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt
index cc6e298b322..5d04823cd0a 100644
--- a/source/blender/blenfont/CMakeLists.txt
+++ b/source/blender/blenfont/CMakeLists.txt
@@ -54,7 +54,7 @@ set(LIB
bf_gpu
bf_intern_guardedalloc
- ${FREETYPE_LIBRARY}
+ ${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES}
)
if(WIN32)
@@ -63,10 +63,6 @@ if(WIN32)
)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
list(APPEND INC
diff --git a/source/blender/blenfont/intern/blf_default.c b/source/blender/blenfont/intern/blf_default.c
index 57eeaa6768d..d5a0d514b5f 100644
--- a/source/blender/blenfont/intern/blf_default.c
+++ b/source/blender/blenfont/intern/blf_default.c
@@ -37,15 +37,15 @@
/* Default size and dpi, for BLF_draw_default. */
static int global_font_default = -1;
static int global_font_dpi = 72;
-/* Keep in sync with `UI_style_get()->widgetlabel.points` */
-static int global_font_size = 11;
+/* Keep in sync with `UI_DEFAULT_TEXT_POINTS` */
+static float global_font_size = 11.0f;
void BLF_default_dpi(int dpi)
{
global_font_dpi = dpi;
}
-void BLF_default_size(int size)
+void BLF_default_size(float size)
{
global_font_size = size;
}
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 14d3a208f69..3ac9fdc7f41 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -34,7 +34,6 @@
#include FT_FREETYPE_H
#include FT_GLYPH_H
-#include FT_ADVANCES_H /* For FT_Get_Advance */
#include "MEM_guardedalloc.h"
@@ -826,7 +825,10 @@ float blf_font_height(FontBLF *font,
float blf_font_fixed_width(FontBLF *font)
{
- return (float)font->fixed_width;
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ float width = (gc) ? (float)gc->fixed_width : font->size / 2.0f;
+ blf_glyph_cache_release(font);
+ return width;
}
static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
@@ -1236,6 +1238,12 @@ FontBLF *blf_font_new(const char *name, const char *filename)
font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new");
err = FT_New_Face(ft_lib, filename, 0, &font->face);
if (err) {
+ if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) {
+ printf("Format of this font file is not supported\n");
+ }
+ else {
+ printf("Error encountered while opening font file\n");
+ }
MEM_freeN(font);
return NULL;
}
@@ -1318,12 +1326,7 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m
void blf_font_free(FontBLF *font)
{
- BLI_spin_lock(&blf_glyph_cache_mutex);
- GlyphCacheBLF *gc;
-
- while ((gc = BLI_pophead(&font->cache))) {
- blf_glyph_cache_free(gc);
- }
+ blf_glyph_cache_clear(font);
if (font->kerning_cache) {
MEM_freeN(font->kerning_cache);
@@ -1337,8 +1340,6 @@ void blf_font_free(FontBLF *font)
MEM_freeN(font->name);
}
MEM_freeN(font);
-
- BLI_spin_unlock(&blf_glyph_cache_mutex);
}
/** \} */
@@ -1347,51 +1348,25 @@ void blf_font_free(FontBLF *font)
/** \name Font Configure
* \{ */
-void blf_font_size(FontBLF *font, float size, unsigned int dpi)
+bool blf_font_size(FontBLF *font, float size, unsigned int dpi)
{
- blf_glyph_cache_acquire(font);
-
/* FreeType uses fixed-point integers in 64ths. */
FT_F26Dot6 ft_size = lroundf(size * 64.0f);
- /* Adjust our size to be on even 64ths. */
+ /* Adjust our new size to be on even 64ths. */
size = (float)ft_size / 64.0f;
- GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi);
- if (gc && (font->size == size && font->dpi == dpi)) {
- /* Optimization: do not call FT_Set_Char_Size if size did not change. */
- }
- else {
- const FT_Error err = FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi);
- if (err) {
- /* FIXME: here we can go through the fixed size and choice a close one */
- printf("The current font don't support the size, %f and dpi, %u\n", size, dpi);
- }
- else {
+ if (font->size != size || font->dpi != dpi) {
+ if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == 0) {
font->size = size;
font->dpi = dpi;
- if (gc == NULL) {
- blf_glyph_cache_new(font);
- }
+ }
+ else {
+ printf("The current font does not support the size, %f and dpi, %u\n", size, dpi);
+ return false;
}
}
- blf_glyph_cache_release(font);
-
- /* Set fixed-width size for monospaced output. */
- FT_UInt gindex = FT_Get_Char_Index(font->face, U'0');
- if (gindex) {
- FT_Fixed advance = 0;
- FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance);
- /* Use CSS 'ch unit' width, advance of zero character. */
- font->fixed_width = (int)(advance >> 16);
- }
- else {
- /* Font does not contain "0" so use CSS fallback of 1/2 of em. */
- font->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6);
- }
- if (font->fixed_width < 1) {
- font->fixed_width = 1;
- }
+ return true;
}
/** \} */
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index 4f25f99b65c..bcd9e1fb3a2 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -34,6 +34,7 @@
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include FT_BITMAP_H
+#include FT_ADVANCES_H /* For FT_Get_Advance. */
#include "MEM_guardedalloc.h"
@@ -73,7 +74,7 @@ static FT_Fixed to_16dot16(double val)
/** \name Glyph Cache
* \{ */
-GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi)
+static GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi)
{
GlyphCacheBLF *gc = (GlyphCacheBLF *)font->cache.first;
while (gc) {
@@ -86,7 +87,7 @@ GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi)
return NULL;
}
-GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
+static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
{
GlyphCacheBLF *gc = (GlyphCacheBLF *)MEM_callocN(sizeof(GlyphCacheBLF), "blf_glyph_cache_new");
@@ -100,6 +101,22 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table));
memset(gc->bucket, 0, sizeof(gc->bucket));
+ /* Determine ideal fixed-width size for monospaced output. */
+ FT_UInt gindex = FT_Get_Char_Index(font->face, U'0');
+ if (gindex) {
+ FT_Fixed advance = 0;
+ FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance);
+ /* Use CSS 'ch unit' width, advance of zero character. */
+ gc->fixed_width = (int)(advance >> 16);
+ }
+ else {
+ /* Font does not contain "0" so use CSS fallback of 1/2 of em. */
+ gc->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6);
+ }
+ if (gc->fixed_width < 1) {
+ gc->fixed_width = 1;
+ }
+
BLI_addhead(&font->cache, gc);
return gc;
}
@@ -122,20 +139,7 @@ void blf_glyph_cache_release(FontBLF *font)
BLI_spin_unlock(font->glyph_cache_mutex);
}
-void blf_glyph_cache_clear(FontBLF *font)
-{
- GlyphCacheBLF *gc;
-
- BLI_spin_lock(font->glyph_cache_mutex);
-
- while ((gc = BLI_pophead(&font->cache))) {
- blf_glyph_cache_free(gc);
- }
-
- BLI_spin_unlock(font->glyph_cache_mutex);
-}
-
-void blf_glyph_cache_free(GlyphCacheBLF *gc)
+static void blf_glyph_cache_free(GlyphCacheBLF *gc)
{
GlyphBLF *g;
for (uint i = 0; i < ARRAY_SIZE(gc->bucket); i++) {
@@ -152,6 +156,19 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc)
MEM_freeN(gc);
}
+void blf_glyph_cache_clear(FontBLF *font)
+{
+ GlyphCacheBLF *gc;
+
+ BLI_spin_lock(font->glyph_cache_mutex);
+
+ while ((gc = BLI_pophead(&font->cache))) {
+ blf_glyph_cache_free(gc);
+ }
+
+ BLI_spin_unlock(font->glyph_cache_mutex);
+}
+
/**
* Try to find a glyph in cache.
*
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index 4e36f522981..d0bb3385e2c 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -56,7 +56,11 @@ struct FontBLF *blf_font_new(const char *name, const char *filename);
struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size);
void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size);
-void blf_font_size(struct FontBLF *font, float size, unsigned int dpi);
+/**
+ * Change font's output size. Returns true if successful in changing the size.
+ */
+bool blf_font_size(struct FontBLF *font, float size, unsigned int dpi);
+
void blf_font_draw(struct FontBLF *font,
const char *str,
size_t str_len,
@@ -65,10 +69,7 @@ void blf_font_draw__wrap(struct FontBLF *font,
const char *str,
size_t str_len,
struct ResultBLF *r_info);
-void blf_font_draw_ascii(struct FontBLF *font,
- const char *str,
- size_t str_len,
- struct ResultBLF *r_info);
+
/**
* Use fixed column width, but an utf8 character may occupy multiple columns.
*/
@@ -137,18 +138,9 @@ int blf_font_count_missing_chars(struct FontBLF *font,
void blf_font_free(struct FontBLF *font);
-/**
- * Find a glyph cache that matches a size, DPI & styles.
- */
-struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font, float size, unsigned int dpi);
-/**
- * Create a new glyph cache for the current size, DPI & styles.
- */
-struct GlyphCacheBLF *blf_glyph_cache_new(struct FontBLF *font);
struct GlyphCacheBLF *blf_glyph_cache_acquire(struct FontBLF *font);
void blf_glyph_cache_release(struct FontBLF *font);
void blf_glyph_cache_clear(struct FontBLF *font);
-void blf_glyph_cache_free(struct GlyphCacheBLF *gc);
/**
* Create (or load from cache) a fully-rendered bitmap glyph.
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 46156edbb1f..c96febd71ae 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -73,6 +73,9 @@ typedef struct GlyphCacheBLF {
bool bold;
bool italic;
+ /* Column width when printing monospaced. */
+ int fixed_width;
+
/* and the glyphs. */
ListBase bucket[257];
@@ -207,9 +210,6 @@ typedef struct FontBLF {
/* font size. */
float size;
- /* Column width when printing monospaced. */
- int fixed_width;
-
/* max texture size. */
int tex_size_max;
diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c
index 06bbd0cf521..f666976547e 100644
--- a/source/blender/blenfont/intern/blf_thumbs.c
+++ b/source/blender/blenfont/intern/blf_thumbs.c
@@ -61,7 +61,6 @@ void BLF_thumb_preview(const char *filename,
int font_shrink = 4;
FontBLF *font;
- GlyphCacheBLF *gc;
/* Create a new blender font obj and fill it with default values */
font = blf_font_new("thumb_font", filename);
@@ -90,10 +89,8 @@ void BLF_thumb_preview(const char *filename,
const size_t draw_str_i18n_len = strlen(draw_str_i18n);
int draw_str_i18n_nbr = 0;
- blf_font_size(font, (float)MAX2(font_size_min, font_size_curr), dpi);
- gc = blf_glyph_cache_find(font, font->size, font->dpi);
- /* There will be no matching glyph cache if blf_font_size() failed to set font size. */
- if (!gc) {
+ CLAMP_MIN(font_size_curr, font_size_min);
+ if (!blf_font_size(font, (float)font_size_curr, dpi)) {
break;
}
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h
index 269d90b868e..4526bc38a70 100644
--- a/source/blender/blenkernel/BKE_DerivedMesh.h
+++ b/source/blender/blenkernel/BKE_DerivedMesh.h
@@ -104,14 +104,6 @@ typedef enum DerivedMeshType {
DM_TYPE_CCGDM,
} DerivedMeshType;
-typedef enum DMDirtyFlag {
- /* dm has valid tessellated faces, but tessellated CDDATA need to be updated. */
- DM_DIRTY_TESS_CDLAYERS = 1 << 0,
-
- /* check this with modifier dependsOnNormals callback to see if normals need recalculation */
- DM_DIRTY_NORMALS = 1 << 1,
-} DMDirtyFlag;
-
typedef struct DerivedMesh DerivedMesh;
struct DerivedMesh {
/** Private DerivedMesh data, only for internal DerivedMesh use */
@@ -120,7 +112,6 @@ struct DerivedMesh {
int needsFree; /* checked on ->release, is set to 0 for cached results */
int deformedOnly; /* set by modifier stack if only deformed from original */
DerivedMeshType type;
- DMDirtyFlag dirty;
/**
* \warning Typical access is done via #getLoopTriArray, #getNumLoopTri.
@@ -139,9 +130,6 @@ struct DerivedMesh {
short tangent_mask; /* which tangent layers are calculated */
- /** Calculate vert and face normals */
- void (*calcNormals)(DerivedMesh *dm);
-
/** Loop tessellation cache (WARNING! Only call inside threading-protected code!) */
void (*recalcLoopTri)(DerivedMesh *dm);
/** accessor functions */
@@ -164,7 +152,6 @@ struct DerivedMesh {
*/
struct MVert *(*getVertArray)(DerivedMesh *dm);
struct MEdge *(*getEdgeArray)(DerivedMesh *dm);
- struct MFace *(*getTessFaceArray)(DerivedMesh *dm);
struct MLoop *(*getLoopArray)(DerivedMesh *dm);
struct MPoly *(*getPolyArray)(DerivedMesh *dm);
@@ -173,7 +160,6 @@ struct DerivedMesh {
*/
void (*copyVertArray)(DerivedMesh *dm, struct MVert *r_vert);
void (*copyEdgeArray)(DerivedMesh *dm, struct MEdge *r_edge);
- void (*copyTessFaceArray)(DerivedMesh *dm, struct MFace *r_face);
void (*copyLoopArray)(DerivedMesh *dm, struct MLoop *r_loop);
void (*copyPolyArray)(DerivedMesh *dm, struct MPoly *r_poly);
@@ -182,37 +168,18 @@ struct DerivedMesh {
*/
struct MVert *(*dupVertArray)(DerivedMesh *dm);
struct MEdge *(*dupEdgeArray)(DerivedMesh *dm);
- struct MFace *(*dupTessFaceArray)(DerivedMesh *dm);
struct MLoop *(*dupLoopArray)(DerivedMesh *dm);
struct MPoly *(*dupPolyArray)(DerivedMesh *dm);
- /** Return a pointer to a single element of vert/edge/face custom data
- * from the derived mesh (this gives a pointer to the actual data, not
- * a copy)
- */
- void *(*getVertData)(DerivedMesh *dm, int index, int type);
- void *(*getEdgeData)(DerivedMesh *dm, int index, int type);
- void *(*getTessFaceData)(DerivedMesh *dm, int index, int type);
- void *(*getPolyData)(DerivedMesh *dm, int index, int type);
-
/** Return a pointer to the entire array of vert/edge/face custom data
* from the derived mesh (this gives a pointer to the actual data, not
* a copy)
*/
void *(*getVertDataArray)(DerivedMesh *dm, int type);
void *(*getEdgeDataArray)(DerivedMesh *dm, int type);
- void *(*getTessFaceDataArray)(DerivedMesh *dm, int type);
void *(*getLoopDataArray)(DerivedMesh *dm, int type);
void *(*getPolyDataArray)(DerivedMesh *dm, int type);
- /** Retrieves the base CustomData structures for
- * verts/edges/tessfaces/loops/faces. */
- CustomData *(*getVertDataLayout)(DerivedMesh *dm);
- CustomData *(*getEdgeDataLayout)(DerivedMesh *dm);
- CustomData *(*getTessFaceDataLayout)(DerivedMesh *dm);
- CustomData *(*getLoopDataLayout)(DerivedMesh *dm);
- CustomData *(*getPolyDataLayout)(DerivedMesh *dm);
-
/** Optional grid access for subsurf */
int (*getNumGrids)(DerivedMesh *dm);
int (*getGridSize)(DerivedMesh *dm);
@@ -231,11 +198,6 @@ struct DerivedMesh {
/** Get smooth vertex normal, undefined if index is not valid */
void (*getVertNo)(DerivedMesh *dm, int index, float r_no[3]);
- void (*getPolyNo)(DerivedMesh *dm, int index, float r_no[3]);
-
- /** Get a map of vertices to faces
- */
- const struct MeshElemMap *(*getPolyMap)(struct Object *ob, DerivedMesh *dm);
/** Release reference to the DerivedMesh. This function decides internally
* if the DerivedMesh will be freed, or cached for later use. */
@@ -265,15 +227,6 @@ void DM_init(DerivedMesh *dm,
* Utility function to initialize a DerivedMesh for the desired number
* of vertices, edges and faces, with a layer setup copied from source
*/
-void DM_from_template_ex(DerivedMesh *dm,
- DerivedMesh *source,
- DerivedMeshType type,
- int numVerts,
- int numEdges,
- int numTessFaces,
- int numLoops,
- int numPolys,
- const struct CustomData_MeshMasks *mask);
void DM_from_template(DerivedMesh *dm,
DerivedMesh *source,
DerivedMeshType type,
@@ -303,26 +256,9 @@ void DM_set_only_copy(DerivedMesh *dm, const struct CustomData_MeshMasks *mask);
void DM_add_vert_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer);
void DM_add_edge_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer);
-void DM_add_tessface_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer);
-void DM_add_loop_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer);
void DM_add_poly_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer);
/* -------------------------------------------------------------------- */
-/** \name Custom Data Access Functions
- *
- * \return pointer to data from first layer which matches type
- * if they return NULL for valid indices, data doesn't exist.
- * \note these return pointers - any change modifies the internals of the mesh.
- * \{ */
-
-void *DM_get_vert_data(struct DerivedMesh *dm, int index, int type);
-void *DM_get_edge_data(struct DerivedMesh *dm, int index, int type);
-void *DM_get_tessface_data(struct DerivedMesh *dm, int index, int type);
-void *DM_get_poly_data(struct DerivedMesh *dm, int index, int type);
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Custom Data Layer Access Functions
*
* \return pointer to first data layer which matches type (a flat array)
@@ -332,7 +268,6 @@ void *DM_get_poly_data(struct DerivedMesh *dm, int index, int type);
void *DM_get_vert_data_layer(struct DerivedMesh *dm, int type);
void *DM_get_edge_data_layer(struct DerivedMesh *dm, int type);
-void *DM_get_tessface_data_layer(struct DerivedMesh *dm, int type);
void *DM_get_poly_data_layer(struct DerivedMesh *dm, int type);
void *DM_get_loop_data_layer(struct DerivedMesh *dm, int type);
@@ -354,8 +289,6 @@ void DM_copy_vert_data(struct DerivedMesh *source,
*/
void DM_DupPolys(DerivedMesh *source, DerivedMesh *target);
-void DM_ensure_normals(DerivedMesh *dm);
-
/**
* Ensure the array is large enough.
*
@@ -399,14 +332,8 @@ bool editbmesh_modifier_is_enabled(struct Scene *scene,
void makeDerivedMesh(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
- struct BMEditMesh *em,
const struct CustomData_MeshMasks *dataMask);
-void DM_calc_loop_tangents(DerivedMesh *dm,
- bool calc_active_tangent,
- const char (*tangent_names)[MAX_NAME],
- int tangent_names_len);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index 0b09bfd8730..5d1b4baedfd 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -365,7 +365,6 @@ void what_does_obaction(struct Object *ob,
char groupname[],
const struct AnimationEvalContext *anim_eval_context);
-/* for proxy */
void BKE_pose_copy_pchan_result(struct bPoseChannel *pchanto,
const struct bPoseChannel *pchanfrom);
/**
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index 8584ce6f508..ede300b19dd 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -163,6 +163,30 @@ void BKE_armature_transform(struct bArmature *arm, const float mat[4][4], bool d
/* Bounding box. */
struct BoundBox *BKE_armature_boundbox_get(struct Object *ob);
+/**
+ * Calculate the axis-aligned bounds of `pchan` in world-space,
+ * taking into account custom transform when set.
+ *
+ * `r_min` and `r_max` are expanded to fit `pchan` so the caller must initialize them
+ * (typically using #INIT_MINMAX).
+ *
+ * \note The bounds are calculated based on the head & tail of the bone
+ * or the custom object's bounds (if the bone uses a custom object).
+ * Visual elements such as the envelopes radius & bendy-bone spline segments are *not* included,
+ * making this not so useful for viewport culling.
+ */
+void BKE_pchan_minmax(const struct Object *ob,
+ const struct bPoseChannel *pchan,
+ float r_min[3],
+ float r_max[3]);
+/**
+ * Calculate the axis aligned bounds of the pose of `ob` in world-space.
+
+ * `r_min` and `r_max` are expanded to fit `ob->pose` so the caller must initialize them
+ * (typically using #INIT_MINMAX).
+ *
+ * \note This uses #BKE_pchan_minmax, see its documentation for details on bounds calculation.
+ */
bool BKE_pose_minmax(
struct Object *ob, float r_min[3], float r_max[3], bool use_hidden, bool use_select);
@@ -619,14 +643,6 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *object);
-void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, struct Object *object);
-void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, struct Object *object);
-void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, struct Object *object);
-
-void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph,
- struct Object *object,
- int pchan_index);
-
/* -------------------------------------------------------------------- */
/** \name Deform 3D Coordinates by Armature (armature_deform.c)
* \{ */
diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h
index f26c3906cee..73ade2ff7ac 100644
--- a/source/blender/blenkernel/BKE_attribute.h
+++ b/source/blender/blenkernel/BKE_attribute.h
@@ -40,11 +40,11 @@ struct ReportList;
/* Attribute.domain */
typedef enum AttributeDomain {
ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */
- ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */
+ ATTR_DOMAIN_POINT = 0, /* Mesh, Curve or Point Cloud Point */
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
ATTR_DOMAIN_FACE = 2, /* Mesh Face */
ATTR_DOMAIN_CORNER = 3, /* Mesh Corner */
- ATTR_DOMAIN_CURVE = 4, /* Hair Curve */
+ ATTR_DOMAIN_CURVE = 4, /* A single curve in a larger curve data-block */
ATTR_DOMAIN_INSTANCE = 5, /* Instance */
ATTR_DOMAIN_NUM
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh
index a7bdca06790..bf773cd6d75 100644
--- a/source/blender/blenkernel/BKE_attribute_math.hh
+++ b/source/blender/blenkernel/BKE_attribute_math.hh
@@ -50,6 +50,9 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f
case CD_PROP_BOOL:
func(bool());
break;
+ case CD_PROP_INT8:
+ func(int8_t());
+ break;
case CD_PROP_COLOR:
func(ColorGeometry4f());
break;
@@ -77,6 +80,9 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func
else if (cpp_type.is<bool>()) {
func(bool());
}
+ else if (cpp_type.is<int8_t>()) {
+ func(int8_t());
+ }
else if (cpp_type.is<ColorGeometry4f>()) {
func(ColorGeometry4f());
}
@@ -93,6 +99,12 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func
template<typename T> T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2);
+template<>
+inline int8_t mix3(const float3 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2)
+{
+ return static_cast<int8_t>(weights.x * v0 + weights.y * v1 + weights.z * v2);
+}
+
template<> inline bool mix3(const float3 &weights, const bool &v0, const bool &v1, const bool &v2)
{
return (weights.x * v0 + weights.y * v1 + weights.z * v2) >= 0.5f;
@@ -147,6 +159,11 @@ template<> inline bool mix2(const float factor, const bool &a, const bool &b)
return ((1.0f - factor) * a + factor * b) >= 0.5f;
}
+template<> inline int8_t mix2(const float factor, const int8_t &a, const int8_t &b)
+{
+ return static_cast<int8_t>((1.0f - factor) * a + factor * b);
+}
+
template<> inline int mix2(const float factor, const int &a, const int &b)
{
return static_cast<int>((1.0f - factor) * a + factor * b);
@@ -231,6 +248,43 @@ template<typename T> class SimpleMixer {
};
/**
+ * Mixes together booleans with "or" while fitting the same interface as the other
+ * mixers in order to be simpler to use. This mixing method has a few benefits:
+ * - An "average" for selections is relatively meaningless.
+ * - Predictable selection propagation is very super important.
+ * - It's generally easier to remove an element from a selection that is slightly too large than
+ * the opposite.
+ */
+class BooleanPropagationMixer {
+ private:
+ MutableSpan<bool> buffer_;
+
+ public:
+ /**
+ * \param buffer: Span where the interpolated values should be stored.
+ */
+ BooleanPropagationMixer(MutableSpan<bool> buffer) : buffer_(buffer)
+ {
+ buffer_.fill(false);
+ }
+
+ /**
+ * Mix a #value into the element with the given #index.
+ */
+ void mix_in(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f)
+ {
+ buffer_[index] |= value;
+ }
+
+ /**
+ * Does not do anything, since the mixing is trivial.
+ */
+ void finalize()
+ {
+ }
+};
+
+/**
* This mixer accumulates values in a type that is different from the one that is mixed.
* Some types cannot encode the floating point weights in their values (e.g. int and bool).
*/
@@ -291,7 +345,7 @@ class ColorGeometryMixer {
};
template<typename T> struct DefaultMixerStruct {
- /* Use void by default. This can be check for in `if constexpr` statements. */
+ /* Use void by default. This can be checked for in `if constexpr` statements. */
using type = void;
};
template<> struct DefaultMixerStruct<float> {
@@ -327,6 +381,32 @@ template<> struct DefaultMixerStruct<bool> {
using type = SimpleMixerWithAccumulationType<bool, float, float_to_bool>;
};
+template<> struct DefaultMixerStruct<int8_t> {
+ static int8_t float_to_int8_t(const float &value)
+ {
+ return static_cast<int8_t>(value);
+ }
+ /* Store interpolated 8 bit integers in a float temporarily to increase accuracy. */
+ using type = SimpleMixerWithAccumulationType<int8_t, float, float_to_int8_t>;
+};
+
+template<typename T> struct DefaultPropatationMixerStruct {
+ /* Use void by default. This can be checked for in `if constexpr` statements. */
+ using type = typename DefaultMixerStruct<T>::type;
+};
+
+template<> struct DefaultPropatationMixerStruct<bool> {
+ using type = BooleanPropagationMixer;
+};
+
+/**
+ * This mixer is meant for propagating attributes when creating new geometry. A key difference
+ * with the default mixer is that booleans are mixed with "or" instead of "at least half"
+ * (the default mixing for booleans).
+ */
+template<typename T>
+using DefaultPropatationMixer = typename DefaultPropatationMixerStruct<T>::type;
+
/* Utility to get a good default mixer for a given type. This is `void` when there is no default
* mixer for the given type. */
template<typename T> using DefaultMixer = typename DefaultMixerStruct<T>::type;
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index d0ab8be9a29..1ba887903c8 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -31,7 +31,7 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 301
+#define BLENDER_VERSION 302
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 5
+#define BLENDER_FILE_SUBVERSION 3
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_blendfile_link_append.h b/source/blender/blenkernel/BKE_blendfile_link_append.h
index e55a2d15dcc..983c93223a1 100644
--- a/source/blender/blenkernel/BKE_blendfile_link_append.h
+++ b/source/blender/blenkernel/BKE_blendfile_link_append.h
@@ -30,8 +30,8 @@ struct LibraryLink_Params;
struct Main;
struct ReportList;
struct Scene;
-struct ViewLayer;
struct View3D;
+struct ViewLayer;
typedef struct BlendfileLinkAppendContext BlendfileLinkAppendContext;
typedef struct BlendfileLinkAppendContextItem BlendfileLinkAppendContextItem;
diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h
index ee78621c11f..550ce4eb601 100644
--- a/source/blender/blenkernel/BKE_camera.h
+++ b/source/blender/blenkernel/BKE_camera.h
@@ -46,7 +46,7 @@ void *BKE_camera_add(struct Main *bmain, const char *name);
/**
* Get the camera's DOF value, takes the DOF object into account.
*/
-float BKE_camera_object_dof_distance(struct Object *ob);
+float BKE_camera_object_dof_distance(const struct Object *ob);
int BKE_camera_sensor_fit(int sensor_fit, float sizex, float sizey);
float BKE_camera_sensor_size(int sensor_fit, float sensor_x, float sensor_y);
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index 402bffea91d..467d74b17da 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -123,12 +123,21 @@ struct Collection *BKE_collection_object_find(struct Main *bmain,
bool BKE_collection_is_empty(const struct Collection *collection);
/**
- * Add object to collection
+ * Add object to given collection, ensuring this collection is 'editable' (i.e. local and not a
+ * liboverride), and finding a suitable parent one otherwise.
*/
bool BKE_collection_object_add(struct Main *bmain,
struct Collection *collection,
struct Object *ob);
/**
+ * Same as #BKE_collection_object_add, but unconditionally adds the object to the given collection.
+ *
+ * NOTE: required in certain cases, like do-versioning or complex ID management tasks.
+ */
+bool BKE_collection_object_add_notest(struct Main *bmain,
+ struct Collection *collection,
+ struct Object *ob);
+/**
* Add \a ob_dst to all scene collections that reference object \a ob_src is in.
* Used for copying objects.
*
diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h
index 55e5cd0a149..2da79e27576 100644
--- a/source/blender/blenkernel/BKE_constraint.h
+++ b/source/blender/blenkernel/BKE_constraint.h
@@ -278,18 +278,6 @@ bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph,
void BKE_constraint_panel_expand(struct bConstraint *con);
-/* Constraints + Proxies function prototypes */
-
-/**
- * Rescue all constraints tagged as being #CONSTRAINT_PROXY_LOCAL
- * (i.e. added to bone that's proxy-synced in this file).
- */
-void BKE_constraints_proxylocal_extract(struct ListBase *dst, struct ListBase *src);
-/**
- * Returns if the owner of the constraint is proxy-protected.
- */
-bool BKE_constraints_proxylocked_owner(struct Object *ob, struct bPoseChannel *pchan);
-
/* Constraint Evaluation function prototypes */
/**
diff --git a/source/blender/blenkernel/BKE_crazyspace.h b/source/blender/blenkernel/BKE_crazyspace.h
index c00e397f552..42d85c70bc1 100644
--- a/source/blender/blenkernel/BKE_crazyspace.h
+++ b/source/blender/blenkernel/BKE_crazyspace.h
@@ -26,10 +26,12 @@
#ifdef __cplusplus
extern "C" {
#endif
+
struct BMEditMesh;
struct Depsgraph;
struct Mesh;
struct Object;
+struct ReportList;
struct Scene;
/* crazyspace.c */
@@ -69,6 +71,31 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph,
float (**deformmats)[3][3],
float (**deformcos)[3]);
+/* -------------------------------------------------------------------- */
+/** \name Crazy-Space API
+ * \{ */
+
+void BKE_crazyspace_api_eval(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct Object *object,
+ struct ReportList *reports);
+
+void BKE_crazyspace_api_displacement_to_deformed(struct Object *object,
+ struct ReportList *reports,
+ int vertex_index,
+ float displacement[3],
+ float r_displacement_deformed[3]);
+
+void BKE_crazyspace_api_displacement_to_original(struct Object *object,
+ struct ReportList *reports,
+ int vertex_index,
+ float displacement_deformed[3],
+ float r_displacement[3]);
+
+void BKE_crazyspace_api_eval_clear(struct Object *object);
+
+/** \} */
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh
index cf5c8b87ede..10649e8703f 100644
--- a/source/blender/blenkernel/BKE_curve_to_mesh.hh
+++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh
@@ -16,8 +16,8 @@
#pragma once
-struct Mesh;
struct CurveEval;
+struct Mesh;
/** \file
* \ingroup bke
diff --git a/source/blender/blenkernel/BKE_curves.h b/source/blender/blenkernel/BKE_curves.h
new file mode 100644
index 00000000000..99839b20121
--- /dev/null
+++ b/source/blender/blenkernel/BKE_curves.h
@@ -0,0 +1,68 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ * \brief Low-level operations for curves.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BoundBox;
+struct CustomDataLayer;
+struct Depsgraph;
+struct Curves;
+struct Main;
+struct Object;
+struct Scene;
+
+void *BKE_curves_add(struct Main *bmain, const char *name);
+
+struct BoundBox *BKE_curves_boundbox_get(struct Object *ob);
+
+void BKE_curves_update_customdata_pointers(struct Curves *curves);
+bool BKE_curves_customdata_required(struct Curves *curves, struct CustomDataLayer *layer);
+
+/* Depsgraph */
+
+struct Curves *BKE_curves_new_for_eval(const struct Curves *curves_src,
+ int totpoint,
+ int totcurve);
+struct Curves *BKE_curves_copy_for_eval(struct Curves *curves_src, bool reference);
+
+void BKE_curves_data_update(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct Object *object);
+
+/* Draw Cache */
+
+enum {
+ BKE_CURVES_BATCH_DIRTY_ALL = 0,
+};
+
+void BKE_curves_batch_cache_dirty_tag(struct Curves *curves, int mode);
+void BKE_curves_batch_cache_free(struct Curves *curves);
+
+extern void (*BKE_curves_batch_cache_dirty_tag_cb)(struct Curves *curves, int mode);
+extern void (*BKE_curves_batch_cache_free_cb)(struct Curves *curves);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index 00eae2e8e6e..38b43e36feb 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -254,6 +254,11 @@ bool CustomData_free_layer_active(struct CustomData *data, int type, int totelem
void CustomData_free_layers(struct CustomData *data, int type, int totelem);
/**
+ * Free all anonymous attributes.
+ */
+void CustomData_free_layers_anonymous(struct CustomData *data, int totelem);
+
+/**
* Returns true if a layer with the specified type exists.
*/
bool CustomData_has_layer(const struct CustomData *data, int type);
@@ -437,6 +442,12 @@ int CustomData_get_clone_layer(const struct CustomData *data, int type);
int CustomData_get_stencil_layer(const struct CustomData *data, int type);
/**
+ * Returns name of the active layer of the given type or NULL
+ * if no such active layer is defined.
+ */
+const char *CustomData_get_active_layer_name(const struct CustomData *data, int type);
+
+/**
* Copies the data from source to the data element at index in the first layer of type
* no effect if there is no layer of type.
*/
diff --git a/source/blender/blenkernel/BKE_duplilist.h b/source/blender/blenkernel/BKE_duplilist.h
index 5eff84b8c9e..303a83d921f 100644
--- a/source/blender/blenkernel/BKE_duplilist.h
+++ b/source/blender/blenkernel/BKE_duplilist.h
@@ -27,11 +27,11 @@ extern "C" {
#endif
struct Depsgraph;
+struct ID;
struct ListBase;
struct Object;
struct ParticleSystem;
struct Scene;
-struct ID;
/* ---------------------------------------------------- */
/* Dupli-Geometry */
diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h
index 5be06dcc5c3..1da7ae3da8a 100644
--- a/source/blender/blenkernel/BKE_editmesh.h
+++ b/source/blender/blenkernel/BKE_editmesh.h
@@ -62,14 +62,6 @@ typedef struct BMEditMesh {
struct BMLoop *(*looptris)[3];
int tottri;
- struct Mesh *mesh_eval_final, *mesh_eval_cage;
-
- /** Cached cage bounding box of `mesh_eval_cage` for selection. */
- struct BoundBox *bb_cage;
-
- /** Evaluated mesh data-mask. */
- CustomData_MeshMasks lastDataMask;
-
/** Selection mode (#SCE_SELECT_VERTEX, #SCE_SELECT_EDGE & #SCE_SELECT_FACE). */
short selectmode;
/** The active material (assigned to newly created faces). */
@@ -121,7 +113,6 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em);
* don't add NULL data check here. caller must do that
*/
BMEditMesh *BKE_editmesh_from_object(struct Object *ob);
-void BKE_editmesh_free_derived_caches(BMEditMesh *em);
/**
* \note Does not free the #BMEditMesh struct itself.
*/
@@ -145,7 +136,7 @@ void BKE_editmesh_lnorspace_update(BMEditMesh *em, struct Mesh *me);
* If auto-smooth not already set, set it.
*/
void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, struct Mesh *me);
-struct BoundBox *BKE_editmesh_cage_boundbox_get(BMEditMesh *em);
+struct BoundBox *BKE_editmesh_cage_boundbox_get(struct Object *object, BMEditMesh *em);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_hair.h b/source/blender/blenkernel/BKE_hair.h
deleted file mode 100644
index 403e461a9dc..00000000000
--- a/source/blender/blenkernel/BKE_hair.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup bke
- * \brief General operations for hairs.
- */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct BoundBox;
-struct CustomDataLayer;
-struct Depsgraph;
-struct Hair;
-struct Main;
-struct Object;
-struct Scene;
-
-void *BKE_hair_add(struct Main *bmain, const char *name);
-
-struct BoundBox *BKE_hair_boundbox_get(struct Object *ob);
-
-void BKE_hair_update_customdata_pointers(struct Hair *hair);
-bool BKE_hair_customdata_required(struct Hair *hair, struct CustomDataLayer *layer);
-
-/* Depsgraph */
-
-struct Hair *BKE_hair_new_for_eval(const struct Hair *hair_src, int totpoint, int totcurve);
-struct Hair *BKE_hair_copy_for_eval(struct Hair *hair_src, bool reference);
-
-void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *object);
-
-/* Draw Cache */
-
-enum {
- BKE_HAIR_BATCH_DIRTY_ALL = 0,
-};
-
-void BKE_hair_batch_cache_dirty_tag(struct Hair *hair, int mode);
-void BKE_hair_batch_cache_free(struct Hair *hair);
-
-extern void (*BKE_hair_batch_cache_dirty_tag_cb)(struct Hair *hair, int mode);
-extern void (*BKE_hair_batch_cache_free_cb)(struct Hair *hair);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index f83675fbb7e..e9e5b183e4a 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -31,11 +31,11 @@
extern "C" {
#endif
+struct BPathForeachPathData;
struct BlendDataReader;
struct BlendExpander;
struct BlendLibReader;
struct BlendWriter;
-struct BPathForeachPathData;
struct ID;
struct LibraryForeachIDData;
struct Main;
@@ -278,7 +278,7 @@ extern IDTypeInfo IDType_ID_PC;
extern IDTypeInfo IDType_ID_CF;
extern IDTypeInfo IDType_ID_WS;
extern IDTypeInfo IDType_ID_LP;
-extern IDTypeInfo IDType_ID_HA;
+extern IDTypeInfo IDType_ID_CV;
extern IDTypeInfo IDType_ID_PT;
extern IDTypeInfo IDType_ID_VO;
extern IDTypeInfo IDType_ID_SIM;
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 80c6b155be0..598818ba3c0 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -24,6 +24,8 @@
#include "BLI_utildefines.h"
+#include "BLI_rect.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -561,19 +563,27 @@ struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image,
* Is the alpha of the `GPUTexture` for a given image/ibuf premultiplied.
*/
bool BKE_image_has_gpu_texture_premultiplied_alpha(struct Image *image, struct ImBuf *ibuf);
+
/**
* Partial update of texture for texture painting.
* This is often much quicker than fully updating the texture for high resolution images.
*/
void BKE_image_update_gputexture(
struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h);
+
/**
* Mark areas on the #GPUTexture that needs to be updated. The areas are marked in chunks.
* The next time the #GPUTexture is used these tiles will be refreshes. This saves time
* when writing to the same place multiple times This happens for during foreground rendering.
*/
-void BKE_image_update_gputexture_delayed(
- struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h);
+void BKE_image_update_gputexture_delayed(struct Image *ima,
+ struct ImageTile *image_tile,
+ struct ImBuf *ibuf,
+ int x,
+ int y,
+ int w,
+ int h);
+
/**
* Called on entering and exiting texture paint mode,
* temporary disabling/enabling mipmapping on all images for quick texture
@@ -591,6 +601,32 @@ bool BKE_image_remove_renderslot(struct Image *ima, struct ImageUser *iuser, int
struct RenderSlot *BKE_image_get_renderslot(struct Image *ima, int index);
bool BKE_image_clear_renderslot(struct Image *ima, struct ImageUser *iuser, int slot);
+/* --- image_partial_update.cc --- */
+/** Image partial updates. */
+struct PartialUpdateUser;
+
+/**
+ * \brief Create a new PartialUpdateUser. An Object that contains data to use partial updates.
+ */
+struct PartialUpdateUser *BKE_image_partial_update_create(const struct Image *image);
+
+/**
+ * \brief free a partial update user.
+ */
+void BKE_image_partial_update_free(struct PartialUpdateUser *user);
+
+/* --- partial updater (image side) --- */
+struct PartialUpdateRegister;
+
+void BKE_image_partial_update_register_free(struct Image *image);
+/** \brief Mark a region of the image to update. */
+void BKE_image_partial_update_mark_region(struct Image *image,
+ const struct ImageTile *image_tile,
+ const struct ImBuf *image_buffer,
+ const rcti *updated_region);
+/** \brief Mark the whole image to be updated. */
+void BKE_image_partial_update_mark_full_update(struct Image *image);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_image_partial_update.hh b/source/blender/blenkernel/BKE_image_partial_update.hh
new file mode 100644
index 00000000000..0fc05809bbd
--- /dev/null
+++ b/source/blender/blenkernel/BKE_image_partial_update.hh
@@ -0,0 +1,298 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * To reduce the overhead of image processing this file contains a mechanism to detect areas of the
+ * image that are changed. These areas are organized in chunks. Changes that happen over time are
+ * organized in changesets.
+ *
+ * A common use case is to update #GPUTexture for drawing where only that part is uploaded that
+ * only changed.
+ */
+
+#pragma once
+
+#include "BLI_utildefines.h"
+
+#include "BLI_rect.h"
+
+#include "DNA_image_types.h"
+
+extern "C" {
+struct PartialUpdateUser;
+struct PartialUpdateRegister;
+}
+
+namespace blender::bke::image {
+
+using TileNumber = int;
+
+namespace partial_update {
+
+/* --- image_partial_update.cc --- */
+/** Image partial updates. */
+
+/**
+ * \brief Result codes of #BKE_image_partial_update_collect_changes.
+ */
+enum class ePartialUpdateCollectResult {
+ /** \brief Unable to construct partial updates. Caller should perform a full update. */
+ FullUpdateNeeded,
+
+ /** \brief No changes detected since the last time requested. */
+ NoChangesDetected,
+
+ /** \brief Changes detected since the last time requested. */
+ PartialChangesDetected,
+};
+
+/**
+ * \brief A region to update.
+ *
+ * Data is organized in tiles. These tiles are in texel space (1 unit is a single texel). When
+ * tiles are requested they are merged with neighboring tiles.
+ */
+struct PartialUpdateRegion {
+ /** \brief region of the image that has been updated. Region can be bigger than actual changes.
+ */
+ struct rcti region;
+
+ /**
+ * \brief Tile number (UDIM) that this region belongs to.
+ */
+ TileNumber tile_number;
+};
+
+/**
+ * \brief Return codes of #BKE_image_partial_update_get_next_change.
+ */
+enum class ePartialUpdateIterResult {
+ /** \brief no tiles left when iterating over tiles. */
+ Finished = 0,
+
+ /** \brief a chunk was available and has been loaded. */
+ ChangeAvailable = 1,
+};
+
+/**
+ * \brief collect the partial update since the last request.
+ *
+ * Invoke #BKE_image_partial_update_get_next_change to iterate over the collected tiles.
+ *
+ * \returns ePartialUpdateCollectResult::FullUpdateNeeded: called should not use partial updates
+ * but recalculate the full image. This result can be expected when called for the first time for a
+ * user and when it isn't possible to reconstruct the changes as the internal state doesn't have
+ * enough data stored. ePartialUpdateCollectResult::NoChangesDetected: The have been no changes
+ * detected since last invoke for the same user.
+ * ePartialUpdateCollectResult::PartialChangesDetected: Parts of the image has been updated since
+ * last invoke for the same user. The changes can be read by using
+ * #BKE_image_partial_update_get_next_change.
+ */
+ePartialUpdateCollectResult BKE_image_partial_update_collect_changes(
+ struct Image *image, struct PartialUpdateUser *user);
+
+ePartialUpdateIterResult BKE_image_partial_update_get_next_change(
+ struct PartialUpdateUser *user, struct PartialUpdateRegion *r_region);
+
+/** \brief Abstract class to load tile data when using the PartialUpdateChecker. */
+class AbstractTileData {
+ protected:
+ virtual ~AbstractTileData() = default;
+
+ public:
+ /**
+ * \brief Load the data for the given tile_number.
+ *
+ * Invoked when changes are on a different tile compared to the previous tile..
+ */
+ virtual void init_data(TileNumber tile_number) = 0;
+ /**
+ * \brief Unload the data that has been loaded.
+ *
+ * Invoked when changes are on a different tile compared to the previous tile or when finished
+ * iterating over the changes.
+ */
+ virtual void free_data() = 0;
+};
+
+/**
+ * \brief Class to not load any tile specific data when iterating over changes.
+ */
+class NoTileData : AbstractTileData {
+ public:
+ NoTileData(Image *UNUSED(image), ImageUser *UNUSED(image_user))
+ {
+ }
+
+ void init_data(TileNumber UNUSED(new_tile_number)) override
+ {
+ }
+
+ void free_data() override
+ {
+ }
+};
+
+/**
+ * \brief Load the ImageTile and ImBuf associated with the partial change.
+ */
+class ImageTileData : AbstractTileData {
+ public:
+ /**
+ * \brief Not owned Image that is being iterated over.
+ */
+ Image *image;
+
+ /**
+ * \brief Local copy of the image user.
+ *
+ * The local copy is required so we don't change the image user of the caller.
+ * We need to change it in order to request data for a specific tile.
+ */
+ ImageUser image_user = {0};
+
+ /**
+ * \brief ImageTile associated with the loaded tile.
+ * Data is not owned by this instance but by the `image`.
+ */
+ ImageTile *tile = nullptr;
+
+ /**
+ * \brief ImBuf of the loaded tile.
+ *
+ * Can be nullptr when the file doesn't exist or when the tile hasn't been initialized.
+ */
+ ImBuf *tile_buffer = nullptr;
+
+ ImageTileData(Image *image, ImageUser *image_user) : image(image)
+ {
+ if (image_user != nullptr) {
+ this->image_user = *image_user;
+ }
+ }
+
+ void init_data(TileNumber new_tile_number) override
+ {
+ image_user.tile = new_tile_number;
+ tile = BKE_image_get_tile(image, new_tile_number);
+ tile_buffer = BKE_image_acquire_ibuf(image, &image_user, NULL);
+ }
+
+ void free_data() override
+ {
+ BKE_image_release_ibuf(image, tile_buffer, nullptr);
+ tile = nullptr;
+ tile_buffer = nullptr;
+ }
+};
+
+template<typename TileData = NoTileData> struct PartialUpdateChecker {
+
+ /**
+ * \brief Not owned Image that is being iterated over.
+ */
+ Image *image;
+ ImageUser *image_user;
+
+ /**
+ * \brief the collected changes are stored inside the PartialUpdateUser.
+ */
+ PartialUpdateUser *user;
+
+ struct CollectResult {
+ PartialUpdateChecker<TileData> *checker;
+
+ /**
+ * \brief Tile specific data.
+ */
+ TileData tile_data;
+ PartialUpdateRegion changed_region;
+ ePartialUpdateCollectResult result_code;
+
+ private:
+ TileNumber last_tile_number;
+
+ public:
+ CollectResult(PartialUpdateChecker<TileData> *checker, ePartialUpdateCollectResult result_code)
+ : checker(checker),
+ tile_data(checker->image, checker->image_user),
+ result_code(result_code)
+ {
+ }
+
+ const ePartialUpdateCollectResult get_result_code() const
+ {
+ return result_code;
+ }
+
+ /**
+ * \brief Load the next changed region.
+ *
+ * This member function can only be called when partial changes are detected.
+ * (`get_result_code()` returns `ePartialUpdateCollectResult::PartialChangesDetected`).
+ *
+ * When changes for another tile than the previous tile is loaded the #tile_data will be
+ * updated.
+ */
+ ePartialUpdateIterResult get_next_change()
+ {
+ BLI_assert(result_code == ePartialUpdateCollectResult::PartialChangesDetected);
+ ePartialUpdateIterResult result = BKE_image_partial_update_get_next_change(checker->user,
+ &changed_region);
+ switch (result) {
+ case ePartialUpdateIterResult::Finished:
+ tile_data.free_data();
+ return result;
+
+ case ePartialUpdateIterResult::ChangeAvailable:
+ if (last_tile_number == changed_region.tile_number) {
+ return result;
+ }
+ tile_data.free_data();
+ tile_data.init_data(changed_region.tile_number);
+ last_tile_number = changed_region.tile_number;
+ return result;
+
+ default:
+ BLI_assert_unreachable();
+ return result;
+ }
+ }
+ };
+
+ public:
+ PartialUpdateChecker(Image *image, ImageUser *image_user, PartialUpdateUser *user)
+ : image(image), image_user(image_user), user(user)
+ {
+ }
+
+ /**
+ * \brief Check for new changes since the last time this method was invoked for this #user.
+ */
+ CollectResult collect_changes()
+ {
+ ePartialUpdateCollectResult collect_result = BKE_image_partial_update_collect_changes(image,
+ user);
+ return CollectResult(this, collect_result);
+ }
+};
+
+} // namespace partial_update
+} // namespace blender::bke::image
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index bc2249b93b9..accdfe1ca25 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -157,6 +157,14 @@ int BKE_layer_collection_findindex(struct ViewLayer *view_layer, const struct La
void BKE_layer_collection_resync_forbid(void);
void BKE_layer_collection_resync_allow(void);
+/**
+ * Helper to fix older pre-2.80 blend-files.
+ *
+ * Ensures the given `view_layer` as a valid first-level layer collection, i.e. a single one
+ * matching the scene's master collection. This is a requirement for `BKE_layer_collection_sync`.
+ */
+void BKE_layer_collection_doversion_2_80(const struct Scene *scene, struct ViewLayer *view_layer);
+
void BKE_main_collection_sync(const struct Main *bmain);
void BKE_scene_collection_sync(const struct Scene *scene);
/**
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index 1d905ad85b1..daf13590ca2 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -376,13 +376,19 @@ enum {
/** Clear asset data (in case the ID can actually be made local, in copy case asset data is never
* copied over). */
LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR = 1 << 3,
-
- /* Special type-specific options. */
- /** For Objects, do not clear the proxy pointers while making the data-block local. */
- LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING = 1 << 16,
};
/**
+ * Helper to decide whether given `id` can be directly made local, or needs to be copied.
+ * `r_force_local` and `r_force_copy` cannot be true together. But both can be false, in case no
+ * action should be performed.
+ *
+ * \note low-level helper to de-duplicate logic between `BKE_lib_id_make_local_generic` and the
+ * specific corner-cases implementations needed for objects and brushes.
+ */
+void BKE_lib_id_make_local_generic_action_define(
+ struct Main *bmain, struct ID *id, int flags, bool *r_force_local, bool *r_force_copy);
+/**
* Generic 'make local' function, works for most of data-block types.
*/
void BKE_lib_id_make_local_generic(struct Main *bmain, struct ID *id, int flags);
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index 30e75259967..e24f8dd8927 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -100,6 +100,9 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
* main. You can add more local IDs to be remapped to use new overriding ones by setting their
* LIB_TAG_DOIT tag.
*
+ * \param owner_library: the library in which the overrides should be created. Besides versioning
+ * and resync code path, this should always be NULL (i.e. the local .blend file).
+ *
* \param reference_library: the library from which the linked data being overridden come from
* (i.e. the library of the linked reference ID).
*
@@ -109,7 +112,8 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
* \return \a true on success, \a false otherwise.
*/
bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
- const struct Library *reference_library,
+ struct Library *owner_library,
+ const struct ID *id_root_reference,
bool do_no_main);
/**
* Advanced 'smart' function to create fully functional overrides.
@@ -122,16 +126,24 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
*
* \param view_layer: the active view layer to search instantiated collections in, can be NULL (in
* which case \a scene's master collection children hierarchy is used instead).
+ *
+ * \param owner_library: the library in which the overrides should be created. Besides versioning
+ * and resync code path, this should always be NULL (i.e. the local .blend file).
+ *
* \param id_root: The root ID to create an override from.
+ *
* \param id_reference: Some reference ID used to do some post-processing after overrides have been
* created, may be NULL. Typically, the Empty object instantiating the linked collection we
* override, currently.
+ *
* \param r_id_root_override: if not NULL, the override generated for the given \a id_root.
+ *
* \return true if override was successfully created.
*/
bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
+ struct Library *owner_library,
struct ID *id_root,
struct ID *id_reference,
struct ID **r_id_root_override);
@@ -160,6 +172,15 @@ bool BKE_lib_override_library_proxy_convert(struct Main *bmain,
*/
void BKE_lib_override_library_main_proxy_convert(struct Main *bmain,
struct BlendFileReadReport *reports);
+
+/**
+ * Find and set the 'hierarchy root' ID pointer of all library overrides in given `bmain`.
+ *
+ * NOTE: Cannot be called from `do_versions_after_linking` as this code needs a single complete
+ * Main database, not a split-by-libraries one.
+ */
+void BKE_lib_override_library_main_hierarchy_root_ensure(struct Main *bmain);
+
/**
* Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked
* data, from an existing override hierarchy.
diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h
index d853cb16b13..dd2e2a2f8e5 100644
--- a/source/blender/blenkernel/BKE_lib_query.h
+++ b/source/blender/blenkernel/BKE_lib_query.h
@@ -63,7 +63,7 @@ enum {
/**
* That ID is not really used by its owner, it's just an internal hint/helper.
- * This addresses Their Highest Ugliness the 'from' pointers: Object->from_proxy and Key->from.
+ * This marks the 'from' pointers issue, like Key->from.
* How to handle that kind of cases totally depends on what caller code is doing... */
IDWALK_CB_LOOPBACK = (1 << 4),
@@ -135,7 +135,6 @@ enum {
/** Do not process ID pointers inside embedded IDs. Needed by depsgraph processing e.g. */
IDWALK_IGNORE_EMBEDDED_ID = (1 << 3),
- IDWALK_NO_INDIRECT_PROXY_DATA_USAGE = (1 << 8), /* Ugly special case :(((( */
/** Also process internal ID pointers like `ID.newid` or `ID.orig_id`.
* WARNING: Dangerous, use with caution. */
IDWALK_DO_INTERNAL_RUNTIME_POINTERS = (1 << 9),
diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index d8842dbce7f..f14cd75c5c6 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -38,6 +38,9 @@
extern "C" {
#endif
+struct ID;
+struct IDRemapper;
+
/* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */
/* Also IDRemap->flag. */
@@ -65,15 +68,6 @@ enum {
* and can cause crashes very easily!
*/
ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3,
- /**
- * Do not consider proxy/_group pointers of local objects as indirect usages...
- * Our oh-so-beloved proxies again...
- * Do not consider data used by local proxy object as indirect usage.
- * This is needed e.g. in reload scenario,
- * since we have to ensure remapping of Armature data of local proxy
- * is also performed. Usual nightmare...
- */
- ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4,
/** Do not remap library override pointers. */
ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5,
/** Don't touch the user count (use for low level actions such as swapping pointers). */
@@ -98,6 +92,19 @@ enum {
};
/**
+ * Replace all references in given Main using the given \a mappings
+ *
+ * \note Is preferred over BKE_libblock_remap_locked due to performance.
+ */
+void BKE_libblock_remap_multiple_locked(struct Main *bmain,
+ const struct IDRemapper *mappings,
+ const short remap_flags);
+
+void BKE_libblock_remap_multiple(struct Main *bmain,
+ const struct IDRemapper *mappings,
+ const short remap_flags);
+
+/**
* Replace all references in given Main to \a old_id by \a new_id
* (if \a new_id is NULL, it unlinks \a old_id).
*
@@ -146,12 +153,61 @@ void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id, int remap_f
ATTR_NONNULL();
typedef void (*BKE_library_free_notifier_reference_cb)(const void *);
-typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *);
+typedef void (*BKE_library_remap_editor_id_reference_cb)(const struct IDRemapper *mappings);
void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func);
void BKE_library_callback_remap_editor_id_reference_set(
BKE_library_remap_editor_id_reference_cb func);
+/* IDRemapper */
+struct IDRemapper;
+typedef enum IDRemapperApplyResult {
+ /** No remapping rules available for the source. */
+ ID_REMAP_RESULT_SOURCE_UNAVAILABLE,
+ /** Source isn't mappable (e.g. NULL). */
+ ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE,
+ /** Source has been remapped to a new pointer. */
+ ID_REMAP_RESULT_SOURCE_REMAPPED,
+ /** Source has been set to NULL. */
+ ID_REMAP_RESULT_SOURCE_UNASSIGNED,
+} IDRemapperApplyResult;
+
+typedef enum IDRemapperApplyOptions {
+ ID_REMAP_APPLY_UPDATE_REFCOUNT = (1 << 0),
+ ID_REMAP_APPLY_ENSURE_REAL = (1 << 1),
+
+ ID_REMAP_APPLY_DEFAULT = 0,
+} IDRemapperApplyOptions;
+
+typedef void (*IDRemapperIterFunction)(struct ID *old_id, struct ID *new_id, void *user_data);
+
+/**
+ * Create a new ID Remapper.
+ *
+ * An ID remapper stores multiple remapping rules.
+ */
+struct IDRemapper *BKE_id_remapper_create(void);
+
+void BKE_id_remapper_clear(struct IDRemapper *id_remapper);
+bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper);
+/** Free the given ID Remapper. */
+void BKE_id_remapper_free(struct IDRemapper *id_remapper);
+/** Add a new remapping. */
+void BKE_id_remapper_add(struct IDRemapper *id_remapper, struct ID *old_id, struct ID *new_id);
+
+/**
+ * Apply a remapping.
+ *
+ * Update the id pointer stored in the given r_id_ptr if a remapping rule exists.
+ */
+IDRemapperApplyResult BKE_id_remapper_apply(const struct IDRemapper *id_remapper,
+ struct ID **r_id_ptr,
+ IDRemapperApplyOptions options);
+bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter);
+void BKE_id_remapper_iter(const struct IDRemapper *id_remapper,
+ IDRemapperIterFunction func,
+ void *user_data);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 4c6eb31db4b..e4f94110eb1 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -182,7 +182,11 @@ typedef struct Main {
ListBase linestyles;
ListBase cachefiles;
ListBase workspaces;
- ListBase hairs;
+ /**
+ * \note The name `hair_curves` is chosen to be different than `curves`,
+ * but they are generic curve data-blocks, not just for hair.
+ */
+ ListBase hair_curves;
ListBase pointclouds;
ListBase volumes;
ListBase simulations;
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 6554a9c72aa..e1c706a82dc 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -119,6 +119,9 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh,
void BKE_mesh_free_data_for_undo(struct Mesh *me);
void BKE_mesh_clear_geometry(struct Mesh *me);
struct Mesh *BKE_mesh_add(struct Main *bmain, const char *name);
+
+void BKE_mesh_free_editmesh(struct Mesh *mesh);
+
/**
* A version of #BKE_mesh_copy_parameters that is intended for evaluated output
* (the modifier stack for example).
diff --git a/source/blender/blenkernel/BKE_mesh_fair.h b/source/blender/blenkernel/BKE_mesh_fair.h
index c4c1af054f0..2884de6d5c1 100644
--- a/source/blender/blenkernel/BKE_mesh_fair.h
+++ b/source/blender/blenkernel/BKE_mesh_fair.h
@@ -12,15 +12,14 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
- * https://github.com/fedackb/mesh-fairing.
*/
#pragma once
/** \file
* \ingroup bke
+ * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
+ * https://github.com/fedackb/mesh-fairing.
*/
#include "BLI_utildefines.h"
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 568cc04eaf1..8fd22c70128 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -245,10 +245,6 @@ typedef struct ModifierTypeInfo {
const struct ModifierEvalContext *ctx,
struct Mesh *mesh);
- struct Hair *(*modifyHair)(struct ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct Hair *hair);
-
/**
* The modifier has to change the geometry set in-place. The geometry set can contain zero or
* more geometry components. This callback can be used by modifiers that don't work on any
@@ -470,6 +466,8 @@ void BKE_modifiers_foreach_tex_link(struct Object *ob, TexWalkFunc walk, void *u
struct ModifierData *BKE_modifiers_findby_type(const struct Object *ob, ModifierType type);
struct ModifierData *BKE_modifiers_findby_name(const struct Object *ob, const char *name);
+struct ModifierData *BKE_modifiers_findby_session_uuid(const struct Object *ob,
+ const SessionUUID *session_uuid);
void BKE_modifiers_clear_errors(struct Object *ob);
/**
* used for buttons, to find out if the 'draw deformed in edit-mode option is there.
@@ -568,7 +566,8 @@ const char *BKE_modifier_path_relbase_from_global(struct Object *ob);
* For a given modifier data, get corresponding original one.
* If the modifier data is already original, return it as-is.
*/
-struct ModifierData *BKE_modifier_get_original(struct ModifierData *md);
+struct ModifierData *BKE_modifier_get_original(const struct Object *object,
+ struct ModifierData *md);
struct ModifierData *BKE_modifier_get_evaluated(struct Depsgraph *depsgraph,
struct Object *object,
struct ModifierData *md);
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 8f408ecd675..359a5662a13 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -274,6 +274,9 @@ typedef struct bNodeType {
char *label,
int maxlen);
+ /** Optional override for node class, used for drawing node header. */
+ int (*ui_class)(const struct bNode *node);
+
/** Called when the node is updated in the editor. */
void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node);
/** Check and update if internal ID data has changed. */
@@ -1178,30 +1181,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_OUTPUT_AOV 707
#define SH_NODE_VECTOR_ROTATE 708
#define SH_NODE_CURVE_FLOAT 709
-
-/* API */
-
-struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree);
-void ntreeShaderEndExecTree(struct bNodeTreeExec *exec);
-/**
- Find an output node of the shader tree.
- *
- * \note it will only return output which is NOT in the group, which isn't how
- * render engines works but it's how the GPU shader compilation works. This we
- * can change in the future and make it a generic function, but for now it stays
- * private here.
- */
-struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target);
-/**
- * This one needs to work on a local tree.
- *
- * TODO: This is *not* part of `blenkernel`, it's defined under "source/blender/nodes/".
- * This declaration should be moved out of BKE.
- */
-void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
- struct GPUMaterial *mat,
- bool *has_surface_output,
- bool *has_volume_output);
+#define SH_NODE_POINT_INFO 710
/** \} */
@@ -1314,6 +1294,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
#define CMP_NODE_POSTERIZE 327
#define CMP_NODE_CONVERT_COLOR_SPACE 328
#define CMP_NODE_SCENE_TIME 329
+#define CMP_NODE_SEPARATE_XYZ 330
+#define CMP_NODE_COMBINE_XYZ 331
/* channel toggles */
#define CMP_CHAN_RGB 1
@@ -1321,12 +1303,13 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
/* filter types */
#define CMP_FILT_SOFT 0
-#define CMP_FILT_SHARP 1
+#define CMP_FILT_SHARP_BOX 1
#define CMP_FILT_LAPLACE 2
#define CMP_FILT_SOBEL 3
#define CMP_FILT_PREWITT 4
#define CMP_FILT_KIRSCH 5
#define CMP_FILT_SHADOW 6
+#define CMP_FILT_SHARP_DIAMOND 7
/* scale node type, in custom1 */
#define CMP_SCALE_RELATIVE 0
@@ -1352,75 +1335,6 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
#define CMP_DEFAULT_SMAA_CONTRAST_LIMIT 0.2f
#define CMP_DEFAULT_SMAA_CORNER_ROUNDING 0.25f
-/* API */
-void ntreeCompositExecTree(struct Scene *scene,
- struct bNodeTree *ntree,
- struct RenderData *rd,
- int rendering,
- int do_previews,
- const struct ColorManagedViewSettings *view_settings,
- const struct ColorManagedDisplaySettings *display_settings,
- const char *view_name);
-
-/**
- * Called from render pipeline, to tag render input and output.
- * need to do all scenes, to prevent errors when you re-render 1 scene.
- */
-void ntreeCompositTagRender(struct Scene *scene);
-/**
- * Update the outputs of the render layer nodes.
- * Since the outputs depend on the render engine, this part is a bit complex:
- * - #ntreeCompositUpdateRLayers is called and loops over all render layer nodes.
- * - Each render layer node calls the update function of the
- * render engine that's used for its scene.
- * - The render engine calls RE_engine_register_pass for each pass.
- * - #RE_engine_register_pass calls #node_cmp_rlayers_register_pass.
- *
- * TODO: This is *not* part of `blenkernel`, it's defined under "source/blender/nodes/".
- * This declaration should be moved out of BKE.
- */
-void ntreeCompositUpdateRLayers(struct bNodeTree *ntree);
-void ntreeCompositClearTags(struct bNodeTree *ntree);
-
-struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree,
- struct bNode *node,
- const char *name,
- struct ImageFormatData *im_format);
-int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node);
-void ntreeCompositOutputFileSetPath(struct bNode *node,
- struct bNodeSocket *sock,
- const char *name);
-void ntreeCompositOutputFileSetLayer(struct bNode *node,
- struct bNodeSocket *sock,
- const char *name);
-/* needed in do_versions */
-void ntreeCompositOutputFileUniquePath(struct ListBase *list,
- struct bNodeSocket *sock,
- const char defname[],
- char delim);
-void ntreeCompositOutputFileUniqueLayer(struct ListBase *list,
- struct bNodeSocket *sock,
- const char defname[],
- char delim);
-
-void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node);
-void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node);
-
-void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node);
-void ntreeCompositCryptomatteSyncFromRemove(bNode *node);
-bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node);
-int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node);
-void ntreeCompositCryptomatteLayerPrefix(const Scene *scene,
- const bNode *node,
- char *r_prefix,
- size_t prefix_len);
-/**
- * Update the runtime layer names with the crypto-matte layer names of the references render layer
- * or image.
- */
-void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node);
-struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node);
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1457,24 +1371,6 @@ struct TexResult;
#define TEX_NODE_PROC 500
#define TEX_NODE_PROC_MAX 600
-/* API */
-void ntreeTexCheckCyclics(struct bNodeTree *ntree);
-
-struct bNodeTreeExec *ntreeTexBeginExecTree(struct bNodeTree *ntree);
-void ntreeTexEndExecTree(struct bNodeTreeExec *exec);
-int ntreeTexExecTree(struct bNodeTree *ntree,
- struct TexResult *target,
- const float co[3],
- float dxt[3],
- float dyt[3],
- int osatex,
- short thread,
- const struct Tex *tex,
- short which_output,
- int cfra,
- int preview,
- struct MTex *mtex);
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1629,6 +1525,11 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ACCUMULATE_FIELD 1146
#define GEO_NODE_INPUT_MESH_EDGE_ANGLE 1147
#define GEO_NODE_FIELD_AT_INDEX 1148
+#define GEO_NODE_CURVE_PRIMITIVE_ARC 1149
+#define GEO_NODE_FLIP_FACES 1150
+#define GEO_NODE_SCALE_ELEMENTS 1151
+#define GEO_NODE_EXTRUDE_MESH 1152
+#define GEO_NODE_MERGE_BY_DISTANCE 1153
/** \} */
diff --git a/source/blender/blenkernel/BKE_node_tree_update.h b/source/blender/blenkernel/BKE_node_tree_update.h
index f77bd83a8ef..443ceafb073 100644
--- a/source/blender/blenkernel/BKE_node_tree_update.h
+++ b/source/blender/blenkernel/BKE_node_tree_update.h
@@ -20,12 +20,12 @@
* \ingroup bke
*/
+struct ID;
+struct Main;
struct bNode;
+struct bNodeLink;
struct bNodeSocket;
struct bNodeTree;
-struct bNodeLink;
-struct Main;
-struct ID;
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index da8dba0c86b..96ed7942067 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -144,18 +144,6 @@ void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_sr
void BKE_object_free_modifiers(struct Object *ob, int flag);
void BKE_object_free_shaderfx(struct Object *ob, int flag);
-/**
- * Proxy rule:
- * - `lib_object->proxy_from` == the one we borrow from, set temporally while object_update.
- * - `local_object->proxy` == pointer to library object, saved in files and read.
- * - `local_object->proxy_group` == pointer to collection dupli-object, saved in files and read.
- */
-void BKE_object_make_proxy(struct Main *bmain,
- struct Object *ob,
- struct Object *target,
- struct Object *cob);
-void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target);
-
bool BKE_object_exists_check(struct Main *bmain, const struct Object *obtest);
/**
* Actual check for internal data, not context or flags.
@@ -444,7 +432,6 @@ void BKE_object_eval_constraints(struct Depsgraph *depsgraph,
struct Object *ob);
void BKE_object_eval_transform_final(struct Depsgraph *depsgraph, struct Object *ob);
-bool BKE_object_eval_proxy_copy(struct Depsgraph *depsgraph, struct Object *object);
void BKE_object_eval_uber_transform(struct Depsgraph *depsgraph, struct Object *ob);
void BKE_object_eval_uber_data(struct Depsgraph *depsgraph,
struct Scene *scene,
@@ -486,12 +473,6 @@ void BKE_object_handle_data_update(struct Depsgraph *depsgraph,
*/
void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
/**
- * Proxy rule:
- * - lib_object->proxy_from == the one we borrow from, only set temporal and cleared here.
- * - local_object->proxy == pointer to library object, saved in files and read.
- *
- * Function below is polluted with proxy exceptions, cleanup will follow!
- *
* The main object update call, for object matrix, constraints, keys and #DispList (modifiers)
* requires flags to be set!
*
@@ -501,8 +482,7 @@ void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene,
void BKE_object_handle_update_ex(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
- struct RigidBodyWorld *rbw,
- bool do_proxy_update);
+ struct RigidBodyWorld *rbw);
void BKE_object_sculpt_data_create(struct Object *ob);
@@ -530,6 +510,9 @@ struct Mesh *BKE_object_get_pre_modified_mesh(const struct Object *object);
*/
struct Mesh *BKE_object_get_original_mesh(const struct Object *object);
+struct Mesh *BKE_object_get_editmesh_eval_final(const struct Object *object);
+struct Mesh *BKE_object_get_editmesh_eval_cage(const struct Object *object);
+
/* Lattice accessors.
* These functions return either the regular lattice, or the edit-mode lattice,
* whichever is currently in use. */
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 89e794cd2fc..8291be9d7e8 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -499,7 +499,6 @@ typedef struct SculptSession {
/* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */
struct MVert *mvert;
- const float (*vert_normals)[3];
struct MPoly *mpoly;
struct MLoop *mloop;
@@ -617,10 +616,6 @@ typedef struct SculptSession {
float init_pivot_rot[4];
float init_pivot_scale[3];
- float prev_pivot_pos[3];
- float prev_pivot_rot[4];
- float prev_pivot_scale[3];
-
union {
struct {
struct SculptVertexPaintGeomMap gmap;
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index 804331a3412..b5e60da540e 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -13,11 +13,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2007 by Janne Karhu.
- * All rights reserved.
- * Adaptive time step
- * Classical SPH
- * Copyright 2011-2012 AutoCRC
+ * Copyright 2007 Janne Karhu. All rights reserved.
+ * 2011-2012 AutoCRC (adaptive time step, Classical SPH).
*/
#pragma once
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 63f6fca2a9d..c85ae04a492 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -38,6 +38,7 @@ struct BlendLibReader;
struct BlendWriter;
struct Header;
struct ID;
+struct IDRemapper;
struct LibraryForeachIDData;
struct ListBase;
struct Menu;
@@ -117,10 +118,7 @@ typedef struct SpaceType {
bContextDataCallback context;
/* Used when we want to replace an ID by another (or NULL). */
- void (*id_remap)(struct ScrArea *area,
- struct SpaceLink *sl,
- struct ID *old_id,
- struct ID *new_id);
+ void (*id_remap)(struct ScrArea *area, struct SpaceLink *sl, const struct IDRemapper *mappings);
int (*space_subtype_get)(struct ScrArea *area);
void (*space_subtype_set)(struct ScrArea *area, int value);
diff --git a/source/blender/blenkernel/BKE_shrinkwrap.h b/source/blender/blenkernel/BKE_shrinkwrap.h
index 510d6773f6d..ea816812344 100644
--- a/source/blender/blenkernel/BKE_shrinkwrap.h
+++ b/source/blender/blenkernel/BKE_shrinkwrap.h
@@ -47,8 +47,8 @@ struct MDeformVert;
struct Mesh;
struct ModifierEvalContext;
struct Object;
-struct ShrinkwrapModifierData;
struct ShrinkwrapGpencilModifierData;
+struct ShrinkwrapModifierData;
struct SpaceTransform;
/* Information about boundary edges in the mesh. */
diff --git a/source/blender/blenkernel/BKE_subdiv_eval.h b/source/blender/blenkernel/BKE_subdiv_eval.h
index 2eb64ae795d..23bcdcce276 100644
--- a/source/blender/blenkernel/BKE_subdiv_eval.h
+++ b/source/blender/blenkernel/BKE_subdiv_eval.h
@@ -30,8 +30,8 @@ extern "C" {
#endif
struct Mesh;
-struct Subdiv;
struct OpenSubdiv_EvaluatorCache;
+struct Subdiv;
typedef enum eSubdivEvaluatorType {
SUBDIV_EVALUATOR_TYPE_CPU,
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index cb32bc29463..efc9cd6e99f 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -119,6 +119,7 @@ set(SRC
intern/crazyspace.c
intern/cryptomatte.cc
intern/curve.cc
+ intern/curves.cc
intern/curve_bevel.c
intern/curve_convert.c
intern/curve_decimate.c
@@ -156,7 +157,6 @@ set(SRC
intern/gpencil_curve.c
intern/gpencil_geom.cc
intern/gpencil_modifier.c
- intern/hair.cc
intern/icons.cc
intern/icons_rasterize.c
intern/idprop.c
@@ -165,6 +165,7 @@ set(SRC
intern/idprop_utils.c
intern/idtype.c
intern/image.c
+ intern/image_partial_update.cc
intern/image_gen.c
intern/image_gpu.cc
intern/image_save.c
@@ -179,7 +180,9 @@ set(SRC
intern/lib_id.c
intern/lib_id_delete.c
intern/lib_id_eval.c
+ intern/lib_id_remapper.cc
intern/lib_override.c
+ intern/lib_override_proxy_conversion.c
intern/lib_query.c
intern/lib_remap.c
intern/library.c
@@ -353,6 +356,7 @@ set(SRC
BKE_cryptomatte.h
BKE_cryptomatte.hh
BKE_curve.h
+ BKE_curves.h
BKE_curve_to_mesh.hh
BKE_curveprofile.h
BKE_customdata.h
@@ -381,7 +385,6 @@ set(SRC
BKE_gpencil_curve.h
BKE_gpencil_geom.h
BKE_gpencil_modifier.h
- BKE_hair.h
BKE_icons.h
BKE_idprop.h
BKE_idprop.hh
@@ -522,7 +525,7 @@ set(LIB
bf_simulation
# For `vfontdata_freetype.c`.
- ${FREETYPE_LIBRARY}
+ ${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES}
)
if(WITH_BINRELOC)
@@ -717,10 +720,6 @@ if(WITH_FFTW3)
add_definitions(-DFFTW3=1)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
@@ -821,8 +820,10 @@ if(WITH_GTESTS)
intern/cryptomatte_test.cc
intern/fcurve_test.cc
intern/idprop_serialize_test.cc
+ intern/image_partial_update_test.cc
intern/lattice_deform_test.cc
intern/layer_test.cc
+ intern/lib_id_remapper_test.cc
intern/lib_id_test.cc
intern/lib_remap_test.cc
intern/tracking_test.cc
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index 13131c24eda..2e779d6fad7 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -127,30 +127,6 @@ static MEdge *dm_getEdgeArray(DerivedMesh *dm)
return medge;
}
-static MFace *dm_getTessFaceArray(DerivedMesh *dm)
-{
- MFace *mface = (MFace *)CustomData_get_layer(&dm->faceData, CD_MFACE);
-
- if (!mface) {
- int numTessFaces = dm->getNumTessFaces(dm);
-
- if (!numTessFaces) {
- /* Do not add layer if there's no elements in it, this leads to issues later when
- * this layer is needed with non-zero size, but currently CD stuff does not check
- * for requested layer size on creation and just returns layer which was previously
- * added (sergey) */
- return nullptr;
- }
-
- mface = (MFace *)CustomData_add_layer(
- &dm->faceData, CD_MFACE, CD_CALLOC, nullptr, numTessFaces);
- CustomData_set_layer_flag(&dm->faceData, CD_MFACE, CD_FLAG_TEMPORARY);
- dm->copyTessFaceArray(dm, mface);
- }
-
- return mface;
-}
-
static MLoop *dm_getLoopArray(DerivedMesh *dm)
{
MLoop *mloop = (MLoop *)CustomData_get_layer(&dm->loopData, CD_MLOOP);
@@ -203,18 +179,6 @@ static MEdge *dm_dupEdgeArray(DerivedMesh *dm)
return tmp;
}
-static MFace *dm_dupFaceArray(DerivedMesh *dm)
-{
- MFace *tmp = (MFace *)MEM_malloc_arrayN(
- dm->getNumTessFaces(dm), sizeof(*tmp), "dm_dupFaceArray tmp");
-
- if (tmp) {
- dm->copyTessFaceArray(dm, tmp);
- }
-
- return tmp;
-}
-
static MLoop *dm_dupLoopArray(DerivedMesh *dm)
{
MLoop *tmp = (MLoop *)MEM_malloc_arrayN(
@@ -270,42 +234,15 @@ static const MLoopTri *dm_getLoopTriArray(DerivedMesh *dm)
return looptri;
}
-static CustomData *dm_getVertCData(DerivedMesh *dm)
-{
- return &dm->vertData;
-}
-
-static CustomData *dm_getEdgeCData(DerivedMesh *dm)
-{
- return &dm->edgeData;
-}
-
-static CustomData *dm_getTessFaceCData(DerivedMesh *dm)
-{
- return &dm->faceData;
-}
-
-static CustomData *dm_getLoopCData(DerivedMesh *dm)
-{
- return &dm->loopData;
-}
-
-static CustomData *dm_getPolyCData(DerivedMesh *dm)
-{
- return &dm->polyData;
-}
-
void DM_init_funcs(DerivedMesh *dm)
{
/* default function implementations */
dm->getVertArray = dm_getVertArray;
dm->getEdgeArray = dm_getEdgeArray;
- dm->getTessFaceArray = dm_getTessFaceArray;
dm->getLoopArray = dm_getLoopArray;
dm->getPolyArray = dm_getPolyArray;
dm->dupVertArray = dm_dupVertArray;
dm->dupEdgeArray = dm_dupEdgeArray;
- dm->dupTessFaceArray = dm_dupFaceArray;
dm->dupLoopArray = dm_dupLoopArray;
dm->dupPolyArray = dm_dupPolyArray;
@@ -314,19 +251,8 @@ void DM_init_funcs(DerivedMesh *dm)
/* subtypes handle getting actual data */
dm->getNumLoopTri = dm_getNumLoopTri;
- dm->getVertDataLayout = dm_getVertCData;
- dm->getEdgeDataLayout = dm_getEdgeCData;
- dm->getTessFaceDataLayout = dm_getTessFaceCData;
- dm->getLoopDataLayout = dm_getLoopCData;
- dm->getPolyDataLayout = dm_getPolyCData;
-
- dm->getVertData = DM_get_vert_data;
- dm->getEdgeData = DM_get_edge_data;
- dm->getTessFaceData = DM_get_tessface_data;
- dm->getPolyData = DM_get_poly_data;
dm->getVertDataArray = DM_get_vert_data_layer;
dm->getEdgeDataArray = DM_get_edge_data_layer;
- dm->getTessFaceDataArray = DM_get_tessface_data_layer;
dm->getPolyDataArray = DM_get_poly_data_layer;
dm->getLoopDataArray = DM_get_loop_data_layer;
}
@@ -349,7 +275,6 @@ void DM_init(DerivedMesh *dm,
DM_init_funcs(dm);
dm->needsFree = 1;
- dm->dirty = (DMDirtyFlag)0;
/* Don't use #CustomData_reset because we don't want to touch custom-data. */
copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1);
@@ -359,16 +284,16 @@ void DM_init(DerivedMesh *dm,
copy_vn_i(dm->polyData.typemap, CD_NUMTYPES, -1);
}
-void DM_from_template_ex(DerivedMesh *dm,
- DerivedMesh *source,
- DerivedMeshType type,
- int numVerts,
- int numEdges,
- int numTessFaces,
- int numLoops,
- int numPolys,
- const CustomData_MeshMasks *mask)
+void DM_from_template(DerivedMesh *dm,
+ DerivedMesh *source,
+ DerivedMeshType type,
+ int numVerts,
+ int numEdges,
+ int numTessFaces,
+ int numLoops,
+ int numPolys)
{
+ const CustomData_MeshMasks *mask = &CD_MASK_DERIVEDMESH;
CustomData_copy(&source->vertData, &dm->vertData, mask->vmask, CD_CALLOC, numVerts);
CustomData_copy(&source->edgeData, &dm->edgeData, mask->emask, CD_CALLOC, numEdges);
CustomData_copy(&source->faceData, &dm->faceData, mask->fmask, CD_CALLOC, numTessFaces);
@@ -387,26 +312,6 @@ void DM_from_template_ex(DerivedMesh *dm,
DM_init_funcs(dm);
dm->needsFree = 1;
- dm->dirty = (DMDirtyFlag)0;
-}
-void DM_from_template(DerivedMesh *dm,
- DerivedMesh *source,
- DerivedMeshType type,
- int numVerts,
- int numEdges,
- int numTessFaces,
- int numLoops,
- int numPolys)
-{
- DM_from_template_ex(dm,
- source,
- type,
- numVerts,
- numEdges,
- numTessFaces,
- numLoops,
- numPolys,
- &CD_MASK_DERIVEDMESH);
}
bool DM_release(DerivedMesh *dm)
@@ -464,14 +369,6 @@ void DM_DupPolys(DerivedMesh *source, DerivedMesh *target)
}
}
-void DM_ensure_normals(DerivedMesh *dm)
-{
- if (dm->dirty & DM_DIRTY_NORMALS) {
- dm->calcNormals(dm);
- }
- BLI_assert((dm->dirty & DM_DIRTY_NORMALS) == 0);
-}
-
void DM_ensure_looptri_data(DerivedMesh *dm)
{
const unsigned int totpoly = dm->numPolyData;
@@ -524,7 +421,7 @@ void DM_set_only_copy(DerivedMesh *dm, const CustomData_MeshMasks *mask)
* see replies to r50969, Campbell */
#if 0
CustomData_set_only_copy(&dm->loopData, mask->lmask);
- CustomData_set_only_copy(&dm->polyData, mask->pmask);
+ Custom(&dm->polyData, mask->pmask);
#endif
}
@@ -552,45 +449,11 @@ void DM_add_edge_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *
CustomData_add_layer(&dm->edgeData, type, alloctype, layer, dm->numEdgeData);
}
-void DM_add_tessface_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer)
-{
- CustomData_add_layer(&dm->faceData, type, alloctype, layer, dm->numTessFaceData);
-}
-
-void DM_add_loop_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer)
-{
- CustomData_add_layer(&dm->loopData, type, alloctype, layer, dm->numLoopData);
-}
-
void DM_add_poly_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer)
{
CustomData_add_layer(&dm->polyData, type, alloctype, layer, dm->numPolyData);
}
-void *DM_get_vert_data(DerivedMesh *dm, int index, int type)
-{
- BLI_assert(index >= 0 && index < dm->getNumVerts(dm));
- return CustomData_get(&dm->vertData, index, type);
-}
-
-void *DM_get_edge_data(DerivedMesh *dm, int index, int type)
-{
- BLI_assert(index >= 0 && index < dm->getNumEdges(dm));
- return CustomData_get(&dm->edgeData, index, type);
-}
-
-void *DM_get_tessface_data(DerivedMesh *dm, int index, int type)
-{
- BLI_assert(index >= 0 && index < dm->getNumTessFaces(dm));
- return CustomData_get(&dm->faceData, index, type);
-}
-
-void *DM_get_poly_data(DerivedMesh *dm, int index, int type)
-{
- BLI_assert(index >= 0 && index < dm->getNumPolys(dm));
- return CustomData_get(&dm->polyData, index, type);
-}
-
void *DM_get_vert_data_layer(DerivedMesh *dm, int type)
{
if (type == CD_MVERT) {
@@ -609,15 +472,6 @@ void *DM_get_edge_data_layer(DerivedMesh *dm, int type)
return CustomData_get_layer(&dm->edgeData, type);
}
-void *DM_get_tessface_data_layer(DerivedMesh *dm, int type)
-{
- if (type == CD_MFACE) {
- return dm->getTessFaceArray(dm);
- }
-
- return CustomData_get_layer(&dm->faceData, type);
-}
-
void *DM_get_poly_data_layer(DerivedMesh *dm, int type)
{
return CustomData_get_layer(&dm->polyData, type);
@@ -1731,13 +1585,6 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
BKE_id_free(nullptr, mesh_orco);
}
- /* Ensure normals calculation below is correct (normal settings have transferred properly).
- * However, nodes modifiers might create meshes from scratch or transfer meshes from other
- * objects with different settings, and in general it doesn't make sense to guarantee that
- * the settings are the same as the original mesh. If necessary, this could become a modifier
- * type flag. */
- BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh);
-
/* Compute normals. */
editbmesh_calc_modifier_final_normals(mesh_final, &final_datamask);
if (mesh_cage && (mesh_cage != mesh_final)) {
@@ -1769,17 +1616,6 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
const CustomData_MeshMasks *dataMask,
const bool need_mapping)
{
- BLI_assert(ob->type == OB_MESH);
-
- /* Evaluated meshes aren't supposed to be created on original instances. If you do,
- * they aren't cleaned up properly on mode switch, causing crashes, e.g T58150. */
- BLI_assert(ob->id.tag & LIB_TAG_COPIED_ON_WRITE);
-
- BKE_object_free_derived_caches(ob);
- if (DEG_is_active(depsgraph)) {
- BKE_sculpt_update_object_before_eval(ob);
- }
-
#if 0 /* XXX This is already taken care of in mesh_calc_modifiers()... */
if (need_mapping) {
/* Also add the flag so that it is recorded in lastDataMask. */
@@ -1846,15 +1682,7 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph,
BMEditMesh *em,
CustomData_MeshMasks *dataMask)
{
- BLI_assert(obedit->id.tag & LIB_TAG_COPIED_ON_WRITE);
-
- BKE_object_free_derived_caches(obedit);
- if (DEG_is_active(depsgraph)) {
- BKE_sculpt_update_object_before_eval(obedit);
- }
-
- BKE_editmesh_free_derived_caches(em);
-
+ Mesh *mesh = static_cast<Mesh *>(obedit->data);
Mesh *me_cage;
Mesh *me_final;
GeometrySet *non_mesh_components;
@@ -1862,13 +1690,33 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph,
editbmesh_calc_modifiers(
depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final, &non_mesh_components);
- em->mesh_eval_final = me_final;
- em->mesh_eval_cage = me_cage;
+ /* The modifier stack result is expected to share edit mesh pointer with the input.
+ * This is similar `mesh_calc_finalize()`. */
+ BKE_mesh_free_editmesh(me_final);
+ BKE_mesh_free_editmesh(me_cage);
+ me_final->edit_mesh = me_cage->edit_mesh = em;
+
+ /* Object has edit_mesh but is not in edit mode (object shares mesh datablock with another object
+ * with is in edit mode).
+ * Convert edit mesh to mesh until the draw manager can draw mesh wrapper which is not in the
+ * edit mode. */
+ if (!(obedit->mode & OB_MODE_EDIT)) {
+ BKE_mesh_wrapper_ensure_mdata(me_final);
+ if (me_final != me_cage) {
+ BKE_mesh_wrapper_ensure_mdata(me_cage);
+ }
+ }
+
+ const bool is_mesh_eval_owned = (me_final != mesh->runtime.mesh_eval);
+ BKE_object_eval_assign_data(obedit, &me_final->id, is_mesh_eval_owned);
+
+ obedit->runtime.editmesh_eval_cage = me_cage;
+
obedit->runtime.geometry_set_eval = non_mesh_components;
- BKE_object_boundbox_calc_from_mesh(obedit, em->mesh_eval_final);
+ BKE_object_boundbox_calc_from_mesh(obedit, me_final);
- em->lastDataMask = *dataMask;
+ obedit->runtime.last_data_mask = *dataMask;
}
static void object_get_datamask(const Depsgraph *depsgraph,
@@ -1924,9 +1772,25 @@ static void object_get_datamask(const Depsgraph *depsgraph,
void makeDerivedMesh(struct Depsgraph *depsgraph,
Scene *scene,
Object *ob,
- BMEditMesh *em,
const CustomData_MeshMasks *dataMask)
{
+ BLI_assert(ob->type == OB_MESH);
+
+ /* Evaluated meshes aren't supposed to be created on original instances. If you do,
+ * they aren't cleaned up properly on mode switch, causing crashes, e.g T58150. */
+ BLI_assert(ob->id.tag & LIB_TAG_COPIED_ON_WRITE);
+
+ BKE_object_free_derived_caches(ob);
+ if (DEG_is_active(depsgraph)) {
+ BKE_sculpt_update_object_before_eval(ob);
+ }
+
+ /* NOTE: Access the `edit_mesh` after freeing the derived caches, so that `ob->data` is restored
+ * to the pre-evaluated state. This is because the evaluated state is not necessarily sharing the
+ * `edit_mesh` pointer with the input. For example, if the object is first evaluated in the
+ * object mode, and then user in another scene moves object to edit mode. */
+ BMEditMesh *em = ((Mesh *)ob->data)->edit_mesh;
+
bool need_mapping;
CustomData_MeshMasks cddata_masks = *dataMask;
object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping);
@@ -1965,8 +1829,9 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph,
!CustomData_MeshMasks_are_matching(&(ob->runtime.last_data_mask), &cddata_masks) ||
(need_mapping && !ob->runtime.last_need_mapping)) {
CustomData_MeshMasks_update(&cddata_masks, &ob->runtime.last_data_mask);
- mesh_build_data(
- depsgraph, scene, ob, &cddata_masks, need_mapping || ob->runtime.last_need_mapping);
+
+ makeDerivedMesh(depsgraph, scene, ob, dataMask);
+
mesh_eval = BKE_object_get_evaluated_mesh(ob);
}
@@ -1981,6 +1846,15 @@ Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph,
Object *ob,
const CustomData_MeshMasks *dataMask)
{
+ BMEditMesh *em = ((Mesh *)ob->data)->edit_mesh;
+ if (em != nullptr) {
+ /* There is no such a concept as deformed mesh in edit mode.
+ * Explicitly disallow this request so that the evaluated result is not modified with evaluated
+ * result from the wrong mode. */
+ BLI_assert_msg(0, "Request of derformed mesh of object which is in edit mode");
+ return nullptr;
+ }
+
/* This function isn't thread-safe and can't be used during evaluation. */
BLI_assert(DEG_is_evaluating(depsgraph) == false);
@@ -2055,12 +1929,12 @@ Mesh *editbmesh_get_eval_cage(struct Depsgraph *depsgraph,
*/
object_get_datamask(depsgraph, obedit, &cddata_masks, nullptr);
- if (!em->mesh_eval_cage ||
- !CustomData_MeshMasks_are_matching(&(em->lastDataMask), &cddata_masks)) {
+ if (!obedit->runtime.editmesh_eval_cage ||
+ !CustomData_MeshMasks_are_matching(&(obedit->runtime.last_data_mask), &cddata_masks)) {
editbmesh_build_data(depsgraph, scene, obedit, em, &cddata_masks);
}
- return em->mesh_eval_cage;
+ return obedit->runtime.editmesh_eval_cage;
}
Mesh *editbmesh_get_eval_cage_from_orig(struct Depsgraph *depsgraph,
@@ -2117,32 +1991,6 @@ void mesh_get_mapped_verts_coords(Mesh *me_eval, float (*r_cos)[3], const int to
}
}
-void DM_calc_loop_tangents(DerivedMesh *dm,
- bool calc_active_tangent,
- const char (*tangent_names)[MAX_NAME],
- int tangent_names_len)
-{
- BKE_mesh_calc_loop_tangent_ex(
- dm->getVertArray(dm),
- dm->getPolyArray(dm),
- dm->getNumPolys(dm),
- dm->getLoopArray(dm),
- dm->getLoopTriArray(dm),
- dm->getNumLoopTri(dm),
- &dm->loopData,
- calc_active_tangent,
- tangent_names,
- tangent_names_len,
- (const float(*)[3])CustomData_get_layer(&dm->vertData, CD_NORMAL),
- (const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL),
- (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
- (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */
- /* result */
- &dm->loopData,
- dm->getNumLoops(dm),
- &dm->tangent_mask);
-}
-
static void mesh_init_origspace(Mesh *mesh)
{
const float default_osf[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index fde42304185..8426f6f4676 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -1956,30 +1956,15 @@ void BKE_pose_blend_read_lib(BlendLibReader *reader, Object *ob, bPose *pose)
return;
}
- /* always rebuild to match proxy or lib changes, but on Undo */
+ /* Always rebuild to match library changes, except on Undo. */
bool rebuild = false;
if (!BLO_read_lib_is_undo(reader)) {
- if (ob->proxy || ob->id.lib != arm->id.lib) {
+ if (ob->id.lib != arm->id.lib) {
rebuild = true;
}
}
- if (ob->proxy) {
- /* sync proxy layer */
- if (pose->proxy_layer) {
- arm->layer = pose->proxy_layer;
- }
-
- /* sync proxy active bone */
- if (pose->proxy_act_bone[0]) {
- Bone *bone = BKE_armature_find_bone_name(arm, pose->proxy_act_bone);
- if (bone) {
- arm->act_bone = bone;
- }
- }
- }
-
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
BKE_constraint_blend_read_lib(reader, (ID *)ob, &pchan->constraints);
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 42b72a7cd66..1c0b465d202 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -1284,8 +1284,8 @@ void BKE_animdata_main_cb(Main *bmain, ID_AnimData_Edit_Callback func, void *use
/* cache files */
ANIMDATA_IDS_CB(bmain->cachefiles.first);
- /* hairs */
- ANIMDATA_IDS_CB(bmain->hairs.first);
+ /* Hair Curves. */
+ ANIMDATA_IDS_CB(bmain->hair_curves.first);
/* pointclouds */
ANIMDATA_IDS_CB(bmain->pointclouds.first);
@@ -1413,8 +1413,8 @@ void BKE_animdata_fix_paths_rename_all_ex(Main *bmain,
/* cache files */
RENAMEFIX_ANIM_IDS(bmain->cachefiles.first);
- /* hairs */
- RENAMEFIX_ANIM_IDS(bmain->hairs.first);
+ /* Hair Curves. */
+ RENAMEFIX_ANIM_IDS(bmain->hair_curves.first);
/* pointclouds */
RENAMEFIX_ANIM_IDS(bmain->pointclouds.first);
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index b5ea68aaadc..c45856adbda 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -3382,8 +3382,8 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, float
/* cache files */
EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM);
- /* hairs */
- EVAL_ANIM_IDS(main->hairs.first, ADT_RECALC_ANIM);
+ /* Hair Curves. */
+ EVAL_ANIM_IDS(main->hair_curves.first, ADT_RECALC_ANIM);
/* pointclouds */
EVAL_ANIM_IDS(main->pointclouds.first, ADT_RECALC_ANIM);
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 5704ef6e42f..1a9f0a9130d 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -69,8 +69,6 @@
#include "CLG_log.h"
-static CLG_LogRef LOG = {"bke.armature"};
-
/* -------------------------------------------------------------------- */
/** \name Prototypes
* \{ */
@@ -2296,161 +2294,6 @@ void BKE_armature_where_is(bArmature *arm)
/** \name Pose Rebuild
* \{ */
-/* if bone layer is protected, copy the data from from->pose
- * when used with linked libraries this copies from the linked pose into the local pose */
-static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
-{
- bPose *pose = ob->pose, *frompose = from->pose;
- bPoseChannel *pchan, *pchanp;
- bConstraint *con;
- int error = 0;
-
- if (frompose == NULL) {
- return;
- }
-
- /* in some cases when rigs change, we can't synchronize
- * to avoid crashing check for possible errors here */
- for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->bone->layer & layer_protected) {
- if (BKE_pose_channel_find_name(frompose, pchan->name) == NULL) {
- CLOG_ERROR(&LOG,
- "failed to sync proxy armature because '%s' is missing pose channel '%s'",
- from->id.name,
- pchan->name);
- error = 1;
- }
- }
- }
-
- if (error) {
- return;
- }
-
- /* clear all transformation values from library */
- BKE_pose_rest(frompose, false);
-
- /* copy over all of the proxy's bone groups */
- /* TODO: for later
- * - implement 'local' bone groups as for constraints
- * NOTE: this isn't trivial, as bones reference groups by index not by pointer,
- * so syncing things correctly needs careful attention */
- BLI_freelistN(&pose->agroups);
- BLI_duplicatelist(&pose->agroups, &frompose->agroups);
- pose->active_group = frompose->active_group;
-
- for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
- pchanp = BKE_pose_channel_find_name(frompose, pchan->name);
-
- if (UNLIKELY(pchanp == NULL)) {
- /* happens for proxies that become invalid because of a missing link
- * for regular cases it shouldn't happen at all */
- }
- else if (pchan->bone->layer & layer_protected) {
- ListBase proxylocal_constraints = {NULL, NULL};
- bPoseChannel pchanw;
-
- /* copy posechannel to temp, but restore important pointers */
- pchanw = *pchanp;
- pchanw.bone = pchan->bone;
- pchanw.prev = pchan->prev;
- pchanw.next = pchan->next;
- pchanw.parent = pchan->parent;
- pchanw.child = pchan->child;
- pchanw.custom_tx = pchan->custom_tx;
- pchanw.bbone_prev = pchan->bbone_prev;
- pchanw.bbone_next = pchan->bbone_next;
-
- pchanw.mpath = pchan->mpath;
- pchan->mpath = NULL;
-
- /* Reset runtime data, we don't want to share that with the proxy. */
- BKE_pose_channel_runtime_reset_on_copy(&pchanw.runtime);
-
- /* this is freed so copy a copy, else undo crashes */
- if (pchanw.prop) {
- pchanw.prop = IDP_CopyProperty(pchanw.prop);
-
- /* use the values from the existing props */
- if (pchan->prop) {
- IDP_SyncGroupValues(pchanw.prop, pchan->prop);
- }
- }
-
- /* Constraints - proxy constraints are flushed... local ones are added after
- * 1: extract constraints not from proxy (CONSTRAINT_PROXY_LOCAL) from pchan's constraints.
- * 2: copy proxy-pchan's constraints on-to new.
- * 3: add extracted local constraints back on top.
- *
- * Note for BKE_constraints_copy:
- * When copying constraints, disable 'do_extern' otherwise
- * we get the libs direct linked in this blend.
- */
- BKE_constraints_proxylocal_extract(&proxylocal_constraints, &pchan->constraints);
- BKE_constraints_copy(&pchanw.constraints, &pchanp->constraints, false);
- BLI_movelisttolist(&pchanw.constraints, &proxylocal_constraints);
-
- /* constraints - set target ob pointer to own object */
- for (con = pchanw.constraints.first; con; con = con->next) {
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
- ListBase targets = {NULL, NULL};
- bConstraintTarget *ct;
-
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(con, &targets);
-
- for (ct = targets.first; ct; ct = ct->next) {
- if (ct->tar == from) {
- ct->tar = ob;
- }
- }
-
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(con, &targets, 0);
- }
- }
- }
-
- /* free stuff from current channel */
- BKE_pose_channel_free(pchan);
-
- /* copy data in temp back over to the cleaned-out (but still allocated) original channel */
- *pchan = pchanw;
- if (pchan->custom) {
- id_us_plus(&pchan->custom->id);
- }
- }
- else {
- /* always copy custom shape */
- pchan->custom = pchanp->custom;
- if (pchan->custom) {
- id_us_plus(&pchan->custom->id);
- }
- if (pchanp->custom_tx) {
- pchan->custom_tx = BKE_pose_channel_find_name(pose, pchanp->custom_tx->name);
- }
-
- /* ID-Property Syncing */
- {
- IDProperty *prop_orig = pchan->prop;
- if (pchanp->prop) {
- pchan->prop = IDP_CopyProperty(pchanp->prop);
- if (prop_orig) {
- /* copy existing values across when types match */
- IDP_SyncGroupValues(pchan->prop, prop_orig);
- }
- }
- else {
- pchan->prop = NULL;
- }
- if (prop_orig) {
- IDP_FreeProperty(prop_orig);
- }
- }
- }
- }
-}
-
/**
* \param r_last_visited_bone_p: The last bone handled by the last call to this function.
*/
@@ -2579,16 +2422,6 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
// printf("rebuild pose %s, %d bones\n", ob->id.name, counter);
- /* synchronize protected layers with proxy */
- /* HACK! To preserve 2.7x behavior that you always can pose even locked bones,
- * do not do any restoration if this is a COW temp copy! */
- /* Switched back to just NO_MAIN tag, for some reasons (c)
- * using COW tag was working this morning, but not anymore... */
- if (ob->proxy != NULL && (ob->id.tag & LIB_TAG_NO_MAIN) == 0) {
- BKE_object_copy_proxy_drivers(ob, ob->proxy);
- pose_proxy_sync(ob, ob->proxy, arm->layer_protected);
- }
-
BKE_pose_update_constraint_flags(pose); /* for IK detection for example */
pose->flag &= ~POSE_RECALC;
@@ -2846,6 +2679,35 @@ BoundBox *BKE_armature_boundbox_get(Object *ob)
return ob->runtime.bb;
}
+void BKE_pchan_minmax(const Object *ob, const bPoseChannel *pchan, float r_min[3], float r_max[3])
+{
+ const bArmature *arm = ob->data;
+ const bPoseChannel *pchan_tx = (pchan->custom && pchan->custom_tx) ? pchan->custom_tx : pchan;
+ const BoundBox *bb_custom = ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) ?
+ BKE_object_boundbox_get(pchan->custom) :
+ NULL;
+ if (bb_custom) {
+ float mat[4][4], smat[4][4], rmat[4][4], tmp[4][4];
+ scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan));
+ rescale_m4(smat, pchan->custom_scale_xyz);
+ eulO_to_mat4(rmat, pchan->custom_rotation_euler, ROT_MODE_XYZ);
+ copy_m4_m4(tmp, pchan_tx->pose_mat);
+ translate_m4(tmp,
+ pchan->custom_translation[0],
+ pchan->custom_translation[1],
+ pchan->custom_translation[2]);
+ mul_m4_series(mat, ob->obmat, tmp, rmat, smat);
+ BKE_boundbox_minmax(bb_custom, mat, r_min, r_max);
+ }
+ else {
+ float vec[3];
+ mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_head);
+ minmax_v3v3_v3(r_min, r_max, vec);
+ mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_tail);
+ minmax_v3v3_v3(r_min, r_max, vec);
+ }
+}
+
bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden, bool use_select)
{
bool changed = false;
@@ -2859,31 +2721,8 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden
* (editarmature.c:2592)... Skip in this case too! */
if (pchan->bone && (!((use_hidden == false) && (PBONE_VISIBLE(arm, pchan->bone) == false)) &&
!((use_select == true) && ((pchan->bone->flag & BONE_SELECTED) == 0)))) {
- bPoseChannel *pchan_tx = (pchan->custom && pchan->custom_tx) ? pchan->custom_tx : pchan;
- BoundBox *bb_custom = ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) ?
- BKE_object_boundbox_get(pchan->custom) :
- NULL;
- if (bb_custom) {
- float mat[4][4], smat[4][4], rmat[4][4], tmp[4][4];
- scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan));
- rescale_m4(smat, pchan->custom_scale_xyz);
- eulO_to_mat4(rmat, pchan->custom_rotation_euler, ROT_MODE_XYZ);
- copy_m4_m4(tmp, pchan_tx->pose_mat);
- translate_m4(tmp,
- pchan->custom_translation[0],
- pchan->custom_translation[1],
- pchan->custom_translation[2]);
- mul_m4_series(mat, ob->obmat, tmp, rmat, smat);
- BKE_boundbox_minmax(bb_custom, mat, r_min, r_max);
- }
- else {
- float vec[3];
- mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_head);
- minmax_v3v3_v3(r_min, r_max, vec);
- mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_tail);
- minmax_v3v3_v3(r_min, r_max, vec);
- }
+ BKE_pchan_minmax(ob, pchan, r_min, r_max);
changed = true;
}
}
diff --git a/source/blender/blenkernel/intern/armature_pose.cc b/source/blender/blenkernel/intern/armature_pose.cc
index a62a32d9633..de26b997e76 100644
--- a/source/blender/blenkernel/intern/armature_pose.cc
+++ b/source/blender/blenkernel/intern/armature_pose.cc
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2015 Blender Foundation.
* All rights reserved.
- *
- * Defines and code for core node types
*/
/** \file
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 05c318663e9..f19065f039b 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2015 Blender Foundation.
* All rights reserved.
- *
- * Defines and code for core node types
*/
/** \file
@@ -850,10 +848,6 @@ void BKE_pose_eval_init(struct Depsgraph *depsgraph, Scene *UNUSED(scene), Objec
}
BLI_assert(pose->chan_array != NULL || BLI_listbase_is_empty(&pose->chanbase));
-
- if (object->proxy != NULL) {
- object->proxy->proxy_from = object;
- }
}
void BKE_pose_eval_init_ik(struct Depsgraph *depsgraph, Scene *scene, Object *object)
@@ -1070,57 +1064,3 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, Scene *scene, Object *ob
BIK_release_tree(scene, object, ctime);
pose_eval_cleanup_common(object);
}
-
-void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, Object *object)
-{
- BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
- DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
-
- BLI_assert(object->pose->chan_array != NULL || BLI_listbase_is_empty(&object->pose->chanbase));
-}
-
-void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, Object *object)
-{
- BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
- DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
-}
-
-void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, Object *object)
-{
- BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
- DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
- pose_eval_cleanup_common(object);
-}
-
-void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph, Object *object, int pchan_index)
-{
- const bArmature *armature = (bArmature *)object->data;
- if (armature->edbo != NULL) {
- return;
- }
- BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
- bPoseChannel *pchan = pose_pchan_get_indexed(object, pchan_index);
- BLI_assert(pchan != NULL);
- DEG_debug_print_eval_subdata(
- depsgraph, __func__, object->id.name, object, "pchan", pchan->name, pchan);
- /* TODO(sergey): Use indexed lookup, once it's guaranteed to be kept
- * around for the time while proxies are evaluating.
- */
-#if 0
- bPoseChannel *pchan_from = pose_pchan_get_indexed(object->proxy_from, pchan_index);
-#else
- bPoseChannel *pchan_from = BKE_pose_channel_find_name(object->proxy_from->pose, pchan->name);
-#endif
- if (pchan_from == NULL) {
- printf(
- "WARNING: Could not find bone %s in linked ID anymore... "
- "You should delete and re-generate your proxy.\n",
- pchan->name);
- return;
- }
- BKE_pose_copy_pchan_result(pchan, pchan_from);
- copy_dq_dq(&pchan->runtime.deform_dual_quat, &pchan_from->runtime.deform_dual_quat);
- BKE_pchan_bbone_segments_cache_copy(pchan, pchan_from);
-
- pose_channel_flush_to_orig_if_needed(depsgraph, object, pchan);
-}
diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc
index eee1f6287c3..06dd623ff28 100644
--- a/source/blender/blenkernel/intern/asset_catalog.cc
+++ b/source/blender/blenkernel/intern/asset_catalog.cc
@@ -520,7 +520,7 @@ CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing(
"A non-empty .blend file path is required to be able to determine where the "
"catalog definition file should be put");
- /* Ask the asset library API for an appropriate location. */
+ /* Ask the asset library API for an appropriate location. */
char suitable_root_path[PATH_MAX];
const bool asset_lib_root_found = BKE_asset_library_find_suitable_root_path_from_path(
blend_file_path.c_str(), suitable_root_path);
diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c
index 2e563e1a30f..4ee5abc1816 100644
--- a/source/blender/blenkernel/intern/attribute.c
+++ b/source/blender/blenkernel/intern/attribute.c
@@ -15,13 +15,12 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- *
- * Implementation of generic geometry attributes management. This is built
- * on top of CustomData, which manages individual domains.
*/
/** \file
* \ingroup bke
+ * Implementation of generic geometry attributes management. This is built
+ * on top of CustomData, which manages individual domains.
*/
#include <string.h>
@@ -29,8 +28,8 @@
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
+#include "DNA_curves_types.h"
#include "DNA_customdata_types.h"
-#include "DNA_hair_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
@@ -38,9 +37,9 @@
#include "BLI_string_utf8.h"
#include "BKE_attribute.h"
+#include "BKE_curves.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
-#include "BKE_hair.h"
#include "BKE_pointcloud.h"
#include "BKE_report.h"
@@ -88,12 +87,12 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
}
break;
}
- case ID_HA: {
- Hair *hair = (Hair *)id;
- info[ATTR_DOMAIN_POINT].customdata = &hair->pdata;
- info[ATTR_DOMAIN_POINT].length = hair->totpoint;
- info[ATTR_DOMAIN_CURVE].customdata = &hair->cdata;
- info[ATTR_DOMAIN_CURVE].length = hair->totcurve;
+ case ID_CV: {
+ Curves *curves = (Curves *)id;
+ info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data;
+ info[ATTR_DOMAIN_POINT].length = curves->geometry.point_size;
+ info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data;
+ info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_size;
break;
}
default:
@@ -313,8 +312,8 @@ bool BKE_id_attribute_required(ID *id, CustomDataLayer *layer)
case ID_PT: {
return BKE_pointcloud_customdata_required((PointCloud *)id, layer);
}
- case ID_HA: {
- return BKE_hair_customdata_required((Hair *)id, layer);
+ case ID_CV: {
+ return BKE_curves_customdata_required((Curves *)id, layer);
}
default:
return false;
@@ -384,8 +383,8 @@ int *BKE_id_attributes_active_index_p(ID *id)
case ID_ME: {
return &((Mesh *)id)->attributes_active_index;
}
- case ID_HA: {
- return &((Hair *)id)->attributes_active_index;
+ case ID_CV: {
+ return &((Curves *)id)->attributes_active_index;
}
default:
return NULL;
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index cc43a3e26a8..68ab11a013b 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -83,6 +83,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty
return &CPPType::get<ColorGeometry4f>();
case CD_PROP_BOOL:
return &CPPType::get<bool>();
+ case CD_PROP_INT8:
+ return &CPPType::get<int8_t>();
default:
return nullptr;
}
@@ -109,6 +111,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
if (type.is<bool>()) {
return CD_PROP_BOOL;
}
+ if (type.is<int8_t>()) {
+ return CD_PROP_INT8;
+ }
return static_cast<CustomDataType>(-1);
}
@@ -117,16 +122,18 @@ static int attribute_data_type_complexity(const CustomDataType data_type)
switch (data_type) {
case CD_PROP_BOOL:
return 0;
- case CD_PROP_INT32:
+ case CD_PROP_INT8:
return 1;
- case CD_PROP_FLOAT:
+ case CD_PROP_INT32:
return 2;
- case CD_PROP_FLOAT2:
+ case CD_PROP_FLOAT:
return 3;
- case CD_PROP_FLOAT3:
+ case CD_PROP_FLOAT2:
return 4;
- case CD_PROP_COLOR:
+ case CD_PROP_FLOAT3:
return 5;
+ case CD_PROP_COLOR:
+ return 6;
#if 0 /* These attribute types are not supported yet. */
case CD_MLOOPCOL:
return 3;
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 2cd128081eb..5341266e182 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -140,7 +140,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
- CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL |
+ CD_MASK_PROP_INT8;
const AttributeDomain domain_;
const CustomDataAccessInfo custom_data_access_;
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 6ae19c8036f..07fd859765a 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -78,6 +78,23 @@
/** \name High Level `.blend` file read/write.
* \{ */
+static bool blendfile_or_libraries_versions_atleast(Main *bmain,
+ const short versionfile,
+ const short subversionfile)
+{
+ if (!MAIN_VERSION_ATLEAST(bmain, versionfile, subversionfile)) {
+ return false;
+ }
+
+ LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
+ if (!MAIN_VERSION_ATLEAST(library, versionfile, subversionfile)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static bool foreach_path_clean_cb(BPathForeachPathData *UNUSED(bpath_data),
char *path_dst,
const char *path_src)
@@ -349,13 +366,18 @@ static void setup_app_data(bContext *C,
do_versions_ipos_to_animato(bmain);
}
- /* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */
- /* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a
- * version bump and check here. */
- if (mode != LOAD_UNDO && !USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) {
+ /* NOTE: readfile's `do_version` does not allow to create new IDs, and only operates on a single
+ * library at a time. This code needs to operate on the whole Main at once. */
+ /* NOTE: Check bmain version (i.e. current blend file version), AND the versions of all the
+ * linked libraries. */
+ if (mode != LOAD_UNDO && !blendfile_or_libraries_versions_atleast(bmain, 302, 1)) {
BKE_lib_override_library_main_proxy_convert(bmain, reports);
}
+ if (mode != LOAD_UNDO && !blendfile_or_libraries_versions_atleast(bmain, 302, 3)) {
+ BKE_lib_override_library_main_hierarchy_root_ensure(bmain);
+ }
+
bmain->recovered = 0;
/* startup.blend or recovered startup */
@@ -603,12 +625,12 @@ UserDef *BKE_blendfile_userdef_from_defaults(void)
const char *addons[] = {
"io_anim_bvh",
"io_curve_svg",
+ "io_import_obj",
"io_mesh_ply",
"io_mesh_stl",
"io_mesh_uv_layout",
"io_scene_fbx",
"io_scene_gltf2",
- "io_scene_obj",
"io_scene_x3d",
"cycles",
"pose_library",
diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c
index 9b3f4c2fae8..025d16007d8 100644
--- a/source/blender/blenkernel/intern/blendfile_link_append.c
+++ b/source/blender/blenkernel/intern/blendfile_link_append.c
@@ -993,6 +993,27 @@ static int foreach_libblock_link_append_callback(LibraryIDLinkCallbackData *cb_d
/** \name Library link/append code.
* \{ */
+static void blendfile_link_append_proxies_convert(Main *bmain, ReportList *reports)
+{
+ /* NOTE: Do not bother checking file versions here, if there are no proxies to convert this code
+ * is quite fast anyway. */
+
+ BlendFileReadReport bf_reports = {.reports = reports};
+ BKE_lib_override_library_main_proxy_convert(bmain, &bf_reports);
+
+ if (bf_reports.count.proxies_to_lib_overrides_success != 0 ||
+ bf_reports.count.proxies_to_lib_overrides_failures != 0) {
+ BKE_reportf(
+ bf_reports.reports,
+ RPT_WARNING,
+ "Proxies have been removed from Blender (%d proxies were automatically converted "
+ "to library overrides, %d proxies could not be converted and were cleared). "
+ "Please consider re-saving any library .blend file with the newest Blender version.",
+ bf_reports.count.proxies_to_lib_overrides_success,
+ bf_reports.count.proxies_to_lib_overrides_failures);
+ }
+}
+
void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *reports)
{
if (lapp_context->num_items == 0) {
@@ -1040,10 +1061,6 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *
if (item->action != LINK_APPEND_ACT_UNSET) {
/* Already set, pass. */
}
- if (GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) {
- CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name);
- item->action = LINK_APPEND_ACT_KEEP_LINKED;
- }
else if (do_reuse_local_id && existing_local_id != NULL) {
CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name);
item->action = LINK_APPEND_ACT_REUSE_LOCAL;
@@ -1098,10 +1115,7 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *
local_appended_new_id = id->newid;
break;
case LINK_APPEND_ACT_MAKE_LOCAL:
- BKE_lib_id_make_local(bmain,
- id,
- make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL |
- LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
+ BKE_lib_id_make_local(bmain, id, make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL);
BLI_assert(id->newid == NULL);
local_appended_new_id = id;
break;
@@ -1210,55 +1224,11 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *
continue;
}
BLI_assert(ID_IS_LINKED(id));
-
- /* Attempt to re-link copied proxy objects. This allows appending of an entire scene
- * from another blend file into this one, even when that blend file contains proxified
- * armatures that have local references. Since the proxified object needs to be linked
- * (not local), this will only work when the "Localize all" checkbox is disabled.
- * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */
- if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) {
- Object *ob = (Object *)id;
- Object *ob_new = (Object *)id->newid;
- bool is_local = false, is_lib = false;
-
- /* Proxies only work when the proxified object is linked-in from a library. */
- if (!ID_IS_LINKED(ob->proxy)) {
- CLOG_WARN(&LOG,
- "Proxy object %s will lose its link to %s, because the "
- "proxified object is local",
- id->newid->name,
- ob->proxy->id.name);
- continue;
- }
-
- BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib);
-
- /* We can only switch the proxy'ing to a made-local proxy if it is no longer
- * referred to from a library. Not checking for local use; if new local proxy
- * was not used locally would be a nasty bug! */
- if (is_local || is_lib) {
- CLOG_WARN(&LOG,
- "Made-local proxy object %s will lose its link to %s, "
- "because the linked-in proxy is referenced (is_local=%i, is_lib=%i)",
- id->newid->name,
- ob->proxy->id.name,
- is_local,
- is_lib);
- }
- else {
- /* we can switch the proxy'ing from the linked-in to the made-local proxy.
- * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that
- * was already allocated by object_make_local() (which called BKE_object_copy). */
- ob_new->proxy = ob->proxy;
- ob_new->proxy_group = ob->proxy_group;
- ob_new->proxy_from = ob->proxy_from;
- ob_new->proxy->proxy_from = ob_new;
- ob->proxy = ob->proxy_from = ob->proxy_group = NULL;
- }
- }
}
BKE_main_id_newptr_and_tag_clear(bmain);
+
+ blendfile_link_append_proxies_convert(bmain, reports);
}
void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *reports)
@@ -1361,6 +1331,10 @@ void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *re
.active_collection = NULL};
loose_data_instantiate(&instantiate_context);
}
+
+ if ((lapp_context->params->flag & FILE_LINK) != 0) {
+ blendfile_link_append_proxies_convert(lapp_context->params->bmain, reports);
+ }
}
/** \} */
@@ -1541,7 +1515,6 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
/* Note that in reload case, we also want to replace indirect usages. */
const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE |
- ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE |
(do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE);
for (item_idx = 0, itemlink = lapp_context->items.list; itemlink;
item_idx++, itemlink = itemlink->next) {
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 153a65d67db..c86d4658cc9 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -149,16 +149,9 @@ static void brush_make_local(Main *bmain, ID *id, const int flags)
Brush *brush = (Brush *)id;
const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
- bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
- bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
- BLI_assert(force_copy == false || force_copy != force_local);
- bool is_local = false, is_lib = false;
-
- /* - only lib users: do nothing (unless force_local is set)
- * - only local users: set flag
- * - mixed: make copy
- */
+ bool force_local, force_copy;
+ BKE_lib_id_make_local_generic_action_define(bmain, id, flags, &force_local, &force_copy);
if (brush->clone.image) {
/* Special case: ima always local immediately. Clone image should only have one user anyway. */
@@ -171,18 +164,6 @@ static void brush_make_local(Main *bmain, ID *id, const int flags)
BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL);
}
- if (!force_local && !force_copy) {
- BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib);
- if (lib_local || is_local) {
- if (!is_lib) {
- force_local = true;
- }
- else {
- force_copy = true;
- }
- }
- }
-
if (force_local) {
BKE_lib_id_clear_library_data(bmain, &brush->id, flags);
BKE_lib_id_expand_local(bmain, &brush->id, flags);
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index 7940936b64a..d9c637eb177 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -218,7 +218,7 @@ void *BKE_camera_add(Main *bmain, const char *name)
return cam;
}
-float BKE_camera_object_dof_distance(Object *ob)
+float BKE_camera_object_dof_distance(const Object *ob)
{
Camera *cam = (Camera *)ob->data;
if (ob->type != OB_CAMERA) {
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index a4f3e84a2bf..d8ad4dd89e4 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -15,13 +15,12 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- * Implementation of CDDerivedMesh.
- *
- * BKE_cdderivedmesh.h contains the function prototypes for this file.
*/
/** \file
* \ingroup bke
+ * Implementation of #CDDerivedMesh.
+ * BKE_cdderivedmesh.h contains the function prototypes for this file.
*/
#include "atomic_ops.h"
@@ -116,12 +115,6 @@ static void cdDM_copyEdgeArray(DerivedMesh *dm, MEdge *r_edge)
memcpy(r_edge, cddm->medge, sizeof(*r_edge) * dm->numEdgeData);
}
-static void cdDM_copyTessFaceArray(DerivedMesh *dm, MFace *r_face)
-{
- CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
- memcpy(r_face, cddm->mface, sizeof(*r_face) * dm->numTessFaceData);
-}
-
static void cdDM_copyLoopArray(DerivedMesh *dm, MLoop *r_loop)
{
CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
@@ -147,20 +140,6 @@ static void cdDM_getVertNo(DerivedMesh *dm, int index, float r_no[3])
copy_v3_v3(r_no, cddm->vert_normals[index]);
}
-static const MeshElemMap *cdDM_getPolyMap(Object *ob, DerivedMesh *dm)
-{
- CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
-
- if (!cddm->pmap && ob->type == OB_MESH) {
- Mesh *me = ob->data;
-
- BKE_mesh_vert_poly_map_create(
- &cddm->pmap, &cddm->pmap_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
- }
-
- return cddm->pmap;
-}
-
static void cdDM_recalc_looptri(DerivedMesh *dm)
{
CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
@@ -216,24 +195,17 @@ static CDDerivedMesh *cdDM_create(const char *desc)
dm->copyVertArray = cdDM_copyVertArray;
dm->copyEdgeArray = cdDM_copyEdgeArray;
- dm->copyTessFaceArray = cdDM_copyTessFaceArray;
dm->copyLoopArray = cdDM_copyLoopArray;
dm->copyPolyArray = cdDM_copyPolyArray;
- dm->getVertData = DM_get_vert_data;
- dm->getEdgeData = DM_get_edge_data;
- dm->getTessFaceData = DM_get_tessface_data;
dm->getVertDataArray = DM_get_vert_data_layer;
dm->getEdgeDataArray = DM_get_edge_data_layer;
- dm->getTessFaceDataArray = DM_get_tessface_data_layer;
dm->recalcLoopTri = cdDM_recalc_looptri;
dm->getVertCo = cdDM_getVertCo;
dm->getVertNo = cdDM_getVertNo;
- dm->getPolyMap = cdDM_getPolyMap;
-
dm->release = cdDM_release;
return cddm;
@@ -265,12 +237,6 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh,
dm->deformedOnly = 1;
dm->cd_flag = mesh->cd_flag;
- if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) {
- dm->dirty |= DM_DIRTY_NORMALS;
- }
- /* TODO: DM_DIRTY_TESS_CDLAYERS ? Maybe not though,
- * since we probably want to switch to looptris? */
-
CustomData_merge(&mesh->vdata, &dm->vertData, cddata_masks.vmask, alloctype, mesh->totvert);
CustomData_merge(&mesh->edata, &dm->edgeData, cddata_masks.emask, alloctype, mesh->totedge);
CustomData_merge(&mesh->fdata,
@@ -282,7 +248,9 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh,
CustomData_merge(&mesh->pdata, &dm->polyData, cddata_masks.pmask, alloctype, mesh->totpoly);
cddm->mvert = CustomData_get_layer(&dm->vertData, CD_MVERT);
- cddm->vert_normals = CustomData_get_layer(&dm->vertData, CD_NORMAL);
+ /* Though this may be an unnecessary calculation, simply retrieving the layer may return nothing
+ * or dirty normals. */
+ cddm->vert_normals = BKE_mesh_vertex_normals_ensure(mesh);
cddm->medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE);
cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP);
cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY);
@@ -327,12 +295,6 @@ DerivedMesh *CDDM_copy(DerivedMesh *source)
DM_from_template(dm, source, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys);
dm->deformedOnly = source->deformedOnly;
dm->cd_flag = source->cd_flag;
- dm->dirty = source->dirty;
-
- /* Tessellation data is never copied, so tag it here.
- * Only tag dirty layers if we really ignored tessellation faces.
- */
- dm->dirty |= DM_DIRTY_TESS_CDLAYERS;
CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts);
CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges);
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index e6ce4eb9440..79f40c1c888 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -1094,14 +1094,12 @@ static bool collection_object_remove(Main *bmain,
return true;
}
-bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
+bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Object *ob)
{
- if (ELEM(NULL, collection, ob)) {
+ if (ob == NULL) {
return false;
}
- collection = collection_parent_editable_find_recursive(collection);
-
/* Only case where this pointer can be NULL is when scene itself is linked, this case should
* never be reached. */
BLI_assert(collection != NULL);
@@ -1122,6 +1120,17 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
return true;
}
+bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
+{
+ if (collection == NULL) {
+ return false;
+ }
+
+ collection = collection_parent_editable_find_recursive(collection);
+
+ return BKE_collection_object_add_notest(bmain, collection, ob);
+}
+
void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst)
{
bool is_instantiated = false;
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index f013ef99dde..3b7f91b93c0 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -5850,14 +5850,6 @@ static void add_new_constraint_to_list(Object *ob, bPoseChannel *pchan, bConstra
BLI_addtail(list, con);
BKE_constraint_unique_name(con, list);
- /* if the target list is a list on some PoseChannel belonging to a proxy-protected
- * Armature layer, we must tag newly added constraints with a flag which allows them
- * to persist after proxy syncing has been done
- */
- if (BKE_constraints_proxylocked_owner(ob, pchan)) {
- con->flag |= CONSTRAINT_PROXY_LOCAL;
- }
-
/* make this constraint the active one */
BKE_constraints_active_set(list, con);
}
@@ -6213,45 +6205,6 @@ bool BKE_constraint_is_nonlocal_in_liboverride(const Object *ob, const bConstrai
(con == NULL || (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) == 0));
}
-/* -------- Constraints and Proxies ------- */
-
-void BKE_constraints_proxylocal_extract(ListBase *dst, ListBase *src)
-{
- bConstraint *con, *next;
-
- /* for each tagged constraint, remove from src and move to dst */
- for (con = src->first; con; con = next) {
- next = con->next;
-
- /* check if tagged */
- if (con->flag & CONSTRAINT_PROXY_LOCAL) {
- BLI_remlink(src, con);
- BLI_addtail(dst, con);
- }
- }
-}
-
-bool BKE_constraints_proxylocked_owner(Object *ob, bPoseChannel *pchan)
-{
- /* Currently, constraints can only be on object or bone level */
- if (ob && ob->proxy) {
- if (ob->pose && pchan) {
- bArmature *arm = ob->data;
-
- /* On bone-level, check if bone is on proxy-protected layer */
- if ((pchan->bone) && (pchan->bone->layer & arm->layer_protected)) {
- return true;
- }
- }
- else {
- /* FIXME: constraints on object-level are not handled well yet */
- return true;
- }
- }
-
- return false;
-}
-
/* -------- Target-Matrix Stuff ------- */
void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,
diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c
index 6bbb9957b03..0bf83ed5036 100644
--- a/source/blender/blenkernel/intern/crazyspace.c
+++ b/source/blender/blenkernel/intern/crazyspace.c
@@ -41,6 +41,7 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
+#include "BKE_report.h"
#include "DEG_depsgraph_query.h"
@@ -109,7 +110,7 @@ float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object
/* disable subsurf temporal, get mapped cos, and enable it */
if (modifiers_disable_subsurf_temporary(scene_eval, obedit_eval)) {
/* need to make new derivemesh */
- makeDerivedMesh(depsgraph, scene_eval, obedit_eval, editmesh_eval, &CD_MASK_BAREMESH);
+ makeDerivedMesh(depsgraph, scene_eval, obedit_eval, &CD_MASK_BAREMESH);
}
/* now get the cage */
@@ -193,13 +194,10 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me,
float (*mappedcos)[3],
float (*quats)[4])
{
- MVert *mvert = me->mvert;
- for (int i = 0; i < me->totvert; i++, mvert++) {
- mvert->flag &= ~ME_VERT_TMP_TAG;
- }
+ BLI_bitmap *vert_tag = BLI_BITMAP_NEW(me->totvert, __func__);
/* first store two sets of tangent vectors in vertices, we derive it just from the face-edges */
- mvert = me->mvert;
+ MVert *mvert = me->mvert;
MPoly *mp = me->mpoly;
MLoop *mloop = me->mloop;
@@ -209,7 +207,7 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me,
MLoop *ml_prev = &ml_next[mp->totloop - 2];
for (int j = 0; j < mp->totloop; j++) {
- if ((mvert[ml_curr->v].flag & ME_VERT_TMP_TAG) == 0) {
+ if (!BLI_BITMAP_TEST(vert_tag, ml_curr->v)) {
const float *co_prev, *co_curr, *co_next; /* orig */
const float *vd_prev, *vd_curr, *vd_next; /* deform */
@@ -232,7 +230,7 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me,
set_crazy_vertex_quat(
quats[ml_curr->v], co_curr, co_next, co_prev, vd_curr, vd_next, vd_prev);
- mvert[ml_curr->v].flag |= ME_VERT_TMP_TAG;
+ BLI_BITMAP_ENABLE(vert_tag, ml_curr->v);
}
ml_prev = ml_curr;
@@ -240,6 +238,8 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me,
ml_next++;
}
}
+
+ MEM_freeN(vert_tag);
}
int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgraph,
@@ -516,3 +516,85 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph,
}
}
}
+
+/* -------------------------------------------------------------------- */
+/** \name Crazyspace API
+ * \{ */
+
+void BKE_crazyspace_api_eval(Depsgraph *depsgraph,
+ Scene *scene,
+ Object *object,
+ struct ReportList *reports)
+{
+ if (object->runtime.crazyspace_deform_imats != NULL ||
+ object->runtime.crazyspace_deform_cos != NULL) {
+ return;
+ }
+
+ if (object->type != OB_MESH) {
+ BKE_report(reports,
+ RPT_ERROR,
+ "Crazyspace transformation is only available for Mesh type of objects");
+ return;
+ }
+
+ const Mesh *mesh = (const Mesh *)object->data;
+ object->runtime.crazyspace_num_verts = mesh->totvert;
+ BKE_crazyspace_build_sculpt(depsgraph,
+ scene,
+ object,
+ &object->runtime.crazyspace_deform_imats,
+ &object->runtime.crazyspace_deform_cos);
+}
+
+void BKE_crazyspace_api_displacement_to_deformed(struct Object *object,
+ struct ReportList *reports,
+ int vertex_index,
+ float displacement[3],
+ float r_displacement_deformed[3])
+{
+ if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_num_verts) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Invalid vertex index %d (expected to be within 0 to %d range)",
+ vertex_index,
+ object->runtime.crazyspace_num_verts);
+ return;
+ }
+
+ mul_v3_m3v3(r_displacement_deformed,
+ object->runtime.crazyspace_deform_imats[vertex_index],
+ displacement);
+}
+
+void BKE_crazyspace_api_displacement_to_original(struct Object *object,
+ struct ReportList *reports,
+ int vertex_index,
+ float displacement_deformed[3],
+ float r_displacement[3])
+{
+ if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_num_verts) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Invalid vertex index %d (expected to be within 0 to %d range))",
+ vertex_index,
+ object->runtime.crazyspace_num_verts);
+ return;
+ }
+
+ float mat[3][3];
+ if (!invert_m3_m3(mat, object->runtime.crazyspace_deform_imats[vertex_index])) {
+ copy_v3_v3(r_displacement, displacement_deformed);
+ return;
+ }
+
+ mul_v3_m3v3(r_displacement, mat, displacement_deformed);
+}
+
+void BKE_crazyspace_api_eval_clear(Object *object)
+{
+ MEM_SAFE_FREE(object->runtime.crazyspace_deform_imats);
+ MEM_SAFE_FREE(object->runtime.crazyspace_deform_cos);
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc
index 755b05e2697..c611e280793 100644
--- a/source/blender/blenkernel/intern/curve.cc
+++ b/source/blender/blenkernel/intern/curve.cc
@@ -30,7 +30,9 @@
#include "BLI_blenlib.h"
#include "BLI_endian_switch.h"
#include "BLI_ghash.h"
+#include "BLI_index_range.hh"
#include "BLI_math.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -58,6 +60,7 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_object.h"
+#include "BKE_spline.hh"
#include "BKE_vfont.h"
#include "DEG_depsgraph.h"
@@ -67,10 +70,13 @@
#include "BLO_read_write.h"
+using blender::float3;
+using blender::IndexRange;
+
/* globals */
/* local */
-static CLG_LogRef LOG = {"bke.curve"};
+// static CLG_LogRef LOG = {"bke.curve"};
static void curve_init_data(ID *id)
{
@@ -500,7 +506,10 @@ BoundBox *BKE_curve_boundbox_get(Object *ob)
float min[3], max[3];
INIT_MINMAX(min, max);
- BKE_curve_minmax(cu, true, min, max);
+ if (!BKE_curve_minmax(cu, true, min, max)) {
+ copy_v3_fl(min, -1.0f);
+ copy_v3_fl(max, 1.0f);
+ }
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = (BoundBox *)MEM_mallocN(sizeof(*ob->runtime.bb), __func__);
@@ -896,7 +905,7 @@ float BKE_nurb_calc_length(const Nurb *nu, int resolution)
pntsit = points + 3;
}
- while (--b) {
+ while (--b > 0) {
length += len_v3v3(prevpntsit, pntsit);
prevpntsit = pntsit;
pntsit += 3;
@@ -1160,81 +1169,34 @@ void BKE_nurb_bpoint_calc_plane(struct Nurb *nu, BPoint *bp, float r_plane[3])
static void calcknots(float *knots, const int pnts, const short order, const short flag)
{
- /* knots: number of pnts NOT corrected for cyclic */
- const int pnts_order = pnts + order;
- float k;
- int a;
+ const bool is_cyclic = flag & CU_NURB_CYCLIC;
+ const bool is_bezier = flag & CU_NURB_BEZIER && !(flag & CU_NURB_ENDPOINT);
+ const bool is_end_point = flag & CU_NURB_ENDPOINT && !(flag & CU_NURB_BEZIER);
+ /* Inner knots are always repeated once except on Bezier case. */
+ const int repeat_inner = is_bezier ? order - 1 : 1;
+ /* How many times to repeat 0.0 at the beginning of knot. */
+ const int head = is_end_point && !is_cyclic ? order : (is_bezier ? order / 2 : 1);
+ /* Number of knots replicating widths of the starting knots.
+ * Covers both Cyclic and EndPoint cases. */
+ const int tail = is_cyclic ? 2 * order - 1 : (is_end_point ? order : 0);
- switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
- case CU_NURB_ENDPOINT:
- k = 0.0;
- for (a = 1; a <= pnts_order; a++) {
- knots[a - 1] = k;
- if (a >= order && a <= pnts) {
- k += 1.0f;
- }
- }
- break;
- case CU_NURB_BEZIER:
- /* Warning, the order MUST be 2 or 4,
- * if this is not enforced, the displist will be corrupt */
- if (order == 4) {
- k = 0.34;
- for (a = 0; a < pnts_order; a++) {
- knots[a] = floorf(k);
- k += (1.0f / 3.0f);
- }
- }
- else if (order == 3) {
- k = 0.6f;
- for (a = 0; a < pnts_order; a++) {
- if (a >= order && a <= pnts) {
- k += 0.5f;
- }
- knots[a] = floorf(k);
- }
- }
- else {
- CLOG_ERROR(&LOG, "bez nurb curve order is not 3 or 4, should never happen");
- }
- break;
- default:
- for (a = 0; a < pnts_order; a++) {
- knots[a] = (float)a;
- }
- break;
- }
-}
-
-static void makecyclicknots(float *knots, int pnts, short order)
-/* pnts, order: number of pnts NOT corrected for cyclic */
-{
- int a, b, order2, c;
+ const int knot_count = pnts + order + (is_cyclic ? order - 1 : 0);
- if (knots == nullptr) {
- return;
- }
+ int r = head;
+ float current = 0.0f;
- order2 = order - 1;
-
- /* do first long rows (order -1), remove identical knots at endpoints */
- if (order > 2) {
- b = pnts + order2;
- for (a = 1; a < order2; a++) {
- if (knots[b] != knots[b - a]) {
- break;
- }
- }
- if (a == order2) {
- knots[pnts + order - 2] += 1.0f;
+ for (const int i : IndexRange(knot_count - tail)) {
+ knots[i] = current;
+ r--;
+ if (r == 0) {
+ current += 1.0;
+ r = repeat_inner;
}
}
- b = order;
- c = pnts + order + order2;
- for (a = pnts + order2; a < c; a++) {
- knots[a] = knots[a - 1] + (knots[b] - knots[b - 1]);
- b--;
+ const int tail_index = knot_count - tail;
+ for (const int i : IndexRange(tail)) {
+ knots[tail_index + i] = current + (knots[i] - knots[0]);
}
}
@@ -1247,13 +1209,7 @@ static void makeknots(Nurb *nu, short uv)
}
if (BKE_nurb_check_valid_u(nu)) {
nu->knotsu = (float *)MEM_calloc_arrayN(KNOTSU(nu) + 1, sizeof(float), "makeknots");
- if (nu->flagu & CU_NURB_CYCLIC) {
- calcknots(nu->knotsu, nu->pntsu, nu->orderu, 0); /* cyclic should be uniform */
- makecyclicknots(nu->knotsu, nu->pntsu, nu->orderu);
- }
- else {
- calcknots(nu->knotsu, nu->pntsu, nu->orderu, nu->flagu);
- }
+ calcknots(nu->knotsu, nu->pntsu, nu->orderu, nu->flagu);
}
else {
nu->knotsu = nullptr;
@@ -1265,13 +1221,7 @@ static void makeknots(Nurb *nu, short uv)
}
if (BKE_nurb_check_valid_v(nu)) {
nu->knotsv = (float *)MEM_calloc_arrayN(KNOTSV(nu) + 1, sizeof(float), "makeknots");
- if (nu->flagv & CU_NURB_CYCLIC) {
- calcknots(nu->knotsv, nu->pntsv, nu->orderv, 0); /* cyclic should be uniform */
- makecyclicknots(nu->knotsv, nu->pntsv, nu->orderv);
- }
- else {
- calcknots(nu->knotsv, nu->pntsv, nu->orderv, nu->flagv);
- }
+ calcknots(nu->knotsv, nu->pntsv, nu->orderv, nu->flagv);
}
else {
nu->knotsv = nullptr;
diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
index 073d9d18a04..833b2fe99ec 100644
--- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
+++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
@@ -66,8 +66,8 @@ static void vert_extrude_to_mesh_data(const Spline &spline,
if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1];
- edge.v1 = vert_offset;
- edge.v2 = vert_offset + eval_size - 1;
+ edge.v1 = vert_offset + eval_size - 1;
+ edge.v2 = vert_offset;
edge.flag = ME_LOOSEEDGE;
}
diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc
new file mode 100644
index 00000000000..f5672e9b288
--- /dev/null
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -0,0 +1,473 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <cmath>
+#include <cstring>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curves_types.h"
+#include "DNA_defaults.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_index_range.hh"
+#include "BLI_listbase.h"
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_rand.hh"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_anim_data.h"
+#include "BKE_curves.h"
+#include "BKE_customdata.h"
+#include "BKE_global.h"
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_lib_remap.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BLT_translation.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "BLO_read_write.h"
+
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::RandomNumberGenerator;
+
+static const char *ATTR_POSITION = "position";
+static const char *ATTR_RADIUS = "radius";
+
+static void curves_random(Curves *curves);
+
+static void curves_init_data(ID *id)
+{
+ Curves *curves = (Curves *)id;
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(curves, id));
+
+ MEMCPY_STRUCT_AFTER(curves, DNA_struct_default_get(Curves), id);
+
+ CustomData_reset(&curves->geometry.point_data);
+ CustomData_reset(&curves->geometry.curve_data);
+
+ CustomData_add_layer_named(&curves->geometry.point_data,
+ CD_PROP_FLOAT3,
+ CD_CALLOC,
+ nullptr,
+ curves->geometry.point_size,
+ ATTR_POSITION);
+ CustomData_add_layer_named(&curves->geometry.point_data,
+ CD_PROP_FLOAT,
+ CD_CALLOC,
+ nullptr,
+ curves->geometry.point_size,
+ ATTR_RADIUS);
+
+ BKE_curves_update_customdata_pointers(curves);
+
+ curves_random(curves);
+}
+
+static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag)
+{
+ Curves *curves_dst = (Curves *)id_dst;
+ const Curves *curves_src = (const Curves *)id_src;
+ curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat));
+
+ curves_dst->geometry.point_size = curves_src->geometry.point_size;
+ curves_dst->geometry.curve_size = curves_src->geometry.curve_size;
+
+ const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
+ CustomData_copy(&curves_src->geometry.point_data,
+ &curves_dst->geometry.point_data,
+ CD_MASK_ALL,
+ alloc_type,
+ curves_dst->geometry.point_size);
+ CustomData_copy(&curves_src->geometry.curve_data,
+ &curves_dst->geometry.curve_data,
+ CD_MASK_ALL,
+ alloc_type,
+ curves_dst->geometry.curve_size);
+ BKE_curves_update_customdata_pointers(curves_dst);
+
+ curves_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(curves_src->geometry.offsets));
+
+ curves_dst->batch_cache = nullptr;
+}
+
+static void curves_free_data(ID *id)
+{
+ Curves *curves = (Curves *)id;
+ BKE_animdata_free(&curves->id, false);
+
+ BKE_curves_batch_cache_free(curves);
+
+ CustomData_free(&curves->geometry.point_data, curves->geometry.point_size);
+ CustomData_free(&curves->geometry.curve_data, curves->geometry.curve_size);
+
+ MEM_SAFE_FREE(curves->geometry.offsets);
+
+ MEM_SAFE_FREE(curves->mat);
+}
+
+static void curves_foreach_id(ID *id, LibraryForeachIDData *data)
+{
+ Curves *curves = (Curves *)id;
+ for (int i = 0; i < curves->totcol; i++) {
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->mat[i], IDWALK_CB_USER);
+ }
+}
+
+static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_address)
+{
+ Curves *curves = (Curves *)id;
+
+ CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE];
+ CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomData_blend_write_prepare(
+ &curves->geometry.point_data, &players, players_buff, ARRAY_SIZE(players_buff));
+ CustomData_blend_write_prepare(
+ &curves->geometry.curve_data, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
+
+ /* Write LibData */
+ BLO_write_id_struct(writer, Curves, id_address, &curves->id);
+ BKE_id_blend_write(writer, &curves->id);
+
+ /* Direct data */
+ CustomData_blend_write(writer,
+ &curves->geometry.point_data,
+ players,
+ curves->geometry.point_size,
+ CD_MASK_ALL,
+ &curves->id);
+ CustomData_blend_write(writer,
+ &curves->geometry.curve_data,
+ clayers,
+ curves->geometry.curve_size,
+ CD_MASK_ALL,
+ &curves->id);
+
+ BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.offsets);
+
+ BLO_write_pointer_array(writer, curves->totcol, curves->mat);
+ if (curves->adt) {
+ BKE_animdata_blend_write(writer, curves->adt);
+ }
+
+ /* Remove temporary data. */
+ if (players && players != players_buff) {
+ MEM_freeN(players);
+ }
+ if (clayers && clayers != clayers_buff) {
+ MEM_freeN(clayers);
+ }
+}
+
+static void curves_blend_read_data(BlendDataReader *reader, ID *id)
+{
+ Curves *curves = (Curves *)id;
+ BLO_read_data_address(reader, &curves->adt);
+ BKE_animdata_blend_read_data(reader, curves->adt);
+
+ /* Geometry */
+ CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size);
+ CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.point_size);
+ BKE_curves_update_customdata_pointers(curves);
+
+ BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.offsets);
+
+ /* Materials */
+ BLO_read_pointer_array(reader, (void **)&curves->mat);
+}
+
+static void curves_blend_read_lib(BlendLibReader *reader, ID *id)
+{
+ Curves *curves = (Curves *)id;
+ for (int a = 0; a < curves->totcol; a++) {
+ BLO_read_id_address(reader, curves->id.lib, &curves->mat[a]);
+ }
+}
+
+static void curves_blend_read_expand(BlendExpander *expander, ID *id)
+{
+ Curves *curves = (Curves *)id;
+ for (int a = 0; a < curves->totcol; a++) {
+ BLO_expand(expander, curves->mat[a]);
+ }
+}
+
+IDTypeInfo IDType_ID_CV = {
+ /*id_code */ ID_CV,
+ /*id_filter */ FILTER_ID_CV,
+ /*main_listbase_index */ INDEX_ID_CV,
+ /*struct_size */ sizeof(Curves),
+ /*name */ "Hair Curves",
+ /*name_plural */ "Hair Curves",
+ /*translation_context */ BLT_I18NCONTEXT_ID_CURVES,
+ /*flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
+ /*asset_type_info */ nullptr,
+
+ /*init_data */ curves_init_data,
+ /*copy_data */ curves_copy_data,
+ /*free_data */ curves_free_data,
+ /*make_local */ nullptr,
+ /*foreach_id */ curves_foreach_id,
+ /*foreach_cache */ nullptr,
+ /*foreach_path */ nullptr,
+ /*owner_get */ nullptr,
+
+ /*blend_write */ curves_blend_write,
+ /*blend_read_data */ curves_blend_read_data,
+ /*blend_read_lib */ curves_blend_read_lib,
+ /*blend_read_expand */ curves_blend_read_expand,
+
+ /*blend_read_undo_preserve */ nullptr,
+
+ /*lib_override_apply_post */ nullptr,
+};
+
+static void curves_random(Curves *curves)
+{
+ CurvesGeometry &geometry = curves->geometry;
+ const int numpoints = 8;
+
+ geometry.curve_size = 500;
+
+ geometry.curve_size = 500;
+ geometry.point_size = geometry.curve_size * numpoints;
+
+ curves->geometry.offsets = (int *)MEM_calloc_arrayN(
+ curves->geometry.curve_size + 1, sizeof(int), __func__);
+ CustomData_realloc(&geometry.point_data, geometry.point_size);
+ CustomData_realloc(&geometry.curve_data, geometry.curve_size);
+ BKE_curves_update_customdata_pointers(curves);
+
+ MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1};
+ MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size};
+ MutableSpan<float> radii{geometry.radius, geometry.point_size};
+
+ for (const int i : offsets.index_range()) {
+ geometry.offsets[i] = numpoints * i;
+ }
+
+ RandomNumberGenerator rng;
+
+ for (int i = 0; i < geometry.curve_size; i++) {
+ const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
+ MutableSpan<float3> curve_positions = positions.slice(curve_range);
+ MutableSpan<float> curve_radii = radii.slice(curve_range);
+
+ const float theta = 2.0f * M_PI * rng.get_float();
+ const float phi = saacosf(2.0f * rng.get_float() - 1.0f);
+
+ float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)};
+ no = blender::math::normalize(no);
+
+ float3 co = no;
+ for (int key = 0; key < numpoints; key++) {
+ float t = key / (float)(numpoints - 1);
+ curve_positions[key] = co;
+ curve_radii[key] = 0.02f * (1.0f - t);
+
+ float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f;
+ co += (offset + no) / numpoints;
+ }
+ }
+}
+
+void *BKE_curves_add(Main *bmain, const char *name)
+{
+ Curves *curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, name));
+
+ return curves;
+}
+
+BoundBox *BKE_curves_boundbox_get(Object *ob)
+{
+ BLI_assert(ob->type == OB_CURVES);
+ Curves *curves = static_cast<Curves *>(ob->data);
+
+ if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
+ return ob->runtime.bb;
+ }
+
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
+
+ float min[3], max[3];
+ INIT_MINMAX(min, max);
+
+ float(*curves_co)[3] = curves->geometry.position;
+ float *curves_radius = curves->geometry.radius;
+ for (int a = 0; a < curves->geometry.point_size; a++) {
+ float *co = curves_co[a];
+ float radius = (curves_radius) ? curves_radius[a] : 0.0f;
+ const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
+ const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
+ DO_MIN(co_min, min);
+ DO_MAX(co_max, max);
+ }
+
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ }
+
+ return ob->runtime.bb;
+}
+
+void BKE_curves_update_customdata_pointers(Curves *curves)
+{
+ curves->geometry.position = (float(*)[3])CustomData_get_layer_named(
+ &curves->geometry.point_data, CD_PROP_FLOAT3, ATTR_POSITION);
+ curves->geometry.radius = (float *)CustomData_get_layer_named(
+ &curves->geometry.point_data, CD_PROP_FLOAT, ATTR_RADIUS);
+}
+
+bool BKE_curves_customdata_required(Curves *UNUSED(curves), CustomDataLayer *layer)
+{
+ return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, ATTR_POSITION);
+}
+
+/* Dependency Graph */
+
+Curves *BKE_curves_new_for_eval(const Curves *curves_src, int totpoint, int totcurve)
+{
+ Curves *curves_dst = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr));
+
+ STRNCPY(curves_dst->id.name, curves_src->id.name);
+ curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat));
+ curves_dst->totcol = curves_src->totcol;
+
+ curves_dst->geometry.point_size = totpoint;
+ curves_dst->geometry.curve_size = totcurve;
+ CustomData_copy(&curves_src->geometry.point_data,
+ &curves_dst->geometry.point_data,
+ CD_MASK_ALL,
+ CD_CALLOC,
+ totpoint);
+ CustomData_copy(&curves_src->geometry.curve_data,
+ &curves_dst->geometry.curve_data,
+ CD_MASK_ALL,
+ CD_CALLOC,
+ totcurve);
+ BKE_curves_update_customdata_pointers(curves_dst);
+
+ return curves_dst;
+}
+
+Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference)
+{
+ int flags = LIB_ID_COPY_LOCALIZE;
+
+ if (reference) {
+ flags |= LIB_ID_COPY_CD_REFERENCE;
+ }
+
+ Curves *result = (Curves *)BKE_id_copy_ex(nullptr, &curves_src->id, nullptr, flags);
+ return result;
+}
+
+static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
+ Curves *curves_input)
+{
+ Curves *curves = curves_input;
+
+ /* Modifier evaluation modes. */
+ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
+ const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
+ ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
+ const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
+
+ /* Get effective list of modifiers to execute. Some effects like shape keys
+ * are added as virtual modifiers before the user created modifiers. */
+ VirtualModifierData virtualModifierData;
+ ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData);
+
+ /* Evaluate modifiers. */
+ for (; md; md = md->next) {
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
+
+ if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
+ continue;
+ }
+
+ if ((mti->type == eModifierTypeType_OnlyDeform) &&
+ (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
+ /* Ensure we are not modifying the input. */
+ if (curves == curves_input) {
+ curves = BKE_curves_copy_for_eval(curves, true);
+ }
+
+ /* Ensure we are not overwriting referenced data. */
+ CustomData_duplicate_referenced_layer_named(&curves->geometry.point_data,
+ CD_PROP_FLOAT3,
+ ATTR_POSITION,
+ curves->geometry.point_size);
+ BKE_curves_update_customdata_pointers(curves);
+
+ /* Created deformed coordinates array on demand. */
+ mti->deformVerts(
+ md, &mectx, nullptr, curves->geometry.position, curves->geometry.point_size);
+ }
+ }
+
+ return curves;
+}
+
+void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
+{
+ /* Free any evaluated data and restore original data. */
+ BKE_object_free_derived_caches(object);
+
+ /* Evaluate modifiers. */
+ Curves *curves = static_cast<Curves *>(object->data);
+ Curves *curves_eval = curves_evaluate_modifiers(depsgraph, scene, object, curves);
+
+ /* Assign evaluated object. */
+ const bool is_owned = (curves != curves_eval);
+ BKE_object_eval_assign_data(object, &curves_eval->id, is_owned);
+}
+
+/* Draw Cache */
+
+void (*BKE_curves_batch_cache_dirty_tag_cb)(Curves *curves, int mode) = nullptr;
+void (*BKE_curves_batch_cache_free_cb)(Curves *curves) = nullptr;
+
+void BKE_curves_batch_cache_dirty_tag(Curves *curves, int mode)
+{
+ if (curves->batch_cache) {
+ BKE_curves_batch_cache_dirty_tag_cb(curves, mode);
+ }
+}
+
+void BKE_curves_batch_cache_free(Curves *curves)
+{
+ if (curves->batch_cache) {
+ BKE_curves_batch_cache_free_cb(curves);
+ }
+}
diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc
index e1bc025efd2..6db5ae7567f 100644
--- a/source/blender/blenkernel/intern/customdata.cc
+++ b/source/blender/blenkernel/intern/customdata.cc
@@ -15,13 +15,13 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- * Implementation of CustomData.
- *
- * BKE_customdata.h contains the function prototypes for this file.
*/
/** \file
* \ingroup bke
+ * Implementation of CustomData.
+ *
+ * BKE_customdata.h contains the function prototypes for this file.
*/
#include "MEM_guardedalloc.h"
@@ -31,7 +31,6 @@
#include "DNA_ID.h"
#include "DNA_customdata_types.h"
-#include "DNA_hair_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_bitmap.h"
@@ -1793,10 +1792,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(float[3]), "vec3f", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 44: CD_RADIUS */
{sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
- /* 45: CD_HAIRCURVE */
- {sizeof(HairCurve), "HairCurve", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
- /* 46: CD_HAIRMAPPING */
- {sizeof(HairMapping), "HairMapping", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
+ /* 45: CD_PROP_INT8 */
+ {sizeof(int8_t), "MInt8Property", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
+ /* 46: CD_HAIRMAPPING */ /* UNUSED */
+ {-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 47: CD_PROP_COLOR */
{sizeof(MPropCol),
"MPropCol",
@@ -1914,7 +1913,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDCustomLoopNormal",
"CDSculptFaceGroups",
/* 43-46 */ "CDHairPoint",
- "CDHairCurve",
+ "CDPropInt8",
"CDHairMapping",
"CDPoint",
"CDPropCol",
@@ -2209,7 +2208,9 @@ void CustomData_realloc(CustomData *data, int totelem)
continue;
}
typeInfo = layerType_getInfo(layer->type);
- layer->data = MEM_reallocN(layer->data, (size_t)totelem * typeInfo->size);
+ /* Use calloc to avoid the need to manually initialize new data in layers.
+ * Useful for types like #MDeformVert which contain a pointer. */
+ layer->data = MEM_recallocN(layer->data, (size_t)totelem * typeInfo->size);
}
}
@@ -2425,6 +2426,13 @@ int CustomData_get_stencil_layer(const CustomData *data, int type)
return (layer_index != -1) ? data->layers[layer_index].active_mask : -1;
}
+const char *CustomData_get_active_layer_name(const struct CustomData *data, const int type)
+{
+ /* Get the layer index of the active layer of this type. */
+ const int layer_index = CustomData_get_active_layer_index(data, type);
+ return layer_index < 0 ? nullptr : data->layers[layer_index].name;
+}
+
void CustomData_set_layer_active(CustomData *data, int type, int n)
{
for (int i = 0; i < data->totlayer; i++) {
@@ -2771,6 +2779,24 @@ void CustomData_free_layers(CustomData *data, int type, int totelem)
}
}
+void CustomData_free_layers_anonymous(struct CustomData *data, int totelem)
+{
+ while (true) {
+ bool found_anonymous_layer = false;
+ for (int i = 0; i < data->totlayer; i++) {
+ const CustomDataLayer *layer = &data->layers[i];
+ if (layer->anonymous_id != nullptr) {
+ CustomData_free_layer(data, layer->type, totelem, i);
+ found_anonymous_layer = true;
+ break;
+ }
+ }
+ if (!found_anonymous_layer) {
+ break;
+ }
+ }
+}
+
bool CustomData_has_layer(const CustomData *data, int type)
{
return (CustomData_get_layer_index(data, type) != -1);
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index 64e0427a810..bc5a0ed1538 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -1518,7 +1518,7 @@ static void dynamic_paint_set_init_color_tex_to_vcol_cb(
multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage, false);
if (texres.tin > pPoint[vert].color[3]) {
- copy_v3_v3(pPoint[vert].color, &texres.tr);
+ copy_v3_v3(pPoint[vert].color, texres.trgba);
pPoint[vert].color[3] = texres.tin;
}
}
@@ -1559,7 +1559,7 @@ static void dynamic_paint_set_init_color_tex_to_imseq_cb(
multitex_ext_safe(tex, uv_final, &texres, NULL, scene_color_manage, false);
/* apply color */
- copy_v3_v3(pPoint[i].color, &texres.tr);
+ copy_v3_v3(pPoint[i].color, texres.trgba);
pPoint[i].color[3] = texres.tin;
}
@@ -1985,9 +1985,6 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *
}
MEM_freeN(fcolor);
-
- /* Mark tessellated CD layers as dirty. */
- // result->dirty |= DM_DIRTY_TESS_CDLAYERS;
}
/* vertex group paint */
else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) {
diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c
index 6ef811c46c7..0774a1a3d88 100644
--- a/source/blender/blenkernel/intern/editmesh.c
+++ b/source/blender/blenkernel/intern/editmesh.c
@@ -39,6 +39,8 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_object.h"
+#include "DEG_depsgraph_query.h"
+
BMEditMesh *BKE_editmesh_create(BMesh *bm)
{
BMEditMesh *em = MEM_callocN(sizeof(BMEditMesh), __func__);
@@ -51,9 +53,6 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em)
BMEditMesh *em_copy = MEM_callocN(sizeof(BMEditMesh), __func__);
*em_copy = *em;
- em_copy->mesh_eval_cage = em_copy->mesh_eval_final = NULL;
- em_copy->bb_cage = NULL;
-
em_copy->bm = BM_mesh_copy(em->bm);
/* The tessellation is NOT calculated on the copy here,
@@ -194,22 +193,8 @@ void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em,
});
}
-void BKE_editmesh_free_derived_caches(BMEditMesh *em)
-{
- if (em->mesh_eval_cage) {
- BKE_id_free(NULL, em->mesh_eval_cage);
- }
- if (em->mesh_eval_final && em->mesh_eval_final != em->mesh_eval_cage) {
- BKE_id_free(NULL, em->mesh_eval_final);
- }
- em->mesh_eval_cage = em->mesh_eval_final = NULL;
-
- MEM_SAFE_FREE(em->bb_cage);
-}
-
void BKE_editmesh_free_data(BMEditMesh *em)
{
- BKE_editmesh_free_derived_caches(em);
if (em->looptris) {
MEM_freeN(em->looptris);
@@ -283,13 +268,15 @@ const float (*BKE_editmesh_vert_coords_when_deformed(struct Depsgraph *depsgraph
*r_is_alloc = false;
Mesh *me = ob->data;
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object_eval);
if ((me->runtime.edit_data != NULL) && (me->runtime.edit_data->vertexCos != NULL)) {
/* Deformed, and we have deformed coords already. */
coords = me->runtime.edit_data->vertexCos;
}
- else if ((em->mesh_eval_final != NULL) &&
- (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
+ else if ((editmesh_eval_final != NULL) &&
+ (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
/* If this is an edit-mesh type, leave NULL as we can use the vertex coords. */
}
else {
@@ -334,18 +321,18 @@ void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, Mesh *me)
}
}
-BoundBox *BKE_editmesh_cage_boundbox_get(BMEditMesh *em)
+BoundBox *BKE_editmesh_cage_boundbox_get(struct Object *object, BMEditMesh *UNUSED(em))
{
- if (em->bb_cage == NULL) {
+ if (object->runtime.editmesh_bb_cage == NULL) {
float min[3], max[3];
INIT_MINMAX(min, max);
- if (em->mesh_eval_cage) {
- BKE_mesh_wrapper_minmax(em->mesh_eval_cage, min, max);
+ if (object->runtime.editmesh_eval_cage) {
+ BKE_mesh_wrapper_minmax(object->runtime.editmesh_eval_cage, min, max);
}
- em->bb_cage = MEM_callocN(sizeof(BoundBox), "BMEditMesh.bb_cage");
- BKE_boundbox_init_from_minmax(em->bb_cage, min, max);
+ object->runtime.editmesh_bb_cage = MEM_callocN(sizeof(BoundBox), "BMEditMesh.bb_cage");
+ BKE_boundbox_init_from_minmax(object->runtime.editmesh_bb_cage, min, max);
}
- return em->bb_cage;
+ return object->runtime.editmesh_bb_cage;
}
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index bbf9e9edfd2..1f1915f60ca 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -908,9 +908,9 @@ static void do_texture_effector(EffectorCache *eff,
eff->pd->tex, tex_co, NULL, NULL, 0, result, 0, NULL, scene_color_manage, false);
if (hasrgb && mode == PFIELD_TEX_RGB) {
- force[0] = (0.5f - result->tr) * strength;
- force[1] = (0.5f - result->tg) * strength;
- force[2] = (0.5f - result->tb) * strength;
+ force[0] = (0.5f - result->trgba[0]) * strength;
+ force[1] = (0.5f - result->trgba[1]) * strength;
+ force[2] = (0.5f - result->trgba[2]) * strength;
}
else if (nabla != 0) {
strength /= nabla;
@@ -933,7 +933,8 @@ static void do_texture_effector(EffectorCache *eff,
/* generate intensity if texture only has rgb value */
if (hasrgb & TEX_RGB) {
for (int i = 0; i < 4; i++) {
- result[i].tin = (1.0f / 3.0f) * (result[i].tr + result[i].tg + result[i].tb);
+ result[i].tin = (1.0f / 3.0f) *
+ (result[i].trgba[0] + result[i].trgba[1] + result[i].trgba[2]);
}
}
force[0] = (result[0].tin - result[1].tin) * strength;
@@ -943,12 +944,12 @@ static void do_texture_effector(EffectorCache *eff,
else { /*PFIELD_TEX_CURL*/
float dbdy, dgdz, drdz, dbdx, dgdx, drdy;
- dbdy = result[2].tb - result[0].tb;
- dgdz = result[3].tg - result[0].tg;
- drdz = result[3].tr - result[0].tr;
- dbdx = result[1].tb - result[0].tb;
- dgdx = result[1].tg - result[0].tg;
- drdy = result[2].tr - result[0].tr;
+ dbdy = result[2].trgba[2] - result[0].trgba[2];
+ dgdz = result[3].trgba[1] - result[0].trgba[1];
+ drdz = result[3].trgba[0] - result[0].trgba[0];
+ dbdx = result[1].trgba[2] - result[0].trgba[2];
+ dgdx = result[1].trgba[1] - result[0].trgba[1];
+ drdy = result[2].trgba[0] - result[0].trgba[0];
force[0] = (dbdy - dgdz) * strength;
force[1] = (drdz - dbdx) * strength;
diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c
index ce30f80ba65..e71217fea9e 100644
--- a/source/blender/blenkernel/intern/fcurve_driver.c
+++ b/source/blender/blenkernel/intern/fcurve_driver.c
@@ -88,14 +88,6 @@ typedef struct DriverVarTypeInfo {
/** \name Driver Target Utilities
* \{ */
-static ID *dtar_id_ensure_proxy_from(ID *id)
-{
- if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) {
- return (ID *)(((Object *)id)->proxy_from);
- }
- return id;
-}
-
/**
* Helper function to obtain a value using RNA from the specified source
* (for evaluating drivers).
@@ -113,7 +105,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
- id = dtar_id_ensure_proxy_from(dtar->id);
+ id = dtar->id;
/* Error check for missing pointer. */
if (id == NULL) {
@@ -217,7 +209,7 @@ bool driver_get_variable_property(ChannelDriver *driver,
return false;
}
- id = dtar_id_ensure_proxy_from(dtar->id);
+ id = dtar->id;
/* Error check for missing pointer. */
if (id == NULL) {
@@ -273,7 +265,7 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
short valid_targets = 0;
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
- Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
+ Object *ob = (Object *)dtar->id;
/* Check if this target has valid data. */
if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
@@ -328,7 +320,7 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
for (int i = 0; i < 2; i++) {
/* Get pointer to loc values to store in. */
DriverTarget *dtar = &dvar->targets[i];
- Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
+ Object *ob = (Object *)dtar->id;
bPoseChannel *pchan;
/* After the checks above, the targets should be valid here. */
@@ -389,7 +381,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
/* NOTE: for now, these are all just world-space */
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
/* Get pointer to loc values to store in. */
- Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
+ Object *ob = (Object *)dtar->id;
bPoseChannel *pchan;
float tmp_loc[3];
@@ -472,7 +464,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
{
DriverTarget *dtar = &dvar->targets[0];
- Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
+ Object *ob = (Object *)dtar->id;
bPoseChannel *pchan;
float mat[4][4];
float oldEul[3] = {0.0f, 0.0f, 0.0f};
diff --git a/source/blender/blenkernel/intern/hair.cc b/source/blender/blenkernel/intern/hair.cc
deleted file mode 100644
index b7ba159f631..00000000000
--- a/source/blender/blenkernel/intern/hair.cc
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup bke
- */
-
-#include <cmath>
-#include <cstring>
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_defaults.h"
-#include "DNA_hair_types.h"
-#include "DNA_material_types.h"
-#include "DNA_object_types.h"
-
-#include "BLI_listbase.h"
-#include "BLI_math_base.h"
-#include "BLI_math_vec_types.hh"
-#include "BLI_rand.h"
-#include "BLI_string.h"
-#include "BLI_utildefines.h"
-
-#include "BKE_anim_data.h"
-#include "BKE_customdata.h"
-#include "BKE_global.h"
-#include "BKE_hair.h"
-#include "BKE_idtype.h"
-#include "BKE_lib_id.h"
-#include "BKE_lib_query.h"
-#include "BKE_lib_remap.h"
-#include "BKE_main.h"
-#include "BKE_modifier.h"
-#include "BKE_object.h"
-
-#include "BLT_translation.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "BLO_read_write.h"
-
-using blender::float3;
-
-static const char *HAIR_ATTR_POSITION = "position";
-static const char *HAIR_ATTR_RADIUS = "radius";
-
-/* Hair datablock */
-
-static void hair_random(Hair *hair);
-
-static void hair_init_data(ID *id)
-{
- Hair *hair = (Hair *)id;
- BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(hair, id));
-
- MEMCPY_STRUCT_AFTER(hair, DNA_struct_default_get(Hair), id);
-
- CustomData_reset(&hair->pdata);
- CustomData_reset(&hair->cdata);
-
- CustomData_add_layer_named(
- &hair->pdata, CD_PROP_FLOAT3, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_POSITION);
- CustomData_add_layer_named(
- &hair->pdata, CD_PROP_FLOAT, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_RADIUS);
- CustomData_add_layer(&hair->cdata, CD_HAIRCURVE, CD_CALLOC, nullptr, hair->totcurve);
- BKE_hair_update_customdata_pointers(hair);
-
- hair_random(hair);
-}
-
-static void hair_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag)
-{
- Hair *hair_dst = (Hair *)id_dst;
- const Hair *hair_src = (const Hair *)id_src;
- hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat));
-
- const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
- CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, alloc_type, hair_dst->totpoint);
- CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, alloc_type, hair_dst->totcurve);
- BKE_hair_update_customdata_pointers(hair_dst);
-
- hair_dst->batch_cache = nullptr;
-}
-
-static void hair_free_data(ID *id)
-{
- Hair *hair = (Hair *)id;
- BKE_animdata_free(&hair->id, false);
-
- BKE_hair_batch_cache_free(hair);
-
- CustomData_free(&hair->pdata, hair->totpoint);
- CustomData_free(&hair->cdata, hair->totcurve);
-
- MEM_SAFE_FREE(hair->mat);
-}
-
-static void hair_foreach_id(ID *id, LibraryForeachIDData *data)
-{
- Hair *hair = (Hair *)id;
- for (int i = 0; i < hair->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, hair->mat[i], IDWALK_CB_USER);
- }
-}
-
-static void hair_blend_write(BlendWriter *writer, ID *id, const void *id_address)
-{
- Hair *hair = (Hair *)id;
-
- CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
- CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
-
- /* Write LibData */
- BLO_write_id_struct(writer, Hair, id_address, &hair->id);
- BKE_id_blend_write(writer, &hair->id);
-
- /* Direct data */
- CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id);
- CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id);
-
- BLO_write_pointer_array(writer, hair->totcol, hair->mat);
- if (hair->adt) {
- BKE_animdata_blend_write(writer, hair->adt);
- }
-
- /* Remove temporary data. */
- if (players && players != players_buff) {
- MEM_freeN(players);
- }
- if (clayers && clayers != clayers_buff) {
- MEM_freeN(clayers);
- }
-}
-
-static void hair_blend_read_data(BlendDataReader *reader, ID *id)
-{
- Hair *hair = (Hair *)id;
- BLO_read_data_address(reader, &hair->adt);
- BKE_animdata_blend_read_data(reader, hair->adt);
-
- /* Geometry */
- CustomData_blend_read(reader, &hair->pdata, hair->totpoint);
- CustomData_blend_read(reader, &hair->cdata, hair->totcurve);
- BKE_hair_update_customdata_pointers(hair);
-
- /* Materials */
- BLO_read_pointer_array(reader, (void **)&hair->mat);
-}
-
-static void hair_blend_read_lib(BlendLibReader *reader, ID *id)
-{
- Hair *hair = (Hair *)id;
- for (int a = 0; a < hair->totcol; a++) {
- BLO_read_id_address(reader, hair->id.lib, &hair->mat[a]);
- }
-}
-
-static void hair_blend_read_expand(BlendExpander *expander, ID *id)
-{
- Hair *hair = (Hair *)id;
- for (int a = 0; a < hair->totcol; a++) {
- BLO_expand(expander, hair->mat[a]);
- }
-}
-
-IDTypeInfo IDType_ID_HA = {
- /*id_code */ ID_HA,
- /*id_filter */ FILTER_ID_HA,
- /*main_listbase_index */ INDEX_ID_HA,
- /*struct_size */ sizeof(Hair),
- /*name */ "Hair",
- /*name_plural */ "hairs",
- /*translation_context */ BLT_I18NCONTEXT_ID_HAIR,
- /*flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
- /*asset_type_info */ nullptr,
-
- /*init_data */ hair_init_data,
- /*copy_data */ hair_copy_data,
- /*free_data */ hair_free_data,
- /*make_local */ nullptr,
- /*foreach_id */ hair_foreach_id,
- /*foreach_cache */ nullptr,
- /*foreach_path */ nullptr,
- /*owner_get */ nullptr,
-
- /*blend_write */ hair_blend_write,
- /*blend_read_data */ hair_blend_read_data,
- /*blend_read_lib */ hair_blend_read_lib,
- /*blend_read_expand */ hair_blend_read_expand,
-
- /*blend_read_undo_preserve */ nullptr,
-
- /*lib_override_apply_post */ nullptr,
-};
-
-static void hair_random(Hair *hair)
-{
- const int numpoints = 8;
-
- hair->totcurve = 500;
- hair->totpoint = hair->totcurve * numpoints;
-
- CustomData_realloc(&hair->pdata, hair->totpoint);
- CustomData_realloc(&hair->cdata, hair->totcurve);
- BKE_hair_update_customdata_pointers(hair);
-
- RNG *rng = BLI_rng_new(0);
-
- for (int i = 0; i < hair->totcurve; i++) {
- HairCurve *curve = &hair->curves[i];
- curve->firstpoint = i * numpoints;
- curve->numpoints = numpoints;
-
- float theta = 2.0f * M_PI * BLI_rng_get_float(rng);
- float phi = saacosf(2.0f * BLI_rng_get_float(rng) - 1.0f);
-
- float no[3] = {sinf(theta) * sinf(phi), cosf(theta) * sinf(phi), cosf(phi)};
- normalize_v3(no);
-
- float co[3];
- copy_v3_v3(co, no);
-
- float(*curve_co)[3] = hair->co + curve->firstpoint;
- float *curve_radius = hair->radius + curve->firstpoint;
- for (int key = 0; key < numpoints; key++) {
- float t = key / (float)(numpoints - 1);
- copy_v3_v3(curve_co[key], co);
- curve_radius[key] = 0.02f * (1.0f - t);
-
- float offset[3] = {2.0f * BLI_rng_get_float(rng) - 1.0f,
- 2.0f * BLI_rng_get_float(rng) - 1.0f,
- 2.0f * BLI_rng_get_float(rng) - 1.0f};
- add_v3_v3(offset, no);
- madd_v3_v3fl(co, offset, 1.0f / numpoints);
- }
- }
-
- BLI_rng_free(rng);
-}
-
-void *BKE_hair_add(Main *bmain, const char *name)
-{
- Hair *hair = static_cast<Hair *>(BKE_id_new(bmain, ID_HA, name));
-
- return hair;
-}
-
-BoundBox *BKE_hair_boundbox_get(Object *ob)
-{
- BLI_assert(ob->type == OB_HAIR);
- Hair *hair = static_cast<Hair *>(ob->data);
-
- if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
- return ob->runtime.bb;
- }
-
- if (ob->runtime.bb == nullptr) {
- ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
-
- float min[3], max[3];
- INIT_MINMAX(min, max);
-
- float(*hair_co)[3] = hair->co;
- float *hair_radius = hair->radius;
- for (int a = 0; a < hair->totpoint; a++) {
- float *co = hair_co[a];
- float radius = (hair_radius) ? hair_radius[a] : 0.0f;
- const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
- const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
- DO_MIN(co_min, min);
- DO_MAX(co_max, max);
- }
-
- BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
- }
-
- return ob->runtime.bb;
-}
-
-void BKE_hair_update_customdata_pointers(Hair *hair)
-{
- hair->co = (float(*)[3])CustomData_get_layer_named(
- &hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION);
- hair->radius = (float *)CustomData_get_layer_named(
- &hair->pdata, CD_PROP_FLOAT, HAIR_ATTR_RADIUS);
- hair->curves = (HairCurve *)CustomData_get_layer(&hair->cdata, CD_HAIRCURVE);
- hair->mapping = (HairMaping *)CustomData_get_layer(&hair->cdata, CD_HAIRMAPPING);
-}
-
-bool BKE_hair_customdata_required(Hair *UNUSED(hair), CustomDataLayer *layer)
-{
- return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, HAIR_ATTR_POSITION);
-}
-
-/* Dependency Graph */
-
-Hair *BKE_hair_new_for_eval(const Hair *hair_src, int totpoint, int totcurve)
-{
- Hair *hair_dst = static_cast<Hair *>(BKE_id_new_nomain(ID_HA, nullptr));
-
- STRNCPY(hair_dst->id.name, hair_src->id.name);
- hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat));
- hair_dst->totcol = hair_src->totcol;
-
- hair_dst->totpoint = totpoint;
- hair_dst->totcurve = totcurve;
- CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint);
- CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, CD_CALLOC, totcurve);
- BKE_hair_update_customdata_pointers(hair_dst);
-
- return hair_dst;
-}
-
-Hair *BKE_hair_copy_for_eval(Hair *hair_src, bool reference)
-{
- int flags = LIB_ID_COPY_LOCALIZE;
-
- if (reference) {
- flags |= LIB_ID_COPY_CD_REFERENCE;
- }
-
- Hair *result = (Hair *)BKE_id_copy_ex(nullptr, &hair_src->id, nullptr, flags);
- return result;
-}
-
-static Hair *hair_evaluate_modifiers(struct Depsgraph *depsgraph,
- struct Scene *scene,
- Object *object,
- Hair *hair_input)
-{
- Hair *hair = hair_input;
-
- /* Modifier evaluation modes. */
- const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
- const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
- ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
- const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
-
- /* Get effective list of modifiers to execute. Some effects like shape keys
- * are added as virtual modifiers before the user created modifiers. */
- VirtualModifierData virtualModifierData;
- ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData);
-
- /* Evaluate modifiers. */
- for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
-
- if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
- continue;
- }
-
- if ((mti->type == eModifierTypeType_OnlyDeform) &&
- (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
- /* Ensure we are not modifying the input. */
- if (hair == hair_input) {
- hair = BKE_hair_copy_for_eval(hair, true);
- }
-
- /* Ensure we are not overwriting referenced data. */
- CustomData_duplicate_referenced_layer_named(
- &hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION, hair->totpoint);
- BKE_hair_update_customdata_pointers(hair);
-
- /* Created deformed coordinates array on demand. */
- mti->deformVerts(md, &mectx, nullptr, hair->co, hair->totpoint);
- }
- else if (mti->modifyHair) {
- /* Ensure we are not modifying the input. */
- if (hair == hair_input) {
- hair = BKE_hair_copy_for_eval(hair, true);
- }
-
- Hair *hair_next = mti->modifyHair(md, &mectx, hair);
-
- if (hair_next && hair_next != hair) {
- /* If the modifier returned a new hair, release the old one. */
- if (hair != hair_input) {
- BKE_id_free(nullptr, hair);
- }
- hair = hair_next;
- }
- }
- }
-
- return hair;
-}
-
-void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
-{
- /* Free any evaluated data and restore original data. */
- BKE_object_free_derived_caches(object);
-
- /* Evaluate modifiers. */
- Hair *hair = static_cast<Hair *>(object->data);
- Hair *hair_eval = hair_evaluate_modifiers(depsgraph, scene, object, hair);
-
- /* Assign evaluated object. */
- const bool is_owned = (hair != hair_eval);
- BKE_object_eval_assign_data(object, &hair_eval->id, is_owned);
-}
-
-/* Draw Cache */
-
-void (*BKE_hair_batch_cache_dirty_tag_cb)(Hair *hair, int mode) = nullptr;
-void (*BKE_hair_batch_cache_free_cb)(Hair *hair) = nullptr;
-
-void BKE_hair_batch_cache_dirty_tag(Hair *hair, int mode)
-{
- if (hair->batch_cache) {
- BKE_hair_batch_cache_dirty_tag_cb(hair, mode);
- }
-}
-
-void BKE_hair_batch_cache_free(Hair *hair)
-{
- if (hair->batch_cache) {
- BKE_hair_batch_cache_free_cb(hair);
- }
-}
diff --git a/source/blender/blenkernel/intern/idprop_serialize.cc b/source/blender/blenkernel/intern/idprop_serialize.cc
index 289bc39f4b5..08a7f13b806 100644
--- a/source/blender/blenkernel/intern/idprop_serialize.cc
+++ b/source/blender/blenkernel/intern/idprop_serialize.cc
@@ -29,7 +29,7 @@ using namespace blender::io::serialize;
/* Forward declarations */
class IDPropertySerializer;
-class DictionaryEntryParser;
+struct DictionaryEntryParser;
static IDProperty *idprop_from_value(const DictionaryValue &value);
static const IDPropertySerializer &serializer_for(eIDPropertyType property_type);
static const IDPropertySerializer &serializer_for(StringRef idprop_typename);
@@ -304,7 +304,7 @@ class IDPStringSerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> entry_to_idprop(
DictionaryEntryParser &entry_reader) const override
{
- BLI_assert(entry_reader.get_type().value() == IDP_STRING);
+ BLI_assert(*(entry_reader.get_type()) == IDP_STRING);
std::optional<std::string> name = entry_reader.get_name();
if (!name.has_value()) {
return nullptr;
@@ -344,7 +344,7 @@ class IDPIntSerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> entry_to_idprop(
DictionaryEntryParser &entry_reader) const override
{
- BLI_assert(entry_reader.get_type().value() == IDP_INT);
+ BLI_assert(*(entry_reader.get_type()) == IDP_INT);
std::optional<std::string> name = entry_reader.get_name();
if (!name.has_value()) {
return nullptr;
@@ -384,7 +384,7 @@ class IDPFloatSerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> entry_to_idprop(
DictionaryEntryParser &entry_reader) const override
{
- BLI_assert(entry_reader.get_type().value() == IDP_FLOAT);
+ BLI_assert(*(entry_reader.get_type()) == IDP_FLOAT);
std::optional<std::string> name = entry_reader.get_name();
if (!name.has_value()) {
return nullptr;
@@ -424,7 +424,7 @@ class IDPDoubleSerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> entry_to_idprop(
DictionaryEntryParser &entry_reader) const override
{
- BLI_assert(entry_reader.get_type().value() == IDP_DOUBLE);
+ BLI_assert(*(entry_reader.get_type()) == IDP_DOUBLE);
std::optional<std::string> name = entry_reader.get_name();
if (!name.has_value()) {
return nullptr;
@@ -502,7 +502,7 @@ class IDPArraySerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> entry_to_idprop(
DictionaryEntryParser &entry_reader) const override
{
- BLI_assert(entry_reader.get_type().value() == IDP_ARRAY);
+ BLI_assert(*(entry_reader.get_type()) == IDP_ARRAY);
std::optional<eIDPropertyType> property_subtype = entry_reader.get_subtype();
if (!property_subtype.has_value()) {
return nullptr;
@@ -556,8 +556,8 @@ class IDPArraySerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> idprop_array_int_from_value(
DictionaryEntryParser &entry_reader) const
{
- BLI_assert(entry_reader.get_type().value() == IDP_ARRAY);
- BLI_assert(entry_reader.get_subtype().value() == IDP_INT);
+ BLI_assert(*(entry_reader.get_type()) == IDP_ARRAY);
+ BLI_assert(*(entry_reader.get_subtype()) == IDP_INT);
std::optional<std::string> name = entry_reader.get_name();
if (!name.has_value()) {
return nullptr;
@@ -572,8 +572,8 @@ class IDPArraySerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> idprop_array_float_from_value(
DictionaryEntryParser &entry_reader) const
{
- BLI_assert(entry_reader.get_type().value() == IDP_ARRAY);
- BLI_assert(entry_reader.get_subtype().value() == IDP_FLOAT);
+ BLI_assert(*(entry_reader.get_type()) == IDP_ARRAY);
+ BLI_assert(*(entry_reader.get_subtype()) == IDP_FLOAT);
std::optional<std::string> name = entry_reader.get_name();
if (!name.has_value()) {
return nullptr;
@@ -588,8 +588,8 @@ class IDPArraySerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> idprop_array_double_from_value(
DictionaryEntryParser &entry_reader) const
{
- BLI_assert(entry_reader.get_type().value() == IDP_ARRAY);
- BLI_assert(entry_reader.get_subtype().value() == IDP_DOUBLE);
+ BLI_assert(*(entry_reader.get_type()) == IDP_ARRAY);
+ BLI_assert(*(entry_reader.get_subtype()) == IDP_DOUBLE);
std::optional<std::string> name = entry_reader.get_name();
if (!name.has_value()) {
return nullptr;
@@ -639,7 +639,7 @@ class IDPGroupSerializer : public IDPropertySerializer {
std::unique_ptr<IDProperty, IDPropertyDeleter> entry_to_idprop(
DictionaryEntryParser &entry_reader) const override
{
- BLI_assert(entry_reader.get_type().value() == IDP_GROUP);
+ BLI_assert(*(entry_reader.get_type()) == IDP_GROUP);
std::optional<std::string> name = entry_reader.get_name();
if (!name.has_value()) {
return nullptr;
@@ -796,7 +796,7 @@ static IDProperty *idprop_from_value(const DictionaryValue &value)
return nullptr;
}
- const IDPropertySerializer &serializer = serializer_for(property_type.value());
+ const IDPropertySerializer &serializer = serializer_for(*property_type);
return serializer.entry_to_idprop(entry_reader).release();
}
diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c
index e6fd6c14d42..98bbd920615 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -15,9 +15,6 @@
*
* The Original Code is Copyright (C) 2005 by the Blender Foundation.
* All rights reserved.
- * Modifier stack implementation.
- *
- * BKE_modifier.h contains the function prototypes for this file.
*/
/** \file
@@ -110,7 +107,7 @@ static void id_type_init(void)
INIT_TYPE(ID_CF);
INIT_TYPE(ID_WS);
INIT_TYPE(ID_LP);
- INIT_TYPE(ID_HA);
+ INIT_TYPE(ID_CV);
INIT_TYPE(ID_PT);
INIT_TYPE(ID_VO);
INIT_TYPE(ID_SIM);
@@ -237,7 +234,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(CU);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);
- CASE_IDFILTER(HA);
+ CASE_IDFILTER(CV);
CASE_IDFILTER(IM);
CASE_IDFILTER(LA);
CASE_IDFILTER(LS);
@@ -286,7 +283,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter)
CASE_IDFILTER(CU);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);
- CASE_IDFILTER(HA);
+ CASE_IDFILTER(CV);
CASE_IDFILTER(IM);
CASE_IDFILTER(LA);
CASE_IDFILTER(LS);
@@ -334,7 +331,7 @@ int BKE_idtype_idcode_to_index(const short idcode)
CASE_IDINDEX(CU);
CASE_IDINDEX(GD);
CASE_IDINDEX(GR);
- CASE_IDINDEX(HA);
+ CASE_IDINDEX(CV);
CASE_IDINDEX(IM);
CASE_IDINDEX(IP);
CASE_IDINDEX(KE);
@@ -393,7 +390,7 @@ short BKE_idtype_idcode_from_index(const int index)
CASE_IDCODE(CU);
CASE_IDCODE(GD);
CASE_IDCODE(GR);
- CASE_IDCODE(HA);
+ CASE_IDCODE(CV);
CASE_IDCODE(IM);
CASE_IDCODE(IP);
CASE_IDCODE(KE);
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 23b1054f814..040257fe976 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -121,7 +121,7 @@ static void image_init(Image *ima, short source, short type);
static void image_free_packedfiles(Image *ima);
static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src);
-/* Reset runtime image fields when datablock is being initialized. */
+/* Reset runtime image fields when data-block is being initialized. */
static void image_runtime_reset(struct Image *image)
{
memset(&image->runtime, 0, sizeof(image->runtime));
@@ -129,11 +129,27 @@ static void image_runtime_reset(struct Image *image)
BLI_mutex_init(image->runtime.cache_mutex);
}
-/* Reset runtime image fields when datablock is being copied. */
+/* Reset runtime image fields when data-block is being copied. */
static void image_runtime_reset_on_copy(struct Image *image)
{
image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex");
BLI_mutex_init(image->runtime.cache_mutex);
+
+ image->runtime.partial_update_register = NULL;
+ image->runtime.partial_update_user = NULL;
+}
+
+static void image_runtime_free_data(struct Image *image)
+{
+ BLI_mutex_end(image->runtime.cache_mutex);
+ MEM_freeN(image->runtime.cache_mutex);
+ image->runtime.cache_mutex = NULL;
+
+ if (image->runtime.partial_update_user != NULL) {
+ BKE_image_partial_update_free(image->runtime.partial_update_user);
+ image->runtime.partial_update_user = NULL;
+ }
+ BKE_image_partial_update_register_free(image);
}
static void image_init_data(ID *id)
@@ -213,10 +229,8 @@ static void image_free_data(ID *id)
BKE_previewimg_free(&image->preview);
BLI_freelistN(&image->tiles);
- BLI_freelistN(&image->gpu_refresh_areas);
- BLI_mutex_end(image->runtime.cache_mutex);
- MEM_freeN(image->runtime.cache_mutex);
+ image_runtime_free_data(image);
}
static void image_foreach_cache(ID *id,
@@ -321,7 +335,8 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres
ima->cache = NULL;
ima->gpuflag = 0;
BLI_listbase_clear(&ima->anims);
- BLI_listbase_clear(&ima->gpu_refresh_areas);
+ ima->runtime.partial_update_register = NULL;
+ ima->runtime.partial_update_user = NULL;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) {
@@ -401,7 +416,6 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id)
ima->lastused = 0;
ima->gpuflag = 0;
- BLI_listbase_clear(&ima->gpu_refresh_areas);
image_runtime_reset(ima);
}
@@ -3553,7 +3567,7 @@ static void image_tag_frame_recalc(Image *ima, ID *iuser_id, ImageUser *iuser, v
iuser->flag |= IMA_NEED_FRAME_RECALC;
if (iuser_id) {
- /* Must copy image user changes to CoW datablock. */
+ /* Must copy image user changes to CoW data-block. */
DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE);
}
}
@@ -3568,7 +3582,7 @@ static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *c
image_update_views_format(ima, iuser);
}
if (iuser_id) {
- /* Must copy image user changes to CoW datablock. */
+ /* Must copy image user changes to CoW data-block. */
DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE);
}
}
@@ -5773,7 +5787,7 @@ static void image_user_id_has_animation(Image *ima,
bool BKE_image_user_id_has_animation(ID *id)
{
/* For the dependency graph, this does not consider nested node
- * trees as these are handled as their own datablock. */
+ * trees as these are handled as their own data-block. */
bool has_animation = false;
bool skip_nested_nodes = true;
image_walk_id_all_users(id, skip_nested_nodes, &has_animation, image_user_id_has_animation);
diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc
index c82de02e52a..eaee1fd2c30 100644
--- a/source/blender/blenkernel/intern/image_gpu.cc
+++ b/source/blender/blenkernel/intern/image_gpu.cc
@@ -38,6 +38,7 @@
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_image_partial_update.hh"
#include "BKE_main.h"
#include "GPU_capabilities.h"
@@ -46,6 +47,10 @@
#include "PIL_time.h"
+using namespace blender::bke::image::partial_update;
+
+extern "C" {
+
/* Prototypes. */
static void gpu_free_unused_buffers();
static void image_free_gpu(Image *ima, const bool immediate);
@@ -53,14 +58,6 @@ static void image_free_gpu_limited_scale(Image *ima);
static void image_update_gputexture_ex(
Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h);
-/* Internal structs. */
-#define IMA_PARTIAL_REFRESH_TILE_SIZE 256
-struct ImagePartialRefresh {
- struct ImagePartialRefresh *next, *prev;
- int tile_x;
- int tile_y;
-};
-
bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf)
{
if (image) {
@@ -337,6 +334,48 @@ static void image_update_reusable_textures(Image *ima,
}
}
+static void image_gpu_texture_partial_update_changes_available(
+ Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes)
+{
+ while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
+ const int tile_offset_x = changes.changed_region.region.xmin;
+ const int tile_offset_y = changes.changed_region.region.ymin;
+ const int tile_width = min_ii(changes.tile_data.tile_buffer->x,
+ BLI_rcti_size_x(&changes.changed_region.region));
+ const int tile_height = min_ii(changes.tile_data.tile_buffer->y,
+ BLI_rcti_size_y(&changes.changed_region.region));
+ image_update_gputexture_ex(image,
+ changes.tile_data.tile,
+ changes.tile_data.tile_buffer,
+ tile_offset_x,
+ tile_offset_y,
+ tile_width,
+ tile_height);
+ }
+}
+
+static void image_gpu_texture_try_partial_update(Image *image, ImageUser *iuser)
+{
+ PartialUpdateChecker<ImageTileData> checker(image, iuser, image->runtime.partial_update_user);
+ PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes();
+ switch (changes.get_result_code()) {
+ case ePartialUpdateCollectResult::FullUpdateNeeded: {
+ image_free_gpu(image, true);
+ break;
+ }
+
+ case ePartialUpdateCollectResult::PartialChangesDetected: {
+ image_gpu_texture_partial_update_changes_available(image, changes);
+ break;
+ }
+
+ case ePartialUpdateCollectResult::NoChangesDetected: {
+ /* GPUTextures are up to date. */
+ break;
+ }
+ }
+}
+
static GPUTexture *image_get_gpu_texture(Image *ima,
ImageUser *iuser,
ImBuf *ibuf,
@@ -370,31 +409,20 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
}
#undef GPU_FLAGS_TO_CHECK
- /* Check if image has been updated and tagged to be updated (full or partial). */
- ImageTile *tile = BKE_image_get_tile(ima, 0);
- if (((ima->gpuflag & IMA_GPU_REFRESH) != 0) ||
- ((ibuf == nullptr || tile == nullptr) && ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) {
- image_free_gpu(ima, true);
- BLI_freelistN(&ima->gpu_refresh_areas);
- ima->gpuflag &= ~(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH);
- }
- else if (ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) {
- BLI_assert(ibuf);
- BLI_assert(tile);
- ImagePartialRefresh *refresh_area;
- while ((
- refresh_area = static_cast<ImagePartialRefresh *>(BLI_pophead(&ima->gpu_refresh_areas)))) {
- const int tile_offset_x = refresh_area->tile_x * IMA_PARTIAL_REFRESH_TILE_SIZE;
- const int tile_offset_y = refresh_area->tile_y * IMA_PARTIAL_REFRESH_TILE_SIZE;
- const int tile_width = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->x - tile_offset_x);
- const int tile_height = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->y - tile_offset_y);
- image_update_gputexture_ex(
- ima, tile, ibuf, tile_offset_x, tile_offset_y, tile_width, tile_height);
- MEM_freeN(refresh_area);
- }
- ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH;
+ /* TODO(jbakker): We should replace the IMA_GPU_REFRESH flag with a call to
+ * BKE_image-partial_update_mark_full_update. Although the flag is quicker it leads to double
+ * administration. */
+ if ((ima->gpuflag & IMA_GPU_REFRESH) != 0) {
+ BKE_image_partial_update_mark_full_update(ima);
+ ima->gpuflag &= ~IMA_GPU_REFRESH;
+ }
+
+ if (ima->runtime.partial_update_user == nullptr) {
+ ima->runtime.partial_update_user = BKE_image_partial_update_create(ima);
}
+ image_gpu_texture_try_partial_update(ima, iuser);
+
/* Tag as in active use for garbage collector. */
BKE_image_tag_time(ima);
@@ -417,6 +445,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
/* Check if we have a valid image. If not, we return a dummy
* texture with zero bind-code so we don't keep trying. */
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
if (tile == nullptr) {
*tex = image_gpu_texture_error_create(textarget);
return *tex;
@@ -427,8 +456,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
if (ibuf_intern == nullptr) {
ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, nullptr);
if (ibuf_intern == nullptr) {
- *tex = image_gpu_texture_error_create(textarget);
- return *tex;
+ return image_gpu_texture_error_create(textarget);
}
}
@@ -477,15 +505,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
break;
}
- /* if `ibuf` was given, we don't own the `ibuf_intern` */
- if (ibuf == nullptr) {
- BKE_image_release_ibuf(ima, ibuf_intern, nullptr);
- }
-
if (*tex) {
GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
}
+ if (ibuf != ibuf_intern) {
+ BKE_image_release_ibuf(ima, ibuf_intern, nullptr);
+ }
+
return *tex;
}
@@ -903,87 +930,29 @@ static void image_update_gputexture_ex(
void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
{
+ ImageTile *image_tile = BKE_image_get_tile_from_iuser(ima, iuser);
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, nullptr);
- ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
-
- if ((ibuf == nullptr) || (w == 0) || (h == 0)) {
- /* Full reload of texture. */
- BKE_image_free_gputextures(ima);
- }
- image_update_gputexture_ex(ima, tile, ibuf, x, y, w, h);
+ BKE_image_update_gputexture_delayed(ima, image_tile, ibuf, x, y, w, h);
BKE_image_release_ibuf(ima, ibuf, nullptr);
}
-void BKE_image_update_gputexture_delayed(
- struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h)
+void BKE_image_update_gputexture_delayed(struct Image *ima,
+ struct ImageTile *image_tile,
+ struct ImBuf *ibuf,
+ int x,
+ int y,
+ int w,
+ int h)
{
/* Check for full refresh. */
- if (ibuf && x == 0 && y == 0 && w == ibuf->x && h == ibuf->y) {
- ima->gpuflag |= IMA_GPU_REFRESH;
- }
- /* Check if we can promote partial refresh to a full refresh. */
- if ((ima->gpuflag & (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) ==
- (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) {
- ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH;
- BLI_freelistN(&ima->gpu_refresh_areas);
- }
- /* Image is already marked for complete refresh. */
- if (ima->gpuflag & IMA_GPU_REFRESH) {
- return;
- }
-
- /* Schedule the tiles that covers the requested area. */
- const int start_tile_x = x / IMA_PARTIAL_REFRESH_TILE_SIZE;
- const int start_tile_y = y / IMA_PARTIAL_REFRESH_TILE_SIZE;
- const int end_tile_x = (x + w) / IMA_PARTIAL_REFRESH_TILE_SIZE;
- const int end_tile_y = (y + h) / IMA_PARTIAL_REFRESH_TILE_SIZE;
- const int num_tiles_x = (end_tile_x + 1) - (start_tile_x);
- const int num_tiles_y = (end_tile_y + 1) - (start_tile_y);
- const int num_tiles = num_tiles_x * num_tiles_y;
- const bool allocate_on_heap = BLI_BITMAP_SIZE(num_tiles) > 16;
- BLI_bitmap *requested_tiles = nullptr;
- if (allocate_on_heap) {
- requested_tiles = BLI_BITMAP_NEW(num_tiles, __func__);
+ if (ibuf != nullptr && ima->source != IMA_SRC_TILED && x == 0 && y == 0 && w == ibuf->x &&
+ h == ibuf->y) {
+ BKE_image_partial_update_mark_full_update(ima);
}
else {
- requested_tiles = BLI_BITMAP_NEW_ALLOCA(num_tiles);
- }
-
- /* Mark the tiles that have already been requested. They don't need to be requested again. */
- int num_tiles_not_scheduled = num_tiles;
- LISTBASE_FOREACH (ImagePartialRefresh *, area, &ima->gpu_refresh_areas) {
- if (area->tile_x < start_tile_x || area->tile_x > end_tile_x || area->tile_y < start_tile_y ||
- area->tile_y > end_tile_y) {
- continue;
- }
- int requested_tile_index = (area->tile_x - start_tile_x) +
- (area->tile_y - start_tile_y) * num_tiles_x;
- BLI_BITMAP_ENABLE(requested_tiles, requested_tile_index);
- num_tiles_not_scheduled--;
- if (num_tiles_not_scheduled == 0) {
- break;
- }
- }
-
- /* Schedule the tiles that aren't requested yet. */
- if (num_tiles_not_scheduled) {
- int tile_index = 0;
- for (int tile_y = start_tile_y; tile_y <= end_tile_y; tile_y++) {
- for (int tile_x = start_tile_x; tile_x <= end_tile_x; tile_x++) {
- if (!BLI_BITMAP_TEST_BOOL(requested_tiles, tile_index)) {
- ImagePartialRefresh *area = static_cast<ImagePartialRefresh *>(
- MEM_mallocN(sizeof(ImagePartialRefresh), __func__));
- area->tile_x = tile_x;
- area->tile_y = tile_y;
- BLI_addtail(&ima->gpu_refresh_areas, area);
- }
- tile_index++;
- }
- }
- ima->gpuflag |= IMA_GPU_PARTIAL_REFRESH;
- }
- if (allocate_on_heap) {
- MEM_freeN(requested_tiles);
+ rcti dirty_region;
+ BLI_rcti_init(&dirty_region, x, x + w, y, y + h);
+ BKE_image_partial_update_mark_region(ima, image_tile, ibuf, &dirty_region);
}
}
@@ -1016,3 +985,4 @@ void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap)
}
/** \} */
+}
diff --git a/source/blender/blenkernel/intern/image_partial_update.cc b/source/blender/blenkernel/intern/image_partial_update.cc
new file mode 100644
index 00000000000..876e5276e5a
--- /dev/null
+++ b/source/blender/blenkernel/intern/image_partial_update.cc
@@ -0,0 +1,599 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+/**
+ * \file image_gpu_partial_update.cc
+ * \ingroup bke
+ *
+ * To reduce the overhead of image processing this file contains a mechanism to detect areas of the
+ * image that are changed. These areas are organized in chunks. Changes that happen over time are
+ * organized in changesets.
+ *
+ * A common use case is to update #GPUTexture for drawing where only that part is uploaded that
+ * only changed.
+ *
+ * Usage:
+ *
+ * ```
+ * Image *image = ...;
+ * ImBuf *image_buffer = ...;
+ *
+ * // Partial_update_user should be kept for the whole session where the changes needs to be
+ * // tracked. Keep this instance alive as long as you need to track image changes.
+ *
+ * PartialUpdateUser *partial_update_user = BKE_image_partial_update_create(image);
+ *
+ * ...
+ *
+ * switch (BKE_image_partial_update_collect_changes(image, image_buffer))
+ * {
+ * case ePartialUpdateCollectResult::FullUpdateNeeded:
+ * // Unable to do partial updates. Perform a full update.
+ * break;
+ * case ePartialUpdateCollectResult::PartialChangesDetected:
+ * PartialUpdateRegion change;
+ * while (BKE_image_partial_update_get_next_change(partial_update_user, &change) ==
+ * ePartialUpdateIterResult::ChangeAvailable){
+ * // Do something with the change.
+ * }
+ * case ePartialUpdateCollectResult::NoChangesDetected:
+ * break;
+ * }
+ *
+ * ...
+ *
+ * // Free partial_update_user.
+ * BKE_image_partial_update_free(partial_update_user);
+ *
+ * ```
+ */
+
+#include <optional>
+
+#include "BKE_image.h"
+#include "BKE_image_partial_update.hh"
+
+#include "DNA_image_types.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "BLI_vector.hh"
+
+namespace blender::bke::image::partial_update {
+
+/** \brief Size of chunks to track changes. */
+constexpr int CHUNK_SIZE = 256;
+
+/**
+ * \brief Max number of changesets to keep in history.
+ *
+ * A higher number would need more memory and processing
+ * to calculate a changeset, but would lead to do partial updates for requests that don't happen
+ * every frame.
+ *
+ * A to small number would lead to more full updates when changes couldn't be reconstructed from
+ * the available history.
+ */
+constexpr int MAX_HISTORY_LEN = 4;
+
+/**
+ * \brief get the chunk number for the give pixel coordinate.
+ *
+ * As chunks are squares the this member can be used for both x and y axis.
+ */
+static int chunk_number_for_pixel(int pixel_offset)
+{
+ int chunk_offset = pixel_offset / CHUNK_SIZE;
+ if (pixel_offset < 0) {
+ chunk_offset -= 1;
+ }
+ return chunk_offset;
+}
+
+struct PartialUpdateUserImpl;
+struct PartialUpdateRegisterImpl;
+
+/**
+ * Wrap PartialUpdateUserImpl to its C-struct (PartialUpdateUser).
+ */
+static struct PartialUpdateUser *wrap(PartialUpdateUserImpl *user)
+{
+ return static_cast<struct PartialUpdateUser *>(static_cast<void *>(user));
+}
+
+/**
+ * Unwrap the PartialUpdateUser C-struct to its CPP counterpart (PartialUpdateUserImpl).
+ */
+static PartialUpdateUserImpl *unwrap(struct PartialUpdateUser *user)
+{
+ return static_cast<PartialUpdateUserImpl *>(static_cast<void *>(user));
+}
+
+/**
+ * Wrap PartialUpdateRegisterImpl to its C-struct (PartialUpdateRegister).
+ */
+static struct PartialUpdateRegister *wrap(PartialUpdateRegisterImpl *partial_update_register)
+{
+ return static_cast<struct PartialUpdateRegister *>(static_cast<void *>(partial_update_register));
+}
+
+/**
+ * Unwrap the PartialUpdateRegister C-struct to its CPP counterpart (PartialUpdateRegisterImpl).
+ */
+static PartialUpdateRegisterImpl *unwrap(struct PartialUpdateRegister *partial_update_register)
+{
+ return static_cast<PartialUpdateRegisterImpl *>(static_cast<void *>(partial_update_register));
+}
+
+using TileNumber = int32_t;
+using ChangesetID = int64_t;
+constexpr ChangesetID UnknownChangesetID = -1;
+
+struct PartialUpdateUserImpl {
+ /** \brief last changeset id that was seen by this user. */
+ ChangesetID last_changeset_id = UnknownChangesetID;
+
+ /** \brief regions that have been updated. */
+ Vector<PartialUpdateRegion> updated_regions;
+
+#ifdef NDEBUG
+ /** \brief reference to image to validate correct API usage. */
+ const void *debug_image_;
+#endif
+
+ /**
+ * \brief Clear the list of updated regions.
+ *
+ * Updated regions should be cleared at the start of #BKE_image_partial_update_collect_changes so
+ * the
+ */
+ void clear_updated_regions()
+ {
+ updated_regions.clear();
+ }
+};
+
+/**
+ * \brief Dirty chunks of an ImageTile.
+ *
+ * Internally dirty tiles are grouped together in change sets to make sure that the correct
+ * answer can be built for different users reducing the amount of merges.
+ */
+struct TileChangeset {
+ private:
+ /** \brief Dirty flag for each chunk. */
+ std::vector<bool> chunk_dirty_flags_;
+ /** \brief are there dirty/ */
+ bool has_dirty_chunks_ = false;
+
+ public:
+ /** \brief Width of the tile in pixels. */
+ int tile_width;
+ /** \brief Height of the tile in pixels. */
+ int tile_height;
+ /** \brief Number of chunks along the x-axis. */
+ int chunk_x_len;
+ /** \brief Number of chunks along the y-axis. */
+ int chunk_y_len;
+
+ TileNumber tile_number;
+
+ void clear()
+ {
+ init_chunks(chunk_x_len, chunk_y_len);
+ }
+
+ /**
+ * \brief Update the resolution of the tile.
+ *
+ * \returns true: resolution has been updated.
+ * false: resolution was unchanged.
+ */
+ bool update_resolution(const ImBuf *image_buffer)
+ {
+ if (tile_width == image_buffer->x && tile_height == image_buffer->y) {
+ return false;
+ }
+
+ tile_width = image_buffer->x;
+ tile_height = image_buffer->y;
+
+ int chunk_x_len = tile_width / CHUNK_SIZE;
+ int chunk_y_len = tile_height / CHUNK_SIZE;
+ init_chunks(chunk_x_len, chunk_y_len);
+ return true;
+ }
+
+ void mark_region(const rcti *updated_region)
+ {
+ int start_x_chunk = chunk_number_for_pixel(updated_region->xmin);
+ int end_x_chunk = chunk_number_for_pixel(updated_region->xmax - 1);
+ int start_y_chunk = chunk_number_for_pixel(updated_region->ymin);
+ int end_y_chunk = chunk_number_for_pixel(updated_region->ymax - 1);
+
+ /* Clamp tiles to tiles in image. */
+ start_x_chunk = max_ii(0, start_x_chunk);
+ start_y_chunk = max_ii(0, start_y_chunk);
+ end_x_chunk = min_ii(chunk_x_len - 1, end_x_chunk);
+ end_y_chunk = min_ii(chunk_y_len - 1, end_y_chunk);
+
+ /* Early exit when no tiles need to be updated. */
+ if (start_x_chunk >= chunk_x_len) {
+ return;
+ }
+ if (start_y_chunk >= chunk_y_len) {
+ return;
+ }
+ if (end_x_chunk < 0) {
+ return;
+ }
+ if (end_y_chunk < 0) {
+ return;
+ }
+
+ mark_chunks_dirty(start_x_chunk, start_y_chunk, end_x_chunk, end_y_chunk);
+ }
+
+ void mark_chunks_dirty(int start_x_chunk, int start_y_chunk, int end_x_chunk, int end_y_chunk)
+ {
+ for (int chunk_y = start_y_chunk; chunk_y <= end_y_chunk; chunk_y++) {
+ for (int chunk_x = start_x_chunk; chunk_x <= end_x_chunk; chunk_x++) {
+ int chunk_index = chunk_y * chunk_x_len + chunk_x;
+ chunk_dirty_flags_[chunk_index] = true;
+ }
+ }
+ has_dirty_chunks_ = true;
+ }
+
+ bool has_dirty_chunks() const
+ {
+ return has_dirty_chunks_;
+ }
+
+ void init_chunks(int chunk_x_len_, int chunk_y_len_)
+ {
+ chunk_x_len = chunk_x_len_;
+ chunk_y_len = chunk_y_len_;
+ const int chunk_len = chunk_x_len * chunk_y_len;
+ const int previous_chunk_len = chunk_dirty_flags_.size();
+
+ chunk_dirty_flags_.resize(chunk_len);
+ /* Fast exit. When the changeset was already empty no need to
+ * re-initialize the chunk_validity. */
+ if (!has_dirty_chunks()) {
+ return;
+ }
+ for (int index = 0; index < min_ii(chunk_len, previous_chunk_len); index++) {
+ chunk_dirty_flags_[index] = false;
+ }
+ has_dirty_chunks_ = false;
+ }
+
+ /** \brief Merge the given changeset into the receiver. */
+ void merge(const TileChangeset &other)
+ {
+ BLI_assert(chunk_x_len == other.chunk_x_len);
+ BLI_assert(chunk_y_len == other.chunk_y_len);
+ const int chunk_len = chunk_x_len * chunk_y_len;
+
+ for (int chunk_index = 0; chunk_index < chunk_len; chunk_index++) {
+ chunk_dirty_flags_[chunk_index] = chunk_dirty_flags_[chunk_index] |
+ other.chunk_dirty_flags_[chunk_index];
+ }
+ has_dirty_chunks_ |= other.has_dirty_chunks_;
+ }
+
+ /** \brief has a chunk changed inside this changeset. */
+ bool is_chunk_dirty(int chunk_x, int chunk_y) const
+ {
+ const int chunk_index = chunk_y * chunk_x_len + chunk_x;
+ return chunk_dirty_flags_[chunk_index];
+ }
+};
+
+/** \brief Changeset keeping track of changes for an image */
+struct Changeset {
+ private:
+ Vector<TileChangeset> tiles;
+
+ public:
+ /** \brief Keep track if any of the tiles have dirty chunks. */
+ bool has_dirty_chunks;
+
+ /**
+ * \brief Retrieve the TileChangeset for the given ImageTile.
+ *
+ * When the TileChangeset isn't found, it will be added.
+ */
+ TileChangeset &operator[](const ImageTile *image_tile)
+ {
+ for (TileChangeset &tile_changeset : tiles) {
+ if (tile_changeset.tile_number == image_tile->tile_number) {
+ return tile_changeset;
+ }
+ }
+
+ TileChangeset tile_changeset;
+ tile_changeset.tile_number = image_tile->tile_number;
+ tiles.append_as(tile_changeset);
+
+ return tiles.last();
+ }
+
+ /** \brief Does this changeset contain data for the given tile. */
+ bool has_tile(const ImageTile *image_tile)
+ {
+ for (TileChangeset &tile_changeset : tiles) {
+ if (tile_changeset.tile_number == image_tile->tile_number) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** \brief Clear this changeset. */
+ void clear()
+ {
+ tiles.clear();
+ has_dirty_chunks = false;
+ }
+};
+
+/**
+ * \brief Partial update changes stored inside the image runtime.
+ *
+ * The PartialUpdateRegisterImpl will keep track of changes over time. Changes are groups inside
+ * TileChangesets.
+ */
+struct PartialUpdateRegisterImpl {
+ /** \brief changeset id of the first changeset kept in #history. */
+ ChangesetID first_changeset_id;
+ /** \brief changeset id of the top changeset kept in #history. */
+ ChangesetID last_changeset_id;
+
+ /** \brief history of changesets. */
+ Vector<Changeset> history;
+ /** \brief The current changeset. New changes will be added to this changeset. */
+ Changeset current_changeset;
+
+ void update_resolution(const ImageTile *image_tile, const ImBuf *image_buffer)
+ {
+ TileChangeset &tile_changeset = current_changeset[image_tile];
+ const bool has_dirty_chunks = tile_changeset.has_dirty_chunks();
+ const bool resolution_changed = tile_changeset.update_resolution(image_buffer);
+
+ if (has_dirty_chunks && resolution_changed && !history.is_empty()) {
+ mark_full_update();
+ }
+ }
+
+ void mark_full_update()
+ {
+ history.clear();
+ last_changeset_id++;
+ current_changeset.clear();
+ first_changeset_id = last_changeset_id;
+ }
+
+ void mark_region(const ImageTile *image_tile, const rcti *updated_region)
+ {
+ TileChangeset &tile_changeset = current_changeset[image_tile];
+ tile_changeset.mark_region(updated_region);
+ current_changeset.has_dirty_chunks |= tile_changeset.has_dirty_chunks();
+ }
+
+ void ensure_empty_changeset()
+ {
+ if (!current_changeset.has_dirty_chunks) {
+ /* No need to create a new changeset when previous changeset does not contain any dirty
+ * tiles. */
+ return;
+ }
+ commit_current_changeset();
+ limit_history();
+ }
+
+ /** \brief Move the current changeset to the history and resets the current changeset. */
+ void commit_current_changeset()
+ {
+ history.append_as(std::move(current_changeset));
+ current_changeset.clear();
+ last_changeset_id++;
+ }
+
+ /** \brief Limit the number of items in the changeset. */
+ void limit_history()
+ {
+ const int num_items_to_remove = max_ii(history.size() - MAX_HISTORY_LEN, 0);
+ if (num_items_to_remove == 0) {
+ return;
+ }
+ history.remove(0, num_items_to_remove);
+ first_changeset_id += num_items_to_remove;
+ }
+
+ /**
+ * /brief Check if data is available to construct the update tiles for the given
+ * changeset_id.
+ *
+ * The update tiles can be created when changeset id is between
+ */
+ bool can_construct(ChangesetID changeset_id)
+ {
+ return changeset_id >= first_changeset_id;
+ }
+
+ /**
+ * \brief collect all historic changes since a given changeset.
+ */
+ std::optional<TileChangeset> changed_tile_chunks_since(const ImageTile *image_tile,
+ const ChangesetID from_changeset)
+ {
+ std::optional<TileChangeset> changed_chunks = std::nullopt;
+ for (int index = from_changeset - first_changeset_id; index < history.size(); index++) {
+ if (!history[index].has_tile(image_tile)) {
+ continue;
+ }
+
+ TileChangeset &tile_changeset = history[index][image_tile];
+ if (!changed_chunks.has_value()) {
+ changed_chunks = std::make_optional<TileChangeset>();
+ changed_chunks->init_chunks(tile_changeset.chunk_x_len, tile_changeset.chunk_y_len);
+ changed_chunks->tile_number = image_tile->tile_number;
+ }
+
+ changed_chunks->merge(tile_changeset);
+ }
+ return changed_chunks;
+ }
+};
+
+static PartialUpdateRegister *image_partial_update_register_ensure(Image *image)
+{
+ if (image->runtime.partial_update_register == nullptr) {
+ PartialUpdateRegisterImpl *partial_update_register = MEM_new<PartialUpdateRegisterImpl>(
+ __func__);
+ image->runtime.partial_update_register = wrap(partial_update_register);
+ }
+ return image->runtime.partial_update_register;
+}
+
+ePartialUpdateCollectResult BKE_image_partial_update_collect_changes(Image *image,
+ PartialUpdateUser *user)
+{
+ PartialUpdateUserImpl *user_impl = unwrap(user);
+#ifdef NDEBUG
+ BLI_assert(image == user_impl->debug_image_);
+#endif
+
+ user_impl->clear_updated_regions();
+
+ PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image));
+ partial_updater->ensure_empty_changeset();
+
+ if (!partial_updater->can_construct(user_impl->last_changeset_id)) {
+ user_impl->last_changeset_id = partial_updater->last_changeset_id;
+ return ePartialUpdateCollectResult::FullUpdateNeeded;
+ }
+
+ /* Check if there are changes since last invocation for the user. */
+ if (user_impl->last_changeset_id == partial_updater->last_changeset_id) {
+ return ePartialUpdateCollectResult::NoChangesDetected;
+ }
+
+ /* Collect changed tiles. */
+ LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
+ std::optional<TileChangeset> changed_chunks = partial_updater->changed_tile_chunks_since(
+ tile, user_impl->last_changeset_id);
+ /* Check if chunks of this tile are dirty. */
+ if (!changed_chunks.has_value()) {
+ continue;
+ }
+ if (!changed_chunks->has_dirty_chunks()) {
+ continue;
+ }
+
+ /* Convert tiles in the changeset to rectangles that are dirty. */
+ for (int chunk_y = 0; chunk_y < changed_chunks->chunk_y_len; chunk_y++) {
+ for (int chunk_x = 0; chunk_x < changed_chunks->chunk_x_len; chunk_x++) {
+ if (!changed_chunks->is_chunk_dirty(chunk_x, chunk_y)) {
+ continue;
+ }
+
+ PartialUpdateRegion region;
+ region.tile_number = tile->tile_number;
+ BLI_rcti_init(&region.region,
+ chunk_x * CHUNK_SIZE,
+ (chunk_x + 1) * CHUNK_SIZE,
+ chunk_y * CHUNK_SIZE,
+ (chunk_y + 1) * CHUNK_SIZE);
+ user_impl->updated_regions.append_as(region);
+ }
+ }
+ }
+
+ user_impl->last_changeset_id = partial_updater->last_changeset_id;
+ return ePartialUpdateCollectResult::PartialChangesDetected;
+}
+
+ePartialUpdateIterResult BKE_image_partial_update_get_next_change(PartialUpdateUser *user,
+ PartialUpdateRegion *r_region)
+{
+ PartialUpdateUserImpl *user_impl = unwrap(user);
+ if (user_impl->updated_regions.is_empty()) {
+ return ePartialUpdateIterResult::Finished;
+ }
+ PartialUpdateRegion region = user_impl->updated_regions.pop_last();
+ *r_region = region;
+ return ePartialUpdateIterResult::ChangeAvailable;
+}
+
+} // namespace blender::bke::image::partial_update
+
+extern "C" {
+
+using namespace blender::bke::image::partial_update;
+
+// TODO(jbakker): cleanup parameter.
+struct PartialUpdateUser *BKE_image_partial_update_create(const struct Image *image)
+{
+ PartialUpdateUserImpl *user_impl = MEM_new<PartialUpdateUserImpl>(__func__);
+
+#ifdef NDEBUG
+ user_impl->debug_image_ = image;
+#else
+ UNUSED_VARS(image);
+#endif
+
+ return wrap(user_impl);
+}
+
+void BKE_image_partial_update_free(PartialUpdateUser *user)
+{
+ PartialUpdateUserImpl *user_impl = unwrap(user);
+ MEM_delete<PartialUpdateUserImpl>(user_impl);
+}
+
+/* --- Image side --- */
+
+void BKE_image_partial_update_register_free(Image *image)
+{
+ PartialUpdateRegisterImpl *partial_update_register = unwrap(
+ image->runtime.partial_update_register);
+ if (partial_update_register) {
+ MEM_delete<PartialUpdateRegisterImpl>(partial_update_register);
+ }
+ image->runtime.partial_update_register = nullptr;
+}
+
+void BKE_image_partial_update_mark_region(Image *image,
+ const ImageTile *image_tile,
+ const ImBuf *image_buffer,
+ const rcti *updated_region)
+{
+ PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image));
+ partial_updater->update_resolution(image_tile, image_buffer);
+ partial_updater->mark_region(image_tile, updated_region);
+}
+
+void BKE_image_partial_update_mark_full_update(Image *image)
+{
+ PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image));
+ partial_updater->mark_full_update();
+}
+}
diff --git a/source/blender/blenkernel/intern/image_partial_update_test.cc b/source/blender/blenkernel/intern/image_partial_update_test.cc
new file mode 100644
index 00000000000..bf12c27241e
--- /dev/null
+++ b/source/blender/blenkernel/intern/image_partial_update_test.cc
@@ -0,0 +1,393 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 by Blender Foundation.
+ */
+#include "testing/testing.h"
+
+#include "CLG_log.h"
+
+#include "BKE_appdir.h"
+#include "BKE_idtype.h"
+#include "BKE_image.h"
+#include "BKE_image_partial_update.hh"
+#include "BKE_main.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_moviecache.h"
+
+#include "DNA_image_types.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::bke::image::partial_update {
+
+constexpr float black_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+class ImagePartialUpdateTest : public testing::Test {
+ protected:
+ Main *bmain;
+ Image *image;
+ ImageTile *image_tile;
+ ImageUser image_user = {nullptr};
+ ImBuf *image_buffer;
+ PartialUpdateUser *partial_update_user;
+
+ private:
+ Image *create_test_image(int width, int height)
+ {
+ return BKE_image_add_generated(bmain,
+ width,
+ height,
+ "Test Image",
+ 32,
+ true,
+ IMA_GENTYPE_BLANK,
+ black_color,
+ false,
+ false,
+ false);
+ }
+
+ protected:
+ void SetUp() override
+ {
+ CLG_init();
+ BKE_idtype_init();
+ BKE_appdir_init();
+ IMB_init();
+
+ bmain = BKE_main_new();
+ /* Creating an image generates a memory-leak during tests. */
+ image = create_test_image(1024, 1024);
+ image_tile = BKE_image_get_tile(image, 0);
+ image_buffer = BKE_image_acquire_ibuf(image, nullptr, nullptr);
+
+ partial_update_user = BKE_image_partial_update_create(image);
+ }
+
+ void TearDown() override
+ {
+ BKE_image_release_ibuf(image, image_buffer, nullptr);
+ BKE_image_partial_update_free(partial_update_user);
+ BKE_main_free(bmain);
+
+ IMB_moviecache_destruct();
+ IMB_exit();
+ BKE_appdir_exit();
+ CLG_exit();
+ }
+};
+
+TEST_F(ImagePartialUpdateTest, mark_full_update)
+{
+ ePartialUpdateCollectResult result;
+ /* First tile should always return a full update. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
+ /* Second invoke should now detect no changes. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ /* Mark full update */
+ BKE_image_partial_update_mark_full_update(image);
+
+ /* Validate need full update followed by no changes. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+}
+
+TEST_F(ImagePartialUpdateTest, mark_single_tile)
+{
+ ePartialUpdateCollectResult result;
+ /* First tile should always return a full update. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
+ /* Second invoke should now detect no changes. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ /* Mark region. */
+ rcti region;
+ BLI_rcti_init(&region, 10, 20, 40, 50);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+
+ /* Partial Update should be available. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+
+ /* Check tiles. */
+ PartialUpdateRegion changed_region;
+ ePartialUpdateIterResult iter_result;
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
+ EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, &region), true);
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished);
+
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+}
+
+TEST_F(ImagePartialUpdateTest, mark_unconnected_tiles)
+{
+ ePartialUpdateCollectResult result;
+ /* First tile should always return a full update. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
+ /* Second invoke should now detect no changes. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ /* Mark region. */
+ rcti region_a;
+ BLI_rcti_init(&region_a, 10, 20, 40, 50);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region_a);
+ rcti region_b;
+ BLI_rcti_init(&region_b, 710, 720, 740, 750);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region_b);
+
+ /* Partial Update should be available. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+
+ /* Check tiles. */
+ PartialUpdateRegion changed_region;
+ ePartialUpdateIterResult iter_result;
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
+ EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, &region_b), true);
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
+ EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, &region_a), true);
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished);
+
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+}
+
+TEST_F(ImagePartialUpdateTest, donot_mark_outside_image)
+{
+ ePartialUpdateCollectResult result;
+ /* First tile should always return a full update. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
+ /* Second invoke should now detect no changes. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ /* Mark region. */
+ rcti region;
+ /* Axis. */
+ BLI_rcti_init(&region, -100, 0, 50, 100);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ BLI_rcti_init(&region, 1024, 1100, 50, 100);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ BLI_rcti_init(&region, 50, 100, -100, 0);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ BLI_rcti_init(&region, 50, 100, 1024, 1100);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ /* Diagonals. */
+ BLI_rcti_init(&region, -100, 0, -100, 0);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ BLI_rcti_init(&region, -100, 0, 1024, 1100);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ BLI_rcti_init(&region, 1024, 1100, -100, 0);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ BLI_rcti_init(&region, 1024, 1100, 1024, 1100);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+}
+
+TEST_F(ImagePartialUpdateTest, mark_inside_image)
+{
+ ePartialUpdateCollectResult result;
+ /* First tile should always return a full update. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
+ /* Second invoke should now detect no changes. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ /* Mark region. */
+ rcti region;
+ BLI_rcti_init(&region, 0, 1, 0, 1);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+ BLI_rcti_init(&region, 1023, 1024, 0, 1);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+ BLI_rcti_init(&region, 1023, 1024, 1023, 1024);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+ BLI_rcti_init(&region, 1023, 1024, 0, 1);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+}
+
+TEST_F(ImagePartialUpdateTest, sequential_mark_region)
+{
+ ePartialUpdateCollectResult result;
+ /* First tile should always return a full update. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
+ /* Second invoke should now detect no changes. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ {
+ /* Mark region. */
+ rcti region;
+ BLI_rcti_init(&region, 10, 20, 40, 50);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+
+ /* Partial Update should be available. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+
+ /* Check tiles. */
+ PartialUpdateRegion changed_region;
+ ePartialUpdateIterResult iter_result;
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
+ EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, &region), true);
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished);
+
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+ }
+
+ {
+ /* Mark different region. */
+ rcti region;
+ BLI_rcti_init(&region, 710, 720, 740, 750);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+
+ /* Partial Update should be available. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+
+ /* Check tiles. */
+ PartialUpdateRegion changed_region;
+ ePartialUpdateIterResult iter_result;
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
+ EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, &region), true);
+ iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
+ EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished);
+
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+ }
+}
+
+TEST_F(ImagePartialUpdateTest, mark_multiple_chunks)
+{
+ ePartialUpdateCollectResult result;
+ /* First tile should always return a full update. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
+ /* Second invoke should now detect no changes. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
+
+ /* Mark region. */
+ rcti region;
+ BLI_rcti_init(&region, 300, 700, 300, 700);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+
+ /* Partial Update should be available. */
+ result = BKE_image_partial_update_collect_changes(image, partial_update_user);
+ EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
+
+ /* Check tiles. */
+ PartialUpdateRegion changed_region;
+ int num_chunks_found = 0;
+ while (BKE_image_partial_update_get_next_change(partial_update_user, &changed_region) ==
+ ePartialUpdateIterResult::ChangeAvailable) {
+ BLI_rcti_isect(&changed_region.region, &region, nullptr);
+ num_chunks_found++;
+ }
+ EXPECT_EQ(num_chunks_found, 4);
+}
+
+TEST_F(ImagePartialUpdateTest, iterator)
+{
+ PartialUpdateChecker<NoTileData> checker(image, &image_user, partial_update_user);
+ /* First tile should always return a full update. */
+ PartialUpdateChecker<NoTileData>::CollectResult changes = checker.collect_changes();
+ EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::FullUpdateNeeded);
+ /* Second invoke should now detect no changes. */
+ changes = checker.collect_changes();
+ EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::NoChangesDetected);
+
+ /* Mark region. */
+ rcti region;
+ BLI_rcti_init(&region, 300, 700, 300, 700);
+ BKE_image_partial_update_mark_region(image, image_tile, image_buffer, &region);
+
+ /* Partial Update should be available. */
+ changes = checker.collect_changes();
+ EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::PartialChangesDetected);
+
+ /* Check tiles. */
+ int num_tiles_found = 0;
+ while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
+ BLI_rcti_isect(&changes.changed_region.region, &region, nullptr);
+ num_tiles_found++;
+ }
+ EXPECT_EQ(num_tiles_found, 4);
+}
+
+} // namespace blender::bke::image::partial_update
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 9e3cea40fb8..a59dd6f2e0e 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -1196,6 +1196,23 @@ static bool view_layer_objects_base_cache_validate(ViewLayer *UNUSED(view_layer)
}
#endif
+void BKE_layer_collection_doversion_2_80(const Scene *scene, ViewLayer *view_layer)
+{
+ LayerCollection *first_layer_collection = view_layer->layer_collections.first;
+ if (BLI_listbase_count_at_most(&view_layer->layer_collections, 2) > 1 ||
+ first_layer_collection->collection != scene->master_collection) {
+ /* In some cases (from older files) we do have a master collection, but no matching layer,
+ * instead all the children of the master collection have their layer collections in the
+ * viewlayer's list. This is not a valid situation, add a layer for the master collection and
+ * add all existing first-level layers as children of that new master layer. */
+ ListBase layer_collections = view_layer->layer_collections;
+ BLI_listbase_clear(&view_layer->layer_collections);
+ LayerCollection *master_layer_collection = layer_collection_add(&view_layer->layer_collections,
+ scene->master_collection);
+ master_layer_collection->layer_collections = layer_collections;
+ }
+}
+
void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
{
if (no_resync) {
@@ -1208,21 +1225,24 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
}
if (BLI_listbase_is_empty(&view_layer->layer_collections)) {
- /* In some cases (from older files) we do have a master collection, yet no matching layer.
- * Create the master one here, so that the rest of the code can work as expected. */
+ /* In some cases (from older files, or when creating a new ViewLayer from
+ * #BKE_view_layer_add), we do have a master collection, yet no matching layer. Create the
+ * master one here, so that the rest of the code can work as expected. */
layer_collection_add(&view_layer->layer_collections, scene->master_collection);
}
- else if (BLI_listbase_count_at_most(&view_layer->layer_collections, 2) > 1) {
- /* In some cases (from older files) we do have a master collection, but no matching layer,
- * instead all the children of the master collection have their layer collections in the
- * viewlayer's list. This is not a valid situation, add a layer for the master collection and
- * add all existing first-level layers as children of that new master layer. */
- ListBase layer_collections = view_layer->layer_collections;
- BLI_listbase_clear(&view_layer->layer_collections);
- LayerCollection *master_layer_collection = layer_collection_add(&view_layer->layer_collections,
- scene->master_collection);
- master_layer_collection->layer_collections = layer_collections;
+
+#ifndef NDEBUG
+ {
+ BLI_assert_msg(BLI_listbase_count_at_most(&view_layer->layer_collections, 2) == 1,
+ "ViewLayer's first level of children layer collections should always have "
+ "exactly one item");
+
+ LayerCollection *first_layer_collection = view_layer->layer_collections.first;
+ BLI_assert_msg(first_layer_collection->collection == scene->master_collection,
+ "ViewLayer's first layer collection should always be the one for the scene's "
+ "master collection");
}
+#endif
/* Free cache. */
MEM_SAFE_FREE(view_layer->object_bases_array);
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 692e27731c5..6c19c7a328b 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -454,38 +454,57 @@ static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id,
}
}
-void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags)
+void BKE_lib_id_make_local_generic_action_define(
+ struct Main *bmain, struct ID *id, int flags, bool *r_force_local, bool *r_force_copy)
{
- if (!ID_IS_LINKED(id)) {
- return;
- }
-
- const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
BLI_assert(force_copy == false || force_copy != force_local);
+ if (force_local || force_copy) {
+ /* Already set by caller code, nothing to do here. */
+ *r_force_local = force_local;
+ *r_force_copy = force_copy;
+ return;
+ }
+
+ const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
bool is_local = false, is_lib = false;
- /* - only lib users: do nothing (unless force_local is set)
- * - only local users: set flag
+ /* - no user (neither lib nor local): make local (happens e.g. with UI-used only data).
+ * - only lib users: do nothing (unless force_local is set)
+ * - only local users: make local
* - mixed: make copy
* In case we make a whole lib's content local,
* we always want to localize, and we skip remapping (done later).
*/
- if (!force_copy && !force_local) {
- BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib);
- if (lib_local || is_local) {
- if (!is_lib) {
- force_local = true;
- }
- else {
- force_copy = true;
- }
+ BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib);
+ if (!lib_local && !is_local && !is_lib) {
+ force_local = true;
+ }
+ else if (lib_local || is_local) {
+ if (!is_lib) {
+ force_local = true;
+ }
+ else {
+ force_copy = true;
}
}
+ *r_force_local = force_local;
+ *r_force_copy = force_copy;
+}
+
+void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags)
+{
+ if (!ID_IS_LINKED(id)) {
+ return;
+ }
+
+ bool force_local, force_copy;
+ BKE_lib_id_make_local_generic_action_define(bmain, id, flags, &force_local, &force_copy);
+
if (force_local) {
BKE_lib_id_clear_library_data(bmain, id, flags);
BKE_lib_id_expand_local(bmain, id, flags);
@@ -516,6 +535,7 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags)
}
}
+ const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
if (!lib_local) {
BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE);
}
@@ -1898,7 +1918,6 @@ void BKE_library_make_local(Main *bmain,
* but complicates slightly the pre-processing of relations between IDs at step 2... */
else if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) &&
ELEM(lib, NULL, id->lib) &&
- !(GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) &&
((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING))) {
BLI_linklist_prepend_arena(&todo_ids, id, linklist_mem);
id->tag |= LIB_TAG_DOIT;
@@ -1962,12 +1981,8 @@ void BKE_library_make_local(Main *bmain,
}
}
else {
- /* In this specific case, we do want to make ID local even if it has no local usage yet...
- * Note that for objects, we don't want proxy pointers to be cleared yet. This will happen
- * down the road in this function.
- */
- BKE_lib_id_make_local(
- bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
+ /* In this specific case, we do want to make ID local even if it has no local usage yet... */
+ BKE_lib_id_make_local(bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY);
if (id->newid) {
if (GS(id->newid->name) == ID_OB) {
@@ -2029,62 +2044,6 @@ void BKE_library_make_local(Main *bmain,
TIMEIT_VALUE_PRINT(make_local);
#endif
- /* Step 5: proxy 'remapping' hack. */
- for (LinkNode *it = copied_ids; it; it = it->next) {
- ID *id = it->link;
-
- /* Attempt to re-link copied proxy objects. This allows appending of an entire scene
- * from another blend file into this one, even when that blend file contains proxified
- * armatures that have local references. Since the proxified object needs to be linked
- * (not local), this will only work when the "Localize all" checkbox is disabled.
- * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */
- if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) {
- Object *ob = (Object *)id;
- Object *ob_new = (Object *)id->newid;
- bool is_local = false, is_lib = false;
-
- /* Proxies only work when the proxified object is linked-in from a library. */
- if (!ID_IS_LINKED(ob->proxy)) {
- CLOG_WARN(&LOG,
- "proxy object %s will lose its link to %s, because the "
- "proxified object is local.",
- id->newid->name,
- ob->proxy->id.name);
- continue;
- }
-
- BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib);
-
- /* We can only switch the proxy'ing to a made-local proxy if it is no longer
- * referred to from a library. Not checking for local use; if new local proxy
- * was not used locally would be a nasty bug! */
- if (is_local || is_lib) {
- CLOG_WARN(&LOG,
- "made-local proxy object %s will lose its link to %s, "
- "because the linked-in proxy is referenced (is_local=%i, is_lib=%i).",
- id->newid->name,
- ob->proxy->id.name,
- is_local,
- is_lib);
- }
- else {
- /* we can switch the proxy'ing from the linked-in to the made-local proxy.
- * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that
- * was already allocated by object_make_local() (which called BKE_object_copy). */
- ob_new->proxy = ob->proxy;
- ob_new->proxy_group = ob->proxy_group;
- ob_new->proxy_from = ob->proxy_from;
- ob_new->proxy->proxy_from = ob_new;
- ob->proxy = ob->proxy_from = ob->proxy_group = NULL;
- }
- }
- }
-
-#ifdef DEBUG_TIME
- printf("Step 5: Proxy 'remapping' hack: Done.\n");
- TIMEIT_VALUE_PRINT(make_local);
-#endif
-
/* This is probably more of a hack than something we should do here, but...
* Issue is, the whole copying + remapping done in complex cases above may leave pose-channels
* of armatures in complete invalid state (more precisely, the bone pointers of the
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index 6d2e89187f7..5db70d85e71 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -154,7 +154,10 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i
}
if (remap_editor_id_reference_cb) {
- remap_editor_id_reference_cb(id, NULL);
+ struct IDRemapper *remapper = BKE_id_remapper_create();
+ BKE_id_remapper_add(remapper, id, NULL);
+ remap_editor_id_reference_cb(remapper);
+ BKE_id_remapper_free(remapper);
}
}
@@ -262,8 +265,8 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
}
for (id = last_remapped_id->next; id; id = id->next) {
/* Will tag 'never NULL' users of this ID too.
- * Note that we cannot use BKE_libblock_unlink() here,
- * since it would ignore indirect (and proxy!)
+ *
+ * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect
* links, this can lead to nasty crashing here in second, actual deleting loop.
* Also, this will also flag users of deleted data that cannot be unlinked
* (object using deleted obdata, etc.), so that they also get deleted. */
@@ -292,32 +295,40 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
* Note that we go forward here, since we want to check dependencies before users
* (e.g. meshes before objects).
* Avoids to have to loop twice. */
+ struct IDRemapper *remapper = BKE_id_remapper_create();
for (i = 0; i < base_count; i++) {
ListBase *lb = lbarray[i];
ID *id, *id_next;
+ BKE_id_remapper_clear(remapper);
for (id = lb->first; id; id = id_next) {
id_next = id->next;
/* NOTE: in case we delete a library, we also delete all its datablocks! */
if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) {
id->tag |= tag;
-
- /* Will tag 'never NULL' users of this ID too.
- * Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect
- * (and proxy!) links, this can lead to nasty crashing here in second,
- * actual deleting loop.
- * Also, this will also flag users of deleted data that cannot be unlinked
- * (object using deleted obdata, etc.), so that they also get deleted. */
- BKE_libblock_remap_locked(bmain,
- id,
- NULL,
- (ID_REMAP_FLAG_NEVER_NULL_USAGE |
- ID_REMAP_FORCE_NEVER_NULL_USAGE |
- ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS));
+ BKE_id_remapper_add(remapper, id, NULL);
}
}
+
+ if (BKE_id_remapper_is_empty(remapper)) {
+ continue;
+ }
+
+ /* Will tag 'never NULL' users of this ID too.
+ *
+ * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect
+ * links, this can lead to nasty crashing here in second, actual deleting loop.
+ * Also, this will also flag users of deleted data that cannot be unlinked
+ * (object using deleted obdata, etc.), so that they also get deleted. */
+ BKE_libblock_remap_multiple_locked(bmain,
+ remapper,
+ (ID_REMAP_FLAG_NEVER_NULL_USAGE |
+ ID_REMAP_FORCE_NEVER_NULL_USAGE |
+ ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS));
}
+ BKE_id_remapper_free(remapper);
}
+
BKE_main_unlock(bmain);
/* In usual reversed order, such that all usage of a given ID, even 'never NULL' ones,
diff --git a/source/blender/blenkernel/intern/lib_id_remapper.cc b/source/blender/blenkernel/intern/lib_id_remapper.cc
new file mode 100644
index 00000000000..c1734c9826a
--- /dev/null
+++ b/source/blender/blenkernel/intern/lib_id_remapper.cc
@@ -0,0 +1,175 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2022 by Blender Foundation.
+ */
+
+#include "DNA_ID.h"
+
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_remap.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_map.hh"
+
+using IDTypeFilter = uint64_t;
+
+namespace blender::bke::id::remapper {
+struct IDRemapper {
+ private:
+ Map<ID *, ID *> mappings;
+ IDTypeFilter source_types = 0;
+
+ public:
+ void clear()
+ {
+ mappings.clear();
+ source_types = 0;
+ }
+
+ bool is_empty() const
+ {
+ return mappings.is_empty();
+ }
+
+ void add(ID *old_id, ID *new_id)
+ {
+ BLI_assert(old_id != nullptr);
+ BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name)));
+ mappings.add(old_id, new_id);
+ source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name));
+ }
+
+ bool contains_mappings_for_any(IDTypeFilter filter) const
+ {
+ return (source_types & filter) != 0;
+ }
+
+ IDRemapperApplyResult apply(ID **r_id_ptr, IDRemapperApplyOptions options) const
+ {
+ BLI_assert(r_id_ptr != nullptr);
+ if (*r_id_ptr == nullptr) {
+ return ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE;
+ }
+
+ if (!mappings.contains(*r_id_ptr)) {
+ return ID_REMAP_RESULT_SOURCE_UNAVAILABLE;
+ }
+
+ if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) {
+ id_us_min(*r_id_ptr);
+ }
+
+ *r_id_ptr = mappings.lookup(*r_id_ptr);
+ if (*r_id_ptr == nullptr) {
+ return ID_REMAP_RESULT_SOURCE_UNASSIGNED;
+ }
+
+ if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) {
+ id_us_plus(*r_id_ptr);
+ }
+
+ if (options & ID_REMAP_APPLY_ENSURE_REAL) {
+ id_us_ensure_real(*r_id_ptr);
+ }
+ return ID_REMAP_RESULT_SOURCE_REMAPPED;
+ }
+
+ void iter(IDRemapperIterFunction func, void *user_data) const
+ {
+ for (auto item : mappings.items()) {
+ func(item.key, item.value, user_data);
+ }
+ }
+};
+
+} // namespace blender::bke::id::remapper
+
+/** \brief wrap CPP IDRemapper to a C handle. */
+static IDRemapper *wrap(blender::bke::id::remapper::IDRemapper *remapper)
+{
+ return static_cast<IDRemapper *>(static_cast<void *>(remapper));
+}
+
+/** \brief wrap C handle to a CPP IDRemapper. */
+static blender::bke::id::remapper::IDRemapper *unwrap(IDRemapper *remapper)
+{
+ return static_cast<blender::bke::id::remapper::IDRemapper *>(static_cast<void *>(remapper));
+}
+
+/** \brief wrap C handle to a CPP IDRemapper. */
+static const blender::bke::id::remapper::IDRemapper *unwrap(const IDRemapper *remapper)
+{
+ return static_cast<const blender::bke::id::remapper::IDRemapper *>(
+ static_cast<const void *>(remapper));
+}
+
+extern "C" {
+
+IDRemapper *BKE_id_remapper_create(void)
+{
+ blender::bke::id::remapper::IDRemapper *remapper =
+ MEM_new<blender::bke::id::remapper::IDRemapper>(__func__);
+ return wrap(remapper);
+}
+
+void BKE_id_remapper_free(IDRemapper *id_remapper)
+{
+ blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
+ MEM_delete<blender::bke::id::remapper::IDRemapper>(remapper);
+}
+
+void BKE_id_remapper_clear(struct IDRemapper *id_remapper)
+{
+ blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
+ remapper->clear();
+}
+
+bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper)
+{
+ const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
+ return remapper->is_empty();
+}
+
+void BKE_id_remapper_add(IDRemapper *id_remapper, ID *old_id, ID *new_id)
+{
+ blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
+ remapper->add(old_id, new_id);
+}
+
+bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter)
+{
+ const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
+ return remapper->contains_mappings_for_any(type_filter);
+}
+
+IDRemapperApplyResult BKE_id_remapper_apply(const IDRemapper *id_remapper,
+ ID **r_id_ptr,
+ const IDRemapperApplyOptions options)
+{
+ const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
+ return remapper->apply(r_id_ptr, options);
+}
+
+void BKE_id_remapper_iter(const struct IDRemapper *id_remapper,
+ IDRemapperIterFunction func,
+ void *user_data)
+{
+ const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
+ remapper->iter(func, user_data);
+}
+}
diff --git a/source/blender/blenkernel/intern/lib_id_remapper_test.cc b/source/blender/blenkernel/intern/lib_id_remapper_test.cc
new file mode 100644
index 00000000000..594f64dac73
--- /dev/null
+++ b/source/blender/blenkernel/intern/lib_id_remapper_test.cc
@@ -0,0 +1,83 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2022 by Blender Foundation.
+ */
+
+#include "testing/testing.h"
+
+#include "BKE_lib_remap.h"
+
+#include "BLI_string.h"
+
+#include "DNA_ID.h"
+
+namespace blender::bke::id::remapper::tests {
+
+TEST(lib_id_remapper, unavailable)
+{
+ ID id1;
+ ID *idp = &id1;
+
+ IDRemapper *remapper = BKE_id_remapper_create();
+ IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
+ EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNAVAILABLE);
+
+ BKE_id_remapper_free(remapper);
+}
+
+TEST(lib_id_remapper, not_mappable)
+{
+ ID *idp = nullptr;
+
+ IDRemapper *remapper = BKE_id_remapper_create();
+ IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
+ EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE);
+
+ BKE_id_remapper_free(remapper);
+}
+
+TEST(lib_id_remapper, mapped)
+{
+ ID id1;
+ ID id2;
+ ID *idp = &id1;
+ BLI_strncpy(id1.name, "OB1", sizeof(id1.name));
+ BLI_strncpy(id2.name, "OB2", sizeof(id2.name));
+
+ IDRemapper *remapper = BKE_id_remapper_create();
+ BKE_id_remapper_add(remapper, &id1, &id2);
+ IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
+ EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_REMAPPED);
+ EXPECT_EQ(idp, &id2);
+
+ BKE_id_remapper_free(remapper);
+}
+
+TEST(lib_id_remapper, unassigned)
+{
+ ID id1;
+ ID *idp = &id1;
+
+ IDRemapper *remapper = BKE_id_remapper_create();
+ BKE_id_remapper_add(remapper, &id1, nullptr);
+ IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
+ EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNASSIGNED);
+ EXPECT_EQ(idp, nullptr);
+
+ BKE_id_remapper_free(remapper);
+}
+
+} // namespace blender::bke::id::remapper::tests
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 38ce8ea88b9..7158b71d7f1 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -161,6 +161,8 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f
(ID *)src_id;
id_us_plus(dst_id->override_library->reference);
+ dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root;
+
if (do_full_copy) {
BLI_duplicatelist(&dst_id->override_library->properties,
&src_id->override_library->properties);
@@ -211,6 +213,7 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo
}
static ID *lib_override_library_create_from(Main *bmain,
+ Library *owner_library,
ID *reference_id,
const int lib_id_copy_flags)
{
@@ -227,6 +230,12 @@ static ID *lib_override_library_create_from(Main *bmain,
}
id_us_min(local_id);
+ /* TODO: Handle this properly in LIB_NO_MAIN case as well (i.e. resync case). Or offload to
+ * generic ID copy code? */
+ if ((lib_id_copy_flags & LIB_ID_CREATE_NO_MAIN) == 0) {
+ local_id->lib = owner_library;
+ }
+
BKE_lib_override_library_init(local_id, reference_id);
/* NOTE: From liboverride perspective (and RNA one), shape keys are considered as local embedded
@@ -281,11 +290,12 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
BLI_assert(reference_id != NULL);
BLI_assert(ID_IS_LINKED(reference_id));
- ID *local_id = lib_override_library_create_from(bmain, reference_id, 0);
+ ID *local_id = lib_override_library_create_from(bmain, NULL, reference_id, 0);
/* We cannot allow automatic hierarchy resync on this ID, it is highly likely to generate a giant
* mess in case there are a lot of hidden, non-instantiated, non-properly organized dependencies.
* Ref T94650. */
local_id->override_library->flag |= IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY;
+ local_id->override_library->hierarchy_root = local_id;
if (do_tagged_remap) {
Key *reference_key, *local_key = NULL;
@@ -320,9 +330,12 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
}
bool BKE_lib_override_library_create_from_tag(Main *bmain,
- const Library *reference_library,
+ Library *owner_library,
+ const ID *id_root_reference,
const bool do_no_main)
{
+ const Library *reference_library = id_root_reference->lib;
+
ID *reference_id;
bool success = true;
@@ -351,7 +364,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
* This requires extra care further down the resync process,
* see: #BKE_lib_override_library_resync. */
reference_id->newid = lib_override_library_create_from(
- bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
+ bmain, owner_library, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
if (reference_id->newid == NULL) {
success = false;
break;
@@ -375,6 +388,8 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
/* Only remap new local ID's pointers, we don't want to force our new overrides onto our whole
* existing linked IDs usages. */
if (success) {
+ ID *hierarchy_root_id = id_root_reference->newid;
+
for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
reference_id = todo_id_iter->data;
ID *local_id = reference_id->newid;
@@ -383,6 +398,8 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
continue;
}
+ local_id->override_library->hierarchy_root = hierarchy_root_id;
+
Key *reference_key, *local_key = NULL;
if ((reference_key = BKE_key_from_id(reference_id)) != NULL) {
local_key = BKE_key_from_id(reference_id->newid);
@@ -451,6 +468,7 @@ typedef struct LibOverrideGroupTagData {
Main *bmain;
Scene *scene;
ID *id_root;
+ ID *hierarchy_root_id;
uint tag;
uint missing_tag;
/* Whether we are looping on override data, or their references (linked) one. */
@@ -616,6 +634,35 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
}
}
+static bool lib_override_linked_group_tag_collections_keep_tagged_check_recursive(
+ LibOverrideGroupTagData *data, Collection *collection)
+{
+ /* NOTE: Collection's object cache (using bases, as returned by #BKE_collection_object_cache_get)
+ * is not usable here, as it may have become invalid from some previous operation and it should
+ * not be updated here. So instead only use collections' reliable 'raw' data to check if some
+ * object in the hierarchy of the given collection is still tagged for override. */
+ for (CollectionObject *collection_object = collection->gobject.first; collection_object != NULL;
+ collection_object = collection_object->next) {
+ Object *object = collection_object->ob;
+ if (object == NULL) {
+ continue;
+ }
+ if ((object->id.tag & data->tag) != 0) {
+ return true;
+ }
+ }
+
+ for (CollectionChild *collection_child = collection->children.first; collection_child != NULL;
+ collection_child = collection_child->next) {
+ if (lib_override_linked_group_tag_collections_keep_tagged_check_recursive(
+ data, collection_child->collection)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
@@ -638,15 +685,8 @@ static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGr
if ((collection->id.tag & data->tag) == 0) {
continue;
}
- bool keep_tagged = false;
- const ListBase object_bases = BKE_collection_object_cache_get(collection);
- LISTBASE_FOREACH (Base *, base, &object_bases) {
- if ((base->object->id.tag & data->tag) != 0) {
- keep_tagged = true;
- break;
- }
- }
- if (!keep_tagged) {
+
+ if (!lib_override_linked_group_tag_collections_keep_tagged_check_recursive(data, collection)) {
collection->id.tag &= ~data->tag;
}
}
@@ -739,6 +779,8 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner));
BLI_assert(data->is_override);
+ ID *id_hierarchy_root = data->hierarchy_root_id;
+
if (ID_IS_OVERRIDE_LIBRARY_REAL(id_owner) &&
(id_owner->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) != 0) {
return;
@@ -770,9 +812,15 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *
if (ELEM(to_id, NULL, id_owner)) {
continue;
}
+ /* Different libraries or different hierarchy roots are break points in override hierarchies.
+ */
if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id_owner->lib)) {
continue;
}
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id) &&
+ to_id->override_library->hierarchy_root != id_hierarchy_root) {
+ continue;
+ }
Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib;
ID *to_id_reference = lib_override_get(bmain, to_id)->reference;
@@ -802,6 +850,11 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(data->is_override);
+ ID *id_hierarchy_root = data->hierarchy_root_id;
+ BLI_assert(id_hierarchy_root != NULL);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root));
+ UNUSED_VARS_NDEBUG(id_hierarchy_root);
+
if (id_root->override_library->reference->tag & LIB_TAG_MISSING) {
id_root->tag |= data->missing_tag;
}
@@ -813,7 +866,10 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
lib_override_overrides_group_tag_recursive(data);
}
-static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_root)
+static bool lib_override_library_create_do(Main *bmain,
+ Scene *scene,
+ Library *owner_library,
+ ID *id_root)
{
BKE_main_relations_create(bmain, 0);
LibOverrideGroupTagData data = {.bmain = bmain,
@@ -832,12 +888,16 @@ static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_roo
BKE_main_relations_free(bmain);
lib_override_group_tag_data_clear(&data);
- return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false);
+ const bool success = BKE_lib_override_library_create_from_tag(
+ bmain, owner_library, id_root, false);
+
+ return success;
}
static void lib_override_library_create_post_process(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
+ const Library *owner_library,
ID *id_root,
ID *id_reference,
Collection *residual_storage,
@@ -859,7 +919,8 @@ static void lib_override_library_create_post_process(Main *bmain,
/* Instantiating the root collection or object should never be needed in resync case, since the
* old override would be remapped to the new one. */
- if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) {
+ if (!is_resync && id_root != NULL && id_root->newid != NULL &&
+ (!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) {
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
@@ -904,7 +965,7 @@ static void lib_override_library_create_post_process(Main *bmain,
Collection *default_instantiating_collection = residual_storage;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
Object *ob_new = (Object *)ob->id.newid;
- if (ob_new == NULL || ID_IS_LINKED(ob_new)) {
+ if (ob_new == NULL || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) {
continue;
}
@@ -967,6 +1028,7 @@ static void lib_override_library_create_post_process(Main *bmain,
bool BKE_lib_override_library_create(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
+ Library *owner_library,
ID *id_root,
ID *id_reference,
ID **r_id_root_override)
@@ -975,7 +1037,7 @@ bool BKE_lib_override_library_create(Main *bmain,
*r_id_root_override = NULL;
}
- const bool success = lib_override_library_create_do(bmain, scene, id_root);
+ const bool success = lib_override_library_create_do(bmain, scene, owner_library, id_root);
if (!success) {
return success;
@@ -986,7 +1048,7 @@ bool BKE_lib_override_library_create(Main *bmain,
}
lib_override_library_create_post_process(
- bmain, scene, view_layer, id_root, id_reference, NULL, false);
+ bmain, scene, view_layer, owner_library, id_root, id_reference, NULL, false);
/* Cleanup. */
BKE_main_id_newptr_and_tag_clear(bmain);
@@ -1011,114 +1073,226 @@ bool BKE_lib_override_library_template_create(struct ID *id)
return true;
}
-bool BKE_lib_override_library_proxy_convert(Main *bmain,
- Scene *scene,
- ViewLayer *view_layer,
- Object *ob_proxy)
+static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int *r_best_level)
{
- /* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is
- * coming. */
- Object *ob_proxy_group = ob_proxy->proxy_group;
- const bool is_override_instancing_object = ob_proxy_group != NULL;
- ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id :
- &ob_proxy->proxy->id;
- ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id;
+ if (curr_level > 1000) {
+ CLOG_ERROR(&LOG,
+ "Levels of dependency relationships between library overrides IDs is way too high, "
+ "skipping further processing loops (involves at least '%s')",
+ id->name);
+ BLI_assert(0);
+ return NULL;
+ }
- /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not
- * sure this is a valid state, but for now just abort the overriding process. */
- if (!ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
- return false;
+ if (!ID_IS_OVERRIDE_LIBRARY(id)) {
+ BLI_assert(0);
+ return NULL;
}
- /* We manually convert the proxy object into a library override, further override handling will
- * then be handled by `BKE_lib_override_library_create()` just as for a regular override
- * creation.
- */
- ob_proxy->proxy->id.tag |= LIB_TAG_DOIT;
- ob_proxy->proxy->id.newid = &ob_proxy->id;
- BKE_lib_override_library_init(&ob_proxy->id, &ob_proxy->proxy->id);
+ MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
+ BLI_assert(entry != NULL);
- ob_proxy->proxy->proxy_from = NULL;
- ob_proxy->proxy = ob_proxy->proxy_group = NULL;
+ int best_level_candidate = curr_level;
+ ID *best_root_id_candidate = id;
- DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE);
+ for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != NULL;
+ from_id_entry = from_id_entry->next) {
+ if ((from_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
+ continue;
+ }
+
+ ID *from_id = from_id_entry->id_pointer.from;
+ if (ELEM(from_id, NULL, id)) {
+ continue;
+ }
+ if (!ID_IS_OVERRIDE_LIBRARY(from_id) || (from_id->lib != id->lib)) {
+ continue;
+ }
- /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created
- * overrides.
- * While this might not be 100% the desired behavior, it is likely to be the case most of the
- * time. Ref: T91711. */
- ID *id_iter;
- FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (!ID_IS_LINKED(id_iter)) {
- id_iter->tag |= LIB_TAG_DOIT;
+ int level_candidate = curr_level + 1;
+ /* Recursively process the parent. */
+ ID *root_id_candidate = lib_override_root_find(
+ bmain, from_id, curr_level + 1, &level_candidate);
+ if (level_candidate > best_level_candidate && root_id_candidate != NULL) {
+ best_root_id_candidate = root_id_candidate;
+ best_level_candidate = level_candidate;
}
}
- FOREACH_MAIN_ID_END;
- return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL);
+ *r_best_level = best_level_candidate;
+ return best_root_id_candidate;
}
-static void lib_override_library_proxy_convert_do(Main *bmain,
- Scene *scene,
- Object *ob_proxy,
- BlendFileReadReport *reports)
+static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID *id_from)
{
- Object *ob_proxy_group = ob_proxy->proxy_group;
- const bool is_override_instancing_object = ob_proxy_group != NULL;
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ if (id->override_library->hierarchy_root == id_root) {
+ /* Already set, nothing else to do here, sub-hierarchy is also assumed to be properly set
+ * then. */
+ return;
+ }
- const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy);
+ /* Hierarchy root already set, and not matching currently propsed one, try to find which is
+ * best. */
+ if (id->override_library->hierarchy_root != NULL) {
+ /* Check if given `id_from` matches with the hierarchy of the linked reference ID, in which
+ * case we assume that the given hierarchy root is the 'real' one.
+ *
+ * NOTE: This can fail if user mixed dependencies between several overrides of a same
+ * reference linked hierarchy. Not much to be done in that case, it's virtually impossible to
+ * fix this automatically in a reliable way. */
+ if (id_from == NULL || !ID_IS_OVERRIDE_LIBRARY_REAL(id_from)) {
+ /* Too complicated to deal with for now. */
+ CLOG_WARN(&LOG,
+ "Inconsistency in library override hierarchy of ID '%s'.\n"
+ "\tNot enough data to verify validity of current proposed root '%s', assuming "
+ "already set one '%s' is valid.",
+ id->name,
+ id_root->name,
+ id->override_library->hierarchy_root->name);
+ return;
+ }
- if (success) {
- CLOG_INFO(&LOG,
- 4,
- "Proxy object '%s' successfully converted to library overrides",
- ob_proxy->id.name);
- /* Remove the instance empty from this scene, the items now have an overridden collection
- * instead. */
- if (is_override_instancing_object) {
- BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true);
+ ID *id_from_ref = id_from->override_library->reference;
+ MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers,
+ id->override_library->reference);
+ BLI_assert(entry != NULL);
+
+ bool do_replace_root = false;
+ for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != NULL;
+ from_id_entry = from_id_entry->next) {
+ if ((from_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
+ continue;
+ }
+
+ if (id_from_ref == from_id_entry->id_pointer.from) {
+ /* A matching parent was found in reference linked data, assume given hierarchy root is
+ * the valid one. */
+ do_replace_root = true;
+ CLOG_WARN(
+ &LOG,
+ "Inconsistency in library override hierarchy of ID '%s'.\n"
+ "\tCurrent proposed root '%s' detected as valid, will replace already set one '%s'.",
+ id->name,
+ id_root->name,
+ id->override_library->hierarchy_root->name);
+ break;
+ }
+ }
+
+ if (!do_replace_root) {
+ CLOG_WARN(
+ &LOG,
+ "Inconsistency in library override hierarchy of ID '%s'.\n"
+ "\tCurrent proposed root '%s' not detected as valid, keeping already set one '%s'.",
+ id->name,
+ id_root->name,
+ id->override_library->hierarchy_root->name);
+ return;
+ }
+ }
+
+ id->override_library->hierarchy_root = id_root;
+ }
+
+ MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
+ BLI_assert(entry != NULL);
+
+ for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
+ to_id_entry = to_id_entry->next) {
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
+ continue;
+ }
+
+ ID *to_id = *to_id_entry->id_pointer.to;
+ if (ELEM(to_id, NULL, id)) {
+ continue;
}
- reports->count.proxies_to_lib_overrides_success++;
+ if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id->lib)) {
+ continue;
+ }
+
+ /* Recursively process the sub-hierarchy. */
+ lib_override_root_hierarchy_set(bmain, id_root, to_id, id);
}
}
-void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports)
+void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain)
{
- LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
- if (object->proxy_group == NULL) {
- continue;
- }
+ ID *id;
+
+ BKE_main_relations_create(bmain, 0);
- lib_override_library_proxy_convert_do(bmain, scene, object, reports);
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
+ if (id->override_library->hierarchy_root != NULL) {
+ continue;
}
- FOREACH_SCENE_OBJECT_END;
- FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
- if (object->proxy == NULL) {
- continue;
- }
+ int best_level = 0;
+ ID *id_root = lib_override_root_find(bmain, id, best_level, &best_level);
- lib_override_library_proxy_convert_do(bmain, scene, object, reports);
+ if (!ELEM(id_root->override_library->hierarchy_root, id_root, NULL)) {
+ CLOG_WARN(&LOG,
+ "Potential inconsistency in library override hierarchy of ID '%s', detected as "
+ "part of the hierarchy of '%s', which has a different root '%s'",
+ id->name,
+ id_root->name,
+ id_root->override_library->hierarchy_root->name);
+ continue;
}
- FOREACH_SCENE_OBJECT_END;
+
+ lib_override_root_hierarchy_set(bmain, id_root, id_root, NULL);
+
+ BLI_assert(id->override_library->hierarchy_root != NULL);
}
+ FOREACH_MAIN_ID_END;
+
+ BKE_main_relations_free(bmain);
+}
- LISTBASE_FOREACH (Object *, object, &bmain->objects) {
- if (ID_IS_LINKED(object)) {
- if (object->proxy != NULL) {
- CLOG_WARN(&LOG, "Did not try to convert linked proxy object '%s'", object->id.name);
- reports->count.linked_proxies++;
+static void lib_override_library_remap(Main *bmain,
+ const ID *id_root_reference,
+ GHash *linkedref_to_old_override)
+{
+ ID *id;
+ struct IDRemapper *remapper = BKE_id_remapper_create();
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
+ ID *id_override_new = id->newid;
+ ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
+ if (id_override_old == NULL) {
+ continue;
}
- continue;
- }
- if (object->proxy_group != NULL || object->proxy != NULL) {
- CLOG_WARN(
- &LOG, "Proxy object '%s' failed to be converted to library override", object->id.name);
- reports->count.proxies_to_lib_overrides_failures++;
+ BKE_id_remapper_add(remapper, id_override_old, id_override_new);
+ /* Remap no-main override IDs we just created too. */
+ GHashIterator linkedref_to_old_override_iter;
+ GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
+ ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter);
+ if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) {
+ continue;
+ }
+
+ BKE_libblock_relink_ex(bmain,
+ id_override_old_iter,
+ id_override_old,
+ id_override_new,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ }
}
}
+ FOREACH_MAIN_ID_END;
+
+ /* Remap all IDs to use the new override. */
+ BKE_libblock_remap_multiple(bmain, remapper, 0);
+ BKE_id_remapper_free(remapper);
}
static bool lib_override_library_resync(Main *bmain,
@@ -1147,6 +1321,7 @@ static bool lib_override_library_resync(Main *bmain,
LibOverrideGroupTagData data = {.bmain = bmain,
.scene = scene,
.id_root = id_root,
+ .hierarchy_root_id = id_root->override_library->hierarchy_root,
.tag = LIB_TAG_DOIT,
.missing_tag = LIB_TAG_MISSING,
.is_override = true,
@@ -1249,7 +1424,7 @@ static bool lib_override_library_resync(Main *bmain,
* override IDs (including within the old overrides themselves, since those are tagged too
* above). */
const bool success = BKE_lib_override_library_create_from_tag(
- bmain, id_root_reference->lib, true);
+ bmain, NULL, id_root_reference, true);
if (!success) {
return success;
@@ -1312,32 +1487,9 @@ static bool lib_override_library_resync(Main *bmain,
}
FOREACH_MAIN_LISTBASE_END;
- /* We need to remap old to new override usages in a separate loop, after all new overrides have
+ /* We remap old to new override usages in a separate loop, after all new overrides have
* been added to Main. */
- FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
- ID *id_override_new = id->newid;
- ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
-
- if (id_override_old != NULL) {
- /* Remap all IDs to use the new override. */
- BKE_libblock_remap(bmain, id_override_old, id_override_new, 0);
- /* Remap no-main override IDs we just created too. */
- GHashIterator linkedref_to_old_override_iter;
- GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
- ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter);
- if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) {
- BKE_libblock_relink_ex(bmain,
- id_override_old_iter,
- id_override_old,
- id_override_new,
- ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
- }
- }
- }
- }
- }
- FOREACH_MAIN_ID_END;
+ lib_override_library_remap(bmain, id_root_reference, linkedref_to_old_override);
BKE_main_collection_sync(bmain);
@@ -1486,6 +1638,7 @@ static bool lib_override_library_resync(Main *bmain,
lib_override_library_create_post_process(bmain,
scene,
view_layer,
+ NULL,
id_root_reference,
id_root,
override_resync_residual_storage,
@@ -1537,11 +1690,13 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
CLOG_ERROR(
&LOG,
"While processing indirect level %d, ID %s from lib %s of indirect level %d detected "
- "as needing resync.",
+ "as needing resync, skipping.",
library_indirect_level,
id->name,
id->lib->filepath,
id->lib->temp_index);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ return;
}
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
@@ -1588,46 +1743,20 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
*
* NOTE: Related to `lib_override_resync_tagging_finalize` above.
*/
-static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level)
-{
- (*level)++;
- ID *return_id = id;
-
- switch (GS(id->name)) {
- case ID_GR: {
- /* Find the highest valid collection in the parenting hierarchy.
- * Note that in practice, in any decent common case there is only one well defined root
- * collection anyway. */
- int max_level = *level;
- Collection *collection = (Collection *)id;
- LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) {
- Collection *collection_parent = collection_parent_iter->collection;
- if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) &&
- collection_parent->id.lib == id->lib) {
- int tmp_level = *level;
- ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id,
- &tmp_level);
- if (tmp_level > max_level) {
- max_level = tmp_level;
- return_id = tmp_id;
- }
- }
- }
- break;
- }
- case ID_OB: {
- Object *object = (Object *)id;
- if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) &&
- object->parent->id.lib == id->lib) {
- return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level);
- }
- break;
+static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->owner_get != NULL) {
+ id = id_type->owner_get(bmain, id);
}
- default:
- break;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
}
- return return_id;
+ ID *hierarchy_root_id = id->override_library->hierarchy_root;
+ BLI_assert(hierarchy_root_id != NULL);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(hierarchy_root_id));
+ return hierarchy_root_id;
}
/* Ensure resync of all overrides at one level of indirect usage.
@@ -1769,14 +1898,13 @@ static void lib_override_library_main_resync_on_library_indirect_level(
Library *library = id->lib;
- int level = 0;
/* In complex non-supported cases, with several different override hierarchies sharing
* relations between each-other, we may end up not actually updating/replacing the given
* root id (see e.g. pro/shots/110_rextoria/110_0150_A/110_0150_A.anim.blend of sprites
* project repository, r2687).
* This can lead to infinite loop here, at least avoid this. */
id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
- id = lib_override_library_main_resync_find_root_recurse(id, &level);
+ id = lib_override_library_main_resync_root_get(bmain, id);
id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
BLI_assert(id->lib == library);
@@ -1908,7 +2036,7 @@ void BKE_lib_override_library_main_resync(Main *bmain,
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
lib_override_library_create_post_process(
- bmain, scene, view_layer, NULL, NULL, override_resync_residual_storage, true);
+ bmain, scene, view_layer, NULL, NULL, NULL, override_resync_residual_storage, true);
if (BKE_collection_is_empty(override_resync_residual_storage)) {
BKE_collection_delete(bmain, override_resync_residual_storage, true);
@@ -1934,6 +2062,7 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
LibOverrideGroupTagData data = {.bmain = bmain,
.scene = NULL,
.id_root = id_root,
+ .hierarchy_root_id = id_root->override_library->hierarchy_root,
.tag = LIB_TAG_DOIT,
.missing_tag = LIB_TAG_MISSING,
.is_override = true,
@@ -2961,10 +3090,6 @@ void BKE_lib_override_library_main_update(Main *bmain)
bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id)
{
- if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) {
- return true;
- }
-
/* The only strong known case currently are objects used by override collections. */
/* TODO: There are most likely other cases... This may need to be addressed in a better way at
* some point. */
diff --git a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c
new file mode 100644
index 00000000000..d99a9b57a6e
--- /dev/null
+++ b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c
@@ -0,0 +1,176 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "CLG_log.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_linklist.h"
+
+/* Required for proxy to liboverrides conversion code. */
+#define DNA_DEPRECATED_ALLOW
+
+#include "DNA_ID.h"
+#include "DNA_collection_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_collection.h"
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_override.h"
+#include "BKE_main.h"
+
+#include "BLO_readfile.h"
+
+static CLG_LogRef LOG = {"bke.liboverride_proxy_conversion"};
+
+bool BKE_lib_override_library_proxy_convert(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ Object *ob_proxy)
+{
+ /* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is
+ * coming. */
+ Object *ob_proxy_group = ob_proxy->proxy_group;
+ const bool is_override_instancing_object = ob_proxy_group != NULL;
+ ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id :
+ &ob_proxy->proxy->id;
+ ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id;
+
+ /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not
+ * sure this is a valid state, but for now just abort the overriding process. */
+ if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_root)) {
+ if (ob_proxy->proxy != NULL) {
+ ob_proxy->proxy->proxy_from = NULL;
+ }
+ id_us_min((ID *)ob_proxy->proxy);
+ ob_proxy->proxy = ob_proxy->proxy_group = NULL;
+ return false;
+ }
+
+ /* We manually convert the proxy object into a library override, further override handling will
+ * then be handled by `BKE_lib_override_library_create()` just as for a regular override
+ * creation.
+ */
+ ob_proxy->proxy->id.tag |= LIB_TAG_DOIT;
+ ob_proxy->proxy->id.newid = &ob_proxy->id;
+ BKE_lib_override_library_init(&ob_proxy->id, &ob_proxy->proxy->id);
+
+ ob_proxy->proxy->proxy_from = NULL;
+ ob_proxy->proxy = ob_proxy->proxy_group = NULL;
+
+ DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE);
+
+ /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created
+ * overrides. Also do that for the IDs from the same lib as the proxy in case it is linked.
+ * While this might not be 100% the desired behavior, it is likely to be the case most of the
+ * time. Ref: T91711. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ if (!ID_IS_LINKED(id_iter) || id_iter->lib == ob_proxy->id.lib) {
+ id_iter->tag |= LIB_TAG_DOIT;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ return BKE_lib_override_library_create(
+ bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_reference, NULL);
+}
+
+static void lib_override_library_proxy_convert_do(Main *bmain,
+ Scene *scene,
+ Object *ob_proxy,
+ BlendFileReadReport *reports)
+{
+ Object *ob_proxy_group = ob_proxy->proxy_group;
+ const bool is_override_instancing_object = ob_proxy_group != NULL;
+
+ const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy);
+
+ if (success) {
+ CLOG_INFO(&LOG,
+ 4,
+ "Proxy object '%s' successfully converted to library overrides",
+ ob_proxy->id.name);
+ /* Remove the instance empty from this scene, the items now have an overridden collection
+ * instead. */
+ if (is_override_instancing_object) {
+ BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true);
+ }
+ reports->count.proxies_to_lib_overrides_success++;
+ }
+}
+
+void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports)
+{
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ LinkNodePair proxy_objects = {NULL};
+
+ FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
+ if (object->proxy_group != NULL) {
+ BLI_linklist_append(&proxy_objects, object);
+ }
+ }
+ FOREACH_SCENE_OBJECT_END;
+
+ FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
+ if (object->proxy != NULL && object->proxy_group == NULL) {
+ BLI_linklist_append(&proxy_objects, object);
+ }
+ }
+ FOREACH_SCENE_OBJECT_END;
+
+ for (LinkNode *proxy_object_iter = proxy_objects.list; proxy_object_iter != NULL;
+ proxy_object_iter = proxy_object_iter->next) {
+ Object *proxy_object = proxy_object_iter->link;
+ lib_override_library_proxy_convert_do(bmain, scene, proxy_object, reports);
+ }
+
+ BLI_linklist_free(proxy_objects.list, NULL);
+ }
+
+ LISTBASE_FOREACH (Object *, object, &bmain->objects) {
+ if (object->proxy_group != NULL || object->proxy != NULL) {
+ if (ID_IS_LINKED(object)) {
+ CLOG_WARN(&LOG,
+ "Linked proxy object '%s' from '%s' failed to be converted to library override",
+ object->id.name + 2,
+ object->id.lib->filepath);
+ }
+ else {
+ CLOG_WARN(&LOG,
+ "Proxy object '%s' failed to be converted to library override",
+ object->id.name + 2);
+ }
+ reports->count.proxies_to_lib_overrides_failures++;
+ if (object->proxy != NULL) {
+ object->proxy->proxy_from = NULL;
+ }
+ id_us_min((ID *)object->proxy);
+ object->proxy = object->proxy_group = NULL;
+ }
+ }
+}
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index 1f20a84098c..f49af9abffe 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -323,6 +323,8 @@ static bool library_foreach_ID_link(Main *bmain,
IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE);
CALLBACK_INVOKE_ID(id->override_library->storage,
IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE);
+
+ CALLBACK_INVOKE_ID(id->override_library->hierarchy_root, IDWALK_CB_LOOPBACK);
}
IDP_foreach_property(id->properties,
@@ -471,7 +473,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
return ELEM(id_type_used, ID_MA);
case ID_WS:
return ELEM(id_type_used, ID_SCR, ID_SCE);
- case ID_HA:
+ case ID_CV:
return ELEM(id_type_used, ID_MA);
case ID_PT:
return ELEM(id_type_used, ID_MA);
@@ -517,7 +519,7 @@ static int foreach_libblock_id_users_callback(LibraryIDLinkCallbackData *cb_data
IDUsersIter *iter = cb_data->user_data;
if (*id_p) {
- /* 'Loopback' ID pointers (the ugly 'from' ones, Object->proxy_from and Key->from).
+ /* 'Loopback' ID pointers (the ugly 'from' ones, like Key->from).
* Those are not actually ID usage, we can ignore them here.
*/
if (cb_flag & IDWALK_CB_LOOPBACK) {
@@ -768,7 +770,7 @@ static int foreach_libblock_used_linked_data_tag_clear_cb(LibraryIDLinkCallbackD
bool *is_changed = cb_data->user_data;
if (*id_p) {
- /* The infamous 'from' pointers (Key.from, Object.proxy_from, ...).
+ /* The infamous 'from' pointers (Key.from, ...).
* those are not actually ID usage, so we ignore them here. */
if (cb_flag & IDWALK_CB_LOOPBACK) {
return IDWALK_RET_NOP;
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index ec97ca83703..8e375ff16b5 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -91,28 +91,20 @@ enum {
ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1, /* There was some skipped 'user_one' usages of old_id. */
};
-static void foreach_libblock_remap_callback_skip(const ID *id_owner,
- ID **id_ptr,
+static void foreach_libblock_remap_callback_skip(const ID *UNUSED(id_owner),
+ ID **UNUSED(id_ptr),
IDRemap *id_remap_data,
const int cb_flag,
const bool is_indirect,
const bool is_reference,
- const bool is_never_null,
- const bool is_obj,
+ const bool violates_never_null,
+ const bool UNUSED(is_obj),
const bool is_obj_editmode)
{
if (is_indirect) {
id_remap_data->skipped_indirect++;
- if (is_obj) {
- Object *ob = (Object *)id_owner;
- if (ob->data == *id_ptr && ob->proxy != NULL) {
- /* And another 'Proudly brought to you by Proxy Hell' hack!
- * This will allow us to avoid clearing 'LIB_EXTERN' flag of obdata of proxies... */
- id_remap_data->skipped_direct++;
- }
- }
}
- else if (is_never_null || is_obj_editmode || is_reference) {
+ else if (violates_never_null || is_obj_editmode || is_reference) {
id_remap_data->skipped_direct++;
}
else {
@@ -135,11 +127,10 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner,
IDRemap *id_remap_data,
const int cb_flag,
const bool is_indirect,
- const bool is_never_null,
- const bool force_user_refcount,
- const bool is_obj_proxy)
+ const bool violates_never_null,
+ const bool force_user_refcount)
{
- if (!is_never_null) {
+ if (!violates_never_null) {
*id_ptr = new_id;
DEG_id_tag_update_ex(id_remap_data->bmain,
id_self,
@@ -170,16 +161,9 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner,
/* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET)
* are assumed to be set as needed, that extra user is processed in final handling. */
}
- if (!is_indirect || is_obj_proxy) {
+ if (!is_indirect) {
id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT;
}
- /* We need to remap proxy_from pointer of remapped proxy... sigh. */
- if (is_obj_proxy && new_id != NULL) {
- Object *ob = (Object *)id_owner;
- if (ob->proxy == (Object *)new_id) {
- ob->proxy->proxy_from = ob;
- }
- }
}
static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
@@ -221,16 +205,13 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
const bool is_reference = (cb_flag & IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE) != 0;
const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0;
const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0;
- /* NOTE: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct,
- * on the other hand since they get reset to lib data on file open/reload it is indirect too.
- * Edit Mode is also a 'skip direct' case. */
const bool is_obj = (GS(id_owner->name) == ID_OB);
- const bool is_obj_proxy = (is_obj &&
- (((Object *)id_owner)->proxy || ((Object *)id_owner)->proxy_group));
+ /* NOTE: Edit Mode is a 'skip direct' case, unless specifically requested, obdata should not be
+ * remapped in this situation. */
const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner) &&
(id_remap_data->flag & ID_REMAP_FORCE_OBDATA_IN_EDITMODE) == 0);
- const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) &&
- (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0);
+ const bool violates_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) &&
+ (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0);
const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0;
const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0;
const bool force_user_refcount = (id_remap_data->flag & ID_REMAP_FORCE_USER_REFCOUNT) != 0;
@@ -258,7 +239,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
/* Special hack in case it's Object->data and we are in edit mode, and new_id is not NULL
* (otherwise, we follow common NEVER_NULL flags).
* (skipped_indirect too). */
- if ((is_never_null && skip_never_null) ||
+ if ((violates_never_null && skip_never_null) ||
(is_obj_editmode && (((Object *)id_owner)->data == *id_p) && new_id != NULL) ||
(skip_indirect && is_indirect) || (is_reference && skip_reference)) {
foreach_libblock_remap_callback_skip(id_owner,
@@ -267,7 +248,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
cb_flag,
is_indirect,
is_reference,
- is_never_null,
+ violates_never_null,
is_obj,
is_obj_editmode);
}
@@ -280,9 +261,8 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
id_remap_data,
cb_flag,
is_indirect,
- is_never_null,
- force_user_refcount,
- is_obj_proxy);
+ violates_never_null,
+ force_user_refcount);
}
return IDWALK_RET_NOP;
@@ -430,10 +410,7 @@ static void libblock_remap_data(
Main *bmain, ID *id, ID *old_id, ID *new_id, const short remap_flags, IDRemap *r_id_remap_data)
{
IDRemap id_remap_data;
- const int foreach_id_flags = ((remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ?
- IDWALK_NO_INDIRECT_PROXY_DATA_USAGE :
- IDWALK_NOP) |
- ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
+ const int foreach_id_flags = ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
IDWALK_DO_INTERNAL_RUNTIME_POINTERS :
IDWALK_NOP);
@@ -510,11 +487,18 @@ static void libblock_remap_data(
#endif
}
-void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
+typedef struct LibblockRemapMultipleUserData {
+ Main *bmain;
+ short remap_flags;
+} LibBlockRemapMultipleUserData;
+
+static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_data)
{
+ LibBlockRemapMultipleUserData *data = user_data;
+ Main *bmain = data->bmain;
+ const short remap_flags = data->remap_flags;
+
IDRemap id_remap_data;
- ID *old_id = old_idv;
- ID *new_id = new_idv;
int skipped_direct, skipped_refcounted;
BLI_assert(old_id != NULL);
@@ -527,13 +511,6 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
free_notifier_reference_cb(old_id);
}
- /* We assume editors do not hold references to their IDs... This is false in some cases
- * (Image is especially tricky here),
- * editors' code is to handle refcount (id->us) itself then. */
- if (remap_editor_id_reference_cb) {
- remap_editor_id_reference_cb(old_id, new_id);
- }
-
skipped_direct = id_remap_data.skipped_direct;
skipped_refcounted = id_remap_data.skipped_refcounted;
@@ -581,7 +558,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
case ID_ME:
case ID_CU:
case ID_MB:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
if (new_id) { /* Only affects us in case obdata was relinked (changed). */
@@ -606,6 +583,41 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
DEG_relations_tag_update(bmain);
}
+void BKE_libblock_remap_multiple_locked(Main *bmain,
+ const struct IDRemapper *mappings,
+ const short remap_flags)
+{
+ if (BKE_id_remapper_is_empty(mappings)) {
+ /* Early exit nothing to do. */
+ return;
+ }
+
+ LibBlockRemapMultipleUserData user_data;
+ user_data.bmain = bmain;
+ user_data.remap_flags = remap_flags;
+ BKE_id_remapper_iter(mappings, libblock_remap_foreach_idpair_cb, &user_data);
+
+ /* We assume editors do not hold references to their IDs... This is false in some cases
+ * (Image is especially tricky here),
+ * editors' code is to handle refcount (id->us) itself then. */
+ if (remap_editor_id_reference_cb) {
+ remap_editor_id_reference_cb(mappings);
+ }
+
+ /* Full rebuild of DEG! */
+ DEG_relations_tag_update(bmain);
+}
+
+void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
+{
+ struct IDRemapper *remapper = BKE_id_remapper_create();
+ ID *old_id = old_idv;
+ ID *new_id = new_idv;
+ BKE_id_remapper_add(remapper, old_id, new_id);
+ BKE_libblock_remap_multiple_locked(bmain, remapper, remap_flags);
+ BKE_id_remapper_free(remapper);
+}
+
void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
{
BKE_main_lock(bmain);
@@ -615,6 +627,17 @@ void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short r
BKE_main_unlock(bmain);
}
+void BKE_libblock_remap_multiple(Main *bmain,
+ const struct IDRemapper *mappings,
+ const short remap_flags)
+{
+ BKE_main_lock(bmain);
+
+ BKE_libblock_remap_multiple_locked(bmain, mappings, remap_flags);
+
+ BKE_main_unlock(bmain);
+}
+
void BKE_libblock_unlink(Main *bmain,
void *idv,
const bool do_flag_never_null,
diff --git a/source/blender/blenkernel/intern/lib_remap_test.cc b/source/blender/blenkernel/intern/lib_remap_test.cc
index 2faf9265ea6..266ada3663d 100644
--- a/source/blender/blenkernel/intern/lib_remap_test.cc
+++ b/source/blender/blenkernel/intern/lib_remap_test.cc
@@ -304,7 +304,7 @@ TEST(lib_remap, never_null_usage_flag_not_requested_on_delete)
EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
EXPECT_EQ(context.test_data.object->id.tag & LIB_TAG_DOIT, 0);
- /* Never null usage isn't requested so the flag should not be set.*/
+ /* Never null usage isn't requested so the flag should not be set. */
BKE_libblock_remap(
context.test_data.bmain, context.test_data.mesh, nullptr, ID_REMAP_SKIP_NEVER_NULL_USAGE);
EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
@@ -339,7 +339,7 @@ TEST(lib_remap, never_null_usage_flag_not_requested_on_remap)
EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
EXPECT_EQ(context.test_data.object->id.tag & LIB_TAG_DOIT, 0);
- /* Never null usage isn't requested so the flag should not be set.*/
+ /* Never null usage isn't requested so the flag should not be set. */
BKE_libblock_remap(
context.test_data.bmain, context.test_data.mesh, other_mesh, ID_REMAP_SKIP_NEVER_NULL_USAGE);
EXPECT_EQ(context.test_data.object->data, other_mesh);
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index 64731be57ac..53b1a9c9e16 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -643,8 +643,8 @@ ListBase *which_libbase(Main *bmain, short type)
return &(bmain->cachefiles);
case ID_WS:
return &(bmain->workspaces);
- case ID_HA:
- return &(bmain->hairs);
+ case ID_CV:
+ return &(bmain->hair_curves);
case ID_PT:
return &(bmain->pointclouds);
case ID_VO:
@@ -688,7 +688,7 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/])
lb[INDEX_ID_ME] = &(bmain->meshes);
lb[INDEX_ID_CU] = &(bmain->curves);
lb[INDEX_ID_MB] = &(bmain->metaballs);
- lb[INDEX_ID_HA] = &(bmain->hairs);
+ lb[INDEX_ID_CV] = &(bmain->hair_curves);
lb[INDEX_ID_PT] = &(bmain->pointclouds);
lb[INDEX_ID_VO] = &(bmain->volumes);
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index d6035887790..1c1b2c2cd27 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -36,10 +36,10 @@
#include "DNA_anim_types.h"
#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
+#include "DNA_curves_types.h"
#include "DNA_customdata_types.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_types.h"
-#include "DNA_hair_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -341,9 +341,9 @@ Material ***BKE_object_material_array_p(Object *ob)
bGPdata *gpd = ob->data;
return &(gpd->mat);
}
- if (ob->type == OB_HAIR) {
- Hair *hair = ob->data;
- return &(hair->mat);
+ if (ob->type == OB_CURVES) {
+ Curves *curves = ob->data;
+ return &(curves->mat);
}
if (ob->type == OB_POINTCLOUD) {
PointCloud *pointcloud = ob->data;
@@ -374,9 +374,9 @@ short *BKE_object_material_len_p(Object *ob)
bGPdata *gpd = ob->data;
return &(gpd->totcol);
}
- if (ob->type == OB_HAIR) {
- Hair *hair = ob->data;
- return &(hair->totcol);
+ if (ob->type == OB_CURVES) {
+ Curves *curves = ob->data;
+ return &(curves->totcol);
}
if (ob->type == OB_POINTCLOUD) {
PointCloud *pointcloud = ob->data;
@@ -403,8 +403,8 @@ Material ***BKE_id_material_array_p(ID *id)
return &(((MetaBall *)id)->mat);
case ID_GD:
return &(((bGPdata *)id)->mat);
- case ID_HA:
- return &(((Hair *)id)->mat);
+ case ID_CV:
+ return &(((Curves *)id)->mat);
case ID_PT:
return &(((PointCloud *)id)->mat);
case ID_VO:
@@ -429,8 +429,8 @@ short *BKE_id_material_len_p(ID *id)
return &(((MetaBall *)id)->totcol);
case ID_GD:
return &(((bGPdata *)id)->totcol);
- case ID_HA:
- return &(((Hair *)id)->totcol);
+ case ID_CV:
+ return &(((Curves *)id)->totcol);
case ID_PT:
return &(((PointCloud *)id)->totcol);
case ID_VO:
@@ -454,7 +454,7 @@ static void material_data_index_remove_id(ID *id, short index)
BKE_curve_material_index_remove((Curve *)id, index);
break;
case ID_MB:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
/* No material indices for these object data types. */
@@ -509,7 +509,7 @@ static void material_data_index_clear_id(ID *id)
BKE_curve_material_index_clear((Curve *)id);
break;
case ID_MB:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
/* No material indices for these object data types. */
@@ -720,8 +720,9 @@ static ID *get_evaluated_object_data_with_materials(Object *ob)
/* Meshes in edit mode need special handling. */
if (ob->type == OB_MESH && ob->mode == OB_MODE_EDIT) {
Mesh *mesh = ob->data;
- if (mesh->edit_mesh && mesh->edit_mesh->mesh_eval_final) {
- data = &mesh->edit_mesh->mesh_eval_final->id;
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob);
+ if (mesh->edit_mesh && editmesh_eval_final) {
+ data = &editmesh_eval_final->id;
}
}
return data;
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c
index ac6b0a04def..3c5cdb1ba78 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.c
@@ -15,6 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ *
* MetaBalls are created from a single Object (with a name without number in it),
* here the DispList and BoundBox also is located.
* All objects with the same name (but with a number in it) are added to this.
@@ -22,10 +27,6 @@
* texture coordinates are patched within the displist
*/
-/** \file
- * \ingroup bke
- */
-
#include <ctype.h>
#include <float.h>
#include <math.h>
diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc
index 9b0cf17a424..c1b1f62a881 100644
--- a/source/blender/blenkernel/intern/mesh.cc
+++ b/source/blender/blenkernel/intern/mesh.cc
@@ -173,19 +173,26 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
BKE_mesh_assert_normals_dirty_or_calculated(mesh_dst);
}
+void BKE_mesh_free_editmesh(struct Mesh *mesh)
+{
+ if (mesh->edit_mesh == nullptr) {
+ return;
+ }
+
+ if (mesh->edit_mesh->is_shallow_copy == false) {
+ BKE_editmesh_free_data(mesh->edit_mesh);
+ }
+ MEM_freeN(mesh->edit_mesh);
+ mesh->edit_mesh = nullptr;
+}
+
static void mesh_free_data(ID *id)
{
Mesh *mesh = (Mesh *)id;
BLI_freelistN(&mesh->vertex_group_names);
- if (mesh->edit_mesh) {
- if (mesh->edit_mesh->is_shallow_copy == false) {
- BKE_editmesh_free_data(mesh->edit_mesh);
- }
- MEM_freeN(mesh->edit_mesh);
- mesh->edit_mesh = nullptr;
- }
+ BKE_mesh_free_editmesh(mesh);
BKE_mesh_runtime_free_data(mesh);
mesh_clear_geometry(mesh);
@@ -678,6 +685,17 @@ static int customdata_compare(
}
break;
}
+ case CD_PROP_INT8: {
+ const int8_t *l1_data = (int8_t *)l1->data;
+ const int8_t *l2_data = (int8_t *)l2->data;
+
+ for (int i = 0; i < total_length; i++) {
+ if (l1_data[i] != l2_data[i]) {
+ return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
+ }
+ }
+ break;
+ }
case CD_PROP_BOOL: {
const bool *l1_data = (bool *)l1->data;
const bool *l2_data = (bool *)l2->data;
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index cbc772d93a6..7d5f156040d 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -1102,8 +1102,11 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
Mesh *mesh_input = (Mesh *)object->data;
/* If we are in edit mode, use evaluated mesh from edit structure, matching to what
* viewport is using for visualization. */
- if (mesh_input->edit_mesh != nullptr && mesh_input->edit_mesh->mesh_eval_final) {
- mesh_input = mesh_input->edit_mesh->mesh_eval_final;
+ if (mesh_input->edit_mesh != nullptr) {
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object);
+ if (editmesh_eval_final != nullptr) {
+ mesh_input = editmesh_eval_final;
+ }
}
return mesh_new_from_mesh(object, mesh_input);
}
diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc
index 50db1bc1564..d0a57310fcf 100644
--- a/source/blender/blenkernel/intern/mesh_fair.cc
+++ b/source/blender/blenkernel/intern/mesh_fair.cc
@@ -12,13 +12,12 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
- * https://github.com/fedackb/mesh-fairing.
*/
/** \file
* \ingroup bke
+ * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
+ * https://github.com/fedackb/mesh-fairing.
*/
#include "BLI_map.hh"
diff --git a/source/blender/blenkernel/intern/mesh_iterators.c b/source/blender/blenkernel/intern/mesh_iterators.c
index b77d0e38f0f..4fb03c7b329 100644
--- a/source/blender/blenkernel/intern/mesh_iterators.c
+++ b/source/blender/blenkernel/intern/mesh_iterators.c
@@ -34,13 +34,23 @@
#include "MEM_guardedalloc.h"
+/* General note on iterating verts/loops/edges/polys and end mode.
+ *
+ * The edit mesh pointer is set for both final and cage meshes in both cases when there are
+ * modifiers applied and not. This helps consistency of checks in the draw manager, where the
+ * existence of the edit mesh pointer does not depend on object configuration.
+ *
+ * For the iterating, however, we need to follow the `CD_ORIGINDEX` code paths when there are
+ * modifiers applied on the cage. In the code terms it means that the check for the edit mode code
+ * path needs to consist of both edit mesh and edit data checks. */
+
void BKE_mesh_foreach_mapped_vert(
Mesh *mesh,
void (*func)(void *userData, int index, const float co[3], const float no[3]),
void *userData,
MeshForeachFlag flag)
{
- if (mesh->edit_mesh != NULL) {
+ if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) {
BMEditMesh *em = mesh->edit_mesh;
BMesh *bm = em->bm;
BMIter iter;
@@ -100,7 +110,7 @@ void BKE_mesh_foreach_mapped_edge(
void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]),
void *userData)
{
- if (mesh->edit_mesh != NULL) {
+ if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) {
BMEditMesh *em = mesh->edit_mesh;
BMesh *bm = em->bm;
BMIter iter;
@@ -158,7 +168,7 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh,
/* We can't use dm->getLoopDataLayout(dm) here,
* we want to always access dm->loopData, EditDerivedBMesh would
* return loop data from bmesh itself. */
- if (mesh->edit_mesh != NULL) {
+ if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) {
BMEditMesh *em = mesh->edit_mesh;
BMesh *bm = em->bm;
BMIter iter;
@@ -231,7 +241,7 @@ void BKE_mesh_foreach_mapped_face_center(
void *userData,
MeshForeachFlag flag)
{
- if (mesh->edit_mesh != NULL) {
+ if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) {
BMEditMesh *em = mesh->edit_mesh;
BMesh *bm = em->bm;
const float(*polyCos)[3];
diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c
index 134a1344f83..3c01d5a4a50 100644
--- a/source/blender/blenkernel/intern/mesh_merge.c
+++ b/source/blender/blenkernel/intern/mesh_merge.c
@@ -27,6 +27,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BLI_bitmap.h"
#include "BLI_edgehash.h"
#include "BLI_ghash.h"
#include "BLI_utildefines.h"
@@ -351,6 +352,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
&poly_map, &poly_map_mem, mesh->mpoly, mesh->mloop, totvert, totpoly, totloop);
} /* done preparing for fast poly compare */
+ BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__);
+
mp = mesh->mpoly;
mv = mesh->mvert;
for (i = 0; i < totpoly; i++, mp++) {
@@ -365,11 +368,11 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
if (vtargetmap[ml->v] == -1) {
all_vertices_merged = false;
/* This will be used to check for poly using several time the same vert. */
- mv[ml->v].flag &= ~ME_VERT_TMP_TAG;
+ BLI_BITMAP_DISABLE(vert_tag, ml->v);
}
else {
/* This will be used to check for poly using several time the same vert. */
- mv[vtargetmap[ml->v]].flag &= ~ME_VERT_TMP_TAG;
+ BLI_BITMAP_DISABLE(vert_tag, vtargetmap[ml->v]);
}
}
@@ -457,8 +460,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
#endif
/* A loop is only valid if its matching edge is,
* and it's not reusing a vertex already used by this poly. */
- if (LIKELY((newe[ml->e] != -1) && ((mv[mlv].flag & ME_VERT_TMP_TAG) == 0))) {
- mv[mlv].flag |= ME_VERT_TMP_TAG;
+ if (LIKELY((newe[ml->e] != -1) && !BLI_BITMAP_TEST(vert_tag, mlv))) {
+ BLI_BITMAP_ENABLE(vert_tag, mlv);
if (UNLIKELY(last_valid_ml != NULL && need_edge_from_last_valid_ml)) {
/* We need to create a new edge between last valid loop and this one! */
@@ -644,6 +647,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
MEM_freeN(oldl);
MEM_freeN(oldp);
+ MEM_freeN(vert_tag);
+
BLI_edgehash_free(ehash, NULL);
if (poly_map != NULL) {
diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c
index 652399a7294..ec3655ac491 100644
--- a/source/blender/blenkernel/intern/mesh_mirror.c
+++ b/source/blender/blenkernel/intern/mesh_mirror.c
@@ -236,7 +236,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
}
/* Copy custom-data to new geometry,
- * copy from its self because this data may have been created in the checks above. */
+ * copy from itself because this data may have been created in the checks above. */
CustomData_copy_data(&result->vdata, &result->vdata, 0, maxVerts, maxVerts);
CustomData_copy_data(&result->edata, &result->edata, 0, maxEdges, maxEdges);
/* loops are copied later */
@@ -420,7 +420,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
/* calculate custom normals into loop_normals, then mirror first half into second half */
BKE_mesh_normals_loop_split(result->mvert,
- BKE_mesh_vertex_normals_ensure(mesh),
+ BKE_mesh_vertex_normals_ensure(result),
result->totvert,
result->medge,
result->totedge,
@@ -428,7 +428,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
loop_normals,
totloop,
result->mpoly,
- BKE_mesh_poly_normals_ensure(mesh),
+ BKE_mesh_poly_normals_ensure(result),
totpoly,
true,
mesh->smoothresh,
diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc
index 08a17060549..1b3c7e01be8 100644
--- a/source/blender/blenkernel/intern/mesh_normals.cc
+++ b/source/blender/blenkernel/intern/mesh_normals.cc
@@ -43,6 +43,7 @@
#include "BLI_span.hh"
#include "BLI_stack.h"
#include "BLI_task.h"
+#include "BLI_task.hh"
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
@@ -373,22 +374,28 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3]
return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL);
}
- Mesh &mesh_mutable = *const_cast<Mesh *>(mesh);
+ float(*vert_normals)[3];
+ float(*poly_normals)[3];
- float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(&mesh_mutable);
- float(*poly_normals)[3] = BKE_mesh_poly_normals_for_write(&mesh_mutable);
+ /* Isolate task because a mutex is locked and computing normals is multi-threaded. */
+ blender::threading::isolate_task([&]() {
+ Mesh &mesh_mutable = *const_cast<Mesh *>(mesh);
- mesh_calc_normals_poly_and_vertex(mesh_mutable.mvert,
- mesh_mutable.totvert,
- mesh_mutable.mloop,
- mesh_mutable.totloop,
- mesh_mutable.mpoly,
- mesh_mutable.totpoly,
- poly_normals,
- vert_normals);
+ vert_normals = BKE_mesh_vertex_normals_for_write(&mesh_mutable);
+ poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable);
- BKE_mesh_vertex_normals_clear_dirty(&mesh_mutable);
- BKE_mesh_poly_normals_clear_dirty(&mesh_mutable);
+ mesh_calc_normals_poly_and_vertex(mesh_mutable.mvert,
+ mesh_mutable.totvert,
+ mesh_mutable.mloop,
+ mesh_mutable.totloop,
+ mesh_mutable.mpoly,
+ mesh_mutable.totpoly,
+ poly_normals,
+ vert_normals);
+
+ BKE_mesh_vertex_normals_clear_dirty(&mesh_mutable);
+ BKE_mesh_poly_normals_clear_dirty(&mesh_mutable);
+ });
BLI_mutex_unlock(normals_mutex);
return vert_normals;
@@ -413,19 +420,24 @@ const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3]
return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL);
}
- Mesh &mesh_mutable = *const_cast<Mesh *>(mesh);
+ float(*poly_normals)[3];
+
+ /* Isolate task because a mutex is locked and computing normals is multi-threaded. */
+ blender::threading::isolate_task([&]() {
+ Mesh &mesh_mutable = *const_cast<Mesh *>(mesh);
- float(*poly_normals)[3] = BKE_mesh_poly_normals_for_write(&mesh_mutable);
+ poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable);
- BKE_mesh_calc_normals_poly(mesh_mutable.mvert,
- mesh_mutable.totvert,
- mesh_mutable.mloop,
- mesh_mutable.totloop,
- mesh_mutable.mpoly,
- mesh_mutable.totpoly,
- poly_normals);
+ BKE_mesh_calc_normals_poly(mesh_mutable.mvert,
+ mesh_mutable.totvert,
+ mesh_mutable.mloop,
+ mesh_mutable.totloop,
+ mesh_mutable.mpoly,
+ mesh_mutable.totpoly,
+ poly_normals);
- BKE_mesh_poly_normals_clear_dirty(&mesh_mutable);
+ BKE_mesh_poly_normals_clear_dirty(&mesh_mutable);
+ });
BLI_mutex_unlock(normals_mutex);
return poly_normals;
diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c
index 005c916b4e0..a5ba2767301 100644
--- a/source/blender/blenkernel/intern/mesh_validate.c
+++ b/source/blender/blenkernel/intern/mesh_validate.c
@@ -28,6 +28,7 @@
#include "CLG_log.h"
+#include "BLI_bitmap.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -560,6 +561,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
* so be sure to leave at most one poly per loop!
*/
{
+ BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__);
+
SortPoly *sort_polys = MEM_callocN(sizeof(SortPoly) * totpoly, "mesh validate's sort_polys");
SortPoly *prev_sp, *sp = sort_polys;
int prev_end;
@@ -608,7 +611,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
* so we have to ensure here all verts of current poly are cleared. */
for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) {
if (ml->v < totvert) {
- mverts[ml->v].flag &= ~ME_VERT_TMP_TAG;
+ BLI_BITMAP_DISABLE(vert_tag, ml->v);
}
}
@@ -619,12 +622,12 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
PRINT_ERR("\tLoop %u has invalid vert reference (%u)", sp->loopstart + j, ml->v);
sp->invalid = true;
}
- else if (mverts[ml->v].flag & ME_VERT_TMP_TAG) {
+ else if (BLI_BITMAP_TEST(vert_tag, ml->v)) {
PRINT_ERR("\tPoly %u has duplicated vert reference at corner (%u)", i, j);
sp->invalid = true;
}
else {
- mverts[ml->v].flag |= ME_VERT_TMP_TAG;
+ BLI_BITMAP_ENABLE(vert_tag, ml->v);
}
*v = ml->v;
}
@@ -698,6 +701,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
}
}
+ MEM_freeN(vert_tag);
+
/* Second check pass, testing polys using the same verts. */
qsort(sort_polys, totpoly, sizeof(SortPoly), search_poly_cmp);
sp = prev_sp = sort_polys;
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index c1a723bcc81..598d4e495d6 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -15,13 +15,12 @@
*
* The Original Code is Copyright (C) 2005 by the Blender Foundation.
* All rights reserved.
- * Modifier stack implementation.
- *
- * BKE_modifier.h contains the function prototypes for this file.
*/
/** \file
* \ingroup bke
+ * Modifier stack implementation.
+ * BKE_modifier.h contains the function prototypes for this file.
*/
/* Allow using deprecated functionality for .blend file I/O. */
@@ -291,6 +290,16 @@ ModifierData *BKE_modifiers_findby_name(const Object *ob, const char *name)
return BLI_findstring(&(ob->modifiers), name, offsetof(ModifierData, name));
}
+ModifierData *BKE_modifiers_findby_session_uuid(const Object *ob, const SessionUUID *session_uuid)
+{
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (BLI_session_uuid_is_equal(&md->session_uuid, session_uuid)) {
+ return md;
+ }
+ }
+ return NULL;
+}
+
void BKE_modifiers_clear_errors(Object *ob)
{
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
@@ -440,9 +449,7 @@ void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_for
#ifndef NDEBUG
if ((md->mode & eModifierMode_Virtual) == 0) {
/* Ensure correct object is passed in. */
- const Object *ob_orig = (Object *)DEG_get_original_id((ID *)&ob->id);
- const ModifierData *md_orig = md->orig_modifier_data ? md->orig_modifier_data : md;
- BLI_assert(BLI_findindex(&ob_orig->modifiers, md_orig) != -1);
+ BLI_assert(BKE_modifier_get_original(ob, md) != NULL);
}
#endif
@@ -1037,8 +1044,11 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval,
BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
/* 'em' might not exist yet in some cases, just after loading a .blend file, see T57878. */
if (em != NULL) {
- me = (get_cage_mesh && em->mesh_eval_cage != NULL) ? em->mesh_eval_cage :
- em->mesh_eval_final;
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval);
+ Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval);
+
+ me = (get_cage_mesh && editmesh_eval_cage != NULL) ? editmesh_eval_cage :
+ editmesh_eval_final;
}
}
if (me == NULL) {
@@ -1050,12 +1060,10 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval,
return me;
}
-ModifierData *BKE_modifier_get_original(ModifierData *md)
+ModifierData *BKE_modifier_get_original(const Object *object, ModifierData *md)
{
- if (md->orig_modifier_data == NULL) {
- return md;
- }
- return md->orig_modifier_data;
+ const Object *object_orig = DEG_get_original_object((Object *)object);
+ return BKE_modifiers_findby_session_uuid(object_orig, &md->session_uuid);
}
struct ModifierData *BKE_modifier_get_evaluated(Depsgraph *depsgraph,
@@ -1066,7 +1074,7 @@ struct ModifierData *BKE_modifier_get_evaluated(Depsgraph *depsgraph,
if (object_eval == object) {
return md;
}
- return BKE_modifiers_findby_name(object_eval, md->name);
+ return BKE_modifiers_findby_session_uuid(object_eval, &md->session_uuid);
}
void BKE_modifier_check_uuids_unique_and_report(const Object *object)
@@ -1180,8 +1188,8 @@ void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase)
#if 0
CollisionModifierData *collmd = (CollisionModifierData *)md;
- // TODO: CollisionModifier should use pointcache
- // + have proper reset events before enabling this
+ /* TODO: CollisionModifier should use pointcache
+ * + have proper reset events before enabling this. */
writestruct(wd, DATA, MVert, collmd->numverts, collmd->x);
writestruct(wd, DATA, MVert, collmd->numverts, collmd->xnew);
writestruct(wd, DATA, MFace, collmd->numfaces, collmd->mfaces);
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index b3bf80cd448..a1c10a733ce 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -648,7 +648,6 @@ static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_addres
bNodeTree *ntree = (bNodeTree *)id;
/* Clean up, important in undo case to reduce false detection of changed datablocks. */
- ntree->init = 0; /* to set callbacks and force setting types */
ntree->is_updating = false;
ntree->typeinfo = nullptr;
ntree->interface_type = nullptr;
@@ -677,7 +676,6 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
{
/* NOTE: writing and reading goes in sync, for speed. */
- ntree->init = 0; /* to set callbacks and force setting types */
ntree->is_updating = false;
ntree->typeinfo = nullptr;
ntree->interface_type = nullptr;
@@ -1145,8 +1143,6 @@ static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo)
}
else {
ntree->typeinfo = &NodeTreeTypeUndefined;
-
- ntree->init &= ~NTREE_TYPE_INIT;
}
/* Deprecated integer type. */
@@ -1177,8 +1173,6 @@ static void node_set_typeinfo(const struct bContext *C,
}
else {
node->typeinfo = &NodeTypeUndefined;
-
- ntree->init &= ~NTREE_TYPE_INIT;
}
}
@@ -1199,8 +1193,6 @@ static void node_socket_set_typeinfo(bNodeTree *ntree,
}
else {
sock->typeinfo = &NodeSocketTypeUndefined;
-
- ntree->init &= ~NTREE_TYPE_INIT;
}
BKE_ntree_update_tag_socket_type(ntree, sock);
}
@@ -1218,8 +1210,6 @@ static void update_typeinfo(Main *bmain,
}
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
- ntree->init |= NTREE_TYPE_INIT;
-
if (treetype && STREQ(ntree->idname, treetype->idname)) {
ntree_set_typeinfo(ntree, unregister ? nullptr : treetype);
}
@@ -1260,8 +1250,6 @@ static void update_typeinfo(Main *bmain,
void ntreeSetTypes(const struct bContext *C, bNodeTree *ntree)
{
- ntree->init |= NTREE_TYPE_INIT;
-
ntree_set_typeinfo(ntree, ntreeTypeFind(ntree->idname));
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -2674,11 +2662,6 @@ bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname)
ntree->id.flag |= LIB_EMBEDDED_DATA;
}
- /* Types are fully initialized at this point,
- * if an undefined node is added later this will be reset.
- */
- ntree->init |= NTREE_TYPE_INIT;
-
BLI_strncpy(ntree->idname, idname, sizeof(ntree->idname));
ntree_set_typeinfo(ntree, ntreeTypeFind(idname));
@@ -4503,6 +4486,8 @@ static void registerCompositNodes()
register_node_type_cmp_sepycca();
register_node_type_cmp_combycca();
register_node_type_cmp_premulkey();
+ register_node_type_cmp_separate_xyz();
+ register_node_type_cmp_combine_xyz();
register_node_type_cmp_diff_matte();
register_node_type_cmp_distance_matte();
@@ -4668,6 +4653,7 @@ static void registerTextureNodes()
register_node_type_sh_tangent();
register_node_type_sh_normal_map();
register_node_type_sh_hair_info();
+ register_node_type_sh_point_info();
register_node_type_sh_volume_info();
register_node_type_tex_checker();
@@ -4746,6 +4732,7 @@ static void registerGeometryNodes()
register_node_type_geo_curve_fillet();
register_node_type_geo_curve_handle_type_selection();
register_node_type_geo_curve_length();
+ register_node_type_geo_curve_primitive_arc();
register_node_type_geo_curve_primitive_bezier_segment();
register_node_type_geo_curve_primitive_circle();
register_node_type_geo_curve_primitive_line();
@@ -4767,7 +4754,9 @@ static void registerGeometryNodes()
register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_dual_mesh();
register_node_type_geo_edge_split();
+ register_node_type_geo_extrude_mesh();
register_node_type_geo_field_at_index();
+ register_node_type_geo_flip_faces();
register_node_type_geo_geometry_to_instance();
register_node_type_geo_image_texture();
register_node_type_geo_input_curve_handles();
@@ -4798,6 +4787,7 @@ static void registerGeometryNodes()
register_node_type_geo_join_geometry();
register_node_type_geo_material_replace();
register_node_type_geo_material_selection();
+ register_node_type_geo_merge_by_distance();
register_node_type_geo_mesh_primitive_circle();
register_node_type_geo_mesh_primitive_cone();
register_node_type_geo_mesh_primitive_cube();
@@ -4823,6 +4813,7 @@ static void registerGeometryNodes()
register_node_type_geo_realize_instances();
register_node_type_geo_rotate_instances();
register_node_type_geo_sample_texture();
+ register_node_type_geo_scale_elements();
register_node_type_geo_scale_instances();
register_node_type_geo_separate_components();
register_node_type_geo_separate_geometry();
diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc
index be9777281cb..904a0de9a90 100644
--- a/source/blender/blenkernel/intern/node_tree_update.cc
+++ b/source/blender/blenkernel/intern/node_tree_update.cc
@@ -34,6 +34,7 @@
#include "NOD_node_declaration.hh"
#include "NOD_node_tree_ref.hh"
+#include "NOD_texture.h"
#include "DEG_depsgraph_query.h"
@@ -272,6 +273,12 @@ static OutputFieldDependency find_group_output_dependencies(
while (!sockets_to_check.is_empty()) {
const InputSocketRef *input_socket = sockets_to_check.pop();
+ if (!input_socket->is_directly_linked() &&
+ !field_state_by_socket_id[input_socket->id()].is_single) {
+ /* This socket uses a field as input by default. */
+ return OutputFieldDependency::ForFieldSource();
+ }
+
for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) {
const NodeRef &origin_node = origin_socket->node();
const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()];
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index 5045851d7f9..8faae6efb26 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -87,7 +87,9 @@
#include "BKE_camera.h"
#include "BKE_collection.h"
#include "BKE_constraint.h"
+#include "BKE_crazyspace.h"
#include "BKE_curve.h"
+#include "BKE_curves.h"
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_duplilist.h"
@@ -102,7 +104,6 @@
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
-#include "BKE_hair.h"
#include "BKE_icons.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
@@ -324,66 +325,6 @@ static void object_free_data(ID *id)
BKE_previewimg_free(&ob->preview);
}
-static void object_make_local(Main *bmain, ID *id, const int flags)
-{
- if (!ID_IS_LINKED(id)) {
- return;
- }
-
- Object *ob = (Object *)id;
- const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
- const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0;
- bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
- bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
- BLI_assert(force_copy == false || force_copy != force_local);
-
- bool is_local = false, is_lib = false;
-
- /* - only lib users: do nothing (unless force_local is set)
- * - only local users: set flag
- * - mixed: make copy
- * In case we make a whole lib's content local,
- * we always want to localize, and we skip remapping (done later).
- */
-
- if (!force_local && !force_copy) {
- BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib);
- if (lib_local || is_local) {
- if (!is_lib) {
- force_local = true;
- }
- else {
- force_copy = true;
- }
- }
- }
-
- if (force_local) {
- BKE_lib_id_clear_library_data(bmain, &ob->id, flags);
- BKE_lib_id_expand_local(bmain, &ob->id, flags);
- if (clear_proxy) {
- if (ob->proxy_from != nullptr) {
- ob->proxy_from->proxy = nullptr;
- ob->proxy_from->proxy_group = nullptr;
- }
- ob->proxy = ob->proxy_from = ob->proxy_group = nullptr;
- }
- }
- else if (force_copy) {
- Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id);
- id_us_min(&ob_new->id);
-
- ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = nullptr;
-
- /* setting newid is mandatory for complex make_lib_local logic... */
- ID_NEW_SET(ob, ob_new);
-
- if (!lib_local) {
- BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE);
- }
- }
-}
-
static void library_foreach_modifiersForeachIDLink(void *user_data,
Object *UNUSED(object),
ID **id_pointer,
@@ -439,51 +380,25 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
{
Object *object = (Object *)id;
- /* Object is special, proxies make things hard... */
- const int proxy_cb_flag = ((BKE_lib_query_foreachid_process_flags_get(data) &
- IDWALK_NO_INDIRECT_PROXY_DATA_USAGE) == 0 &&
- (object->proxy || object->proxy_group)) ?
- IDWALK_CB_INDIRECT_USAGE :
- 0;
-
/* object data special case */
if (object->type == OB_EMPTY) {
/* empty can have nullptr or Image */
- BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, proxy_cb_flag | IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, IDWALK_CB_USER);
}
else {
/* when set, this can't be nullptr */
if (object->data) {
- BKE_LIB_FOREACHID_PROCESS_ID(
- data, object->data, proxy_cb_flag | IDWALK_CB_USER | IDWALK_CB_NEVER_NULL);
+ BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL);
}
}
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF);
- /* object->proxy is refcounted, but not object->proxy_group... *sigh* */
- BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF);
- BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP);
-
- /* Special case!
- * Since this field is set/owned by 'user' of this ID (and not ID itself),
- * it is only indirect usage if proxy object is linked... Twisted. */
- {
- const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override(
- data,
- (object->proxy_from != nullptr && ID_IS_LINKED(object->proxy_from)) ?
- IDWALK_CB_INDIRECT_USAGE :
- 0,
- true);
- BKE_LIB_FOREACHID_PROCESS_IDSUPER(
- data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF);
- BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true);
- }
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER);
for (int i = 0; i < object->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], IDWALK_CB_USER);
}
/* Note that ob->gpd is deprecated, so no need to handle it here. */
@@ -496,8 +411,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
/* Note that ob->effect is deprecated, so no need to handle it here. */
if (object->pose) {
- const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override(
- data, proxy_cb_flag, false);
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
data,
@@ -512,7 +425,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_constraints_id_loop(
&pchan->constraints, library_foreach_constraintObjectLooper, data));
}
- BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true);
}
if (object->rigidbody_constraint) {
@@ -647,9 +559,6 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre
bArmature *arm = nullptr;
if (ob->type == OB_ARMATURE) {
arm = (bArmature *)ob->data;
- if (arm && ob->pose && arm->act_bone) {
- BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone));
- }
}
BKE_pose_blend_write(writer, ob->pose, arm);
@@ -1325,7 +1234,7 @@ IDTypeInfo IDType_ID_OB = {
/* init_data */ object_init_data,
/* copy_data */ object_copy_data,
/* free_data */ object_free_data,
- /* make_local */ object_make_local,
+ /* make_local */ nullptr,
/* foreach_id */ object_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ object_foreach_path,
@@ -1506,10 +1415,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
}
/* Only geometry objects should be able to get modifiers T25291. */
- if (ob->type == OB_HAIR) {
- return (mti->modifyHair != nullptr) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
- }
- if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) {
+ if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME, OB_CURVES)) {
return (mti->modifyGeometrySet != nullptr);
}
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
@@ -1867,6 +1773,12 @@ void BKE_object_free_derived_caches(Object *ob)
object_update_from_subsurf_ccg(ob);
+ if (ob->runtime.editmesh_eval_cage &&
+ ob->runtime.editmesh_eval_cage != reinterpret_cast<Mesh *>(ob->runtime.data_eval)) {
+ BKE_mesh_eval_delete(ob->runtime.editmesh_eval_cage);
+ }
+ ob->runtime.editmesh_eval_cage = nullptr;
+
if (ob->runtime.data_eval != nullptr) {
if (ob->runtime.is_data_eval_owned) {
ID *data_eval = ob->runtime.data_eval;
@@ -1896,6 +1808,8 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_object_to_curve_clear(ob);
BKE_object_free_curve_cache(ob);
+ BKE_crazyspace_api_eval_clear(ob);
+
/* Clear grease pencil data. */
if (ob->runtime.gpd_eval != nullptr) {
BKE_gpencil_eval_delete(ob->runtime.gpd_eval);
@@ -1906,6 +1820,8 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_geometry_set_free(ob->runtime.geometry_set_eval);
ob->runtime.geometry_set_eval = nullptr;
}
+
+ MEM_SAFE_FREE(ob->runtime.editmesh_bb_cage);
}
void BKE_object_free_caches(Object *object)
@@ -2182,8 +2098,8 @@ static const char *get_obdata_defname(int type)
return DATA_("Armature");
case OB_SPEAKER:
return DATA_("Speaker");
- case OB_HAIR:
- return DATA_("Hair");
+ case OB_CURVES:
+ return DATA_("HairCurves");
case OB_POINTCLOUD:
return DATA_("PointCloud");
case OB_VOLUME:
@@ -2257,8 +2173,8 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name)
return BKE_lightprobe_add(bmain, name);
case OB_GPENCIL:
return BKE_gpencil_data_addnew(bmain, name);
- case OB_HAIR:
- return BKE_hair_add(bmain, name);
+ case OB_CURVES:
+ return BKE_curves_add(bmain, name);
case OB_POINTCLOUD:
return BKE_pointcloud_add_default(bmain, name);
case OB_VOLUME:
@@ -2295,8 +2211,8 @@ int BKE_object_obdata_to_type(const ID *id)
return OB_ARMATURE;
case ID_LP:
return OB_LIGHTPROBE;
- case ID_HA:
- return OB_HAIR;
+ case ID_CV:
+ return OB_CURVES;
case ID_PT:
return OB_POINTCLOUD;
case ID_VO:
@@ -2813,8 +2729,8 @@ Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplica
id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
- case OB_HAIR:
- if (dupflag & USER_DUP_HAIR) {
+ case OB_CURVES:
+ if (dupflag & USER_DUP_CURVES) {
id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
@@ -2886,161 +2802,6 @@ bool BKE_object_obdata_is_libdata(const Object *ob)
return (ob && ob->data && ID_IS_LINKED(ob->data));
}
-/* -------------------------------------------------------------------- */
-/** \name Object Proxy API
- * \{ */
-
-/* when you make proxy, ensure the exposed layers are extern */
-static void armature_set_id_extern(Object *ob)
-{
- bArmature *arm = (bArmature *)ob->data;
- unsigned int lay = arm->layer_protected;
-
- LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
- if (!(pchan->bone->layer & lay)) {
- id_lib_extern((ID *)pchan->custom);
- }
- }
-}
-
-void BKE_object_copy_proxy_drivers(Object *ob, Object *target)
-{
- if ((target->adt) && (target->adt->drivers.first)) {
-
- /* add new animdata block */
- if (!ob->adt) {
- ob->adt = BKE_animdata_ensure_id(&ob->id);
- }
-
- /* make a copy of all the drivers (for now), then correct any links that need fixing */
- BKE_fcurves_free(&ob->adt->drivers);
- BKE_fcurves_copy(&ob->adt->drivers, &target->adt->drivers);
-
- LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->drivers) {
- ChannelDriver *driver = fcu->driver;
-
- LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
- /* all drivers */
- DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
- if (dtar->id) {
- if ((Object *)dtar->id == target) {
- dtar->id = (ID *)ob;
- }
- else {
- /* only on local objects because this causes indirect links
- * 'a -> b -> c', blend to point directly to a.blend
- * when a.blend has a proxy that's linked into `c.blend`. */
- if (!ID_IS_LINKED(ob)) {
- id_lib_extern((ID *)dtar->id);
- }
- }
- }
- }
- DRIVER_TARGETS_LOOPER_END;
- }
- }
- }
-}
-
-void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob)
-{
- /* paranoia checks */
- if (ID_IS_LINKED(ob) || !ID_IS_LINKED(target)) {
- CLOG_ERROR(&LOG, "cannot make proxy");
- return;
- }
-
- ob->proxy = target;
- id_us_plus(&target->id);
- ob->proxy_group = cob;
-
- DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
- DEG_id_tag_update(&target->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
-
- /* copy transform
- * - cob means this proxy comes from a collection, just apply the matrix
- * so the object won't move from its dupli-transform.
- *
- * - no cob means this is being made from a linked object,
- * this is closer to making a copy of the object - in-place. */
- if (cob) {
- ob->rotmode = target->rotmode;
- mul_m4_m4m4(ob->obmat, cob->obmat, target->obmat);
- if (cob->instance_collection) { /* should always be true */
- float tvec[3];
- mul_v3_mat3_m4v3(tvec, ob->obmat, cob->instance_collection->instance_offset);
- sub_v3_v3(ob->obmat[3], tvec);
- }
- BKE_object_apply_mat4(ob, ob->obmat, false, true);
- }
- else {
- BKE_object_transform_copy(ob, target);
- ob->parent = target->parent; /* libdata */
- copy_m4_m4(ob->parentinv, target->parentinv);
- }
-
- /* copy animdata stuff - drivers only for now... */
- BKE_object_copy_proxy_drivers(ob, target);
-
- /* skip constraints? */
- /* FIXME: this is considered by many as a bug */
-
- /* set object type and link to data */
- ob->type = target->type;
- ob->data = target->data;
- id_us_plus((ID *)ob->data); /* ensures lib data becomes LIB_TAG_EXTERN */
-
- /* copy material and index information */
- ob->actcol = ob->totcol = 0;
- if (ob->mat) {
- MEM_freeN(ob->mat);
- }
- if (ob->matbits) {
- MEM_freeN(ob->matbits);
- }
- ob->mat = nullptr;
- ob->matbits = nullptr;
- if ((target->totcol) && (target->mat) && OB_TYPE_SUPPORT_MATERIAL(ob->type)) {
- int i;
-
- ob->actcol = target->actcol;
- ob->totcol = target->totcol;
-
- ob->mat = (Material **)MEM_dupallocN(target->mat);
- ob->matbits = (char *)MEM_dupallocN(target->matbits);
- for (i = 0; i < target->totcol; i++) {
- /* don't need to run BKE_object_materials_test
- * since we know this object is new and not used elsewhere */
- id_us_plus((ID *)ob->mat[i]);
- }
- }
-
- /* type conversions */
- if (target->type == OB_ARMATURE) {
- copy_object_pose(ob, target, 0); /* data copy, object pointers in constraints */
- BKE_pose_rest(ob->pose, false); /* clear all transforms in channels */
- BKE_pose_rebuild(bmain, ob, (bArmature *)ob->data, true); /* set all internal links */
-
- armature_set_id_extern(ob);
- }
- else if (target->type == OB_EMPTY) {
- ob->empty_drawtype = target->empty_drawtype;
- ob->empty_drawsize = target->empty_drawsize;
- }
-
- /* copy IDProperties */
- if (ob->id.properties) {
- IDP_FreeProperty(ob->id.properties);
- ob->id.properties = nullptr;
- }
- if (target->id.properties) {
- ob->id.properties = IDP_CopyProperty(target->id.properties);
- }
-
- /* copy drawtype info */
- ob->dt = target->dt;
-}
-
void BKE_object_obdata_size_init(struct Object *ob, const float size)
{
/* apply radius as a scale to types that support it */
@@ -3082,8 +2843,6 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size)
}
}
-/** \} */
-
/* -------------------------------------------------------------------- */
/** \name Object Matrix Get/Set API
* \{ */
@@ -3411,7 +3170,8 @@ static void give_parvert(Object *par, int nr, float vec[3])
if (par->type == OB_MESH) {
Mesh *me = (Mesh *)par->data;
BMEditMesh *em = me->edit_mesh;
- Mesh *me_eval = (em) ? em->mesh_eval_final : BKE_object_get_evaluated_mesh(par);
+ Mesh *me_eval = (em) ? BKE_object_get_editmesh_eval_final(par) :
+ BKE_object_get_evaluated_mesh(par);
if (me_eval) {
int count = 0;
@@ -3864,8 +3624,8 @@ BoundBox *BKE_object_boundbox_get(Object *ob)
case OB_GPENCIL:
bb = BKE_gpencil_boundbox_get(ob);
break;
- case OB_HAIR:
- bb = BKE_hair_boundbox_get(ob);
+ case OB_CURVES:
+ bb = BKE_curves_boundbox_get(ob);
break;
case OB_POINTCLOUD:
bb = BKE_pointcloud_boundbox_get(ob);
@@ -4066,8 +3826,8 @@ void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool us
}
break;
}
- case OB_HAIR: {
- BoundBox bb = *BKE_hair_boundbox_get(ob);
+ case OB_CURVES: {
+ BoundBox bb = *BKE_curves_boundbox_get(ob);
BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max);
changed = true;
break;
@@ -4198,7 +3958,11 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
/* pass */
}
else {
- BoundBox *bb = BKE_object_boundbox_get(dob->ob);
+ Object temp_ob = *dob->ob;
+ /* Do not modify the original boundbox. */
+ temp_ob.runtime.bb = nullptr;
+ BKE_object_replace_data_on_shallow_copy(&temp_ob, dob->ob_data);
+ BoundBox *bb = BKE_object_boundbox_get(&temp_ob);
if (bb) {
int i;
@@ -4210,6 +3974,8 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
ok = true;
}
+
+ MEM_SAFE_FREE(temp_ob.runtime.bb);
}
}
free_object_duplilist(lb); /* does restore */
@@ -4368,33 +4134,10 @@ void BKE_object_tfm_restore(Object *ob, void *obtfm_pt)
/** \name Object Evaluation/Update API
* \{ */
-static void object_handle_update_proxy(Depsgraph *depsgraph,
- Scene *scene,
- Object *object,
- const bool do_proxy_update)
-{
- /* The case when this is a collection proxy, object_update is called in collection.c */
- if (object->proxy == nullptr) {
- return;
- }
- /* set pointer in library proxy target, for copying, but restore it */
- object->proxy->proxy_from = object;
- // printf("set proxy pointer for later collection stuff %s\n", ob->id.name);
-
- /* the no-group proxy case, we call update */
- if (object->proxy_group == nullptr) {
- if (do_proxy_update) {
- // printf("call update, lib ob %s proxy %s\n", ob->proxy->id.name, ob->id.name);
- BKE_object_handle_update(depsgraph, scene, object->proxy);
- }
- }
-}
-
void BKE_object_handle_update_ex(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
- RigidBodyWorld *rbw,
- const bool do_proxy_update)
+ RigidBodyWorld *rbw)
{
const ID *object_data = (ID *)ob->data;
const bool recalc_object = (ob->id.recalc & ID_RECALC_ALL) != 0;
@@ -4402,7 +4145,6 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
((object_data->recalc & ID_RECALC_ALL) != 0) :
false;
if (!recalc_object && !recalc_data) {
- object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update);
return;
}
/* Speed optimization for animation lookups. */
@@ -4431,22 +4173,17 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
printf("recalcob %s\n", ob->id.name + 2);
}
- /* Handle proxy copy for target. */
- if (!BKE_object_eval_proxy_copy(depsgraph, ob)) {
- BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr);
- }
+ BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr);
}
if (recalc_data) {
BKE_object_handle_data_update(depsgraph, scene, ob);
}
-
- object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update);
}
void BKE_object_handle_update(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
- BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr, true);
+ BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr);
}
void BKE_object_sculpt_data_create(Object *ob)
@@ -4574,6 +4311,33 @@ Mesh *BKE_object_get_original_mesh(const Object *object)
return result;
}
+Mesh *BKE_object_get_editmesh_eval_final(const Object *object)
+{
+ BLI_assert(!DEG_is_original_id(&object->id));
+ BLI_assert(object->type == OB_MESH);
+
+ const Mesh *mesh = static_cast<const Mesh *>(object->data);
+ if (mesh->edit_mesh == nullptr) {
+ /* Happens when requesting material of evaluated 3d font object: the evaluated object get
+ * converted to mesh, and it does not have edit mesh. */
+ return nullptr;
+ }
+
+ return reinterpret_cast<Mesh *>(object->runtime.data_eval);
+}
+
+Mesh *BKE_object_get_editmesh_eval_cage(const Object *object)
+{
+ BLI_assert(!DEG_is_original_id(&object->id));
+ BLI_assert(object->type == OB_MESH);
+
+ const Mesh *mesh = static_cast<const Mesh *>(object->data);
+ BLI_assert(mesh->edit_mesh != nullptr);
+ UNUSED_VARS_NDEBUG(mesh);
+
+ return object->runtime.editmesh_eval_cage;
+}
+
Lattice *BKE_object_get_lattice(const Object *object)
{
ID *data = (ID *)object->data;
@@ -5177,7 +4941,7 @@ bool BKE_object_supports_material_slots(struct Object *ob)
OB_SURF,
OB_FONT,
OB_MBALL,
- OB_HAIR,
+ OB_CURVES,
OB_POINTCLOUD,
OB_VOLUME,
OB_GPENCIL);
@@ -5204,6 +4968,9 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
runtime->object_as_temp_mesh = nullptr;
runtime->object_as_temp_curve = nullptr;
runtime->geometry_set_eval = nullptr;
+
+ runtime->crazyspace_deform_imats = nullptr;
+ runtime->crazyspace_deform_cos = nullptr;
}
void BKE_object_runtime_free_data(Object *object)
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 5dcb753abf4..3082d6f25f3 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -371,7 +371,7 @@ static const Mesh *mesh_data_from_duplicator_object(Object *ob,
if (em != nullptr) {
/* Note that this will only show deformation if #eModifierMode_OnCage is enabled.
* We could change this but it matches 2.7x behavior. */
- me_eval = em->mesh_eval_cage;
+ me_eval = BKE_object_get_editmesh_eval_cage(ob);
if ((me_eval == nullptr) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : nullptr;
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 904fe9be51f..12ae57af2c3 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 20014 by Blender Foundation.
+ * The Original Code is Copyright (C) 2014 by Blender Foundation.
* All rights reserved.
*/
@@ -41,12 +41,12 @@
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
+#include "BKE_curves.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
#include "BKE_effect.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
-#include "BKE_hair.h"
#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
@@ -160,12 +160,6 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
/* includes all keys and modifiers */
switch (ob->type) {
case OB_MESH: {
-#if 0
- BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? BKE_editmesh_from_object(ob) : NULL;
-#else
- BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? ((Mesh *)ob->data)->edit_mesh : NULL;
-#endif
-
CustomData_MeshMasks cddata_masks = scene->customdata_mask;
CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH);
/* Custom attributes should not be removed automatically. They might be used by the render
@@ -192,25 +186,11 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
- if (em) {
- makeDerivedMesh(depsgraph, scene, ob, em, &cddata_masks); /* was CD_MASK_BAREMESH */
- }
- else {
- makeDerivedMesh(depsgraph, scene, ob, NULL, &cddata_masks);
- }
+ makeDerivedMesh(depsgraph, scene, ob, &cddata_masks); /* was CD_MASK_BAREMESH */
break;
}
case OB_ARMATURE:
- if (ID_IS_LINKED(ob) && ob->proxy_from) {
- if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) {
- printf("Proxy copy error, lib Object: %s proxy Object: %s\n",
- ob->id.name + 2,
- ob->proxy_from->id.name + 2);
- }
- }
- else {
- BKE_pose_where_is(depsgraph, scene, ob);
- }
+ BKE_pose_where_is(depsgraph, scene, ob);
break;
case OB_MBALL:
@@ -234,8 +214,8 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
BKE_gpencil_update_layer_transforms(depsgraph, ob);
break;
}
- case OB_HAIR:
- BKE_hair_data_update(depsgraph, scene, ob);
+ case OB_CURVES:
+ BKE_curves_data_update(depsgraph, scene, ob);
break;
case OB_POINTCLOUD:
BKE_pointcloud_data_update(depsgraph, scene, ob);
@@ -322,33 +302,9 @@ void BKE_object_sync_to_original(Depsgraph *depsgraph, Object *object)
object_sync_boundbox_to_original(object_orig, object);
}
-bool BKE_object_eval_proxy_copy(Depsgraph *depsgraph, Object *object)
-{
- /* Handle proxy copy for target, */
- if (ID_IS_LINKED(object) && object->proxy_from) {
- DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
- if (object->proxy_from->proxy_group) {
- /* Transform proxy into group space. */
- Object *obg = object->proxy_from->proxy_group;
- float imat[4][4];
- invert_m4_m4(imat, obg->obmat);
- mul_m4_m4m4(object->obmat, imat, object->proxy_from->obmat);
- /* Should always be true. */
- if (obg->instance_collection) {
- add_v3_v3(object->obmat[3], obg->instance_collection->instance_offset);
- }
- }
- else {
- copy_m4_m4(object->obmat, object->proxy_from->obmat);
- }
- return true;
- }
- return false;
-}
-
-void BKE_object_eval_uber_transform(Depsgraph *depsgraph, Object *object)
+void BKE_object_eval_uber_transform(Depsgraph *UNUSED(depsgraph), Object *UNUSED(object))
{
- BKE_object_eval_proxy_copy(depsgraph, object);
+ return;
}
void BKE_object_data_batch_cache_dirty_tag(ID *object_data)
@@ -370,8 +326,8 @@ void BKE_object_data_batch_cache_dirty_tag(ID *object_data)
case ID_GD:
BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)object_data);
break;
- case ID_HA:
- BKE_hair_batch_cache_dirty_tag((struct Hair *)object_data, BKE_HAIR_BATCH_DIRTY_ALL);
+ case ID_CV:
+ BKE_curves_batch_cache_dirty_tag((struct Curves *)object_data, BKE_CURVES_BATCH_DIRTY_ALL);
break;
case ID_PT:
BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)object_data,
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index 97326c24a61..dacc24c32da 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -15,13 +15,13 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * Based on original code by Drew Whitehouse / Houdini Ocean Toolkit
- * OpenMP hints by Christian Schnellhammer
*/
/** \file
* \ingroup bke
+ *
+ * Based on original code by Drew Whitehouse / Houdini Ocean Toolkit
+ * OpenMP hints by Christian Schnellhammer
*/
#include <math.h>
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 4dba13ce4c2..aa08a6494d1 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -4640,7 +4640,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim,
* account when subdividing for instance. */
pind.mesh = psys_in_edit_mode(sim->depsgraph, psys) ?
NULL :
- psys->hair_out_mesh; /* XXX(@sybren) EEK. */
+ psys->hair_out_mesh; /* XXX(@sybren): EEK. */
init_particle_interpolation(sim->ob, psys, pa, &pind);
do_particle_interpolation(psys, p, pa, t, &pind, state);
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index e489f9e2bac..6953ecf38c3 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -13,11 +13,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2007 by Janne Karhu.
- * All rights reserved.
- * Adaptive time step
- * Classical SPH
- * Copyright 2011-2012 AutoCRC
+ * Copyright 2007 Janne Karhu. All rights reserved.
+ * 2011-2012 AutoCRC (adaptive time step, Classical SPH).
*/
/** \file
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 1926bbcda02..bfedd4d3f49 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -1007,6 +1007,28 @@ typedef struct PBVHUpdateData {
bool show_sculpt_face_sets;
} PBVHUpdateData;
+static void pbvh_update_normals_clear_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ PBVHUpdateData *data = userdata;
+ PBVH *pbvh = data->pbvh;
+ PBVHNode *node = data->nodes[n];
+ float(*vnors)[3] = data->vnors;
+
+ if (node->flag & PBVH_UpdateNormals) {
+ const int *verts = node->vert_indices;
+ const int totvert = node->uniq_verts;
+ for (int i = 0; i < totvert; i++) {
+ const int v = verts[i];
+ const MVert *mvert = &pbvh->verts[v];
+ if (mvert->flag & ME_VERT_PBVH_UPDATE) {
+ zero_v3(vnors[v]);
+ }
+ }
+ }
+}
+
static void pbvh_update_normals_accum_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
@@ -1107,6 +1129,8 @@ static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode)
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ /* Zero normals before accumulation. */
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_clear_task_cb, &settings);
BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings);
BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings);
}
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 38575f3048f..602546db8df 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -831,31 +831,23 @@ static void ptcache_rigidbody_interpolate(int index,
RigidBodyOb *rbo = ob->rigidbody_object;
if (rbo->type == RBO_TYPE_ACTIVE) {
- ParticleKey keys[4];
- ParticleKey result;
- float dfra;
-
- memset(keys, 0, sizeof(keys));
-
- copy_v3_v3(keys[1].co, rbo->pos);
- copy_qt_qt(keys[1].rot, rbo->orn);
+ /* It may be possible to improve results by taking into account velocity
+ * for interpolation using psys_interpolate_particle, however this is
+ * not currently cached. */
+ float pos[3], orn[4];
if (old_data) {
- memcpy(keys[2].co, data, sizeof(float[3]));
- memcpy(keys[2].rot, data + 3, sizeof(float[4]));
+ memcpy(pos, data, sizeof(float[3]));
+ memcpy(orn, data + 3, sizeof(float[4]));
}
else {
- BKE_ptcache_make_particle_key(&keys[2], 0, data, cfra2);
+ PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, index, pos);
+ PTCACHE_DATA_TO(data, BPHYS_DATA_ROTATION, index, orn);
}
- dfra = cfra2 - cfra1;
-
- /* NOTE: keys[0] and keys[3] unused for type < 1 (crappy). */
- psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, &result, true);
- interp_qt_qtqt(result.rot, keys[1].rot, keys[2].rot, (cfra - cfra1) / dfra);
-
- copy_v3_v3(rbo->pos, result.co);
- copy_qt_qt(rbo->orn, result.rot);
+ const float t = (cfra - cfra1) / (cfra2 - cfra1);
+ interp_v3_v3v3(rbo->pos, rbo->pos, pos, t);
+ interp_qt_qtqt(rbo->orn, rbo->orn, orn, t);
}
}
}
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 916a2786a98..ed9f10aaa91 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -2522,7 +2522,7 @@ void BKE_scene_update_sound(Depsgraph *depsgraph, Main *bmain)
Scene *scene = DEG_get_evaluated_scene(depsgraph);
const int recalc = scene->id.recalc;
BKE_sound_ensure_scene(scene);
- if (recalc & ID_RECALC_AUDIO_SEEK) {
+ if (recalc & ID_RECALC_FRAME_CHANGE) {
BKE_sound_seek_scene(bmain, scene);
}
if (recalc & ID_RECALC_AUDIO_FPS) {
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
index 719ba4b7ecd..5993b9a9a27 100644
--- a/source/blender/blenkernel/intern/spline_nurbs.cc
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -179,65 +179,35 @@ int NURBSpline::knots_size() const
void NURBSpline::calculate_knots() const
{
const KnotsMode mode = this->knots_mode;
- const int length = this->size();
const int order = order_;
+ const bool is_bezier = mode == NURBSpline::KnotsMode::Bezier;
+ const bool is_end_point = mode == NURBSpline::KnotsMode::EndPoint;
+ /* Inner knots are always repeated once except on Bezier case. */
+ const int repeat_inner = is_bezier ? order - 1 : 1;
+ /* How many times to repeat 0.0 at the beginning of knot. */
+ const int head = is_end_point && !is_cyclic_ ? order : (is_bezier ? order / 2 : 1);
+ /* Number of knots replicating widths of the starting knots.
+ * Covers both Cyclic and EndPoint cases. */
+ const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0);
knots_.resize(this->knots_size());
-
MutableSpan<float> knots = knots_;
- if (mode == NURBSpline::KnotsMode::Normal || is_cyclic_) {
- for (const int i : knots.index_range()) {
- knots[i] = static_cast<float>(i);
- }
- }
- else if (mode == NURBSpline::KnotsMode::EndPoint) {
- float k = 0.0f;
- for (const int i : IndexRange(1, knots.size())) {
- knots[i - 1] = k;
- if (i >= order && i <= length) {
- k += 1.0f;
- }
- }
- }
- else if (mode == NURBSpline::KnotsMode::Bezier) {
- BLI_assert(ELEM(order, 3, 4));
- if (order == 3) {
- float k = 0.6f;
- for (const int i : knots.index_range()) {
- if (i >= order && i <= length) {
- k += 0.5f;
- }
- knots[i] = std::floor(k);
- }
- }
- else {
- float k = 0.34f;
- for (const int i : knots.index_range()) {
- knots[i] = std::floor(k);
- k += 1.0f / 3.0f;
- }
- }
- }
+ int r = head;
+ float current = 0.0f;
- if (is_cyclic_) {
- const int b = length + order - 1;
- if (order > 2) {
- for (const int i : IndexRange(1, order - 2)) {
- if (knots[b] != knots[b - i]) {
- if (i == order - 1) {
- knots[length + order - 2] += 1.0f;
- break;
- }
- }
- }
+ for (const int i : IndexRange(knots.size() - tail)) {
+ knots[i] = current;
+ r--;
+ if (r == 0) {
+ current += 1.0;
+ r = repeat_inner;
}
+ }
- int c = order;
- for (int i = b; i < this->knots_size(); i++) {
- knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
- c--;
- }
+ const int tail_index = knots.size() - tail;
+ for (const int i : IndexRange(tail)) {
+ knots[tail_index + i] = current + (knots[i] - knots[0]);
}
}
diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c
index 65809782f8f..525c4837bc4 100644
--- a/source/blender/blenkernel/intern/subdiv_modifier.c
+++ b/source/blender/blenkernel/intern/subdiv_modifier.c
@@ -92,7 +92,7 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene,
return false;
}
- if (!GPU_compute_shader_support()) {
+ if (!(GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support())) {
return false;
}
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 9d66c354b54..2f63edebb67 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -580,7 +580,6 @@ static void ss_sync_ccg_from_derivedmesh(CCGSubSurf *ss,
#endif
MVert *mvert = dm->getVertArray(dm);
MEdge *medge = dm->getEdgeArray(dm);
- // MFace *mface = dm->getTessFaceArray(dm); /* UNUSED */
MVert *mv;
MEdge *me;
MLoop *mloop = dm->getLoopArray(dm), *ml;
@@ -1129,44 +1128,6 @@ static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, MEdge *medge)
}
}
-static void ccgDM_copyFinalFaceArray(DerivedMesh *dm, MFace *mface)
-{
- CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
- CCGSubSurf *ss = ccgdm->ss;
- int index;
- int totface;
- int gridSize = ccgSubSurf_getGridSize(ss);
- int edgeSize = ccgSubSurf_getEdgeSize(ss);
- int i = 0;
- DMFlagMat *faceFlags = ccgdm->faceFlags;
-
- totface = dm->getNumTessFaces(dm);
- for (index = 0; index < totface; index++) {
- CCGFace *f = ccgdm->faceMap[index].face;
- int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f);
- /* keep types in sync with MFace, avoid many conversions */
- char flag = (faceFlags) ? faceFlags[index].flag : ME_SMOOTH;
- short mat_nr = (faceFlags) ? faceFlags[index].mat_nr : 0;
-
- for (S = 0; S < numVerts; S++) {
- for (y = 0; y < gridSize - 1; y++) {
- for (x = 0; x < gridSize - 1; x++) {
- MFace *mf = &mface[i];
- mf->v1 = getFaceIndex(ss, f, S, x + 0, y + 0, edgeSize, gridSize);
- mf->v2 = getFaceIndex(ss, f, S, x + 0, y + 1, edgeSize, gridSize);
- mf->v3 = getFaceIndex(ss, f, S, x + 1, y + 1, edgeSize, gridSize);
- mf->v4 = getFaceIndex(ss, f, S, x + 1, y + 0, edgeSize, gridSize);
- mf->mat_nr = mat_nr;
- mf->flag = flag;
- mf->edcode = 0;
-
- i++;
- }
- }
- }
- }
-}
-
typedef struct CopyFinalLoopArrayData {
CCGDerivedMesh *ccgdm;
MLoop *mloop;
@@ -1457,63 +1418,6 @@ static void *ccgDM_get_edge_data_layer(DerivedMesh *dm, int type)
return DM_get_edge_data_layer(dm, type);
}
-static void *ccgDM_get_tessface_data_layer(DerivedMesh *dm, int type)
-{
- if (type == CD_ORIGINDEX) {
- /* create origindex on demand to save memory */
- int *origindex;
-
- /* Avoid re-creation if the layer exists already */
- origindex = DM_get_tessface_data_layer(dm, CD_ORIGINDEX);
- if (origindex) {
- return origindex;
- }
-
- DM_add_tessface_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL);
- origindex = DM_get_tessface_data_layer(dm, CD_ORIGINDEX);
-
- /* silly loop counting up */
- range_vn_i(origindex, dm->getNumTessFaces(dm), 0);
-
- return origindex;
- }
-
- if (type == CD_TESSLOOPNORMAL) {
- /* Create tessloopnormal on demand to save memory. */
- /* Note that since tessellated face corners are the same a loops in CCGDM,
- * and since all faces have four loops/corners, we can simplify the code
- * here by converting tessloopnormals from 'short (*)[4][3]' to 'short (*)[3]'. */
- short(*tlnors)[3];
-
- /* Avoid re-creation if the layer exists already */
- tlnors = DM_get_tessface_data_layer(dm, CD_TESSLOOPNORMAL);
- if (!tlnors) {
- float(*lnors)[3];
- short(*tlnors_it)[3];
- const int numLoops = ccgDM_getNumLoops(dm);
- int i;
-
- lnors = dm->getLoopDataArray(dm, CD_NORMAL);
- if (!lnors) {
- return NULL;
- }
-
- DM_add_tessface_layer(dm, CD_TESSLOOPNORMAL, CD_CALLOC, NULL);
- tlnors = tlnors_it = (short(*)[3])DM_get_tessface_data_layer(dm, CD_TESSLOOPNORMAL);
-
- /* With ccgdm, we have a simple one to one mapping between loops
- * and tessellated face corners. */
- for (i = 0; i < numLoops; i++, tlnors_it++, lnors++) {
- normal_float_to_short_v3(*tlnors_it, *lnors);
- }
- }
-
- return tlnors;
- }
-
- return DM_get_tessface_data_layer(dm, type);
-}
-
static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type)
{
if (type == CD_ORIGINDEX) {
@@ -1551,46 +1455,6 @@ static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type)
return DM_get_poly_data_layer(dm, type);
}
-static void *ccgDM_get_vert_data(DerivedMesh *dm, int index, int type)
-{
- if (type == CD_ORIGINDEX) {
- /* ensure creation of CD_ORIGINDEX layer */
- ccgDM_get_vert_data_layer(dm, type);
- }
-
- return DM_get_vert_data(dm, index, type);
-}
-
-static void *ccgDM_get_edge_data(DerivedMesh *dm, int index, int type)
-{
- if (type == CD_ORIGINDEX) {
- /* ensure creation of CD_ORIGINDEX layer */
- ccgDM_get_edge_data_layer(dm, type);
- }
-
- return DM_get_edge_data(dm, index, type);
-}
-
-static void *ccgDM_get_tessface_data(DerivedMesh *dm, int index, int type)
-{
- if (ELEM(type, CD_ORIGINDEX, CD_TESSLOOPNORMAL)) {
- /* ensure creation of CD_ORIGINDEX/CD_TESSLOOPNORMAL layers */
- ccgDM_get_tessface_data_layer(dm, type);
- }
-
- return DM_get_tessface_data(dm, index, type);
-}
-
-static void *ccgDM_get_poly_data(DerivedMesh *dm, int index, int type)
-{
- if (type == CD_ORIGINDEX) {
- /* ensure creation of CD_ORIGINDEX layer */
- ccgDM_get_tessface_data_layer(dm, type);
- }
-
- return DM_get_poly_data(dm, index, type);
-}
-
static int ccgDM_getNumGrids(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
@@ -1705,25 +1569,6 @@ static BLI_bitmap **ccgDM_getGridHidden(DerivedMesh *dm)
return ccgdm->gridHidden;
}
-static const MeshElemMap *ccgDM_getPolyMap(Object *ob, DerivedMesh *dm)
-{
- CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
-
- if (!ccgdm->multires.mmd && !ccgdm->pmap && ob->type == OB_MESH) {
- Mesh *me = ob->data;
-
- BKE_mesh_vert_poly_map_create(&ccgdm->pmap,
- &ccgdm->pmap_mem,
- me->mpoly,
- me->mloop,
- me->totvert,
- me->totpoly,
- me->totloop);
- }
-
- return ccgdm->pmap;
-}
-
/* WARNING! *MUST* be called in an 'loops_cache_rwlock' protected thread context! */
static void ccgDM_recalcLoopTri(DerivedMesh *dm)
{
@@ -1773,17 +1618,11 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm)
ccgdm->dm.copyVertArray = ccgDM_copyFinalVertArray;
ccgdm->dm.copyEdgeArray = ccgDM_copyFinalEdgeArray;
- ccgdm->dm.copyTessFaceArray = ccgDM_copyFinalFaceArray;
ccgdm->dm.copyLoopArray = ccgDM_copyFinalLoopArray;
ccgdm->dm.copyPolyArray = ccgDM_copyFinalPolyArray;
- ccgdm->dm.getVertData = ccgDM_get_vert_data;
- ccgdm->dm.getEdgeData = ccgDM_get_edge_data;
- ccgdm->dm.getTessFaceData = ccgDM_get_tessface_data;
- ccgdm->dm.getPolyData = ccgDM_get_poly_data;
ccgdm->dm.getVertDataArray = ccgDM_get_vert_data_layer;
ccgdm->dm.getEdgeDataArray = ccgDM_get_edge_data_layer;
- ccgdm->dm.getTessFaceDataArray = ccgDM_get_tessface_data_layer;
ccgdm->dm.getPolyDataArray = ccgDM_get_poly_data_layer;
ccgdm->dm.getNumGrids = ccgDM_getNumGrids;
ccgdm->dm.getGridSize = ccgDM_getGridSize;
@@ -1792,7 +1631,6 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm)
ccgdm->dm.getGridKey = ccgDM_getGridKey;
ccgdm->dm.getGridFlagMats = ccgDM_getGridFlagMats;
ccgdm->dm.getGridHidden = ccgDM_getGridHidden;
- ccgdm->dm.getPolyMap = ccgDM_getPolyMap;
ccgdm->dm.recalcLoopTri = ccgDM_recalcLoopTri;
@@ -1848,7 +1686,7 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm,
int index;
int i;
int vertNum = 0, edgeNum = 0, faceNum = 0;
- int *vertOrigIndex, *faceOrigIndex, *polyOrigIndex, *base_polyOrigIndex, *edgeOrigIndex;
+ int *vertOrigIndex, *polyOrigIndex, *base_polyOrigIndex, *edgeOrigIndex;
short *edgeFlags = ccgdm->edgeFlags;
DMFlagMat *faceFlags = ccgdm->faceFlags;
int *polyidx = NULL;
@@ -1884,7 +1722,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm,
vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX);
edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX);
- faceOrigIndex = DM_get_tessface_data_layer(&ccgdm->dm, CD_ORIGINDEX);
polyOrigIndex = DM_get_poly_data_layer(&ccgdm->dm, CD_ORIGINDEX);
has_edge_cd = ((ccgdm->dm.edgeData.totlayer - (edgeOrigIndex ? 1 : 0)) != 0);
@@ -2006,12 +1843,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm,
/* Copy over poly data, e.g. #CD_FACEMAP. */
CustomData_copy_data(&dm->polyData, &ccgdm->dm.polyData, origIndex, faceNum, 1);
- /* Set original index data. */
- if (faceOrigIndex) {
- /* reference the index in 'polyOrigIndex' */
- *faceOrigIndex = faceNum;
- faceOrigIndex++;
- }
if (polyOrigIndex) {
*polyOrigIndex = base_polyOrigIndex ? base_polyOrigIndex[origIndex] : origIndex;
polyOrigIndex++;
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index ee9247e6e60..c37e2fb6144 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -67,6 +67,8 @@
#include "BKE_scene.h"
#include "BKE_texture.h"
+#include "NOD_texture.h"
+
#include "RE_texture.h"
#include "BLO_read_write.h"
@@ -721,10 +723,10 @@ void BKE_texture_get_value_ex(const Scene *scene,
* if the texture didn't give an RGB value, copy the intensity across
*/
if (result_type & TEX_RGB) {
- texres->tin = (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb);
+ texres->tin = (1.0f / 3.0f) * (texres->trgba[0] + texres->trgba[1] + texres->trgba[2]);
}
else {
- copy_v3_fl(&texres->tr, texres->tin);
+ copy_v3_fl(texres->trgba, texres->tin);
}
}
diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc
index cb05337ef2a..b2011d2baf7 100644
--- a/source/blender/blenkernel/intern/type_conversions.cc
+++ b/source/blender/blenkernel/intern/type_conversions.cc
@@ -62,6 +62,11 @@ static bool float_to_bool(const float &a)
{
return a > 0.0f;
}
+static int8_t float_to_int8(const float &a)
+{
+ return std::clamp(
+ a, float(std::numeric_limits<int8_t>::min()), float(std::numeric_limits<int8_t>::max()));
+}
static ColorGeometry4f float_to_color(const float &a)
{
return ColorGeometry4f(a, a, a, 1.0f);
@@ -83,6 +88,10 @@ static bool float2_to_bool(const float2 &a)
{
return !is_zero_v2(a);
}
+static int8_t float2_to_int8(const float2 &a)
+{
+ return float_to_int8((a.x + a.y) / 2.0f);
+}
static ColorGeometry4f float2_to_color(const float2 &a)
{
return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f);
@@ -92,6 +101,10 @@ static bool float3_to_bool(const float3 &a)
{
return !is_zero_v3(a);
}
+static int8_t float3_to_int8(const float3 &a)
+{
+ return float_to_int8((a.x + a.y + a.z) / 3.0f);
+}
static float float3_to_float(const float3 &a)
{
return (a.x + a.y + a.z) / 3.0f;
@@ -113,6 +126,11 @@ static bool int_to_bool(const int32_t &a)
{
return a > 0;
}
+static int8_t int_to_int8(const int32_t &a)
+{
+ return std::clamp(
+ a, int(std::numeric_limits<int8_t>::min()), int(std::numeric_limits<int8_t>::max()));
+}
static float int_to_float(const int32_t &a)
{
return (float)a;
@@ -130,10 +148,39 @@ static ColorGeometry4f int_to_color(const int32_t &a)
return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
}
+static bool int8_to_bool(const int8_t &a)
+{
+ return a > 0;
+}
+static int int8_to_int(const int8_t &a)
+{
+ return static_cast<int>(a);
+}
+static float int8_to_float(const int8_t &a)
+{
+ return (float)a;
+}
+static float2 int8_to_float2(const int8_t &a)
+{
+ return float2((float)a);
+}
+static float3 int8_to_float3(const int8_t &a)
+{
+ return float3((float)a);
+}
+static ColorGeometry4f int8_to_color(const int8_t &a)
+{
+ return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
+}
+
static float bool_to_float(const bool &a)
{
return (bool)a;
}
+static int8_t bool_to_int8(const bool &a)
+{
+ return static_cast<int8_t>(a);
+}
static int32_t bool_to_int(const bool &a)
{
return (int32_t)a;
@@ -163,6 +210,10 @@ static int32_t color_to_int(const ColorGeometry4f &a)
{
return (int)rgb_to_grayscale(a);
}
+static int8_t color_to_int8(const ColorGeometry4f &a)
+{
+ return int_to_int8(color_to_int(a));
+}
static float2 color_to_float2(const ColorGeometry4f &a)
{
return float2(a.r, a.g);
@@ -180,33 +231,46 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<float, float3, float_to_float3>(conversions);
add_implicit_conversion<float, int32_t, float_to_int>(conversions);
add_implicit_conversion<float, bool, float_to_bool>(conversions);
+ add_implicit_conversion<float, int8_t, float_to_int8>(conversions);
add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions);
add_implicit_conversion<float2, float3, float2_to_float3>(conversions);
add_implicit_conversion<float2, float, float2_to_float>(conversions);
add_implicit_conversion<float2, int32_t, float2_to_int>(conversions);
add_implicit_conversion<float2, bool, float2_to_bool>(conversions);
+ add_implicit_conversion<float2, int8_t, float2_to_int8>(conversions);
add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions);
add_implicit_conversion<float3, bool, float3_to_bool>(conversions);
+ add_implicit_conversion<float3, int8_t, float3_to_int8>(conversions);
add_implicit_conversion<float3, float, float3_to_float>(conversions);
add_implicit_conversion<float3, int32_t, float3_to_int>(conversions);
add_implicit_conversion<float3, float2, float3_to_float2>(conversions);
add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions);
add_implicit_conversion<int32_t, bool, int_to_bool>(conversions);
+ add_implicit_conversion<int32_t, int8_t, int_to_int8>(conversions);
add_implicit_conversion<int32_t, float, int_to_float>(conversions);
add_implicit_conversion<int32_t, float2, int_to_float2>(conversions);
add_implicit_conversion<int32_t, float3, int_to_float3>(conversions);
add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions);
+ add_implicit_conversion<int8_t, bool, int8_to_bool>(conversions);
+ add_implicit_conversion<int8_t, int32_t, int8_to_int>(conversions);
+ add_implicit_conversion<int8_t, float, int8_to_float>(conversions);
+ add_implicit_conversion<int8_t, float2, int8_to_float2>(conversions);
+ add_implicit_conversion<int8_t, float3, int8_to_float3>(conversions);
+ add_implicit_conversion<int8_t, ColorGeometry4f, int8_to_color>(conversions);
+
add_implicit_conversion<bool, float, bool_to_float>(conversions);
+ add_implicit_conversion<bool, int8_t, bool_to_int8>(conversions);
add_implicit_conversion<bool, int32_t, bool_to_int>(conversions);
add_implicit_conversion<bool, float2, bool_to_float2>(conversions);
add_implicit_conversion<bool, float3, bool_to_float3>(conversions);
add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions);
add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions);
+ add_implicit_conversion<ColorGeometry4f, int8_t, color_to_int8>(conversions);
add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions);
add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions);
add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions);
diff --git a/source/blender/blenkernel/intern/vfontdata_freetype.c b/source/blender/blenkernel/intern/vfontdata_freetype.c
index 9b79d5635d1..60b7e4e5c8b 100644
--- a/source/blender/blenkernel/intern/vfontdata_freetype.c
+++ b/source/blender/blenkernel/intern/vfontdata_freetype.c
@@ -15,6 +15,10 @@
*
* The Original Code is written by Rob Haarsma (phase)
* All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
*
* This code parses the Freetype font outline data to chains of Blender's bezier-triples.
* Additional information can be found at the bottom of this file.
@@ -22,10 +26,6 @@
* Code that uses exotic character maps is present but commented out.
*/
-/** \file
- * \ingroup bke
- */
-
#include <ft2build.h>
#include FT_FREETYPE_H
/* not needed yet */
diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c
index a4f20f980b4..93c8c550663 100644
--- a/source/blender/blenkernel/intern/writeavi.c
+++ b/source/blender/blenkernel/intern/writeavi.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Functions for writing avi-format files.
- * Added interface for generic movie support (ton)
*/
/** \file
+ * Functions for writing avi-format files.
+ * Added interface for generic movie support (ton)
* \ingroup bke
*/
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 4d94132e6fd..9effeb831b6 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -726,14 +726,12 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
- if (codec_id == AV_CODEC_ID_VP9) {
- if (rd->im_format.planes == R_IMF_PLANES_RGBA) {
- c->pix_fmt = AV_PIX_FMT_YUVA420P;
- }
+ if (codec_id == AV_CODEC_ID_VP9 && rd->im_format.planes == R_IMF_PLANES_RGBA) {
+ c->pix_fmt = AV_PIX_FMT_YUVA420P;
}
-
- /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */
- if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && context->ffmpeg_crf == 0) {
+ else if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) &&
+ context->ffmpeg_crf == 0) {
+ /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */
c->pix_fmt = AV_PIX_FMT_YUV444P;
}
diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
index b5981028893..ce7df1ff4b9 100644
--- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh
+++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
@@ -28,10 +28,12 @@
namespace blender::threading {
+#ifndef WITH_TBB
namespace enumerable_thread_specific_utils {
inline std::atomic<int> next_id = 0;
inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed);
} // namespace enumerable_thread_specific_utils
+#endif
/**
* This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index 81c969d02d0..f451f58f6cf 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -107,6 +107,20 @@ struct float4x4 {
return &values[0][0];
}
+ float *operator[](const int64_t index)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < 4);
+ return &values[index][0];
+ }
+
+ const float *operator[](const int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < 4);
+ return &values[index][0];
+ }
+
using c_style_float4x4 = float[4][4];
c_style_float4x4 &ptr()
{
diff --git a/source/blender/blenlib/BLI_math.h b/source/blender/blenlib/BLI_math.h
index 5768b098d2f..b3ce6c0747a 100644
--- a/source/blender/blenlib/BLI_math.h
+++ b/source/blender/blenlib/BLI_math.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index f6462233106..69abe49c207 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h
index 0798acbb790..e1956d2bc2b 100644
--- a/source/blender/blenlib/BLI_math_color.h
+++ b/source/blender/blenlib/BLI_math_color.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
diff --git a/source/blender/blenlib/BLI_math_color_blend.h b/source/blender/blenlib/BLI_math_color_blend.h
index 2aff629def8..df70da0eec3 100644
--- a/source/blender/blenlib/BLI_math_color_blend.h
+++ b/source/blender/blenlib/BLI_math_color_blend.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 6d7159f73c6..2556aae6aa4 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
diff --git a/source/blender/blenlib/BLI_math_inline.h b/source/blender/blenlib/BLI_math_inline.h
index 70f56d15164..35e08898385 100644
--- a/source/blender/blenlib/BLI_math_inline.h
+++ b/source/blender/blenlib/BLI_math_inline.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 65d654bc930..ac9364fc254 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
@@ -404,6 +402,7 @@ void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]);
void scale_m3_fl(float R[3][3], float scale);
void scale_m4_fl(float R[4][4], float scale);
+void scale_m4_v2(float R[4][4], const float scale[2]);
/**
* This computes the overall volume scale factor of a transformation matrix.
@@ -430,6 +429,20 @@ void mat3_to_size(float size[3], const float M[3][3]);
void mat4_to_size(float size[3], const float M[4][4]);
/**
+ * Return the largest scale on any axis, the equivalent of calling:
+ * \code{.c}
+ * mat3_to_size(size_v3, mat);
+ * size = size_v3[max_axis_v3(size_v3)];
+ * \endcode
+ * .. without 2x unnecessary `sqrtf` calls.
+ */
+float mat3_to_size_max_axis(const float M[3][3]);
+/**
+ * Only the first 3 axes are used.
+ */
+float mat4_to_size_max_axis(const float M[4][4]);
+
+/**
* Extract scale factors from the matrix, with correction to ensure
* exact volume in case of a sheared matrix.
*/
diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h
index c27cf71ce5f..907dc5601fe 100644
--- a/source/blender/blenlib/BLI_math_rotation.h
+++ b/source/blender/blenlib/BLI_math_rotation.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index 1ed8d4fc005..04b7ac5db62 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
#pragma once
diff --git a/source/blender/blenlib/BLI_math_vector.hh b/source/blender/blenlib/BLI_math_vector.hh
index e7d765df842..1d60447445e 100644
--- a/source/blender/blenlib/BLI_math_vector.hh
+++ b/source/blender/blenlib/BLI_math_vector.hh
@@ -365,6 +365,11 @@ template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T interpolate(const T &a,
return a * (1 - t) + b * t;
}
+template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T midpoint(const T &a, const T &b)
+{
+ return (a + b) * 0.5;
+}
+
template<typename T, BLI_ENABLE_IF_FLT_VEC(T)>
inline T faceforward(const T &vector, const T &incident, const T &reference)
{
diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h
index d1999a468b8..caf946cabe3 100644
--- a/source/blender/blenlib/BLI_mempool.h
+++ b/source/blender/blenlib/BLI_mempool.h
@@ -62,7 +62,7 @@ void BLI_mempool_clear_ex(BLI_mempool *pool, int totelem_reserve) ATTR_NONNULL(1
*/
void BLI_mempool_clear(BLI_mempool *pool) ATTR_NONNULL(1);
/**
- * Free the mempool its self (and all elements).
+ * Free the mempool itself (and all elements).
*/
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1);
int BLI_mempool_len(const BLI_mempool *pool) ATTR_NONNULL(1);
diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h
index 658cc0c3825..8881983fbec 100644
--- a/source/blender/blenlib/BLI_path_util.h
+++ b/source/blender/blenlib/BLI_path_util.h
@@ -49,7 +49,7 @@ void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1);
* This function uses an alternative method to get environment variables that does pick up on
* runtime environment variables. The result will be UTF-8 encoded.
*/
-const char *BLI_getenv(const char *env) ATTR_NONNULL(1);
+const char *BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Returns in `string` the concatenation of `dir` and `file` (also with `relabase` on the
@@ -337,13 +337,13 @@ void BLI_path_frame_strip(char *path, char *r_ext) ATTR_NONNULL();
/**
* Check if we have '#' chars, usable for #BLI_path_frame, #BLI_path_frame_range
*/
-bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL();
+bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Checks for a relative path (ignoring Blender's "//") prefix
* (unlike `!BLI_path_is_rel(path)`).
* When false, #BLI_path_abs_from_cwd would expand the absolute path.
*/
-bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL();
+bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Checks for relative path, expanding them relative to the current working directory.
* \returns true if the expansion was performed.
@@ -367,7 +367,7 @@ bool BLI_path_is_rel(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
/**
* Return true if the path is a UNC share.
*/
-bool BLI_path_is_unc(const char *path);
+bool BLI_path_is_unc(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Creates a display string from path to be used menus and the user interface.
@@ -404,6 +404,20 @@ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char
# define BLI_path_ncmp strncmp
#endif
+/**
+ * Returns the result of #BLI_path_cmp with both paths normalized and slashes made native.
+ *
+ * \note #BLI_path_cmp is used for Blender's internal logic to consider paths to be the same
+ * #BLI_path_cmp_normalized may be used in when handling other kinds of paths
+ * (e.g. importers/exporters) but should be used consistently.
+ *
+ * Checking the normalized paths is not a guarantee the paths reference different files.
+ * An equivalent to Python's `os.path.samefile` could be supported for checking if paths
+ * point to the same location on the file-system (following symbolic-links).
+ */
+int BLI_path_cmp_normalized(const char *p1, const char *p2)
+ ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT;
+
/* These values need to be hard-coded in structs, dna does not recognize defines */
/* also defined in `DNA_space_types.h`. */
#ifndef FILE_MAXDIR
diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh
index 3769e711a50..32a03213dc1 100644
--- a/source/blender/blenlib/BLI_string_ref.hh
+++ b/source/blender/blenlib/BLI_string_ref.hh
@@ -337,6 +337,18 @@ constexpr int64_t StringRefBase::find(StringRef str, int64_t pos) const
return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast<size_t>(pos)));
}
+constexpr int64_t StringRefBase::rfind(char c, int64_t pos) const
+{
+ BLI_assert(pos >= 0);
+ return index_or_npos_to_int64(std::string_view(*this).rfind(c, static_cast<size_t>(pos)));
+}
+
+constexpr int64_t StringRefBase::rfind(StringRef str, int64_t pos) const
+{
+ BLI_assert(pos >= 0);
+ return index_or_npos_to_int64(std::string_view(*this).rfind(str, static_cast<size_t>(pos)));
+}
+
constexpr int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const
{
BLI_assert(pos >= 0);
@@ -346,7 +358,9 @@ constexpr int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) con
constexpr int64_t StringRefBase::find_first_of(char c, int64_t pos) const
{
- return this->find_first_of(StringRef(&c, 1), pos);
+ BLI_assert(pos >= 0);
+ return index_or_npos_to_int64(
+ std::string_view(*this).find_first_of(c, static_cast<size_t>(pos)));
}
constexpr int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const
@@ -358,7 +372,8 @@ constexpr int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) cons
constexpr int64_t StringRefBase::find_last_of(char c, int64_t pos) const
{
- return this->find_last_of(StringRef(&c, 1), pos);
+ BLI_assert(pos >= 0);
+ return index_or_npos_to_int64(std::string_view(*this).find_last_of(c, static_cast<size_t>(pos)));
}
constexpr int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const
@@ -370,7 +385,9 @@ constexpr int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos)
constexpr int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const
{
- return this->find_first_not_of(StringRef(&c, 1), pos);
+ BLI_assert(pos >= 0);
+ return index_or_npos_to_int64(
+ std::string_view(*this).find_first_not_of(c, static_cast<size_t>(pos)));
}
constexpr int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const
@@ -382,7 +399,9 @@ constexpr int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos)
constexpr int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const
{
- return this->find_last_not_of(StringRef(&c, 1), pos);
+ BLI_assert(pos >= 0);
+ return index_or_npos_to_int64(
+ std::string_view(*this).find_last_not_of(c, static_cast<size_t>(pos)));
}
constexpr StringRef StringRefBase::trim() const
diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh
index 84d5cd39bb4..8f75aa19cfe 100644
--- a/source/blender/blenlib/BLI_task.hh
+++ b/source/blender/blenlib/BLI_task.hh
@@ -31,6 +31,7 @@
# include <tbb/blocked_range.h>
# include <tbb/parallel_for.h>
# include <tbb/parallel_for_each.h>
+# include <tbb/parallel_invoke.h>
# include <tbb/parallel_reduce.h>
# include <tbb/task_arena.h>
# ifdef WIN32
@@ -103,6 +104,19 @@ Value parallel_reduce(IndexRange range,
#endif
}
+/**
+ * Execute all of the provided functions. The functions might be executed in parallel or in serial
+ * or some combination of both.
+ */
+template<typename... Functions> void parallel_invoke(Functions &&...functions)
+{
+#ifdef WITH_TBB
+ tbb::parallel_invoke(std::forward<Functions>(functions)...);
+#else
+ (functions(), ...);
+#endif
+}
+
/** See #BLI_task_isolate for a description of what isolating a task means. */
template<typename Function> void isolate_task(const Function &function)
{
diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh
index 0aac96f93bc..ed744d09314 100644
--- a/source/blender/blenlib/BLI_vector_set.hh
+++ b/source/blender/blenlib/BLI_vector_set.hh
@@ -570,6 +570,10 @@ class VectorSet {
if (this->size() == 0) {
try {
slots_.reinitialize(total_slots);
+ if (keys_ != nullptr) {
+ this->deallocate_keys_array(keys_);
+ keys_ = nullptr;
+ }
keys_ = this->allocate_keys_array(usable_slots);
}
catch (...) {
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 90c6760019a..31e550379f1 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -252,9 +252,9 @@ set(SRC
BLI_math_solvers.h
BLI_math_statistics.h
BLI_math_time.h
- BLI_math_vector.h
- BLI_math_vec_types.hh
BLI_math_vec_mpq_types.hh
+ BLI_math_vec_types.hh
+ BLI_math_vector.h
BLI_memarena.h
BLI_memblock.h
BLI_memiter.h
@@ -363,8 +363,8 @@ if(WITH_GMP)
endif()
if(WIN32)
- if (WITH_BLENDER_THUMBNAILER)
- # Needed for querying the thumbnailer .dll in winstuff.c
+ if(WITH_BLENDER_THUMBNAILER)
+ # Needed for querying the `thumbnailer .dll` in `winstuff.c`.
add_definitions(-DWITH_BLENDER_THUMBNAILER)
endif()
list(APPEND INC
diff --git a/source/blender/blenlib/intern/BLI_dynstr.c b/source/blender/blenlib/intern/BLI_dynstr.c
index 262d112d914..bc61282fa20 100644
--- a/source/blender/blenlib/intern/BLI_dynstr.c
+++ b/source/blender/blenlib/intern/BLI_dynstr.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Dynamically sized string ADT
*/
/** \file
* \ingroup bli
+ * Dynamically sized string ADT.
*/
#include <stdio.h>
diff --git a/source/blender/blenlib/intern/BLI_linklist.c b/source/blender/blenlib/intern/BLI_linklist.c
index 765d2f0be55..f34179b2fa6 100644
--- a/source/blender/blenlib/intern/BLI_linklist.c
+++ b/source/blender/blenlib/intern/BLI_linklist.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Support for linked lists.
*/
/** \file
diff --git a/source/blender/blenlib/intern/bitmap_draw_2d.c b/source/blender/blenlib/intern/bitmap_draw_2d.c
index 670ea75e9ea..31b8939753e 100644
--- a/source/blender/blenlib/intern/bitmap_draw_2d.c
+++ b/source/blender/blenlib/intern/bitmap_draw_2d.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/hash_md5.c b/source/blender/blenlib/intern/hash_md5.c
index 6a0ca8bb33f..a2cc1a0fc12 100644
--- a/source/blender/blenlib/intern/hash_md5.c
+++ b/source/blender/blenlib/intern/hash_md5.c
@@ -12,7 +12,7 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * Copyright (C) 1995 Software Foundation, Inc.
+ * Copyright (C) 1995 Free Software Foundation, Inc.
*
* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>.
*/
diff --git a/source/blender/blenlib/intern/list_sort_impl.h b/source/blender/blenlib/intern/list_sort_impl.h
index 71f7f0e29a8..626956e2fb6 100644
--- a/source/blender/blenlib/intern/list_sort_impl.h
+++ b/source/blender/blenlib/intern/list_sort_impl.h
@@ -34,7 +34,7 @@
* - `SORT_IMPL_LINKTYPE`:
* Struct type for sorting.
* - `SORT_IMPL_LINKTYPE_DATA`:
- * Data pointer or leave undefined to pass the link its self.
+ * Data pointer or leave undefined to pass the link itself.
* - `SORT_IMPL_FUNC`:
* Function name of the sort function.
*
diff --git a/source/blender/blenlib/intern/math_base.c b/source/blender/blenlib/intern/math_base.c
index be70acf622a..f2e7f411524 100644
--- a/source/blender/blenlib/intern/math_base.c
+++ b/source/blender/blenlib/intern/math_base.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index cfcc54b1136..a7729d83726 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c
index 5e52873649e..60931f0c474 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/math_color_blend_inline.c b/source/blender/blenlib/intern/math_color_blend_inline.c
index 73ecb2cf798..7d1003a05ba 100644
--- a/source/blender/blenlib/intern/math_color_blend_inline.c
+++ b/source/blender/blenlib/intern/math_color_blend_inline.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c
index a7f229e7147..27cad310aa5 100644
--- a/source/blender/blenlib/intern/math_color_inline.c
+++ b/source/blender/blenlib/intern/math_color_inline.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 3800fc58a5b..afbaf0733b6 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
@@ -3299,8 +3297,8 @@ static bool point_in_slice(const float p[3],
return (h >= 0.0f && h <= 1.0f);
}
-/* adult sister defining the slice planes by the origin and the normal
- * NOTE |normal| may not be 1 but defining the thickness of the slice */
+/* Adult sister defining the slice planes by the origin and the normal.
+ * NOTE: |normal| may not be 1 but defining the thickness of the slice. */
static bool point_in_slice_as(const float p[3], const float origin[3], const float normal[3])
{
float h, rp[3];
diff --git a/source/blender/blenlib/intern/math_geom_inline.c b/source/blender/blenlib/intern/math_geom_inline.c
index 09028a1eb9a..17580d84c38 100644
--- a/source/blender/blenlib/intern/math_geom_inline.c
+++ b/source/blender/blenlib/intern/math_geom_inline.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/math_interp.c b/source/blender/blenlib/intern/math_interp.c
index 54beb74abca..fed330aa2f0 100644
--- a/source/blender/blenlib/intern/math_interp.c
+++ b/source/blender/blenlib/intern/math_interp.c
@@ -706,9 +706,9 @@ void BLI_ewa_filter(const int width,
}
}
- /* d should hopefully never be zero anymore */
+ /* `d` should hopefully never be zero anymore. */
d = 1.0f / d;
mul_v3_fl(result, d);
- /* clipping can be ignored if alpha used, texr->ta already includes filtered edge */
+ /* Clipping can be ignored if alpha used, `texr->trgba[3]` already includes filtered edge. */
result[3] = use_alpha ? result[3] * d : 1.0f;
}
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index eaf76696a0a..73e64c2bee7 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
@@ -2144,6 +2142,16 @@ void mat4_to_size(float size[3], const float M[4][4])
size[2] = len_v3(M[2]);
}
+float mat3_to_size_max_axis(const float M[3][3])
+{
+ return sqrtf(max_fff(len_squared_v3(M[0]), len_squared_v3(M[1]), len_squared_v3(M[2])));
+}
+
+float mat4_to_size_max_axis(const float M[4][4])
+{
+ return sqrtf(max_fff(len_squared_v3(M[0]), len_squared_v3(M[1]), len_squared_v3(M[2])));
+}
+
void mat4_to_size_fix_shear(float size[3], const float M[4][4])
{
mat4_to_size(size, M);
@@ -2296,6 +2304,17 @@ void scale_m4_fl(float R[4][4], float scale)
R[3][0] = R[3][1] = R[3][2] = 0.0;
}
+void scale_m4_v2(float R[4][4], const float scale[2])
+{
+ R[0][0] = scale[0];
+ R[1][1] = scale[1];
+ R[2][2] = R[3][3] = 1.0;
+ R[0][1] = R[0][2] = R[0][3] = 0.0;
+ R[1][0] = R[1][2] = R[1][3] = 0.0;
+ R[2][0] = R[2][1] = R[2][3] = 0.0;
+ R[3][0] = R[3][1] = R[3][2] = 0.0;
+}
+
void translate_m4(float mat[4][4], float Tx, float Ty, float Tz)
{
mat[3][0] += (Tx * mat[0][0] + Ty * mat[1][0] + Tz * mat[2][0]);
diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c
index dbcf3a6500c..9de9e3d2045 100644
--- a/source/blender/blenlib/intern/math_rotation.c
+++ b/source/blender/blenlib/intern/math_rotation.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index a0afab8a179..fb23018b0e0 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index 3022dbbe4ff..3e932572038 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 56ff71fb670..3b9b7ed6ddb 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * various string, file, list operations.
*/
/** \file
* \ingroup bli
+ * Various string, file, list operations.
*/
#include <ctype.h>
@@ -1737,7 +1737,7 @@ bool BLI_path_contains(const char *container_path, const char *containee_path)
char containee_native[PATH_MAX];
/* Keep space for a trailing slash. If the path is truncated by this, the containee path is
- * longer than PATH_MAX and the result is ill-defined. */
+ * longer than PATH_MAX and the result is ill-defined. */
BLI_strncpy(container_native, container_path, PATH_MAX - 1);
BLI_strncpy(containee_native, containee_path, PATH_MAX);
@@ -1829,3 +1829,23 @@ void BLI_path_slash_native(char *path)
BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP);
#endif
}
+
+int BLI_path_cmp_normalized(const char *p1, const char *p2)
+{
+ BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), "Paths arguments must be absolute");
+
+ /* Normalize the paths so we can compare them. */
+ char norm_p1[FILE_MAX];
+ char norm_p2[FILE_MAX];
+
+ BLI_strncpy(norm_p1, p1, sizeof(norm_p1));
+ BLI_strncpy(norm_p2, p2, sizeof(norm_p2));
+
+ BLI_path_slash_native(norm_p1);
+ BLI_path_slash_native(norm_p2);
+
+ BLI_path_normalize(NULL, norm_p1);
+ BLI_path_normalize(NULL, norm_p2);
+
+ return BLI_path_cmp(norm_p1, norm_p2);
+}
diff --git a/source/blender/blenlib/intern/polyfill_2d.c b/source/blender/blenlib/intern/polyfill_2d.c
index 0ade306bcb9..aec34659884 100644
--- a/source/blender/blenlib/intern/polyfill_2d.c
+++ b/source/blender/blenlib/intern/polyfill_2d.c
@@ -25,7 +25,7 @@
* and that triangles will have non-overlapping indices (even for degenerate geometry).
* - Self-intersections are considered degenerate (resulting triangles will overlap).
* - While multiple polygons aren't supported, holes can still be defined using *key-holes*
- * (where the polygon doubles back on its self with *exactly* matching coordinates).
+ * (where the polygon doubles back on itself with *exactly* matching coordinates).
*
* \note
*
diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c
index 9fe82069d2c..4fd6670507a 100644
--- a/source/blender/blenlib/intern/scanfill.c
+++ b/source/blender/blenlib/intern/scanfill.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * (uit traces) maart 95
*/
/** \file
diff --git a/source/blender/blenlib/intern/voronoi_2d.c b/source/blender/blenlib/intern/voronoi_2d.c
index 5b998973a20..65f9994bce7 100644
--- a/source/blender/blenlib/intern/voronoi_2d.c
+++ b/source/blender/blenlib/intern/voronoi_2d.c
@@ -777,7 +777,7 @@ static void voronoi_addTriangle(
*r_triangles = MEM_reallocN(*r_triangles, sizeof(int[3]) * (*r_triangles_total + 1));
}
else {
- *r_triangles = MEM_callocN(sizeof(int[3]), "trianglulation triangles");
+ *r_triangles = MEM_callocN(sizeof(int[3]), "triangulation triangles");
}
triangle = (int *)&(*r_triangles)[(*r_triangles_total)];
diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c
index 11345fc7242..57f3b740a03 100644
--- a/source/blender/blenlib/intern/winstuff.c
+++ b/source/blender/blenlib/intern/winstuff.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Windows-posix compatibility layer, windows-specific functions.
*/
/** \file
* \ingroup bli
+ * Windows-posix compatibility layer, windows-specific functions.
*/
#ifdef WIN32
diff --git a/source/blender/blenlib/intern/winstuff_dir.c b/source/blender/blenlib/intern/winstuff_dir.c
index 6f99ea075bb..f8ab3dc8403 100644
--- a/source/blender/blenlib/intern/winstuff_dir.c
+++ b/source/blender/blenlib/intern/winstuff_dir.c
@@ -12,7 +12,6 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * Windows-posix compatibility layer for opendir/readdir/closedir
*/
/** \file
diff --git a/source/blender/blenlib/tests/BLI_task_test.cc b/source/blender/blenlib/tests/BLI_task_test.cc
index 3bb6f6f753c..1ed732c1f18 100644
--- a/source/blender/blenlib/tests/BLI_task_test.cc
+++ b/source/blender/blenlib/tests/BLI_task_test.cc
@@ -1,6 +1,7 @@
/* Apache License, Version 2.0 */
#include "testing/testing.h"
+#include <atomic>
#include <cstring>
#include "atomic_ops.h"
@@ -12,6 +13,7 @@
#include "BLI_listbase.h"
#include "BLI_mempool.h"
#include "BLI_task.h"
+#include "BLI_task.hh"
#define NUM_ITEMS 10000
@@ -280,3 +282,15 @@ TEST(task, ListBaseIter)
MEM_freeN(items_buffer);
BLI_threadapi_exit();
}
+
+TEST(task, ParallelInvoke)
+{
+ std::atomic<int> counter = 0;
+ blender::threading::parallel_invoke([&]() { counter++; },
+ [&]() { counter++; },
+ [&]() { counter++; },
+ [&]() { counter++; },
+ [&]() { counter++; },
+ [&]() { counter++; });
+ EXPECT_EQ(counter, 6);
+}
diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc
index c4016ca75e1..fe3130a846f 100644
--- a/source/blender/blenlib/tests/BLI_vector_set_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc
@@ -271,4 +271,14 @@ TEST(vector_set, LookupKey)
EXPECT_EQ(set.lookup_key_ptr("a"), set.lookup_key_ptr_as("a"));
}
+TEST(vector_set, GrowWhenEmpty)
+{
+ /* Tests that the internal keys array is freed correctly when growing an empty set. */
+ VectorSet<int> set;
+ set.add(4);
+ set.remove(4);
+ EXPECT_TRUE(set.is_empty());
+ set.reserve(100);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index c4c3b42cb63..066b180dcc9 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -117,8 +117,6 @@ typedef struct BlendFileReadReport {
/* Number of root override IDs that were resynced. */
int resynced_lib_overrides;
- /* Number of (non-converted) linked proxies. */
- int linked_proxies;
/* Number of proxies converted to library overrides. */
int proxies_to_lib_overrides_success;
/* Number of proxies that failed to convert to library overrides. */
diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h
index 0e2c22d7e4d..772abebeb26 100644
--- a/source/blender/blenloader/BLO_undofile.h
+++ b/source/blender/blenloader/BLO_undofile.h
@@ -15,13 +15,13 @@
*
* The Original Code is Copyright (C) 2004 Blender Foundation.
* All rights reserved.
- * external writefile function prototypes
*/
#pragma once
/** \file
* \ingroup blenloader
+ * External writefile function prototypes.
*/
#include "BLI_filereader.h"
@@ -91,6 +91,8 @@ void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t s
/**
* Not memfile itself.
*/
+/* **************** support for memory-write, for undo buffers *************** */
+
extern void BLO_memfile_free(MemFile *memfile);
/**
* Result is that 'first' is being freed.
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index 245514d4977..1ff713160df 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -86,10 +86,6 @@ if(WITH_BUILDINFO)
add_definitions(-DWITH_BUILDINFO)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_CODEC_FFMPEG)
add_definitions(-DWITH_FFMPEG)
endif()
diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c
index f3c92aec338..f01d6afb22d 100644
--- a/source/blender/blenloader/intern/readblenentry.c
+++ b/source/blender/blenloader/intern/readblenentry.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * .blend file reading entry point
*/
/** \file
* \ingroup blenloader
+ * `.blend` file reading entry point.
*/
#include <stddef.h>
@@ -419,9 +419,6 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain,
fd->skip_flags = params->skip_flags;
BLI_strncpy(fd->relabase, filename, sizeof(fd->relabase));
- /* clear ob->proxy_from pointers in old main */
- blo_clear_proxy_pointers_from_lib(oldmain);
-
/* separate libraries from old main */
blo_split_main(&old_mainlist, oldmain);
/* add the library pointers in oldmap lookup */
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index f325cec31fd..6757177a385 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -1580,15 +1580,6 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist,
}
}
-void blo_clear_proxy_pointers_from_lib(Main *oldmain)
-{
- LISTBASE_FOREACH (Object *, ob, &oldmain->objects) {
- if (ID_IS_LINKED(ob) && ob->proxy_from != NULL && !ID_IS_LINKED(ob->proxy_from)) {
- ob->proxy_from = NULL;
- }
- }
-}
-
/* XXX disabled this feature - packed files also belong in temp saves and quit.blend,
* to make restore work. */
@@ -2008,6 +1999,7 @@ static void lib_link_id(BlendLibReader *reader, ID *id)
if (id->override_library) {
BLO_read_id_address(reader, id->lib, &id->override_library->reference);
BLO_read_id_address(reader, id->lib, &id->override_library->storage);
+ BLO_read_id_address(reader, id->lib, &id->override_library->hierarchy_root);
}
lib_link_id_embedded_id(reader, id);
@@ -2544,7 +2536,6 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
else if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
sfile->op = NULL;
- sfile->previews_timer = NULL;
sfile->tags = FILE_TAG_REBUILD_MAIN_FILES;
}
else if (sl->spacetype == SPACE_ACTION) {
@@ -2998,7 +2989,7 @@ static const char *dataname(short id_code)
return "Data from CF";
case ID_WS:
return "Data from WS";
- case ID_HA:
+ case ID_CV:
return "Data from HA";
case ID_PT:
return "Data from PT";
@@ -3208,18 +3199,8 @@ static void read_libblock_undo_restore_identical(
id_old->recalc |= direct_link_id_restore_recalc_exceptions(id_old);
id_old->recalc_after_undo_push = 0;
- /* As usual, proxies require some special love...
- * In `blo_clear_proxy_pointers_from_lib()` we clear all `proxy_from` pointers to local IDs, for
- * undo. This is required since we do not re-read linked data in that case, so we also do not
- * re-'lib_link' their pointers.
- * Those `proxy_from` pointers are then re-defined properly when lib_linking the newly read local
- * object. However, in case of re-used data 'as-is', we never lib_link it again, so we have to
- * fix those backward pointers here. */
if (GS(id_old->name) == ID_OB) {
Object *ob = (Object *)id_old;
- if (ob->proxy != NULL) {
- ob->proxy->proxy_from = ob;
- }
/* For undo we stay in object mode during undo presses, so keep editmode disabled for re-used
* data-blocks too. */
ob->mode &= ~OB_MODE_EDIT;
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index 21b0354b097..d75aab9ba9c 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * blenloader readfile private function prototypes
*/
/** \file
* \ingroup blenloader
+ * blenloader readfile private function prototypes.
*/
#pragma once
@@ -144,14 +144,6 @@ FileData *blo_filedata_from_memfile(struct MemFile *memfile,
const struct BlendFileReadParams *params,
struct BlendFileReadReport *reports);
-/**
- * Lib linked proxy objects point to our local data, we need
- * to clear that pointer before reading the undo memfile since
- * the object might be removed, it is set again in reading
- * if the local object still exists.
- * This is only valid for local proxy objects though, linked ones should not be affected here.
- */
-void blo_clear_proxy_pointers_from_lib(struct Main *oldmain);
void blo_make_packed_pointer_map(FileData *fd, struct Main *oldmain);
/**
* Set old main packed data to zero if it has been restored
diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c
index dfa6135dac9..638e6a13b01 100644
--- a/source/blender/blenloader/intern/undofile.c
+++ b/source/blender/blenloader/intern/undofile.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2004 Blender Foundation
* All rights reserved.
- * .blend file reading entry point
*/
/** \file
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index 8a22fd07c24..c34b8f735e5 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -78,6 +78,7 @@
#include "IMB_imbuf.h" /* for proxy / time-code versioning stuff. */
#include "NOD_common.h"
+#include "NOD_composite.h"
#include "NOD_texture.h"
#include "BLO_readfile.h"
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index d9052c6b1f7..57105ca5884 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -349,7 +349,7 @@ static void do_version_scene_collection_convert(
LISTBASE_FOREACH (LinkData *, link, &sc->objects) {
Object *ob = link->data;
if (ob) {
- BKE_collection_object_add(bmain, collection, ob);
+ BKE_collection_object_add_notest(bmain, collection, ob);
id_us_min(&ob->id);
}
}
@@ -402,6 +402,8 @@ static void do_version_scene_collection_to_collection(Main *bmain, Scene *scene)
do_version_layer_collection_pre(
view_layer, &view_layer->layer_collections, enabled_set, selectable_set);
+ BKE_layer_collection_doversion_2_80(scene, view_layer);
+
BKE_layer_collection_sync(scene, view_layer);
do_version_layer_collection_post(
@@ -457,7 +459,7 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene)
/* Note usually this would do slow collection syncing for view layers,
* but since no view layers exists yet at this point it's fast. */
- BKE_collection_object_add(bmain, collections[layer], base->object);
+ BKE_collection_object_add_notest(bmain, collections[layer], base->object);
}
if (base->flag & SELECT) {
@@ -1233,7 +1235,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
(*collection_hidden)->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER;
}
- BKE_collection_object_add(bmain, *collection_hidden, ob);
+ BKE_collection_object_add_notest(bmain, *collection_hidden, ob);
BKE_collection_object_remove(bmain, collection, ob, true);
}
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 888bd244007..87b5da09a60 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -32,11 +32,11 @@
#include "DNA_cachefile_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
+#include "DNA_curves_types.h"
#include "DNA_fluid_types.h"
#include "DNA_genfile.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
-#include "DNA_hair_types.h"
#include "DNA_light_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -1120,10 +1120,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /* Hair and PointCloud attributes. */
- for (Hair *hair = bmain->hairs.first; hair != NULL; hair = hair->id.next) {
- do_versions_point_attributes(&hair->pdata);
- }
+ /* PointCloud attributes. */
for (PointCloud *pointcloud = bmain->pointclouds.first; pointcloud != NULL;
pointcloud = pointcloud->id.next) {
do_versions_point_attributes(&pointcloud->pdata);
@@ -1422,10 +1419,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /* Hair and PointCloud attributes names. */
- LISTBASE_FOREACH (Hair *, hair, &bmain->hairs) {
- do_versions_point_attribute_names(&hair->pdata);
- }
+ /* PointCloud attributes names. */
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
do_versions_point_attribute_names(&pointcloud->pdata);
}
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 78ec2276af7..001dffdca10 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -782,19 +782,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - #blo_do_versions_300 in this file.
- * - "versioning_userdef.c", #blo_do_versions_userdef
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
-
+ if (!MAIN_VERSION_ATLEAST(bmain, 301, 6)) {
{ /* Ensure driver variable names are unique within the driver. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
@@ -829,6 +817,20 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
}
}
}
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #blo_do_versions_300 in this file.
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
}
static void version_switch_node_input_prefix(Main *bmain)
@@ -1105,6 +1107,15 @@ static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data))
return true;
}
+static bool seq_transform_filter_set(Sequence *seq, void *UNUSED(user_data))
+{
+ StripTransform *transform = seq->strip->transform;
+ if (seq->strip->transform != NULL) {
+ transform->filter = SEQ_TRANSFORM_FILTER_BILINEAR;
+ }
+ return true;
+}
+
static void do_version_subsurface_methods(bNode *node)
{
if (node->type == SH_NODE_SUBSURFACE_SCATTERING) {
@@ -2485,18 +2496,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - "versioning_userdef.c", #blo_do_versions_userdef
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
-
+ if (!MAIN_VERSION_ATLEAST(bmain, 301, 6)) {
/* Add node storage for map range node. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -2551,8 +2551,31 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
ntree, GEO_NODE_STRING_TO_CURVES, "Curves", "Curve Instances");
version_node_output_socket_name(
ntree, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Angle", "Unsigned Angle");
+ version_node_output_socket_name(
+ ntree, GEO_NODE_INPUT_MESH_ISLAND, "Index", "Island Index");
version_node_input_socket_name(ntree, GEO_NODE_TRANSFER_ATTRIBUTE, "Target", "Source");
}
}
}
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 302, 2)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->ed != NULL) {
+ SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_filter_set, NULL);
+ }
+ }
+ }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
}
diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c
index f1a2e678b2e..1f18405cdf9 100644
--- a/source/blender/blenloader/intern/versioning_cycles.c
+++ b/source/blender/blenloader/intern/versioning_cycles.c
@@ -42,6 +42,8 @@
#include "BKE_main.h"
#include "BKE_node.h"
+#include "NOD_shader.h"
+
#include "MEM_guardedalloc.h"
#include "IMB_colormanagement.h"
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 064d7977c68..520059649e6 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -973,6 +973,13 @@ void blo_do_versions_userdef(UserDef *userdef)
*/
{
/* Keep this block, even when empty. */
+ if (!USER_VERSION_ATLEAST(301, 7)) {
+ /* io_scene_obj directory is gone, split into io_import_obj and io_export_obj,
+ * with io_import_obj enabled by default and io_export_obj replaced by the C++ version.
+ */
+ BKE_addon_remove_safe(&userdef->addons, "io_scene_obj");
+ BKE_addon_ensure(&userdef->addons, "io_import_obj");
+ }
}
LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) {
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index aa3eef4b475..4eda63d0817 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -999,7 +999,7 @@ static void write_libraries(WriteData *wd, Main *main)
if (!BKE_idtype_idcode_is_linkable(GS(id->name))) {
printf(
"ERROR: write file: data-block '%s' from lib '%s' is not linkable "
- "but is flagged as directly linked",
+ "but is flagged as directly linked\n",
id->name,
main->curlib->filepath_abs);
BLI_assert(0);
diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h
index 21296143226..379ff923229 100644
--- a/source/blender/blentranslation/BLT_translation.h
+++ b/source/blender/blentranslation/BLT_translation.h
@@ -55,24 +55,14 @@ bool BLT_lang_is_ime_supported(void);
#define N_(msgid) msgid
#define CTX_N_(context, msgid) msgid
-/* Those macros should be used everywhere in UI code. */
-#ifdef WITH_INTERNATIONAL
+/* These macros should be used everywhere in UI code. */
/*# define _(msgid) BLT_gettext(msgid) */
-# define IFACE_(msgid) BLT_translate_do_iface(NULL, msgid)
-# define TIP_(msgid) BLT_translate_do_tooltip(NULL, msgid)
-# define DATA_(msgid) BLT_translate_do_new_dataname(NULL, msgid)
-# define CTX_IFACE_(context, msgid) BLT_translate_do_iface(context, msgid)
-# define CTX_TIP_(context, msgid) BLT_translate_do_tooltip(context, msgid)
-# define CTX_DATA_(context, msgid) BLT_translate_do_new_dataname(context, msgid)
-#else
-/*# define _(msgid) msgid */
-# define IFACE_(msgid) msgid
-# define TIP_(msgid) msgid
-# define DATA_(msgid) msgid
-# define CTX_IFACE_(context, msgid) ((void)(0 ? (context) : 0), msgid)
-# define CTX_TIP_(context, msgid) ((void)(0 ? (context) : 0), msgid)
-# define CTX_DATA_(context, msgid) ((void)(0 ? (context) : 0), msgid)
-#endif
+#define IFACE_(msgid) BLT_translate_do_iface(NULL, msgid)
+#define TIP_(msgid) BLT_translate_do_tooltip(NULL, msgid)
+#define DATA_(msgid) BLT_translate_do_new_dataname(NULL, msgid)
+#define CTX_IFACE_(context, msgid) BLT_translate_do_iface(context, msgid)
+#define CTX_TIP_(context, msgid) BLT_translate_do_tooltip(context, msgid)
+#define CTX_DATA_(context, msgid) BLT_translate_do_new_dataname(context, msgid)
/* Helper macro, when we want to define a same msgid for multiple msgctxt...
* Does nothing in C, but is "parsed" by our i18n py tools.
@@ -121,7 +111,7 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_CURVE "Curve"
#define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle"
#define BLT_I18NCONTEXT_ID_GPENCIL "GPencil"
-#define BLT_I18NCONTEXT_ID_HAIR "Hair"
+#define BLT_I18NCONTEXT_ID_CURVES "Curves"
#define BLT_I18NCONTEXT_ID_ID "ID"
#define BLT_I18NCONTEXT_ID_IMAGE "Image"
// #define BLT_I18NCONTEXT_ID_IPO "Ipo" /* DEPRECATED */
@@ -183,7 +173,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \
- BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_HAIR, "id_hair"), \
+ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVES, "id_curves"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ID, "id_id"), \
BLT_I18NCONTEXTS_ITEM( \
BLT_I18NCONTEXT_ID_IMAGE, \
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index f57d4da4d26..a61327238fe 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -98,7 +98,7 @@ set(SRC
intern/bmesh_marking.h
intern/bmesh_mesh.c
intern/bmesh_mesh.h
- intern/bmesh_mesh_convert.c
+ intern/bmesh_mesh_convert.cc
intern/bmesh_mesh_convert.h
intern/bmesh_mesh_debug.c
intern/bmesh_mesh_debug.h
@@ -207,10 +207,6 @@ if(WITH_BULLET)
add_definitions(-DWITH_BULLET)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.cc
index a7dd5be5cbd..d6c642ff80b 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc
@@ -40,7 +40,7 @@
*
* - The active key-block is used for BMesh vertex locations on entering edit-mode.
* So obviously the meshes vertex locations remain unchanged and the shape key
- * its self is not being edited directly.
+ * itself is not being edited directly.
* Simply the #BMVert.co is a initialized from active shape key (when its set).
* - All key-blocks are added as CustomData layers (read code for details).
*
@@ -79,8 +79,11 @@
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
+#include "BLI_array.hh"
+#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
+#include "BLI_span.hh"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
@@ -95,6 +98,10 @@
#include "bmesh.h"
#include "intern/bmesh_private.h" /* For element checking. */
+using blender::Array;
+using blender::IndexRange;
+using blender::Span;
+
void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag)
{
const char cd_flag_all = BM_mesh_cd_flag_from_bmesh(bm) | cd_flag;
@@ -107,9 +114,9 @@ void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag)
void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag)
{
/* CustomData_bmesh_init_pool() must run first */
- BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL);
- BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL);
- BLI_assert(bm->pdata.totlayer == 0 || bm->pdata.pool != NULL);
+ BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != nullptr);
+ BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != nullptr);
+ BLI_assert(bm->pdata.totlayer == 0 || bm->pdata.pool != nullptr);
if (cd_flag & ME_CDFLAG_VERT_BWEIGHT) {
if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) {
@@ -175,35 +182,28 @@ char BM_mesh_cd_flag_from_bmesh(BMesh *bm)
}
/* Static function for alloc (duplicate in modifiers_bmesh.c) */
-static BMFace *bm_face_create_from_mpoly(
- MPoly *mp, MLoop *ml, BMesh *bm, BMVert **vtable, BMEdge **etable)
+static BMFace *bm_face_create_from_mpoly(BMesh &bm,
+ Span<MLoop> loops,
+ Span<BMVert *> vtable,
+ Span<BMEdge *> etable)
{
- BMVert **verts = BLI_array_alloca(verts, mp->totloop);
- BMEdge **edges = BLI_array_alloca(edges, mp->totloop);
- int j;
+ Array<BMVert *, BM_DEFAULT_NGON_STACK_SIZE> verts(loops.size());
+ Array<BMEdge *, BM_DEFAULT_NGON_STACK_SIZE> edges(loops.size());
- for (j = 0; j < mp->totloop; j++, ml++) {
- verts[j] = vtable[ml->v];
- edges[j] = etable[ml->e];
+ for (const int i : loops.index_range()) {
+ verts[i] = vtable[loops[i].v];
+ edges[i] = etable[loops[i].e];
}
- return BM_face_create(bm, verts, edges, mp->totloop, NULL, BM_CREATE_SKIP_CD);
+ return BM_face_create(&bm, verts.data(), edges.data(), loops.size(), nullptr, BM_CREATE_SKIP_CD);
}
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshParams *params)
{
const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer ||
bm->pdata.totlayer || bm->ldata.totlayer));
- MVert *mvert;
- MEdge *medge;
- MLoop *mloop;
- MPoly *mp;
- KeyBlock *actkey, *block;
- BMVert *v, **vtable = NULL;
- BMEdge *e, **etable = NULL;
- BMFace *f, **ftable = NULL;
- float(*keyco)[3] = NULL;
- int totloops, i;
+ KeyBlock *actkey;
+ float(*keyco)[3] = nullptr;
CustomData_MeshMasks mask = CD_MASK_BMESH;
CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
@@ -225,7 +225,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Only copy normals to the new BMesh if they are not already dirty. This avoids unnecessary
* work, but also accessing normals on an incomplete mesh, for example when restoring undo steps
* in edit mode. */
- const float(*vert_normals)[3] = NULL;
+ const float(*vert_normals)[3] = nullptr;
if (!BKE_mesh_vertex_normals_are_dirty(me)) {
vert_normals = BKE_mesh_vertex_normals_ensure(me);
}
@@ -246,7 +246,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* -------------------------------------------------------------------- */
/* Shape Key */
int tot_shape_keys = 0;
- if (me->key != NULL && DEG_is_original_id(&me->id)) {
+ if (me->key != nullptr && DEG_is_original_id(&me->id)) {
/* Evaluated meshes can be topologically inconsistent with their shape keys.
* Shape keys are also already integrated into the state of the evaluated
* mesh, so considering them here would kind of apply them twice. */
@@ -273,20 +273,20 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
if (is_new == false) {
tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY));
}
- const float(**shape_key_table)[3] = tot_shape_keys ?
- BLI_array_alloca(shape_key_table, tot_shape_keys) :
- NULL;
+ const float(**shape_key_table)[3] = tot_shape_keys ? (const float(**)[3])BLI_array_alloca(
+ shape_key_table, tot_shape_keys) :
+ nullptr;
if ((params->active_shapekey != 0) && tot_shape_keys > 0) {
- actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1);
+ actkey = static_cast<KeyBlock *>(BLI_findlink(&me->key->block, params->active_shapekey - 1));
}
else {
- actkey = NULL;
+ actkey = nullptr;
}
if (is_new) {
if (tot_shape_keys || params->add_key_index) {
- CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
+ CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, nullptr, 0);
}
}
@@ -301,26 +301,29 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
__func__);
me->key->uidgen = 1;
- for (block = me->key->block.first; block; block = block->next) {
+ LISTBASE_FOREACH (KeyBlock *, block, &me->key->block) {
block->uid = me->key->uidgen++;
}
}
}
if (actkey && actkey->totelem == me->totvert) {
- keyco = params->use_shapekey ? actkey->data : NULL;
+ keyco = params->use_shapekey ? static_cast<float(*)[3]>(actkey->data) : nullptr;
if (is_new) {
bm->shapenr = params->active_shapekey;
}
}
- for (i = 0, block = me->key->block.first; i < tot_shape_keys; block = block->next, i++) {
+ int i;
+ KeyBlock *block;
+ for (i = 0, block = static_cast<KeyBlock *>(me->key->block.first); i < tot_shape_keys;
+ block = block->next, i++) {
if (is_new) {
- CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_ASSIGN, NULL, 0, block->name);
+ CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_ASSIGN, nullptr, 0, block->name);
int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
bm->vdata.layers[j].uid = block->uid;
}
- shape_key_table[i] = (const float(*)[3])block->data;
+ shape_key_table[i] = static_cast<const float(*)[3]>(block->data);
}
}
@@ -349,17 +352,18 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :
-1;
- vtable = MEM_mallocN(sizeof(BMVert **) * me->totvert, __func__);
-
- for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
- v = vtable[i] = BM_vert_create(bm, keyco ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD);
+ Span<MVert> mvert{me->mvert, me->totvert};
+ Array<BMVert *> vtable(me->totvert);
+ for (const int i : mvert.index_range()) {
+ BMVert *v = vtable[i] = BM_vert_create(
+ bm, keyco ? keyco[i] : mvert[i].co, nullptr, BM_CREATE_SKIP_CD);
BM_elem_index_set(v, i); /* set_ok */
/* Transfer flag. */
- v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT);
+ v->head.hflag = BM_vert_flag_from_mflag(mvert[i].flag & ~SELECT);
/* This is necessary for selection counts to work properly. */
- if (mvert->flag & SELECT) {
+ if (mvert[i].flag & SELECT) {
BM_vert_select_set(bm, v, true);
}
@@ -371,7 +375,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true);
if (cd_vert_bweight_offset != -1) {
- BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f);
+ BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert[i].bweight / 255.0f);
}
/* Set shape key original index. */
@@ -381,7 +385,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Set shape-key data. */
if (tot_shape_keys) {
- float(*co_dst)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset);
+ float(*co_dst)[3] = (float(*)[3])BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset);
for (int j = 0; j < tot_shape_keys; j++, co_dst++) {
copy_v3_v3(*co_dst, shape_key_table[j][i]);
}
@@ -391,19 +395,18 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
bm->elem_index_dirty &= ~BM_VERT; /* Added in order, clear dirty flag. */
}
- etable = MEM_mallocN(sizeof(BMEdge **) * me->totedge, __func__);
-
- medge = me->medge;
- for (i = 0; i < me->totedge; i++, medge++) {
- e = etable[i] = BM_edge_create(
- bm, vtable[medge->v1], vtable[medge->v2], NULL, BM_CREATE_SKIP_CD);
+ Span<MEdge> medge{me->medge, me->totedge};
+ Array<BMEdge *> etable(me->totedge);
+ for (const int i : medge.index_range()) {
+ BMEdge *e = etable[i] = BM_edge_create(
+ bm, vtable[medge[i].v1], vtable[medge[i].v2], nullptr, BM_CREATE_SKIP_CD);
BM_elem_index_set(e, i); /* set_ok */
/* Transfer flags. */
- e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT);
+ e->head.hflag = BM_edge_flag_from_mflag(medge[i].flag & ~SELECT);
/* This is necessary for selection counts to work properly. */
- if (medge->flag & SELECT) {
+ if (medge[i].flag & SELECT) {
BM_edge_select_set(bm, e, true);
}
@@ -411,33 +414,35 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true);
if (cd_edge_bweight_offset != -1) {
- BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge->bweight / 255.0f);
+ BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge[i].bweight / 255.0f);
}
if (cd_edge_crease_offset != -1) {
- BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge->crease / 255.0f);
+ BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge[i].crease / 255.0f);
}
}
if (is_new) {
bm->elem_index_dirty &= ~BM_EDGE; /* Added in order, clear dirty flag. */
}
+ Span<MPoly> mpoly{me->mpoly, me->totpoly};
+ Span<MLoop> mloop{me->mloop, me->totloop};
+
/* Only needed for selection. */
+
+ Array<BMFace *> ftable;
if (me->mselect && me->totselect != 0) {
- ftable = MEM_mallocN(sizeof(BMFace **) * me->totpoly, __func__);
+ ftable.reinitialize(me->totpoly);
}
- mloop = me->mloop;
- mp = me->mpoly;
- for (i = 0, totloops = 0; i < me->totpoly; i++, mp++) {
- BMLoop *l_iter;
- BMLoop *l_first;
-
- f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart, bm, vtable, etable);
- if (ftable != NULL) {
+ int totloops = 0;
+ for (const int i : mpoly.index_range()) {
+ BMFace *f = bm_face_create_from_mpoly(
+ *bm, mloop.slice(mpoly[i].loopstart, mpoly[i].totloop), vtable, etable);
+ if (!ftable.is_empty()) {
ftable[i] = f;
}
- if (UNLIKELY(f == NULL)) {
+ if (UNLIKELY(f == nullptr)) {
printf(
"%s: Warning! Bad face in mesh"
" \"%s\" at index %d!, skipping\n",
@@ -451,20 +456,21 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
BM_elem_index_set(f, bm->totface - 1); /* set_ok */
/* Transfer flag. */
- f->head.hflag = BM_face_flag_from_mflag(mp->flag & ~ME_FACE_SEL);
+ f->head.hflag = BM_face_flag_from_mflag(mpoly[i].flag & ~ME_FACE_SEL);
/* This is necessary for selection counts to work properly. */
- if (mp->flag & ME_FACE_SEL) {
+ if (mpoly[i].flag & ME_FACE_SEL) {
BM_face_select_set(bm, f, true);
}
- f->mat_nr = mp->mat_nr;
+ f->mat_nr = mpoly[i].mat_nr;
if (i == me->act_face) {
bm->act_face = f;
}
- int j = mp->loopstart;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ int j = mpoly[i].loopstart;
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
+ BMLoop *l_iter = l_first;
do {
/* Don't use 'j' since we may have skipped some faces, hence some loops. */
BM_elem_index_set(l_iter, totloops++); /* set_ok */
@@ -485,44 +491,39 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
}
/* -------------------------------------------------------------------- */
- /* MSelect clears the array elements (avoid adding multiple times).
+ /* MSelect clears the array elements (to avoid adding multiple times).
*
* Take care to keep this last and not use (v/e/ftable) after this.
*/
if (me->mselect && me->totselect != 0) {
- MSelect *msel;
- for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) {
+ for (const int i : IndexRange(me->totselect)) {
+ const MSelect &msel = me->mselect[i];
+
BMElem **ele_p;
- switch (msel->type) {
+ switch (msel.type) {
case ME_VSEL:
- ele_p = (BMElem **)&vtable[msel->index];
+ ele_p = (BMElem **)&vtable[msel.index];
break;
case ME_ESEL:
- ele_p = (BMElem **)&etable[msel->index];
+ ele_p = (BMElem **)&etable[msel.index];
break;
case ME_FSEL:
- ele_p = (BMElem **)&ftable[msel->index];
+ ele_p = (BMElem **)&ftable[msel.index];
break;
default:
continue;
}
- if (*ele_p != NULL) {
+ if (*ele_p != nullptr) {
BM_select_history_store_notest(bm, *ele_p);
- *ele_p = NULL;
+ *ele_p = nullptr;
}
}
}
else {
BM_select_history_clear(bm);
}
-
- MEM_freeN(vtable);
- MEM_freeN(etable);
- if (ftable) {
- MEM_freeN(ftable);
- }
}
/**
@@ -531,7 +532,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
{
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
- BMVert **vertMap = NULL;
+ BMVert **vertMap = nullptr;
BMVert *eve;
int i = 0;
BMIter iter;
@@ -539,7 +540,7 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
/* Caller needs to ensure this. */
BLI_assert(ototvert > 0);
- vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap");
+ vertMap = static_cast<BMVert **>(MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap"));
if (cd_shape_keyindex_offset != -1) {
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
@@ -547,7 +548,7 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
/* Not fool-proof, but chances are if we have many verts with the same index,
* we will want to use the first one,
* since the second is more likely to be a duplicate. */
- (vertMap[keyi] == NULL)) {
+ (vertMap[keyi] == nullptr)) {
vertMap[keyi] = eve;
}
}
@@ -590,8 +591,8 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
{
/* This is a cheap way to set the edge draw, its not precise and will
* pick the first 2 faces an edge uses.
- * The dot comparison is a little arbitrary, but set so that a 5 subd
- * IcoSphere won't vanish but subd 6 will (as with pre-bmesh Blender). */
+ * The dot comparison is a little arbitrary, but set so that a 5 subdivisions
+ * ico-sphere won't vanish but 6 subdivisions will (as with pre-bmesh Blender). */
if (/* (med->flag & ME_EDGEDRAW) && */ /* Assume to be true. */
(e->l && (e->l != e->l->radial_next)) &&
@@ -617,7 +618,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
- MVert *oldverts = NULL;
+ MVert *oldverts = nullptr;
const int ototvert = me->totvert;
if (me->key && (cd_shape_keyindex_offset != -1)) {
@@ -628,9 +629,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
oldverts = MEM_dupallocN(me->mvert);
#else
oldverts = me->mvert;
- me->mvert = NULL;
+ me->mvert = nullptr;
CustomData_update_typemap(&me->vdata);
- CustomData_set_layer(&me->vdata, CD_MVERT, NULL);
+ CustomData_set_layer(&me->vdata, CD_MVERT, nullptr);
#endif
}
@@ -647,7 +648,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
me->totloop = bm->totloop;
me->totpoly = bm->totface;
/* Will be overwritten with a valid value if 'dotess' is set, otherwise we
- * end up with 'me->totface' and me->mface == NULL which can crash T28625. */
+ * end up with 'me->totface' and me->mface == nullptr which can crash T28625. */
me->totface = 0;
me->act_face = -1;
@@ -660,10 +661,14 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly);
}
- MVert *mvert = bm->totvert ? MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") : NULL;
- MEdge *medge = bm->totedge ? MEM_callocN(sizeof(MEdge) * bm->totedge, "bm_to_me.edge") : NULL;
- MLoop *mloop = bm->totloop ? MEM_callocN(sizeof(MLoop) * bm->totloop, "bm_to_me.loop") : NULL;
- MPoly *mpoly = bm->totface ? MEM_callocN(sizeof(MPoly) * bm->totface, "bm_to_me.poly") : NULL;
+ MVert *mvert = bm->totvert ? (MVert *)MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") :
+ nullptr;
+ MEdge *medge = bm->totedge ? (MEdge *)MEM_callocN(sizeof(MEdge) * bm->totedge, "bm_to_me.edge") :
+ nullptr;
+ MLoop *mloop = bm->totloop ? (MLoop *)MEM_callocN(sizeof(MLoop) * bm->totloop, "bm_to_me.loop") :
+ nullptr;
+ MPoly *mpoly = bm->totface ? (MPoly *)MEM_callocN(sizeof(MPoly) * bm->totface, "bm_to_me.poly") :
+ nullptr;
CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert);
CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge);
@@ -677,7 +682,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm);
/* This is called again, 'dotess' arg is used there. */
- BKE_mesh_update_customdata_pointers(me, 0);
+ BKE_mesh_update_customdata_pointers(me, false);
i = 0;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
@@ -767,15 +772,13 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
/* Patch hook indices and vertex parents. */
if (params->calc_object_remap && (ototvert > 0)) {
- BLI_assert(bmain != NULL);
- Object *ob;
- ModifierData *md;
- BMVert **vertMap = NULL;
+ BLI_assert(bmain != nullptr);
+ BMVert **vertMap = nullptr;
- for (ob = bmain->objects.first; ob; ob = ob->id.next) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if ((ob->parent) && (ob->parent->data == me) && ELEM(ob->partype, PARVERT1, PARVERT3)) {
- if (vertMap == NULL) {
+ if (vertMap == nullptr) {
vertMap = bm_to_mesh_vertex_map(bm, ototvert);
}
@@ -799,11 +802,11 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
}
if (ob->data == me) {
- for (md = ob->modifiers.first; md; md = md->next) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *)md;
- if (vertMap == NULL) {
+ if (vertMap == nullptr) {
vertMap = bm_to_mesh_vertex_map(bm, ototvert);
}
@@ -834,15 +837,15 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
BKE_mesh_update_customdata_pointers(me, false);
{
- BMEditSelection *selected;
me->totselect = BLI_listbase_count(&(bm->selected));
MEM_SAFE_FREE(me->mselect);
if (me->totselect != 0) {
- me->mselect = MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
+ me->mselect = static_cast<MSelect *>(
+ MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history"));
}
- for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
+ LISTBASE_FOREACH_INDEX (BMEditSelection *, selected, &bm->selected, i) {
if (selected->htype == BM_VERT) {
me->mselect[i].type = ME_VSEL;
}
@@ -861,9 +864,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
if (me->key) {
KeyBlock *currkey;
- KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
+ KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&me->key->block, bm->shapenr - 1));
- float(*ofs)[3] = NULL;
+ float(*ofs)[3] = nullptr;
/* Go through and find any shape-key custom-data layers
* that might not have corresponding KeyBlocks, and add them if necessary. */
@@ -872,7 +875,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
continue;
}
- for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ for (currkey = (KeyBlock *)me->key->block.first; currkey; currkey = currkey->next) {
if (currkey->uid == bm->vdata.layers[i].uid) {
break;
}
@@ -890,10 +893,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
/* Unlikely, but the active key may not be valid if the
* BMesh and the mesh are out of sync. */
- (actkey != NULL) &&
+ (actkey != nullptr) &&
/* Not used here, but 'oldverts' is used later for applying 'ofs'. */
- (oldverts != NULL) &&
+ (oldverts != nullptr) &&
/* Needed for referencing oldverts. */
(cd_shape_keyindex_offset != -1)) {
@@ -902,9 +905,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
/* Active key is a base. */
if (act_is_basis) {
- const float(*fp)[3] = actkey->data;
+ const float(*fp)[3] = static_cast<const float(*)[3]>(actkey->data);
- ofs = MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data");
+ ofs = static_cast<float(*)[3]>(
+ MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data"));
mvert = me->mvert;
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
@@ -918,7 +922,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
* because it will only work for the existing vertices and not the new
* ones, creating a mess when doing e.g. subdivide + translate. */
MEM_freeN(ofs);
- ofs = NULL;
+ ofs = nullptr;
break;
}
@@ -927,7 +931,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
}
- for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ LISTBASE_FOREACH (KeyBlock *, currkey, &me->key->block) {
int keyi;
const float(*ofs_pt)[3] = ofs;
float *newkey, (*oldkey)[3], *fp;
@@ -937,11 +941,12 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
CustomData_get_n_offset(&bm->vdata,
CD_SHAPEKEY,
currkey_uuid);
- const bool apply_offset = (cd_shape_offset != -1) && (ofs != NULL) && (currkey != actkey) &&
- (bm->shapenr - 1 == currkey->relative);
+ const bool apply_offset = (cd_shape_offset != -1) && (ofs != nullptr) &&
+ (currkey != actkey) && (bm->shapenr - 1 == currkey->relative);
- fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data");
- oldkey = currkey->data;
+ fp = newkey = static_cast<float *>(
+ MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"));
+ oldkey = static_cast<float(*)[3]>(currkey->data);
mvert = me->mvert;
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
@@ -962,9 +967,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
else if (cd_shape_offset != -1) {
/* In most cases this runs. */
- copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
+ copy_v3_v3(fp, (const float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
}
- else if ((oldkey != NULL) && (cd_shape_keyindex_offset != -1) &&
+ else if ((oldkey != nullptr) && (cd_shape_keyindex_offset != -1) &&
((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
(keyi < currkey->totelem)) {
/* Old method of reconstructing keys via vertices original key indices,
@@ -984,7 +989,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
* Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh,
* we'll apply diff from previous call to #BM_mesh_bm_to_me,
* to shape-key values from *original creation of the BMesh*. See T50524. */
- copy_v3_v3(BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp);
+ copy_v3_v3((float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp);
}
fp += 3;
@@ -1014,7 +1019,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
}
- if (oldverts != NULL) {
+ if (oldverts != nullptr) {
MEM_freeN(oldverts);
}
@@ -1029,7 +1034,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
{
/* Must be an empty mesh. */
BLI_assert(me->totvert == 0);
- BLI_assert(cd_mask_extra == NULL || (cd_mask_extra->vmask & CD_MASK_SHAPEKEY) == 0);
+ BLI_assert(cd_mask_extra == nullptr || (cd_mask_extra->vmask & CD_MASK_SHAPEKEY) == 0);
me->totvert = bm->totvert;
me->totedge = bm->totedge;
@@ -1037,19 +1042,19 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
me->totloop = bm->totloop;
me->totpoly = bm->totface;
- CustomData_add_layer(&me->vdata, CD_ORIGINDEX, CD_CALLOC, NULL, bm->totvert);
- CustomData_add_layer(&me->edata, CD_ORIGINDEX, CD_CALLOC, NULL, bm->totedge);
- CustomData_add_layer(&me->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, bm->totface);
+ CustomData_add_layer(&me->vdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totvert);
+ CustomData_add_layer(&me->edata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totedge);
+ CustomData_add_layer(&me->pdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totface);
- CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, bm->totvert);
- CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, NULL, bm->totedge);
- CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, bm->totloop);
- CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, bm->totface);
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, bm->totvert);
+ CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, bm->totedge);
+ CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, nullptr, bm->totloop);
+ CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, nullptr, bm->totface);
/* Don't process shape-keys, we only feed them through the modifier stack as needed,
* e.g. for applying modifiers or the like. */
CustomData_MeshMasks mask = CD_MASK_DERIVEDMESH;
- if (cd_mask_extra != NULL) {
+ if (cd_mask_extra != nullptr) {
CustomData_MeshMasks_update(&mask, cd_mask_extra);
}
mask.vmask &= ~CD_MASK_SHAPEKEY;
@@ -1082,7 +1087,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
/* Don't add origindex layer if one already exists. */
add_orig = !CustomData_has_layer(&bm->pdata, CD_ORIGINDEX);
- index = CustomData_get_layer(&me->vdata, CD_ORIGINDEX);
+ index = (int *)CustomData_get_layer(&me->vdata, CD_ORIGINDEX);
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
MVert *mv = &mvert[i];
@@ -1105,7 +1110,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
}
bm->elem_index_dirty &= ~BM_VERT;
- index = CustomData_get_layer(&me->edata, CD_ORIGINDEX);
+ index = (int *)CustomData_get_layer(&me->edata, CD_ORIGINDEX);
BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) {
MEdge *med = &medge[i];
@@ -1138,7 +1143,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
}
bm->elem_index_dirty &= ~BM_EDGE;
- index = CustomData_get_layer(&me->pdata, CD_ORIGINDEX);
+ index = (int *)CustomData_get_layer(&me->pdata, CD_ORIGINDEX);
j = 0;
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
BMLoop *l_iter;
diff --git a/source/blender/bmesh/intern/bmesh_operators.h b/source/blender/bmesh/intern/bmesh_operators.h
index e5ede75f737..900844af4e5 100644
--- a/source/blender/bmesh/intern/bmesh_operators.h
+++ b/source/blender/bmesh/intern/bmesh_operators.h
@@ -93,6 +93,7 @@ enum {
SIMVERT_FACE,
SIMVERT_VGROUP,
SIMVERT_EDGE,
+ SIMVERT_CREASE,
};
/* Poke face center calculation */
diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h
index 977142578ca..4b3c4cbff82 100644
--- a/source/blender/bmesh/intern/bmesh_private.h
+++ b/source/blender/bmesh/intern/bmesh_private.h
@@ -27,6 +27,10 @@
* parts of the bmesh internals.
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* returns positive nonzero on error */
#ifdef NDEBUG
@@ -102,3 +106,7 @@ void poly_rotate_plane(const float normal[3], float (*verts)[3], uint nverts);
/* include the rest of our private declarations */
#include "bmesh_structure.h"
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/bmesh/intern/bmesh_query.h b/source/blender/bmesh/intern/bmesh_query.h
index 63ce95a19d3..841549945ca 100644
--- a/source/blender/bmesh/intern/bmesh_query.h
+++ b/source/blender/bmesh/intern/bmesh_query.h
@@ -561,7 +561,7 @@ BMFace *BM_face_find_double(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
* many overlapping faces.
*
* An example of how this is used: when 2 tri's are selected that share an edge,
- * pressing Fkey would make a new overlapping quad (without a check like this)
+ * pressing F-key would make a new overlapping quad (without a check like this)
*
* \a earr and \a varr can be in any order, however they _must_ form a closed loop.
*/
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 2f471bf0b81..b429399f310 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -451,6 +451,16 @@ static bool nearly_parallel_normalized(const float d1[3], const float d2[3])
return compare_ff(fabsf(direction_dot), 1.0f, BEVEL_EPSILON_ANG_DOT);
}
+/**
+ * calculate the determinant of a matrix formed by three vectors
+ * \return dot(a, cross(b, c)) = determinant(a, b, c)
+ */
+static float determinant_v3v3v3(const float a[3], const float b[3], const float c[3])
+{
+ return a[0] * b[1] * c[2] + a[1] * b[2] * c[0] + a[2] * b[0] * c[1] - a[0] * b[2] * c[1] -
+ a[1] * b[0] * c[2] - a[2] * b[1] * c[0];
+}
+
/* Make a new BoundVert of the given kind, inserting it at the end of the circular linked
* list with entry point bv->boundstart, and return it. */
static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float co[3])
@@ -4118,44 +4128,114 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
VMesh *vm_out = new_adj_vmesh(bp->mem_arena, n_boundary, ns_out, vm_in->boundstart);
/* First we adjust the boundary vertices of the input mesh, storing in output mesh. */
+ BoundVert *bndv = vm_in->boundstart;
for (int i = 0; i < n_boundary; i++) {
+ float co1[3], co2[3], acc[3];
+ EdgeHalf *e = bndv->elast;
+ /* Generate tangents. This is hacked together and would ideally be done elsewhere and then only
+ * used here. */
+ float tangent[3], tangent2[3], normal[3];
+ bool convex = true;
+ bool orthogonal = false;
+ float stretch = 0.0f;
+ if (e) {
+ /* Projection direction is direction of the edge. */
+ sub_v3_v3v3(tangent, e->e->v1->co, e->e->v2->co);
+ if (e->is_rev) {
+ negate_v3(tangent);
+ }
+ normalize_v3(tangent);
+ if (bndv->is_arc_start || bndv->is_patch_start) {
+ BMFace *face = e->fnext;
+ if (face) {
+ copy_v3_v3(normal, face->no);
+ }
+ else {
+ zero_v3(normal);
+ }
+ madd_v3_v3v3fl(co2, bndv->profile.middle, normal, 0.1f);
+ }
+ if (bndv->is_arc_start || bp->affect_type == BEVEL_AFFECT_VERTICES) {
+ EdgeHalf *e1 = bndv->next->elast;
+ BLI_assert(e1);
+ sub_v3_v3v3(tangent2, e1->e->v1->co, e1->e->v2->co);
+ if (e1->is_rev) {
+ negate_v3(tangent2);
+ }
+ normalize_v3(tangent2);
+
+ convex = determinant_v3v3v3(tangent2, tangent, normal) < 0;
+
+ add_v3_v3(tangent2, tangent);
+ normalize_v3(tangent2);
+ copy_v3_v3(tangent, tangent2);
+ }
+ /* Calculate a factor which determines how much the interpolated mesh is
+ * going to be stretched out into the direction of the tangent.
+ * It is currently using the difference along the tangent of the
+ * central point on the profile and the current center vertex position. */
+ get_profile_point(bp, &bndv->profile, ns_in2, ns_in, co);
+ stretch = dot_v3v3(tangent, mesh_vert(vm_in, i, ns_in2, ns_in2)->co) - dot_v3v3(tangent, co);
+ stretch = fabsf(stretch);
+ /* Scale the tangent by stretch. The divide by ns_in2 comes from the Levin Paper. */
+ mul_v3_fl(tangent, stretch / ns_in2);
+ orthogonal = bndv->is_patch_start;
+ }
+ else if (bndv->prev->is_patch_start) {
+ /* If this is the second edge of a patch and therefore #e is NULL,
+ * then e->fprev has to be used/not NULL. */
+ BLI_assert(bndv->prev->elast);
+ BMFace *face = bndv->prev->elast->fnext;
+ if (face) {
+ copy_v3_v3(normal, face->no);
+ }
+ else {
+ zero_v3(normal);
+ }
+ orthogonal = true;
+ }
+ else {
+ /** Should only come here from make_cube_corner_adj_vmesh. */
+ sub_v3_v3v3(co1, mesh_vert(vm_in, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 1)->co);
+ sub_v3_v3v3(co2, mesh_vert(vm_in, i, 0, 1)->co, mesh_vert(vm_in, i, 0, 2)->co);
+ cross_v3_v3v3(tangent, co1, co2);
+ /** The following constant is chosen to best match the old results. */
+ normalize_v3_length(tangent, 1.5f / ns_out);
+ }
+ /** Copy corner vertex. */
copy_v3_v3(mesh_vert(vm_out, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 0)->co);
+ /** Copy the rest of the boundary vertices. */
for (int k = 1; k < ns_in; k++) {
copy_v3_v3(co, mesh_vert(vm_in, i, 0, k)->co);
- /* Smooth boundary rule. Custom profiles shouldn't be smoothed. */
- if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
- float co1[3], co2[3], acc[3];
- copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co);
- copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co);
-
- add_v3_v3v3(acc, co1, co2);
- madd_v3_v3fl(acc, co, -2.0f);
- madd_v3_v3fl(co, acc, -1.0f / 6.0f);
- }
+ copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co);
+ copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co);
+
+ add_v3_v3v3(acc, co1, co2);
+ if (bndv->is_arc_start) {
+ sub_v3_v3(co1, co);
+ sub_v3_v3(co2, co);
+ normalize_v3(co1);
+ normalize_v3(co2);
+ add_v3_v3v3(tangent, co1, co2);
+ /* This is an empirical formula to make the result look good. */
+ normalize_v3(tangent);
+ float dot = convex ? fminf(0, dot_v3v3(tangent2, tangent)) : 1.0f;
+ mul_v3_fl(tangent, stretch / ns_in * dot);
+ }
+ else if (orthogonal) {
+ sub_v3_v3(co1, co);
+ cross_v3_v3v3(tangent, normal, co1);
+ /* This is an empirical formula to make the result look good. */
+ normalize_v3_length(tangent, -bp->offset * 0.7071f / ns_in);
+ }
+ mul_v3_fl(co, 2.0f);
+ madd_v3_v3fl(co, acc, -0.25f);
+ madd_v3_v3fl(co, mesh_vert(vm_in, i, 1, k)->co, -0.5f);
+ add_v3_v3(co, tangent);
copy_v3_v3(mesh_vert_canon(vm_out, i, 0, 2 * k)->co, co);
}
- }
- /* Now adjust odd boundary vertices in output mesh, based on even ones. */
- BoundVert *bndv = vm_out->boundstart;
- for (int i = 0; i < n_boundary; i++) {
- for (int k = 1; k < ns_out; k += 2) {
- get_profile_point(bp, &bndv->profile, k, ns_out, co);
-
- /* Smooth if using a non-custom profile. */
- if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
- float co1[3], co2[3], acc[3];
- copy_v3_v3(co1, mesh_vert_canon(vm_out, i, 0, k - 1)->co);
- copy_v3_v3(co2, mesh_vert_canon(vm_out, i, 0, k + 1)->co);
-
- add_v3_v3v3(acc, co1, co2);
- madd_v3_v3fl(acc, co, -2.0f);
- madd_v3_v3fl(co, acc, -1.0f / 6.0f);
- }
-
- copy_v3_v3(mesh_vert_canon(vm_out, i, 0, k)->co, co);
- }
bndv = bndv->next;
}
vmesh_copy_equiv_verts(vm_out);
@@ -4163,7 +4243,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
/* Copy adjusted verts back into vm_in. */
for (int i = 0; i < n_boundary; i++) {
for (int k = 0; k < ns_in; k++) {
- copy_v3_v3(mesh_vert(vm_in, i, 0, k)->co, mesh_vert(vm_out, i, 0, 2 * k)->co);
+ copy_v3_v3(mesh_vert_canon(vm_in, i, 0, k)->co, mesh_vert_canon(vm_out, i, 0, 2 * k)->co);
}
}
@@ -4248,7 +4328,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
vmesh_copy_equiv_verts(vm_out);
/* The center vertex is special. */
- gamma = sabin_gamma(n_boundary);
+ gamma = sabin_gamma(n_boundary) * 0.5f;
beta = -gamma;
/* Accumulate edge verts in co1, face verts in co2. */
float co1[3], co2[3];
diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc
index e244bc377db..91883b0adee 100644
--- a/source/blender/bmesh/tools/bmesh_boolean.cc
+++ b/source/blender/bmesh/tools/bmesh_boolean.cc
@@ -192,12 +192,12 @@ static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out, bool keep_hidden
BM_elem_flag_enable(bmv, KEEP_FLAG);
}
else {
- new_bmvs[v] = NULL;
+ new_bmvs[v] = nullptr;
}
}
for (int v : m_out.vert_index_range()) {
const Vert *vertp = m_out.vert(v);
- if (new_bmvs[v] == NULL) {
+ if (new_bmvs[v] == nullptr) {
float co[3];
const double3 &d_co = vertp->co;
for (int i = 0; i < 3; ++i) {
diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
index e90e50ef8ff..c653b2b8007 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
@@ -847,7 +847,7 @@ BLI_INLINE int bm_edge_is_manifold_or_boundary(BMLoop *l)
/* less optimized version of check below */
return (BM_edge_is_manifold(l->e) || BM_edge_is_boundary(l->e);
#else
- /* if the edge is a boundary it points to its self, else this must be a manifold */
+ /* if the edge is a boundary it points to itself, else this must be a manifold */
return LIKELY(l) && LIKELY(l->radial_next->radial_next == l);
#endif
}
@@ -855,7 +855,7 @@ BLI_INLINE int bm_edge_is_manifold_or_boundary(BMLoop *l)
static bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first)
{
/* simply check that there is no overlap between faces and edges of each vert,
- * (excluding the 2 faces attached to 'e' and 'e' its self) */
+ * (excluding the 2 faces attached to 'e' and 'e' itself) */
BMEdge *e_iter;
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index d5d8154d7ee..2f473ef2945 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -38,8 +38,8 @@ set(INC
../render/intern
../../../extern/clew/include
../../../intern/atomic
- ../../../intern/guardedalloc
../../../intern/clog
+ ../../../intern/guardedalloc
# dna_type_offsets.h
${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern
@@ -140,6 +140,8 @@ set(SRC
nodes/COM_OutputFileNode.h
nodes/COM_RenderLayersNode.cc
nodes/COM_RenderLayersNode.h
+ nodes/COM_SceneTimeNode.cc
+ nodes/COM_SceneTimeNode.h
nodes/COM_SwitchNode.cc
nodes/COM_SwitchNode.h
nodes/COM_SwitchViewNode.cc
@@ -150,8 +152,6 @@ set(SRC
nodes/COM_TimeNode.h
nodes/COM_ValueNode.cc
nodes/COM_ValueNode.h
- nodes/COM_SceneTimeNode.cc
- nodes/COM_SceneTimeNode.h
# output nodes
nodes/COM_CompositorNode.cc
@@ -274,10 +274,14 @@ set(SRC
# converter nodes
nodes/COM_CombineColorNode.cc
nodes/COM_CombineColorNode.h
+ nodes/COM_CombineXYZNode.cc
+ nodes/COM_CombineXYZNode.h
nodes/COM_IDMaskNode.cc
nodes/COM_IDMaskNode.h
nodes/COM_SeparateColorNode.cc
nodes/COM_SeparateColorNode.h
+ nodes/COM_SeparateXYZNode.cc
+ nodes/COM_SeparateXYZNode.h
nodes/COM_MapRangeNode.cc
nodes/COM_MapRangeNode.h
@@ -573,10 +577,10 @@ set(SRC
operations/COM_IDMaskOperation.cc
operations/COM_IDMaskOperation.h
- operations/COM_DotproductOperation.cc
- operations/COM_DotproductOperation.h
operations/COM_ConvertColorSpaceOperation.cc
operations/COM_ConvertColorSpaceOperation.h
+ operations/COM_DotproductOperation.cc
+ operations/COM_DotproductOperation.h
# Matte operation
operations/COM_BoxMaskOperation.cc
@@ -631,10 +635,6 @@ list(APPEND SRC
unset(GENSRC)
unset(GENSRC_DIR)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_OPENIMAGEDENOISE)
add_definitions(-DWITH_OPENIMAGEDENOISE)
add_definitions(-DOIDN_STATIC_LIB)
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index 6cf6c698a2f..d0b3ae74446 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -44,6 +44,7 @@
#include "COM_ColorSpillNode.h"
#include "COM_ColorToBWNode.h"
#include "COM_CombineColorNode.h"
+#include "COM_CombineXYZNode.h"
#include "COM_CompositorNode.h"
#include "COM_ConvertAlphaNode.h"
#include "COM_ConvertColorSpaceNode.h"
@@ -96,6 +97,7 @@
#include "COM_ScaleOperation.h"
#include "COM_SceneTimeNode.h"
#include "COM_SeparateColorNode.h"
+#include "COM_SeparateXYZNode.h"
#include "COM_SetAlphaNode.h"
#include "COM_SetValueOperation.h"
#include "COM_SplitViewerNode.h"
@@ -434,6 +436,12 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_CONVERT_COLOR_SPACE:
node = new ConvertColorSpaceNode(b_node);
break;
+ case CMP_NODE_SEPARATE_XYZ:
+ node = new SeparateXYZNode(b_node);
+ break;
+ case CMP_NODE_COMBINE_XYZ:
+ node = new CombineXYZNode(b_node);
+ break;
}
return node;
}
diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
index b11c57041d9..d1b2e69d7ff 100644
--- a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
+++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
@@ -1,6 +1,4 @@
/*
- * Copyright 2017, Blender Foundation.
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -15,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Contributor: IRIE Shinsuke
+ * Copyright 2017, Blender Foundation.
*/
#include "COM_AntiAliasingNode.h"
diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.h b/source/blender/compositor/nodes/COM_AntiAliasingNode.h
index 05c51d5856a..bcae235b434 100644
--- a/source/blender/compositor/nodes/COM_AntiAliasingNode.h
+++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.h
@@ -1,6 +1,4 @@
/*
- * Copyright 2017, Blender Foundation.
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -15,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Contributor: IRIE Shinsuke
+ * Copyright 2017, Blender Foundation.
*/
#pragma once
diff --git a/source/blender/compositor/nodes/COM_CombineXYZNode.cc b/source/blender/compositor/nodes/COM_CombineXYZNode.cc
new file mode 100644
index 00000000000..2b71b94e192
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_CombineXYZNode.cc
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_CombineXYZNode.h"
+
+#include "COM_ConvertOperation.h"
+
+namespace blender::compositor {
+
+CombineXYZNode::CombineXYZNode(bNode *editor_node) : Node(editor_node)
+{
+}
+
+void CombineXYZNode::convert_to_operations(NodeConverter &converter,
+ const CompositorContext &UNUSED(context)) const
+{
+ NodeInput *input_x_socket = this->get_input_socket(0);
+ NodeInput *input_y_socket = this->get_input_socket(1);
+ NodeInput *input_z_socket = this->get_input_socket(2);
+ NodeOutput *output_socket = this->get_output_socket(0);
+
+ CombineChannelsOperation *operation = new CombineChannelsOperation();
+ if (input_x_socket->is_linked()) {
+ operation->set_canvas_input_index(0);
+ }
+ else if (input_y_socket->is_linked()) {
+ operation->set_canvas_input_index(1);
+ }
+ else {
+ operation->set_canvas_input_index(2);
+ }
+ converter.add_operation(operation);
+
+ converter.map_input_socket(input_x_socket, operation->get_input_socket(0));
+ converter.map_input_socket(input_y_socket, operation->get_input_socket(1));
+ converter.map_input_socket(input_z_socket, operation->get_input_socket(2));
+ converter.map_output_socket(output_socket, operation->get_output_socket());
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CombineXYZNode.h b/source/blender/compositor/nodes/COM_CombineXYZNode.h
new file mode 100644
index 00000000000..c3bb69b03ed
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_CombineXYZNode.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_Node.h"
+
+namespace blender::compositor {
+
+/**
+ * \brief SeparateXYZNode
+ * \ingroup Node
+ */
+class CombineXYZNode : public Node {
+ public:
+ CombineXYZNode(bNode *editor_node);
+ void convert_to_operations(NodeConverter &converter,
+ const CompositorContext &context) const override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
index 605dc1dc84d..c360e519cf8 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
@@ -16,9 +16,12 @@
* Copyright 2018, Blender Foundation.
*/
-#include "COM_CryptomatteNode.h"
#include "BKE_node.h"
+
+#include "NOD_composite.h"
+
#include "COM_ConvertOperation.h"
+#include "COM_CryptomatteNode.h"
#include "COM_MultilayerImageOperation.h"
#include "COM_RenderLayersProg.h"
#include "COM_SetAlphaMultiplyOperation.h"
diff --git a/source/blender/compositor/nodes/COM_FilterNode.cc b/source/blender/compositor/nodes/COM_FilterNode.cc
index 2108e68cbec..4eca1492fe9 100644
--- a/source/blender/compositor/nodes/COM_FilterNode.cc
+++ b/source/blender/compositor/nodes/COM_FilterNode.cc
@@ -48,7 +48,7 @@ void FilterNode::convert_to_operations(NodeConverter &converter,
2 / 16.0f,
1 / 16.0f);
break;
- case CMP_FILT_SHARP:
+ case CMP_FILT_SHARP_BOX:
operation = new ConvolutionFilterOperation();
operation->set3x3Filter(-1, -1, -1, -1, 9, -1, -1, -1, -1);
break;
@@ -80,6 +80,10 @@ void FilterNode::convert_to_operations(NodeConverter &converter,
operation = new ConvolutionFilterOperation();
operation->set3x3Filter(1, 2, 1, 0, 1, 0, -1, -2, -1);
break;
+ case CMP_FILT_SHARP_DIAMOND:
+ operation = new ConvolutionFilterOperation();
+ operation->set3x3Filter(0, -1, 0, -1, 5, -1, 0, -1, 0);
+ break;
default:
operation = new ConvolutionFilterOperation();
operation->set3x3Filter(0, 0, 0, 0, 1, 0, 0, 0, 0);
diff --git a/source/blender/compositor/nodes/COM_SeparateXYZNode.cc b/source/blender/compositor/nodes/COM_SeparateXYZNode.cc
new file mode 100644
index 00000000000..749116d6217
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_SeparateXYZNode.cc
@@ -0,0 +1,63 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_SeparateXYZNode.h"
+
+#include "COM_ConvertOperation.h"
+
+namespace blender::compositor {
+
+SeparateXYZNode::SeparateXYZNode(bNode *editor_node) : Node(editor_node)
+{
+ /* pass */
+}
+
+void SeparateXYZNode::convert_to_operations(NodeConverter &converter,
+ const CompositorContext &UNUSED(context)) const
+{
+ NodeInput *vector_socket = this->get_input_socket(0);
+ NodeOutput *output_x_socket = this->get_output_socket(0);
+ NodeOutput *output_y_socket = this->get_output_socket(1);
+ NodeOutput *output_z_socket = this->get_output_socket(2);
+
+ {
+ SeparateChannelOperation *operation = new SeparateChannelOperation();
+ operation->set_channel(0);
+ converter.add_operation(operation);
+ converter.map_input_socket(vector_socket, operation->get_input_socket(0));
+ converter.map_output_socket(output_x_socket, operation->get_output_socket(0));
+ }
+
+ {
+ SeparateChannelOperation *operation = new SeparateChannelOperation();
+ operation->set_channel(1);
+ converter.add_operation(operation);
+ converter.map_input_socket(vector_socket, operation->get_input_socket(0));
+ converter.map_output_socket(output_y_socket, operation->get_output_socket(0));
+ }
+
+ {
+ SeparateChannelOperation *operation = new SeparateChannelOperation();
+ operation->set_channel(2);
+ converter.add_operation(operation);
+ converter.map_input_socket(vector_socket, operation->get_input_socket(0));
+ converter.map_output_socket(output_z_socket, operation->get_output_socket(0));
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateXYZNode.h b/source/blender/compositor/nodes/COM_SeparateXYZNode.h
new file mode 100644
index 00000000000..1efa017d9e3
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_SeparateXYZNode.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_Node.h"
+
+namespace blender::compositor {
+
+/**
+ * \brief SeparateXYZNode
+ * \ingroup Node
+ */
+class SeparateXYZNode : public Node {
+ public:
+ SeparateXYZNode(bNode *editor_node);
+ void convert_to_operations(NodeConverter &converter,
+ const CompositorContext &context) const override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc
index 20e85c69ac8..c466338b61c 100644
--- a/source/blender/compositor/operations/COM_SMAAOperation.cc
+++ b/source/blender/compositor/operations/COM_SMAAOperation.cc
@@ -1,6 +1,4 @@
/*
- * Copyright 2017, Blender Foundation.
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -15,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Contributor: IRIE Shinsuke
+ * Copyright 2017, Blender Foundation.
*/
#include "COM_SMAAOperation.h"
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.h b/source/blender/compositor/operations/COM_SMAAOperation.h
index ec04594e0aa..bac33f0a2b4 100644
--- a/source/blender/compositor/operations/COM_SMAAOperation.h
+++ b/source/blender/compositor/operations/COM_SMAAOperation.h
@@ -1,6 +1,4 @@
/*
- * Copyright 2017, Blender Foundation.
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -15,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Contributor: IRIE Shinsuke
+ * Copyright 2017, Blender Foundation.
*/
#pragma once
diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc
index f5d47478a8d..069d00b5e66 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.cc
+++ b/source/blender/compositor/operations/COM_TextureOperation.cc
@@ -22,6 +22,8 @@
#include "BKE_image.h"
#include "BKE_node.h"
+#include "NOD_texture.h"
+
namespace blender::compositor {
TextureBaseOperation::TextureBaseOperation()
@@ -131,11 +133,9 @@ void TextureBaseOperation::execute_pixel_sampled(float output[4],
retval = multitex_ext(
texture_, vec, nullptr, nullptr, 0, &texres, thread_id, pool_, scene_color_manage_, false);
- output[3] = texres.talpha ? texres.ta : texres.tin;
+ output[3] = texres.talpha ? texres.trgba[3] : texres.tin;
if (retval & TEX_RGB) {
- output[0] = texres.tr;
- output[1] = texres.tg;
- output[2] = texres.tb;
+ copy_v3_v3(output, texres.trgba);
}
else {
output[0] = output[1] = output[2] = output[3];
@@ -184,11 +184,9 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
scene_color_manage_,
false);
- it.out[3] = tex_result.talpha ? tex_result.ta : tex_result.tin;
+ it.out[3] = tex_result.talpha ? tex_result.trgba[3] : tex_result.tin;
if (retval & TEX_RGB) {
- it.out[0] = tex_result.tr;
- it.out[1] = tex_result.tg;
- it.out[2] = tex_result.tb;
+ copy_v3_v3(it.out, tex_result.trgba);
}
else {
it.out[0] = it.out[1] = it.out[2] = it.out[3];
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 0461d8b63fd..8e4a7dafcd6 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -111,9 +111,6 @@ typedef enum eDepsObjectComponentType {
/* Parameters Component - Default when nothing else fits
* (i.e. just SDNA property setting). */
DEG_OB_COMP_PARAMETERS,
- /* Generic "Proxy-Inherit" Component.
- * TODO(sergey): Also for instancing of subgraphs? */
- DEG_OB_COMP_PROXY,
/* Animation Component.
*
* TODO(sergey): merge in with parameters? */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index b2e136c3d3b..990021dc29b 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -125,10 +125,6 @@ bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel
bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan)
{
- /* Proxies don't have BONE_SEGMENTS */
- if (ID_IS_LINKED(object) && object->proxy_from != nullptr) {
- return false;
- }
return check_pchan_has_bbone(object, pchan);
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 79d674e8415..ba1432d9ec8 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -599,7 +599,7 @@ void DepsgraphNodeBuilder::build_id(ID *id)
case ID_CU:
case ID_LT:
case ID_GD:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
build_object_data_geometry_datablock(id);
@@ -726,9 +726,6 @@ void DepsgraphNodeBuilder::build_object(int base_index,
eDepsNode_LinkedState_Type linked_state,
bool is_visible)
{
- if (object->proxy != nullptr) {
- object->proxy->proxy_from = object;
- }
const bool has_object = built_map_.checkIsBuiltAndTag(object);
/* When there is already object in the dependency graph accumulate visibility an linked state
@@ -819,9 +816,6 @@ void DepsgraphNodeBuilder::build_object(int base_index,
(object->pd->tex != nullptr)) {
build_texture(object->pd->tex);
}
- /* Proxy object to copy from. */
- build_object_proxy_from(object, is_visible);
- build_object_proxy_group(object, is_visible);
/* Object dupligroup. */
if (object->instance_collection != nullptr) {
build_object_instance_collection(object, is_visible);
@@ -875,22 +869,6 @@ void DepsgraphNodeBuilder::build_object_flags(int base_index,
});
}
-void DepsgraphNodeBuilder::build_object_proxy_from(Object *object, bool is_object_visible)
-{
- if (object->proxy_from == nullptr) {
- return;
- }
- build_object(-1, object->proxy_from, DEG_ID_LINKED_INDIRECTLY, is_object_visible);
-}
-
-void DepsgraphNodeBuilder::build_object_proxy_group(Object *object, bool is_object_visible)
-{
- if (object->proxy_group == nullptr) {
- return;
- }
- build_object(-1, object->proxy_group, DEG_ID_LINKED_INDIRECTLY, is_object_visible);
-}
-
void DepsgraphNodeBuilder::build_object_instance_collection(Object *object, bool is_object_visible)
{
if (object->instance_collection == nullptr) {
@@ -916,18 +894,13 @@ void DepsgraphNodeBuilder::build_object_data(Object *object)
case OB_MBALL:
case OB_LATTICE:
case OB_GPENCIL:
- case OB_HAIR:
+ case OB_CURVES:
case OB_POINTCLOUD:
case OB_VOLUME:
build_object_data_geometry(object);
break;
case OB_ARMATURE:
- if (ID_IS_LINKED(object) && object->proxy_from != nullptr) {
- build_proxy_rig(object);
- }
- else {
- build_rig(object);
- }
+ build_rig(object);
break;
case OB_LAMP:
build_object_data_light(object);
@@ -1183,12 +1156,6 @@ void DepsgraphNodeBuilder::build_driver_variables(ID *id, FCurve *fcurve)
}
build_id(dtar->id);
build_driver_id_property(dtar->id, dtar->rna_path);
- /* Corresponds to dtar_id_ensure_proxy_from(). */
- if ((GS(dtar->id->name) == ID_OB) && (((Object *)dtar->id)->proxy_from != nullptr)) {
- Object *proxy_from = ((Object *)dtar->id)->proxy_from;
- build_id(&proxy_from->id);
- build_driver_id_property(&proxy_from->id, dtar->rna_path);
- }
}
DRIVER_TARGETS_LOOPER_END;
}
@@ -1596,7 +1563,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
op_node->set_as_entry();
break;
}
- case ID_HA: {
+ case ID_CV: {
op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
op_node->set_as_entry();
break;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index a1db4aaf693..6e319383478 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -184,8 +184,6 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
Object *object,
eDepsNode_LinkedState_Type linked_state,
bool is_visible);
- virtual void build_object_proxy_from(Object *object, bool is_object_visible);
- virtual void build_object_proxy_group(Object *object, bool is_object_visible);
virtual void build_object_instance_collection(Object *object, bool is_object_visible);
virtual void build_object_from_layer(int base_index,
Object *object,
@@ -232,7 +230,6 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);
virtual void build_splineik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);
virtual void build_rig(Object *object);
- virtual void build_proxy_rig(Object *object);
virtual void build_armature(bArmature *armature);
virtual void build_armature_bones(ListBase *bones);
virtual void build_shapekeys(Key *key);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
index e8dda7b8de4..0e196450f60 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
@@ -306,72 +306,4 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
}
}
-void DepsgraphNodeBuilder::build_proxy_rig(Object *object)
-{
- bArmature *armature = (bArmature *)object->data;
- OperationNode *op_node;
- Object *object_cow = get_cow_datablock(object);
- /* Sanity check. */
- BLI_assert(object->pose != nullptr);
- /* Armature. */
- build_armature(armature);
- /* speed optimization for animation lookups */
- BKE_pose_channels_hash_ensure(object->pose);
- if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
- BKE_pose_update_constraint_flags(object->pose);
- }
- op_node = add_operation_node(
- &object->id,
- NodeType::EVAL_POSE,
- OperationCode::POSE_INIT,
- [object_cow](::Depsgraph *depsgraph) { BKE_pose_eval_proxy_init(depsgraph, object_cow); });
- op_node->set_as_entry();
-
- int pchan_index = 0;
- LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
- op_node = add_operation_node(
- &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL);
- op_node->set_as_entry();
- /* Bone is ready for solvers. */
- add_operation_node(&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_READY);
- /* Bone is fully evaluated. */
- op_node = add_operation_node(&object->id,
- NodeType::BONE,
- pchan->name,
- OperationCode::BONE_DONE,
- [object_cow, pchan_index](::Depsgraph *depsgraph) {
- BKE_pose_eval_proxy_copy_bone(
- depsgraph, object_cow, pchan_index);
- });
- op_node->set_as_exit();
-
- /* Custom properties. */
- if (pchan->prop != nullptr) {
- build_idproperties(pchan->prop);
- add_operation_node(
- &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan->name);
- }
-
- /* Custom shape. */
- if (pchan->custom != nullptr) {
- /* NOTE: The relation builder will ensure visibility of the custom shape object. */
- build_object(-1, pchan->custom, DEG_ID_LINKED_INDIRECTLY, false);
- }
-
- pchan_index++;
- }
- op_node = add_operation_node(&object->id,
- NodeType::EVAL_POSE,
- OperationCode::POSE_CLEANUP,
- [object_cow](::Depsgraph *depsgraph) {
- BKE_pose_eval_proxy_cleanup(depsgraph, object_cow);
- });
- op_node = add_operation_node(
- &object->id,
- NodeType::EVAL_POSE,
- OperationCode::POSE_DONE,
- [object_cow](::Depsgraph *depsgraph) { BKE_pose_eval_proxy_done(depsgraph, object_cow); });
- op_node->set_as_exit();
-}
-
} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index cb43ef685d4..26dd7bc1363 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -555,7 +555,7 @@ void DepsgraphRelationBuilder::build_id(ID *id)
case ID_MB:
case ID_CU:
case ID_LT:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
case ID_GD:
@@ -787,9 +787,6 @@ void DepsgraphRelationBuilder::build_object(Object *object)
(object->pd->tex != nullptr)) {
build_texture(object->pd->tex);
}
- /* Proxy object to copy from. */
- build_object_proxy_from(object);
- build_object_proxy_group(object);
/* Object dupligroup. */
if (object->instance_collection != nullptr) {
build_collection(nullptr, object, object->instance_collection);
@@ -804,31 +801,6 @@ void DepsgraphRelationBuilder::build_object(Object *object)
build_parameters(&object->id);
}
-void DepsgraphRelationBuilder::build_object_proxy_from(Object *object)
-{
- if (object->proxy_from == nullptr) {
- return;
- }
- /* Object is linked here (comes from the library). */
- build_object(object->proxy_from);
- ComponentKey ob_transform_key(&object->proxy_from->id, NodeType::TRANSFORM);
- ComponentKey proxy_transform_key(&object->id, NodeType::TRANSFORM);
- add_relation(ob_transform_key, proxy_transform_key, "Proxy Transform");
-}
-
-void DepsgraphRelationBuilder::build_object_proxy_group(Object *object)
-{
- if (ELEM(object->proxy_group, nullptr, object->proxy)) {
- return;
- }
- /* Object is local here (local in .blend file, users interacts with it). */
- build_object(object->proxy_group);
- OperationKey proxy_group_eval_key(
- &object->proxy_group->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
- OperationKey transform_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
- add_relation(proxy_group_eval_key, transform_eval_key, "Proxy Group Transform");
-}
-
void DepsgraphRelationBuilder::build_object_from_layer_relations(Object *object)
{
OperationKey object_from_layer_entry_key(
@@ -877,7 +849,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object)
case OB_MBALL:
case OB_LATTICE:
case OB_GPENCIL:
- case OB_HAIR:
+ case OB_CURVES:
case OB_POINTCLOUD:
case OB_VOLUME: {
build_object_data_geometry(object);
@@ -895,12 +867,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object)
break;
}
case OB_ARMATURE:
- if (ID_IS_LINKED(object) && object->proxy_from != nullptr) {
- build_proxy_rig(object);
- }
- else {
- build_rig(object);
- }
+ build_rig(object);
break;
case OB_LAMP:
build_object_data_light(object);
@@ -1663,18 +1630,9 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu)
}
build_id(target_id);
build_driver_id_property(target_id, dtar->rna_path);
- /* Look up the proxy - matches dtar_id_ensure_proxy_from during evaluation. */
Object *object = nullptr;
if (GS(target_id->name) == ID_OB) {
object = (Object *)target_id;
- if (object->proxy_from != nullptr) {
- /* Redirect the target to the proxy, like in evaluation. */
- object = object->proxy_from;
- target_id = &object->id;
- /* Prepare the redirected target. */
- build_id(target_id);
- build_driver_id_property(target_id, dtar->rna_path);
- }
}
/* Special handling for directly-named bones. */
if ((dtar->flag & DTAR_FLAG_STRUCT_REF) && (object && object->type == OB_ARMATURE) &&
@@ -2034,7 +1992,7 @@ void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part)
"Particle Texture -> Particle Reset",
RELATION_FLAG_FLUSH_USER_EDIT_ONLY);
add_relation(texture_key, particle_settings_eval_key, "Particle Texture -> Particle Eval");
- /* TODO(sergey): Consider moving texture space handling to an own
+ /* TODO(sergey): Consider moving texture space handling to its own
* function. */
if (mtex->texco == TEXCO_OBJECT && mtex->object != nullptr) {
ComponentKey object_key(&mtex->object->id, NodeType::TRANSFORM);
@@ -2343,7 +2301,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
}
break;
}
- case ID_HA:
+ case ID_CV:
break;
case ID_PT:
break;
@@ -2967,7 +2925,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node)
continue;
}
int rel_flag = (RELATION_FLAG_NO_FLUSH | RELATION_FLAG_GODMODE);
- if ((ELEM(id_type, ID_ME, ID_HA, ID_PT, ID_VO) && comp_node->type == NodeType::GEOMETRY) ||
+ if ((ELEM(id_type, ID_ME, ID_CV, ID_PT, ID_VO) && comp_node->type == NodeType::GEOMETRY) ||
(id_type == ID_CF && comp_node->type == NodeType::CACHE)) {
rel_flag &= ~RELATION_FLAG_NO_FLUSH;
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index 787e3799029..df315176a92 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -71,8 +71,8 @@ struct Scene;
struct Simulation;
struct Speaker;
struct Tex;
-struct ViewLayer;
struct VFont;
+struct ViewLayer;
struct World;
struct bAction;
struct bArmature;
@@ -216,8 +216,6 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
Object *object,
Collection *collection);
virtual void build_object(Object *object);
- virtual void build_object_proxy_from(Object *object);
- virtual void build_object_proxy_group(Object *object);
virtual void build_object_from_layer_relations(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
@@ -273,7 +271,6 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
const bPoseChannel *rootchan,
const RootPChanMap *root_map);
virtual void build_rig(Object *object);
- virtual void build_proxy_rig(Object *object);
virtual void build_shapekeys(Key *key);
virtual void build_armature(bArmature *armature);
virtual void build_armature_bones(ListBase *bones);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
index 856e37ee473..377ef9fc357 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
@@ -471,65 +471,4 @@ void DepsgraphRelationBuilder::build_rig(Object *object)
}
}
-void DepsgraphRelationBuilder::build_proxy_rig(Object *object)
-{
- bArmature *armature = (bArmature *)object->data;
- Object *proxy_from = object->proxy_from;
- build_armature(armature);
- OperationKey pose_init_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_INIT);
- OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE);
- OperationKey pose_cleanup_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_CLEANUP);
- LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
- build_idproperties(pchan->prop);
- OperationKey bone_local_key(
- &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL);
- OperationKey bone_ready_key(
- &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_READY);
- OperationKey bone_done_key(&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_DONE);
- OperationKey from_bone_done_key(
- &proxy_from->id, NodeType::BONE, pchan->name, OperationCode::BONE_DONE);
- add_relation(pose_init_key, bone_local_key, "Pose Init -> Bone Local");
- add_relation(bone_local_key, bone_ready_key, "Local -> Ready");
- add_relation(bone_ready_key, bone_done_key, "Ready -> Done");
- add_relation(bone_done_key, pose_cleanup_key, "Bone Done -> Pose Cleanup");
- add_relation(bone_done_key, pose_done_key, "Bone Done -> Pose Done", RELATION_FLAG_GODMODE);
- /* Make sure bone in the proxy is not done before its FROM is done. */
- if (check_pchan_has_bbone(object, pchan)) {
- OperationKey from_bone_segments_key(
- &proxy_from->id, NodeType::BONE, pchan->name, OperationCode::BONE_SEGMENTS);
- add_relation(from_bone_segments_key,
- bone_done_key,
- "Bone Segments -> Bone Done",
- RELATION_FLAG_GODMODE);
- }
- else {
- add_relation(from_bone_done_key, bone_done_key, "Bone Done -> Bone Done");
- }
-
- /* Parent relation: even though the proxy bone itself doesn't need
- * the parent bone, some users expect the parent to be ready if the
- * bone itself is (e.g. for computing the local space matrix).
- */
- if (pchan->parent != nullptr) {
- OperationKey parent_key(
- &object->id, NodeType::BONE, pchan->parent->name, OperationCode::BONE_DONE);
- add_relation(parent_key, bone_done_key, "Parent Bone -> Child Bone");
- }
-
- if (pchan->prop != nullptr) {
- OperationKey bone_parameters(
- &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, pchan->name);
- OperationKey from_bone_parameters(
- &proxy_from->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, pchan->name);
- add_relation(from_bone_parameters, bone_parameters, "Proxy Bone Parameters");
- }
-
- /* Custom shape. */
- if (pchan->custom != nullptr) {
- build_object(pchan->custom);
- add_visibility_relation(&pchan->custom->id, &armature->id);
- }
- }
-}
-
} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
index 9e62432ea54..b77b935b9f6 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
@@ -63,17 +63,6 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder {
return DepsgraphNodeBuilder::need_pull_base_into_graph(base);
}
- void build_object_proxy_group(Object *object, bool is_visible) override
- {
- if (object->proxy_group == nullptr) {
- return;
- }
- if (!filter_.contains(&object->proxy_group->id)) {
- return;
- }
- DepsgraphNodeBuilder::build_object_proxy_group(object, is_visible);
- }
-
protected:
DepsgraphFromIDsFilter filter_;
};
@@ -96,17 +85,6 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder {
return DepsgraphRelationBuilder::need_pull_base_into_graph(base);
}
- void build_object_proxy_group(Object *object) override
- {
- if (object->proxy_group == nullptr) {
- return;
- }
- if (!filter_.contains(&object->proxy_group->id)) {
- return;
- }
- DepsgraphRelationBuilder::build_object_proxy_group(object);
- }
-
protected:
DepsgraphFromIDsFilter filter_;
};
diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
index 79fcfc52446..8c89bbf1660 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
@@ -36,10 +36,7 @@ namespace deg {
* visibility and other flags assigned to the objects.
* All other bases (the ones which points to object which is outside of the set of IDs) are
* completely ignored.
- *
- * - Proxy groups pointing to objects which are outside of the IDs set are also ignored.
- * This way we avoid high-poly character body pulled into the dependency graph when it's coming
- * from a library into an animation file and the dependency graph constructed for a proxy rig. */
+ */
class FromIDsBuilderPipeline : public AbstractBuilderPipeline {
public:
diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
index 36120ae76d1..ef82584d671 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
+++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
@@ -99,7 +99,6 @@ static const int deg_debug_node_type_color_map[][2] = {
/* Outer Types */
{NodeType::PARAMETERS, 2},
- {NodeType::PROXY, 3},
{NodeType::ANIMATION, 4},
{NodeType::TRANSFORM, 5},
{NodeType::GEOMETRY, 6},
@@ -404,7 +403,6 @@ static void deg_debug_graphviz_node(DotExportContext &ctx,
case NodeType::PARAMETERS:
case NodeType::ANIMATION:
case NodeType::TRANSFORM:
- case NodeType::PROXY:
case NodeType::GEOMETRY:
case NodeType::SEQUENCER:
case NodeType::EVAL_POSE:
diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc
index 7952f27507f..ae94fbcfdb8 100644
--- a/source/blender/depsgraph/intern/depsgraph_eval.cc
+++ b/source/blender/depsgraph/intern/depsgraph_eval.cc
@@ -72,6 +72,14 @@ void DEG_evaluate_on_refresh(Depsgraph *graph)
deg_graph->frame = frame;
deg_graph->ctime = ctime;
}
+ else if (scene->id.recalc & ID_RECALC_FRAME_CHANGE) {
+ /* Comparing depsgraph & scene frame fails in the case of undo,
+ * since the undo state is stored before updates from the frame change have been applied.
+ * In this case reading back the undo state will behave as if no updates on frame change
+ * is needed as the #Depsgraph.ctime & frame will match the values in the input scene.
+ * Use #ID_RECALC_FRAME_CHANGE to detect that recalculation is necessary. see: T66913. */
+ deg_graph->tag_time_source();
+ }
deg_flush_updates_and_refresh(deg_graph);
}
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 4bc9e0d2d14..b9ec01d729c 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -87,7 +87,7 @@ void depsgraph_geometry_tag_to_component(const ID *id, NodeType *component_type)
bool is_selectable_data_id_type(const ID_Type id_type)
{
- return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD, ID_HA, ID_PT, ID_VO);
+ return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD, ID_CV, ID_PT, ID_VO);
}
void depsgraph_select_tag_to_component_opcode(const ID *id,
@@ -211,7 +211,7 @@ void depsgraph_tag_to_component_opcode(const ID *id,
case ID_RECALC_SEQUENCER_STRIPS:
*component_type = NodeType::SEQUENCER;
break;
- case ID_RECALC_AUDIO_SEEK:
+ case ID_RECALC_FRAME_CHANGE:
case ID_RECALC_AUDIO_FPS:
case ID_RECALC_AUDIO_VOLUME:
case ID_RECALC_AUDIO_MUTE:
@@ -284,6 +284,7 @@ void depsgraph_tag_component(Depsgraph *graph,
* here. */
if (component_node == nullptr) {
if (component_type == NodeType::ANIMATION) {
+ id_node->is_cow_explicitly_tagged = true;
depsgraph_id_tag_copy_on_write(graph, id_node, update_source);
}
return;
@@ -301,6 +302,9 @@ void depsgraph_tag_component(Depsgraph *graph,
if (component_node->need_tag_cow_before_update()) {
depsgraph_id_tag_copy_on_write(graph, id_node, update_source);
}
+ if (component_type == NodeType::COPY_ON_WRITE) {
+ id_node->is_cow_explicitly_tagged = true;
+ }
}
/* This is a tag compatibility with legacy code.
@@ -522,12 +526,6 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph)
* this. */
for (deg::IDNode *id_node : graph->id_nodes) {
const ID_Type id_type = GS(id_node->id_orig->name);
- if (id_type == ID_OB) {
- Object *object_orig = reinterpret_cast<Object *>(id_node->id_orig);
- if (object_orig->proxy != nullptr) {
- object_orig->proxy->proxy_from = object_orig;
- }
- }
if (!id_node->visible_components_mask) {
/* ID has no components which affects anything visible.
@@ -593,7 +591,7 @@ NodeType geometry_tag_to_component(const ID *id)
case OB_LATTICE:
case OB_MBALL:
case OB_GPENCIL:
- case OB_HAIR:
+ case OB_CURVES:
case OB_POINTCLOUD:
case OB_VOLUME:
return NodeType::GEOMETRY;
@@ -607,7 +605,7 @@ NodeType geometry_tag_to_component(const ID *id)
case ID_CU:
case ID_LT:
case ID_MB:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
case ID_GR:
@@ -733,8 +731,8 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag)
return "EDITORS";
case ID_RECALC_SEQUENCER_STRIPS:
return "SEQUENCER_STRIPS";
- case ID_RECALC_AUDIO_SEEK:
- return "AUDIO_SEEK";
+ case ID_RECALC_FRAME_CHANGE:
+ return "FRAME_CHANGE";
case ID_RECALC_AUDIO_FPS:
return "AUDIO_FPS";
case ID_RECALC_AUDIO_VOLUME:
@@ -888,6 +886,7 @@ void DEG_ids_clear_recalc(Depsgraph *depsgraph, const bool backup)
* correctly when there are multiple depsgraph with others still using
* the recalc flag. */
id_node->is_user_modified = false;
+ id_node->is_cow_explicitly_tagged = false;
deg_graph_clear_id_recalc_flags(id_node->id_cow);
if (deg_graph->is_active) {
deg_graph_clear_id_recalc_flags(id_node->id_orig);
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
index b1204c9366c..b64f94568f6 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
@@ -646,8 +646,8 @@ void set_particle_system_modifiers_loaded(Object *object_cow)
void reset_particle_system_edit_eval(const Depsgraph *depsgraph, Object *object_cow)
{
- /* Inactive (and render) dependency graphs are living in own little bubble, should not care about
- * edit mode at all. */
+ /* Inactive (and render) dependency graphs are living in their own little bubble, should not care
+ * about edit mode at all. */
if (!DEG_is_active(reinterpret_cast<const ::Depsgraph *>(depsgraph))) {
return;
}
@@ -674,12 +674,6 @@ void update_pose_orig_pointers(const bPose *pose_orig, bPose *pose_cow)
update_list_orig_pointers(&pose_orig->chanbase, &pose_cow->chanbase, &bPoseChannel::orig_pchan);
}
-void update_modifiers_orig_pointers(const Object *object_orig, Object *object_cow)
-{
- update_list_orig_pointers(
- &object_orig->modifiers, &object_cow->modifiers, &ModifierData::orig_modifier_data);
-}
-
void update_nla_strips_orig_pointers(const ListBase *strips_orig, ListBase *strips_cow)
{
NlaStrip *strip_orig = reinterpret_cast<NlaStrip *>(strips_orig->first);
@@ -714,25 +708,6 @@ void update_animation_data_after_copy(const ID *id_orig, ID *id_cow)
update_nla_tracks_orig_pointers(&anim_data_orig->nla_tracks, &anim_data_cow->nla_tracks);
}
-/* Some builders (like motion path one) will ignore proxies from being built. This code makes it so
- * proxy and proxy_group pointers never point to an original objects, preventing evaluation code
- * from assign evaluated pointer to an original proxy->proxy_from. */
-void update_proxy_pointers_after_copy(const Depsgraph *depsgraph,
- const Object *object_orig,
- Object *object_cow)
-{
- if (object_cow->proxy != nullptr) {
- if (!deg_check_id_in_depsgraph(depsgraph, &object_orig->proxy->id)) {
- object_cow->proxy = nullptr;
- }
- }
- if (object_cow->proxy_group != nullptr) {
- if (!deg_check_id_in_depsgraph(depsgraph, &object_orig->proxy_group->id)) {
- object_cow->proxy_group = nullptr;
- }
- }
-}
-
/* Do some special treatment of data transfer from original ID to its
* CoW complementary part.
*
@@ -766,8 +741,6 @@ void update_id_after_copy(const Depsgraph *depsgraph,
BKE_gpencil_update_orig_pointers(object_orig, object_cow);
}
update_particles_after_copy(depsgraph, object_orig, object_cow);
- update_modifiers_orig_pointers(object_orig, object_cow);
- update_proxy_pointers_after_copy(depsgraph, object_orig, object_cow);
break;
}
case ID_SCE: {
@@ -898,6 +871,29 @@ ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode
if (!deg_copy_on_write_is_needed(id_orig)) {
return id_cow;
}
+
+ /* When updating object data in edit-mode, don't request COW update since this will duplicate
+ * all object data which is unnecessary when the edit-mode data is used for calculating
+ * modifiers.
+ *
+ * TODO: Investigate modes besides edit-mode. */
+ if (check_datablock_expanded(id_cow) && !id_node->is_cow_explicitly_tagged) {
+ const ID_Type id_type = GS(id_orig->name);
+ if (OB_DATA_SUPPORT_EDITMODE(id_type) && BKE_object_data_is_in_editmode(id_orig)) {
+ /* Make sure pointers in the edit mode data are updated in the copy.
+ * This allows depsgraph to pick up changes made in another context after it has been
+ * evaluated. Consider the following scenario:
+ *
+ * - ObjectA in SceneA is using Mesh.
+ * - ObjectB in SceneB is using Mesh (same exact datablock).
+ * - Depsgraph of SceneA is evaluated.
+ * - Depsgraph of SceneB is evaluated.
+ * - User enters edit mode of ObjectA in SceneA. */
+ update_edit_mode_pointers(depsgraph, id_orig, id_cow);
+ return id_cow;
+ }
+ }
+
RuntimeBackup backup(depsgraph);
backup.init_from_id(id_cow);
deg_free_copy_on_write_datablock(id_cow);
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
index bc023766a46..728f631f789 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
@@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 20137Blender Foundation.
+ * The Original Code is Copyright (C) 2017 Blender Foundation.
* All rights reserved.
*/
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
index 1081528ece1..10507948246 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
@@ -71,7 +71,6 @@ void ObjectRuntimeBackup::backup_modifier_runtime_data(Object *object)
const SessionUUID &session_uuid = modifier_data->session_uuid;
BLI_assert(BLI_session_uuid_is_generated(&session_uuid));
- BLI_assert(modifier_data->orig_modifier_data != nullptr);
modifier_runtime_data.add(session_uuid, ModifierDataBackup(modifier_data));
modifier_data->runtime = nullptr;
}
@@ -128,7 +127,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object)
}
}
}
- else if (ELEM(object->type, OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) {
+ else if (ELEM(object->type, OB_CURVES, OB_POINTCLOUD, OB_VOLUME)) {
if (object->id.recalc & ID_RECALC_GEOMETRY) {
/* Free evaluated caches. */
object->data = data_orig;
@@ -150,8 +149,9 @@ void ObjectRuntimeBackup::restore_to_object(Object *object)
void ObjectRuntimeBackup::restore_modifier_runtime_data(Object *object)
{
LISTBASE_FOREACH (ModifierData *, modifier_data, &object->modifiers) {
- BLI_assert(modifier_data->orig_modifier_data != nullptr);
const SessionUUID &session_uuid = modifier_data->session_uuid;
+ BLI_assert(BLI_session_uuid_is_generated(&session_uuid));
+
optional<ModifierDataBackup> backup = modifier_runtime_data.pop_try(session_uuid);
if (backup.has_value()) {
modifier_data->runtime = backup->runtime;
diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc
index 075bfd35ec1..1eb76d76c22 100644
--- a/source/blender/depsgraph/intern/node/deg_node.cc
+++ b/source/blender/depsgraph/intern/node/deg_node.cc
@@ -67,8 +67,6 @@ const char *nodeTypeAsString(NodeType type)
/* **** Outer Types **** */
case NodeType::PARAMETERS:
return "PARAMETERS";
- case NodeType::PROXY:
- return "PROXY";
case NodeType::ANIMATION:
return "ANIMATION";
case NodeType::TRANSFORM:
@@ -174,7 +172,6 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type)
case NodeType::BONE:
case NodeType::SHADING:
case NodeType::CACHE:
- case NodeType::PROXY:
case NodeType::SIMULATION:
case NodeType::NTREE_OUTPUT:
return DEG_SCENE_COMP_PARAMETERS;
@@ -194,8 +191,6 @@ NodeType nodeTypeFromObjectComponent(eDepsObjectComponentType component_type)
return NodeType::UNDEFINED;
case DEG_OB_COMP_PARAMETERS:
return NodeType::PARAMETERS;
- case DEG_OB_COMP_PROXY:
- return NodeType::PROXY;
case DEG_OB_COMP_ANIMATION:
return NodeType::ANIMATION;
case DEG_OB_COMP_TRANSFORM:
@@ -219,8 +214,6 @@ eDepsObjectComponentType nodeTypeToObjectComponent(NodeType type)
switch (type) {
case NodeType::PARAMETERS:
return DEG_OB_COMP_PARAMETERS;
- case NodeType::PROXY:
- return DEG_OB_COMP_PROXY;
case NodeType::ANIMATION:
return DEG_OB_COMP_ANIMATION;
case NodeType::TRANSFORM:
diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h
index 25bbb9a7237..72d3e375bcd 100644
--- a/source/blender/depsgraph/intern/node/deg_node.h
+++ b/source/blender/depsgraph/intern/node/deg_node.h
@@ -77,8 +77,6 @@ enum class NodeType {
/* Parameters Component - Default when nothing else fits
* (i.e. just SDNA property setting). */
PARAMETERS,
- /* Generic "Proxy-Inherit" Component. */
- PROXY,
/* Animation Component */
ANIMATION,
/* Transform Component (Parenting/Constraints) */
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc
index 4c430904e44..e14f758c739 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_component.cc
@@ -339,7 +339,6 @@ DEG_COMPONENT_NODE_DEFINE(Particles, PARTICLE_SYSTEM, ID_RECALC_GEOMETRY);
DEG_COMPONENT_NODE_DEFINE(ParticleSettings, PARTICLE_SETTINGS, 0);
DEG_COMPONENT_NODE_DEFINE(PointCache, POINT_CACHE, 0);
DEG_COMPONENT_NODE_DEFINE(Pose, EVAL_POSE, ID_RECALC_GEOMETRY);
-DEG_COMPONENT_NODE_DEFINE(Proxy, PROXY, ID_RECALC_GEOMETRY);
DEG_COMPONENT_NODE_DEFINE(Sequencer, SEQUENCER, 0);
DEG_COMPONENT_NODE_DEFINE(Shading, SHADING, ID_RECALC_SHADING);
DEG_COMPONENT_NODE_DEFINE(Transform, TRANSFORM, ID_RECALC_TRANSFORM);
@@ -373,7 +372,6 @@ void deg_register_component_depsnodes()
register_node_typeinfo(&DNTI_PARTICLE_SETTINGS);
register_node_typeinfo(&DNTI_POINT_CACHE);
register_node_typeinfo(&DNTI_IMAGE_ANIMATION);
- register_node_typeinfo(&DNTI_PROXY);
register_node_typeinfo(&DNTI_EVAL_POSE);
register_node_typeinfo(&DNTI_SEQUENCER);
register_node_typeinfo(&DNTI_SHADING);
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h
index 8fd28dfc497..d45d4642937 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.h
+++ b/source/blender/depsgraph/intern/node/deg_node_component.h
@@ -163,23 +163,6 @@ struct ComponentNode : public Node {
DEG_COMPONENT_NODE_DECLARE; \
}
-/* When updating object data in edit-mode, don't request COW update since this will duplicate
- * all object data which is unnecessary when the edit-mode data is used for calculating modifiers.
- *
- * TODO: Investigate modes besides edit-mode. */
-#define DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_OBDATA_IN_EDIT_MODE(name) \
- struct name##ComponentNode : public ComponentNode { \
- DEG_COMPONENT_NODE_DECLARE; \
- virtual bool need_tag_cow_before_update() override \
- { \
- if (OB_DATA_SUPPORT_EDITMODE(owner->id_type) && \
- BKE_object_data_is_in_editmode(owner->id_orig)) { \
- return false; \
- } \
- return true; \
- } \
- }
-
#define DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(name) \
struct name##ComponentNode : public ComponentNode { \
DEG_COMPONENT_NODE_DECLARE; \
@@ -202,14 +185,13 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(Animation);
DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(BatchCache);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Cache);
DEG_COMPONENT_NODE_DECLARE_GENERIC(CopyOnWrite);
-DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_OBDATA_IN_EDIT_MODE(Geometry);
+DEG_COMPONENT_NODE_DECLARE_GENERIC(Geometry);
DEG_COMPONENT_NODE_DECLARE_GENERIC(ImageAnimation);
DEG_COMPONENT_NODE_DECLARE_GENERIC(LayerCollections);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Particles);
DEG_COMPONENT_NODE_DECLARE_GENERIC(ParticleSettings);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Pose);
DEG_COMPONENT_NODE_DECLARE_GENERIC(PointCache);
-DEG_COMPONENT_NODE_DECLARE_GENERIC(Proxy);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Sequencer);
DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(Shading);
DEG_COMPONENT_NODE_DECLARE_GENERIC(ShadingParameters);
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h
index 257e42b8e67..f18c44b6332 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.h
+++ b/source/blender/depsgraph/intern/node/deg_node_id.h
@@ -121,6 +121,9 @@ struct IDNode : public Node {
/* Accumulated flag from operation. Is initialized and used during updates flush. */
bool is_user_modified;
+ /* Copy-on-Write component has been explicitly tagged for update. */
+ bool is_cow_explicitly_tagged;
+
/* Accumulate recalc flags from multiple update passes. */
int id_cow_recalc_backup;
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 8dd7e3d7dbf..17feb39a072 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -45,6 +45,7 @@ set(INC
../../../intern/glew-mx
../../../intern/guardedalloc
../../../intern/opensubdiv
+ ../../../intern/clog
# dna_type_offsets.h
${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern
@@ -85,9 +86,9 @@ set(SRC
intern/mesh_extractors/extract_mesh_vbo_vcol.cc
intern/mesh_extractors/extract_mesh_vbo_weights.cc
intern/draw_cache_impl_curve.cc
+ intern/draw_cache_impl_curves.cc
intern/draw_cache_impl_displist.c
intern/draw_cache_impl_gpencil.c
- intern/draw_cache_impl_hair.cc
intern/draw_cache_impl_lattice.c
intern/draw_cache_impl_mesh.c
intern/draw_cache_impl_metaball.c
@@ -154,7 +155,7 @@ set(SRC
engines/workbench/workbench_materials.c
engines/workbench/workbench_opaque.c
engines/workbench/workbench_render.c
- engines/workbench/workbench_shader.c
+ engines/workbench/workbench_shader.cc
engines/workbench/workbench_shadow.c
engines/workbench/workbench_transparent.c
engines/workbench/workbench_volume.c
@@ -212,8 +213,8 @@ set(SRC
intern/draw_manager_profiling.h
intern/draw_manager_testing.h
intern/draw_manager_text.h
- intern/draw_shader_shared.h
intern/draw_shader.h
+ intern/draw_shader_shared.h
intern/draw_subdivision.h
intern/draw_texture_pool.h
intern/draw_view.h
@@ -227,11 +228,17 @@ set(SRC
engines/eevee/eevee_lut.h
engines/eevee/eevee_private.h
engines/external/external_engine.h
- engines/image/image_drawing_mode_image_space.hh
+ engines/image/image_batches.hh
+ engines/image/image_drawing_mode.hh
engines/image/image_engine.h
+ engines/image/image_instance_data.hh
+ engines/image/image_partial_updater.hh
engines/image/image_private.hh
+ engines/image/image_shader_params.hh
engines/image/image_space_image.hh
engines/image/image_space_node.hh
+ engines/image/image_space.hh
+ engines/image/image_wrappers.hh
engines/workbench/workbench_engine.h
engines/workbench/workbench_private.h
engines/workbench/workbench_shader_shared.h
@@ -342,7 +349,6 @@ set(GLSL_SRC
engines/workbench/shaders/workbench_common_lib.glsl
engines/workbench/shaders/workbench_composite_frag.glsl
engines/workbench/shaders/workbench_curvature_lib.glsl
- engines/workbench/shaders/workbench_data_lib.glsl
engines/workbench/shaders/workbench_effect_cavity_frag.glsl
engines/workbench/shaders/workbench_effect_dof_frag.glsl
engines/workbench/shaders/workbench_effect_outline_frag.glsl
@@ -357,7 +363,6 @@ set(GLSL_SRC
engines/workbench/shaders/workbench_prepass_hair_vert.glsl
engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl
engines/workbench/shaders/workbench_prepass_vert.glsl
- engines/workbench/shaders/workbench_shader_interface_lib.glsl
engines/workbench/shaders/workbench_shadow_caps_geom.glsl
engines/workbench/shaders/workbench_shadow_debug_frag.glsl
engines/workbench/shaders/workbench_shadow_geom.glsl
@@ -378,6 +383,7 @@ set(GLSL_SRC
intern/shaders/common_hair_refine_comp.glsl
intern/shaders/common_math_lib.glsl
intern/shaders/common_math_geom_lib.glsl
+ intern/shaders/common_view_clipping_lib.glsl
intern/shaders/common_view_lib.glsl
intern/shaders/common_fxaa_lib.glsl
intern/shaders/common_smaa_lib.glsl
@@ -513,8 +519,10 @@ set(GLSL_SRC
engines/overlay/shaders/wireframe_frag.glsl
engines/overlay/shaders/xray_fade_frag.glsl
- engines/image/shaders/engine_image_frag.glsl
- engines/image/shaders/engine_image_vert.glsl
+ engines/image/shaders/image_engine_color_frag.glsl
+ engines/image/shaders/image_engine_color_vert.glsl
+ engines/image/shaders/image_engine_depth_frag.glsl
+ engines/image/shaders/image_engine_depth_vert.glsl
)
set(GLSL_C)
@@ -532,7 +540,7 @@ set(GLSL_SOURCE_CONTENT "")
foreach(GLSL_FILE ${GLSL_SRC})
get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME)
string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME})
- string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\"\)\n")
+ string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n")
endforeach()
set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_draw_source_list.h")
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 80207523a65..8f02e96b168 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -56,7 +56,7 @@
#include "BLI_math_bits.h"
#include "BLI_rect.h"
-#include "DNA_hair_types.h"
+#include "DNA_curves_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
@@ -248,25 +248,25 @@ static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedat
return grp;
}
-static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- ParticleSystem *psys,
- ModifierData *md,
- Material *material)
+static void eevee_cryptomatte_curves_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob,
+ ParticleSystem *psys,
+ ModifierData *md,
+ Material *material)
{
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, true);
DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
-void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob)
+void EEVEE_cryptomatte_object_curves_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob)
{
- BLI_assert(ob->type == OB_HAIR);
- Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR);
- eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material);
+ BLI_assert(ob->type == OB_CURVES);
+ Material *material = BKE_object_material_get_eval(ob, CURVES_MATERIAL_NR);
+ eevee_cryptomatte_curves_cache_populate(vedata, sldata, ob, NULL, NULL, material);
}
void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
@@ -291,7 +291,7 @@ void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
continue;
}
Material *material = BKE_object_material_get_eval(ob, part->omat);
- eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material);
+ eevee_cryptomatte_curves_cache_populate(vedata, sldata, ob, psys, md, material);
}
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index b453df284ed..64553acd228 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -177,7 +177,7 @@ static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, b
if (hair) {
EEVEE_HairMotionData *hair_step;
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
- int psys_len = (ob->type != OB_HAIR) ? BLI_listbase_count(&ob->modifiers) : 1;
+ int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1;
hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len,
__func__);
hair_step->psys_len = psys_len;
diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
index 4748323d6a7..d0c0635a1fb 100644
--- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c
+++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
@@ -251,7 +251,7 @@ int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata),
effects->dof_coc_params[1] = -aperture *
fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
- /* FIXME(fclem) This is broken for vertically fit sensor. */
+ /* FIXME(@fclem): This is broken for vertically fit sensor. */
effects->dof_coc_params[1] *= viewport_size[0] / sensor_scaled;
if ((scene_eval->eevee.flag & SCE_EEVEE_DOF_JITTER) != 0) {
@@ -625,7 +625,7 @@ static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl,
}
if (txl->dof_reduced_color) {
- /* TODO(fclem) In the future, we need to check if mip_count did not change.
+ /* TODO(@fclem): In the future, we need to check if mip_count did not change.
* For now it's ok as we always define all mip level. */
if (res[0] != GPU_texture_width(txl->dof_reduced_color) ||
res[1] != GPU_texture_width(txl->dof_reduced_color)) {
@@ -642,7 +642,8 @@ static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl,
txl->dof_reduced_coc = GPU_texture_create_2d(
"dof_reduced_coc", UNPACK2(res), mip_count, GPU_R16F, NULL);
- /* TODO(fclem) Remove once we have immutable storage or when mips are generated on creation. */
+ /* TODO(@fclem): Remove once we have immutable storage or when mips are generated on creation.
+ */
GPU_texture_generate_mipmap(txl->dof_reduced_color);
GPU_texture_generate_mipmap(txl->dof_reduced_coc);
}
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index fc9b8b0cde4..9b6b5c5f08d 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -125,7 +125,7 @@ void EEVEE_cache_populate(void *vedata, Object *ob)
if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
}
- else if (ob->type == OB_HAIR) {
+ else if (ob->type == OB_CURVES) {
EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_VOLUME) {
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c
index c51fc18a406..c7a8d2cc001 100644
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c
@@ -1077,8 +1077,8 @@ void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata,
pinfo->intensity_fac = intensity;
- /* find cell position on the virtual 3D texture */
- /* NOTE : Keep in sync with load_irradiance_cell() */
+ /* Find cell position on the virtual 3D texture. */
+ /* NOTE: Keep in sync with `load_irradiance_cell()`. */
#if defined(IRRADIANCE_SH_L2)
int size[2] = {3, 3};
#elif defined(IRRADIANCE_HL2)
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index a027a29c813..b8586ee0f68 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -33,7 +33,7 @@
#include "BKE_paint.h"
#include "BKE_particle.h"
-#include "DNA_hair_types.h"
+#include "DNA_curves_types.h"
#include "DNA_modifier_types.h"
#include "DNA_view3d_types.h"
#include "DNA_world_types.h"
@@ -925,7 +925,7 @@ void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
Object *ob,
bool *cast_shadow)
{
- eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, HAIR_MATERIAL_NR, cast_shadow);
+ eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, CURVES_MATERIAL_NR, cast_shadow);
}
void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 996716d6def..883d2eff852 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -176,7 +176,7 @@ enum {
VAR_MAT_MESH = (1 << 0),
VAR_MAT_VOLUME = (1 << 1),
VAR_MAT_HAIR = (1 << 2),
- /* VAR_MAT_PROBE = (1 << 3), UNUSED */
+ VAR_MAT_POINTCLOUD = (1 << 3),
VAR_MAT_BLEND = (1 << 4),
VAR_MAT_LOOKDEV = (1 << 5),
VAR_MAT_HOLDOUT = (1 << 6),
@@ -1385,9 +1385,9 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s
void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob);
-void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob);
+void EEVEE_cryptomatte_object_curves_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob);
void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
/**
* Register the render passes needed for cryptomatte
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 9e7a67060da..2fd033b6745 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -244,10 +244,10 @@ void EEVEE_render_cache(void *vedata,
EEVEE_cryptomatte_cache_populate(data, sldata, ob);
}
}
- else if (ob->type == OB_HAIR) {
+ else if (ob->type == OB_CURVES) {
EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
if (do_cryptomatte) {
- EEVEE_cryptomatte_object_hair_cache_populate(data, sldata, ob);
+ EEVEE_cryptomatte_object_curves_cache_populate(data, sldata, ob);
}
}
else if (ob->type == OB_VOLUME) {
@@ -517,8 +517,7 @@ static void eevee_render_draw_background(EEVEE_Data *vedata)
EEVEE_PassList *psl = vedata->psl;
/* Prevent background to write to data buffers.
- * NOTE : This also make sure the textures are bound
- * to the right double buffer. */
+ * NOTE: This also make sure the textures are bound to the right double buffer. */
GPU_framebuffer_ensure_config(&fbl->main_fb,
{GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
index adede7676d5..c4108969c99 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ b/source/blender/draw/engines/eevee/eevee_shaders.c
@@ -887,10 +887,7 @@ struct GPUShader *EEVEE_shaders_volumes_integration_sh_get()
datatoc_volumetric_geom_glsl,
datatoc_volumetric_integration_frag_glsl,
e_data.lib,
- USE_VOLUME_OPTI ? "#extension GL_ARB_shader_image_load_store: enable\n"
- "#extension GL_ARB_shading_language_420pack: enable\n"
- "#define USE_VOLUME_OPTI\n" SHADER_DEFINES :
- SHADER_DEFINES);
+ USE_VOLUME_OPTI ? "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : SHADER_DEFINES);
}
return e_data.volumetric_integration_sh;
}
@@ -1357,6 +1354,9 @@ static char *eevee_get_defines(int options)
if ((options & VAR_MAT_HAIR) != 0) {
BLI_dynstr_append(ds, "#define HAIR_SHADER\n");
}
+ if ((options & VAR_MAT_POINTCLOUD) != 0) {
+ BLI_dynstr_append(ds, "#define POINTCLOUD_SHADER\n");
+ }
if ((options & VAR_WORLD_PROBE) != 0) {
BLI_dynstr_append(ds, "#define PROBE_CAPTURE\n");
}
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c
index 48c24d138e6..81d9d560320 100644
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.c
@@ -49,7 +49,7 @@ void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
if (effects->enabled_effects & EFFECT_SSS) {
- /* NOTE : we need another stencil because the stencil buffer is on the same texture
+ /* NOTE: we need another stencil because the stencil buffer is on the same texture
* as the depth buffer we are sampling from. This could be avoided if the stencil is
* a separate texture but that needs OpenGL 4.4 or ARB_texture_stencil8.
* OR OpenGL 4.3 / ARB_ES3_compatibility if using a render-buffer instead. */
diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
index 1061b2f91a2..1c7ef775ac2 100644
--- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
@@ -56,7 +56,7 @@ vec2 get_ao_noise(void)
{
vec2 noise = texelfetch_noise_tex(gl_FragCoord.xy).xy;
/* Decorrelate noise from AA. */
- /* TODO(fclem) we should use a more general approach for more random number dimensions. */
+ /* TODO(@fclem): we should use a more general approach for more random number dimensions. */
noise = fract(noise * 6.1803402007);
return noise;
}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
index 4f9791ac95f..5bf20fe6979 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
@@ -43,7 +43,7 @@ void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in,
inout ClosureOutputDiffuse cl_out)
{
float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
- /* TODO(fclem) We could try to shadow lights that are shadowless with the ambient_occlusion
+ /* TODO(@fclem): We could try to shadow lights that are shadowless with the ambient_occlusion
* factor here. */
cl_out.radiance += light.data.l_color *
(light.data.l_diff * light.vis * light.contact_shadow * radiance);
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
index 00d265a48b0..584aacc9e19 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
@@ -63,7 +63,7 @@ ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in,
/* The brdf split sum LUT is applied after the radiance accumulation.
* Correct the LTC so that its energy is constant. */
- /* TODO(fclem) Optimize this so that only one scale factor is stored. */
+ /* TODO(@fclem): Optimize this so that only one scale factor is stored. */
vec4 ltc_brdf = texture(utilTex, vec3(lut_uv, LTC_BRDF_LAYER)).barg;
vec2 split_sum_brdf = ltc_brdf.zw;
cl_eval.ltc_brdf_scale = (ltc_brdf.x + ltc_brdf.y) / (split_sum_brdf.x + split_sum_brdf.y);
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl
index db9ae0f7034..f5c45d147e6 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl
@@ -52,7 +52,7 @@ const float unit_ring_radius = 1.0 / float(gather_ring_count);
const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5);
const float large_kernel_radius = 0.5 + float(gather_ring_count);
const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring);
-/* NOTE(fclem) the bias is reducing issues with density change visible transition. */
+/* NOTE(@fclem): the bias is reducing issues with density change visible transition. */
const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius;
const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1);
const float coc_radius_error = 2.0;
@@ -83,7 +83,7 @@ void dof_gather_init(float base_radius,
#endif
center_co = gl_FragCoord.xy + jitter_ofs * base_radius * unit_sample_radius;
- /* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving
+ /* TODO(@fclem): Seems like the default lod selection is too big. Bias to avoid blocky moving
* out of focus shapes. */
const float lod_bias = -2.0;
lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0);
@@ -111,7 +111,7 @@ void dof_gather_accumulator(float base_radius,
* a ring. So we need to compensate for fast gather that does not check CoC intersection. */
base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius;
}
- /* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */
+ /* TODO(@fclem): another seed? For now Cranly-Partterson rotation with golden ratio. */
noise.x = fract(noise.x + 0.61803398875);
float lod, isect_mul;
@@ -172,7 +172,7 @@ void dof_gather_accumulator(float base_radius,
}
#ifdef DOF_FOREGROUND_PASS /* Reduce issue with closer foreground over distant foreground. */
- /* TODO(fclem) this seems to not be completely correct as the issue remains. */
+ /* TODO(@fclem): This seems to not be completely correct as the issue remains. */
float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) -
sqr(float(ring) - 0.5 + coc_radius_error)) *
sqr(base_radius * unit_sample_radius);
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
index e5b68637563..e288e1a55ea 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
@@ -66,7 +66,7 @@ float dof_hdr_color_weight(vec4 color)
{
/* From UE4. Very fast "luma" weighting. */
float luma = (color.g * 2.0) + (color.r + color.b);
- /* TODO(fclem) Pass correct exposure. */
+ /* TODO(@fclem): Pass correct exposure. */
const float exposure = 1.0;
return 1.0 / (luma * exposure + 4.0);
}
@@ -92,7 +92,7 @@ vec4 dof_downsample_bilateral_coc_weights(vec4 cocs)
{
float chosen_coc = dof_coc_select(cocs);
- const float scale = 4.0; /* TODO(fclem) revisit. */
+ const float scale = 4.0; /* TODO(@fclem): revisit. */
/* NOTE: The difference between the cocs should be inside a abs() function,
* but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */
return saturate(1.0 - (chosen_coc - cocs) * scale);
@@ -373,7 +373,7 @@ void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2],
#if 0
const float mirroring_threshold = -layer_threshold - layer_offset;
- /* TODO(fclem) Promote to parameter? dither with Noise? */
+ /* TODO(@fclem): Promote to parameter? dither with Noise? */
const float mirroring_min_distance = 15.0;
if (pair_data[0].coc < mirroring_threshold &&
(pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) {
@@ -487,7 +487,8 @@ void dof_gather_accumulate_sample_ring(DofGatherData ring_data,
}
}
-/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for slightfocus gather. */
+/* FIXME(@fclem): Seems to be wrong since it needs `ringcount + 1` as input for slightfocus gather.
+ */
int dof_gather_total_sample_count(const int ring_count, const int ring_density)
{
return (ring_count * ring_count - ring_count) * ring_density + 1;
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl
index f349806d37e..59564890d7e 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl
@@ -25,7 +25,8 @@ flat out float spritesize;
/* Load 4 Circle of confusion values. texel_co is centered around the 4 taps. */
vec4 fetch_cocs(vec2 texel_co)
{
- /* TODO(fclem) The textureGather(sampler, co, comp) variant isn't here on some implementations.
+ /* TODO(@fclem): The `textureGather(sampler, co, comp)` variant isn't here on some
+ * implementations.
*/
#if 0 // GPU_ARB_texture_gather
vec2 uvs = texel_co / vec2(textureSize(cocBuffer, 0));
diff --git a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
index 70f1e9f1e66..85f8a12aa88 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
@@ -45,7 +45,7 @@ vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float dept
}
}
-/* TODO(fclem) port to a common place for other effects to use. */
+/* TODO(@fclem): port to a common place for other effects to use. */
bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg)
{
vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y)));
diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl
index 2f1efd588f7..f4ff28eaee4 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl
@@ -31,7 +31,7 @@ void main()
{
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
/* Decorrelate from AA. */
- /* TODO(fclem) we should use a more general approach for more random number dimensions. */
+ /* TODO(@fclem): we should use a more general approach for more random number dimensions. */
vec2 random_px = floor(fract(rand.xy * 2.2074408460575947536) * 1.99999) - 0.5;
rand.xy = fract(rand.xy * 3.2471795724474602596);
diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
index ee48c468630..ba90f5ae531 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
@@ -212,7 +212,7 @@ vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float dept
}
}
-/* TODO(fclem) port to a common place for other effects to use. */
+/* TODO(@fclem): port to a common place for other effects to use. */
bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg)
{
vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y)));
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
index 0e342938396..cbfa9737a84 100644
--- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
@@ -26,6 +26,9 @@ void main()
worldNormal = cross(hairTangent, binor);
vec3 world_pos = pos;
+#elif defined(POINTCLOUD_SHADER)
+ pointcloud_get_pos_and_radius(pointPosition, pointRadius);
+ pointID = gl_VertexID;
#else
vec3 world_pos = point_object_to_world(pos);
#endif
diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
index 7d016d57c46..d7fc5e0b52a 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
@@ -42,3 +42,13 @@ IN_OUT ShaderHairInterface
flat int hairStrandID;
};
#endif
+
+#ifdef POINTCLOUD_SHADER
+IN_OUT ShaderPointCloudInterface
+{
+ /* world space */
+ float pointRadius;
+ float pointPosition;
+ flat int pointID;
+};
+#endif
diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
index 0ad1393dd70..51e9eda6cc2 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
@@ -31,6 +31,9 @@ void main()
hairThickTime);
worldNormal = cross(hairTangent, binor);
vec3 world_pos = pos;
+#elif defined(POINTCLOUD_SHADER)
+ pointcloud_get_pos_and_radius(pointPosition, pointRadius);
+ pointID = gl_VertexID;
#else
vec3 world_pos = point_object_to_world(pos);
#endif
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
index 8aba1090b58..5a79dfc2663 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
@@ -472,7 +472,7 @@ GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void)
GPENCIL_ViewLayerData **vldata = (GPENCIL_ViewLayerData **)DRW_view_layer_engine_data_ensure(
&draw_engine_gpencil_type, gpencil_view_layer_data_free);
- /* NOTE(fclem) Putting this stuff in viewlayer means it is shared by all viewports.
+ /* NOTE(&fclem): Putting this stuff in viewlayer means it is shared by all viewports.
* For now it is ok, but in the future, it could become a problem if we implement
* the caching system. */
if (*vldata == NULL) {
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 9bc340a2786..dfa0d350485 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -788,7 +788,7 @@ static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL
const float clear_col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
float clear_depth = ob->is_drawmode3d ? 1.0f : 0.0f;
bool inverted = false;
- /* OPTI(fclem) we could optimize by only clearing if the new mask_bits does not contain all
+ /* OPTI(@fclem): we could optimize by only clearing if the new mask_bits does not contain all
* the masks already rendered in the buffer, and drawing only the layers not already drawn. */
bool cleared = false;
diff --git a/source/blender/draw/engines/image/image_batches.hh b/source/blender/draw/engines/image/image_batches.hh
new file mode 100644
index 00000000000..5f82504197a
--- /dev/null
+++ b/source/blender/draw/engines/image/image_batches.hh
@@ -0,0 +1,106 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+#include "image_texture_info.hh"
+
+/** \brief Create GPUBatch for a IMAGE_ScreenSpaceTextureInfo. */
+class BatchUpdater {
+ TextureInfo &info;
+
+ GPUVertFormat format = {0};
+ int pos_id;
+ int uv_id;
+
+ public:
+ BatchUpdater(TextureInfo &info) : info(info)
+ {
+ }
+
+ void update_batch()
+ {
+ ensure_clear_batch();
+ ensure_format();
+ init_batch();
+ }
+
+ void discard_batch()
+ {
+ GPU_BATCH_DISCARD_SAFE(info.batch);
+ }
+
+ private:
+ void ensure_clear_batch()
+ {
+ GPU_BATCH_CLEAR_SAFE(info.batch);
+ if (info.batch == nullptr) {
+ info.batch = GPU_batch_calloc();
+ }
+ }
+
+ void init_batch()
+ {
+ GPUVertBuf *vbo = create_vbo();
+ GPU_batch_init_ex(info.batch, GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO);
+ }
+
+ GPUVertBuf *create_vbo()
+ {
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, 4);
+ float pos[4][2];
+ fill_tri_fan_from_rctf(pos, info.clipping_bounds);
+ float uv[4][2];
+ fill_tri_fan_from_rctf(uv, info.clipping_uv_bounds);
+
+ for (int i = 0; i < 4; i++) {
+ GPU_vertbuf_attr_set(vbo, pos_id, i, pos[i]);
+ GPU_vertbuf_attr_set(vbo, uv_id, i, uv[i]);
+ }
+
+ return vbo;
+ }
+
+ static void fill_tri_fan_from_rctf(float result[4][2], rctf &rect)
+ {
+ result[0][0] = rect.xmin;
+ result[0][1] = rect.ymin;
+ result[1][0] = rect.xmax;
+ result[1][1] = rect.ymin;
+ result[2][0] = rect.xmax;
+ result[2][1] = rect.ymax;
+ result[3][0] = rect.xmin;
+ result[3][1] = rect.ymax;
+ }
+
+ void ensure_format()
+ {
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+ pos_id = GPU_vertformat_attr_id_get(&format, "pos");
+ uv_id = GPU_vertformat_attr_id_get(&format, "uv");
+ }
+ }
+};
diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh b/source/blender/draw/engines/image/image_drawing_mode.hh
new file mode 100644
index 00000000000..52d57ea2ba5
--- /dev/null
+++ b/source/blender/draw/engines/image/image_drawing_mode.hh
@@ -0,0 +1,518 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+#include "BKE_image_partial_update.hh"
+
+#include "IMB_imbuf_types.h"
+
+#include "BLI_float4x4.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "image_batches.hh"
+#include "image_private.hh"
+#include "image_wrappers.hh"
+
+namespace blender::draw::image_engine {
+
+constexpr float EPSILON_UV_BOUNDS = 0.00001f;
+
+/**
+ * \brief Screen space method using a single texture spawning the whole screen.
+ */
+struct OneTextureMethod {
+ IMAGE_InstanceData *instance_data;
+
+ OneTextureMethod(IMAGE_InstanceData *instance_data) : instance_data(instance_data)
+ {
+ }
+
+ /** \brief Update the texture slot uv and screen space bounds. */
+ void update_screen_space_bounds(const ARegion *region)
+ {
+ /* Create a single texture that covers the visible screen space. */
+ BLI_rctf_init(
+ &instance_data->texture_infos[0].clipping_bounds, 0, region->winx, 0, region->winy);
+ instance_data->texture_infos[0].visible = true;
+
+ /* Mark the other textures as invalid. */
+ for (int i = 1; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ BLI_rctf_init_minmax(&instance_data->texture_infos[i].clipping_bounds);
+ instance_data->texture_infos[i].visible = false;
+ }
+ }
+
+ void update_region_uv_bounds(const ARegion *region)
+ {
+ TextureInfo &info = instance_data->texture_infos[0];
+ if (!BLI_rctf_compare(&info.region_uv_bounds, &region->v2d.cur, EPSILON_UV_BOUNDS)) {
+ info.region_uv_bounds = region->v2d.cur;
+ info.dirty = true;
+ }
+
+ /* Mark the other textures as invalid. */
+ for (int i = 1; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ BLI_rctf_init_minmax(&instance_data->texture_infos[i].clipping_bounds);
+ }
+ }
+
+ void update_screen_uv_bounds()
+ {
+ for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ update_screen_uv_bounds(instance_data->texture_infos[0]);
+ }
+ }
+
+ void update_screen_uv_bounds(TextureInfo &info)
+ {
+ /* Although this works, computing an inverted matrix adds some precision issues and leads to
+ * tearing artifacts. This should be modified to use the scaling and transformation from the
+ * not inverted matrix.*/
+ float4x4 mat(instance_data->ss_to_texture);
+ float4x4 mat_inv = mat.inverted();
+ float3 min_uv = mat_inv * float3(0.0f, 0.0f, 0.0f);
+ float3 max_uv = mat_inv * float3(1.0f, 1.0f, 0.0f);
+ BLI_rctf_init(&info.clipping_uv_bounds, min_uv[0], max_uv[0], min_uv[1], max_uv[1]);
+ }
+};
+
+using namespace blender::bke::image::partial_update;
+
+template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractDrawingMode {
+ private:
+ DRWPass *create_image_pass() const
+ {
+ DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS |
+ DRW_STATE_BLEND_ALPHA_PREMUL);
+ return DRW_pass_create("Image", state);
+ }
+
+ DRWPass *create_depth_pass() const
+ {
+ /* Depth is needed for background overlay rendering. Near depth is used for
+ * transparency checker and Far depth is used for indicating the image size. */
+ DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL);
+ return DRW_pass_create("Depth", state);
+ }
+
+ void add_shgroups(const IMAGE_InstanceData *instance_data) const
+ {
+ const ShaderParameters &sh_params = instance_data->sh_params;
+ GPUShader *shader = IMAGE_shader_image_get();
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+
+ DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_data->passes.image_pass);
+ DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", sh_params.far_near);
+ DRW_shgroup_uniform_vec4_copy(shgrp, "shuffle", sh_params.shuffle);
+ DRW_shgroup_uniform_int_copy(shgrp, "drawFlags", sh_params.flags);
+ DRW_shgroup_uniform_bool_copy(shgrp, "imgPremultiplied", sh_params.use_premul_alpha);
+ DRW_shgroup_uniform_texture(shgrp, "depth_texture", dtxl->depth);
+ float image_mat[4][4];
+ unit_m4(image_mat);
+ for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ const TextureInfo &info = instance_data->texture_infos[i];
+ if (!info.visible) {
+ continue;
+ }
+
+ DRWShadingGroup *shgrp_sub = DRW_shgroup_create_sub(shgrp);
+ DRW_shgroup_uniform_texture_ex(shgrp_sub, "imageTexture", info.texture, GPU_SAMPLER_DEFAULT);
+ DRW_shgroup_call_obmat(shgrp_sub, info.batch, image_mat);
+ }
+ }
+
+ /**
+ * \brief add depth drawing calls.
+ *
+ * The depth is used to identify if the tile exist or transparent.
+ */
+ void add_depth_shgroups(IMAGE_InstanceData &instance_data,
+ Image *image,
+ ImageUser *image_user) const
+ {
+ GPUShader *shader = IMAGE_shader_depth_get();
+ DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_data.passes.depth_pass);
+
+ float image_mat[4][4];
+ unit_m4(image_mat);
+
+ ImageUser tile_user = {0};
+ if (image_user) {
+ tile_user = *image_user;
+ }
+
+ for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ const TextureInfo &info = instance_data.texture_infos[i];
+ if (!info.visible) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
+ const ImageTileWrapper image_tile(image_tile_ptr);
+ const int tile_x = image_tile.get_tile_x_offset();
+ const int tile_y = image_tile.get_tile_y_offset();
+ tile_user.tile = image_tile.get_tile_number();
+
+ /* NOTE: `BKE_image_has_ibuf` doesn't work as it fails for render results. That could be a
+ * bug or a feature. For now we just acquire to determine if there is a texture. */
+ void *lock;
+ ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock);
+ if (tile_buffer == nullptr) {
+ continue;
+ }
+ BKE_image_release_ibuf(image, tile_buffer, lock);
+
+ DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp);
+ float4 min_max_uv(tile_x, tile_y, tile_x + 1, tile_y + 1);
+ DRW_shgroup_uniform_vec4_copy(shsub, "min_max_uv", min_max_uv);
+ DRW_shgroup_call_obmat(shsub, info.batch, image_mat);
+ }
+ }
+ }
+
+ /**
+ * \brief Update GPUTextures for drawing the image.
+ *
+ * GPUTextures that are marked dirty are rebuild. GPUTextures that aren't marked dirty are
+ * updated with changed region of the image.
+ */
+ void update_textures(IMAGE_InstanceData &instance_data,
+ Image *image,
+ ImageUser *image_user) const
+ {
+ PartialUpdateChecker<ImageTileData> checker(
+ image, image_user, instance_data.partial_update.user);
+ PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes();
+
+ switch (changes.get_result_code()) {
+ case ePartialUpdateCollectResult::FullUpdateNeeded:
+ instance_data.mark_all_texture_slots_dirty();
+ break;
+ case ePartialUpdateCollectResult::NoChangesDetected:
+ break;
+ case ePartialUpdateCollectResult::PartialChangesDetected:
+ /* Partial update when wrap repeat is enabled is not supported. */
+ if (instance_data.flags.do_tile_drawing) {
+ instance_data.mark_all_texture_slots_dirty();
+ }
+ else {
+ do_partial_update(changes, instance_data);
+ }
+ break;
+ }
+ do_full_update_for_dirty_textures(instance_data, image_user);
+ }
+
+ void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator,
+ IMAGE_InstanceData &instance_data) const
+ {
+ while (iterator.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
+ /* Quick exit when tile_buffer isn't available. */
+ if (iterator.tile_data.tile_buffer == nullptr) {
+ continue;
+ }
+ ensure_float_buffer(*iterator.tile_data.tile_buffer);
+ const float tile_width = static_cast<float>(iterator.tile_data.tile_buffer->x);
+ const float tile_height = static_cast<float>(iterator.tile_data.tile_buffer->y);
+
+ for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ const TextureInfo &info = instance_data.texture_infos[i];
+ /* Dirty images will receive a full update. No need to do a partial one now. */
+ if (info.dirty) {
+ continue;
+ }
+ if (!info.visible) {
+ continue;
+ }
+ GPUTexture *texture = info.texture;
+ const float texture_width = GPU_texture_width(texture);
+ const float texture_height = GPU_texture_height(texture);
+ /* TODO: early bound check. */
+ ImageTileWrapper tile_accessor(iterator.tile_data.tile);
+ float tile_offset_x = static_cast<float>(tile_accessor.get_tile_x_offset());
+ float tile_offset_y = static_cast<float>(tile_accessor.get_tile_y_offset());
+ rcti *changed_region_in_texel_space = &iterator.changed_region.region;
+ rctf changed_region_in_uv_space;
+ BLI_rctf_init(&changed_region_in_uv_space,
+ static_cast<float>(changed_region_in_texel_space->xmin) /
+ static_cast<float>(iterator.tile_data.tile_buffer->x) +
+ tile_offset_x,
+ static_cast<float>(changed_region_in_texel_space->xmax) /
+ static_cast<float>(iterator.tile_data.tile_buffer->x) +
+ tile_offset_x,
+ static_cast<float>(changed_region_in_texel_space->ymin) /
+ static_cast<float>(iterator.tile_data.tile_buffer->y) +
+ tile_offset_y,
+ static_cast<float>(changed_region_in_texel_space->ymax) /
+ static_cast<float>(iterator.tile_data.tile_buffer->y) +
+ tile_offset_y);
+ rctf changed_overlapping_region_in_uv_space;
+ const bool region_overlap = BLI_rctf_isect(&info.region_uv_bounds,
+ &changed_region_in_uv_space,
+ &changed_overlapping_region_in_uv_space);
+ if (!region_overlap) {
+ continue;
+ }
+ /* Convert the overlapping region to texel space and to ss_pixel space...
+ * TODO: first convert to ss_pixel space as integer based. and from there go back to texel
+ * space. But perhaps this isn't needed and we could use an extraction offset somehow. */
+ rcti gpu_texture_region_to_update;
+ BLI_rcti_init(
+ &gpu_texture_region_to_update,
+ floor((changed_overlapping_region_in_uv_space.xmin - info.region_uv_bounds.xmin) *
+ texture_width / BLI_rctf_size_x(&info.region_uv_bounds)),
+ floor((changed_overlapping_region_in_uv_space.xmax - info.region_uv_bounds.xmin) *
+ texture_width / BLI_rctf_size_x(&info.region_uv_bounds)),
+ ceil((changed_overlapping_region_in_uv_space.ymin - info.region_uv_bounds.ymin) *
+ texture_height / BLI_rctf_size_y(&info.region_uv_bounds)),
+ ceil((changed_overlapping_region_in_uv_space.ymax - info.region_uv_bounds.ymin) *
+ texture_height / BLI_rctf_size_y(&info.region_uv_bounds)));
+
+ rcti tile_region_to_extract;
+ BLI_rcti_init(
+ &tile_region_to_extract,
+ floor((changed_overlapping_region_in_uv_space.xmin - tile_offset_x) * tile_width),
+ floor((changed_overlapping_region_in_uv_space.xmax - tile_offset_x) * tile_width),
+ ceil((changed_overlapping_region_in_uv_space.ymin - tile_offset_y) * tile_height),
+ ceil((changed_overlapping_region_in_uv_space.ymax - tile_offset_y) * tile_height));
+
+ /* Create an image buffer with a size.
+ * Extract and scale into an imbuf. */
+ const int texture_region_width = BLI_rcti_size_x(&gpu_texture_region_to_update);
+ const int texture_region_height = BLI_rcti_size_y(&gpu_texture_region_to_update);
+
+ ImBuf extracted_buffer;
+ IMB_initImBuf(
+ &extracted_buffer, texture_region_width, texture_region_height, 32, IB_rectfloat);
+
+ int offset = 0;
+ ImBuf *tile_buffer = iterator.tile_data.tile_buffer;
+ for (int y = gpu_texture_region_to_update.ymin; y < gpu_texture_region_to_update.ymax;
+ y++) {
+ float yf = y / (float)texture_height;
+ float v = info.region_uv_bounds.ymax * yf + info.region_uv_bounds.ymin * (1.0 - yf) -
+ tile_offset_y;
+ for (int x = gpu_texture_region_to_update.xmin; x < gpu_texture_region_to_update.xmax;
+ x++) {
+ float xf = x / (float)texture_width;
+ float u = info.region_uv_bounds.xmax * xf + info.region_uv_bounds.xmin * (1.0 - xf) -
+ tile_offset_x;
+ nearest_interpolation_color(tile_buffer,
+ nullptr,
+ &extracted_buffer.rect_float[offset * 4],
+ u * tile_buffer->x,
+ v * tile_buffer->y);
+ offset++;
+ }
+ }
+
+ GPU_texture_update_sub(texture,
+ GPU_DATA_FLOAT,
+ extracted_buffer.rect_float,
+ gpu_texture_region_to_update.xmin,
+ gpu_texture_region_to_update.ymin,
+ 0,
+ extracted_buffer.x,
+ extracted_buffer.y,
+ 0);
+ imb_freerectImbuf_all(&extracted_buffer);
+ }
+ }
+ }
+
+ void do_full_update_for_dirty_textures(IMAGE_InstanceData &instance_data,
+ const ImageUser *image_user) const
+ {
+ for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ TextureInfo &info = instance_data.texture_infos[i];
+ if (!info.dirty) {
+ continue;
+ }
+ if (!info.visible) {
+ continue;
+ }
+ do_full_update_gpu_texture(info, instance_data, image_user);
+ }
+ }
+
+ void do_full_update_gpu_texture(TextureInfo &info,
+ IMAGE_InstanceData &instance_data,
+ const ImageUser *image_user) const
+ {
+
+ ImBuf texture_buffer;
+ const int texture_width = GPU_texture_width(info.texture);
+ const int texture_height = GPU_texture_height(info.texture);
+ IMB_initImBuf(&texture_buffer, texture_width, texture_height, 0, IB_rectfloat);
+ ImageUser tile_user = {0};
+ if (image_user) {
+ tile_user = *image_user;
+ }
+
+ void *lock;
+
+ Image *image = instance_data.image;
+ LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
+ const ImageTileWrapper image_tile(image_tile_ptr);
+ tile_user.tile = image_tile.get_tile_number();
+
+ ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock);
+ if (tile_buffer == nullptr) {
+ /* Couldn't load the image buffer of the tile. */
+ continue;
+ }
+ do_full_update_texture_slot(instance_data, info, texture_buffer, *tile_buffer, image_tile);
+ BKE_image_release_ibuf(image, tile_buffer, lock);
+ }
+ GPU_texture_update(info.texture, GPU_DATA_FLOAT, texture_buffer.rect_float);
+ imb_freerectImbuf_all(&texture_buffer);
+ }
+
+ /**
+ * \brief Ensure that the float buffer of the given image buffer is available.
+ *
+ * Returns true when a float buffer was created. Somehow the VSE cache increases the ref
+ * counter, but might use a different mechanism for destructing the image, that doesn't free the
+ * rect_float as the reference-counter isn't 0. To work around this we destruct any created local
+ * buffers ourself.
+ */
+ bool ensure_float_buffer(ImBuf &image_buffer) const
+ {
+ if (image_buffer.rect_float == nullptr) {
+ IMB_float_from_rect(&image_buffer);
+ return true;
+ }
+ return false;
+ }
+
+ void do_full_update_texture_slot(const IMAGE_InstanceData &instance_data,
+ const TextureInfo &texture_info,
+ ImBuf &texture_buffer,
+ ImBuf &tile_buffer,
+ const ImageTileWrapper &image_tile) const
+ {
+ const int texture_width = texture_buffer.x;
+ const int texture_height = texture_buffer.y;
+ const bool float_buffer_created = ensure_float_buffer(tile_buffer);
+ /* TODO(jbakker): Find leak when rendering VSE and don't free here. */
+ const bool do_free_float_buffer = float_buffer_created &&
+ instance_data.image->type == IMA_TYPE_R_RESULT;
+
+ /* IMB_transform works in a non-consistent space. This should be documented or fixed!.
+ * Construct a variant of the info_uv_to_texture that adds the texel space
+ * transformation.*/
+ float uv_to_texel[4][4];
+ copy_m4_m4(uv_to_texel, instance_data.ss_to_texture);
+ float scale[3] = {static_cast<float>(texture_width) / static_cast<float>(tile_buffer.x),
+ static_cast<float>(texture_height) / static_cast<float>(tile_buffer.y),
+ 1.0f};
+ rescale_m4(uv_to_texel, scale);
+ uv_to_texel[3][0] += image_tile.get_tile_x_offset() /
+ BLI_rctf_size_x(&texture_info.region_uv_bounds);
+ uv_to_texel[3][1] += image_tile.get_tile_y_offset() /
+ BLI_rctf_size_y(&texture_info.region_uv_bounds);
+ uv_to_texel[3][0] *= texture_width;
+ uv_to_texel[3][1] *= texture_height;
+ invert_m4(uv_to_texel);
+
+ rctf crop_rect;
+ rctf *crop_rect_ptr = nullptr;
+ eIMBTransformMode transform_mode;
+ if (instance_data.flags.do_tile_drawing) {
+ transform_mode = IMB_TRANSFORM_MODE_WRAP_REPEAT;
+ }
+ else {
+ BLI_rctf_init(&crop_rect, 0.0, tile_buffer.x, 0.0, tile_buffer.y);
+ crop_rect_ptr = &crop_rect;
+ transform_mode = IMB_TRANSFORM_MODE_CROP_SRC;
+ }
+
+ IMB_transform(&tile_buffer,
+ &texture_buffer,
+ transform_mode,
+ IMB_FILTER_NEAREST,
+ uv_to_texel,
+ crop_rect_ptr);
+
+ if (do_free_float_buffer) {
+ imb_freerectfloatImBuf(&tile_buffer);
+ }
+ }
+
+ public:
+ void cache_init(IMAGE_Data *vedata) const override
+ {
+ IMAGE_InstanceData *instance_data = vedata->instance_data;
+ instance_data->passes.image_pass = create_image_pass();
+ instance_data->passes.depth_pass = create_depth_pass();
+ }
+
+ void cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser) const override
+ {
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ IMAGE_InstanceData *instance_data = vedata->instance_data;
+ TextureMethod method(instance_data);
+
+ instance_data->partial_update.ensure_image(image);
+ instance_data->clear_dirty_flag();
+
+ /* Step: Find out which screen space textures are needed to draw on the screen. Remove the
+ * screen space textures that aren't needed. */
+ const ARegion *region = draw_ctx->region;
+ method.update_screen_space_bounds(region);
+ method.update_region_uv_bounds(region);
+ method.update_screen_uv_bounds();
+
+ /* Step: Update the GPU textures based on the changes in the image. */
+ instance_data->update_gpu_texture_allocations();
+ update_textures(*instance_data, image, iuser);
+
+ /* Step: Add the GPU textures to the shgroup. */
+ instance_data->update_batches();
+ add_depth_shgroups(*instance_data, image, iuser);
+ add_shgroups(instance_data);
+ }
+
+ void draw_finish(IMAGE_Data *UNUSED(vedata)) const override
+ {
+ }
+
+ void draw_scene(IMAGE_Data *vedata) const override
+ {
+ IMAGE_InstanceData *instance_data = vedata->instance_data;
+
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ GPU_framebuffer_bind(dfbl->default_fb);
+ static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0);
+
+ DRW_view_set_active(instance_data->view);
+ DRW_draw_pass(instance_data->passes.depth_pass);
+ GPU_framebuffer_bind(dfbl->color_only_fb);
+ DRW_draw_pass(instance_data->passes.image_pass);
+ DRW_view_set_active(nullptr);
+ GPU_framebuffer_bind(dfbl->default_fb);
+ }
+}; // namespace clipping
+
+} // namespace blender::draw::image_engine
diff --git a/source/blender/draw/engines/image/image_drawing_mode_image_space.hh b/source/blender/draw/engines/image/image_drawing_mode_image_space.hh
deleted file mode 100644
index 26f4bc28106..00000000000
--- a/source/blender/draw/engines/image/image_drawing_mode_image_space.hh
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2021, Blender Foundation.
- */
-
-/** \file
- * \ingroup draw_engine
- */
-
-#pragma once
-
-#include "image_private.hh"
-
-namespace blender::draw::image_engine {
-
-class ImageSpaceDrawingMode : public AbstractDrawingMode {
- private:
- DRWPass *create_image_pass() const
- {
- /* Write depth is needed for background overlay rendering. Near depth is used for
- * transparency checker and Far depth is used for indicating the image size. */
- DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
- DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL);
- return DRW_pass_create("Image", state);
- }
-
- void add_to_shgroup(AbstractSpaceAccessor *space,
- DRWShadingGroup *grp,
- const Image *image,
- const ImBuf *image_buffer) const
- {
- float image_mat[4][4];
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const ARegion *region = draw_ctx->region;
- space->get_image_mat(image_buffer, region, image_mat);
-
- GPUBatch *geom = DRW_cache_quad_get();
-
- const float translate_x = image_mat[3][0];
- const float translate_y = image_mat[3][1];
- LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
- const int tile_x = ((tile->tile_number - 1001) % 10);
- const int tile_y = ((tile->tile_number - 1001) / 10);
- image_mat[3][0] = (float)tile_x + translate_x;
- image_mat[3][1] = (float)tile_y + translate_y;
- DRW_shgroup_call_obmat(grp, geom, image_mat);
- }
- }
-
- public:
- void cache_init(IMAGE_Data *vedata) const override
- {
- IMAGE_PassList *psl = vedata->psl;
-
- psl->image_pass = create_image_pass();
- }
-
- void cache_image(AbstractSpaceAccessor *space,
- IMAGE_Data *vedata,
- Image *image,
- ImageUser *iuser,
- ImBuf *image_buffer) const override
- {
- IMAGE_PassList *psl = vedata->psl;
- IMAGE_StorageList *stl = vedata->stl;
- IMAGE_PrivateData *pd = stl->pd;
-
- GPUTexture *tex_tile_data = nullptr;
- space->get_gpu_textures(
- image, iuser, image_buffer, &pd->texture, &pd->owns_texture, &tex_tile_data);
- if (pd->texture == nullptr) {
- return;
- }
- const bool is_tiled_texture = tex_tile_data != nullptr;
-
- ShaderParameters sh_params;
- sh_params.use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image,
- image_buffer);
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene = draw_ctx->scene;
- if (scene->camera && scene->camera->type == OB_CAMERA) {
- Camera *camera = static_cast<Camera *>(scene->camera->data);
- copy_v2_fl2(sh_params.far_near, camera->clip_end, camera->clip_start);
- }
- space->get_shader_parameters(sh_params, image_buffer, is_tiled_texture);
-
- GPUShader *shader = IMAGE_shader_image_get(is_tiled_texture);
- DRWShadingGroup *shgrp = DRW_shgroup_create(shader, psl->image_pass);
- if (is_tiled_texture) {
- DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, GPU_SAMPLER_DEFAULT);
- DRW_shgroup_uniform_texture(shgrp, "imageTileData", tex_tile_data);
- }
- else {
- DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, GPU_SAMPLER_DEFAULT);
- }
- DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", sh_params.far_near);
- DRW_shgroup_uniform_vec4_copy(shgrp, "color", ShaderParameters::color);
- DRW_shgroup_uniform_vec4_copy(shgrp, "shuffle", sh_params.shuffle);
- DRW_shgroup_uniform_int_copy(shgrp, "drawFlags", sh_params.flags);
- DRW_shgroup_uniform_bool_copy(shgrp, "imgPremultiplied", sh_params.use_premul_alpha);
-
- add_to_shgroup(space, shgrp, image, image_buffer);
- }
-
- void draw_finish(IMAGE_Data *vedata) const override
- {
- IMAGE_StorageList *stl = vedata->stl;
- IMAGE_PrivateData *pd = stl->pd;
-
- if (pd->texture && pd->owns_texture) {
- GPU_texture_free(pd->texture);
- pd->owns_texture = false;
- }
- pd->texture = nullptr;
- }
-
- void draw_scene(IMAGE_Data *vedata) const override
- {
- IMAGE_PassList *psl = vedata->psl;
- IMAGE_PrivateData *pd = vedata->stl->pd;
-
- DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
- GPU_framebuffer_bind(dfbl->default_fb);
- static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0);
-
- DRW_view_set_active(pd->view);
- DRW_draw_pass(psl->image_pass);
- DRW_view_set_active(nullptr);
- }
-};
-
-} // namespace blender::draw::image_engine
diff --git a/source/blender/draw/engines/image/image_engine.cc b/source/blender/draw/engines/image/image_engine.cc
index be5946f34eb..201733f41a7 100644
--- a/source/blender/draw/engines/image/image_engine.cc
+++ b/source/blender/draw/engines/image/image_engine.cc
@@ -41,7 +41,7 @@
#include "GPU_batch.h"
-#include "image_drawing_mode_image_space.hh"
+#include "image_drawing_mode.hh"
#include "image_engine.h"
#include "image_private.hh"
#include "image_space_image.hh"
@@ -68,7 +68,7 @@ template<
*
* Useful during development to switch between drawing implementations.
*/
- typename DrawingMode = ImageSpaceDrawingMode>
+ typename DrawingMode = ScreenSpaceDrawingMode<OneTextureMethod>>
class ImageEngine {
private:
const DRWContextState *draw_ctx;
@@ -86,48 +86,58 @@ class ImageEngine {
void cache_init()
{
- IMAGE_StorageList *stl = vedata->stl;
- IMAGE_PrivateData *pd = stl->pd;
-
+ IMAGE_InstanceData *instance_data = vedata->instance_data;
drawing_mode.cache_init(vedata);
- pd->view = nullptr;
- if (space->has_view_override()) {
- const ARegion *region = draw_ctx->region;
- pd->view = space->create_view_override(region);
- }
+
+ /* Setup full screen view matrix. */
+ const ARegion *region = draw_ctx->region;
+ float winmat[4][4], viewmat[4][4];
+ orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0);
+ unit_m4(winmat);
+ instance_data->view = DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr);
}
void cache_populate()
{
- IMAGE_StorageList *stl = vedata->stl;
- IMAGE_PrivateData *pd = stl->pd;
+ IMAGE_InstanceData *instance_data = vedata->instance_data;
Main *bmain = CTX_data_main(draw_ctx->evil_C);
- pd->image = space->get_image(bmain);
- if (pd->image == nullptr) {
+ instance_data->image = space->get_image(bmain);
+ if (instance_data->image == nullptr) {
/* Early exit, nothing to draw. */
return;
}
- pd->ibuf = space->acquire_image_buffer(pd->image, &pd->lock);
+ instance_data->flags.do_tile_drawing = instance_data->image->source != IMA_SRC_TILED &&
+ space->use_tile_drawing();
+ void *lock;
+ ImBuf *image_buffer = space->acquire_image_buffer(instance_data->image, &lock);
+
+ /* Setup the matrix to go from screen UV coordinates to UV texture space coordinates. */
+ float image_resolution[2] = {image_buffer ? image_buffer->x : 1024.0f,
+ image_buffer ? image_buffer->y : 1024.0f};
+ space->init_ss_to_texture_matrix(
+ draw_ctx->region, image_resolution, instance_data->ss_to_texture);
+
+ const Scene *scene = DRW_context_state_get()->scene;
+ instance_data->sh_params.update(space.get(), scene, instance_data->image, image_buffer);
+ space->release_buffer(instance_data->image, image_buffer, lock);
+
ImageUser *iuser = space->get_image_user();
- drawing_mode.cache_image(space.get(), vedata, pd->image, iuser, pd->ibuf);
+ drawing_mode.cache_image(vedata, instance_data->image, iuser);
}
void draw_finish()
{
drawing_mode.draw_finish(vedata);
- IMAGE_StorageList *stl = vedata->stl;
- IMAGE_PrivateData *pd = stl->pd;
- space->release_buffer(pd->image, pd->ibuf, pd->lock);
- pd->image = nullptr;
- pd->ibuf = nullptr;
+ IMAGE_InstanceData *instance_data = vedata->instance_data;
+ instance_data->image = nullptr;
}
void draw_scene()
{
drawing_mode.draw_scene(vedata);
}
-};
+}; // namespace blender::draw::image_engine
/* -------------------------------------------------------------------- */
/** \name Engine Callbacks
@@ -135,17 +145,10 @@ class ImageEngine {
static void IMAGE_engine_init(void *ved)
{
- IMAGE_shader_library_ensure();
IMAGE_Data *vedata = (IMAGE_Data *)ved;
- IMAGE_StorageList *stl = vedata->stl;
- if (!stl->pd) {
- stl->pd = static_cast<IMAGE_PrivateData *>(MEM_callocN(sizeof(IMAGE_PrivateData), __func__));
+ if (vedata->instance_data == nullptr) {
+ vedata->instance_data = MEM_new<IMAGE_InstanceData>(__func__);
}
- IMAGE_PrivateData *pd = stl->pd;
-
- pd->ibuf = nullptr;
- pd->lock = nullptr;
- pd->texture = nullptr;
}
static void IMAGE_cache_init(void *vedata)
@@ -174,6 +177,12 @@ static void IMAGE_engine_free()
IMAGE_shader_free();
}
+static void IMAGE_instance_free(void *_instance_data)
+{
+ IMAGE_InstanceData *instance_data = reinterpret_cast<IMAGE_InstanceData *>(_instance_data);
+ MEM_delete(instance_data);
+}
+
/** \} */
static const DrawEngineDataSize IMAGE_data_size = DRW_VIEWPORT_DATA_SIZE(IMAGE_Data);
@@ -191,7 +200,7 @@ DrawEngineType draw_engine_image_type = {
&IMAGE_data_size, /* vedata_size */
&IMAGE_engine_init, /* engine_init */
&IMAGE_engine_free, /* engine_free */
- nullptr, /* instance_free */
+ &IMAGE_instance_free, /* instance_free */
&IMAGE_cache_init, /* cache_init */
&IMAGE_cache_populate, /* cache_populate */
nullptr, /* cache_finish */
diff --git a/source/blender/draw/engines/image/image_instance_data.hh b/source/blender/draw/engines/image/image_instance_data.hh
new file mode 100644
index 00000000000..1a7a20b8b9a
--- /dev/null
+++ b/source/blender/draw/engines/image/image_instance_data.hh
@@ -0,0 +1,119 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+#include "image_batches.hh"
+#include "image_partial_updater.hh"
+#include "image_private.hh"
+#include "image_shader_params.hh"
+#include "image_texture_info.hh"
+#include "image_wrappers.hh"
+
+#include "DRW_render.h"
+
+/**
+ * \brief max allowed textures to use by the ScreenSpaceDrawingMode.
+ *
+ * 4 textures are used to reduce uploading screen space textures when translating the image.
+ */
+constexpr int SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN = 4;
+
+struct IMAGE_InstanceData {
+ struct Image *image;
+
+ PartialImageUpdater partial_update;
+
+ struct DRWView *view;
+ ShaderParameters sh_params;
+ struct {
+ /**
+ * \brief should we perform tiled drawing (wrap repeat).
+ *
+ * Option is true when image is capable of tile drawing (image is not tile) and the tiled
+ * option is set in the space.
+ */
+ bool do_tile_drawing : 1;
+ } flags;
+
+ struct {
+ DRWPass *image_pass;
+ DRWPass *depth_pass;
+ } passes;
+
+ /** \brief Transform matrix to convert a normalized screen space coordinates to texture space. */
+ float ss_to_texture[4][4];
+ TextureInfo texture_infos[SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN];
+
+ public:
+ void clear_dirty_flag()
+ {
+ reset_dirty_flag(false);
+ }
+ void mark_all_texture_slots_dirty()
+ {
+ reset_dirty_flag(true);
+ }
+
+ void update_gpu_texture_allocations()
+ {
+ for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ TextureInfo &info = texture_infos[i];
+ const bool is_allocated = info.texture != nullptr;
+ const bool is_visible = info.visible;
+ const bool should_be_freed = !is_visible && is_allocated;
+ const bool should_be_created = is_visible && !is_allocated;
+
+ if (should_be_freed) {
+ GPU_texture_free(info.texture);
+ info.texture = nullptr;
+ }
+
+ if (should_be_created) {
+ DRW_texture_ensure_fullscreen_2d(
+ &info.texture, GPU_RGBA16F, static_cast<DRWTextureFlag>(0));
+ }
+ info.dirty |= should_be_created;
+ }
+ }
+
+ void update_batches()
+ {
+ for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ TextureInfo &info = texture_infos[i];
+ if (!info.dirty) {
+ continue;
+ }
+ BatchUpdater batch_updater(info);
+ batch_updater.update_batch();
+ }
+ }
+
+ private:
+ /** \brief Set dirty flag of all texture slots to the given value. */
+ void reset_dirty_flag(bool new_value)
+ {
+ for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
+ texture_infos[i].dirty = new_value;
+ }
+ }
+};
diff --git a/source/blender/draw/engines/image/image_partial_updater.hh b/source/blender/draw/engines/image/image_partial_updater.hh
new file mode 100644
index 00000000000..f0c1db2331a
--- /dev/null
+++ b/source/blender/draw/engines/image/image_partial_updater.hh
@@ -0,0 +1,78 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+#include "BKE_image.h"
+#include "BKE_image_partial_update.hh"
+
+struct PartialImageUpdater {
+ struct PartialUpdateUser *user;
+ const struct Image *image;
+
+ /**
+ * \brief Ensure that there is a partial update user for the given image.
+ */
+ void ensure_image(const Image *image)
+ {
+ if (!is_valid(image)) {
+ free();
+ create(image);
+ }
+ }
+
+ virtual ~PartialImageUpdater()
+ {
+ free();
+ }
+
+ private:
+ /**
+ * \brief check if the partial update user can still be used for the given image.
+ *
+ * When switching to a different image the partial update user should be recreated.
+ */
+ bool is_valid(const Image *new_image) const
+ {
+ if (image != new_image) {
+ return false;
+ }
+
+ return user != nullptr;
+ }
+
+ void create(const Image *image)
+ {
+ BLI_assert(user == nullptr);
+ user = BKE_image_partial_update_create(image);
+ image = image;
+ }
+
+ void free()
+ {
+ if (user != nullptr) {
+ BKE_image_partial_update_free(user);
+ user = nullptr;
+ image = nullptr;
+ }
+ }
+};
diff --git a/source/blender/draw/engines/image/image_private.hh b/source/blender/draw/engines/image/image_private.hh
index fc3ce72d0aa..d8f8adb7e84 100644
--- a/source/blender/draw/engines/image/image_private.hh
+++ b/source/blender/draw/engines/image/image_private.hh
@@ -24,6 +24,11 @@
#include <optional>
+#include "BKE_image.h"
+
+#include "image_instance_data.hh"
+#include "image_texture_info.hh"
+
/* Forward declarations */
extern "C" {
struct GPUTexture;
@@ -35,32 +40,13 @@ struct Image;
namespace blender::draw::image_engine {
-/* GPUViewport.storage
- * Is freed every time the viewport engine changes. */
-struct IMAGE_PassList {
- DRWPass *image_pass;
-};
-
-struct IMAGE_PrivateData {
- void *lock;
- struct ImBuf *ibuf;
- struct Image *image;
- struct DRWView *view;
-
- struct GPUTexture *texture;
- bool owns_texture;
-};
-
-struct IMAGE_StorageList {
- IMAGE_PrivateData *pd;
-};
-
struct IMAGE_Data {
void *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
- IMAGE_PassList *psl;
- IMAGE_StorageList *stl;
+ DRWViewportEmptyList *psl;
+ DRWViewportEmptyList *stl;
+ IMAGE_InstanceData *instance_data;
};
/* Shader parameters. */
@@ -68,106 +54,6 @@ struct IMAGE_Data {
#define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1)
#define IMAGE_DRAW_FLAG_SHUFFLING (1 << 2)
#define IMAGE_DRAW_FLAG_DEPTH (1 << 3)
-#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4)
-#define IMAGE_DRAW_FLAG_USE_WORLD_POS (1 << 5)
-
-struct ShaderParameters {
- constexpr static float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
-
- int flags = 0;
- float shuffle[4];
- float far_near[2];
- bool use_premul_alpha = false;
-
- ShaderParameters()
- {
- copy_v4_fl(shuffle, 1.0f);
- copy_v2_fl2(far_near, 100.0f, 0.0f);
- }
-};
-
-/**
- * Space accessor.
- *
- * Image engine is used to draw the images inside multiple spaces \see SpaceLink.
- * The AbstractSpaceAccessor is an interface to communicate with a space.
- */
-class AbstractSpaceAccessor {
- public:
- virtual ~AbstractSpaceAccessor() = default;
-
- /**
- * Return the active image of the space.
- *
- * The returned image will be drawn in the space.
- *
- * The return value is optional.
- */
- virtual Image *get_image(Main *bmain) = 0;
-
- /**
- * Return the #ImageUser of the space.
- *
- * The return value is optional.
- */
- virtual ImageUser *get_image_user() = 0;
-
- /**
- * Acquire the image buffer of the image.
- *
- * \param image: Image to get the buffer from. Image is the same as returned from the #get_image
- * member.
- * \param lock: pointer to a lock object.
- * \return Image buffer of the given image.
- */
- virtual ImBuf *acquire_image_buffer(Image *image, void **lock) = 0;
-
- /**
- * Release a previous locked image from #acquire_image_buffer.
- */
- virtual void release_buffer(Image *image, ImBuf *image_buffer, void *lock) = 0;
-
- /**
- * Update the r_shader_parameters with space specific settings.
- *
- * Only update the #ShaderParameters.flags and #ShaderParameters.shuffle. Other parameters
- * are updated inside the image engine.
- */
- virtual void get_shader_parameters(ShaderParameters &r_shader_parameters,
- ImBuf *image_buffer,
- bool is_tiled) = 0;
-
- /**
- * Retrieve the gpu textures to draw.
- */
- virtual void get_gpu_textures(Image *image,
- ImageUser *iuser,
- ImBuf *image_buffer,
- GPUTexture **r_gpu_texture,
- bool *r_owns_texture,
- GPUTexture **r_tex_tile_data) = 0;
-
- /**
- * Does this space override the view.
- * When so this member should return true and the create_view_override must return the view to
- * use during drawing.
- */
- virtual bool has_view_override() const = 0;
-
- /**
- * Override the view for drawing.
- * Should match #has_view_override.
- */
- virtual DRWView *create_view_override(const ARegion *UNUSED(region)) = 0;
-
- /**
- * Initialize the matrix that will be used to draw the image. The matrix will be send as object
- * matrix to the drawing pipeline.
- */
- virtual void get_image_mat(const ImBuf *image_buffer,
- const ARegion *region,
- float r_mat[4][4]) const = 0;
-}; // namespace blender::draw::image_engine
/**
* Abstract class for a drawing mode of the image engine.
@@ -179,18 +65,14 @@ class AbstractDrawingMode {
public:
virtual ~AbstractDrawingMode() = default;
virtual void cache_init(IMAGE_Data *vedata) const = 0;
- virtual void cache_image(AbstractSpaceAccessor *space,
- IMAGE_Data *vedata,
- Image *image,
- ImageUser *iuser,
- ImBuf *image_buffer) const = 0;
+ virtual void cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser) const = 0;
virtual void draw_scene(IMAGE_Data *vedata) const = 0;
virtual void draw_finish(IMAGE_Data *vedata) const = 0;
};
/* image_shader.c */
-GPUShader *IMAGE_shader_image_get(bool is_tiled_image);
-void IMAGE_shader_library_ensure();
+GPUShader *IMAGE_shader_image_get();
+GPUShader *IMAGE_shader_depth_get();
void IMAGE_shader_free();
} // namespace blender::draw::image_engine
diff --git a/source/blender/draw/engines/image/image_shader.cc b/source/blender/draw/engines/image/image_shader.cc
index 1c6abf36505..952843d7dd7 100644
--- a/source/blender/draw/engines/image/image_shader.cc
+++ b/source/blender/draw/engines/image/image_shader.cc
@@ -29,50 +29,33 @@
#include "image_engine.h"
#include "image_private.hh"
-extern "C" {
-extern char datatoc_common_colormanagement_lib_glsl[];
-extern char datatoc_common_globals_lib_glsl[];
-extern char datatoc_common_view_lib_glsl[];
-
-extern char datatoc_engine_image_frag_glsl[];
-extern char datatoc_engine_image_vert_glsl[];
-}
-
namespace blender::draw::image_engine {
struct IMAGE_Shaders {
- GPUShader *image_sh[2];
+ GPUShader *image_sh;
+ GPUShader *depth_sh;
};
static struct {
IMAGE_Shaders shaders;
- DRWShaderLibrary *lib;
-} e_data = {{{nullptr}}}; /* Engine data */
+} e_data = {{nullptr}}; /* Engine data */
-void IMAGE_shader_library_ensure()
+GPUShader *IMAGE_shader_image_get()
{
- if (e_data.lib == nullptr) {
- e_data.lib = DRW_shader_library_create();
- /* NOTE: These need to be ordered by dependencies. */
- DRW_SHADER_LIB_ADD(e_data.lib, common_colormanagement_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_globals_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib);
+ IMAGE_Shaders *sh_data = &e_data.shaders;
+ if (sh_data->image_sh == nullptr) {
+ sh_data->image_sh = GPU_shader_create_from_info_name("image_engine_color_shader");
}
+ return sh_data->image_sh;
}
-GPUShader *IMAGE_shader_image_get(bool is_tiled_image)
+GPUShader *IMAGE_shader_depth_get()
{
- const int index = is_tiled_image ? 1 : 0;
IMAGE_Shaders *sh_data = &e_data.shaders;
- if (sh_data->image_sh[index] == nullptr) {
- sh_data->image_sh[index] = DRW_shader_create_with_shaderlib(
- datatoc_engine_image_vert_glsl,
- nullptr,
- datatoc_engine_image_frag_glsl,
- e_data.lib,
- is_tiled_image ? "#define TILED_IMAGE\n" : nullptr);
+ if (sh_data->depth_sh == nullptr) {
+ sh_data->depth_sh = GPU_shader_create_from_info_name("image_engine_depth_shader");
}
- return sh_data->image_sh[index];
+ return sh_data->depth_sh;
}
void IMAGE_shader_free()
@@ -81,8 +64,6 @@ void IMAGE_shader_free()
for (int i = 0; i < (sizeof(IMAGE_Shaders) / sizeof(GPUShader *)); i++) {
DRW_SHADER_FREE_SAFE(sh_data_as_array[i]);
}
-
- DRW_SHADER_LIB_FREE_SAFE(e_data.lib);
}
} // namespace blender::draw::image_engine
diff --git a/source/blender/draw/engines/image/image_shader_params.hh b/source/blender/draw/engines/image/image_shader_params.hh
new file mode 100644
index 00000000000..9f3da3f9e9e
--- /dev/null
+++ b/source/blender/draw/engines/image/image_shader_params.hh
@@ -0,0 +1,57 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+#include "DNA_camera_types.h"
+#include "DNA_image_types.h"
+#include "DNA_scene_types.h"
+
+#include "IMB_imbuf_types.h"
+
+#include "BKE_image.h"
+
+#include "BLI_math.h"
+
+#include "image_space.hh"
+
+struct ShaderParameters {
+ int flags = 0;
+ float shuffle[4];
+ float far_near[2];
+ bool use_premul_alpha = false;
+
+ void update(AbstractSpaceAccessor *space, const Scene *scene, Image *image, ImBuf *image_buffer)
+ {
+ flags = 0;
+ copy_v4_fl(shuffle, 1.0f);
+ copy_v2_fl2(far_near, 100.0f, 0.0f);
+
+ use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
+
+ if (scene->camera && scene->camera->type == OB_CAMERA) {
+ Camera *camera = static_cast<Camera *>(scene->camera->data);
+ copy_v2_fl2(far_near, camera->clip_end, camera->clip_start);
+ }
+ space->get_shader_parameters(*this, image_buffer);
+ }
+};
diff --git a/source/blender/draw/engines/image/image_space.hh b/source/blender/draw/engines/image/image_space.hh
new file mode 100644
index 00000000000..fbb92ce24aa
--- /dev/null
+++ b/source/blender/draw/engines/image/image_space.hh
@@ -0,0 +1,98 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+struct ShaderParameters;
+
+/**
+ * Space accessor.
+ *
+ * Image engine is used to draw the images inside multiple spaces \see SpaceLink.
+ * The AbstractSpaceAccessor is an interface to communicate with a space.
+ */
+class AbstractSpaceAccessor {
+ public:
+ virtual ~AbstractSpaceAccessor() = default;
+
+ /**
+ * Return the active image of the space.
+ *
+ * The returned image will be drawn in the space.
+ *
+ * The return value is optional.
+ */
+ virtual Image *get_image(Main *bmain) = 0;
+
+ /**
+ * Return the #ImageUser of the space.
+ *
+ * The return value is optional.
+ */
+ virtual ImageUser *get_image_user() = 0;
+
+ /**
+ * Acquire the image buffer of the image.
+ *
+ * \param image: Image to get the buffer from. Image is the same as returned from the #get_image
+ * member.
+ * \param lock: pointer to a lock object.
+ * \return Image buffer of the given image.
+ */
+ virtual ImBuf *acquire_image_buffer(Image *image, void **lock) = 0;
+
+ /**
+ * Release a previous locked image from #acquire_image_buffer.
+ */
+ virtual void release_buffer(Image *image, ImBuf *image_buffer, void *lock) = 0;
+
+ /**
+ * Update the r_shader_parameters with space specific settings.
+ *
+ * Only update the #ShaderParameters.flags and #ShaderParameters.shuffle. Other parameters
+ * are updated inside the image engine.
+ */
+ virtual void get_shader_parameters(ShaderParameters &r_shader_parameters,
+ ImBuf *image_buffer) = 0;
+
+ /**
+ * Retrieve the gpu textures to draw.
+ */
+ virtual void get_gpu_textures(Image *image,
+ ImageUser *iuser,
+ ImBuf *image_buffer,
+ GPUTexture **r_gpu_texture,
+ bool *r_owns_texture,
+ GPUTexture **r_tex_tile_data) = 0;
+
+ /** \brief Is (wrap) repeat option enabled in the space. */
+ virtual bool use_tile_drawing() const = 0;
+
+ /**
+ * \brief Initialize r_uv_to_texture matrix to transform from normalized screen space coordinates
+ * (0..1) to texture space UV coordinates.
+ */
+ virtual void init_ss_to_texture_matrix(const ARegion *region,
+ const float image_resolution[2],
+ float r_uv_to_texture[4][4]) const = 0;
+
+}; // namespace blender::draw::image_engine
diff --git a/source/blender/draw/engines/image/image_space_image.hh b/source/blender/draw/engines/image/image_space_image.hh
index 7728a963254..090481a351d 100644
--- a/source/blender/draw/engines/image/image_space_image.hh
+++ b/source/blender/draw/engines/image/image_space_image.hh
@@ -54,14 +54,9 @@ class SpaceImageAccessor : public AbstractSpaceAccessor {
ED_space_image_release_buffer(sima, image_buffer, lock);
}
- void get_shader_parameters(ShaderParameters &r_shader_parameters,
- ImBuf *image_buffer,
- bool is_tiled) override
+ void get_shader_parameters(ShaderParameters &r_shader_parameters, ImBuf *image_buffer) override
{
const int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(image_buffer);
- const bool do_repeat = (!is_tiled) && ((sima->flag & SI_DRAW_TILE) != 0);
- SET_FLAG_FROM_TEST(r_shader_parameters.flags, do_repeat, IMAGE_DRAW_FLAG_DO_REPEAT);
- SET_FLAG_FROM_TEST(r_shader_parameters.flags, is_tiled, IMAGE_DRAW_FLAG_USE_WORLD_POS);
if ((sima_flag & SI_USE_ALPHA) != 0) {
/* Show RGBA */
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHOW_ALPHA | IMAGE_DRAW_FLAG_APPLY_ALPHA;
@@ -102,15 +97,6 @@ class SpaceImageAccessor : public AbstractSpaceAccessor {
}
}
- bool has_view_override() const override
- {
- return false;
- }
- DRWView *create_view_override(const ARegion *UNUSED(region)) override
- {
- return nullptr;
- }
-
void get_gpu_textures(Image *image,
ImageUser *iuser,
ImBuf *image_buffer,
@@ -171,11 +157,25 @@ class SpaceImageAccessor : public AbstractSpaceAccessor {
}
}
- void get_image_mat(const ImBuf *UNUSED(image_buffer),
- const ARegion *UNUSED(region),
- float r_mat[4][4]) const override
+ bool use_tile_drawing() const override
+ {
+ return (sima->flag & SI_DRAW_TILE) != 0;
+ }
+
+ void init_ss_to_texture_matrix(const ARegion *region,
+ const float UNUSED(image_resolution[2]),
+ float r_uv_to_texture[4][4]) const override
{
- unit_m4(r_mat);
+ unit_m4(r_uv_to_texture);
+ float scale_x = 1.0 / BLI_rctf_size_x(&region->v2d.cur);
+ float scale_y = 1.0 / BLI_rctf_size_y(&region->v2d.cur);
+ float translate_x = scale_x * -region->v2d.cur.xmin;
+ float translate_y = scale_y * -region->v2d.cur.ymin;
+
+ r_uv_to_texture[0][0] = scale_x;
+ r_uv_to_texture[1][1] = scale_y;
+ r_uv_to_texture[3][0] = translate_x;
+ r_uv_to_texture[3][1] = translate_y;
}
};
diff --git a/source/blender/draw/engines/image/image_space_node.hh b/source/blender/draw/engines/image/image_space_node.hh
index 3ca18eec742..15eef8f6499 100644
--- a/source/blender/draw/engines/image/image_space_node.hh
+++ b/source/blender/draw/engines/image/image_space_node.hh
@@ -54,23 +54,7 @@ class SpaceNodeAccessor : public AbstractSpaceAccessor {
BKE_image_release_ibuf(image, ibuf, lock);
}
- bool has_view_override() const override
- {
- return true;
- }
-
- DRWView *create_view_override(const ARegion *region) override
- {
- /* Setup a screen pixel view. The backdrop of the node editor doesn't follow the region. */
- float winmat[4][4], viewmat[4][4];
- orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0);
- unit_m4(winmat);
- return DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr);
- }
-
- void get_shader_parameters(ShaderParameters &r_shader_parameters,
- ImBuf *ibuf,
- bool UNUSED(is_tiled)) override
+ void get_shader_parameters(ShaderParameters &r_shader_parameters, ImBuf *ibuf) override
{
if ((snode->flag & SNODE_USE_ALPHA) != 0) {
/* Show RGBA */
@@ -120,18 +104,33 @@ class SpaceNodeAccessor : public AbstractSpaceAccessor {
*r_tex_tile_data = nullptr;
}
- void get_image_mat(const ImBuf *image_buffer,
- const ARegion *region,
- float r_mat[4][4]) const override
+ bool use_tile_drawing() const override
+ {
+ return false;
+ }
+
+ /**
+ * The backdrop of the node editor isn't drawn in screen space UV space. But is locked with the
+ * screen.
+ */
+ void init_ss_to_texture_matrix(const ARegion *region,
+ const float image_resolution[2],
+ float r_uv_to_texture[4][4]) const override
{
- unit_m4(r_mat);
- const float ibuf_width = image_buffer->x;
- const float ibuf_height = image_buffer->y;
-
- r_mat[0][0] = ibuf_width * snode->zoom;
- r_mat[1][1] = ibuf_height * snode->zoom;
- r_mat[3][0] = (region->winx - snode->zoom * ibuf_width) / 2 + snode->xof;
- r_mat[3][1] = (region->winy - snode->zoom * ibuf_height) / 2 + snode->yof;
+ unit_m4(r_uv_to_texture);
+ float display_resolution[2];
+ mul_v2_v2fl(display_resolution, image_resolution, snode->zoom);
+ const float scale_x = display_resolution[0] / region->winx;
+ const float scale_y = display_resolution[1] / region->winy;
+ const float translate_x = ((region->winx - display_resolution[0]) * 0.5f + snode->xof) /
+ region->winx;
+ const float translate_y = ((region->winy - display_resolution[1]) * 0.5f + snode->yof) /
+ region->winy;
+
+ r_uv_to_texture[0][0] = scale_x;
+ r_uv_to_texture[1][1] = scale_y;
+ r_uv_to_texture[3][0] = translate_x;
+ r_uv_to_texture[3][1] = translate_y;
}
};
diff --git a/source/blender/draw/engines/image/image_texture_info.hh b/source/blender/draw/engines/image/image_texture_info.hh
new file mode 100644
index 00000000000..8c3b7494831
--- /dev/null
+++ b/source/blender/draw/engines/image/image_texture_info.hh
@@ -0,0 +1,78 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2022, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+#include "BLI_rect.h"
+
+#include "GPU_batch.h"
+#include "GPU_texture.h"
+
+struct TextureInfo {
+ /**
+ * \brief Is the texture clipped.
+ *
+ * Resources of clipped textures are freed and ignored when performing partial updates.
+ */
+ bool visible : 1;
+
+ /**
+ * \brief does this texture need a full update.
+ *
+ * When set to false the texture can be updated using a partial update.
+ */
+ bool dirty : 1;
+
+ /** \brief area of the texture in screen space. */
+ rctf clipping_bounds;
+ /** \brief uv area of the texture (copy from ARegion). */
+ rctf region_uv_bounds;
+ /** \brief uv area of the texture in screen space. */
+ rctf clipping_uv_bounds;
+
+ /**
+ * \brief Batch to draw the associated text on the screen.
+ *
+ * Contains a VBO with `pos` and `uv`.
+ * `pos` (2xF32) is relative to the origin of the space.
+ * `uv` (2xF32) reflect the uv bounds.
+ */
+ GPUBatch *batch;
+
+ /**
+ * \brief GPU Texture for a partial region of the image editor.
+ */
+ GPUTexture *texture;
+
+ ~TextureInfo()
+ {
+ if (batch != nullptr) {
+ GPU_batch_discard(batch);
+ batch = nullptr;
+ }
+
+ if (texture != nullptr) {
+ GPU_texture_free(texture);
+ texture = nullptr;
+ }
+ }
+};
diff --git a/source/blender/draw/engines/image/image_wrappers.hh b/source/blender/draw/engines/image/image_wrappers.hh
new file mode 100644
index 00000000000..63dbbde12ef
--- /dev/null
+++ b/source/blender/draw/engines/image/image_wrappers.hh
@@ -0,0 +1,49 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2022, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+#include "DNA_image_types.h"
+
+struct ImageTileWrapper {
+ ImageTile *image_tile;
+ ImageTileWrapper(ImageTile *image_tile) : image_tile(image_tile)
+ {
+ }
+
+ int get_tile_number() const
+ {
+ return image_tile->tile_number;
+ }
+
+ int get_tile_x_offset() const
+ {
+ int tile_number = get_tile_number();
+ return (tile_number - 1001) % 10;
+ }
+
+ int get_tile_y_offset() const
+ {
+ int tile_number = get_tile_number();
+ return (tile_number - 1001) / 10;
+ }
+};
diff --git a/source/blender/draw/engines/image/shaders/engine_image_frag.glsl b/source/blender/draw/engines/image/shaders/engine_image_frag.glsl
deleted file mode 100644
index 55a2f2a72f1..00000000000
--- a/source/blender/draw/engines/image/shaders/engine_image_frag.glsl
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl)
-
-/* Keep in sync with image_engine.c */
-#define IMAGE_DRAW_FLAG_SHOW_ALPHA (1 << 0)
-#define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1)
-#define IMAGE_DRAW_FLAG_SHUFFLING (1 << 2)
-#define IMAGE_DRAW_FLAG_DEPTH (1 << 3)
-#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4)
-
-#ifdef TILED_IMAGE
-uniform sampler2DArray imageTileArray;
-uniform sampler1DArray imageTileData;
-#else
-uniform sampler2D imageTexture;
-#endif
-
-uniform bool imgPremultiplied;
-uniform int drawFlags;
-uniform vec2 farNearDistances;
-uniform vec4 color;
-uniform vec4 shuffle;
-
-#define FAR_DISTANCE farNearDistances.x
-#define NEAR_DISTANCE farNearDistances.y
-
-in vec2 uvs;
-
-out vec4 fragColor;
-
-#ifdef TILED_IMAGE
-/* TODO(fclem): deduplicate code. */
-bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map)
-{
- vec2 tile_pos = floor(co.xy);
-
- if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) {
- return false;
- }
-
- float tile = 10.0 * tile_pos.y + tile_pos.x;
- if (tile >= textureSize(map, 0).x) {
- return false;
- }
-
- /* Fetch tile information. */
- float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x;
- if (tile_layer < 0.0) {
- return false;
- }
-
- vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0);
-
- co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer);
- return true;
-}
-#endif
-
-void main()
-{
- vec4 tex_color;
- /* Read texture */
-#ifdef TILED_IMAGE
- vec3 co = vec3(uvs, 0.0);
- if (node_tex_tile_lookup(co, imageTileArray, imageTileData)) {
- tex_color = texture(imageTileArray, co);
- }
- else {
- tex_color = vec4(1.0, 0.0, 1.0, 1.0);
- }
-#else
- vec2 uvs_clamped = ((drawFlags & IMAGE_DRAW_FLAG_DO_REPEAT) != 0) ?
- fract(uvs) :
- clamp(uvs, vec2(0.0), vec2(1.0));
- tex_color = texture(imageTexture, uvs_clamped);
-#endif
-
- if ((drawFlags & IMAGE_DRAW_FLAG_APPLY_ALPHA) != 0) {
- if (!imgPremultiplied) {
- tex_color.rgb *= tex_color.a;
- }
- }
- if ((drawFlags & IMAGE_DRAW_FLAG_DEPTH) != 0) {
- tex_color = smoothstep(FAR_DISTANCE, NEAR_DISTANCE, tex_color);
- }
-
- if ((drawFlags & IMAGE_DRAW_FLAG_SHUFFLING) != 0) {
- tex_color = color * dot(tex_color, shuffle);
- }
- if ((drawFlags & IMAGE_DRAW_FLAG_SHOW_ALPHA) == 0) {
- tex_color.a = 1.0;
- }
-
- fragColor = tex_color;
-}
diff --git a/source/blender/draw/engines/image/shaders/engine_image_vert.glsl b/source/blender/draw/engines/image/shaders/engine_image_vert.glsl
deleted file mode 100644
index da2b1907c41..00000000000
--- a/source/blender/draw/engines/image/shaders/engine_image_vert.glsl
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4)
-#define IMAGE_DRAW_FLAG_USE_WORLD_POS (1 << 5)
-#define IMAGE_Z_DEPTH 0.75
-
-uniform int drawFlags;
-
-in vec3 pos;
-out vec2 uvs;
-
-void main()
-{
- /* `pos` contains the coordinates of a quad (-1..1). but we need the coordinates of an image
- * plane (0..1) */
- vec3 image_pos = pos * 0.5 + 0.5;
-
- if ((drawFlags & IMAGE_DRAW_FLAG_DO_REPEAT) != 0) {
- gl_Position = vec4(pos.xy, IMAGE_Z_DEPTH, 1.0);
- uvs = point_view_to_object(image_pos).xy;
- }
- else {
- vec3 world_pos = point_object_to_world(image_pos);
- vec4 position = point_world_to_ndc(world_pos);
- /* Move drawn pixels to the front. In the overlay engine the depth is used
- * to detect if a transparency texture or the background color should be drawn.
- * Vertices are between 0.0 and 0.2, Edges between 0.2 and 0.4
- * actual pixels are at 0.75, 1.0 is used for the background. */
- position.z = IMAGE_Z_DEPTH;
- gl_Position = position;
- /* UDIM texture uses the world position for tile selection. */
- uvs = ((drawFlags & IMAGE_DRAW_FLAG_USE_WORLD_POS) != 0) ? world_pos.xy : image_pos.xy;
- }
-}
diff --git a/source/blender/draw/engines/image/shaders/image_engine_color_frag.glsl b/source/blender/draw/engines/image/shaders/image_engine_color_frag.glsl
new file mode 100644
index 00000000000..0edc18836f0
--- /dev/null
+++ b/source/blender/draw/engines/image/shaders/image_engine_color_frag.glsl
@@ -0,0 +1,38 @@
+#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl)
+
+/* Keep in sync with image_engine.c */
+#define IMAGE_DRAW_FLAG_SHOW_ALPHA (1 << 0)
+#define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1)
+#define IMAGE_DRAW_FLAG_SHUFFLING (1 << 2)
+#define IMAGE_DRAW_FLAG_DEPTH (1 << 3)
+
+#define FAR_DISTANCE farNearDistances.x
+#define NEAR_DISTANCE farNearDistances.y
+
+void main()
+{
+ ivec2 uvs_clamped = ivec2(uv_screen);
+ float depth = texelFetch(depth_texture, uvs_clamped, 0).r;
+ if (depth == 1.0) {
+ discard;
+ }
+
+ vec4 tex_color = texelFetch(imageTexture, uvs_clamped, 0);
+
+ if ((drawFlags & IMAGE_DRAW_FLAG_APPLY_ALPHA) != 0) {
+ if (!imgPremultiplied) {
+ tex_color.rgb *= tex_color.a;
+ }
+ }
+ if ((drawFlags & IMAGE_DRAW_FLAG_DEPTH) != 0) {
+ tex_color = smoothstep(FAR_DISTANCE, NEAR_DISTANCE, tex_color);
+ }
+
+ if ((drawFlags & IMAGE_DRAW_FLAG_SHUFFLING) != 0) {
+ tex_color = vec4(dot(tex_color, shuffle));
+ }
+ if ((drawFlags & IMAGE_DRAW_FLAG_SHOW_ALPHA) == 0) {
+ tex_color.a = 1.0;
+ }
+ fragColor = tex_color;
+}
diff --git a/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl b/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl
new file mode 100644
index 00000000000..fb72a132613
--- /dev/null
+++ b/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl
@@ -0,0 +1,11 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+void main()
+{
+ vec3 image_pos = vec3(pos, 0.0);
+ uv_screen = image_pos.xy;
+
+ vec3 world_pos = point_object_to_world(image_pos);
+ vec4 position = point_world_to_ndc(world_pos);
+ gl_Position = position;
+}
diff --git a/source/blender/draw/engines/image/shaders/image_engine_depth_frag.glsl b/source/blender/draw/engines/image/shaders/image_engine_depth_frag.glsl
new file mode 100644
index 00000000000..88610fb97fd
--- /dev/null
+++ b/source/blender/draw/engines/image/shaders/image_engine_depth_frag.glsl
@@ -0,0 +1,16 @@
+#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl)
+
+#define Z_DEPTH_BORDER 1.0
+#define Z_DEPTH_IMAGE 0.75
+
+bool is_border(vec2 uv)
+{
+ return (uv.x < min_max_uv.x || uv.y < min_max_uv.y || uv.x >= min_max_uv.z ||
+ uv.y >= min_max_uv.w);
+}
+
+void main()
+{
+ bool border = is_border(uv_image);
+ gl_FragDepth = border ? Z_DEPTH_BORDER : Z_DEPTH_IMAGE;
+}
diff --git a/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl b/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl
new file mode 100644
index 00000000000..3181a85ff55
--- /dev/null
+++ b/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl
@@ -0,0 +1,11 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+void main()
+{
+ vec3 image_pos = vec3(pos, 0.0);
+ uv_image = uv;
+
+ vec3 world_pos = point_object_to_world(image_pos);
+ vec4 position = point_world_to_ndc(world_pos);
+ gl_Position = position;
+}
diff --git a/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh b/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh
new file mode 100644
index 00000000000..8b671686a5c
--- /dev/null
+++ b/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh
@@ -0,0 +1,30 @@
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_INTERFACE_INFO(image_engine_color_iface, "").smooth(Type::VEC2, "uv_screen");
+
+GPU_SHADER_CREATE_INFO(image_engine_color_shader)
+ .vertex_in(0, Type::VEC2, "pos")
+ .vertex_out(image_engine_color_iface)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .push_constant(Type::VEC4, "shuffle")
+ .push_constant(Type::VEC2, "farNearDistances")
+ .push_constant(Type::INT, "drawFlags")
+ .push_constant(Type::BOOL, "imgPremultiplied")
+ .sampler(0, ImageType::FLOAT_2D, "imageTexture")
+ .sampler(1, ImageType::DEPTH_2D, "depth_texture")
+ .vertex_source("image_engine_color_vert.glsl")
+ .fragment_source("image_engine_color_frag.glsl")
+ .additional_info("draw_modelmat")
+ .do_static_compilation(true);
+
+GPU_SHADER_INTERFACE_INFO(image_engine_depth_iface, "").smooth(Type::VEC2, "uv_image");
+
+GPU_SHADER_CREATE_INFO(image_engine_depth_shader)
+ .vertex_in(0, Type::VEC2, "pos")
+ .vertex_in(1, Type::VEC2, "uv")
+ .vertex_out(image_engine_depth_iface)
+ .push_constant(Type::VEC4, "min_max_uv")
+ .vertex_source("image_engine_depth_vert.glsl")
+ .fragment_source("image_engine_depth_frag.glsl")
+ .additional_info("draw_modelmat")
+ .do_static_compilation(true);
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c
index 668a1255843..5abebe2f32b 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.c
@@ -191,20 +191,17 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
sh = OVERLAY_shader_armature_sphere(false);
grp = DRW_shgroup_create(sh, armature_ps);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f);
cb->solid.point_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_point_get());
grp = DRW_shgroup_create(sh, armature_ps);
DRW_shgroup_state_disable(grp, DRW_STATE_WRITE_DEPTH);
DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_float_copy(grp, "alpha", wire_alpha * 0.4f);
cb->transp.point_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_point_get());
sh = OVERLAY_shader_armature_shape(false);
grp = DRW_shgroup_create(sh, armature_ps);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f);
cb->solid.custom_fill = grp;
cb->solid.box_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_box_get());
@@ -213,7 +210,6 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
grp = DRW_shgroup_create(sh, armature_ps);
DRW_shgroup_state_disable(grp, DRW_STATE_WRITE_DEPTH);
DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_float_copy(grp, "alpha", wire_alpha * 0.6f);
cb->transp.custom_fill = grp;
cb->transp.box_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_box_get());
@@ -335,7 +331,6 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
sh = OVERLAY_shader_armature_envelope(false);
grp = DRW_shgroup_create(sh, armature_ps);
DRW_shgroup_state_enable(grp, DRW_STATE_CULL_BACK);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_bool_copy(grp, "isDistance", false);
DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f);
cb->solid.envelope_fill = BUF_INSTANCE(grp, format, DRW_cache_bone_envelope_solid_get());
@@ -371,7 +366,6 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
sh = OVERLAY_shader_armature_envelope(false);
grp = DRW_shgroup_create(sh, armature_transp_ps);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f);
DRW_shgroup_uniform_bool_copy(grp, "isDistance", true);
DRW_shgroup_state_enable(grp, DRW_STATE_CULL_FRONT);
@@ -667,7 +661,7 @@ static void drw_shgroup_bone_custom_solid(ArmatureDrawContext *ctx,
/* TODO(fclem): arg... less than ideal but we never iter on this object
* to assure batch cache is valid. */
- DRW_mesh_batch_cache_validate(mesh);
+ DRW_mesh_batch_cache_validate(custom, mesh);
struct GPUBatch *surf = DRW_mesh_batch_cache_get_surface(mesh);
struct GPUBatch *edges = DRW_mesh_batch_cache_get_edge_detection(mesh, NULL);
@@ -715,7 +709,7 @@ static void drw_shgroup_bone_custom_wire(ArmatureDrawContext *ctx,
}
/* TODO(fclem): arg... less than ideal but we never iter on this object
* to assure batch cache is valid. */
- DRW_mesh_batch_cache_validate(mesh);
+ DRW_mesh_batch_cache_validate(custom, mesh);
struct GPUBatch *geom = DRW_mesh_batch_cache_get_all_edges(mesh);
if (geom) {
@@ -2044,6 +2038,126 @@ static void draw_bone_name(ArmatureDrawContext *ctx,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Pose Bone Culling
+ *
+ * Used for selection since drawing many bones can be slow, see: T91253.
+ *
+ * Bounding spheres are used with margins added to ensure bones are included.
+ * An added margin is needed because #BKE_pchan_minmax only returns the bounds
+ * of the bones head & tail which doesn't account for parts of the bone users may select
+ * (octahedral spheres or envelope radius for example).
+ * \{ */
+
+static void pchan_culling_calc_bsphere(const Object *ob,
+ const bPoseChannel *pchan,
+ BoundSphere *r_bsphere)
+{
+ float min[3], max[3];
+ INIT_MINMAX(min, max);
+ BKE_pchan_minmax(ob, pchan, min, max);
+ mid_v3_v3v3(r_bsphere->center, min, max);
+ r_bsphere->radius = len_v3v3(min, r_bsphere->center);
+}
+
+/**
+ * \return true when bounding sphere from `pchan` intersect the view.
+ * (same for other "test" functions defined here).
+ */
+static bool pchan_culling_test_simple(const DRWView *view,
+ const Object *ob,
+ const bPoseChannel *pchan)
+{
+ BoundSphere bsphere;
+ pchan_culling_calc_bsphere(ob, pchan, &bsphere);
+ return DRW_culling_sphere_test(view, &bsphere);
+}
+
+static bool pchan_culling_test_with_radius_scale(const DRWView *view,
+ const Object *ob,
+ const bPoseChannel *pchan,
+ const float scale)
+{
+ BoundSphere bsphere;
+ pchan_culling_calc_bsphere(ob, pchan, &bsphere);
+ bsphere.radius *= scale;
+ return DRW_culling_sphere_test(view, &bsphere);
+}
+
+static bool pchan_culling_test_custom(const DRWView *view,
+ const Object *ob,
+ const bPoseChannel *pchan)
+{
+ /* For more aggressive culling the bounding box of the custom-object could be used. */
+ return pchan_culling_test_simple(view, ob, pchan);
+}
+
+static bool pchan_culling_test_wire(const DRWView *view,
+ const Object *ob,
+ const bPoseChannel *pchan)
+{
+ BLI_assert(((const bArmature *)ob->data)->drawtype == ARM_WIRE);
+ return pchan_culling_test_simple(view, ob, pchan);
+}
+
+static bool pchan_culling_test_line(const DRWView *view,
+ const Object *ob,
+ const bPoseChannel *pchan)
+{
+ BLI_assert(((const bArmature *)ob->data)->drawtype == ARM_LINE);
+ /* Account for the end-points, as the line end-points size is in pixels, this is a rough value.
+ * Since the end-points are small the difference between having any margin or not is unlikely
+ * to be noticeable. */
+ const float scale = 1.1f;
+ return pchan_culling_test_with_radius_scale(view, ob, pchan, scale);
+}
+
+static bool pchan_culling_test_envelope(const DRWView *view,
+ const Object *ob,
+ const bPoseChannel *pchan)
+{
+ const bArmature *arm = ob->data;
+ BLI_assert(arm->drawtype == ARM_ENVELOPE);
+ BoundSphere bsphere;
+ pchan_culling_calc_bsphere(ob, pchan, &bsphere);
+ bsphere.radius += max_ff(pchan->bone->rad_head, pchan->bone->rad_tail) *
+ mat4_to_size_max_axis(ob->obmat) * mat4_to_size_max_axis(pchan->disp_mat);
+ return DRW_culling_sphere_test(view, &bsphere);
+}
+
+static bool pchan_culling_test_bbone(const DRWView *view,
+ const Object *ob,
+ const bPoseChannel *pchan)
+{
+ const bArmature *arm = ob->data;
+ BLI_assert(arm->drawtype == ARM_B_BONE);
+ const float ob_scale = mat4_to_size_max_axis(ob->obmat);
+ const Mat4 *bbones_mat = (const Mat4 *)pchan->draw_data->bbone_matrix;
+ for (int i = pchan->bone->segments; i--; bbones_mat++) {
+ BoundSphere bsphere;
+ float size[3];
+ mat4_to_size(size, bbones_mat->mat);
+ copy_v3_v3(bsphere.center, bbones_mat->mat[3]);
+ bsphere.radius = len_v3(size) * ob_scale;
+ if (DRW_culling_sphere_test(view, &bsphere)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool pchan_culling_test_octohedral(const DRWView *view,
+ const Object *ob,
+ const bPoseChannel *pchan)
+{
+ /* No type assertion as this is a fallback (files from the future will end up here). */
+ /* Account for spheres on the end-points. */
+ const float scale = 1.2f;
+ return pchan_culling_test_with_radius_scale(view, ob, pchan, scale);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Main Draw Loops
* \{ */
@@ -2084,7 +2198,9 @@ static void draw_armature_edit(ArmatureDrawContext *ctx)
boneflag &= ~BONE_DRAW_LOCKED_WEIGHT;
- draw_bone_relations(ctx, eBone, NULL, arm, boneflag, constflag);
+ if (!is_select) {
+ draw_bone_relations(ctx, eBone, NULL, arm, boneflag, constflag);
+ }
if (arm->drawtype == ARM_ENVELOPE) {
draw_bone_update_disp_matrix_default(eBone, NULL);
@@ -2107,12 +2223,14 @@ static void draw_armature_edit(ArmatureDrawContext *ctx)
draw_bone_octahedral(ctx, eBone, NULL, arm, boneflag, constflag, select_id);
}
- if (show_text && (arm->flag & ARM_DRAWNAMES)) {
- draw_bone_name(ctx, eBone, NULL, arm, boneflag);
- }
+ if (!is_select) {
+ if (show_text && (arm->flag & ARM_DRAWNAMES)) {
+ draw_bone_name(ctx, eBone, NULL, arm, boneflag);
+ }
- if (arm->flag & ARM_DRAWAXES) {
- draw_axes(ctx, eBone, NULL, arm);
+ if (arm->flag & ARM_DRAWAXES) {
+ draw_axes(ctx, eBone, NULL, arm);
+ }
}
}
}
@@ -2187,6 +2305,8 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
}
}
+ const DRWView *view = is_pose_select ? DRW_view_default_get() : NULL;
+
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next, index += 0x10000) {
Bone *bone = pchan->bone;
const bool bone_visible = (bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)) == 0;
@@ -2221,43 +2341,60 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
boneflag &= ~BONE_DRAW_LOCKED_WEIGHT;
}
- draw_bone_relations(ctx, NULL, pchan, arm, boneflag, constflag);
+ if (!is_pose_select) {
+ draw_bone_relations(ctx, NULL, pchan, arm, boneflag, constflag);
+ }
if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) {
draw_bone_update_disp_matrix_custom(pchan);
- draw_bone_custom_shape(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ if (!is_pose_select || pchan_culling_test_custom(view, ob, pchan)) {
+ draw_bone_custom_shape(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ }
}
else if (arm->drawtype == ARM_ENVELOPE) {
draw_bone_update_disp_matrix_default(NULL, pchan);
- draw_bone_envelope(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ if (!is_pose_select || pchan_culling_test_envelope(view, ob, pchan)) {
+ draw_bone_envelope(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ }
}
else if (arm->drawtype == ARM_LINE) {
draw_bone_update_disp_matrix_default(NULL, pchan);
- draw_bone_line(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ if (!is_pose_select || pchan_culling_test_line(view, ob, pchan)) {
+ draw_bone_line(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ }
}
else if (arm->drawtype == ARM_WIRE) {
draw_bone_update_disp_matrix_bbone(NULL, pchan);
- draw_bone_wire(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ if (!is_pose_select || pchan_culling_test_wire(view, ob, pchan)) {
+ draw_bone_wire(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ }
}
else if (arm->drawtype == ARM_B_BONE) {
draw_bone_update_disp_matrix_bbone(NULL, pchan);
- draw_bone_box(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ if (!is_pose_select || pchan_culling_test_bbone(view, ob, pchan)) {
+ draw_bone_box(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ }
}
else {
draw_bone_update_disp_matrix_default(NULL, pchan);
- draw_bone_octahedral(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ if (!is_pose_select || pchan_culling_test_octohedral(view, ob, pchan)) {
+ draw_bone_octahedral(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ }
}
- if (draw_dofs) {
- draw_bone_degrees_of_freedom(ctx, pchan);
- }
+ /* These aren't included in the selection. */
+ if (!is_pose_select) {
+ if (draw_dofs) {
+ draw_bone_degrees_of_freedom(ctx, pchan);
+ }
- if (show_text && (arm->flag & ARM_DRAWNAMES)) {
- draw_bone_name(ctx, NULL, pchan, arm, boneflag);
- }
+ if (show_text && (arm->flag & ARM_DRAWNAMES)) {
+ draw_bone_name(ctx, NULL, pchan, arm, boneflag);
+ }
- if (arm->flag & ARM_DRAWAXES) {
- draw_axes(ctx, NULL, pchan, arm);
+ if (arm->flag & ARM_DRAWAXES) {
+ draw_axes(ctx, NULL, pchan, arm);
+ }
}
}
}
@@ -2365,9 +2502,6 @@ static bool POSE_is_driven_by_active_armature(Object *ob)
if (ob_arm) {
const DRWContextState *draw_ctx = DRW_context_state_get();
bool is_active = OVERLAY_armature_is_pose_mode(ob_arm, draw_ctx);
- if (!is_active && ob_arm->proxy_from) {
- is_active = OVERLAY_armature_is_pose_mode(ob_arm->proxy_from, draw_ctx);
- }
return is_active;
}
diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
index 3a2871249a2..ad929cc0835 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
@@ -28,6 +28,7 @@
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
+#include "BKE_object.h"
#include "draw_cache_impl.h"
#include "draw_manager_text.h"
@@ -229,7 +230,10 @@ static void overlay_edit_mesh_add_ob_to_pass(OVERLAY_PrivateData *pd, Object *ob
Mesh *me = (Mesh *)ob->data;
BMEditMesh *embm = me->edit_mesh;
if (embm) {
- has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final);
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob);
+ Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob);
+
+ has_edit_mesh_cage = editmesh_eval_cage && (editmesh_eval_cage != editmesh_eval_final);
has_skin_roots = CustomData_get_offset(&embm->bm->vdata, CD_MVERT_SKIN) != -1;
}
diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c
index 983df1ceac8..f51df908fbf 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_uv.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c
@@ -412,7 +412,7 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
draw_ctx->view_layer, NULL, &objects_len, draw_ctx->object_mode);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *object_eval = DEG_get_evaluated_object(draw_ctx->depsgraph, objects[ob_index]);
- DRW_mesh_batch_cache_validate((Mesh *)object_eval->data);
+ DRW_mesh_batch_cache_validate(object_eval, (Mesh *)object_eval->data);
overlay_edit_uv_cache_populate(vedata, object_eval);
}
MEM_freeN(objects);
@@ -441,22 +441,22 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (has_active_edit_uvmap) {
if (pd->edit_uv.do_uv_overlay) {
- geom = DRW_mesh_batch_cache_get_edituv_edges(ob->data);
+ geom = DRW_mesh_batch_cache_get_edituv_edges(ob, ob->data);
if (geom) {
DRW_shgroup_call_obmat(pd->edit_uv_edges_grp, geom, NULL);
}
- geom = DRW_mesh_batch_cache_get_edituv_verts(ob->data);
+ geom = DRW_mesh_batch_cache_get_edituv_verts(ob, ob->data);
if (geom) {
DRW_shgroup_call_obmat(pd->edit_uv_verts_grp, geom, NULL);
}
if (pd->edit_uv.do_faces) {
- geom = DRW_mesh_batch_cache_get_edituv_faces(ob->data);
+ geom = DRW_mesh_batch_cache_get_edituv_faces(ob, ob->data);
if (geom) {
DRW_shgroup_call_obmat(pd->edit_uv_faces_grp, geom, NULL);
}
}
if (pd->edit_uv.do_face_dots) {
- geom = DRW_mesh_batch_cache_get_edituv_facedots(ob->data);
+ geom = DRW_mesh_batch_cache_get_edituv_facedots(ob, ob->data);
if (geom) {
DRW_shgroup_call_obmat(pd->edit_uv_face_dots_grp, geom, NULL);
}
@@ -465,14 +465,14 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (pd->edit_uv.do_uv_stretching_overlay) {
if (pd->edit_uv.draw_type == SI_UVDT_STRETCH_ANGLE) {
- geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(me);
+ geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(ob, me);
}
else /* SI_UVDT_STRETCH_AREA */ {
OVERLAY_StretchingAreaTotals *totals = MEM_mallocN(sizeof(OVERLAY_StretchingAreaTotals),
__func__);
BLI_addtail(&pd->edit_uv.totals, totals);
geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_area(
- me, &totals->total_area, &totals->total_area_uv);
+ ob, me, &totals->total_area, &totals->total_area_uv);
}
if (geom) {
DRW_shgroup_call_obmat(pd->edit_uv_stretching_grp, geom, NULL);
@@ -482,7 +482,7 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (draw_shadows && (has_active_object_uvmap || has_active_edit_uvmap)) {
if (pd->edit_uv.do_uv_shadow_overlay) {
- geom = DRW_mesh_batch_cache_get_uv_edges(ob->data);
+ geom = DRW_mesh_batch_cache_get_uv_edges(ob, ob->data);
if (geom) {
DRW_shgroup_call_obmat(pd->edit_uv_shadow_edges_grp, geom, NULL);
}
diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c
index 12db2bd02cf..54e8ef80854 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.c
@@ -262,7 +262,7 @@ static bool overlay_object_is_edit_mode(const OVERLAY_PrivateData *pd, const Obj
return pd->ctx_mode == CTX_MODE_EDIT_METABALL;
case OB_FONT:
return pd->ctx_mode == CTX_MODE_EDIT_TEXT;
- case OB_HAIR:
+ case OB_CURVES:
case OB_POINTCLOUD:
case OB_VOLUME:
/* No edit mode yet. */
@@ -316,7 +316,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
OB_MBALL,
OB_FONT,
OB_GPENCIL,
- OB_HAIR,
+ OB_CURVES,
OB_POINTCLOUD,
OB_VOLUME);
const bool draw_surface = (ob->dt >= OB_WIRE) && (renderable || (ob->dt == OB_WIRE));
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index a2362cd8850..de0003625a2 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -484,7 +484,7 @@ static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, cons
texcosize = mb->size;
break;
}
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO: {
/* No user defined texture space support. */
diff --git a/source/blender/draw/engines/overlay/overlay_fade.c b/source/blender/draw/engines/overlay/overlay_fade.c
index 0971021f1c0..557a8976ff7 100644
--- a/source/blender/draw/engines/overlay/overlay_fade.c
+++ b/source/blender/draw/engines/overlay/overlay_fade.c
@@ -43,7 +43,6 @@ void OVERLAY_fade_cache_init(OVERLAY_Data *vedata)
GPUShader *sh = OVERLAY_shader_uniform_color();
pd->fade_grp[i] = DRW_shgroup_create(sh, psl->fade_ps[i]);
- DRW_shgroup_uniform_block(pd->fade_grp[i], "globalsBlock", G_draw.block_ubo);
const DRWContextState *draw_ctx = DRW_context_state_get();
float color[4];
diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c
index 9899dce5df6..4c09349c35d 100644
--- a/source/blender/draw/engines/overlay/overlay_shader.c
+++ b/source/blender/draw/engines/overlay/overlay_shader.c
@@ -385,7 +385,7 @@ GPUShader *OVERLAY_shader_armature_sphere(bool use_outline)
const DRWContextState *draw_ctx = DRW_context_state_get();
const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg];
OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
- const char extensions[] = "#extension GL_ARB_conservative_depth : enable\n";
+ const char extensions[] = "";
if (use_outline && !sh_data->armature_sphere_outline) {
sh_data->armature_sphere_outline = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg->lib,
diff --git a/source/blender/draw/engines/overlay/overlay_volume.c b/source/blender/draw/engines/overlay/overlay_volume.c
index ad0ccf1c7c4..b13351984a3 100644
--- a/source/blender/draw/engines/overlay/overlay_volume.c
+++ b/source/blender/draw/engines/overlay/overlay_volume.c
@@ -37,7 +37,6 @@ void OVERLAY_volume_cache_init(OVERLAY_Data *vedata)
GPUShader *sh = OVERLAY_shader_depth_only();
DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->volume_ps);
pd->volume_selection_surface_grp = grp;
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
}
else {
psl->volume_ps = NULL;
diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c
index 449130c4c5b..1eb8fc981cf 100644
--- a/source/blender/draw/engines/overlay/overlay_wireframe.c
+++ b/source/blender/draw/engines/overlay/overlay_wireframe.c
@@ -185,10 +185,11 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
Mesh *me = ob->data;
if (is_edit_mode) {
BLI_assert(me->edit_mesh);
- BMEditMesh *embm = me->edit_mesh;
- has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final);
- if (embm->mesh_eval_final) {
- me = embm->mesh_eval_final;
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob);
+ Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob);
+ has_edit_mesh_cage = editmesh_eval_cage && (editmesh_eval_cage != editmesh_eval_final);
+ if (editmesh_eval_final) {
+ me = editmesh_eval_final;
}
}
is_mesh_verts_only = me->totedge == 0 && me->totvert > 0;
diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl
index f7792dc0371..8f90c1acbbb 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_frag.glsl
@@ -40,7 +40,7 @@ void main()
}
else if (lineStyle == OVERLAY_UV_LINE_STYLE_DASH) {
if (fract(line_distance / dashLength) < 0.5) {
- inner_color = mix(vec4(1.0), colorEdgeSelect, selectionFac_f);
+ inner_color = mix(vec4(vec3(0.35), 1.0), colorEdgeSelect, selectionFac_f);
}
}
else if (lineStyle == OVERLAY_UV_LINE_STYLE_BLACK) {
diff --git a/source/blender/draw/engines/select/select_draw_utils.c b/source/blender/draw/engines/select/select_draw_utils.c
index e9930dbdb30..2801f2d7720 100644
--- a/source/blender/draw/engines/select/select_draw_utils.c
+++ b/source/blender/draw/engines/select/select_draw_utils.c
@@ -49,7 +49,7 @@ void select_id_object_min_max(Object *obj, float r_min[3], float r_max[3])
BoundBox *bb;
BMEditMesh *em = BKE_editmesh_from_object(obj);
if (em) {
- bb = BKE_editmesh_cage_boundbox_get(em);
+ bb = BKE_editmesh_cage_boundbox_get(obj, em);
}
else {
bb = BKE_object_boundbox_get(obj);
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_composite_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_composite_info.hh
new file mode 100644
index 00000000000..e93f241ad3c
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_composite_info.hh
@@ -0,0 +1,41 @@
+
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Base Composite
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_composite)
+ .sampler(0, ImageType::FLOAT_2D, "normalBuffer", Frequency::PASS)
+ .sampler(1, ImageType::FLOAT_2D, "materialBuffer", Frequency::PASS)
+ .uniform_buf(4, "WorldData", "world_data", Frequency::PASS)
+ .push_constant(Type::BOOL, "forceShadowing")
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .typedef_source("workbench_shader_shared.h")
+ .fragment_source("workbench_composite_frag.glsl")
+ .additional_info("draw_fullscreen", "draw_view");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Lighting Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_composite_studio)
+ .define("V3D_LIGHTING_STUDIO")
+ .additional_info("workbench_composite")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(workbench_composite_matcap)
+ .define("V3D_LIGHTING_MATCAP")
+ .sampler(2, ImageType::FLOAT_2D, "matcap_diffuse_tx", Frequency::PASS)
+ .sampler(3, ImageType::FLOAT_2D, "matcap_specular_tx", Frequency::PASS)
+ .additional_info("workbench_composite")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(workbench_composite_flat)
+ .define("V3D_LIGHTING_FLAT")
+ .additional_info("workbench_composite")
+ .do_static_compilation(true);
+
+/** \} */
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh
new file mode 100644
index 00000000000..3e0124546b0
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh
@@ -0,0 +1,64 @@
+
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name TAA
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_taa)
+ .sampler(0, ImageType::FLOAT_2D, "colorBuffer")
+ .push_constant(Type::FLOAT, "samplesWeights", 9)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .fragment_source("workbench_effect_taa_frag.glsl")
+ .additional_info("draw_fullscreen")
+ .do_static_compilation(true);
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name SMAA
+ * \{ */
+
+GPU_SHADER_INTERFACE_INFO(workbench_smaa_iface, "")
+ .smooth(Type::VEC2, "uvs")
+ .smooth(Type::VEC2, "pixcoord")
+ .smooth(Type::VEC4, "offset[3]");
+
+GPU_SHADER_CREATE_INFO(workbench_smaa)
+ .define("SMAA_GLSL_3")
+ .define("SMAA_RT_METRICS", "viewportMetrics")
+ .define("SMAA_PRESET_HIGH")
+ .define("SMAA_LUMA_WEIGHT", "float4(1.0, 1.0, 1.0, 1.0)")
+ .define("SMAA_NO_DISCARD")
+ .vertex_out(workbench_smaa_iface)
+ .push_constant(Type::VEC4, "viewportMetrics")
+ .vertex_source("workbench_effect_smaa_vert.glsl")
+ .fragment_source("workbench_effect_smaa_frag.glsl");
+
+GPU_SHADER_CREATE_INFO(workbench_smaa_stage_0)
+ .define("SMAA_STAGE", "0")
+ .sampler(0, ImageType::FLOAT_2D, "colorTex")
+ .fragment_out(0, Type::VEC2, "out_edges")
+ .additional_info("workbench_smaa")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(workbench_smaa_stage_1)
+ .define("SMAA_STAGE", "1")
+ .sampler(0, ImageType::FLOAT_2D, "edgesTex")
+ .sampler(1, ImageType::FLOAT_2D, "areaTex")
+ .sampler(2, ImageType::FLOAT_2D, "searchTex")
+ .fragment_out(0, Type::VEC4, "out_weights")
+ .additional_info("workbench_smaa")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(workbench_smaa_stage_2)
+ .define("SMAA_STAGE", "2")
+ .sampler(0, ImageType::FLOAT_2D, "colorTex")
+ .sampler(1, ImageType::FLOAT_2D, "blendTex")
+ .push_constant(Type::FLOAT, "mixFactor")
+ .push_constant(Type::FLOAT, "taaAccumulatedWeight")
+ .fragment_out(0, Type::VEC4, "out_color")
+ .additional_info("workbench_smaa")
+ .do_static_compilation(true);
+
+/** \} */
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh
index 31e5f5e7641..29468d6002a 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_info.hh
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh
@@ -3,10 +3,9 @@
GPU_SHADER_CREATE_INFO(workbench_effect_cavity_common)
.fragment_out(0, Type::VEC4, "fragColor")
- .sampler(0, ImageType::FLOAT_2D, "depthBuffer")
- .sampler(1, ImageType::FLOAT_2D, "normalBuffer")
- .sampler(2, ImageType::UINT_2D, "objectIdBuffer")
- .uniform_buf(3, "vec4", "samples_coords[512]")
+ .sampler(0, ImageType::FLOAT_2D, "normalBuffer")
+ .uniform_buf(4, "WorldData", "world_data", Frequency::PASS)
+ .typedef_source("workbench_shader_shared.h")
.fragment_source("workbench_effect_cavity_frag.glsl")
.additional_info("draw_fullscreen")
.additional_info("draw_view");
@@ -14,15 +13,23 @@ GPU_SHADER_CREATE_INFO(workbench_effect_cavity_common)
GPU_SHADER_CREATE_INFO(workbench_effect_cavity)
.do_static_compilation(true)
.define("USE_CAVITY")
+ .uniform_buf(3, "vec4", "samples_coords[512]")
+ .sampler(1, ImageType::DEPTH_2D, "depthBuffer")
+ .sampler(2, ImageType::FLOAT_2D, "cavityJitter")
.additional_info("workbench_effect_cavity_common");
GPU_SHADER_CREATE_INFO(workbench_effect_curvature)
.do_static_compilation(true)
.define("USE_CURVATURE")
+ .sampler(1, ImageType::UINT_2D, "objectIdBuffer")
.additional_info("workbench_effect_cavity_common");
GPU_SHADER_CREATE_INFO(workbench_effect_cavity_curvature)
.do_static_compilation(true)
.define("USE_CAVITY")
.define("USE_CURVATURE")
+ .uniform_buf(3, "vec4", "samples_coords[512]")
+ .sampler(1, ImageType::DEPTH_2D, "depthBuffer")
+ .sampler(2, ImageType::FLOAT_2D, "cavityJitter")
+ .sampler(3, ImageType::UINT_2D, "objectIdBuffer")
.additional_info("workbench_effect_cavity_common");
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh
new file mode 100644
index 00000000000..252a7d4f3a3
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh
@@ -0,0 +1,55 @@
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(workbench_effect_dof)
+ /* TODO(fclem): Split resources per stage. */
+ .sampler(0, ImageType::FLOAT_2D, "inputCocTex")
+ .sampler(1, ImageType::FLOAT_2D, "maxCocTilesTex")
+ .sampler(2, ImageType::FLOAT_2D, "sceneColorTex")
+ .sampler(3, ImageType::FLOAT_2D, "sceneDepthTex")
+ .sampler(4, ImageType::FLOAT_2D, "backgroundTex")
+ .sampler(5, ImageType::FLOAT_2D, "halfResColorTex")
+ .sampler(6, ImageType::FLOAT_2D, "blurTex")
+ .sampler(7, ImageType::FLOAT_2D, "noiseTex")
+ .push_constant(Type::VEC2, "invertedViewportSize")
+ .push_constant(Type::VEC2, "nearFar")
+ .push_constant(Type::VEC3, "dofParams")
+ .push_constant(Type::FLOAT, "noiseOffset")
+ .fragment_source("workbench_effect_dof_frag.glsl")
+ .additional_info("draw_fullscreen")
+ .additional_info("draw_view");
+
+GPU_SHADER_CREATE_INFO(workbench_effect_dof_prepare)
+ .define("PREPARE")
+ .fragment_out(0, Type::VEC4, "halfResColor")
+ .fragment_out(1, Type::VEC2, "normalizedCoc")
+ .additional_info("workbench_effect_dof")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(workbench_effect_dof_downsample)
+ .define("DOWNSAMPLE")
+ .fragment_out(0, Type::VEC4, "outColor")
+ .fragment_out(1, Type::VEC2, "outCocs")
+ .additional_info("workbench_effect_dof")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(workbench_effect_dof_blur1)
+ .define("BLUR1")
+ .define("NUM_SAMPLES", "49")
+ .uniform_buf(1, "vec4", "samples[49]")
+ .fragment_out(0, Type::VEC4, "blurColor")
+ .additional_info("workbench_effect_dof")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(workbench_effect_dof_blur2)
+ .define("BLUR2")
+ .fragment_out(0, Type::VEC4, "finalColor")
+ .additional_info("workbench_effect_dof")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(workbench_effect_dof_resolve)
+ .define("RESOLVE")
+ .fragment_out(0, Type::VEC4, "finalColorAdd", DualBlend::SRC_0)
+ .fragment_out(0, Type::VEC4, "finalColorMul", DualBlend::SRC_1)
+ .additional_info("workbench_effect_dof")
+ .do_static_compilation(true); \ No newline at end of file
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh
new file mode 100644
index 00000000000..3849fe57a25
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh
@@ -0,0 +1,11 @@
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(workbench_effect_outline)
+ .typedef_source("workbench_shader_shared.h")
+ .fragment_source("workbench_effect_outline_frag.glsl")
+ .sampler(0, ImageType::UINT_2D, "objectIdBuffer")
+ .uniform_buf(4, "WorldData", "world_data", Frequency::PASS)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .additional_info("draw_fullscreen")
+ .do_static_compilation(true);
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh
new file mode 100644
index 00000000000..78403c292f0
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh
@@ -0,0 +1,9 @@
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(workbench_merge_infront)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .sampler(0, ImageType::DEPTH_2D, "depthBuffer")
+ .fragment_source("workbench_merge_infront_frag.glsl")
+ .additional_info("draw_fullscreen")
+ .do_static_compilation(true);
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh
new file mode 100644
index 00000000000..29eadc8048a
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh
@@ -0,0 +1,149 @@
+
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Object Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_mesh)
+ .vertex_in(0, Type::VEC3, "pos")
+ .vertex_in(1, Type::VEC3, "nor")
+ .vertex_in(2, Type::VEC4, "ac")
+ .vertex_in(3, Type::VEC2, "au")
+ .vertex_source("workbench_prepass_vert.glsl")
+ .additional_info("draw_mesh")
+ .additional_info("draw_resource_handle");
+
+GPU_SHADER_CREATE_INFO(workbench_hair)
+ .sampler(0, ImageType::FLOAT_BUFFER, "ac", Frequency::BATCH)
+ .sampler(1, ImageType::FLOAT_BUFFER, "au", Frequency::BATCH)
+ .vertex_source("workbench_prepass_hair_vert.glsl")
+ .additional_info("draw_hair")
+ .additional_info("draw_resource_handle");
+
+GPU_SHADER_CREATE_INFO(workbench_pointcloud)
+ .vertex_source("workbench_prepass_pointcloud_vert.glsl")
+ .additional_info("draw_pointcloud")
+ .additional_info("draw_resource_handle");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Texture Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_texture_none).define("TEXTURE_NONE");
+
+GPU_SHADER_CREATE_INFO(workbench_texture_single)
+ .sampler(2, ImageType::FLOAT_2D, "imageTexture", Frequency::BATCH)
+ .push_constant(Type::BOOL, "imagePremult")
+ .push_constant(Type::FLOAT, "imageTransparencyCutoff")
+ .define("V3D_SHADING_TEXTURE_COLOR");
+
+GPU_SHADER_CREATE_INFO(workbench_texture_tile)
+ .sampler(2, ImageType::FLOAT_2D_ARRAY, "imageTileArray", Frequency::BATCH)
+ .sampler(3, ImageType::FLOAT_1D_ARRAY, "imageTileData", Frequency::BATCH)
+ .push_constant(Type::BOOL, "imagePremult")
+ .push_constant(Type::FLOAT, "imageTransparencyCutoff")
+ .define("V3D_SHADING_TEXTURE_COLOR")
+ .define("TEXTURE_IMAGE_ARRAY");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Lighting Type (only for transparent)
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_lighting_flat).define("V3D_LIGHTING_FLAT");
+GPU_SHADER_CREATE_INFO(workbench_lighting_studio).define("V3D_LIGHTING_STUDIO");
+GPU_SHADER_CREATE_INFO(workbench_lighting_matcap)
+ .define("V3D_LIGHTING_MATCAP")
+ .sampler(4, ImageType::FLOAT_2D, "matcap_diffuse_tx", Frequency::PASS)
+ .sampler(5, ImageType::FLOAT_2D, "matcap_specular_tx", Frequency::PASS);
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material Interface
+ * \{ */
+
+GPU_SHADER_INTERFACE_INFO(workbench_material_iface, "")
+ .smooth(Type::VEC3, "normal_interp")
+ .smooth(Type::VEC3, "color_interp")
+ .smooth(Type::FLOAT, "alpha_interp")
+ .smooth(Type::VEC2, "uv_interp")
+ .flat(Type::INT, "object_id")
+ .flat(Type::FLOAT, "roughness")
+ .flat(Type::FLOAT, "metallic");
+
+GPU_SHADER_CREATE_INFO(workbench_material)
+ .uniform_buf(4, "WorldData", "world_data", Frequency::PASS)
+ .uniform_buf(5, "vec4", "materials_data[4096]", Frequency::PASS)
+ .push_constant(Type::INT, "materialIndex")
+ .push_constant(Type::BOOL, "useMatcap")
+ .vertex_out(workbench_material_iface);
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pipeline Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_transparent_accum)
+ /* Note: Blending will be skipped on objectId because output is a
+ non-normalized integer buffer. */
+ .fragment_out(0, Type::VEC4, "transparentAccum")
+ .fragment_out(1, Type::VEC4, "revealageAccum")
+ .fragment_out(2, Type::UINT, "objectId")
+ .push_constant(Type::BOOL, "forceShadowing")
+ .typedef_source("workbench_shader_shared.h")
+ .fragment_source("workbench_transparent_accum_frag.glsl");
+
+GPU_SHADER_CREATE_INFO(workbench_opaque)
+ .fragment_out(0, Type::VEC4, "materialData")
+ .fragment_out(1, Type::VEC2, "normalData")
+ .fragment_out(2, Type::UINT, "objectId")
+ .typedef_source("workbench_shader_shared.h")
+ .fragment_source("workbench_prepass_frag.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Variations Declaration
+ * \{ */
+
+#define WORKBENCH_FINAL_VARIATION(name, ...) \
+ GPU_SHADER_CREATE_INFO(name).additional_info(__VA_ARGS__).do_static_compilation(true);
+
+#define WORKBENCH_CLIPPING_VARIATIONS(prefix, ...) \
+ WORKBENCH_FINAL_VARIATION(prefix##_clip, "drw_clipped", __VA_ARGS__) \
+ WORKBENCH_FINAL_VARIATION(prefix##_no_clip, __VA_ARGS__)
+
+#define WORKBENCH_TEXTURE_VARIATIONS(prefix, ...) \
+ WORKBENCH_CLIPPING_VARIATIONS(prefix##_tex_none, "workbench_texture_none", __VA_ARGS__) \
+ WORKBENCH_CLIPPING_VARIATIONS(prefix##_tex_single, "workbench_texture_single", __VA_ARGS__) \
+ WORKBENCH_CLIPPING_VARIATIONS(prefix##_tex_tile, "workbench_texture_tile", __VA_ARGS__)
+
+#define WORKBENCH_DATATYPE_VARIATIONS(prefix, ...) \
+ WORKBENCH_TEXTURE_VARIATIONS(prefix##_mesh, "workbench_mesh", __VA_ARGS__) \
+ WORKBENCH_TEXTURE_VARIATIONS(prefix##_hair, "workbench_hair", __VA_ARGS__) \
+ WORKBENCH_TEXTURE_VARIATIONS(prefix##_ptcloud, "workbench_pointcloud", __VA_ARGS__)
+
+#define WORKBENCH_PIPELINE_VARIATIONS(prefix, ...) \
+ WORKBENCH_DATATYPE_VARIATIONS(prefix##_transp_studio, \
+ "workbench_transparent_accum", \
+ "workbench_lighting_studio", \
+ __VA_ARGS__) \
+ WORKBENCH_DATATYPE_VARIATIONS(prefix##_transp_matcap, \
+ "workbench_transparent_accum", \
+ "workbench_lighting_matcap", \
+ __VA_ARGS__) \
+ WORKBENCH_DATATYPE_VARIATIONS(prefix##_transp_flat, \
+ "workbench_transparent_accum", \
+ "workbench_lighting_flat", \
+ __VA_ARGS__) \
+ WORKBENCH_DATATYPE_VARIATIONS(prefix##_opaque, "workbench_opaque", __VA_ARGS__)
+
+WORKBENCH_PIPELINE_VARIATIONS(workbench, "workbench_material");
+
+/** \} */
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_shadow_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_shadow_info.hh
new file mode 100644
index 00000000000..c26d3c3aaf8
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_shadow_info.hh
@@ -0,0 +1,98 @@
+
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Common
+ * \{ */
+
+GPU_SHADER_INTERFACE_INFO(workbench_shadow_iface, "vData")
+ .smooth(Type::VEC3, "pos")
+ .smooth(Type::VEC4, "frontPosition")
+ .smooth(Type::VEC4, "backPosition");
+
+GPU_SHADER_CREATE_INFO(workbench_shadow_common)
+ .vertex_in(0, Type::VEC3, "pos")
+ .vertex_out(workbench_shadow_iface)
+ .push_constant(Type::FLOAT, "lightDistance")
+ .push_constant(Type::VEC3, "lightDirection")
+ .vertex_source("workbench_shadow_vert.glsl")
+ .additional_info("draw_mesh");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Manifold Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_shadow_manifold)
+ .geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::TRIANGLE_STRIP, 4, 1)
+ .geometry_source("workbench_shadow_geom.glsl");
+
+GPU_SHADER_CREATE_INFO(workbench_shadow_no_manifold)
+ .geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::TRIANGLE_STRIP, 4, 2)
+ .geometry_source("workbench_shadow_geom.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Caps Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_shadow_caps)
+ .geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3, 2)
+ .geometry_source("workbench_shadow_caps_geom.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Debug Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_shadow_no_debug)
+ .fragment_source("gpu_shader_depth_only_frag.glsl");
+
+GPU_SHADER_CREATE_INFO(workbench_shadow_debug)
+ .fragment_out(0, Type::VEC4, "materialData")
+ .fragment_out(1, Type::VEC4, "normalData")
+ .fragment_out(2, Type::UINT, "objectId")
+ .fragment_source("workbench_shadow_debug_frag.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Variations Declaration
+ * \{ */
+
+#define WORKBENCH_SHADOW_VARIATIONS(suffix, ...) \
+ GPU_SHADER_CREATE_INFO(workbench_shadow_pass_manifold_no_caps##suffix) \
+ .define("SHADOW_PASS") \
+ .additional_info("workbench_shadow_common", "workbench_shadow_manifold", __VA_ARGS__) \
+ .do_static_compilation(true); \
+ GPU_SHADER_CREATE_INFO(workbench_shadow_pass_no_manifold_no_caps##suffix) \
+ .define("SHADOW_PASS") \
+ .define("DOUBLE_MANIFOLD") \
+ .additional_info("workbench_shadow_common", "workbench_shadow_no_manifold", __VA_ARGS__) \
+ .do_static_compilation(true); \
+ GPU_SHADER_CREATE_INFO(workbench_shadow_fail_manifold_caps##suffix) \
+ .define("SHADOW_FAIL") \
+ .additional_info("workbench_shadow_common", "workbench_shadow_caps", __VA_ARGS__) \
+ .do_static_compilation(true); \
+ GPU_SHADER_CREATE_INFO(workbench_shadow_fail_manifold_no_caps##suffix) \
+ .define("SHADOW_FAIL") \
+ .additional_info("workbench_shadow_common", "workbench_shadow_manifold", __VA_ARGS__) \
+ .do_static_compilation(true); \
+ GPU_SHADER_CREATE_INFO(workbench_shadow_fail_no_manifold_caps##suffix) \
+ .define("SHADOW_FAIL") \
+ .define("DOUBLE_MANIFOLD") \
+ .additional_info("workbench_shadow_common", "workbench_shadow_caps", __VA_ARGS__) \
+ .do_static_compilation(true); \
+ GPU_SHADER_CREATE_INFO(workbench_shadow_fail_no_manifold_no_caps##suffix) \
+ .define("SHADOW_FAIL") \
+ .define("DOUBLE_MANIFOLD") \
+ .additional_info("workbench_shadow_common", "workbench_shadow_no_manifold", __VA_ARGS__) \
+ .do_static_compilation(true);
+
+WORKBENCH_SHADOW_VARIATIONS(, "workbench_shadow_no_debug")
+WORKBENCH_SHADOW_VARIATIONS(_debug, "workbench_shadow_debug")
+
+/** \} */
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh
new file mode 100644
index 00000000000..e5b7bc8e2a7
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh
@@ -0,0 +1,10 @@
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(workbench_transparent_resolve)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .sampler(0, ImageType::FLOAT_2D, "transparentAccum")
+ .sampler(1, ImageType::FLOAT_2D, "transparentRevealage")
+ .fragment_source("workbench_transparent_resolve_frag.glsl")
+ .additional_info("draw_fullscreen")
+ .do_static_compilation(true);
diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh
new file mode 100644
index 00000000000..dd9492481ec
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh
@@ -0,0 +1,114 @@
+
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Volume shader base
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_volume)
+ .vertex_in(0, Type::VEC3, "pos")
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .sampler(0, ImageType::DEPTH_2D, "depthBuffer")
+ .sampler(1, ImageType::FLOAT_3D, "densityTexture")
+ .push_constant(Type::INT, "samplesLen")
+ .push_constant(Type::FLOAT, "noiseOfs")
+ .push_constant(Type::FLOAT, "stepLength")
+ .push_constant(Type::FLOAT, "densityScale")
+ .vertex_source("workbench_volume_vert.glsl")
+ .fragment_source("workbench_volume_frag.glsl")
+ .additional_info("draw_object_infos");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Smoke variation
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_volume_smoke)
+ .define("VOLUME_SMOKE")
+ .sampler(2, ImageType::FLOAT_3D, "flameTexture")
+ .sampler(3, ImageType::FLOAT_1D, "flameColorTexture")
+ .additional_info("draw_mesh", "draw_resource_id_varying");
+
+GPU_SHADER_CREATE_INFO(workbench_volume_object)
+ .define("VOLUME_OBJECT")
+ .push_constant(Type::MAT4, "volumeTextureToObject")
+ /* FIXME(fclem): This overflow the push_constant limit. */
+ .push_constant(Type::MAT4, "volumeObjectToTexture")
+ .additional_info("draw_volume", "draw_resource_id_varying");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Color Band variation
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_volume_coba)
+ .define("USE_COBA")
+ .sampler(4, ImageType::UINT_3D, "flagTexture")
+ .sampler(5, ImageType::FLOAT_1D, "transferTexture")
+ .push_constant(Type::BOOL, "showPhi")
+ .push_constant(Type::BOOL, "showFlags")
+ .push_constant(Type::BOOL, "showPressure")
+ .push_constant(Type::FLOAT, "gridScale");
+
+GPU_SHADER_CREATE_INFO(workbench_volume_no_coba)
+ .sampler(4, ImageType::FLOAT_3D, "shadowTexture")
+ .sampler(5, ImageType::UINT_2D, "transferTexture")
+ .push_constant(Type::VEC3, "activeColor");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sampling variation
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(workbench_volume_linear).define("USE_TRILINEAR");
+GPU_SHADER_CREATE_INFO(workbench_volume_cubic).define("USE_TRICUBIC");
+GPU_SHADER_CREATE_INFO(workbench_volume_closest).define("USE_CLOSEST");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Slice variation
+ * \{ */
+
+GPU_SHADER_INTERFACE_INFO(workbench_volume_iface, "").smooth(Type::VEC3, "localPos");
+
+GPU_SHADER_CREATE_INFO(workbench_volume_slice)
+ .define("VOLUME_SLICE")
+ .vertex_in(1, Type::VEC3, "uvs")
+ .vertex_out(workbench_volume_iface)
+ .push_constant(Type::INT, "sliceAxis") /* -1 is no slice. */
+ .push_constant(Type::FLOAT, "slicePosition");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Variations Declaration
+ * \{ */
+
+#define WORKBENCH_VOLUME_SLICE_VARIATIONS(prefix, ...) \
+ GPU_SHADER_CREATE_INFO(prefix##_slice) \
+ .additional_info("workbench_volume_slice", __VA_ARGS__) \
+ .do_static_compilation(true); \
+ GPU_SHADER_CREATE_INFO(prefix##_no_slice) \
+ .additional_info(__VA_ARGS__) \
+ .do_static_compilation(true);
+
+#define WORKBENCH_VOLUME_COBA_VARIATIONS(prefix, ...) \
+ WORKBENCH_VOLUME_SLICE_VARIATIONS(prefix##_coba, "workbench_volume_coba", __VA_ARGS__) \
+ WORKBENCH_VOLUME_SLICE_VARIATIONS(prefix##_no_coba, "workbench_volume_no_coba", __VA_ARGS__)
+
+#define WORKBENCH_VOLUME_INTERP_VARIATIONS(prefix, ...) \
+ WORKBENCH_VOLUME_COBA_VARIATIONS(prefix##_linear, "workbench_volume_linear", __VA_ARGS__) \
+ WORKBENCH_VOLUME_COBA_VARIATIONS(prefix##_cubic, "workbench_volume_cubic", __VA_ARGS__) \
+ WORKBENCH_VOLUME_COBA_VARIATIONS(prefix##_closest, "workbench_volume_closest", __VA_ARGS__)
+
+#define WORKBENCH_VOLUME_SMOKE_VARIATIONS(prefix, ...) \
+ WORKBENCH_VOLUME_INTERP_VARIATIONS(prefix##_smoke, "workbench_volume_smoke", __VA_ARGS__) \
+ WORKBENCH_VOLUME_INTERP_VARIATIONS(prefix##_object, "workbench_volume_object", __VA_ARGS__)
+
+WORKBENCH_VOLUME_SMOKE_VARIATIONS(workbench_volume, "workbench_volume")
+
+/** \} */
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
index 9038aae533b..880f17b0c9d 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
@@ -1,18 +1,12 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
-layout(std140) uniform samples_block
-{
- vec4 samples_coords[512];
-};
-
-uniform sampler2D cavityJitter;
-
/* From The Alchemy screen-space ambient obscurance algorithm
* http://graphics.cs.williams.edu/papers/AlchemyHPG11/VV11AlchemyAO.pdf */
+#ifdef USE_CAVITY
+
void cavity_compute(vec2 screenco,
sampler2D depthBuffer,
sampler2D normalBuffer,
@@ -98,3 +92,5 @@ void cavity_compute(vec2 screenco,
cavities = clamp(cavities * world_data.cavity_valley_factor, 0.0, 1.0);
edges = edges * world_data.cavity_ridge_factor;
}
+
+#endif /* USE_CAVITY */
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl
index c5b2ce0fd99..5e43fe27f38 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl
@@ -4,13 +4,6 @@
#pragma BLENDER_REQUIRE(workbench_matcap_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_world_light_lib.glsl)
-uniform sampler2D materialBuffer;
-uniform sampler2D normalBuffer;
-
-in vec4 uvcoordsvar;
-
-out vec4 fragColor;
-
void main()
{
/* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */
@@ -27,7 +20,7 @@ void main()
/* When using matcaps, mat_data.a is the back-face sign. */
N = (mat_data.a > 0.0) ? N : -N;
- fragColor.rgb = get_matcap_lighting(base_color, N, I);
+ fragColor.rgb = get_matcap_lighting(matcap_diffuse_tx, matcap_specular_tx, base_color, N, I);
#endif
#ifdef V3D_LIGHTING_STUDIO
@@ -38,7 +31,7 @@ void main()
fragColor.rgb = base_color;
#endif
- fragColor.rgb *= get_shadow(N);
+ fragColor.rgb *= get_shadow(N, forceShadowing);
fragColor.a = 1.0;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl
index a4d81393dbc..a6f7a1f522a 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl
@@ -1,5 +1,7 @@
-#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
+#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
+
+#ifdef USE_CURVATURE
float curvature_soft_clamp(float curvature, float control)
{
@@ -45,3 +47,5 @@ void curvature_compute(vec2 uv,
curvature = 2.0 * curvature_soft_clamp(normal_diff, world_data.curvature_ridge);
}
}
+
+#endif /* USE_CURVATURE */
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl
deleted file mode 100644
index c784c8b2db9..00000000000
--- a/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl
+++ /dev/null
@@ -1,48 +0,0 @@
-
-#ifndef WORKBENCH_SHADER_SHARED_H
-struct LightData {
- vec4 direction;
- vec4 specular_color;
- vec4 diffuse_color_wrap; /* rgb: diffuse col a: wrapped lighting factor */
-};
-
-struct WorldData {
- vec4 viewport_size;
- vec4 object_outline_color;
- vec4 shadow_direction_vs;
- float shadow_focus;
- float shadow_shift;
- float shadow_mul;
- float shadow_add;
- /* - 16 bytes alignment - */
- LightData lights[4];
- vec4 ambient_color;
-
- int cavity_sample_start;
- int cavity_sample_end;
- float cavity_sample_count_inv;
- float cavity_jitter_scale;
-
- float cavity_valley_factor;
- float cavity_ridge_factor;
- float cavity_attenuation;
- float cavity_distance;
-
- float curvature_ridge;
- float curvature_valley;
- float ui_scale;
- float _pad0;
-
- int matcap_orientation;
- bool use_specular;
- int _pad1;
- int _pad2;
-};
-
-# define viewport_size_inv viewport_size.zw
-
-layout(std140) uniform world_block
-{
- WorldData world_data;
-};
-#endif
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl
index 0b571040df5..59222b588a0 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl
@@ -4,18 +4,6 @@
#pragma BLENDER_REQUIRE(workbench_cavity_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_curvature_lib.glsl)
-#ifndef DRW_SHADER_SHARED_H
-
-uniform sampler2D depthBuffer;
-uniform sampler2D normalBuffer;
-uniform usampler2D objectIdBuffer;
-
-in vec4 uvcoordsvar;
-
-out vec4 fragColor;
-
-#endif
-
void main()
{
float cavity = 0.0, edges = 0.0, curvature = 0.0;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
index c4580e6ffc3..e9525ce9de0 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
@@ -7,19 +7,6 @@
* Converted and adapted from HLSL to GLSL by Clément Foucault
*/
-uniform vec2 invertedViewportSize;
-uniform vec2 nearFar;
-uniform vec3 dofParams;
-uniform float noiseOffset;
-uniform sampler2D inputCocTex;
-uniform sampler2D maxCocTilesTex;
-uniform sampler2D sceneColorTex;
-uniform sampler2D sceneDepthTex;
-uniform sampler2D backgroundTex;
-uniform sampler2D halfResColorTex;
-uniform sampler2D blurTex;
-uniform sampler2D noiseTex;
-
#define dof_aperturesize dofParams.x
#define dof_distance dofParams.y
#define dof_invsensorsize dofParams.z
@@ -53,9 +40,6 @@ float decode_signed_coc(vec2 cocs)
*/
#ifdef PREPARE
-layout(location = 0) out vec4 halfResColor;
-layout(location = 1) out vec2 normalizedCoc;
-
void main()
{
ivec4 texel = ivec4(gl_FragCoord.xyxy) * 2 + ivec4(0, 0, 1, 1);
@@ -99,9 +83,6 @@ void main()
*/
#ifdef DOWNSAMPLE
-layout(location = 0) out vec4 outColor;
-layout(location = 1) out vec2 outCocs;
-
void main()
{
vec4 texel = vec4(gl_FragCoord.xyxy) * 2.0 + vec4(0.0, 0.0, 1.0, 1.0);
@@ -216,14 +197,6 @@ void main()
* Outputs vertical blur and combined blur in MRT
*/
#ifdef BLUR1
-layout(location = 0) out vec4 blurColor;
-
-# define NUM_SAMPLES 49
-
-layout(std140) uniform dofSamplesBlock
-{
- vec4 samples[NUM_SAMPLES];
-};
vec2 get_random_vector(float offset)
{
@@ -308,7 +281,6 @@ void main()
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef BLUR2
-out vec4 finalColor;
void main()
{
@@ -385,9 +357,6 @@ void main()
*/
#ifdef RESOLVE
-layout(location = 0, index = 0) out vec4 finalColorAdd;
-layout(location = 0, index = 1) out vec4 finalColorMul;
-
void main()
{
/* Fullscreen pass */
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl
index fb6fdb93462..5d74933abf4 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl
@@ -1,12 +1,4 @@
-#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
-
-uniform usampler2D objectIdBuffer;
-
-in vec4 uvcoordsvar;
-
-out vec4 fragColor;
-
void main()
{
vec3 offset = vec3(world_data.viewport_size_inv, 0.0) * world_data.ui_scale;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl
index 9797a5e3301..8b9e3f968ea 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl
@@ -1,50 +1,35 @@
-uniform sampler2D edgesTex;
-uniform sampler2D areaTex;
-uniform sampler2D searchTex;
-uniform sampler2D blendTex;
-uniform sampler2D colorTex;
-uniform float mixFactor;
-uniform float taaAccumulatedWeight;
-
-in vec2 uvs;
-in vec2 pixcoord;
-in vec4 offset[3];
-
-#if SMAA_STAGE == 0
-out vec2 fragColor;
-#else
-out vec4 fragColor;
-#endif
+#pragma BLENDER_REQUIRE(common_smaa_lib.glsl)
void main()
{
#if SMAA_STAGE == 0
/* Detect edges in color and revealage buffer. */
- fragColor = SMAALumaEdgeDetectionPS(uvs, offset, colorTex);
+ out_edges = SMAALumaEdgeDetectionPS(uvs, offset, colorTex);
/* Discard if there is no edge. */
- if (dot(fragColor, float2(1.0, 1.0)) == 0.0) {
+ if (dot(out_edges, float2(1.0, 1.0)) == 0.0) {
discard;
}
#elif SMAA_STAGE == 1
- fragColor = SMAABlendingWeightCalculationPS(
+ out_weights = SMAABlendingWeightCalculationPS(
uvs, pixcoord, offset, edgesTex, areaTex, searchTex, vec4(0));
#elif SMAA_STAGE == 2
- fragColor = vec4(0.0);
+ out_color = vec4(0.0);
if (mixFactor > 0.0) {
- fragColor += SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex) * mixFactor;
+ out_color += SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex) * mixFactor;
}
if (mixFactor < 1.0) {
- fragColor += texture(colorTex, uvs) * (1.0 - mixFactor);
+ out_color += texture(colorTex, uvs) * (1.0 - mixFactor);
}
- fragColor /= taaAccumulatedWeight;
- fragColor = exp2(fragColor) - 0.5;
+ out_color /= taaAccumulatedWeight;
+ /* Exit log2 space used for Antialiasing. */
+ out_color = exp2(out_color) - 0.5;
/* Avoid float precision issue. */
- if (fragColor.a > 0.999) {
- fragColor.a = 1.0;
+ if (out_color.a > 0.999) {
+ out_color.a = 1.0;
}
#endif
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl
index 07734d19972..b76433a23e5 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl
@@ -1,7 +1,5 @@
-out vec2 uvs;
-out vec2 pixcoord;
-out vec4 offset[3];
+#pragma BLENDER_REQUIRE(common_smaa_lib.glsl)
void main()
{
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl
index d021e4696f7..0c4dee11756 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl
@@ -1,9 +1,4 @@
-uniform sampler2D colorBuffer;
-uniform float samplesWeights[9];
-
-out vec4 fragColor;
-
void main()
{
vec2 texel_size = 1.0 / vec2(textureSize(colorBuffer, 0));
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl
index 49e3f57ab2e..78782bdc777 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl
@@ -25,15 +25,6 @@ bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map)
return true;
}
-#ifndef WORKBENCH_SHADER_SHARED_H
-uniform sampler2DArray imageTileArray;
-uniform sampler1DArray imageTileData;
-uniform sampler2D imageTexture;
-
-uniform float imageTransparencyCutoff = 0.1;
-uniform bool imagePremult;
-#endif
-
vec3 workbench_image_color(vec2 uvs)
{
#ifdef V3D_SHADING_TEXTURE_COLOR
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl
index 2d18cc1b014..a0cec54251d 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl
@@ -1,6 +1,4 @@
-#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
-
vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped)
{
/* Quick creation of an orthonormal basis */
@@ -15,16 +13,14 @@ vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped)
return matcap_uv * 0.496 + 0.5;
}
-uniform sampler2D matcapDiffuseImage;
-uniform sampler2D matcapSpecularImage;
-
-vec3 get_matcap_lighting(vec3 base_color, vec3 N, vec3 I)
+vec3 get_matcap_lighting(
+ sampler2D diffuse_matcap, sampler2D specular_matcap, vec3 base_color, vec3 N, vec3 I)
{
bool flipped = world_data.matcap_orientation != 0;
vec2 uv = matcap_uv_compute(I, N, flipped);
- vec3 diffuse = textureLod(matcapDiffuseImage, uv, 0.0).rgb;
- vec3 specular = textureLod(matcapSpecularImage, uv, 0.0).rgb;
+ vec3 diffuse = textureLod(diffuse_matcap, uv, 0.0).rgb;
+ vec3 specular = textureLod(specular_matcap, uv, 0.0).rgb;
return diffuse * base_color + specular * float(world_data.use_specular);
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl
index 1d8950e34b3..b6dc26ecc65 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl
@@ -1,17 +1,9 @@
-layout(std140) uniform material_block
-{
- vec4 mat_data[4096];
-};
-
-/* If set to -1, the resource handle is used instead. */
-uniform int materialIndex;
-
void workbench_material_data_get(
int handle, out vec3 color, out float alpha, out float roughness, out float metallic)
{
handle = (materialIndex != -1) ? materialIndex : handle;
- vec4 data = mat_data[uint(handle) & 0xFFFu];
+ vec4 data = materials_data[uint(handle) & 0xFFFu];
color = data.rgb;
uint encoded_data = floatBitsToUint(data.w);
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl
index 856654549ca..ae564435258 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl
@@ -1,10 +1,4 @@
-uniform sampler2D depthBuffer;
-
-in vec4 uvcoordsvar;
-
-out vec4 fragColor;
-
void main()
{
float depth = texture(depthBuffer, uvcoordsvar.st).r;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
index 7a3ebc4035e..1b20171b3ff 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
@@ -1,22 +1,13 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
-#ifndef WORKBENCH_SHADER_SHARED_H
-layout(location = 0) out vec4 materialData;
-layout(location = 1) out vec2 normalData;
-layout(location = 2) out uint objectId;
-#endif
-
-uniform bool useMatcap = false;
-
void main()
{
normalData = workbench_normal_encode(gl_FrontFacing, normal_interp);
- materialData = vec4(color_interp, packed_rough_metal);
+ materialData = vec4(color_interp, workbench_float_pair_encode(roughness, metallic));
objectId = uint(object_id);
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
index c3faa3957ef..65b9f4de4b6 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
@@ -1,15 +1,10 @@
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_material_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
-#ifndef WORKBENCH_SHADER_SHARED_H
-uniform samplerBuffer ac; /* active color layer */
-uniform samplerBuffer au; /* active texture layer */
-#endif
-
/* From http://libnoise.sourceforge.net/noisegen/index.html */
float integer_noise(int n)
{
@@ -65,19 +60,12 @@ void main()
float hair_rand = integer_noise(hair_get_strand_id());
vec3 nor = workbench_hair_random_normal(tan, binor, hair_rand);
-#ifdef USE_WORLD_CLIP_PLANES
- world_clip_planes_calc_clip_distance(world_pos);
-#endif
+ view_clipping_distances(world_pos);
uv_interp = hair_get_customdata_vec2(au);
normal_interp = normalize(normal_world_to_view(nor));
-#ifndef WORKBENCH_SHADER_SHARED_H
-# ifdef OPAQUE_MATERIAL
- float metallic, roughness;
-# endif
-#endif
workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic);
if (materialIndex == 0) {
@@ -90,9 +78,5 @@ void main()
workbench_hair_random_material(hair_rand, color_interp, roughness, metallic);
-#ifdef OPAQUE_MATERIAL
- packed_rough_metal = workbench_float_pair_encode(roughness, metallic);
-#endif
-
object_id = int(uint(resource_handle) & 0xFFFFu) + 1;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_info.hh b/source/blender/draw/engines/workbench/shaders/workbench_prepass_info.hh
deleted file mode 100644
index 1dd706c9460..00000000000
--- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_info.hh
+++ /dev/null
@@ -1,136 +0,0 @@
-
-#include "gpu_shader_create_info.hh"
-
-/* -------------------------------------------------------------------- */
-/** \name Object Type
- * \{ */
-
-GPU_SHADER_CREATE_INFO(workbench_mesh)
- .vertex_in(0, Type::VEC3, "pos")
- .vertex_in(1, Type::VEC3, "nor")
- .vertex_in(2, Type::VEC4, "ac")
- .vertex_in(3, Type::VEC2, "au")
- .vertex_source("workbench_prepass_vert.glsl")
- .additional_info("draw_mesh");
-
-GPU_SHADER_CREATE_INFO(workbench_hair)
- .sampler(0, ImageType::FLOAT_BUFFER, "ac", Frequency::BATCH)
- .sampler(1, ImageType::FLOAT_BUFFER, "au", Frequency::BATCH)
- .vertex_source("workbench_prepass_hair_vert.glsl")
- .additional_info("draw_hair");
-
-GPU_SHADER_CREATE_INFO(workbench_pointcloud)
- .vertex_source("workbench_prepass_pointcloud_vert.glsl")
- .additional_info("draw_pointcloud");
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Texture Type
- * \{ */
-
-GPU_SHADER_CREATE_INFO(workbench_texture_none).define("TEXTURE_NONE");
-
-GPU_SHADER_CREATE_INFO(workbench_texture_single)
- .sampler(2, ImageType::FLOAT_2D, "imageTexture", Frequency::BATCH)
- .push_constant(1, Type::BOOL, "imagePremult")
- .push_constant(2, Type::FLOAT, "imageTransparencyCutoff")
- .define("V3D_SHADING_TEXTURE_COLOR");
-
-GPU_SHADER_CREATE_INFO(workbench_texture_tile)
- .sampler(2, ImageType::FLOAT_2D_ARRAY, "imageTileArray", Frequency::BATCH)
- .sampler(3, ImageType::FLOAT_1D_ARRAY, "imageTileData", Frequency::BATCH)
- .push_constant(1, Type::BOOL, "imagePremult")
- .push_constant(2, Type::FLOAT, "imageTransparencyCutoff")
- .define("TEXTURE_IMAGE_ARRAY");
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Lighting Type
- * \{ */
-
-GPU_SHADER_CREATE_INFO(workbench_lighting_studio).define("V3D_LIGHTING_STUDIO");
-GPU_SHADER_CREATE_INFO(workbench_lighting_matcap).define("V3D_LIGHTING_MATCAP");
-GPU_SHADER_CREATE_INFO(workbench_lighting_flat).define("V3D_LIGHTING_FLAT");
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Material Interface
- * \{ */
-
-GPU_SHADER_INTERFACE_INFO(workbench_material_iface, "")
- .smooth(Type::VEC3, "normal_interp")
- .smooth(Type::VEC3, "color_interp")
- .smooth(Type::FLOAT, "alpha_interp")
- .smooth(Type::VEC2, "uv_interp")
- .flat(Type::INT, "object_id")
- .flat(Type::FLOAT, "roughness")
- .flat(Type::FLOAT, "metallic");
-
-GPU_SHADER_CREATE_INFO(workbench_material).vertex_out(workbench_material_iface);
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Pipeline Type
- * \{ */
-
-GPU_SHADER_CREATE_INFO(workbench_transparent_accum)
- /* Note: Blending will be skipped on objectId because output is a
- non-normalized integer buffer. */
- .fragment_out(0, Type::VEC4, "transparentAccum")
- .fragment_out(1, Type::VEC4, "revealageAccum")
- .fragment_out(2, Type::UINT, "objectId")
- .uniform_buf(4, "WorldData", "world_data", Frequency::PASS)
- .typedef_source("workbench_shader_shared.h")
- .fragment_source("workbench_transparent_accum_frag.glsl")
- .additional_info("workbench_material");
-
-GPU_SHADER_CREATE_INFO(workbench_opaque)
- .fragment_out(0, Type::VEC4, "materialData")
- .fragment_out(1, Type::VEC2, "normalData")
- .fragment_out(2, Type::UINT, "objectId")
- .uniform_buf(4, "WorldData", "world_data", Frequency::PASS)
- .typedef_source("workbench_shader_shared.h")
- .fragment_source("workbench_prepass_frag.glsl")
- .additional_info("workbench_material");
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Variations Declaration
- * \{ */
-
-#define WORKBENCH_SURFACETYPE_VARIATIONS(prefix, ...) \
- GPU_SHADER_CREATE_INFO(prefix##_mesh) \
- .additional_info("workbench_mesh", __VA_ARGS__) \
- .do_static_compilation(true); \
- GPU_SHADER_CREATE_INFO(prefix##_hair) \
- .additional_info("workbench_hair", __VA_ARGS__) \
- .do_static_compilation(true); \
- GPU_SHADER_CREATE_INFO(prefix##_ptcloud) \
- .additional_info("workbench_pointcloud", __VA_ARGS__) \
- .do_static_compilation(true);
-
-#define WORKBENCH_PIPELINE_VARIATIONS(prefix, ...) \
- WORKBENCH_SURFACETYPE_VARIATIONS(prefix##_transp_studio, \
- "workbench_transparent_accum", \
- "workbench_lighting_studio", \
- __VA_ARGS__) \
- WORKBENCH_SURFACETYPE_VARIATIONS(prefix##_transp_matcap, \
- "workbench_transparent_accum", \
- "workbench_lighting_matcap", \
- __VA_ARGS__) \
- WORKBENCH_SURFACETYPE_VARIATIONS(prefix##_transp_flat, \
- "workbench_transparent_accum", \
- "workbench_lighting_flat", \
- __VA_ARGS__) \
- WORKBENCH_SURFACETYPE_VARIATIONS(prefix##_opaque, "workbench_opaque", __VA_ARGS__)
-
-WORKBENCH_PIPELINE_VARIATIONS(workbench_tex_none, "workbench_texture_none")
-WORKBENCH_PIPELINE_VARIATIONS(workbench_tex_single, "workbench_texture_single")
-WORKBENCH_PIPELINE_VARIATIONS(workbench_tex_tile, "workbench_texture_tile")
-
-/** \} */
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl
index 8efe10b8236..911d6f5b036 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl
@@ -1,7 +1,7 @@
+#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_pointcloud_lib.glsl)
-#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_material_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
@@ -15,26 +15,15 @@ void main()
gl_Position = point_world_to_ndc(world_pos);
-#ifdef USE_WORLD_CLIP_PLANES
- world_clip_planes_calc_clip_distance(world_pos);
-#endif
+ view_clipping_distances(world_pos);
uv_interp = vec2(0.0);
-#ifndef WORKBENCH_SHADER_SHARED_H
-# ifdef OPAQUE_MATERIAL
- float metallic, roughness;
-# endif
-#endif
workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic);
if (materialIndex == 0) {
color_interp = vec3(1.0);
}
-#ifdef OPAQUE_MATERIAL
- packed_rough_metal = workbench_float_pair_encode(roughness, metallic);
-#endif
-
object_id = int(uint(resource_handle) & 0xFFFFu) + 1;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl
index 1f6a8a63944..3a63b141c5f 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl
@@ -1,44 +1,26 @@
+#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_material_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
-#ifndef WORKBENCH_SHADER_SHARED_H
-in vec3 pos;
-in vec3 nor;
-in vec4 ac; /* active color */
-in vec2 au; /* active texture layer */
-#endif
-
void main()
{
vec3 world_pos = point_object_to_world(pos);
gl_Position = point_world_to_ndc(world_pos);
-#ifdef USE_WORLD_CLIP_PLANES
- world_clip_planes_calc_clip_distance(world_pos);
-#endif
+ view_clipping_distances(world_pos);
uv_interp = au;
normal_interp = normalize(normal_object_to_view(nor));
-#ifndef WORKBENCH_SHADER_SHARED_H
-# ifdef OPAQUE_MATERIAL
- float metallic, roughness;
-# endif
-#endif
workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic);
if (materialIndex == 0) {
color_interp = ac.rgb;
}
-#ifdef OPAQUE_MATERIAL
- packed_rough_metal = workbench_float_pair_encode(roughness, metallic);
-#endif
-
object_id = int(uint(resource_handle) & 0xFFFFu) + 1;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl
deleted file mode 100644
index 178e61c8a8d..00000000000
--- a/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-
-#ifndef WORKBENCH_SHADER_SHARED_H
-IN_OUT ShaderStageInterface
-{
- vec3 normal_interp;
- vec3 color_interp;
- float alpha_interp;
- vec2 uv_interp;
-# ifdef TRANSPARENT_MATERIAL
- flat float roughness;
- flat float metallic;
-# else
- flat float packed_rough_metal;
-# endif
- flat int object_id;
-};
-#endif
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl
index 09bafb8ff11..4a7b1522426 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_caps_geom.glsl
@@ -1,41 +1,7 @@
-#extension GL_ARB_gpu_shader5 : enable
-
-#ifdef GL_ARB_gpu_shader5
+#ifdef GPU_ARB_gpu_shader5
# define USE_INVOC_EXT
#endif
-#ifdef DOUBLE_MANIFOLD
-# ifdef USE_INVOC_EXT
-# define invoc_len 2
-# else
-# define vert_len 6
-# endif
-#else
-# ifdef USE_INVOC_EXT
-# define invoc_len 2
-# else
-# define vert_len 6
-# endif
-#endif
-
-#ifdef USE_INVOC_EXT
-layout(triangles, invocations = invoc_len) in;
-layout(triangle_strip, max_vertices = 3) out;
-#else
-layout(triangles) in;
-layout(triangle_strip, max_vertices = vert_len) out;
-#endif
-
-uniform vec3 lightDirection = vec3(0.57, 0.57, -0.57);
-
-in VertexData
-{
- vec3 pos; /* local position */
- vec4 frontPosition; /* final ndc position */
- vec4 backPosition;
-}
-vData[];
-
vec4 get_pos(int v, bool backface)
{
return (backface) ? vData[v].backPosition : vData[v].frontPosition;
@@ -76,18 +42,19 @@ void main()
/* In case of non manifold geom, we only increase/decrease
* the stencil buffer by one but do every faces as they were facing the light. */
bool invert = backface;
+ const bool is_manifold = false;
#else
const bool invert = false;
- if (!backface) {
+ const bool is_manifold = true;
#endif
+
+ if (!is_manifold || !backface) {
#ifdef USE_INVOC_EXT
- bool do_front = (gl_InvocationID & 1) == 0;
- emit_cap(do_front, invert);
+ bool do_front = (gl_InvocationID & 1) == 0;
+ emit_cap(do_front, invert);
#else
emit_cap(true, invert);
emit_cap(false, invert);
#endif
-#ifndef DOUBLE_MANIFOLD
-}
-#endif
+ }
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl
index 6fa76510e6e..c9977a8d91a 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl
@@ -1,10 +1,4 @@
-out vec4 fragColor;
-
-layout(location = 0) out vec4 materialData;
-layout(location = 1) out vec4 normalData;
-layout(location = 2) out uint objectId;
-
void main()
{
const float a = 0.25;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl
index 2c9190bfcf4..9902884fc12 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_geom.glsl
@@ -1,41 +1,7 @@
-#extension GL_ARB_gpu_shader5 : enable
-
-#ifdef GL_ARB_gpu_shader5
+#ifdef GPU_ARB_gpu_shader5
# define USE_INVOC_EXT
#endif
-#ifdef DOUBLE_MANIFOLD
-# ifdef USE_INVOC_EXT
-# define invoc_len 2
-# else
-# define vert_len 8
-# endif
-#else
-# ifdef USE_INVOC_EXT
-# define invoc_len 1
-# else
-# define vert_len 4
-# endif
-#endif
-
-#ifdef USE_INVOC_EXT
-layout(lines_adjacency, invocations = invoc_len) in;
-layout(triangle_strip, max_vertices = 4) out;
-#else
-layout(lines_adjacency) in;
-layout(triangle_strip, max_vertices = vert_len) out;
-#endif
-
-uniform vec3 lightDirection = vec3(0.57, 0.57, -0.57);
-
-in VertexData
-{
- vec3 pos; /* local position */
- vec4 frontPosition; /* final ndc position */
- vec4 backPosition;
-}
-vData[];
-
#define DEGENERATE_TRIS_WORKAROUND
#define DEGENERATE_TRIS_AREA_THRESHOLD 4e-17
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl
index e07f87525e2..a220434ec45 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl
@@ -1,17 +1,5 @@
-#define INFINITE 1000.0
-uniform vec3 lightDirection = vec3(0.57, 0.57, -0.57);
-uniform float lightDistance = 1e4;
-
-in vec3 pos;
-
-out VertexData
-{
- vec3 pos; /* local position */
- vec4 frontPosition; /* final ndc position */
- vec4 backPosition;
-}
-vData;
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
void main()
{
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl
index 0062cbe17a2..9c0f93c67d9 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl
@@ -1,21 +1,10 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_matcap_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_world_light_lib.glsl)
-#ifndef WORKBENCH_SHADER_SHARED_H
-/* Revealage is actually stored in transparentAccum alpha channel.
- * This is a workaround to older hardware not having separate blend equation per render target. */
-layout(location = 0) out vec4 transparentAccum;
-layout(location = 1) out vec4 revealageAccum;
-
-/* NOTE: Blending will be skipped on objectId because output is a non-normalized integer buffer. */
-layout(location = 2) out uint objectId;
-#endif
-
/* Special function only to be used with calculate_transparent_weight(). */
float linear_zdepth(float depth, vec4 viewvecs[2], mat4 proj_mat)
{
@@ -69,7 +58,7 @@ void main()
#endif
#ifdef V3D_LIGHTING_MATCAP
- vec3 shaded_color = get_matcap_lighting(color, N, I);
+ vec3 shaded_color = get_matcap_lighting(matcap_diffuse_tx, matcap_specular_tx, color, N, I);
#endif
#ifdef V3D_LIGHTING_STUDIO
@@ -80,7 +69,7 @@ void main()
vec3 shaded_color = color;
#endif
- shaded_color *= get_shadow(N);
+ shaded_color *= get_shadow(N, forceShadowing);
/* Listing 4 */
float weight = calculate_transparent_weight() * alpha_interp;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl
index d985737a35b..a2c45d2f8e3 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl
@@ -1,11 +1,4 @@
-uniform sampler2D transparentAccum;
-uniform sampler2D transparentRevealage;
-
-in vec4 uvcoordsvar;
-
-out vec4 fragColor;
-
/* Based on :
* McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of
* Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122–141, 2013
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
index 48102b4dcca..076f6e80104 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
@@ -1,40 +1,8 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl)
-#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
-uniform sampler2D depthBuffer;
-
-uniform sampler3D densityTexture;
-uniform sampler3D shadowTexture;
-uniform sampler3D flameTexture;
-uniform usampler3D flagTexture;
-uniform sampler1D flameColorTexture;
-uniform sampler1D transferTexture;
-uniform mat4 volumeObjectToTexture;
-
-uniform int samplesLen = 256;
-uniform float noiseOfs = 0.0;
-uniform float stepLength; /* Step length in local space. */
-uniform float densityScale; /* Simple Opacity multiplicator. */
-uniform float gridScale; /* Multiplicator for grid scaling. */
-uniform vec3 activeColor;
-
-uniform float slicePosition;
-uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */
-
-uniform bool showPhi = false;
-uniform bool showFlags = false;
-uniform bool showPressure = false;
-
-#ifdef VOLUME_SLICE
-in vec3 localPos;
-#endif
-
-out vec4 fragColor;
-
float phase_function_isotropic()
{
return 1.0 / (4.0 * M_PI);
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl
index 7327a92e04f..d2b12f41421 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl
@@ -1,22 +1,8 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl)
-
-uniform float slicePosition;
-uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */
-
-uniform mat4 volumeTextureToObject;
-
-in vec3 pos;
RESOURCE_ID_VARYING
-#ifdef VOLUME_SLICE
-in vec3 uvs;
-
-out vec3 localPos;
-#endif
-
void main()
{
#ifdef VOLUME_SLICE
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
index 41ef516ee4d..531ed461057 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
@@ -1,6 +1,4 @@
-#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
-
/* [Drobot2014a] Low Level Optimizations for GCN */
vec4 fast_rcp(vec4 v)
{
@@ -120,12 +118,10 @@ vec3 get_world_lighting(vec3 base_color, float roughness, float metallic, vec3 N
return diffuse_light + specular_light;
}
-uniform bool forceShadowing = false;
-
-float get_shadow(vec3 N)
+float get_shadow(vec3 N, bool force_shadowing)
{
float light_factor = -dot(N, world_data.shadow_direction_vs.xyz);
float shadow_mix = smoothstep(world_data.shadow_shift, world_data.shadow_focus, light_factor);
- shadow_mix *= forceShadowing ? 0.0 : world_data.shadow_mul;
+ shadow_mix *= force_shadowing ? 0.0 : world_data.shadow_mul;
return shadow_mix + world_data.shadow_add;
}
diff --git a/source/blender/draw/engines/workbench/workbench_effect_cavity.c b/source/blender/draw/engines/workbench/workbench_effect_cavity.c
index b294b9a62ca..e58c88c5dcc 100644
--- a/source/blender/draw/engines/workbench/workbench_effect_cavity.c
+++ b/source/blender/draw/engines/workbench/workbench_effect_cavity.c
@@ -164,10 +164,10 @@ void workbench_cavity_cache_init(WORKBENCH_Data *data)
grp = DRW_shgroup_create(sh, psl->cavity_ps);
DRW_shgroup_uniform_texture(grp, "normalBuffer", wpd->normal_buffer_tx);
- DRW_shgroup_uniform_block(grp, "samples_block", wpd->vldata->cavity_sample_ubo);
- DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
if (SSAO_ENABLED(wpd)) {
+ DRW_shgroup_uniform_block(grp, "samples_coords", wpd->vldata->cavity_sample_ubo);
DRW_shgroup_uniform_texture(grp, "depthBuffer", dtxl->depth);
DRW_shgroup_uniform_texture(grp, "cavityJitter", wpd->vldata->cavity_jitter_tx);
}
diff --git a/source/blender/draw/engines/workbench/workbench_effect_dof.c b/source/blender/draw/engines/workbench/workbench_effect_dof.c
index 2261790226a..b5f43251a43 100644
--- a/source/blender/draw/engines/workbench/workbench_effect_dof.c
+++ b/source/blender/draw/engines/workbench/workbench_effect_dof.c
@@ -328,7 +328,7 @@ void workbench_dof_cache_init(WORKBENCH_Data *vedata)
float offset = wpd->taa_sample / (float)max_ii(1, wpd->taa_sample_len);
DRWShadingGroup *grp = DRW_shgroup_create(blur1_sh, psl->dof_blur1_ps);
- DRW_shgroup_uniform_block(grp, "dofSamplesBlock", wpd->vldata->dof_sample_ubo);
+ DRW_shgroup_uniform_block(grp, "samples", wpd->vldata->dof_sample_ubo);
DRW_shgroup_uniform_texture(grp, "noiseTex", wpd->vldata->cavity_jitter_tx);
DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx);
DRW_shgroup_uniform_texture(grp, "halfResColorTex", txl->dof_source_tx);
diff --git a/source/blender/draw/engines/workbench/workbench_effect_outline.c b/source/blender/draw/engines/workbench/workbench_effect_outline.c
index d1bc6b6c435..4f716c5a7bc 100644
--- a/source/blender/draw/engines/workbench/workbench_effect_outline.c
+++ b/source/blender/draw/engines/workbench/workbench_effect_outline.c
@@ -46,7 +46,7 @@ void workbench_outline_cache_init(WORKBENCH_Data *data)
grp = DRW_shgroup_create(sh, psl->outline_ps);
DRW_shgroup_uniform_texture(grp, "objectIdBuffer", wpd->object_id_tx);
DRW_shgroup_uniform_texture(grp, "depthBuffer", dtxl->depth);
- DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
else {
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 5bc2c53e253..8773c78e82f 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -28,13 +28,14 @@
#include "BLI_alloca.h"
+#include "BKE_editmesh.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
+#include "DNA_curves_types.h"
#include "DNA_fluid_types.h"
-#include "DNA_hair_types.h"
#include "DNA_image_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
@@ -51,8 +52,6 @@ void workbench_engine_init(void *ved)
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_TextureList *txl = vedata->txl;
- workbench_shader_library_ensure();
-
workbench_private_data_alloc(stl);
WORKBENCH_PrivateData *wpd = stl->wpd;
workbench_private_data_init(wpd);
@@ -241,6 +240,26 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd,
DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
+static const CustomData *workbench_mesh_get_loop_custom_data(const Mesh *mesh)
+{
+ if (mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ BLI_assert(mesh->edit_mesh != NULL);
+ BLI_assert(mesh->edit_mesh->bm != NULL);
+ return &mesh->edit_mesh->bm->ldata;
+ }
+ return &mesh->ldata;
+}
+
+static const CustomData *workbench_mesh_get_vert_custom_data(const Mesh *mesh)
+{
+ if (mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ BLI_assert(mesh->edit_mesh != NULL);
+ BLI_assert(mesh->edit_mesh->bm != NULL);
+ return &mesh->edit_mesh->bm->vdata;
+ }
+ return &mesh->vdata;
+}
+
/**
* Decide what color-type to draw the object with.
* In some cases it can be overwritten by #workbench_material_setup().
@@ -253,6 +272,8 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
{
eV3DShadingColorType color_type = wpd->shading.color_type;
const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL;
+ const CustomData *ldata = (me == NULL) ? NULL : workbench_mesh_get_loop_custom_data(me);
+ const CustomData *vdata = (me == NULL) ? NULL : workbench_mesh_get_vert_custom_data(me);
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool is_active = (ob == draw_ctx->obact);
@@ -266,19 +287,19 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
if (ob->dt < OB_TEXTURE) {
color_type = V3D_SHADING_MATERIAL_COLOR;
}
- else if ((me == NULL) || (me->mloopuv == NULL)) {
+ else if ((me == NULL) || !CustomData_has_layer(ldata, CD_MLOOPUV)) {
/* Disable color mode if data layer is unavailable. */
color_type = V3D_SHADING_MATERIAL_COLOR;
}
}
else if (color_type == V3D_SHADING_VERTEX_COLOR) {
if (U.experimental.use_sculpt_vertex_colors) {
- if ((me == NULL) || !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
+ if ((me == NULL) || !CustomData_has_layer(vdata, CD_PROP_COLOR)) {
color_type = V3D_SHADING_OBJECT_COLOR;
}
}
else {
- if ((me == NULL) || !CustomData_has_layer(&me->ldata, CD_MLOOPCOL)) {
+ if ((me == NULL) || !CustomData_has_layer(ldata, CD_MLOOPCOL)) {
color_type = V3D_SHADING_OBJECT_COLOR;
}
}
@@ -293,13 +314,13 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
if (!is_sculpt_pbvh && !is_render) {
/* Force texture or vertex mode if object is in paint mode. */
- if (is_texpaint_mode && me && me->mloopuv) {
+ if (is_texpaint_mode && me && CustomData_has_layer(ldata, CD_MLOOPUV)) {
color_type = V3D_SHADING_TEXTURE_COLOR;
if (r_texpaint_mode) {
*r_texpaint_mode = true;
}
}
- else if (is_vertpaint_mode && me && me->mloopcol) {
+ else if (is_vertpaint_mode && me && CustomData_has_layer(ldata, CD_MLOOPCOL)) {
color_type = V3D_SHADING_VERTEX_COLOR;
}
}
@@ -398,9 +419,9 @@ void workbench_cache_populate(void *ved, Object *ob)
workbench_shadow_cache_populate(vedata, ob, has_transp_mat);
}
}
- else if (ob->type == OB_HAIR) {
+ else if (ob->type == OB_CURVES) {
int color_type = workbench_color_type_get(wpd, ob, NULL, NULL, NULL);
- workbench_cache_hair_populate(wpd, ob, NULL, NULL, color_type, false, HAIR_MATERIAL_NR);
+ workbench_cache_hair_populate(wpd, ob, NULL, NULL, color_type, false, CURVES_MATERIAL_NR);
}
else if (ob->type == OB_VOLUME) {
if (wpd->shading.type != OB_WIRE) {
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index c24cab009c7..1b12e29617c 100644
--- a/source/blender/draw/engines/workbench/workbench_materials.c
+++ b/source/blender/draw/engines/workbench/workbench_materials.c
@@ -210,7 +210,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
DRWShadingGroup *grp = prepass->common_shgrp;
*grp_mat = grp = DRW_shgroup_create_sub(grp);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", mat_id);
return grp;
}
@@ -234,7 +234,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
DRWShadingGroup **grp = &wpd->prepass[transp][infront][datatype].common_shgrp;
if (resource_changed) {
*grp = DRW_shgroup_create_sub(*grp);
- DRW_shgroup_uniform_block(*grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(*grp, "materials_data", wpd->material_ubo_curr);
}
if (r_transp && transp) {
*r_transp = true;
@@ -293,5 +293,6 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
DRW_shgroup_uniform_texture_ex(grp, "imageTexture", tex, sampler);
}
DRW_shgroup_uniform_bool_copy(grp, "imagePremult", (ima && ima->alpha_mode == IMA_ALPHA_PREMUL));
+ DRW_shgroup_uniform_float_copy(grp, "imageTransparencyCutoff", 0.1f);
return grp;
}
diff --git a/source/blender/draw/engines/workbench/workbench_opaque.c b/source/blender/draw/engines/workbench/workbench_opaque.c
index 9fdefed019f..e4534d923ab 100644
--- a/source/blender/draw/engines/workbench/workbench_opaque.c
+++ b/source/blender/draw/engines/workbench/workbench_opaque.c
@@ -88,26 +88,26 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
sh = workbench_shader_opaque_get(wpd, data);
wpd->prepass[opaque][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1);
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
wpd->prepass[opaque][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
sh = workbench_shader_opaque_image_get(wpd, data, false);
wpd->prepass[opaque][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
sh = workbench_shader_opaque_image_get(wpd, data, true);
wpd->prepass[opaque][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
}
@@ -121,7 +121,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
sh = workbench_shader_composite_get(wpd);
grp = DRW_shgroup_create(sh, psl->composite_ps);
- DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_texture(grp, "materialBuffer", wpd->material_buffer_tx);
DRW_shgroup_uniform_texture(grp, "normalBuffer", wpd->normal_buffer_tx);
DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", false);
@@ -135,8 +135,8 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
struct GPUTexture *spec_tx = wpd->studio_light->matcap_specular.gputexture;
const bool use_spec = workbench_is_specular_highlight_enabled(wpd);
spec_tx = (use_spec && spec_tx) ? spec_tx : diff_tx;
- DRW_shgroup_uniform_texture(grp, "matcapDiffuseImage", diff_tx);
- DRW_shgroup_uniform_texture(grp, "matcapSpecularImage", spec_tx);
+ DRW_shgroup_uniform_texture(grp, "matcap_diffuse_tx", diff_tx);
+ DRW_shgroup_uniform_texture(grp, "matcap_specular_tx", spec_tx);
}
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h
index fb4600316ec..2cf96a8e139 100644
--- a/source/blender/draw/engines/workbench/workbench_private.h
+++ b/source/blender/draw/engines/workbench/workbench_private.h
@@ -459,7 +459,6 @@ void workbench_shader_depth_of_field_get(GPUShader **prepare_sh,
GPUShader **blur2_sh,
GPUShader **resolve_sh);
-void workbench_shader_library_ensure(void);
void workbench_shader_free(void);
/* workbench_effect_antialiasing.c */
diff --git a/source/blender/draw/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c
deleted file mode 100644
index ad610a6a885..00000000000
--- a/source/blender/draw/engines/workbench/workbench_shader.c
+++ /dev/null
@@ -1,562 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2020, Blender Foundation.
- */
-
-/** \file
- * \ingroup draw_engine
- */
-
-#include "DRW_render.h"
-
-#include "BLI_dynstr.h"
-#include "BLI_string_utils.h"
-
-#include "workbench_engine.h"
-#include "workbench_private.h"
-
-extern char datatoc_common_math_lib_glsl[];
-extern char datatoc_common_math_geom_lib_glsl[];
-extern char datatoc_common_hair_lib_glsl[];
-extern char datatoc_common_pointcloud_lib_glsl[];
-extern char datatoc_common_view_lib_glsl[];
-extern char datatoc_common_smaa_lib_glsl[];
-
-extern char datatoc_workbench_prepass_vert_glsl[];
-extern char datatoc_workbench_prepass_hair_vert_glsl[];
-extern char datatoc_workbench_prepass_pointcloud_vert_glsl[];
-extern char datatoc_workbench_prepass_frag_glsl[];
-
-extern char datatoc_workbench_effect_cavity_frag_glsl[];
-extern char datatoc_workbench_effect_outline_frag_glsl[];
-extern char datatoc_workbench_effect_dof_frag_glsl[];
-extern char datatoc_workbench_effect_taa_frag_glsl[];
-extern char datatoc_workbench_effect_smaa_frag_glsl[];
-extern char datatoc_workbench_effect_smaa_vert_glsl[];
-
-extern char datatoc_workbench_composite_frag_glsl[];
-
-extern char datatoc_workbench_transparent_accum_frag_glsl[];
-extern char datatoc_workbench_transparent_resolve_frag_glsl[];
-
-extern char datatoc_workbench_merge_infront_frag_glsl[];
-
-extern char datatoc_workbench_shadow_vert_glsl[];
-extern char datatoc_workbench_shadow_geom_glsl[];
-extern char datatoc_workbench_shadow_caps_geom_glsl[];
-extern char datatoc_workbench_shadow_debug_frag_glsl[];
-
-extern char datatoc_workbench_volume_vert_glsl[];
-extern char datatoc_workbench_volume_frag_glsl[];
-
-extern char datatoc_workbench_cavity_lib_glsl[];
-extern char datatoc_workbench_common_lib_glsl[];
-extern char datatoc_workbench_curvature_lib_glsl[];
-extern char datatoc_workbench_data_lib_glsl[];
-extern char datatoc_workbench_image_lib_glsl[];
-extern char datatoc_workbench_matcap_lib_glsl[];
-extern char datatoc_workbench_material_lib_glsl[];
-extern char datatoc_workbench_shader_interface_lib_glsl[];
-extern char datatoc_workbench_world_light_lib_glsl[];
-
-extern char datatoc_gpu_shader_depth_only_frag_glsl[];
-extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
-
-/* Maximum number of variations. */
-#define MAX_LIGHTING 3
-#define MAX_COLOR 3
-
-enum {
- VOLUME_SH_SLICE = 0,
- VOLUME_SH_COBA,
- VOLUME_SH_CUBIC,
-};
-
-#define VOLUME_SH_MAX (1 << (VOLUME_SH_CUBIC + 1))
-
-static struct {
- struct GPUShader *opaque_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX][MAX_COLOR];
- struct GPUShader *transp_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX]
- [MAX_LIGHTING][MAX_COLOR];
-
- struct GPUShader *opaque_composite_sh[MAX_LIGHTING];
- struct GPUShader *oit_resolve_sh;
- struct GPUShader *outline_sh;
- struct GPUShader *merge_infront_sh;
-
- struct GPUShader *shadow_depth_pass_sh[2];
- struct GPUShader *shadow_depth_fail_sh[2][2];
-
- struct GPUShader *cavity_sh[2][2];
-
- struct GPUShader *dof_prepare_sh;
- struct GPUShader *dof_downsample_sh;
- struct GPUShader *dof_blur1_sh;
- struct GPUShader *dof_blur2_sh;
- struct GPUShader *dof_resolve_sh;
-
- struct GPUShader *aa_accum_sh;
- struct GPUShader *smaa_sh[3];
-
- struct GPUShader *volume_sh[2][2][3][2];
-
- struct DRWShaderLibrary *lib;
-} e_data = {{{{NULL}}}};
-
-void workbench_shader_library_ensure(void)
-{
- if (e_data.lib == NULL) {
- e_data.lib = DRW_shader_library_create();
- /* NOTE: These need to be ordered by dependencies. */
- DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_pointcloud_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_shader_interface_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_common_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_image_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_material_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_data_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_matcap_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_cavity_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_curvature_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, workbench_world_light_lib);
- }
-}
-
-static char *workbench_build_defines(
- WORKBENCH_PrivateData *wpd, bool textured, bool tiled, bool cavity, bool curvature)
-{
- char *str = NULL;
-
- DynStr *ds = BLI_dynstr_new();
-
- if (wpd && wpd->shading.light == V3D_LIGHTING_STUDIO) {
- BLI_dynstr_append(ds, "#define V3D_LIGHTING_STUDIO\n");
- }
- else if (wpd && wpd->shading.light == V3D_LIGHTING_MATCAP) {
- BLI_dynstr_append(ds, "#define V3D_LIGHTING_MATCAP\n");
- }
- else {
- BLI_dynstr_append(ds, "#define V3D_LIGHTING_FLAT\n");
- }
-
- if (NORMAL_ENCODING_ENABLED()) {
- BLI_dynstr_append(ds, "#define WORKBENCH_ENCODE_NORMALS\n");
- }
-
- if (textured) {
- BLI_dynstr_append(ds, "#define V3D_SHADING_TEXTURE_COLOR\n");
- }
- if (tiled) {
- BLI_dynstr_append(ds, "#define TEXTURE_IMAGE_ARRAY\n");
- }
- if (cavity) {
- BLI_dynstr_append(ds, "#define USE_CAVITY\n");
- }
- if (curvature) {
- BLI_dynstr_append(ds, "#define USE_CURVATURE\n");
- }
-
- str = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
- return str;
-}
-
-static int workbench_color_index(WORKBENCH_PrivateData *UNUSED(wpd), bool textured, bool tiled)
-{
- BLI_assert(2 < MAX_COLOR);
- return (textured) ? (tiled ? 2 : 1) : 0;
-}
-
-static GPUShader *workbench_shader_get_ex(WORKBENCH_PrivateData *wpd,
- bool transp,
- eWORKBENCH_DataType datatype,
- bool textured,
- bool tiled)
-{
- int color = workbench_color_index(wpd, textured, tiled);
- int light = wpd->shading.light;
- BLI_assert(light < MAX_LIGHTING);
- struct GPUShader **shader =
- (transp) ? &e_data.transp_prepass_sh_cache[wpd->sh_cfg][datatype][light][color] :
- &e_data.opaque_prepass_sh_cache[wpd->sh_cfg][datatype][color];
-
- if (*shader == NULL) {
- char *defines = workbench_build_defines(wpd, textured, tiled, false, false);
-
- char *frag_file = transp ? datatoc_workbench_transparent_accum_frag_glsl :
- datatoc_workbench_prepass_frag_glsl;
- char *frag_src = DRW_shader_library_create_shader_string(e_data.lib, frag_file);
-
- char *vert_file = (datatype == WORKBENCH_DATATYPE_HAIR) ?
- datatoc_workbench_prepass_hair_vert_glsl :
- ((datatype == WORKBENCH_DATATYPE_POINTCLOUD) ?
- datatoc_workbench_prepass_pointcloud_vert_glsl :
- datatoc_workbench_prepass_vert_glsl);
- char *vert_src = DRW_shader_library_create_shader_string(e_data.lib, vert_file);
-
- const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[wpd->sh_cfg];
-
- *shader = GPU_shader_create_from_arrays({
- .vert = (const char *[]){sh_cfg_data->lib, vert_src, NULL},
- .frag = (const char *[]){frag_src, NULL},
- .defs = (const char *[]){sh_cfg_data->def,
- defines,
- transp ? "#define TRANSPARENT_MATERIAL\n" :
- "#define OPAQUE_MATERIAL\n",
- (datatype == WORKBENCH_DATATYPE_POINTCLOUD) ?
- "#define UNIFORM_RESOURCE_ID\n"
- "#define INSTANCED_ATTR\n" :
- NULL,
- NULL},
- });
-
- MEM_freeN(defines);
- MEM_freeN(frag_src);
- MEM_freeN(vert_src);
- }
- return *shader;
-}
-
-GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, eWORKBENCH_DataType datatype)
-{
- return workbench_shader_get_ex(wpd, false, datatype, false, false);
-}
-
-GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd,
- eWORKBENCH_DataType datatype,
- bool tiled)
-{
- return workbench_shader_get_ex(wpd, false, datatype, true, tiled);
-}
-
-GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd,
- eWORKBENCH_DataType datatype)
-{
- return workbench_shader_get_ex(wpd, true, datatype, false, false);
-}
-
-GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd,
- eWORKBENCH_DataType datatype,
- bool tiled)
-{
- return workbench_shader_get_ex(wpd, true, datatype, true, tiled);
-}
-
-GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd)
-{
- int light = wpd->shading.light;
- struct GPUShader **shader = &e_data.opaque_composite_sh[light];
- BLI_assert(light < MAX_LIGHTING);
-
- if (*shader == NULL) {
- char *defines = workbench_build_defines(wpd, false, false, false, false);
- char *frag = DRW_shader_library_create_shader_string(e_data.lib,
- datatoc_workbench_composite_frag_glsl);
-
- *shader = DRW_shader_create_fullscreen(frag, defines);
-
- MEM_freeN(defines);
- MEM_freeN(frag);
- }
- return *shader;
-}
-
-GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *UNUSED(wpd))
-{
- if (e_data.merge_infront_sh == NULL) {
- char *frag = DRW_shader_library_create_shader_string(
- e_data.lib, datatoc_workbench_merge_infront_frag_glsl);
-
- e_data.merge_infront_sh = DRW_shader_create_fullscreen(frag, NULL);
-
- MEM_freeN(frag);
- }
- return e_data.merge_infront_sh;
-}
-
-GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *wpd)
-{
- if (e_data.oit_resolve_sh == NULL) {
- char *defines = workbench_build_defines(wpd, false, false, false, false);
-
- e_data.oit_resolve_sh = DRW_shader_create_fullscreen(
- datatoc_workbench_transparent_resolve_frag_glsl, defines);
-
- MEM_freeN(defines);
- }
- return e_data.oit_resolve_sh;
-}
-
-static GPUShader *workbench_shader_shadow_pass_get_ex(bool depth_pass, bool manifold, bool cap)
-{
- struct GPUShader **shader = (depth_pass) ? &e_data.shadow_depth_pass_sh[manifold] :
- &e_data.shadow_depth_fail_sh[manifold][cap];
-
- if (*shader == NULL) {
-#if DEBUG_SHADOW_VOLUME
- const char *shadow_frag = datatoc_workbench_shadow_debug_frag_glsl;
-#else
- const char *shadow_frag = datatoc_gpu_shader_depth_only_frag_glsl;
-#endif
-
- *shader = GPU_shader_create_from_arrays({
- .vert = (const char *[]){datatoc_common_view_lib_glsl,
- datatoc_workbench_shadow_vert_glsl,
- NULL},
- .geom = (const char *[]){(cap) ? datatoc_workbench_shadow_caps_geom_glsl :
- datatoc_workbench_shadow_geom_glsl,
- NULL},
- .frag = (const char *[]){shadow_frag, NULL},
- .defs = (const char *[]){(depth_pass) ? "#define SHADOW_PASS\n" : "#define SHADOW_FAIL\n",
- (manifold) ? "" : "#define DOUBLE_MANIFOLD\n",
- NULL},
- });
- }
- return *shader;
-}
-
-GPUShader *workbench_shader_shadow_pass_get(bool manifold)
-{
- return workbench_shader_shadow_pass_get_ex(true, manifold, false);
-}
-
-GPUShader *workbench_shader_shadow_fail_get(bool manifold, bool cap)
-{
- return workbench_shader_shadow_pass_get_ex(false, manifold, cap);
-}
-
-GPUShader *workbench_shader_cavity_get(bool cavity, bool curvature)
-{
- BLI_assert(cavity || curvature);
- struct GPUShader **shader = &e_data.cavity_sh[cavity][curvature];
-
- if (*shader == NULL) {
- char *defines = workbench_build_defines(NULL, false, false, cavity, curvature);
- char *frag = DRW_shader_library_create_shader_string(
- e_data.lib, datatoc_workbench_effect_cavity_frag_glsl);
-
- *shader = DRW_shader_create_fullscreen(frag, defines);
-
- MEM_freeN(defines);
- MEM_freeN(frag);
- }
- return *shader;
-}
-
-GPUShader *workbench_shader_outline_get(void)
-{
- if (e_data.outline_sh == NULL) {
- char *frag = DRW_shader_library_create_shader_string(
- e_data.lib, datatoc_workbench_effect_outline_frag_glsl);
-
- e_data.outline_sh = DRW_shader_create_fullscreen(frag, NULL);
-
- MEM_freeN(frag);
- }
- return e_data.outline_sh;
-}
-
-void workbench_shader_depth_of_field_get(GPUShader **prepare_sh,
- GPUShader **downsample_sh,
- GPUShader **blur1_sh,
- GPUShader **blur2_sh,
- GPUShader **resolve_sh)
-{
- if (e_data.dof_prepare_sh == NULL) {
- e_data.dof_prepare_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define PREPARE\n");
- e_data.dof_downsample_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DOWNSAMPLE\n");
-#if 0 /* TODO(fclem): finish COC min_max optimization */
- e_data.dof_flatten_v_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define FLATTEN_VERTICAL\n");
- e_data.dof_flatten_h_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define FLATTEN_HORIZONTAL\n");
- e_data.dof_dilate_v_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DILATE_VERTICAL\n");
- e_data.dof_dilate_h_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DILATE_HORIZONTAL\n");
-#endif
- e_data.dof_blur1_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define BLUR1\n");
- e_data.dof_blur2_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define BLUR2\n");
- e_data.dof_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define RESOLVE\n");
- }
-
- *prepare_sh = e_data.dof_prepare_sh;
- *downsample_sh = e_data.dof_downsample_sh;
- *blur1_sh = e_data.dof_blur1_sh;
- *blur2_sh = e_data.dof_blur2_sh;
- *resolve_sh = e_data.dof_resolve_sh;
-}
-
-GPUShader *workbench_shader_antialiasing_accumulation_get(void)
-{
- if (e_data.aa_accum_sh == NULL) {
- char *frag = DRW_shader_library_create_shader_string(e_data.lib,
- datatoc_workbench_effect_taa_frag_glsl);
-
- e_data.aa_accum_sh = DRW_shader_create_fullscreen(frag, NULL);
-
- MEM_freeN(frag);
- }
- return e_data.aa_accum_sh;
-}
-
-GPUShader *workbench_shader_antialiasing_get(int stage)
-{
- BLI_assert(stage < 3);
- if (!e_data.smaa_sh[stage]) {
- char stage_define[32];
- BLI_snprintf(stage_define, sizeof(stage_define), "#define SMAA_STAGE %d\n", stage);
-
- e_data.smaa_sh[stage] = GPU_shader_create_from_arrays({
- .vert =
- (const char *[]){
- "#define SMAA_INCLUDE_VS 1\n",
- "#define SMAA_INCLUDE_PS 0\n",
- "uniform vec4 viewportMetrics;\n",
- datatoc_common_smaa_lib_glsl,
- datatoc_workbench_effect_smaa_vert_glsl,
- NULL,
- },
- .frag =
- (const char *[]){
- "#define SMAA_INCLUDE_VS 0\n",
- "#define SMAA_INCLUDE_PS 1\n",
- "uniform vec4 viewportMetrics;\n",
- datatoc_common_smaa_lib_glsl,
- datatoc_workbench_effect_smaa_frag_glsl,
- NULL,
- },
- .defs =
- (const char *[]){
- "#define SMAA_GLSL_3\n",
- "#define SMAA_RT_METRICS viewportMetrics\n",
- "#define SMAA_PRESET_HIGH\n",
- "#define SMAA_LUMA_WEIGHT float4(1.0, 1.0, 1.0, 1.0)\n",
- "#define SMAA_NO_DISCARD\n",
- stage_define,
- NULL,
- },
- });
- }
- return e_data.smaa_sh[stage];
-}
-
-GPUShader *workbench_shader_volume_get(bool slice,
- bool coba,
- eWORKBENCH_VolumeInterpType interp_type,
- bool smoke)
-{
- GPUShader **shader = &e_data.volume_sh[slice][coba][interp_type][smoke];
-
- if (*shader == NULL) {
- DynStr *ds = BLI_dynstr_new();
-
- if (slice) {
- BLI_dynstr_append(ds, "#define VOLUME_SLICE\n");
- }
- if (coba) {
- BLI_dynstr_append(ds, "#define USE_COBA\n");
- }
- switch (interp_type) {
- case WORKBENCH_VOLUME_INTERP_LINEAR:
- BLI_dynstr_append(ds, "#define USE_TRILINEAR\n");
- break;
- case WORKBENCH_VOLUME_INTERP_CUBIC:
- BLI_dynstr_append(ds, "#define USE_TRICUBIC\n");
- break;
- case WORKBENCH_VOLUME_INTERP_CLOSEST:
- BLI_dynstr_append(ds, "#define USE_CLOSEST\n");
- break;
- }
- if (smoke) {
- BLI_dynstr_append(ds, "#define VOLUME_SMOKE\n");
- }
-
- char *defines = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
- char *vert = DRW_shader_library_create_shader_string(e_data.lib,
- datatoc_workbench_volume_vert_glsl);
- char *frag = DRW_shader_library_create_shader_string(e_data.lib,
- datatoc_workbench_volume_frag_glsl);
-
- *shader = DRW_shader_create(vert, NULL, frag, defines);
-
- MEM_freeN(vert);
- MEM_freeN(frag);
- MEM_freeN(defines);
- }
- return *shader;
-}
-
-void workbench_shader_free(void)
-{
- for (int j = 0; j < sizeof(e_data.opaque_prepass_sh_cache) / sizeof(void *); j++) {
- struct GPUShader **sh_array = &e_data.opaque_prepass_sh_cache[0][0][0];
- DRW_SHADER_FREE_SAFE(sh_array[j]);
- }
- for (int j = 0; j < sizeof(e_data.transp_prepass_sh_cache) / sizeof(void *); j++) {
- struct GPUShader **sh_array = &e_data.transp_prepass_sh_cache[0][0][0][0];
- DRW_SHADER_FREE_SAFE(sh_array[j]);
- }
- for (int j = 0; j < ARRAY_SIZE(e_data.opaque_composite_sh); j++) {
- struct GPUShader **sh_array = &e_data.opaque_composite_sh[0];
- DRW_SHADER_FREE_SAFE(sh_array[j]);
- }
- for (int j = 0; j < ARRAY_SIZE(e_data.shadow_depth_pass_sh); j++) {
- struct GPUShader **sh_array = &e_data.shadow_depth_pass_sh[0];
- DRW_SHADER_FREE_SAFE(sh_array[j]);
- }
- for (int j = 0; j < sizeof(e_data.shadow_depth_fail_sh) / sizeof(void *); j++) {
- struct GPUShader **sh_array = &e_data.shadow_depth_fail_sh[0][0];
- DRW_SHADER_FREE_SAFE(sh_array[j]);
- }
- for (int j = 0; j < sizeof(e_data.cavity_sh) / sizeof(void *); j++) {
- struct GPUShader **sh_array = &e_data.cavity_sh[0][0];
- DRW_SHADER_FREE_SAFE(sh_array[j]);
- }
- for (int j = 0; j < ARRAY_SIZE(e_data.smaa_sh); j++) {
- struct GPUShader **sh_array = &e_data.smaa_sh[0];
- DRW_SHADER_FREE_SAFE(sh_array[j]);
- }
- for (int j = 0; j < sizeof(e_data.volume_sh) / sizeof(void *); j++) {
- struct GPUShader **sh_array = &e_data.volume_sh[0][0][0][0];
- DRW_SHADER_FREE_SAFE(sh_array[j]);
- }
-
- DRW_SHADER_FREE_SAFE(e_data.oit_resolve_sh);
- DRW_SHADER_FREE_SAFE(e_data.outline_sh);
- DRW_SHADER_FREE_SAFE(e_data.merge_infront_sh);
-
- DRW_SHADER_FREE_SAFE(e_data.dof_prepare_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_blur1_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_blur2_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh);
-
- DRW_SHADER_FREE_SAFE(e_data.aa_accum_sh);
-
- DRW_SHADER_LIB_FREE_SAFE(e_data.lib);
-}
diff --git a/source/blender/draw/engines/workbench/workbench_shader.cc b/source/blender/draw/engines/workbench/workbench_shader.cc
new file mode 100644
index 00000000000..bbc0bc02b03
--- /dev/null
+++ b/source/blender/draw/engines/workbench/workbench_shader.cc
@@ -0,0 +1,398 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2020, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#include "DRW_render.h"
+
+#include <string>
+
+#include "workbench_engine.h"
+#include "workbench_private.h"
+
+/* Maximum number of variations. */
+#define MAX_LIGHTING 3
+
+enum eWORKBENCH_TextureType {
+ TEXTURE_SH_NONE = 0,
+ TEXTURE_SH_SINGLE,
+ TEXTURE_SH_TILED,
+ TEXTURE_SH_MAX,
+};
+
+static struct {
+ struct GPUShader
+ *opaque_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX][TEXTURE_SH_MAX];
+ struct GPUShader *transp_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX]
+ [MAX_LIGHTING][TEXTURE_SH_MAX];
+
+ struct GPUShader *opaque_composite_sh[MAX_LIGHTING];
+ struct GPUShader *oit_resolve_sh;
+ struct GPUShader *outline_sh;
+ struct GPUShader *merge_infront_sh;
+
+ struct GPUShader *shadow_depth_pass_sh[2];
+ struct GPUShader *shadow_depth_fail_sh[2][2];
+
+ struct GPUShader *cavity_sh[2][2];
+
+ struct GPUShader *dof_prepare_sh;
+ struct GPUShader *dof_downsample_sh;
+ struct GPUShader *dof_blur1_sh;
+ struct GPUShader *dof_blur2_sh;
+ struct GPUShader *dof_resolve_sh;
+
+ struct GPUShader *aa_accum_sh;
+ struct GPUShader *smaa_sh[3];
+
+ struct GPUShader *volume_sh[2][2][3][2];
+
+} e_data = {{{{nullptr}}}};
+
+/* -------------------------------------------------------------------- */
+/** \name Conversions
+ * \{ */
+
+static const char *workbench_lighting_mode_to_str(int light)
+{
+ switch (light) {
+ default:
+ BLI_assert_msg(0, "Error: Unknown lighting mode.");
+ ATTR_FALLTHROUGH;
+ case V3D_LIGHTING_STUDIO:
+ return "_studio";
+ case V3D_LIGHTING_MATCAP:
+ return "_matcap";
+ case V3D_LIGHTING_FLAT:
+ return "_flat";
+ return "";
+ }
+}
+
+static const char *workbench_datatype_mode_to_str(eWORKBENCH_DataType datatype)
+{
+ switch (datatype) {
+ default:
+ BLI_assert_msg(0, "Error: Unknown data mode.");
+ ATTR_FALLTHROUGH;
+ case WORKBENCH_DATATYPE_MESH:
+ return "_mesh";
+ case WORKBENCH_DATATYPE_HAIR:
+ return "_hair";
+ case WORKBENCH_DATATYPE_POINTCLOUD:
+ return "_ptcloud";
+ }
+}
+
+static const char *workbench_volume_interp_to_str(eWORKBENCH_VolumeInterpType interp_type)
+{
+ switch (interp_type) {
+ default:
+ BLI_assert_msg(0, "Error: Unknown lighting mode.");
+ ATTR_FALLTHROUGH;
+ case WORKBENCH_VOLUME_INTERP_LINEAR:
+ return "_linear";
+ case WORKBENCH_VOLUME_INTERP_CUBIC:
+ return "_cubic";
+ case WORKBENCH_VOLUME_INTERP_CLOSEST:
+ return "_closest";
+ }
+}
+
+static const char *workbench_texture_type_to_str(eWORKBENCH_TextureType tex_type)
+{
+ switch (tex_type) {
+ default:
+ BLI_assert_msg(0, "Error: Unknown texture mode.");
+ ATTR_FALLTHROUGH;
+ case TEXTURE_SH_NONE:
+ return "_tex_none";
+ case TEXTURE_SH_TILED:
+ return "_tex_tile";
+ case TEXTURE_SH_SINGLE:
+ return "_tex_single";
+ }
+}
+
+static eWORKBENCH_TextureType workbench_texture_type_get(bool textured, bool tiled)
+{
+ return textured ? (tiled ? TEXTURE_SH_TILED : TEXTURE_SH_SINGLE) : TEXTURE_SH_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shader request
+ * \{ */
+
+static GPUShader *workbench_shader_get_ex(WORKBENCH_PrivateData *wpd,
+ bool transp,
+ eWORKBENCH_DataType datatype,
+ bool textured,
+ bool tiled)
+{
+ eWORKBENCH_TextureType tex_type = workbench_texture_type_get(textured, tiled);
+ int light = wpd->shading.light;
+ BLI_assert(light < MAX_LIGHTING);
+ struct GPUShader **shader =
+ (transp) ? &e_data.transp_prepass_sh_cache[wpd->sh_cfg][datatype][light][tex_type] :
+ &e_data.opaque_prepass_sh_cache[wpd->sh_cfg][datatype][tex_type];
+
+ if (*shader == nullptr) {
+ std::string create_info_name = "workbench";
+ create_info_name += (transp) ? "_transp" : "_opaque";
+ if (transp) {
+ create_info_name += workbench_lighting_mode_to_str(light);
+ }
+ create_info_name += workbench_datatype_mode_to_str(datatype);
+ create_info_name += workbench_texture_type_to_str(tex_type);
+ create_info_name += (wpd->sh_cfg == GPU_SHADER_CFG_CLIPPED) ? "_clip" : "_no_clip";
+
+ *shader = GPU_shader_create_from_info_name(create_info_name.c_str());
+ }
+ return *shader;
+}
+
+GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, eWORKBENCH_DataType datatype)
+{
+ return workbench_shader_get_ex(wpd, false, datatype, false, false);
+}
+
+GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd,
+ eWORKBENCH_DataType datatype,
+ bool tiled)
+{
+ return workbench_shader_get_ex(wpd, false, datatype, true, tiled);
+}
+
+GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd,
+ eWORKBENCH_DataType datatype)
+{
+ return workbench_shader_get_ex(wpd, true, datatype, false, false);
+}
+
+GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd,
+ eWORKBENCH_DataType datatype,
+ bool tiled)
+{
+ return workbench_shader_get_ex(wpd, true, datatype, true, tiled);
+}
+
+GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd)
+{
+ int light = wpd->shading.light;
+ struct GPUShader **shader = &e_data.opaque_composite_sh[light];
+ BLI_assert(light < MAX_LIGHTING);
+
+ if (*shader == nullptr) {
+ std::string create_info_name = "workbench_composite";
+ create_info_name += workbench_lighting_mode_to_str(light);
+ *shader = GPU_shader_create_from_info_name(create_info_name.c_str());
+ }
+ return *shader;
+}
+
+GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *UNUSED(wpd))
+{
+ if (e_data.merge_infront_sh == nullptr) {
+ e_data.merge_infront_sh = GPU_shader_create_from_info_name("workbench_merge_infront");
+ }
+ return e_data.merge_infront_sh;
+}
+
+GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *UNUSED(wpd))
+{
+ if (e_data.oit_resolve_sh == nullptr) {
+ e_data.oit_resolve_sh = GPU_shader_create_from_info_name("workbench_transparent_resolve");
+ }
+ return e_data.oit_resolve_sh;
+}
+
+static GPUShader *workbench_shader_shadow_pass_get_ex(bool depth_pass, bool manifold, bool cap)
+{
+ struct GPUShader **shader = (depth_pass) ? &e_data.shadow_depth_pass_sh[manifold] :
+ &e_data.shadow_depth_fail_sh[manifold][cap];
+
+ if (*shader == nullptr) {
+ std::string create_info_name = "workbench_shadow";
+ create_info_name += (depth_pass) ? "_pass" : "_fail";
+ create_info_name += (manifold) ? "_manifold" : "_no_manifold";
+ create_info_name += (cap) ? "_caps" : "_no_caps";
+#if DEBUG_SHADOW_VOLUME
+ create_info_name += "_debug";
+#endif
+ *shader = GPU_shader_create_from_info_name(create_info_name.c_str());
+ }
+ return *shader;
+}
+
+GPUShader *workbench_shader_shadow_pass_get(bool manifold)
+{
+ return workbench_shader_shadow_pass_get_ex(true, manifold, false);
+}
+
+GPUShader *workbench_shader_shadow_fail_get(bool manifold, bool cap)
+{
+ return workbench_shader_shadow_pass_get_ex(false, manifold, cap);
+}
+
+GPUShader *workbench_shader_cavity_get(bool cavity, bool curvature)
+{
+ BLI_assert(cavity || curvature);
+ struct GPUShader **shader = &e_data.cavity_sh[cavity][curvature];
+
+ if (*shader == nullptr) {
+ std::string create_info_name = "workbench_effect";
+ create_info_name += (cavity) ? "_cavity" : "";
+ create_info_name += (curvature) ? "_curvature" : "";
+ *shader = GPU_shader_create_from_info_name(create_info_name.c_str());
+ }
+ return *shader;
+}
+
+GPUShader *workbench_shader_outline_get(void)
+{
+ if (e_data.outline_sh == nullptr) {
+ e_data.outline_sh = GPU_shader_create_from_info_name("workbench_effect_outline");
+ }
+ return e_data.outline_sh;
+}
+
+void workbench_shader_depth_of_field_get(GPUShader **prepare_sh,
+ GPUShader **downsample_sh,
+ GPUShader **blur1_sh,
+ GPUShader **blur2_sh,
+ GPUShader **resolve_sh)
+{
+ if (e_data.dof_prepare_sh == nullptr) {
+ e_data.dof_prepare_sh = GPU_shader_create_from_info_name("workbench_effect_dof_prepare");
+ e_data.dof_downsample_sh = GPU_shader_create_from_info_name("workbench_effect_dof_downsample");
+#if 0 /* TODO(fclem): finish COC min_max optimization */
+ e_data.dof_flatten_v_sh = GPU_shader_create_from_info_name("workbench_effect_dof_flatten_v");
+ e_data.dof_flatten_h_sh = GPU_shader_create_from_info_name("workbench_effect_dof_flatten_h");
+ e_data.dof_dilate_v_sh = GPU_shader_create_from_info_name("workbench_effect_dof_dilate_v");
+ e_data.dof_dilate_h_sh = GPU_shader_create_from_info_name("workbench_effect_dof_dilate_h");
+#endif
+ e_data.dof_blur1_sh = GPU_shader_create_from_info_name("workbench_effect_dof_blur1");
+ e_data.dof_blur2_sh = GPU_shader_create_from_info_name("workbench_effect_dof_blur2");
+ e_data.dof_resolve_sh = GPU_shader_create_from_info_name("workbench_effect_dof_resolve");
+ }
+
+ *prepare_sh = e_data.dof_prepare_sh;
+ *downsample_sh = e_data.dof_downsample_sh;
+ *blur1_sh = e_data.dof_blur1_sh;
+ *blur2_sh = e_data.dof_blur2_sh;
+ *resolve_sh = e_data.dof_resolve_sh;
+}
+
+GPUShader *workbench_shader_antialiasing_accumulation_get(void)
+{
+ if (e_data.aa_accum_sh == nullptr) {
+ e_data.aa_accum_sh = GPU_shader_create_from_info_name("workbench_taa");
+ }
+ return e_data.aa_accum_sh;
+}
+
+GPUShader *workbench_shader_antialiasing_get(int stage)
+{
+ BLI_assert(stage < 3);
+ GPUShader **shader = &e_data.smaa_sh[stage];
+
+ if (*shader == nullptr) {
+ std::string create_info_name = "workbench_smaa_stage_";
+ create_info_name += std::to_string(stage);
+ *shader = GPU_shader_create_from_info_name(create_info_name.c_str());
+ }
+ return e_data.smaa_sh[stage];
+}
+
+GPUShader *workbench_shader_volume_get(bool slice,
+ bool coba,
+ eWORKBENCH_VolumeInterpType interp_type,
+ bool smoke)
+{
+ GPUShader **shader = &e_data.volume_sh[slice][coba][interp_type][smoke];
+
+ if (*shader == nullptr) {
+ std::string create_info_name = "workbench_volume";
+ create_info_name += (smoke) ? "_smoke" : "_object";
+ create_info_name += workbench_volume_interp_to_str(interp_type);
+ create_info_name += (coba) ? "_coba" : "_no_coba";
+ create_info_name += (slice) ? "_slice" : "_no_slice";
+ *shader = GPU_shader_create_from_info_name(create_info_name.c_str());
+ }
+ return *shader;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cleanup
+ * \{ */
+
+void workbench_shader_free(void)
+{
+ for (int j = 0; j < sizeof(e_data.opaque_prepass_sh_cache) / sizeof(void *); j++) {
+ struct GPUShader **sh_array = &e_data.opaque_prepass_sh_cache[0][0][0];
+ DRW_SHADER_FREE_SAFE(sh_array[j]);
+ }
+ for (int j = 0; j < sizeof(e_data.transp_prepass_sh_cache) / sizeof(void *); j++) {
+ struct GPUShader **sh_array = &e_data.transp_prepass_sh_cache[0][0][0][0];
+ DRW_SHADER_FREE_SAFE(sh_array[j]);
+ }
+ for (int j = 0; j < ARRAY_SIZE(e_data.opaque_composite_sh); j++) {
+ struct GPUShader **sh_array = &e_data.opaque_composite_sh[0];
+ DRW_SHADER_FREE_SAFE(sh_array[j]);
+ }
+ for (int j = 0; j < ARRAY_SIZE(e_data.shadow_depth_pass_sh); j++) {
+ struct GPUShader **sh_array = &e_data.shadow_depth_pass_sh[0];
+ DRW_SHADER_FREE_SAFE(sh_array[j]);
+ }
+ for (int j = 0; j < sizeof(e_data.shadow_depth_fail_sh) / sizeof(void *); j++) {
+ struct GPUShader **sh_array = &e_data.shadow_depth_fail_sh[0][0];
+ DRW_SHADER_FREE_SAFE(sh_array[j]);
+ }
+ for (int j = 0; j < sizeof(e_data.cavity_sh) / sizeof(void *); j++) {
+ struct GPUShader **sh_array = &e_data.cavity_sh[0][0];
+ DRW_SHADER_FREE_SAFE(sh_array[j]);
+ }
+ for (int j = 0; j < ARRAY_SIZE(e_data.smaa_sh); j++) {
+ struct GPUShader **sh_array = &e_data.smaa_sh[0];
+ DRW_SHADER_FREE_SAFE(sh_array[j]);
+ }
+ for (int j = 0; j < sizeof(e_data.volume_sh) / sizeof(void *); j++) {
+ struct GPUShader **sh_array = &e_data.volume_sh[0][0][0][0];
+ DRW_SHADER_FREE_SAFE(sh_array[j]);
+ }
+
+ DRW_SHADER_FREE_SAFE(e_data.oit_resolve_sh);
+ DRW_SHADER_FREE_SAFE(e_data.outline_sh);
+ DRW_SHADER_FREE_SAFE(e_data.merge_infront_sh);
+
+ DRW_SHADER_FREE_SAFE(e_data.dof_prepare_sh);
+ DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh);
+ DRW_SHADER_FREE_SAFE(e_data.dof_blur1_sh);
+ DRW_SHADER_FREE_SAFE(e_data.dof_blur2_sh);
+ DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh);
+
+ DRW_SHADER_FREE_SAFE(e_data.aa_accum_sh);
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/workbench/workbench_shader_shared.h b/source/blender/draw/engines/workbench/workbench_shader_shared.h
index 42d38e54d9a..c63760a634d 100644
--- a/source/blender/draw/engines/workbench/workbench_shader_shared.h
+++ b/source/blender/draw/engines/workbench/workbench_shader_shared.h
@@ -1,6 +1,6 @@
#ifndef GPU_SHADER
-# include "gpu_shader_shared_utils.h"
+# include "GPU_shader_shared_utils.h"
#endif
#define WORKBENCH_SHADER_SHARED_H
@@ -45,4 +45,3 @@ struct WorldData {
};
#define viewport_size_inv viewport_size.zw
-#define packed_rough_metal roughness
diff --git a/source/blender/draw/engines/workbench/workbench_transparent.c b/source/blender/draw/engines/workbench/workbench_transparent.c
index 3be005b43d0..2f9b85acf31 100644
--- a/source/blender/draw/engines/workbench/workbench_transparent.c
+++ b/source/blender/draw/engines/workbench/workbench_transparent.c
@@ -65,7 +65,7 @@ void workbench_transparent_engine_init(WORKBENCH_Data *data)
static void workbench_transparent_lighting_uniforms(WORKBENCH_PrivateData *wpd,
DRWShadingGroup *grp)
{
- DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", false);
if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) {
@@ -76,8 +76,8 @@ static void workbench_transparent_lighting_uniforms(WORKBENCH_PrivateData *wpd,
struct GPUTexture *spec_tx = wpd->studio_light->matcap_specular.gputexture;
const bool use_spec = workbench_is_specular_highlight_enabled(wpd);
spec_tx = (use_spec && spec_tx) ? spec_tx : diff_tx;
- DRW_shgroup_uniform_texture(grp, "matcapDiffuseImage", diff_tx);
- DRW_shgroup_uniform_texture(grp, "matcapSpecularImage", spec_tx);
+ DRW_shgroup_uniform_texture(grp, "matcap_diffuse_tx", diff_tx);
+ DRW_shgroup_uniform_texture(grp, "matcap_specular_tx", spec_tx);
}
}
@@ -111,25 +111,25 @@ void workbench_transparent_cache_init(WORKBENCH_Data *vedata)
sh = workbench_shader_transparent_get(wpd, data);
wpd->prepass[transp][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1);
workbench_transparent_lighting_uniforms(wpd, grp);
wpd->prepass[transp][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */
sh = workbench_shader_transparent_image_get(wpd, data, false);
wpd->prepass[transp][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
workbench_transparent_lighting_uniforms(wpd, grp);
sh = workbench_shader_transparent_image_get(wpd, data, true);
wpd->prepass[transp][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
- DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
+ DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
workbench_transparent_lighting_uniforms(wpd, grp);
}
diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c
index daeadce3059..9632060d69c 100644
--- a/source/blender/draw/engines/workbench/workbench_volume.c
+++ b/source/blender/draw/engines/workbench/workbench_volume.c
@@ -129,7 +129,7 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata,
float step_length = max_ff(1e-16f, dim[axis] * 0.05f);
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
- DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_float_copy(grp, "slicePosition", fds->slice_depth);
DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis);
DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length);
@@ -148,7 +148,7 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata,
step_length = len_v3(dim);
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
- DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slice);
DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length);
DRW_shgroup_uniform_float_copy(grp, "noiseOfs", noise_ofs);
@@ -272,7 +272,7 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata,
const float slice_position = volume->display.slice_depth;
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
- DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_float_copy(grp, "slicePosition", slice_position);
DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis);
DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length);
@@ -299,7 +299,7 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata,
/* Set uniforms. */
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
- DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slice);
DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length);
DRW_shgroup_uniform_float_copy(grp, "noiseOfs", noise_ofs);
diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh
index 7a9bdb377fe..d4491223c10 100644
--- a/source/blender/draw/intern/DRW_gpu_wrapper.hh
+++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh
@@ -72,10 +72,7 @@
#include "draw_texture_pool.h"
-#include "BLI_float4.hh"
-#include "BLI_int2.hh"
-#include "BLI_int3.hh"
-#include "BLI_int4.hh"
+#include "BLI_math_vec_types.hh"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
#include "BLI_utility_mixins.hh"
@@ -105,7 +102,8 @@ class DataBuffer {
T *data_ = nullptr;
int64_t len_ = len;
- BLI_STATIC_ASSERT((sizeof(T) % 16) == 0, "Type need to be aligned to size of float4.");
+ BLI_STATIC_ASSERT(((sizeof(T) * len) % 16) == 0,
+ "Buffer size need to be aligned to size of float4.");
public:
/**
@@ -288,14 +286,18 @@ template<
/** The number of values that can be stored in this uniform buffer. */
int64_t len
/** True if the buffer only resides on GPU memory and cannot be accessed. */
- /* TODO(fclem): Currently unsupported. */
+ /* TODO(@fclem): Currently unsupported. */
/* bool device_only = false */>
class UniformArrayBuffer : public detail::UniformCommon<T, len, false> {
public:
UniformArrayBuffer()
{
- /* TODO(fclem) We should map memory instead. */
- this->data_ = MEM_mallocN_aligned(this->name_);
+ /* TODO(@fclem): We should map memory instead. */
+ this->data_ = (T *)MEM_mallocN_aligned(len * sizeof(T), 16, this->name_);
+ }
+ ~UniformArrayBuffer()
+ {
+ MEM_freeN(this->data_);
}
};
@@ -303,13 +305,13 @@ template<
/** Type of the values stored in this uniform buffer. */
typename T
/** True if the buffer only resides on GPU memory and cannot be accessed. */
- /* TODO(fclem): Currently unsupported. */
+ /* TODO(@fclem): Currently unsupported. */
/* bool device_only = false */>
class UniformBuffer : public T, public detail::UniformCommon<T, 1, false> {
public:
UniformBuffer()
{
- /* TODO(fclem) How could we map this? */
+ /* TODO(@fclem): How could we map this? */
this->data_ = static_cast<T *>(this);
}
@@ -484,7 +486,7 @@ class Texture : NonCopyable {
* Ensure the texture has the correct properties. Recreating it if needed.
* Return true if a texture has been created.
*/
- bool ensure_2d(eGPUTextureFormat format, const int2 &extent, float *data = nullptr, int mips = 1)
+ bool ensure_2d(eGPUTextureFormat format, int2 extent, float *data = nullptr, int mips = 1)
{
return ensure_impl(UNPACK2(extent), 0, mips, format, data, false, false);
}
@@ -493,11 +495,8 @@ class Texture : NonCopyable {
* Ensure the texture has the correct properties. Recreating it if needed.
* Return true if a texture has been created.
*/
- bool ensure_2d_array(eGPUTextureFormat format,
- const int2 &extent,
- int layers,
- float *data = nullptr,
- int mips = 1)
+ bool ensure_2d_array(
+ eGPUTextureFormat format, int2 extent, int layers, float *data = nullptr, int mips = 1)
{
return ensure_impl(UNPACK2(extent), layers, mips, format, data, true, false);
}
@@ -506,7 +505,7 @@ class Texture : NonCopyable {
* Ensure the texture has the correct properties. Recreating it if needed.
* Return true if a texture has been created.
*/
- bool ensure_3d(eGPUTextureFormat format, const int3 &extent, float *data = nullptr, int mips = 1)
+ bool ensure_3d(eGPUTextureFormat format, int3 extent, float *data = nullptr, int mips = 1)
{
return ensure_impl(UNPACK3(extent), mips, format, data, false, false);
}
@@ -599,14 +598,6 @@ class Texture : NonCopyable {
/**
* Clear the entirety of the texture using one pixel worth of data.
*/
- void clear(uchar4 values)
- {
- GPU_texture_clear(tx_, GPU_DATA_UBYTE, &values[0]);
- }
-
- /**
- * Clear the entirety of the texture using one pixel worth of data.
- */
void clear(int4 values)
{
GPU_texture_clear(tx_, GPU_DATA_INT, &values[0]);
@@ -634,6 +625,15 @@ class Texture : NonCopyable {
GPU_TEXTURE_FREE_SAFE(tx_);
}
+ /**
+ * Swap the content of the two textures.
+ */
+ static void swap(Texture &a, Texture &b)
+ {
+ SWAP(GPUTexture *, a.tx_, b.tx_);
+ SWAP(const char *, a.name_, b.name_);
+ }
+
private:
bool ensure_impl(int w,
int h = 0,
@@ -645,7 +645,7 @@ class Texture : NonCopyable {
bool cubemap = false)
{
- /* TODO(fclem) In the future, we need to check if mip_count did not change.
+ /* TODO(@fclem): In the future, we need to check if mip_count did not change.
* For now it's ok as we always define all MIP level. */
if (tx_) {
int3 size = this->size();
@@ -657,7 +657,7 @@ class Texture : NonCopyable {
if (tx_ == nullptr) {
tx_ = create(w, h, d, mips, format, data, layered, cubemap);
if (mips > 1) {
- /* TODO(fclem) Remove once we have immutable storage or when mips are
+ /* TODO(@fclem): Remove once we have immutable storage or when mips are
* generated on creation. */
GPU_texture_generate_mipmap(tx_);
}
@@ -678,20 +678,20 @@ class Texture : NonCopyable {
if (h == 0) {
return GPU_texture_create_1d(name_, w, mips, format, data);
}
- else if (d == 0) {
+ else if (cubemap) {
if (layered) {
- return GPU_texture_create_1d_array(name_, w, h, mips, format, data);
+ return GPU_texture_create_cube_array(name_, w, d, mips, format, data);
}
else {
- return GPU_texture_create_2d(name_, w, h, mips, format, data);
+ return GPU_texture_create_cube(name_, w, mips, format, data);
}
}
- else if (cubemap) {
+ else if (d == 0) {
if (layered) {
- return GPU_texture_create_cube_array(name_, w, d, mips, format, data);
+ return GPU_texture_create_1d_array(name_, w, h, mips, format, data);
}
else {
- return GPU_texture_create_cube(name_, w, mips, format, data);
+ return GPU_texture_create_2d(name_, w, h, mips, format, data);
}
}
else {
@@ -713,7 +713,7 @@ class TextureFromPool : public Texture, NonMovable {
TextureFromPool(const char *name = "gpu::Texture") : Texture(name){};
/* Always use `release()` after rendering. */
- void acquire(int w, int h, eGPUTextureFormat format, void *owner_)
+ void acquire(int2 extent, eGPUTextureFormat format, void *owner_)
{
if (this->tx_ == nullptr) {
if (tx_tmp_saved_ != nullptr) {
@@ -721,7 +721,7 @@ class TextureFromPool : public Texture, NonMovable {
return;
}
DrawEngineType *owner = (DrawEngineType *)owner_;
- this->tx_ = DRW_texture_pool_query_2d(w, h, format, owner);
+ this->tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), format, owner);
}
}
@@ -750,11 +750,6 @@ class TextureFromPool : public Texture, NonMovable {
bool ensure_cube_array(int, int, int, eGPUTextureFormat, float *) = delete;
void filter_mode(bool) = delete;
void free() = delete;
- /**
- * Forbid the use of DRW_shgroup_uniform_texture.
- * Use DRW_shgroup_uniform_texture_ref instead.
- */
- operator GPUTexture *() const = delete;
};
/** \} */
@@ -805,6 +800,15 @@ class Framebuffer : NonCopyable {
{
return fb_;
}
+
+ /**
+ * Swap the content of the two framebuffer.
+ */
+ static void swap(Framebuffer &a, Framebuffer &b)
+ {
+ SWAP(GPUFrameBuffer *, a.fb_, b.fb_);
+ SWAP(const char *, a.name_, b.name_);
+ }
};
/** \} */
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index b16caf49209..d531d8ad9f8 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -66,6 +66,15 @@
extern "C" {
#endif
+/* Uncomment to track unused resource bindings. */
+// #define DRW_UNUSED_RESOURCE_TRACKING
+
+#ifdef DRW_UNUSED_RESOURCE_TRACKING
+# define DRW_DEBUG_FILE_LINE_ARGS , const char *file, int line
+#else
+# define DRW_DEBUG_FILE_LINE_ARGS
+#endif
+
struct GPUBatch;
struct GPUMaterial;
struct GPUShader;
@@ -293,7 +302,9 @@ DRWShaderLibrary *DRW_shader_library_create(void);
/**
* \warning Each library must be added after all its dependencies.
*/
-void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name);
+void DRW_shader_library_add_file(DRWShaderLibrary *lib,
+ const char *lib_code,
+ const char *lib_name);
#define DRW_SHADER_LIB_ADD(lib, lib_name) \
DRW_shader_library_add_file(lib, datatoc_##lib_name##_glsl, STRINGIFY(lib_name) ".glsl")
@@ -466,6 +477,10 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
int groups_x_len,
int groups_y_len,
int groups_z_len);
+/**
+ * \warning this keeps the ref to groups_ref until it actually dispatch.
+ */
+void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]);
void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count);
void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count);
void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count);
@@ -532,6 +547,11 @@ void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup,
void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask);
/**
+ * Issue a barrier command.
+ */
+void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type);
+
+/**
* Issue a clear command.
*/
void DRW_shgroup_clear_framebuffer(DRWShadingGroup *shgroup,
@@ -557,12 +577,12 @@ void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup,
void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup,
const char *name,
struct GPUTexture **tex);
-void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup,
- const char *name,
- const struct GPUUniformBuf *ubo);
-void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup,
- const char *name,
- struct GPUUniformBuf **ubo);
+void DRW_shgroup_uniform_block_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ const struct GPUUniformBuf *ubo DRW_DEBUG_FILE_LINE_ARGS);
+void DRW_shgroup_uniform_block_ref_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ struct GPUUniformBuf **ubo DRW_DEBUG_FILE_LINE_ARGS);
void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup,
const char *name,
const float *value,
@@ -622,9 +642,32 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup,
const char *name,
const float (*value)[4],
int arraysize);
-void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
- const char *name,
- struct GPUVertBuf *vertex_buffer);
+void DRW_shgroup_vertex_buffer_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ struct GPUVertBuf *vertex_buffer DRW_DEBUG_FILE_LINE_ARGS);
+void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ struct GPUVertBuf **vertex_buffer DRW_DEBUG_FILE_LINE_ARGS);
+
+#ifdef DRW_UNUSED_RESOURCE_TRACKING
+# define DRW_shgroup_vertex_buffer(shgroup, name, vert) \
+ DRW_shgroup_vertex_buffer_ex(shgroup, name, vert, __FILE__, __LINE__)
+# define DRW_shgroup_vertex_buffer_ref(shgroup, name, vert) \
+ DRW_shgroup_vertex_buffer_ref_ex(shgroup, name, vert, __FILE__, __LINE__)
+# define DRW_shgroup_uniform_block(shgroup, name, ubo) \
+ DRW_shgroup_uniform_block_ex(shgroup, name, ubo, __FILE__, __LINE__)
+# define DRW_shgroup_uniform_block_ref(shgroup, name, ubo) \
+ DRW_shgroup_uniform_block_ref_ex(shgroup, name, ubo, __FILE__, __LINE__)
+#else
+# define DRW_shgroup_vertex_buffer(shgroup, name, vert) \
+ DRW_shgroup_vertex_buffer_ex(shgroup, name, vert)
+# define DRW_shgroup_vertex_buffer_ref(shgroup, name, vert) \
+ DRW_shgroup_vertex_buffer_ref_ex(shgroup, name, vert)
+# define DRW_shgroup_uniform_block(shgroup, name, ubo) \
+ DRW_shgroup_uniform_block_ex(shgroup, name, ubo)
+# define DRW_shgroup_uniform_block_ref(shgroup, name, ubo) \
+ DRW_shgroup_uniform_block_ref_ex(shgroup, name, ubo)
+#endif
bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup);
@@ -696,7 +739,7 @@ const DRWView *DRW_view_default_get(void);
/**
* MUST only be called once per render and only in render mode. Sets default view.
*/
-void DRW_view_default_set(DRWView *view);
+void DRW_view_default_set(const DRWView *view);
/**
* \warning Only use in render AND only if you are going to set view_default again.
*/
@@ -704,7 +747,7 @@ void DRW_view_reset(void);
/**
* Set active view for rendering.
*/
-void DRW_view_set_active(DRWView *view);
+void DRW_view_set_active(const DRWView *view);
const DRWView *DRW_view_get_active(void);
/**
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index 1110658e3b2..430b28f1224 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -19,7 +19,7 @@
*/
#include "DNA_curve_types.h"
-#include "DNA_hair_types.h"
+#include "DNA_curves_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
@@ -835,7 +835,7 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold)
return NULL;
case OB_MBALL:
return DRW_cache_mball_edge_detection_get(ob, r_is_manifold);
- case OB_HAIR:
+ case OB_CURVES:
return NULL;
case OB_POINTCLOUD:
return NULL;
@@ -859,7 +859,7 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob)
return NULL;
case OB_MBALL:
return DRW_cache_mball_face_wireframe_get(ob);
- case OB_HAIR:
+ case OB_CURVES:
return NULL;
case OB_POINTCLOUD:
return DRW_pointcloud_batch_cache_get_dots(ob);
@@ -886,7 +886,7 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob)
return NULL;
case OB_MBALL:
return NULL;
- case OB_HAIR:
+ case OB_CURVES:
return NULL;
case OB_POINTCLOUD:
return NULL;
@@ -910,7 +910,7 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob)
return NULL;
case OB_MBALL:
return DRW_cache_mball_surface_get(ob);
- case OB_HAIR:
+ case OB_CURVES:
return NULL;
case OB_POINTCLOUD:
return DRW_cache_pointcloud_surface_get(ob);
@@ -935,7 +935,7 @@ GPUVertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob)
return DRW_curve_batch_cache_pos_vertbuf_get(ob->data);
case OB_MBALL:
return DRW_mball_batch_cache_pos_vertbuf_get(ob);
- case OB_HAIR:
+ case OB_CURVES:
return NULL;
case OB_POINTCLOUD:
return NULL;
@@ -960,15 +960,15 @@ int DRW_cache_object_material_count_get(struct Object *ob)
switch (type) {
case OB_MESH:
- return DRW_mesh_material_count_get((me != NULL) ? me : ob->data);
+ return DRW_mesh_material_count_get(ob, (me != NULL) ? me : ob->data);
case OB_CURVE:
case OB_SURF:
case OB_FONT:
return DRW_curve_material_count_get(ob->data);
case OB_MBALL:
return DRW_metaball_material_count_get(ob->data);
- case OB_HAIR:
- return DRW_hair_material_count_get(ob->data);
+ case OB_CURVES:
+ return DRW_curves_material_count_get(ob->data);
case OB_POINTCLOUD:
return DRW_pointcloud_material_count_get(ob->data);
case OB_VOLUME:
@@ -994,7 +994,7 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob,
return NULL;
case OB_MBALL:
return DRW_cache_mball_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
- case OB_HAIR:
+ case OB_CURVES:
return NULL;
case OB_POINTCLOUD:
return DRW_cache_pointcloud_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
@@ -2875,7 +2875,7 @@ GPUBatch *DRW_cache_mesh_surface_get(Object *ob)
GPUBatch *DRW_cache_mesh_surface_edges_get(Object *ob)
{
BLI_assert(ob->type == OB_MESH);
- return DRW_mesh_batch_cache_get_surface_edges(ob->data);
+ return DRW_mesh_batch_cache_get_surface_edges(ob, ob->data);
}
GPUBatch **DRW_cache_mesh_surface_shaded_get(Object *ob,
@@ -2883,31 +2883,31 @@ GPUBatch **DRW_cache_mesh_surface_shaded_get(Object *ob,
uint gpumat_array_len)
{
BLI_assert(ob->type == OB_MESH);
- return DRW_mesh_batch_cache_get_surface_shaded(ob->data, gpumat_array, gpumat_array_len);
+ return DRW_mesh_batch_cache_get_surface_shaded(ob, ob->data, gpumat_array, gpumat_array_len);
}
GPUBatch **DRW_cache_mesh_surface_texpaint_get(Object *ob)
{
BLI_assert(ob->type == OB_MESH);
- return DRW_mesh_batch_cache_get_surface_texpaint(ob->data);
+ return DRW_mesh_batch_cache_get_surface_texpaint(ob, ob->data);
}
GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(Object *ob)
{
BLI_assert(ob->type == OB_MESH);
- return DRW_mesh_batch_cache_get_surface_texpaint_single(ob->data);
+ return DRW_mesh_batch_cache_get_surface_texpaint_single(ob, ob->data);
}
GPUBatch *DRW_cache_mesh_surface_vertpaint_get(Object *ob)
{
BLI_assert(ob->type == OB_MESH);
- return DRW_mesh_batch_cache_get_surface_vertpaint(ob->data);
+ return DRW_mesh_batch_cache_get_surface_vertpaint(ob, ob->data);
}
GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(Object *ob)
{
BLI_assert(ob->type == OB_MESH);
- return DRW_mesh_batch_cache_get_surface_sculpt(ob->data);
+ return DRW_mesh_batch_cache_get_surface_sculpt(ob, ob->data);
}
GPUBatch *DRW_cache_mesh_surface_weights_get(Object *ob)
@@ -3091,7 +3091,7 @@ GPUBatch **DRW_cache_surf_surface_shaded_get(Object *ob,
struct Curve *cu = ob->data;
struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(ob);
if (mesh_eval != NULL) {
- return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len);
+ return DRW_mesh_batch_cache_get_surface_shaded(ob, mesh_eval, gpumat_array, gpumat_array_len);
}
return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len);
@@ -3385,7 +3385,7 @@ void drw_batch_cache_validate(Object *ob)
struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(ob);
switch (ob->type) {
case OB_MESH:
- DRW_mesh_batch_cache_validate((Mesh *)ob->data);
+ DRW_mesh_batch_cache_validate(ob, (Mesh *)ob->data);
break;
case OB_CURVE:
case OB_FONT:
@@ -3393,7 +3393,7 @@ void drw_batch_cache_validate(Object *ob)
break;
case OB_SURF:
if (mesh_eval != NULL) {
- DRW_mesh_batch_cache_validate(mesh_eval);
+ DRW_mesh_batch_cache_validate(ob, mesh_eval);
}
DRW_curve_batch_cache_validate((Curve *)ob->data);
break;
@@ -3403,8 +3403,8 @@ void drw_batch_cache_validate(Object *ob)
case OB_LATTICE:
DRW_lattice_batch_cache_validate((Lattice *)ob->data);
break;
- case OB_HAIR:
- DRW_hair_batch_cache_validate((Hair *)ob->data);
+ case OB_CURVES:
+ DRW_curves_batch_cache_validate((Curves *)ob->data);
break;
case OB_POINTCLOUD:
DRW_pointcloud_batch_cache_validate((PointCloud *)ob->data);
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index 30e5a10df91..b94dd466364 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -246,14 +246,14 @@ struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob,
struct GPUBatch *DRW_cache_mball_face_wireframe_get(struct Object *ob);
struct GPUBatch *DRW_cache_mball_edge_detection_get(struct Object *ob, bool *r_is_manifold);
-/* Hair */
-
-struct GPUBatch *DRW_cache_hair_surface_get(struct Object *ob);
-struct GPUBatch **DRW_cache_hair_surface_shaded_get(struct Object *ob,
- struct GPUMaterial **gpumat_array,
- uint gpumat_array_len);
-struct GPUBatch *DRW_cache_hair_face_wireframe_get(struct Object *ob);
-struct GPUBatch *DRW_cache_hair_edge_detection_get(struct Object *ob, bool *r_is_manifold);
+/* Curves */
+
+struct GPUBatch *DRW_cache_curves_surface_get(struct Object *ob);
+struct GPUBatch **DRW_cache_curves_surface_shaded_get(struct Object *ob,
+ struct GPUMaterial **gpumat_array,
+ uint gpumat_array_len);
+struct GPUBatch *DRW_cache_curves_face_wireframe_get(struct Object *ob);
+struct GPUBatch *DRW_cache_curves_edge_detection_get(struct Object *ob, bool *r_is_manifold);
/* PointCloud */
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index bd4edaf09fb..e7f66ebacd0 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -28,6 +28,7 @@ struct TaskGraph;
#include "DNA_customdata_types.h"
#include "BKE_attribute.h"
+#include "BKE_object.h"
#include "GPU_batch.h"
#include "GPU_index_buffer.h"
@@ -107,11 +108,13 @@ ENUM_OPERATORS(eMRDataType, MR_DATA_POLYS_SORTED)
extern "C" {
#endif
-BLI_INLINE int mesh_render_mat_len_get(const Mesh *me)
+BLI_INLINE int mesh_render_mat_len_get(const Object *object, const Mesh *me)
{
- /* In edit mode, the displayed mesh is stored in the edit-mesh. */
- if (me->edit_mesh && me->edit_mesh->mesh_eval_final) {
- return MAX2(1, me->edit_mesh->mesh_eval_final->totcol);
+ if (me->edit_mesh != NULL) {
+ const Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object);
+ if (editmesh_eval_final != NULL) {
+ return MAX2(1, editmesh_eval_final->totcol);
+ }
}
return MAX2(1, me->totcol);
}
@@ -328,6 +331,7 @@ typedef struct MeshBatchCache {
void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshBatchCache *cache,
MeshBufferCache *mbc,
+ Object *object,
Mesh *me,
bool is_editmode,
bool is_paint_mode,
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
index fe55778527b..987ddf3a938 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -570,6 +570,7 @@ static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *t
static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshBatchCache *cache,
MeshBufferCache *mbc,
+ Object *object,
Mesh *me,
const bool is_editmode,
@@ -615,7 +616,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
*/
const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
GPU_use_hq_normals_workaround();
- const bool override_single_mat = mesh_render_mat_len_get(me) <= 1;
+ const bool override_single_mat = mesh_render_mat_len_get(object, me) <= 1;
/* Create an array containing all the extractors that needs to be executed. */
ExtractorRunDatas extractors;
@@ -700,7 +701,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
#endif
MeshRenderData *mr = mesh_render_data_create(
- me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts);
+ object, me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts);
mr->use_hide = use_hide;
mr->use_subsurf_fdots = use_subsurf_fdots;
mr->use_final_mesh = do_final;
@@ -902,6 +903,7 @@ extern "C" {
void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshBatchCache *cache,
MeshBufferCache *mbc,
+ Object *object,
Mesh *me,
const bool is_editmode,
@@ -918,6 +920,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
blender::draw::mesh_buffer_cache_create_requested(task_graph,
cache,
mbc,
+ object,
me,
is_editmode,
is_paint_mode,
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
index 29430c46d93..a47a124bd24 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
@@ -438,7 +438,8 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_
}
}
-MeshRenderData *mesh_render_data_create(Mesh *me,
+MeshRenderData *mesh_render_data_create(Object *object,
+ Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
const bool is_mode_active,
@@ -449,15 +450,18 @@ MeshRenderData *mesh_render_data_create(Mesh *me,
{
MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__);
mr->toolsettings = ts;
- mr->mat_len = mesh_render_mat_len_get(me);
+ mr->mat_len = mesh_render_mat_len_get(object, me);
copy_m4_m4(mr->obmat, obmat);
if (is_editmode) {
- BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final);
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object);
+ Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(object);
+
+ BLI_assert(editmesh_eval_cage && editmesh_eval_final);
mr->bm = me->edit_mesh->bm;
mr->edit_bmesh = me->edit_mesh;
- mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage;
+ mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage;
mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL;
if (mr->edit_data) {
@@ -507,7 +511,7 @@ MeshRenderData *mesh_render_data_create(Mesh *me,
/* Seems like the mesh_eval_final do not have the right origin indices.
* Force not mapped in this case. */
- if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) {
+ if (has_mdata && do_final && editmesh_eval_final != editmesh_eval_cage) {
// mr->edit_bmesh = NULL;
mr->extract_type = MR_EXTRACT_MESH;
}
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index 77b507bc2b6..9c6814d910e 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -33,7 +33,7 @@ struct ParticleSystem;
struct TaskGraph;
struct Curve;
-struct Hair;
+struct Curves;
struct Lattice;
struct Mesh;
struct MetaBall;
@@ -60,7 +60,7 @@ void DRW_curve_batch_cache_validate(struct Curve *cu);
void DRW_curve_batch_cache_free(struct Curve *cu);
void DRW_mesh_batch_cache_dirty_tag(struct Mesh *me, eMeshBatchDirtyMode mode);
-void DRW_mesh_batch_cache_validate(struct Mesh *me);
+void DRW_mesh_batch_cache_validate(struct Object *object, struct Mesh *me);
void DRW_mesh_batch_cache_free(struct Mesh *me);
void DRW_lattice_batch_cache_dirty_tag(struct Lattice *lt, int mode);
@@ -73,9 +73,9 @@ void DRW_particle_batch_cache_free(struct ParticleSystem *psys);
void DRW_gpencil_batch_cache_dirty_tag(struct bGPdata *gpd);
void DRW_gpencil_batch_cache_free(struct bGPdata *gpd);
-void DRW_hair_batch_cache_dirty_tag(struct Hair *hair, int mode);
-void DRW_hair_batch_cache_validate(struct Hair *hair);
-void DRW_hair_batch_cache_free(struct Hair *hair);
+void DRW_curves_batch_cache_dirty_tag(struct Curves *curves, int mode);
+void DRW_curves_batch_cache_validate(struct Curves *curves);
+void DRW_curves_batch_cache_free(struct Curves *curves);
void DRW_pointcloud_batch_cache_dirty_tag(struct PointCloud *pointcloud, int mode);
void DRW_pointcloud_batch_cache_validate(struct PointCloud *pointcloud);
@@ -188,7 +188,7 @@ struct GPUBatch *DRW_lattice_batch_cache_get_edit_verts(struct Lattice *lt);
/** \name Hair
* \{ */
-int DRW_hair_material_count_get(struct Hair *hair);
+int DRW_curves_material_count_get(struct Curves *curves);
/** \} */
@@ -236,14 +236,18 @@ struct GPUBatch *DRW_mesh_batch_cache_get_all_edges(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_loose_edges(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edge_detection(struct Mesh *me, bool *r_is_manifold);
struct GPUBatch *DRW_mesh_batch_cache_get_surface(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_surface_edges(struct Mesh *me);
-struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Mesh *me,
+struct GPUBatch *DRW_mesh_batch_cache_get_surface_edges(struct Object *object, struct Mesh *me);
+struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Object *object,
+ struct Mesh *me,
struct GPUMaterial **gpumat_array,
uint gpumat_array_len);
-struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(struct Mesh *me);
+struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Object *object,
+ struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Object *object,
+ struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Object *object,
+ struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(struct Object *object, struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_sculpt_overlays(struct Mesh *me);
@@ -293,14 +297,16 @@ struct GPUBatch *DRW_mesh_batch_cache_get_wireframes_face(struct Mesh *me);
* The `cache->tot_area` and cache->tot_uv_area` update are calculation are
* only valid after calling `DRW_mesh_batch_cache_create_requested`.
*/
-struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(struct Mesh *me,
+struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(struct Object *object,
+ struct Mesh *me,
float **tot_area,
float **tot_uv_area);
-struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(struct Object *object,
+ struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(struct Object *object, struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(struct Object *object, struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(struct Object *object, struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Object *object, struct Mesh *me);
/** \} */
@@ -308,7 +314,7 @@ struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Mesh *me);
/** \name For Image UV Editor
* \{ */
-struct GPUBatch *DRW_mesh_batch_cache_get_uv_edges(struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_uv_edges(struct Object *object, struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(struct Mesh *me);
/** \} */
@@ -321,7 +327,7 @@ struct GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(struct Mesh *me);
struct GPUVertBuf *DRW_curve_batch_cache_pos_vertbuf_get(struct Curve *cu);
struct GPUVertBuf *DRW_mball_batch_cache_pos_vertbuf_get(struct Object *ob);
-int DRW_mesh_material_count_get(const struct Mesh *me);
+int DRW_mesh_material_count_get(const struct Object *object, const struct Mesh *me);
/* See 'common_globals_lib.glsl' for duplicate defines. */
diff --git a/source/blender/draw/intern/draw_cache_impl_hair.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc
index 80f0a3daecc..8ec97495fcf 100644
--- a/source/blender/draw/intern/draw_cache_impl_hair.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curves.cc
@@ -29,13 +29,16 @@
#include "BLI_listbase.h"
#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_math_vector.h"
+#include "BLI_math_vector.hh"
+#include "BLI_span.hh"
#include "BLI_utildefines.h"
-#include "DNA_hair_types.h"
+#include "DNA_curves_types.h"
#include "DNA_object_types.h"
-#include "BKE_hair.h"
+#include "BKE_curves.h"
#include "GPU_batch.h"
#include "GPU_material.h"
@@ -44,7 +47,11 @@
#include "draw_cache_impl.h" /* own include */
#include "draw_hair_private.h" /* own include */
-static void hair_batch_cache_clear(Hair *hair);
+using blender::float3;
+using blender::IndexRange;
+using blender::Span;
+
+static void curves_batch_cache_clear(Curves *curves);
/* ---------------------------------------------------------------------- */
/* Hair GPUBatch Cache */
@@ -58,19 +65,19 @@ struct HairBatchCache {
/* GPUBatch cache management. */
-static bool hair_batch_cache_valid(Hair *hair)
+static bool curves_batch_cache_valid(Curves *curves)
{
- HairBatchCache *cache = static_cast<HairBatchCache *>(hair->batch_cache);
+ HairBatchCache *cache = static_cast<HairBatchCache *>(curves->batch_cache);
return (cache && cache->is_dirty == false);
}
-static void hair_batch_cache_init(Hair *hair)
+static void curves_batch_cache_init(Curves *curves)
{
- HairBatchCache *cache = static_cast<HairBatchCache *>(hair->batch_cache);
+ HairBatchCache *cache = static_cast<HairBatchCache *>(curves->batch_cache);
if (!cache) {
cache = MEM_cnew<HairBatchCache>(__func__);
- hair->batch_cache = cache;
+ curves->batch_cache = cache;
}
else {
memset(cache, 0, sizeof(*cache));
@@ -79,28 +86,28 @@ static void hair_batch_cache_init(Hair *hair)
cache->is_dirty = false;
}
-void DRW_hair_batch_cache_validate(Hair *hair)
+void DRW_curves_batch_cache_validate(Curves *curves)
{
- if (!hair_batch_cache_valid(hair)) {
- hair_batch_cache_clear(hair);
- hair_batch_cache_init(hair);
+ if (!curves_batch_cache_valid(curves)) {
+ curves_batch_cache_clear(curves);
+ curves_batch_cache_init(curves);
}
}
-static HairBatchCache *hair_batch_cache_get(Hair *hair)
+static HairBatchCache *curves_batch_cache_get(Curves *curves)
{
- DRW_hair_batch_cache_validate(hair);
- return static_cast<HairBatchCache *>(hair->batch_cache);
+ DRW_curves_batch_cache_validate(curves);
+ return static_cast<HairBatchCache *>(curves->batch_cache);
}
-void DRW_hair_batch_cache_dirty_tag(Hair *hair, int mode)
+void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode)
{
- HairBatchCache *cache = static_cast<HairBatchCache *>(hair->batch_cache);
+ HairBatchCache *cache = static_cast<HairBatchCache *>(curves->batch_cache);
if (cache == nullptr) {
return;
}
switch (mode) {
- case BKE_HAIR_BATCH_DIRTY_ALL:
+ case BKE_CURVES_BATCH_DIRTY_ALL:
cache->is_dirty = true;
break;
default:
@@ -108,9 +115,9 @@ void DRW_hair_batch_cache_dirty_tag(Hair *hair, int mode)
}
}
-static void hair_batch_cache_clear(Hair *hair)
+static void curves_batch_cache_clear(Curves *curves)
{
- HairBatchCache *cache = static_cast<HairBatchCache *>(hair->batch_cache);
+ HairBatchCache *cache = static_cast<HairBatchCache *>(curves->batch_cache);
if (!cache) {
return;
}
@@ -118,69 +125,67 @@ static void hair_batch_cache_clear(Hair *hair)
particle_batch_cache_clear_hair(&cache->hair);
}
-void DRW_hair_batch_cache_free(Hair *hair)
+void DRW_curves_batch_cache_free(Curves *curves)
{
- hair_batch_cache_clear(hair);
- MEM_SAFE_FREE(hair->batch_cache);
+ curves_batch_cache_clear(curves);
+ MEM_SAFE_FREE(curves->batch_cache);
}
-static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache)
+static void ensure_seg_pt_count(Curves *curves, ParticleHairCache *curves_cache)
{
- if ((hair_cache->pos != nullptr && hair_cache->indices != nullptr) ||
- (hair_cache->proc_point_buf != nullptr)) {
+ if ((curves_cache->pos != nullptr && curves_cache->indices != nullptr) ||
+ (curves_cache->proc_point_buf != nullptr)) {
return;
}
- hair_cache->strands_len = 0;
- hair_cache->elems_len = 0;
- hair_cache->point_len = 0;
-
- HairCurve *curve = hair->curves;
- int num_curves = hair->totcurve;
- for (int i = 0; i < num_curves; i++, curve++) {
- hair_cache->strands_len++;
- hair_cache->elems_len += curve->numpoints + 1;
- hair_cache->point_len += curve->numpoints;
- }
+ curves_cache->strands_len = curves->geometry.curve_size;
+ curves_cache->elems_len = curves->geometry.point_size + curves->geometry.curve_size;
+ curves_cache->point_len = curves->geometry.point_size;
}
-static void hair_batch_cache_fill_segments_proc_pos(Hair *hair,
- GPUVertBufRaw *attr_step,
- GPUVertBufRaw *length_step)
+static void curves_batch_cache_fill_segments_proc_pos(Curves *curves,
+ GPUVertBufRaw *attr_step,
+ GPUVertBufRaw *length_step)
{
/* TODO: use hair radius layer if available. */
- HairCurve *curve = hair->curves;
- int num_curves = hair->totcurve;
- for (int i = 0; i < num_curves; i++, curve++) {
- float(*curve_co)[3] = hair->co + curve->firstpoint;
+ const int curve_size = curves->geometry.curve_size;
+ Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1};
+
+ Span<float3> positions{(float3 *)curves->geometry.position, curves->geometry.point_size};
+
+ for (const int i : IndexRange(curve_size)) {
+ const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
+
+ Span<float3> spline_positions = positions.slice(curve_range);
float total_len = 0.0f;
- float *co_prev = nullptr, *seg_data_first;
- for (int j = 0; j < curve->numpoints; j++) {
+ float *seg_data_first;
+ for (const int i_spline : spline_positions.index_range()) {
float *seg_data = (float *)GPU_vertbuf_raw_step(attr_step);
- copy_v3_v3(seg_data, curve_co[j]);
- if (co_prev) {
- total_len += len_v3v3(co_prev, curve_co[j]);
+ copy_v3_v3(seg_data, spline_positions[i_spline]);
+ if (i_spline == 0) {
+ seg_data_first = seg_data;
}
else {
- seg_data_first = seg_data;
+ total_len += blender::math::distance(spline_positions[i_spline - 1],
+ spline_positions[i_spline]);
}
seg_data[3] = total_len;
- co_prev = curve_co[j];
}
- /* Assign length value*/
+ /* Assign length value. */
*(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
- for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) {
+ for ([[maybe_unused]] const int i_spline : spline_positions.index_range()) {
seg_data_first[3] /= total_len;
+ seg_data_first += 4;
}
}
}
}
-static void hair_batch_cache_ensure_procedural_pos(Hair *hair,
- ParticleHairCache *cache,
- GPUMaterial *gpu_material)
+static void curves_batch_cache_ensure_procedural_pos(Curves *curves,
+ ParticleHairCache *cache,
+ GPUMaterial *gpu_material)
{
if (cache->proc_point_buf == nullptr) {
/* initialize vertex format */
@@ -203,7 +208,7 @@ static void hair_batch_cache_ensure_procedural_pos(Hair *hair,
GPUVertBufRaw length_step;
GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step);
- hair_batch_cache_fill_segments_proc_pos(hair, &point_step, &length_step);
+ curves_batch_cache_fill_segments_proc_pos(curves, &point_step, &length_step);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(cache->proc_point_buf);
@@ -222,19 +227,23 @@ static void hair_batch_cache_ensure_procedural_pos(Hair *hair,
}
}
-static void hair_batch_cache_fill_strands_data(Hair *hair,
- GPUVertBufRaw *data_step,
- GPUVertBufRaw *seg_step)
+static void curves_batch_cache_fill_strands_data(Curves *curves,
+ GPUVertBufRaw *data_step,
+ GPUVertBufRaw *seg_step)
{
- HairCurve *curve = hair->curves;
- int num_curves = hair->totcurve;
- for (int i = 0; i < num_curves; i++, curve++) {
- *(uint *)GPU_vertbuf_raw_step(data_step) = curve->firstpoint;
- *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve->numpoints - 1;
+ const int curve_size = curves->geometry.curve_size;
+ Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1};
+
+ for (const int i : IndexRange(curve_size)) {
+ const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
+
+ *(uint *)GPU_vertbuf_raw_step(data_step) = curve_range.start();
+ *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve_range.size() - 1;
}
}
-static void hair_batch_cache_ensure_procedural_strand_data(Hair *hair, ParticleHairCache *cache)
+static void curves_batch_cache_ensure_procedural_strand_data(Curves *curves,
+ ParticleHairCache *cache)
{
GPUVertBufRaw data_step, seg_step;
@@ -253,18 +262,18 @@ static void hair_batch_cache_ensure_procedural_strand_data(Hair *hair, ParticleH
GPU_vertbuf_data_alloc(cache->proc_strand_seg_buf, cache->strands_len);
GPU_vertbuf_attr_get_raw_data(cache->proc_strand_seg_buf, seg_id, &seg_step);
- hair_batch_cache_fill_strands_data(hair, &data_step, &seg_step);
+ curves_batch_cache_fill_strands_data(curves, &data_step, &seg_step);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(cache->proc_strand_buf);
- cache->strand_tex = GPU_texture_create_from_vertbuf("hair_strand", cache->proc_strand_buf);
+ cache->strand_tex = GPU_texture_create_from_vertbuf("curves_strand", cache->proc_strand_buf);
GPU_vertbuf_use(cache->proc_strand_seg_buf);
- cache->strand_seg_tex = GPU_texture_create_from_vertbuf("hair_strand_seg",
+ cache->strand_seg_tex = GPU_texture_create_from_vertbuf("curves_strand_seg",
cache->proc_strand_seg_buf);
}
-static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *cache, int subdiv)
+static void curves_batch_cache_ensure_procedural_final_points(ParticleHairCache *cache, int subdiv)
{
/* Same format as point_tex. */
GPUVertFormat format = {0};
@@ -285,14 +294,15 @@ static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *c
cache->final[subdiv].proc_buf);
}
-static void hair_batch_cache_fill_segments_indices(Hair *hair,
- const int res,
- GPUIndexBufBuilder *elb)
+static void curves_batch_cache_fill_segments_indices(Curves *curves,
+ const int res,
+ GPUIndexBufBuilder *elb)
{
- HairCurve *curve = hair->curves;
- int num_curves = hair->totcurve;
+ const int curve_size = curves->geometry.curve_size;
+
uint curr_point = 0;
- for (int i = 0; i < num_curves; i++, curve++) {
+
+ for ([[maybe_unused]] const int i : IndexRange(curve_size)) {
for (int k = 0; k < res; k++) {
GPU_indexbuf_add_generic_vert(elb, curr_point++);
}
@@ -300,10 +310,10 @@ static void hair_batch_cache_fill_segments_indices(Hair *hair,
}
}
-static void hair_batch_cache_ensure_procedural_indices(Hair *hair,
- ParticleHairCache *cache,
- int thickness_res,
- int subdiv)
+static void curves_batch_cache_ensure_procedural_indices(Curves *curves,
+ ParticleHairCache *cache,
+ int thickness_res,
+ int subdiv)
{
BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */
@@ -328,7 +338,7 @@ static void hair_batch_cache_ensure_procedural_indices(Hair *hair,
GPUIndexBufBuilder elb;
GPU_indexbuf_init_ex(&elb, prim_type, element_count, element_count);
- hair_batch_cache_fill_segments_indices(hair, verts_per_hair, &elb);
+ curves_batch_cache_fill_segments_indices(curves, verts_per_hair, &elb);
cache->final[subdiv].proc_hairs[thickness_res - 1] = GPU_batch_create_ex(
prim_type, vbo, GPU_indexbuf_build(&elb), GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
@@ -341,9 +351,9 @@ bool hair_ensure_procedural_data(Object *object,
int thickness_res)
{
bool need_ft_update = false;
- Hair *hair = static_cast<Hair *>(object->data);
+ Curves *curves = static_cast<Curves *>(object->data);
- HairBatchCache *cache = hair_batch_cache_get(hair);
+ HairBatchCache *cache = curves_batch_cache_get(curves);
*r_hair_cache = &cache->hair;
const int steps = 2; /* TODO: don't hard-code? */
@@ -351,29 +361,29 @@ bool hair_ensure_procedural_data(Object *object,
/* Refreshed on combing and simulation. */
if ((*r_hair_cache)->proc_point_buf == nullptr) {
- ensure_seg_pt_count(hair, &cache->hair);
- hair_batch_cache_ensure_procedural_pos(hair, &cache->hair, gpu_material);
+ ensure_seg_pt_count(curves, &cache->hair);
+ curves_batch_cache_ensure_procedural_pos(curves, &cache->hair, gpu_material);
need_ft_update = true;
}
/* Refreshed if active layer or custom data changes. */
if ((*r_hair_cache)->strand_tex == nullptr) {
- hair_batch_cache_ensure_procedural_strand_data(hair, &cache->hair);
+ curves_batch_cache_ensure_procedural_strand_data(curves, &cache->hair);
}
/* Refreshed only on subdiv count change. */
if ((*r_hair_cache)->final[subdiv].proc_buf == nullptr) {
- hair_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
+ curves_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
need_ft_update = true;
}
if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == nullptr) {
- hair_batch_cache_ensure_procedural_indices(hair, &cache->hair, thickness_res, subdiv);
+ curves_batch_cache_ensure_procedural_indices(curves, &cache->hair, thickness_res, subdiv);
}
return need_ft_update;
}
-int DRW_hair_material_count_get(Hair *hair)
+int DRW_curves_material_count_get(Curves *curves)
{
- return max_ii(1, hair->totcol);
+ return max_ii(1, curves->totcol);
}
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 1e5ffc14911..e7e0e97499f 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -280,9 +280,16 @@ BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a)
*((uint32_t *)a) = 0;
}
-BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me)
+BLI_INLINE const Mesh *editmesh_final_or_this(const Object *object, const Mesh *me)
{
- return (me->edit_mesh && me->edit_mesh->mesh_eval_final) ? me->edit_mesh->mesh_eval_final : me;
+ if (me->edit_mesh != NULL) {
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object);
+ if (editmesh_eval_final != NULL) {
+ return editmesh_eval_final;
+ }
+ }
+
+ return me;
}
static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *cd_used)
@@ -443,9 +450,11 @@ BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me)
return &me->vdata;
}
-static void mesh_cd_calc_active_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
+static void mesh_cd_calc_active_uv_layer(const Object *object,
+ const Mesh *me,
+ DRW_MeshCDMask *cd_used)
{
- const Mesh *me_final = editmesh_final_or_this(me);
+ const Mesh *me_final = editmesh_final_or_this(object, me);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
if (layer != -1) {
@@ -453,9 +462,11 @@ static void mesh_cd_calc_active_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used
}
}
-static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
+static void mesh_cd_calc_active_mask_uv_layer(const Object *object,
+ const Mesh *me,
+ DRW_MeshCDMask *cd_used)
{
- const Mesh *me_final = editmesh_final_or_this(me);
+ const Mesh *me_final = editmesh_final_or_this(object, me);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
int layer = CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV);
if (layer != -1) {
@@ -463,9 +474,11 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd
}
}
-static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *attrs_used)
+static void mesh_cd_calc_active_vcol_layer(const Object *object,
+ const Mesh *me,
+ DRW_MeshAttributes *attrs_used)
{
- const Mesh *me_final = editmesh_final_or_this(me);
+ const Mesh *me_final = editmesh_final_or_this(object, me);
const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR);
@@ -474,9 +487,11 @@ static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *a
}
}
-static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
+static void mesh_cd_calc_active_mloopcol_layer(const Object *object,
+ const Mesh *me,
+ DRW_MeshCDMask *cd_used)
{
- const Mesh *me_final = editmesh_final_or_this(me);
+ const Mesh *me_final = editmesh_final_or_this(object, me);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL);
@@ -490,8 +505,9 @@ static bool custom_data_match_attribute(const CustomData *custom_data,
int *r_layer_index,
int *r_type)
{
- const int possible_attribute_types[6] = {
+ const int possible_attribute_types[7] = {
CD_PROP_BOOL,
+ CD_PROP_INT8,
CD_PROP_INT32,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
@@ -514,12 +530,13 @@ static bool custom_data_match_attribute(const CustomData *custom_data,
return false;
}
-static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
+static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
+ const Mesh *me,
struct GPUMaterial **gpumat_array,
int gpumat_array_len,
DRW_MeshAttributes *attributes)
{
- const Mesh *me_final = editmesh_final_or_this(me);
+ const Mesh *me_final = editmesh_final_or_this(object, me);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
const CustomData *cd_pdata = mesh_cd_pdata_get_from_mesh(me_final);
const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
@@ -639,6 +656,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
break;
}
case CD_PROP_BOOL:
+ case CD_PROP_INT8:
case CD_PROP_INT32:
case CD_PROP_FLOAT:
case CD_PROP_FLOAT2:
@@ -793,7 +811,7 @@ BLI_INLINE void mesh_batch_cache_add_request(MeshBatchCache *cache, DRWBatchFlag
/* GPUBatch cache management. */
-static bool mesh_batch_cache_valid(Mesh *me)
+static bool mesh_batch_cache_valid(Object *object, Mesh *me)
{
MeshBatchCache *cache = me->runtime.batch_cache;
@@ -809,14 +827,14 @@ static bool mesh_batch_cache_valid(Mesh *me)
return false;
}
- if (cache->mat_len != mesh_render_mat_len_get(me)) {
+ if (cache->mat_len != mesh_render_mat_len_get(object, me)) {
return false;
}
return true;
}
-static void mesh_batch_cache_init(Mesh *me)
+static void mesh_batch_cache_init(Object *object, Mesh *me)
{
MeshBatchCache *cache = me->runtime.batch_cache;
@@ -836,7 +854,7 @@ static void mesh_batch_cache_init(Mesh *me)
// cache->vert_len = mesh_render_verts_len_get(me);
}
- cache->mat_len = mesh_render_mat_len_get(me);
+ cache->mat_len = mesh_render_mat_len_get(object, me);
cache->surface_per_mat = MEM_callocN(sizeof(*cache->surface_per_mat) * cache->mat_len, __func__);
cache->tris_per_mat = MEM_callocN(sizeof(*cache->tris_per_mat) * cache->mat_len, __func__);
@@ -847,11 +865,11 @@ static void mesh_batch_cache_init(Mesh *me)
drw_mesh_weight_state_clear(&cache->weight_state);
}
-void DRW_mesh_batch_cache_validate(Mesh *me)
+void DRW_mesh_batch_cache_validate(Object *object, Mesh *me)
{
- if (!mesh_batch_cache_valid(me)) {
+ if (!mesh_batch_cache_valid(object, me)) {
mesh_batch_cache_clear(me);
- mesh_batch_cache_init(me);
+ mesh_batch_cache_init(object, me);
}
}
@@ -1095,24 +1113,24 @@ void DRW_mesh_batch_cache_free(Mesh *me)
/** \name Public API
* \{ */
-static void texpaint_request_active_uv(MeshBatchCache *cache, Mesh *me)
+static void texpaint_request_active_uv(MeshBatchCache *cache, Object *object, Mesh *me)
{
DRW_MeshCDMask cd_needed;
mesh_cd_layers_type_clear(&cd_needed);
- mesh_cd_calc_active_uv_layer(me, &cd_needed);
+ mesh_cd_calc_active_uv_layer(object, me, &cd_needed);
BLI_assert(cd_needed.uv != 0 &&
"No uv layer available in texpaint, but batches requested anyway!");
- mesh_cd_calc_active_mask_uv_layer(me, &cd_needed);
+ mesh_cd_calc_active_mask_uv_layer(object, me, &cd_needed);
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
}
-static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me)
+static void texpaint_request_active_vcol(MeshBatchCache *cache, Object *object, Mesh *me)
{
DRW_MeshCDMask cd_needed;
mesh_cd_layers_type_clear(&cd_needed);
- mesh_cd_calc_active_mloopcol_layer(me, &cd_needed);
+ mesh_cd_calc_active_mloopcol_layer(object, me, &cd_needed);
BLI_assert(cd_needed.vcol != 0 &&
"No MLOOPCOL layer available in vertpaint, but batches requested anyway!");
@@ -1120,11 +1138,11 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me)
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
}
-static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me)
+static void sculpt_request_active_vcol(MeshBatchCache *cache, Object *object, Mesh *me)
{
DRW_MeshAttributes attrs_needed;
drw_mesh_attributes_clear(&attrs_needed);
- mesh_cd_calc_active_vcol_layer(me, &attrs_needed);
+ mesh_cd_calc_active_vcol_layer(object, me, &attrs_needed);
BLI_assert(attrs_needed.num_requests != 0 &&
"No MPropCol layer available in Sculpt, but batches requested anyway!");
@@ -1197,7 +1215,8 @@ GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(Mesh *me)
return DRW_batch_request(&cache->batch.edit_mesh_analysis);
}
-GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me,
+GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object,
+ Mesh *me,
struct GPUMaterial **gpumat_array,
uint gpumat_array_len)
{
@@ -1205,7 +1224,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me,
DRW_MeshAttributes attrs_needed;
drw_mesh_attributes_clear(&attrs_needed);
DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(
- me, gpumat_array, gpumat_array_len, &attrs_needed);
+ object, me, gpumat_array, gpumat_array_len, &attrs_needed);
BLI_assert(gpumat_array_len == cache->mat_len);
@@ -1216,41 +1235,41 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me,
return cache->surface_per_mat;
}
-GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me)
+GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- texpaint_request_active_uv(cache, me);
+ texpaint_request_active_uv(cache, object, me);
mesh_batch_cache_request_surface_batches(cache);
return cache->surface_per_mat;
}
-GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- texpaint_request_active_uv(cache, me);
+ texpaint_request_active_uv(cache, object, me);
mesh_batch_cache_request_surface_batches(cache);
return cache->batch.surface;
}
-GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- texpaint_request_active_vcol(cache, me);
+ texpaint_request_active_vcol(cache, object, me);
mesh_batch_cache_request_surface_batches(cache);
return cache->batch.surface;
}
-GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- sculpt_request_active_vcol(cache, me);
+ sculpt_request_active_vcol(cache, object, me);
mesh_batch_cache_request_surface_batches(cache);
return cache->batch.surface;
}
-int DRW_mesh_material_count_get(const Mesh *me)
+int DRW_mesh_material_count_get(const Object *object, const Mesh *me)
{
- return mesh_render_mat_len_get(me);
+ return mesh_render_mat_len_get(object, me);
}
GPUBatch *DRW_mesh_batch_cache_get_sculpt_overlays(Mesh *me)
@@ -1375,26 +1394,27 @@ GPUBatch *DRW_mesh_batch_cache_get_verts_with_select_id(Mesh *me)
/** \name UV Image editor API
* \{ */
-static void edituv_request_active_uv(MeshBatchCache *cache, Mesh *me)
+static void edituv_request_active_uv(MeshBatchCache *cache, Object *object, Mesh *me)
{
DRW_MeshCDMask cd_needed;
mesh_cd_layers_type_clear(&cd_needed);
- mesh_cd_calc_active_uv_layer(me, &cd_needed);
+ mesh_cd_calc_active_uv_layer(object, me, &cd_needed);
mesh_cd_calc_edit_uv_layer(me, &cd_needed);
BLI_assert(cd_needed.edit_uv != 0 &&
"No uv layer available in edituv, but batches requested anyway!");
- mesh_cd_calc_active_mask_uv_layer(me, &cd_needed);
+ mesh_cd_calc_active_mask_uv_layer(object, me, &cd_needed);
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
}
-GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Mesh *me,
+GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Object *object,
+ Mesh *me,
float **tot_area,
float **tot_uv_area)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- edituv_request_active_uv(cache, me);
+ edituv_request_active_uv(cache, object, me);
mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES_STRETCH_AREA);
if (tot_area != NULL) {
@@ -1406,58 +1426,58 @@ GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Mesh *me,
return DRW_batch_request(&cache->batch.edituv_faces_stretch_area);
}
-GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- edituv_request_active_uv(cache, me);
+ edituv_request_active_uv(cache, object, me);
mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES_STRETCH_ANGLE);
return DRW_batch_request(&cache->batch.edituv_faces_stretch_angle);
}
-GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_edituv_faces(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- edituv_request_active_uv(cache, me);
+ edituv_request_active_uv(cache, object, me);
mesh_batch_cache_add_request(cache, MBC_EDITUV_FACES);
return DRW_batch_request(&cache->batch.edituv_faces);
}
-GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_edituv_edges(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- edituv_request_active_uv(cache, me);
+ edituv_request_active_uv(cache, object, me);
mesh_batch_cache_add_request(cache, MBC_EDITUV_EDGES);
return DRW_batch_request(&cache->batch.edituv_edges);
}
-GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_edituv_verts(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- edituv_request_active_uv(cache, me);
+ edituv_request_active_uv(cache, object, me);
mesh_batch_cache_add_request(cache, MBC_EDITUV_VERTS);
return DRW_batch_request(&cache->batch.edituv_verts);
}
-GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- edituv_request_active_uv(cache, me);
+ edituv_request_active_uv(cache, object, me);
mesh_batch_cache_add_request(cache, MBC_EDITUV_FACEDOTS);
return DRW_batch_request(&cache->batch.edituv_fdots);
}
-GPUBatch *DRW_mesh_batch_cache_get_uv_edges(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_uv_edges(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- edituv_request_active_uv(cache, me);
+ edituv_request_active_uv(cache, object, me);
mesh_batch_cache_add_request(cache, MBC_WIRE_LOOPS_UVS);
return DRW_batch_request(&cache->batch.wire_loops_uvs);
}
-GPUBatch *DRW_mesh_batch_cache_get_surface_edges(Mesh *me)
+GPUBatch *DRW_mesh_batch_cache_get_surface_edges(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- texpaint_request_active_uv(cache, me);
+ texpaint_request_active_uv(cache, object, me);
mesh_batch_cache_add_request(cache, MBC_WIRE_LOOPS);
return DRW_batch_request(&cache->batch.wire_loops);
}
@@ -1560,20 +1580,12 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
/* Sanity check. */
if ((me->edit_mesh != NULL) && (ob->mode & OB_MODE_EDIT)) {
- BLI_assert(me->edit_mesh->mesh_eval_final != NULL);
+ BLI_assert(BKE_object_get_editmesh_eval_final(ob) != NULL);
}
- /* Don't check `DRW_object_is_in_edit_mode(ob)` here because it means the same mesh
- * may draw with edit-mesh data and regular mesh data.
- * In this case the custom-data layers used won't always match in `me->runtime.batch_cache`.
- * If we want to display regular mesh data, we should have a separate cache for the edit-mesh.
- * See T77359. */
const bool is_editmode = (me->edit_mesh != NULL) &&
- /* In rare cases we have the edit-mode data but not the generated cache.
- * This can happen when switching an objects data to a mesh which
- * happens to be in edit-mode in another scene, see: T82952. */
- (me->edit_mesh->mesh_eval_final !=
- NULL) /* && DRW_object_is_in_edit_mode(ob) */;
+ (BKE_object_get_editmesh_eval_final(ob) != NULL) &&
+ DRW_object_is_in_edit_mode(ob);
/* This could be set for paint mode too, currently it's only used for edit-mode. */
const bool is_mode_active = is_editmode && DRW_object_is_in_edit_mode(ob);
@@ -1599,7 +1611,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
/* Modifiers will only generate an orco layer if the mesh is deformed. */
if (cache->cd_needed.orco != 0) {
/* Orco is always extracted from final mesh. */
- Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
+ Mesh *me_final = (me->edit_mesh) ? BKE_object_get_editmesh_eval_final(ob) : me;
if (CustomData_get_layer(&me_final->vdata, CD_ORCO) == NULL) {
/* Skip orco calculation */
cache->cd_needed.orco = 0;
@@ -1705,10 +1717,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
cache->batch_ready |= batch_requested;
- const bool do_cage = (is_editmode &&
- (me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage));
+ bool do_cage = false, do_uvcage = false;
+ if (is_editmode) {
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob);
+ Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob);
- const bool do_uvcage = is_editmode && !me->edit_mesh->mesh_eval_final->runtime.is_original;
+ do_cage = editmesh_eval_final != editmesh_eval_cage;
+ do_uvcage = !editmesh_eval_final->runtime.is_original;
+ }
const int required_mode = BKE_subsurf_modifier_eval_required_mode(DRW_state_is_scene_render(),
is_editmode);
@@ -2029,6 +2045,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
mesh_buffer_cache_create_requested(task_graph,
cache,
&cache->uv_cage,
+ ob,
me,
is_editmode,
is_paint_mode,
@@ -2046,6 +2063,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
mesh_buffer_cache_create_requested(task_graph,
cache,
&cache->cage,
+ ob,
me,
is_editmode,
is_paint_mode,
@@ -2071,6 +2089,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
mesh_buffer_cache_create_requested(task_graph,
cache,
&cache->final,
+ ob,
me,
is_editmode,
is_paint_mode,
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
index 4583b90b144..9493a6debeb 100644
--- a/source/blender/draw/intern/draw_cache_impl_particles.c
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -634,7 +634,7 @@ static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_
seg_data[3] = total_len;
co_prev = path[j].co;
}
- /* Assign length value*/
+ /* Assign length value. */
*(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
index 37bc9435c76..4a8670a9ee2 100644
--- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc
+++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
@@ -573,6 +573,9 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache)
GPU_VERTBUF_DISCARD_SAFE(cache->subdiv_vertex_face_adjacency);
cache->resolution = 0;
cache->num_subdiv_loops = 0;
+ cache->num_subdiv_edges = 0;
+ cache->num_subdiv_verts = 0;
+ cache->num_subdiv_triangles = 0;
cache->num_coarse_poly = 0;
cache->num_subdiv_quads = 0;
draw_subdiv_free_edit_mode_cache(cache);
@@ -749,9 +752,10 @@ static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_con
cache->subdiv_loop_poly_index = static_cast<int *>(
MEM_mallocN(cache->num_subdiv_loops * sizeof(int), "subdiv_loop_poly_index"));
+ const int num_coarse_vertices = ctx->coarse_mesh->totvert;
cache->point_indices = static_cast<int *>(
- MEM_mallocN(cache->num_subdiv_verts * sizeof(int), "point_indices"));
- for (int i = 0; i < num_vertices; i++) {
+ MEM_mallocN(num_coarse_vertices * sizeof(int), "point_indices"));
+ for (int i = 0; i < num_coarse_vertices; i++) {
cache->point_indices[i] = -1;
}
@@ -976,6 +980,7 @@ static bool draw_subdiv_build_cache(DRWSubdivCache *cache,
}
DRWCacheBuildingContext cache_building_context;
+ memset(&cache_building_context, 0, sizeof(DRWCacheBuildingContext));
cache_building_context.coarse_mesh = mesh_eval;
cache_building_context.settings = &to_mesh_settings;
cache_building_context.cache = cache;
@@ -994,7 +999,7 @@ static bool draw_subdiv_build_cache(DRWSubdivCache *cache,
cache->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv);
- // Build patch coordinates for all the face dots
+ /* Build patch coordinates for all the face dots. */
cache->fdots_patch_coords = gpu_vertbuf_create_from_format(get_blender_patch_coords_format(),
mesh_eval->totpoly);
CompressedPatchCoord *blender_fdots_patch_coords = (CompressedPatchCoord *)GPU_vertbuf_get_data(
@@ -1755,7 +1760,7 @@ static void draw_subdiv_cache_ensure_mat_offsets(DRWSubdivCache *cache,
int *mat_start = static_cast<int *>(MEM_callocN(sizeof(int) * mat_len, "subdiv mat_start"));
int *subdiv_polygon_offset = cache->subdiv_polygon_offset;
- // TODO: parallel_reduce?
+ /* TODO: parallel_reduce? */
for (int i = 0; i < mesh_eval->totpoly; i++) {
const MPoly *mpoly = &mesh_eval->mpoly[i];
const int next_offset = (i == mesh_eval->totpoly - 1) ? number_of_quads :
@@ -1823,7 +1828,7 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene,
Mesh *mesh_eval = mesh;
BMesh *bm = nullptr;
if (mesh->edit_mesh) {
- mesh_eval = mesh->edit_mesh->mesh_eval_final;
+ mesh_eval = BKE_object_get_editmesh_eval_final(ob);
bm = mesh->edit_mesh->bm;
}
diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index 65afc5ed3d8..82b830f6799 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -431,7 +431,7 @@ bool DRW_object_is_flat(Object *ob, int *r_axis)
OB_SURF,
OB_FONT,
OB_MBALL,
- OB_HAIR,
+ OB_CURVES,
OB_POINTCLOUD,
OB_VOLUME)) {
/* Non-meshes object cannot be considered as flat. */
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 6c3e0773a15..fdb7cfba580 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -24,12 +24,12 @@
struct DRWShadingGroup;
struct FluidModifierData;
+struct GPUMaterial;
struct ModifierData;
struct Object;
struct ParticleSystem;
struct RegionView3D;
struct ViewLayer;
-struct GPUMaterial;
#define UBO_FIRST_COLOR colorWire
#define UBO_LAST_COLOR colorUVShadow
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index 0abb00a71a9..e9010d7a81a 100644
--- a/source/blender/draw/intern/draw_hair.c
+++ b/source/blender/draw/intern/draw_hair.c
@@ -54,7 +54,7 @@
BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void)
{
#ifdef USE_COMPUTE_SHADERS
- if (GPU_compute_shader_support()) {
+ if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) {
return PART_REFINE_SHADER_COMPUTE;
}
#endif
@@ -130,7 +130,7 @@ static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, con
GPUShader *shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass);
drw_hair_particle_cache_shgrp_attach_resources(shgrp, cache, subdiv);
- DRW_shgroup_vertex_buffer(shgrp, "hairPointOutputBuffer", cache->final[subdiv].proc_buf);
+ DRW_shgroup_vertex_buffer(shgrp, "posTime", cache->final[subdiv].proc_buf);
const int max_strands_per_call = GPU_max_work_group_count(0);
int strands_start = 0;
diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c
index 7e1d1208698..ca01a99c0d3 100644
--- a/source/blender/draw/intern/draw_instance_data.c
+++ b/source/blender/draw/intern/draw_instance_data.c
@@ -80,7 +80,7 @@ typedef struct DRWTempInstancingHandle {
GPUBatch *batch;
/** Batch containing instancing attributes. */
GPUBatch *instancer;
- /** Callbuffer to be used instead of instancer. */
+ /** Call-buffer to be used instead of instancer. */
GPUVertBuf *buf;
/** Original non-instanced batch pointer. */
GPUBatch *geom;
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 276a8cc3a13..039fae43329 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -35,11 +35,11 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_curve.h"
+#include "BKE_curves.h"
#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
-#include "BKE_hair.h"
#include "BKE_lattice.h"
#include "BKE_main.h"
#include "BKE_mball.h"
@@ -212,21 +212,7 @@ bool DRW_object_is_in_edit_mode(const Object *ob)
if (BKE_object_is_in_editmode(ob)) {
if (ob->type == OB_MESH) {
if ((ob->mode & OB_MODE_EDIT) == 0) {
- Mesh *me = (Mesh *)ob->data;
- BMEditMesh *embm = me->edit_mesh;
- /* Sanity check when rendering in multiple windows. */
- if (embm && embm->mesh_eval_final == NULL) {
- return false;
- }
- /* Do not draw ob with edit overlay when edit data is present and is modified. */
- if (embm && embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final)) {
- return false;
- }
- /* Check if the object that we are drawing is modified. */
- if (!DEG_is_original_id(&me->id)) {
- return false;
- }
- return true;
+ return false;
}
}
return true;
@@ -1496,7 +1482,7 @@ void DRW_draw_callbacks_post_scene(void)
* Don't trust them! */
DRW_state_reset();
- /* needed so gizmo isn't obscured */
+ /* Needed so gizmo isn't occluded. */
if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
GPU_depth_test(GPU_DEPTH_NONE);
DRW_draw_gizmo_3d();
@@ -2962,8 +2948,8 @@ void DRW_engines_register(void)
BKE_gpencil_batch_cache_dirty_tag_cb = DRW_gpencil_batch_cache_dirty_tag;
BKE_gpencil_batch_cache_free_cb = DRW_gpencil_batch_cache_free;
- BKE_hair_batch_cache_dirty_tag_cb = DRW_hair_batch_cache_dirty_tag;
- BKE_hair_batch_cache_free_cb = DRW_hair_batch_cache_free;
+ BKE_curves_batch_cache_dirty_tag_cb = DRW_curves_batch_cache_dirty_tag;
+ BKE_curves_batch_cache_free_cb = DRW_curves_batch_cache_free;
BKE_pointcloud_batch_cache_dirty_tag_cb = DRW_pointcloud_batch_cache_dirty_tag;
BKE_pointcloud_batch_cache_free_cb = DRW_pointcloud_batch_cache_free;
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index d27eb8be9e0..73fd157426c 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -205,8 +205,10 @@ typedef enum {
/* Compute Commands. */
DRW_CMD_COMPUTE = 8,
+ DRW_CMD_COMPUTE_REF = 9,
/* Other Commands */
+ DRW_CMD_BARRIER = 11,
DRW_CMD_CLEAR = 12,
DRW_CMD_DRWSTATE = 13,
DRW_CMD_STENCIL = 14,
@@ -249,6 +251,14 @@ typedef struct DRWCommandCompute {
int groups_z_len;
} DRWCommandCompute;
+typedef struct DRWCommandComputeRef {
+ int *groups_ref;
+} DRWCommandComputeRef;
+
+typedef struct DRWCommandBarrier {
+ eGPUBarrier type;
+} DRWCommandBarrier;
+
typedef struct DRWCommandDrawProcedural {
GPUBatch *batch;
DRWResourceHandle handle;
@@ -286,6 +296,8 @@ typedef union DRWCommand {
DRWCommandDrawInstanceRange instance_range;
DRWCommandDrawProcedural procedural;
DRWCommandCompute compute;
+ DRWCommandComputeRef compute_ref;
+ DRWCommandBarrier barrier;
DRWCommandSetMutableState state;
DRWCommandSetStencil stencil;
DRWCommandSetSelectID select_id;
@@ -300,7 +312,7 @@ struct DRWCallBuffer {
};
/** Used by #DRWUniform.type */
-/* TODO(jbakker): rename to DRW_RESOURCE/DRWResourceType. */
+/* TODO(@jbakker): rename to DRW_RESOURCE/DRWResourceType. */
typedef enum {
DRW_UNIFORM_INT = 0,
DRW_UNIFORM_INT_COPY,
@@ -314,6 +326,7 @@ typedef enum {
DRW_UNIFORM_BLOCK_REF,
DRW_UNIFORM_TFEEDBACK_TARGET,
DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE,
+ DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF,
/** Per drawcall uniforms/UBO */
DRW_UNIFORM_BLOCK_OBMATS,
DRW_UNIFORM_BLOCK_OBINFOS,
@@ -345,6 +358,11 @@ struct DRWUniform {
GPUUniformBuf *block;
GPUUniformBuf **block_ref;
};
+ /* DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE */
+ union {
+ GPUVertBuf *vertbuf;
+ GPUVertBuf **vertbuf_ref;
+ };
/* DRW_UNIFORM_FLOAT_COPY */
float fvalue[4];
/* DRW_UNIFORM_INT_COPY */
@@ -529,7 +547,7 @@ typedef struct DRWData {
struct GHash *obattrs_ubo_pool;
uint ubo_len;
/** Texture pool to reuse temp texture across engines. */
- /* TODO(fclem) the pool could be shared even between viewports. */
+ /* TODO(@fclem): The pool could be shared even between view-ports. */
struct DRWTexturePool *texture_pool;
/** Per stereo view data. Contains engine data and default framebuffers. */
struct DRWViewData *view_data[2];
@@ -549,7 +567,7 @@ typedef struct DupliKey {
typedef struct DRWManager {
/* TODO: clean up this struct a bit. */
/* Cache generation */
- /* TODO(fclem) Rename to data. */
+ /* TODO(@fclem): Rename to data. */
DRWData *vmempool;
/** Active view data structure for one of the 2 stereo view. Not related to DRWView. */
struct DRWViewData *view_data_active;
@@ -572,7 +590,7 @@ typedef struct DRWManager {
struct ID *dupli_origin_data;
/** Hash-map: #DupliKey -> void pointer for each enabled engine. */
struct GHash *dupli_ghash;
- /** TODO(fclem): try to remove usage of this. */
+ /** TODO(@fclem): try to remove usage of this. */
DRWInstanceData *object_instance_data[MAX_INSTANCE_DATA_SIZE];
/* Dupli data for the current dupli for each enabled engine. */
void **dupli_datas;
@@ -615,7 +633,7 @@ typedef struct DRWManager {
DRWView *view_active;
DRWView *view_previous;
uint primary_view_ct;
- /** TODO(fclem): Remove this. Only here to support
+ /** TODO(@fclem): Remove this. Only here to support
* shaders without common_view_lib.glsl */
DRWViewUboStorage view_storage_cpy;
@@ -640,7 +658,7 @@ typedef struct DRWManager {
GPUDrawList *draw_list;
struct {
- /* TODO(fclem): optimize: use chunks. */
+ /* TODO(@fclem): optimize: use chunks. */
DRWDebugLine *lines;
DRWDebugSphere *spheres;
} debug;
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index f1c13bc039a..3f299e878c4 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -283,19 +283,45 @@ void DRW_shgroup_uniform_image_ref(DRWShadingGroup *shgroup, const char *name, G
drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_IMAGE_REF, tex, 0, 0, 1);
}
-void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup,
- const char *name,
- const GPUUniformBuf *ubo)
+void DRW_shgroup_uniform_block_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ const GPUUniformBuf *ubo DRW_DEBUG_FILE_LINE_ARGS)
{
BLI_assert(ubo != NULL);
int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name);
+ if (loc == -1) {
+#ifdef DRW_UNUSED_RESOURCE_TRACKING
+ printf("%s:%d: Unable to locate binding of shader uniform buffer object: %s.\n",
+ file,
+ line,
+ name);
+#else
+ /* TODO(@fclem): Would be good to have, but eevee has too much of this for the moment. */
+ // BLI_assert_msg(0, "Unable to locate binding of shader uniform buffer objects.");
+#endif
+ return;
+ }
drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK, ubo, 0, 0, 1);
}
-void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, GPUUniformBuf **ubo)
+void DRW_shgroup_uniform_block_ref_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUUniformBuf **ubo DRW_DEBUG_FILE_LINE_ARGS)
{
BLI_assert(ubo != NULL);
int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name);
+ if (loc == -1) {
+#ifdef DRW_UNUSED_RESOURCE_TRACKING
+ printf("%s:%d: Unable to locate binding of shader uniform buffer object: %s.\n",
+ file,
+ line,
+ name);
+#else
+ /* TODO(@fclem): Would be good to have, but eevee has too much of this for the moment. */
+ // BLI_assert_msg(0, "Unable to locate binding of shader uniform buffer objects.");
+#endif
+ return;
+ }
drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK_REF, ubo, 0, 0, 1);
}
@@ -447,19 +473,46 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup,
}
}
-void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
- const char *name,
- GPUVertBuf *vertex_buffer)
+void DRW_shgroup_vertex_buffer_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUVertBuf *vertex_buffer DRW_DEBUG_FILE_LINE_ARGS)
{
int location = GPU_shader_get_ssbo(shgroup->shader, name);
if (location == -1) {
+#ifdef DRW_UNUSED_RESOURCE_TRACKING
+ printf("%s:%d: Unable to locate binding of shader storage buffer object: %s.\n",
+ file,
+ line,
+ name);
+#else
BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects.");
+#endif
return;
}
drw_shgroup_uniform_create_ex(
shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1);
}
+void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUVertBuf **vertex_buffer DRW_DEBUG_FILE_LINE_ARGS)
+{
+ int location = GPU_shader_get_ssbo(shgroup->shader, name);
+ if (location == -1) {
+#ifdef DRW_UNUSED_RESOURCE_TRACKING
+ printf("%s:%d: Unable to locate binding of shader storage buffer object: %s.\n",
+ file,
+ line,
+ name);
+#else
+ BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects.");
+#endif
+ return;
+ }
+ drw_shgroup_uniform_create_ex(
+ shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, vertex_buffer, 0, 0, 1);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -730,6 +783,18 @@ static void drw_command_compute(DRWShadingGroup *shgroup,
cmd->groups_z_len = groups_z_len;
}
+static void drw_command_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3])
+{
+ DRWCommandComputeRef *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE_REF);
+ cmd->groups_ref = groups_ref;
+}
+
+static void drw_command_barrier(DRWShadingGroup *shgroup, eGPUBarrier type)
+{
+ DRWCommandBarrier *cmd = drw_command_create(shgroup, DRW_CMD_BARRIER);
+ cmd->type = type;
+}
+
static void drw_command_draw_procedural(DRWShadingGroup *shgroup,
GPUBatch *batch,
DRWResourceHandle handle,
@@ -816,7 +881,7 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup,
culling->user_data = user_data;
}
if (bypass_culling) {
- /* NOTE this will disable culling for the whole object. */
+ /* NOTE: this will disable culling for the whole object. */
culling->bsphere.radius = -1.0f;
}
}
@@ -855,6 +920,20 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len);
}
+void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3])
+{
+ BLI_assert(GPU_compute_shader_support());
+
+ drw_command_compute_ref(shgroup, groups_ref);
+}
+
+void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type)
+{
+ BLI_assert(GPU_compute_shader_support());
+
+ drw_command_barrier(shgroup, type);
+}
+
static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup,
GPUBatch *geom,
Object *ob,
@@ -1248,6 +1327,17 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader)
int chunkid_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_RESOURCE_CHUNK);
int resourceid_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_RESOURCE_ID);
+ /* TODO(@fclem): Will take the place of the above after the GPUShaderCreateInfo port. */
+ if (view_ubo_location == -1) {
+ view_ubo_location = GPU_shader_get_builtin_block(shader, GPU_UNIFORM_BLOCK_DRW_VIEW);
+ }
+ if (model_ubo_location == -1) {
+ model_ubo_location = GPU_shader_get_builtin_block(shader, GPU_UNIFORM_BLOCK_DRW_MODEL);
+ }
+ if (info_ubo_location == -1) {
+ info_ubo_location = GPU_shader_get_builtin_block(shader, GPU_UNIFORM_BLOCK_DRW_INFOS);
+ }
+
if (chunkid_location != -1) {
drw_shgroup_uniform_create_ex(
shgroup, chunkid_location, DRW_UNIFORM_RESOURCE_CHUNK, NULL, 0, 0, 1);
@@ -1893,10 +1983,10 @@ void DRW_view_reset(void)
DST.view_previous = NULL;
}
-void DRW_view_default_set(DRWView *view)
+void DRW_view_default_set(const DRWView *view)
{
BLI_assert(DST.view_default == NULL);
- DST.view_default = view;
+ DST.view_default = (DRWView *)view;
}
void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len)
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index 8dd24c01337..a579403975f 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -354,9 +354,9 @@ static bool draw_call_is_culled(const DRWResourceHandle *handle, DRWView *view)
return (culling->mask & view->culling_mask) != 0;
}
-void DRW_view_set_active(DRWView *view)
+void DRW_view_set_active(const DRWView *view)
{
- DST.view_active = (view) ? view : DST.view_default;
+ DST.view_active = (view != NULL) ? ((DRWView *)view) : DST.view_default;
}
const DRWView *DRW_view_get_active(void)
@@ -662,8 +662,11 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
*use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
((GPUVertBuf *)uni->pvalue));
break;
+ case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF:
+ GPU_vertbuf_bind_as_ssbo(*uni->vertbuf_ref, uni->location);
+ break;
case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE:
- GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location);
+ GPU_vertbuf_bind_as_ssbo(uni->vertbuf, uni->location);
break;
/* Legacy/Fallback support. */
case DRW_UNIFORM_BASE_INSTANCE:
@@ -1049,6 +1052,15 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
cmd->compute.groups_y_len,
cmd->compute.groups_z_len);
break;
+ case DRW_CMD_COMPUTE_REF:
+ GPU_compute_dispatch(shgroup->shader,
+ cmd->compute_ref.groups_ref[0],
+ cmd->compute_ref.groups_ref[1],
+ cmd->compute_ref.groups_ref[2]);
+ break;
+ case DRW_CMD_BARRIER:
+ GPU_memory_barrier(cmd->barrier.type);
+ break;
}
}
diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index 84440a8effe..3cfbb3fe416 100644
--- a/source/blender/draw/intern/draw_manager_shader.c
+++ b/source/blender/draw/intern/draw_manager_shader.c
@@ -46,8 +46,10 @@
#include "draw_manager.h"
-extern char datatoc_gpu_shader_2D_vert_glsl[];
-extern char datatoc_gpu_shader_3D_vert_glsl[];
+#include "CLG_log.h"
+
+static CLG_LogRef LOG = {"draw.manager.shader"};
+
extern char datatoc_gpu_shader_depth_only_frag_glsl[];
extern char datatoc_common_fullscreen_vert_glsl[];
@@ -567,7 +569,7 @@ void DRW_shader_free(GPUShader *shader)
#define MAX_LIB_DEPS 8
struct DRWShaderLibrary {
- char *libs[MAX_LIB];
+ const char *libs[MAX_LIB];
char libs_name[MAX_LIB][MAX_LIB_NAME];
uint32_t libs_deps[MAX_LIB];
};
@@ -616,11 +618,11 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c
}
dbg_name[i + 1] = '\0';
- printf(
- "Error: Dependency not found: %s\n"
- "This might be due to bad lib ordering.\n",
- dbg_name);
- BLI_assert(0);
+ CLOG_INFO(&LOG,
+ 0,
+ "Dependency '%s' not found\n"
+ "This might be due to bad lib ordering or overriding a builtin shader.\n",
+ dbg_name);
}
else {
deps |= 1u << (uint32_t)dep;
@@ -629,7 +631,7 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c
return deps;
}
-void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name)
+void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, const char *lib_name)
{
int index = -1;
for (int i = 0; i < MAX_LIB; i++) {
diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.c
index 121a0acd059..28ac76ccd7f 100644
--- a/source/blender/draw/intern/draw_shader.c
+++ b/source/blender/draw/intern/draw_shader.c
@@ -47,12 +47,7 @@ static struct {
static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement))
{
- GPUShader *sh = NULL;
- sh = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl,
- datatoc_common_hair_lib_glsl,
- "#define HAIR_PHASE_SUBDIV\n",
- __func__);
- return sh;
+ return GPU_shader_create_from_info_name("draw_hair_refine_compute");
}
static GPUShader *hair_refine_shader_transform_feedback_create(
diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h
index aa117f44e84..fba8f91b2f6 100644
--- a/source/blender/draw/intern/draw_shader_shared.h
+++ b/source/blender/draw/intern/draw_shader_shared.h
@@ -1,6 +1,6 @@
#ifndef GPU_SHADER
-# include "gpu_shader_shared_utils.h"
+# include "GPU_shader_shared_utils.h"
#endif
#define DRW_SHADER_SHARED_H
@@ -16,21 +16,21 @@ struct ViewInfos {
float4x4 winmat;
float4x4 wininv;
- float4 clipplanes[6];
+ float4 clip_planes[6];
float4 viewvecs[2];
/* Should not be here. Not view dependent (only main view). */
float4 viewcamtexcofac;
};
BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
-/* TODO(fclem) Mass rename. */
+/* TODO(@fclem): Mass rename. */
#define ViewProjectionMatrix drw_view.persmat
#define ViewProjectionMatrixInverse drw_view.persinv
#define ViewMatrix drw_view.viewmat
#define ViewMatrixInverse drw_view.viewinv
#define ProjectionMatrix drw_view.winmat
#define ProjectionMatrixInverse drw_view.wininv
-#define clipPlanes drw_view.clipplanes
+#define clipPlanes drw_view.clip_planes
#define ViewVecs drw_view.viewvecs
#define CameraTexCoFactors drw_view.viewcamtexcofac
@@ -39,3 +39,14 @@ struct ObjectMatrices {
float4x4 drw_modelMatrixInverse;
};
BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
+
+struct ObjectInfos {
+ float4 drw_OrcoTexCoFactors[2];
+ float4 drw_ObjectColor;
+ float4 drw_Infos;
+};
+BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
+
+#define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors)
+#define ObjectInfo (drw_infos[resource_id].drw_Infos)
+#define ObjectColor (drw_infos[resource_id].drw_ObjectColor)
diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h
index 9dfcdabc0fe..eeef2156e35 100644
--- a/source/blender/draw/intern/draw_subdivision.h
+++ b/source/blender/draw/intern/draw_subdivision.h
@@ -106,7 +106,7 @@ typedef struct DRWSubdivCache {
struct GPUVertBuf *edges_orig_index;
/* Owned by #Subdiv. Indexed by coarse polygon index, difference between value (i + 1) and (i)
- * gives the number of ptex faces for coarse polygon (i). */
+ * gives the number of ptex faces for coarse polygon (i). */
int *face_ptex_offset;
/* Vertex buffer for face_ptex_offset. */
struct GPUVertBuf *face_ptex_offset_buffer;
diff --git a/source/blender/draw/intern/draw_view_data.h b/source/blender/draw/intern/draw_view_data.h
index 494c28cc067..98ada5a59cb 100644
--- a/source/blender/draw/intern/draw_view_data.h
+++ b/source/blender/draw/intern/draw_view_data.h
@@ -31,11 +31,11 @@
extern "C" {
#endif
-struct GPUViewport;
-struct DrawEngineType;
struct DRWRegisteredDrawEngine;
+struct DrawEngineType;
+struct GPUViewport;
-/* NOTE these structs are only here for reading the actual lists from the engine.
+/* NOTE: these structs are only here for reading the actual lists from the engine.
* The actual length of them is stored in a ViewportEngineData_Info.
* The length of 1 is just here to avoid compiler warning. */
typedef struct FramebufferList {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h
index bb55d0c5b2c..37eb4f80442 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h
@@ -281,7 +281,8 @@ typedef struct MeshExtract {
* \param is_mode_active: When true, use the modifiers from the edit-data,
* otherwise don't use modifiers as they are not from this object.
*/
-MeshRenderData *mesh_render_data_create(Mesh *me,
+MeshRenderData *mesh_render_data_create(Object *object,
+ Mesh *me,
bool is_editmode,
bool is_paint_mode,
bool is_mode_active,
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
index 3ea3e67a8da..c3f89ab96ee 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
@@ -157,7 +157,7 @@ static void extract_points_finish(const MeshRenderData *UNUSED(mr),
}
static void extract_points_init_subdiv(const DRWSubdivCache *subdiv_cache,
- const MeshRenderData *UNUSED(mr),
+ const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *UNUSED(buffer),
void *data)
@@ -165,9 +165,9 @@ static void extract_points_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
/* Copy the points as the data upload will free them. */
elb->data = (uint *)MEM_dupallocN(subdiv_cache->point_indices);
- elb->index_len = subdiv_cache->num_subdiv_verts;
+ elb->index_len = mr->vert_len;
elb->index_min = 0;
- elb->index_max = subdiv_cache->num_subdiv_loops - 1;
+ elb->index_max = subdiv_cache->num_subdiv_loops + mr->loop_loose_len;
elb->prim_type = GPU_PRIM_POINTS;
}
@@ -184,9 +184,6 @@ static void extract_points_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache,
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
- elb->data = static_cast<uint32_t *>(
- MEM_reallocN(elb->data, sizeof(uint) * (subdiv_cache->num_subdiv_loops + loop_loose_len)));
-
const Mesh *coarse_mesh = subdiv_cache->mesh;
const MEdge *coarse_edges = coarse_mesh->medge;
@@ -195,22 +192,18 @@ static void extract_points_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache,
for (int i = 0; i < loose_geom->edge_len; i++) {
const MEdge *loose_edge = &coarse_edges[loose_geom->edges[i]];
if (elb->data[loose_edge->v1] == -1u) {
- elb->data[loose_edge->v1] = offset;
+ GPU_indexbuf_set_point_vert(elb, loose_edge->v1, offset);
}
if (elb->data[loose_edge->v2] == -1u) {
- elb->data[loose_edge->v2] = offset + 1;
+ GPU_indexbuf_set_point_vert(elb, loose_edge->v2, offset + 1);
}
- elb->index_max += 2;
- elb->index_len += 2;
offset += 2;
}
for (int i = 0; i < loose_geom->vert_len; i++) {
if (elb->data[loose_geom->verts[i]] == -1u) {
- elb->data[loose_geom->verts[i]] = offset;
+ GPU_indexbuf_set_point_vert(elb, loose_geom->verts[i], offset);
}
- elb->index_max += 1;
- elb->index_len += 1;
offset += 1;
}
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
index b846da3f016..b27633405b9 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
@@ -99,9 +99,10 @@ static uint gpu_component_size_for_attribute_type(CustomDataType type)
{
switch (type) {
case CD_PROP_BOOL:
+ case CD_PROP_INT8:
case CD_PROP_INT32:
case CD_PROP_FLOAT: {
- /* TODO(kevindietrich) : should be 1 when scalar attributes conversion is handled by us. See
+ /* TODO(@kevindietrich): should be 1 when scalar attributes conversion is handled by us. See
* comment #extract_attr_init. */
return 3;
}
@@ -317,7 +318,7 @@ static void extract_attr_init(const MeshRenderData *mr,
init_vbo_for_attribute(mr, vbo, request, false, static_cast<uint32_t>(mr->loop_len));
- /* TODO(kevindietrich) : float3 is used for scalar attributes as the implicit conversion done by
+ /* TODO(@kevindietrich): float3 is used for scalar attributes as the implicit conversion done by
* OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the
* Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar
* texture as for volume attribute, so we can control the conversion ourselves. */
@@ -326,6 +327,10 @@ static void extract_attr_init(const MeshRenderData *mr,
extract_attr_generic<bool, float3>(mr, vbo, request);
break;
}
+ case CD_PROP_INT8: {
+ extract_attr_generic<int8_t, float3>(mr, vbo, request);
+ break;
+ }
case CD_PROP_INT32: {
extract_attr_generic<int32_t, float3>(mr, vbo, request);
break;
@@ -378,6 +383,10 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache,
extract_attr_generic<bool, float3>(mr, src_data, request);
break;
}
+ case CD_PROP_INT8: {
+ extract_attr_generic<int8_t, float3>(mr, src_data, request);
+ break;
+ }
case CD_PROP_INT32: {
extract_attr_generic<int32_t, float3>(mr, src_data, request);
break;
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
index 8470a71059f..4185f2f84a2 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
@@ -78,7 +78,7 @@ static void extract_edge_fac_init(const MeshRenderData *mr,
data->edge_loop_count = static_cast<uchar *>(
MEM_callocN(sizeof(uint32_t) * mr->edge_len, __func__));
- /* HACK(fclem) Detecting the need for edge render.
+ /* HACK(@fclem): Detecting the need for edge render.
* We could have a flag in the mesh instead or check the modifier stack. */
const MEdge *med = mr->medge;
for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
index 5d2ea923658..11f1515275c 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
@@ -270,7 +270,7 @@ static void extract_pos_nor_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache
const MVert *coarse_verts = coarse_mesh->mvert;
uint offset = subdiv_cache->num_subdiv_loops;
- /* TODO(kevindietrich) : replace this when compressed normals are supported. */
+ /* TODO(@kevindietrich): replace this when compressed normals are supported. */
struct SubdivPosNorLoop {
float pos[3];
float nor[3];
diff --git a/source/blender/draw/intern/shaders/common_fxaa_lib.glsl b/source/blender/draw/intern/shaders/common_fxaa_lib.glsl
index bf59972fbaa..599d875c500 100644
--- a/source/blender/draw/intern/shaders/common_fxaa_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_fxaa_lib.glsl
@@ -51,7 +51,7 @@
/*============================================================================
FXAA QUALITY - TUNING KNOBS
------------------------------------------------------------------------------
-NOTE the other tuning knobs are now in the shader function inputs!
+NOTE: the other tuning knobs are now in the shader function inputs!
============================================================================*/
#ifndef FXAA_QUALITY__PRESET
/*
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index 6cc7f09a852..ed8b8aeb849 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -5,6 +5,9 @@
* of data the CPU has to precompute and transfer for each update.
*/
+/* TODO(fclem): Keep documentation but remove the uniform declaration. */
+#ifndef USE_GPU_SHADER_CREATE_INFO
+
/**
* hairStrandsRes: Number of points per hair strand.
* 2 - no subdivision
@@ -33,8 +36,6 @@ uniform int hairStrandOffset = 0;
/* -- Per control points -- */
uniform samplerBuffer hairPointBuffer; /* RGBA32F */
-#define point_position xyz
-#define point_time w /* Position along the hair length */
/* -- Per strands data -- */
uniform usamplerBuffer hairStrandBuffer; /* R32UI */
@@ -43,6 +44,10 @@ uniform usamplerBuffer hairStrandSegBuffer; /* R16UI */
/* Not used, use one buffer per uv layer */
// uniform samplerBuffer hairUVBuffer; /* RG32F */
// uniform samplerBuffer hairColBuffer; /* RGBA16 linear color */
+#endif
+
+#define point_position xyz
+#define point_time w /* Position along the hair length */
/* -- Subdivision stage -- */
/**
diff --git a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl
index 4dcde4b0245..6a3a7815cdc 100644
--- a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#ifndef USE_GPU_SHADER_CREATE_INFO
/*
* To be compiled with common_hair_lib.glsl.
*/
@@ -9,6 +11,7 @@ layout(std430, binding = 0) writeonly buffer hairPointOutputBuffer
vec4 posTime[];
}
out_vertbuf;
+#endif
void main(void)
{
@@ -20,5 +23,5 @@ void main(void)
vec4 result = hair_interp_data(data0, data1, data2, data3, weights);
uint index = uint(hair_get_id() * hairStrandsRes) + gl_GlobalInvocationID.y;
- out_vertbuf.posTime[index] = result;
+ posTime[index] = result;
}
diff --git a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
index 2da16d3f6a9..dd725ad327f 100644
--- a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
@@ -22,13 +22,22 @@ mat3 pointcloud_get_facing_matrix(vec3 p)
return facing_mat;
}
+/* Returns world center position and radius. */
+void pointcloud_get_pos_and_radius(out vec3 outpos, out float outradius)
+{
+ outpos = point_object_to_world(pos.xyz);
+ outradius = dot(abs(mat3(ModelMatrix) * pos.www), vec3(1.0 / 3.0));
+}
+
/* Return world position and normal. */
void pointcloud_get_pos_and_nor(out vec3 outpos, out vec3 outnor)
{
- vec3 p = point_object_to_world(pos.xyz);
+ vec3 p;
+ float radius;
+ pointcloud_get_pos_and_radius(p, radius);
+
mat3 facing_mat = pointcloud_get_facing_matrix(p);
- float radius = dot(abs(mat3(ModelMatrix) * pos.www), vec3(1.0 / 3.0));
/* TODO(fclem): remove multiplication here. Here only for keeping the size correct for now. */
radius *= 0.01;
outpos = p + (facing_mat * pos_inst) * radius;
diff --git a/source/blender/draw/intern/shaders/common_smaa_lib.glsl b/source/blender/draw/intern/shaders/common_smaa_lib.glsl
index 36ffb4d8b32..73f65fb0799 100644
--- a/source/blender/draw/intern/shaders/common_smaa_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_smaa_lib.glsl
@@ -736,9 +736,11 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord,
float2 edges = step(threshold, delta.xy);
# ifndef SMAA_NO_DISCARD
+# ifdef GPU_FRAGMENT_SHADER
// Then discard if there is no edge:
if (dot(edges, float2(1.0, 1.0)) == 0.0)
discard;
+# endif
# endif
// Calculate right and bottom deltas:
@@ -804,9 +806,11 @@ float2 SMAAColorEdgeDetectionPS(float2 texcoord,
float2 edges = step(threshold, delta.xy);
# ifndef SMAA_NO_DISCARD
+# ifdef GPU_FRAGMENT_SHADER
// Then discard if there is no edge:
if (dot(edges, float2(1.0, 1.0)) == 0.0)
discard;
+# endif
# endif
// Calculate right and bottom deltas:
@@ -851,8 +855,10 @@ float2 SMAADepthEdgeDetectionPS(float2 texcoord, float4 offset[3], SMAATexture2D
float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z));
float2 edges = step(SMAA_DEPTH_THRESHOLD, delta);
+# ifdef GPU_FRAGMENT_SHADER
if (dot(edges, float2(1.0, 1.0)) == 0.0)
discard;
+# endif
return edges;
}
diff --git a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl
index 005561964b8..e6538d80111 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl
@@ -98,10 +98,10 @@ uint get_index(uint i)
/* Duplicate of #PosNorLoop from the mesh extract CPU code.
* We do not use a vec3 for the position as it will be padded to a vec4 which is incompatible with
- * the format. */
+ * the format. */
struct PosNorLoop {
float x, y, z;
- /* TODO(kevindietrich) : figure how to compress properly as GLSL does not have char/short types,
+ /* TODO(@kevindietrich): figure how to compress properly as GLSL does not have char/short types,
* bit operations get tricky. */
float nx, ny, nz;
float flag;
diff --git a/source/blender/draw/intern/shaders/common_view_clipping_lib.glsl b/source/blender/draw/intern/shaders/common_view_clipping_lib.glsl
new file mode 100644
index 00000000000..d55808c42d2
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_view_clipping_lib.glsl
@@ -0,0 +1,33 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+#if defined(GPU_VERTEX_SHADER) || defined(GPU_GEOMETRY_SHADER)
+
+void view_clipping_distances(vec3 wpos)
+{
+# ifdef USE_WORLD_CLIP_PLANES
+ vec4 pos = vec4(wpos, 1.0);
+ gl_ClipDistance[0] = dot(drw_view.clip_planes[0], pos);
+ gl_ClipDistance[1] = dot(drw_view.clip_planes[1], pos);
+ gl_ClipDistance[2] = dot(drw_view.clip_planes[2], pos);
+ gl_ClipDistance[3] = dot(drw_view.clip_planes[3], pos);
+ gl_ClipDistance[4] = dot(drw_view.clip_planes[4], pos);
+ gl_ClipDistance[5] = dot(drw_view.clip_planes[5], pos);
+# endif
+}
+
+/* Kept as define for compiler compatibility. */
+# ifdef USE_WORLD_CLIP_PLANES
+# define view_clipping_distances_set(c) \
+ gl_ClipDistance[0] = (c).gl_ClipDistance[0]; \
+ gl_ClipDistance[1] = (c).gl_ClipDistance[1]; \
+ gl_ClipDistance[2] = (c).gl_ClipDistance[2]; \
+ gl_ClipDistance[3] = (c).gl_ClipDistance[3]; \
+ gl_ClipDistance[4] = (c).gl_ClipDistance[4]; \
+ gl_ClipDistance[5] = (c).gl_ClipDistance[5];
+
+# else
+# define view_clipping_distances_set(c)
+# endif
+
+#endif
diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl
index b0d405165f2..2ac157ad208 100644
--- a/source/blender/draw/intern/shaders/common_view_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_view_lib.glsl
@@ -1,5 +1,5 @@
/* Temporary until we fully make the switch. */
-#ifndef DRW_SHADER_SHARED_H
+#ifndef USE_GPU_SHADER_CREATE_INFO
# define DRW_RESOURCE_CHUNK_LEN 512
@@ -24,7 +24,13 @@ layout(std140) uniform viewBlock
vec4 CameraTexCoFactors;
};
-#endif /* DRW_SHADER_SHARED_H */
+#endif /* USE_GPU_SHADER_CREATE_INFO */
+
+#ifdef USE_GPU_SHADER_CREATE_INFO
+# ifndef DRW_RESOURCE_CHUNK_LEN
+# error "Missing draw_view additional create info on shader create info"
+# endif
+#endif
#define ViewNear (ViewVecs[0].w)
#define ViewFar (ViewVecs[1].w)
@@ -84,66 +90,91 @@ vec4 pack_line_data(vec2 frag_co, vec2 edge_start, vec2 edge_pos)
}
}
-uniform int resourceChunk;
+/* Temporary until we fully make the switch. */
+#ifndef DRW_SHADER_SHARED_H
+uniform int drw_resourceChunk;
+#endif /* DRW_SHADER_SHARED_H */
#ifdef GPU_VERTEX_SHADER
-# ifdef GPU_ARB_shader_draw_parameters
-# define baseInstance gl_BaseInstanceARB
-# else /* no ARB_shader_draw_parameters */
-uniform int baseInstance;
-# endif
-# if defined(IN_PLACE_INSTANCES) || defined(INSTANCED_ATTR)
-/* When drawing instances of an object at the same position. */
-# define instanceId 0
-# elif defined(GPU_DEPRECATED_AMD_DRIVER)
-/* A driver bug make it so that when using an attribute with GL_INT_2_10_10_10_REV as format,
- * the gl_InstanceID is incremented by the 2 bit component of the attribute.
- * Ignore gl_InstanceID then. */
-# define instanceId 0
-# else
-# define instanceId gl_InstanceID
-# endif
+/* Temporary until we fully make the switch. */
+# ifndef DRW_SHADER_SHARED_H
-# ifdef UNIFORM_RESOURCE_ID
-/* This is in the case we want to do a special instance drawcall but still want to have the
- * right resourceId and all the correct ubo datas. */
-uniform int resourceId;
-# define resource_id resourceId
-# else
-# define resource_id (baseInstance + instanceId)
-# endif
+/* clang-format off */
+# if defined(IN_PLACE_INSTANCES) || defined(INSTANCED_ATTR) || defined(DRW_LEGACY_MODEL_MATRIX) || defined(GPU_DEPRECATED_AMD_DRIVER)
+/* clang-format on */
+/* When drawing instances of an object at the same position. */
+# define instanceId 0
+# else
+# define instanceId gl_InstanceID
+# endif
+
+# if defined(UNIFORM_RESOURCE_ID)
+/* This is in the case we want to do a special instance drawcall for one object but still want to
+ * have the right resourceId and all the correct ubo datas. */
+uniform int drw_ResourceID;
+# define resource_id drw_ResourceID
+# else
+# define resource_id (gpu_BaseInstance + instanceId)
+# endif
/* Use this to declare and pass the value if
* the fragment shader uses the resource_id. */
-# ifdef USE_GEOMETRY_SHADER
-# define RESOURCE_ID_VARYING flat out int resourceIDGeom;
-# define PASS_RESOURCE_ID resourceIDGeom = resource_id;
-# else
-# define RESOURCE_ID_VARYING flat out int resourceIDFrag;
-# define PASS_RESOURCE_ID resourceIDFrag = resource_id;
+# ifdef USE_GEOMETRY_SHADER
+# define RESOURCE_ID_VARYING flat out int resourceIDGeom;
+# define PASS_RESOURCE_ID resourceIDGeom = resource_id;
+# else
+# define RESOURCE_ID_VARYING flat out int resourceIDFrag;
+# define PASS_RESOURCE_ID resourceIDFrag = resource_id;
+# endif
+
+# endif /* DRW_SHADER_SHARED_H */
+
+#endif /* GPU_VERTEX_SHADER */
+
+/* Temporary until we fully make the switch. */
+#ifdef DRW_SHADER_SHARED_H
+/* TODO(fclem): Rename PASS_RESOURCE_ID to DRW_RESOURCE_ID_VARYING_SET */
+# if defined(UNIFORM_RESOURCE_ID)
+# define resource_id drw_ResourceID
+# define PASS_RESOURCE_ID
+
+# elif defined(GPU_VERTEX_SHADER)
+# define resource_id gpu_InstanceIndex
+# define PASS_RESOURCE_ID drw_ResourceID_iface.resource_index = resource_id;
+
+# elif defined(GPU_GEOMETRY_SHADER)
+# define resource_id drw_ResourceID_iface_in[0].index
+# define PASS_RESOURCE_ID drw_ResourceID_iface_out.resource_index = resource_id;
+
+# elif defined(GPU_FRAGMENT_SHADER)
+# define resource_id drw_ResourceID_iface.resource_index
# endif
-#endif
+/* TODO(fclem): Remove. */
+# define RESOURCE_ID_VARYING
+
+#else
/* If used in a fragment / geometry shader, we pass
* resource_id as varying. */
-#ifdef GPU_GEOMETRY_SHADER
-# define RESOURCE_ID_VARYING \
- flat out int resourceIDFrag; \
- flat in int resourceIDGeom[];
+# ifdef GPU_GEOMETRY_SHADER
+# define RESOURCE_ID_VARYING \
+ flat out int resourceIDFrag; \
+ flat in int resourceIDGeom[];
-# define resource_id resourceIDGeom
-# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0];
-#endif
+# define resource_id resourceIDGeom
+# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0];
+# endif
-#ifdef GPU_FRAGMENT_SHADER
+# ifdef GPU_FRAGMENT_SHADER
flat in int resourceIDFrag;
-# define resource_id resourceIDFrag
+# define resource_id resourceIDFrag
+# endif
#endif
/* Breaking this across multiple lines causes issues for some older GLSL compilers. */
/* clang-format off */
-#if !defined(GPU_INTEL) && !defined(GPU_DEPRECATED_AMD_DRIVER) && !defined(OS_MAC) && !defined(INSTANCED_ATTR)
+#if !defined(GPU_INTEL) && !defined(GPU_DEPRECATED_AMD_DRIVER) && !defined(OS_MAC) && !defined(INSTANCED_ATTR) && !defined(DRW_LEGACY_MODEL_MATRIX)
/* clang-format on */
/* Temporary until we fully make the switch. */
@@ -158,10 +189,10 @@ layout(std140) uniform modelBlock
{
ObjectMatrices drw_matrices[DRW_RESOURCE_CHUNK_LEN];
};
-# endif /* DRW_SHADER_SHARED_H */
-# define ModelMatrix (drw_matrices[resource_id].drw_modelMatrix)
-# define ModelMatrixInverse (drw_matrices[resource_id].drw_modelMatrixInverse)
+# define ModelMatrix (drw_matrices[resource_id].drw_modelMatrix)
+# define ModelMatrixInverse (drw_matrices[resource_id].drw_modelMatrixInverse)
+# endif /* DRW_SHADER_SHARED_H */
#else /* GPU_INTEL */
@@ -177,7 +208,10 @@ uniform mat4 ModelMatrixInverse;
#endif
-#define resource_handle (resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id)
+/* Temporary until we fully make the switch. */
+#ifndef DRW_SHADER_SHARED_H
+# define resource_handle (drw_resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id)
+#endif
/** Transform shortcuts. */
/* Rule of thumb: Try to reuse world positions and normals because converting through viewspace
diff --git a/source/blender/draw/intern/shaders/draw_hair_refine_info.hh b/source/blender/draw/intern/shaders/draw_hair_refine_info.hh
new file mode 100644
index 00000000000..bdfc26b7dcd
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_hair_refine_info.hh
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2022 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(draw_hair_refine_compute)
+ .local_group_size(1, 1)
+ .storage_buf(0, Qualifier::WRITE, "vec4", "posTime[]")
+ .sampler(0, ImageType::FLOAT_BUFFER, "hairPointBuffer")
+ .sampler(1, ImageType::UINT_BUFFER, "hairStrandBuffer")
+ .sampler(2, ImageType::UINT_BUFFER, "hairStrandSegBuffer")
+ .push_constant(Type::VEC4, "hairDupliMatrix", 4)
+ .push_constant(Type::BOOL, "hairCloseTip")
+ .push_constant(Type::FLOAT, "hairRadShape")
+ .push_constant(Type::FLOAT, "hairRadTip")
+ .push_constant(Type::FLOAT, "hairRadRoot")
+ .push_constant(Type::INT, "hairThicknessRes")
+ .push_constant(Type::INT, "hairStrandsRes")
+ .push_constant(Type::INT, "hairStrandOffset")
+ .compute_source("common_hair_refine_comp.glsl")
+ .define("HAIR_PHASE_SUBDIV")
+ .do_static_compilation(true);
diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh
index 10b3754eebb..17a32c7d5ed 100644
--- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh
+++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh
@@ -2,4 +2,5 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(draw_object_infos)
+ .typedef_source("draw_shader_shared.h")
.uniform_buf(1, "ObjectInfos", "drw_infos[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH);
diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh
index a92284efa5b..f9dcf291f95 100644
--- a/source/blender/draw/intern/shaders/draw_view_info.hh
+++ b/source/blender/draw/intern/shaders/draw_view_info.hh
@@ -2,6 +2,43 @@
#include "gpu_shader_create_info.hh"
/* -------------------------------------------------------------------- */
+/** \name Resource ID
+ *
+ * This is used to fetch per object data in drw_matrices and other object indexed
+ * buffers. There is multiple possibilities depending on how we are drawing the object.
+ *
+ * \{ */
+
+/* Standard way. Use gpu_InstanceIndex to index the object data. */
+GPU_SHADER_CREATE_INFO(draw_resource_id).define("DYNAMIC_RESOURCE_ID");
+
+/**
+ * Used if the resource index needs to be passed to the fragment shader.
+ * IMPORTANT: Vertex and Geometry shaders need to use PASS_RESOURCE_ID in main().
+ */
+GPU_SHADER_INTERFACE_INFO(draw_resource_id_iface, "drw_ResourceID_iface")
+ .flat(Type::INT, "resource_index");
+
+GPU_SHADER_CREATE_INFO(draw_resource_id_varying)
+ .vertex_out(draw_resource_id_iface)
+ .geometry_out(draw_resource_id_iface); /* Used if needed. */
+
+/* Variation used when drawing multiple instances for one object. */
+GPU_SHADER_CREATE_INFO(draw_resource_id_uniform)
+ .define("UNIFORM_RESOURCE_ID")
+ .push_constant(Type::INT, "drw_ResourceID");
+
+/**
+ * Declare a resource handle that identify a unique object.
+ * Requires draw_resource_id[_uniform].
+ */
+GPU_SHADER_CREATE_INFO(draw_resource_handle)
+ .define("resource_handle (drw_resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id)")
+ .push_constant(Type::INT, "drw_resourceChunk");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Draw View
* \{ */
@@ -9,32 +46,60 @@ GPU_SHADER_CREATE_INFO(draw_view)
.uniform_buf(0, "ViewInfos", "drw_view", Frequency::PASS)
.typedef_source("draw_shader_shared.h");
-GPU_SHADER_CREATE_INFO(draw_view_instanced_attr)
- .push_constant(0, Type::MAT4, "ModelMatrix")
- .push_constant(16, Type::MAT4, "ModelMatrixInverse")
+GPU_SHADER_CREATE_INFO(draw_modelmat)
+ .uniform_buf(8, "ObjectMatrices", "drw_matrices[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH)
+ .define("ModelMatrix", "(drw_matrices[resource_id].drw_modelMatrix)")
+ .define("ModelMatrixInverse", "(drw_matrices[resource_id].drw_modelMatrixInverse)")
.additional_info("draw_view");
+GPU_SHADER_CREATE_INFO(draw_modelmat_legacy)
+ .define("DRW_LEGACY_MODEL_MATRIX")
+ .push_constant(Type::MAT4, "ModelMatrix")
+ .push_constant(Type::MAT4, "ModelMatrixInverse")
+ .additional_info("draw_view");
+
+GPU_SHADER_CREATE_INFO(draw_modelmat_instanced_attr)
+ .push_constant(Type::MAT4, "ModelMatrix")
+ .push_constant(Type::MAT4, "ModelMatrixInverse")
+ .additional_info("draw_view");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw View
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(drw_clipped).define("USE_WORLD_CLIP_PLANES");
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Geometry Type
* \{ */
-GPU_SHADER_CREATE_INFO(draw_mesh)
- .uniform_buf(8, "ObjectMatrices", "drw_matrices[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH)
- .additional_info("draw_view");
+GPU_SHADER_CREATE_INFO(draw_mesh).additional_info("draw_modelmat", "draw_resource_id");
GPU_SHADER_CREATE_INFO(draw_hair)
- /* TODO(fclem) Finish */
- .uniform_buf(8, "ObjectMatrices", "drw_matrices[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH)
- .additional_info("draw_view");
+ .sampler(15, ImageType::FLOAT_BUFFER, "hairPointBuffer")
+ .sampler(14, ImageType::UINT_BUFFER, "hairStrandBuffer")
+ .sampler(13, ImageType::UINT_BUFFER, "hairStrandSegBuffer")
+ /* TODO(@fclem): Pack these into one UBO. */
+ .push_constant(Type::INT, "hairStrandsRes")
+ .push_constant(Type::INT, "hairThicknessRes")
+ .push_constant(Type::FLOAT, "hairRadRoot")
+ .push_constant(Type::FLOAT, "hairRadTip")
+ .push_constant(Type::FLOAT, "hairRadShape")
+ .push_constant(Type::BOOL, "hairCloseTip")
+ .push_constant(Type::INT, "hairStrandOffset")
+ .push_constant(Type::VEC4, "hairDupliMatrix", 4)
+ .additional_info("draw_modelmat", "draw_resource_id");
GPU_SHADER_CREATE_INFO(draw_pointcloud)
.vertex_in(0, Type::VEC4, "pos")
.vertex_in(1, Type::VEC3, "pos_inst")
.vertex_in(2, Type::VEC3, "nor")
- .define("UNIFORM_RESOURCE_ID")
- .define("INSTANCED_ATTR")
- .additional_info("draw_view_instanced_attr");
+ .additional_info("draw_modelmat_instanced_attr", "draw_resource_id_uniform");
+
+GPU_SHADER_CREATE_INFO(draw_volume).additional_info("draw_modelmat", "draw_resource_id_uniform");
/** \} */
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
index ef702821a59..80f87eea7ca 100644
--- a/source/blender/draw/tests/shaders_test.cc
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -27,8 +27,6 @@ using namespace blender::draw::image_engine;
static void test_workbench_glsl_shaders()
{
- workbench_shader_library_ensure();
-
const int MAX_WPD = 6;
WORKBENCH_PrivateData wpds[MAX_WPD];
@@ -185,10 +183,8 @@ DRAW_TEST(gpencil_glsl_shaders)
static void test_image_glsl_shaders()
{
- IMAGE_shader_library_ensure();
-
- EXPECT_NE(IMAGE_shader_image_get(false), nullptr);
- EXPECT_NE(IMAGE_shader_image_get(true), nullptr);
+ EXPECT_NE(IMAGE_shader_image_get(), nullptr);
+ EXPECT_NE(IMAGE_shader_depth_get(), nullptr);
IMAGE_shader_free();
}
diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt
index 6fa4d94df3a..0baac40660d 100644
--- a/source/blender/editors/animation/CMakeLists.txt
+++ b/source/blender/editors/animation/CMakeLists.txt
@@ -60,10 +60,6 @@ set(LIB
bf_blenlib
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 2eaa42ee578..a697fd2fc96 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -35,8 +35,8 @@
#include "DNA_armature_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
+#include "DNA_curves_types.h"
#include "DNA_gpencil_types.h"
-#include "DNA_hair_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_light_types.h"
@@ -700,8 +700,8 @@ static int acf_object_icon(bAnimListElem *ale)
return ICON_OUTLINER_OB_FONT;
case OB_SURF:
return ICON_OUTLINER_OB_SURFACE;
- case OB_HAIR:
- return ICON_OUTLINER_OB_HAIR;
+ case OB_CURVES:
+ return ICON_OUTLINER_OB_CURVES;
case OB_POINTCLOUD:
return ICON_OUTLINER_OB_POINTCLOUD;
case OB_VOLUME:
@@ -2813,15 +2813,15 @@ static bAnimChannelType ACF_DSSPK = {
/* Hair Expander ------------------------------------------- */
/* TODO: just get this from RNA? */
-static int acf_dshair_icon(bAnimListElem *UNUSED(ale))
+static int acf_dscurves_icon(bAnimListElem *UNUSED(ale))
{
- return ICON_HAIR_DATA;
+ return ICON_CURVES_DATA;
}
/* Get the appropriate flag(s) for the setting when it is valid. */
-static int acf_dshair_setting_flag(bAnimContext *UNUSED(ac),
- eAnimChannel_Settings setting,
- bool *neg)
+static int acf_dscurves_setting_flag(bAnimContext *UNUSED(ac),
+ eAnimChannel_Settings setting,
+ bool *neg)
{
/* clear extra return data first */
*neg = false;
@@ -2846,22 +2846,24 @@ static int acf_dshair_setting_flag(bAnimContext *UNUSED(ac),
}
/* get pointer to the setting */
-static void *acf_dshair_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings setting, short *type)
+static void *acf_dscurves_setting_ptr(bAnimListElem *ale,
+ eAnimChannel_Settings setting,
+ short *type)
{
- Hair *hair = (Hair *)ale->data;
+ Curves *curves = (Curves *)ale->data;
/* clear extra return data first */
*type = 0;
switch (setting) {
case ACHANNEL_SETTING_EXPAND: /* expanded */
- return GET_ACF_FLAG_PTR(hair->flag, type);
+ return GET_ACF_FLAG_PTR(curves->flag, type);
case ACHANNEL_SETTING_SELECT: /* selected */
case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
- if (hair->adt) {
- return GET_ACF_FLAG_PTR(hair->adt->flag, type);
+ if (curves->adt) {
+ return GET_ACF_FLAG_PTR(curves->adt->flag, type);
}
return NULL;
@@ -2870,9 +2872,9 @@ static void *acf_dshair_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se
}
}
-/* hair expander type define */
+/* Curves expander type define */
static bAnimChannelType ACF_DSHAIR = {
- "Hair Expander", /* type name */
+ "Curves Expander", /* type name */
ACHANNEL_ROLE_EXPANDER, /* role */
acf_generic_dataexpand_color, /* backdrop color */
@@ -2882,11 +2884,11 @@ static bAnimChannelType ACF_DSHAIR = {
acf_generic_idblock_name, /* name */
acf_generic_idblock_name_prop, /* name prop */
- acf_dshair_icon, /* icon */
+ acf_dscurves_icon, /* icon */
acf_generic_dataexpand_setting_valid, /* has setting */
- acf_dshair_setting_flag, /* flag for setting */
- acf_dshair_setting_ptr /* pointer for setting */
+ acf_dscurves_setting_flag, /* flag for setting */
+ acf_dscurves_setting_ptr /* pointer for setting */
};
/* PointCloud Expander ------------------------------------------- */
@@ -3418,7 +3420,7 @@ static void acf_gpd_color(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale),
/* TODO: just get this from RNA? */
static int acf_gpd_icon(bAnimListElem *UNUSED(ale))
{
- return ICON_GREASEPENCIL;
+ return ICON_OUTLINER_OB_GREASEPENCIL;
}
/* check if some setting exists for this channel */
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index c1a09b9d21f..3307385b84a 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -47,8 +47,8 @@
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
+#include "DNA_curves_types.h"
#include "DNA_gpencil_types.h"
-#include "DNA_hair_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_layer_types.h"
@@ -791,10 +791,10 @@ static bAnimListElem *make_new_animlistelem(void *data,
break;
}
case ANIMTYPE_DSHAIR: {
- Hair *hair = (Hair *)data;
- AnimData *adt = hair->adt;
+ Curves *curves = (Curves *)data;
+ AnimData *adt = curves->adt;
- ale->flag = FILTER_HAIR_OBJD(hair);
+ ale->flag = FILTER_CURVES_OBJD(curves);
ale->key_data = (adt) ? adt->action : NULL;
ale->datatype = ALE_ACT;
@@ -2616,16 +2616,16 @@ static size_t animdata_filter_ds_obdata(
expanded = FILTER_SPK_OBJD(spk);
break;
}
- case OB_HAIR: /* ---------- Hair ----------- */
+ case OB_CURVES: /* ---------- Curves ----------- */
{
- Hair *hair = (Hair *)ob->data;
+ Curves *curves = (Curves *)ob->data;
if (ads->filterflag2 & ADS_FILTER_NOHAIR) {
return 0;
}
type = ANIMTYPE_DSHAIR;
- expanded = FILTER_HAIR_OBJD(hair);
+ expanded = FILTER_CURVES_OBJD(curves);
break;
}
case OB_POINTCLOUD: /* ---------- PointCloud ----------- */
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index 1e60a129535..8c52480b89a 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -169,7 +169,7 @@ static void change_frame_apply(bContext *C, wmOperator *op)
FRAMENUMBER_MIN_CLAMP(CFRA);
/* do updates */
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
}
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 6fba2d9c258..ae8d04f51d3 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -496,7 +496,7 @@ static void ED_keylist_draw_list_elem_prepare_for_drawing(AnimKeylistDrawListEle
}
typedef struct AnimKeylistDrawList {
- ListBase /* AnimKeylistDrawListElem*/ channels;
+ ListBase /* AnimKeylistDrawListElem */ channels;
} AnimKeylistDrawList;
AnimKeylistDrawList *ED_keylist_draw_list_create(void)
diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c
index 145d67b7810..dfe6566df67 100644
--- a/source/blender/editors/animation/keyframes_edit.c
+++ b/source/blender/editors/animation/keyframes_edit.c
@@ -1283,6 +1283,61 @@ static short set_bezt_sine(KeyframeEditData *UNUSED(ked), BezTriple *bezt)
return 0;
}
+static void handle_flatten(float vec[3][3], const int idx, const float direction[2])
+{
+ BLI_assert_msg(idx == 0 || idx == 2, "handle_flatten() expects a handle index");
+
+ add_v2_v2v2(vec[idx], vec[1], direction);
+}
+
+static void handle_set_length(float vec[3][3], const int idx, const float handle_length)
+{
+ BLI_assert_msg(idx == 0 || idx == 2, "handle_set_length() expects a handle index");
+
+ float handle_direction[2];
+ sub_v2_v2v2(handle_direction, vec[idx], vec[1]);
+ normalize_v2_length(handle_direction, handle_length);
+ add_v2_v2v2(vec[idx], vec[1], handle_direction);
+}
+
+void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu,
+ const eEditKeyframes_Equalize mode,
+ const float handle_length,
+ const bool flatten)
+{
+ uint i;
+ BezTriple *bezt;
+ const float flat_direction_left[2] = {-handle_length, 0.f};
+ const float flat_direction_right[2] = {handle_length, 0.f};
+
+ /* Loop through an F-Curves keyframes. */
+ for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
+ if ((bezt->f2 & SELECT) == 0) {
+ continue;
+ }
+
+ /* Perform handle equalization if mode is 'Both' or 'Left'. */
+ if (mode & EQUALIZE_HANDLES_LEFT) {
+ if (flatten) {
+ handle_flatten(bezt->vec, 0, flat_direction_left);
+ }
+ else {
+ handle_set_length(bezt->vec, 0, handle_length);
+ }
+ }
+
+ /* Perform handle equalization if mode is 'Both' or 'Right'. */
+ if (mode & EQUALIZE_HANDLES_RIGHT) {
+ if (flatten) {
+ handle_flatten(bezt->vec, 2, flat_direction_right);
+ }
+ else {
+ handle_set_length(bezt->vec, 2, handle_length);
+ }
+ }
+ }
+}
+
KeyframeEditFunc ANIM_editkeyframes_ipo(short mode)
{
switch (mode) {
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index aff5803f037..c8ca27c64a3 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -63,9 +63,5 @@ set(LIB
bf_blenlib
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_armature "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index 02ecfdb4ea6..e1d4b5fec73 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Operators and API's for creating bones
*/
/** \file
* \ingroup edarmature
+ * Operators and API's for creating bones.
*/
#include "DNA_anim_types.h"
@@ -581,8 +581,6 @@ static void updateDuplicateLocRotConstraintSettings(Object *ob,
{
/* This code assumes that bRotLimitConstraint and bLocLimitConstraint have the same fields in
* the same memory locations. */
- BLI_assert(sizeof(bLocLimitConstraint) == sizeof(bRotLimitConstraint));
-
bRotLimitConstraint *limit = (bRotLimitConstraint *)curcon->data;
float local_mat[4][4], imat[4][4];
@@ -798,6 +796,13 @@ static void updateDuplicateTransformConstraintSettings(Object *ob,
trans->to_min_rot[i] = temp_vec[i];
}
}
+
+ if (trans->from == TRANS_ROTATION && trans->map[1] == Y) {
+ /* Y Rot to Y Rot: Flip and invert */
+ trans->to_max_rot[1] = -trans->to_min_rot[1];
+ trans->to_min_rot[1] = -temp_vec[1];
+ }
+
break;
}
/* convert back to the settings space */
@@ -854,14 +859,28 @@ static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Obj
Main *bmain = CTX_data_main(C);
char name_flip[MAX_ID_NAME - 2];
+ /* Invert the X location */
+ pchan->custom_translation[0] *= -1;
+ /* Invert the Y rotation */
+ pchan->custom_rotation_euler[1] *= -1;
+ /* Invert the Z rotation */
+ pchan->custom_rotation_euler[2] *= -1;
+
/* Skip the first two chars in the object name as those are used to store object type */
BLI_string_flip_side_name(name_flip, pchan->custom->id.name + 2, false, sizeof(name_flip));
Object *shape_ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name_flip);
+ /* If name_flip doesn't exist, BKE_libblock_find_name() returns pchan->custom (best match) */
+ shape_ob = shape_ob == pchan->custom ? NULL : shape_ob;
+
if (shape_ob != NULL) {
/* A flipped shape object exists, use it! */
pchan->custom = shape_ob;
}
+ else {
+ /* Flip shape */
+ pchan->custom_scale_xyz[0] *= -1;
+ }
}
}
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index b709980cabe..0094783e50f 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Armature EditMode tools - transforms, chain based editing, and other settings
*/
/** \file
* \ingroup edarmature
+ * Armature EditMode tools - transforms, chain based editing, and other settings.
*/
#include "DNA_armature_types.h"
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
index 252cf806e34..aaeac29b7d0 100644
--- a/source/blender/editors/armature/armature_intern.h
+++ b/source/blender/editors/armature/armature_intern.h
@@ -27,6 +27,7 @@
struct wmOperatorType;
struct Base;
+struct GPUSelectResult;
struct Object;
struct Scene;
struct bContext;
@@ -323,21 +324,21 @@ struct Bone *ED_armature_pick_bone(struct bContext *C,
struct EditBone *ED_armature_pick_ebone_from_selectbuffer(struct Base **bases,
uint bases_len,
- const uint *buffer,
+ const struct GPUSelectResult *buffer,
short hits,
bool findunsel,
bool do_nearest,
struct Base **r_base);
struct bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(struct Base **bases,
uint bases_len,
- const uint *buffer,
+ const struct GPUSelectResult *buffer,
short hits,
bool findunsel,
bool do_nearest,
struct Base **r_base);
struct Bone *ED_armature_pick_bone_from_selectbuffer(struct Base **bases,
uint bases_len,
- const uint *buffer,
+ const struct GPUSelectResult *buffer,
short hits,
bool findunsel,
bool do_nearest,
diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c
index 750c64d74a7..c45c6297d86 100644
--- a/source/blender/editors/armature/armature_naming.c
+++ b/source/blender/editors/armature/armature_naming.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Operators and API's for renaming bones both in and out of Edit Mode
*/
/** \file
* \ingroup edarmature
+ * Operators and API's for renaming bones both in and out of Edit Mode.
*
* This file contains functions/API's for renaming bones and/or working with them.
*/
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
index eebe8a447f7..17d25aec198 100644
--- a/source/blender/editors/armature/armature_relations.c
+++ b/source/blender/editors/armature/armature_relations.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Operators for relations between bones and for transferring bones between armature objects
*/
/** \file
* \ingroup edarmature
+ * Operators for relations between bones and for transferring bones between armature objects.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 5e4cb813064..111989f1d86 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * API's and Operators for selecting armature bones in EditMode
*/
/** \file
* \ingroup edarmature
+ * API's and Operators for selecting armature bones in EditMode.
*/
#include "MEM_guardedalloc.h"
@@ -55,6 +55,8 @@
#include "DEG_depsgraph.h"
+#include "GPU_select.h"
+
#include "armature_intern.h"
/* utility macros for storing a temp int in the bone (selection flag) */
@@ -67,10 +69,10 @@
Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases,
uint bases_len,
- int hit,
+ const uint select_id,
EditBone **r_ebone)
{
- const uint hit_object = hit & 0xFFFF;
+ const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
EditBone *ebone = NULL;
/* TODO(campbell): optimize, eg: sort & binary search. */
@@ -81,7 +83,7 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases,
}
}
if (base != NULL) {
- const uint hit_bone = (hit & ~BONESEL_ANY) >> 16;
+ const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
bArmature *arm = base->object->data;
ebone = BLI_findlink(arm->edbo, hit_bone);
}
@@ -91,10 +93,10 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases,
Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects,
uint objects_len,
- int hit,
+ const uint select_id,
EditBone **r_ebone)
{
- const uint hit_object = hit & 0xFFFF;
+ const uint hit_object = select_id & 0xFFFF;
Object *ob = NULL;
EditBone *ebone = NULL;
/* TODO(campbell): optimize, eg: sort & binary search. */
@@ -105,7 +107,7 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects,
}
}
if (ob != NULL) {
- const uint hit_bone = (hit & ~BONESEL_ANY) >> 16;
+ const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
bArmature *arm = ob->data;
ebone = BLI_findlink(arm->edbo, hit_bone);
}
@@ -115,10 +117,10 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects,
Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases,
uint bases_len,
- int hit,
+ const uint select_id,
bPoseChannel **r_pchan)
{
- const uint hit_object = hit & 0xFFFF;
+ const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
bPoseChannel *pchan = NULL;
/* TODO(campbell): optimize, eg: sort & binary search. */
@@ -130,7 +132,7 @@ Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases,
}
if (base != NULL) {
if (base->object->pose != NULL) {
- const uint hit_bone = (hit & ~BONESEL_ANY) >> 16;
+ const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
/* pchan may be NULL. */
pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone);
}
@@ -141,11 +143,11 @@ Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases,
Base *ED_armature_base_and_bone_from_select_buffer(Base **bases,
uint bases_len,
- int hit,
+ const uint select_id,
Bone **r_bone)
{
bPoseChannel *pchan = NULL;
- Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, bases_len, hit, &pchan);
+ Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, bases_len, select_id, &pchan);
*r_bone = pchan ? pchan->bone : NULL;
return base;
}
@@ -166,8 +168,8 @@ Base *ED_armature_base_and_bone_from_select_buffer(Base **bases,
static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode,
Base **bases,
uint bases_len,
- const uint *buffer,
- short hits,
+ const GPUSelectResult *buffer,
+ const short hits,
bool findunsel,
bool do_nearest,
Base **r_base)
@@ -181,7 +183,7 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode
int minsel = 0xffffffff, minunsel = 0xffffffff;
for (short i = 0; i < hits; i++) {
- hitresult = buffer[3 + (i * 4)];
+ hitresult = buffer[i].id;
if (hitresult & BONESEL_ANY) { /* to avoid including objects in selection */
Base *base = NULL;
@@ -221,10 +223,10 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode
if (data) {
if (sel) {
if (do_nearest) {
- if (minsel > buffer[4 * i + 1]) {
+ if (minsel > buffer[i].depth) {
firstSel = data;
firstSel_base = base;
- minsel = buffer[4 * i + 1];
+ minsel = buffer[i].depth;
}
}
else {
@@ -237,10 +239,10 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode
}
else {
if (do_nearest) {
- if (minunsel > buffer[4 * i + 1]) {
+ if (minunsel > buffer[i].depth) {
firstunSel = data;
firstunSel_base = base;
- minunsel = buffer[4 * i + 1];
+ minunsel = buffer[i].depth;
}
}
else {
@@ -268,8 +270,8 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode
EditBone *ED_armature_pick_ebone_from_selectbuffer(Base **bases,
uint bases_len,
- const uint *buffer,
- short hits,
+ const GPUSelectResult *buffer,
+ const short hits,
bool findunsel,
bool do_nearest,
Base **r_base)
@@ -281,8 +283,8 @@ EditBone *ED_armature_pick_ebone_from_selectbuffer(Base **bases,
bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(Base **bases,
uint bases_len,
- const uint *buffer,
- short hits,
+ const GPUSelectResult *buffer,
+ const short hits,
bool findunsel,
bool do_nearest,
Base **r_base)
@@ -294,8 +296,8 @@ bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(Base **bases,
Bone *ED_armature_pick_bone_from_selectbuffer(Base **bases,
uint bases_len,
- const uint *buffer,
- short hits,
+ const GPUSelectResult *buffer,
+ const short hits,
bool findunsel,
bool do_nearest,
Base **r_base)
@@ -327,7 +329,7 @@ static void *ed_armature_pick_bone_impl(
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
rcti rect;
- uint buffer[MAXPICKBUF];
+ GPUSelectResult buffer[MAXPICKELEMS];
short hits;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
@@ -340,7 +342,7 @@ static void *ed_armature_pick_bone_impl(
hits = view3d_opengl_select_with_id_filter(&vc,
buffer,
- MAXPICKBUF,
+ ARRAY_SIZE(buffer),
&rect,
VIEW3D_SELECT_PICK_NEAREST,
VIEW3D_SELECT_FILTER_NOP,
@@ -636,15 +638,15 @@ void ARMATURE_OT_select_linked_pick(wmOperatorType *ot)
* \{ */
/* utility function for get_nearest_editbonepoint */
-static int selectbuffer_ret_hits_12(uint *UNUSED(buffer), const int hits12)
+static int selectbuffer_ret_hits_12(GPUSelectResult *UNUSED(buffer), const int hits12)
{
return hits12;
}
-static int selectbuffer_ret_hits_5(uint *buffer, const int hits12, const int hits5)
+static int selectbuffer_ret_hits_5(GPUSelectResult *buffer, const int hits12, const int hits5)
{
- const int ofs = 4 * hits12;
- memcpy(buffer, buffer + ofs, 4 * hits5 * sizeof(uint));
+ const int ofs = hits12;
+ memcpy(buffer, buffer + ofs, hits5 * sizeof(*buffer));
return hits5;
}
@@ -653,7 +655,7 @@ static int selectbuffer_ret_hits_5(uint *buffer, const int hits12, const int hit
static EditBone *get_nearest_editbonepoint(
ViewContext *vc, bool findunsel, bool use_cycle, Base **r_base, int *r_selmask)
{
- uint buffer[MAXPICKBUF];
+ GPUSelectResult buffer[MAXPICKELEMS];
struct {
uint hitresult;
Base *base;
@@ -692,7 +694,7 @@ static EditBone *get_nearest_editbonepoint(
rcti rect;
BLI_rcti_init_pt_radius(&rect, vc->mval, 12);
const int hits12 = view3d_opengl_select_with_id_filter(
- vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter, select_id_ignore);
+ vc, buffer, ARRAY_SIZE(buffer), &rect, select_mode, select_filter, select_id_ignore);
if (hits12 == 1) {
hits = selectbuffer_ret_hits_12(buffer, hits12);
@@ -701,10 +703,15 @@ static EditBone *get_nearest_editbonepoint(
else if (hits12 > 0) {
int ofs;
- ofs = 4 * hits12;
+ ofs = hits12;
BLI_rcti_init_pt_radius(&rect, vc->mval, 5);
- const int hits5 = view3d_opengl_select_with_id_filter(
- vc, buffer + ofs, MAXPICKBUF - ofs, &rect, select_mode, select_filter, select_id_ignore);
+ const int hits5 = view3d_opengl_select_with_id_filter(vc,
+ buffer + ofs,
+ ARRAY_SIZE(buffer) - ofs,
+ &rect,
+ select_mode,
+ select_filter,
+ select_id_ignore);
if (hits5 == 1) {
hits = selectbuffer_ret_hits_5(buffer, hits12, hits5);
@@ -732,7 +739,7 @@ cache_end:
/* See if there are any selected bones in this group */
if (hits > 0) {
if (hits == 1) {
- result_bias.hitresult = buffer[3];
+ result_bias.hitresult = buffer->id;
result_bias.base = ED_armature_base_and_ebone_from_select_buffer(
bases, bases_len, result_bias.hitresult, &result_bias.ebone);
}
@@ -771,7 +778,7 @@ cache_end:
}
for (int i = 0; i < hits; i++) {
- const uint hitresult = buffer[3 + (i * 4)];
+ const uint hitresult = buffer[i].id;
Base *base = NULL;
EditBone *ebone;
diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c
index ec5c665402b..9ef198f442c 100644
--- a/source/blender/editors/armature/armature_skinning.c
+++ b/source/blender/editors/armature/armature_skinning.c
@@ -15,12 +15,12 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * API's for creating vertex groups from bones
- * - Interfaces with heat weighting in meshlaplacian
*/
/** \file
* \ingroup edarmature
+ * API's for creating vertex groups from bones
+ * - Interfaces with heat weighting in meshlaplacian.
*/
#include "DNA_armature_types.h"
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index 787d7cbaab0..b3b6c37cf61 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -12,11 +12,11 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * meshlaplacian.c: Algorithms using the mesh laplacian.
*/
/** \file
* \ingroup edarmature
+ * Algorithms using the mesh laplacian.
*/
#include "MEM_guardedalloc.h"
@@ -1742,14 +1742,15 @@ static void harmonic_coordinates_bind(MeshDeformModifierData *mmd, MeshDeformBin
free_bvhtree_from_mesh(&mdb->bvhdata);
}
-void ED_mesh_deform_bind_callback(MeshDeformModifierData *mmd,
+void ED_mesh_deform_bind_callback(Object *object,
+ MeshDeformModifierData *mmd,
Mesh *cagemesh,
float *vertexcos,
int totvert,
float cagemat[4][4])
{
MeshDeformModifierData *mmd_orig = (MeshDeformModifierData *)BKE_modifier_get_original(
- &mmd->modifier);
+ object, &mmd->modifier);
MeshDeformBind mdb;
MVert *mvert;
int a;
diff --git a/source/blender/editors/armature/meshlaplacian.h b/source/blender/editors/armature/meshlaplacian.h
index 7e7b64c7510..f024b17f1cc 100644
--- a/source/blender/editors/armature/meshlaplacian.h
+++ b/source/blender/editors/armature/meshlaplacian.h
@@ -12,12 +12,11 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * BIF_meshlaplacian.h: Algorithms using the mesh laplacian.
*/
/** \file
* \ingroup edarmature
+ * BIF_meshlaplacian.h: Algorithms using the mesh laplacian.
*/
#pragma once
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index 8bd6c9f54fd..5db4a4b6eaa 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Pose Mode API's and Operators for Pose Mode armatures
*/
/** \file
* \ingroup edarmature
+ * Pose Mode API's and Operators for Pose Mode armatures.
*/
#include "MEM_guardedalloc.h"
@@ -149,36 +149,6 @@ bool ED_object_posemode_exit(bContext *C, Object *ob)
return ok;
}
-/* if a selected or active bone is protected, throw error (only if warn == 1) and return 1 */
-/* only_selected == 1: the active bone is allowed to be protected */
-#if 0 /* UNUSED 2.5 */
-static bool pose_has_protected_selected(Object *ob, short warn)
-{
- /* check protection */
- if (ob->proxy) {
- bPoseChannel *pchan;
- bArmature *arm = ob->data;
-
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->bone && BKE_pose_is_layer_visible(arm, pchan)) {
- if (pchan->bone->layer & arm->layer_protected) {
- if (pchan->bone->flag & BONE_SELECTED) {
- break;
- }
- }
- }
- }
- if (pchan) {
- if (warn) {
- error("Cannot change Proxy protected bones");
- }
- return 1;
- }
- }
- return 0;
-}
-#endif
-
/* ********************************************** */
/* Motion Paths */
@@ -1056,10 +1026,6 @@ static int pose_hide_exec(bContext *C, wmOperator *op)
Object *ob_iter = objects[ob_index];
bArmature *arm = ob_iter->data;
- if (ob_iter->proxy != NULL) {
- BKE_report(op->reports, RPT_INFO, "Undo of hiding can only be done with Reveal Selected");
- }
-
bool changed = bone_looper(ob_iter, arm->bonebase.first, hide_select_p, hide_pose_bone_fn) !=
0;
if (changed) {
diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c
index 38d15d8b880..10ffa3fcae2 100644
--- a/source/blender/editors/armature/pose_group.c
+++ b/source/blender/editors/armature/pose_group.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2008 Blender Foundation
* All rights reserved.
- * Implementation of Bone Groups operators and editing API's
*/
/** \file
* \ingroup edarmature
+ * Implementation of Bone Groups operators and editing API's.
*/
#include <string.h>
@@ -62,8 +62,8 @@ static bool pose_group_poll(bContext *C)
}
Object *obpose = ED_pose_object_from_context(C);
- if ((obpose->proxy != NULL) || (obpose->proxy_group != NULL) || ID_IS_OVERRIDE_LIBRARY(obpose)) {
- CTX_wm_operator_poll_msg_set(C, "Cannot edit bone groups for proxies or library overrides");
+ if (ID_IS_OVERRIDE_LIBRARY(obpose)) {
+ CTX_wm_operator_poll_msg_set(C, "Cannot edit bone groups for library overrides");
return false;
}
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index 0b889149f9d..f41c3657431 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -240,8 +240,8 @@ void ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
View3D *v3d,
Base *base,
- const uint *buffer,
- short hits,
+ const struct GPUSelectResult *buffer,
+ const short hits,
bool extend,
bool deselect,
bool toggle,
diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt
index 086fab4ab47..d91102528e5 100644
--- a/source/blender/editors/asset/CMakeLists.txt
+++ b/source/blender/editors/asset/CMakeLists.txt
@@ -21,6 +21,7 @@ set(INC
../../blenkernel
../../blenlib
../../blenloader
+ ../../blentranslation
../../makesdna
../../makesrna
../../windowmanager
diff --git a/source/blender/editors/asset/ED_asset_catalog.h b/source/blender/editors/asset/ED_asset_catalog.h
index be99de01173..acb0b67d9c0 100644
--- a/source/blender/editors/asset/ED_asset_catalog.h
+++ b/source/blender/editors/asset/ED_asset_catalog.h
@@ -28,8 +28,8 @@
extern "C" {
#endif
-struct Main;
struct AssetLibrary;
+struct Main;
void ED_asset_catalogs_save_from_main_path(struct AssetLibrary *library, const struct Main *bmain);
diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h
index 512a1d94535..8f10e769c52 100644
--- a/source/blender/editors/asset/ED_asset_mark_clear.h
+++ b/source/blender/editors/asset/ED_asset_mark_clear.h
@@ -25,8 +25,8 @@ extern "C" {
#endif
struct ID;
-struct bContext;
struct Main;
+struct bContext;
/**
* Mark the datablock as asset.
diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h
index 36cbb4591e9..e1c327808aa 100644
--- a/source/blender/editors/asset/ED_asset_type.h
+++ b/source/blender/editors/asset/ED_asset_type.h
@@ -30,7 +30,7 @@ struct ID;
bool ED_asset_type_id_is_non_experimental(const struct ID *id);
#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \
- (FILTER_ID_MA | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO)
+ (FILTER_ID_MA | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO | FILTER_ID_NT)
/**
* Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset,
@@ -52,7 +52,8 @@ int64_t ED_asset_types_supported_as_filter_flags(void);
* strings with this (not all UI code supports dynamic strings nicely).
* Should start with a consonant, so usages can prefix it with "a" (not "an").
*/
-#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Object, Pose Action, or World"
+#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING \
+ "Material, Object, Pose Action, Node Group or World"
#ifdef __cplusplus
}
diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc
index c075ae390d9..fa0946ffe3d 100644
--- a/source/blender/editors/asset/intern/asset_list.cc
+++ b/source/blender/editors/asset/intern/asset_list.cc
@@ -41,6 +41,7 @@
#include "WM_api.h"
/* XXX uses private header of file-space. */
+#include "../space_file/file_indexer.h"
#include "../space_file/filelist.h"
#include "ED_asset_handle.h"
@@ -170,7 +171,8 @@ void AssetList::setup()
"",
"");
- filelist_setindexer(files, &file_indexer_asset);
+ const bool use_asset_indexer = !USER_EXPERIMENTAL_TEST(&U, no_asset_indexing);
+ filelist_setindexer(files, use_asset_indexer ? &file_indexer_asset : &file_indexer_noop);
char path[FILE_MAXDIR] = "";
if (user_library) {
diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index f7755aa9fea..0469f14487d 100644
--- a/source/blender/editors/asset/intern/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -39,6 +39,8 @@
/* XXX needs access to the file list, should all be done via the asset system in future. */
#include "ED_fileselect.h"
+#include "BLT_translation.h"
+
#include "RNA_access.h"
#include "RNA_define.h"
@@ -342,8 +344,8 @@ static bool asset_clear_poll(bContext *C)
IDVecStats ctx_stats = asset_operation_get_id_vec_stats_from_context(C);
if (!ctx_stats.has_asset) {
- const char *msg_single = "Data-block is not marked as asset";
- const char *msg_multiple = "No data-block selected that is marked as asset";
+ const char *msg_single = TIP_("Data-block is not marked as asset");
+ const char *msg_multiple = TIP_("No data-block selected that is marked as asset");
CTX_wm_operator_poll_msg_set(C, ctx_stats.is_single ? msg_single : msg_multiple);
return false;
}
@@ -365,8 +367,8 @@ static char *asset_clear_get_description(struct bContext *UNUSED(C),
}
return BLI_strdup(
- "Delete all asset metadata, turning the selected asset data-blocks back into normal "
- "data-blocks, and set Fake User to ensure the data-blocks will still be saved");
+ TIP_("Delete all asset metadata, turning the selected asset data-blocks back into normal "
+ "data-blocks, and set Fake User to ensure the data-blocks will still be saved"));
}
static void ASSET_OT_clear(wmOperatorType *ot)
diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc
index 028c0cb9ffc..3d6ce3e3409 100644
--- a/source/blender/editors/asset/intern/asset_type.cc
+++ b/source/blender/editors/asset/intern/asset_type.cc
@@ -30,7 +30,7 @@ bool ED_asset_type_id_is_non_experimental(const ID *id)
{
/* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and
* #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */
- return ELEM(GS(id->name), ID_MA, ID_OB, ID_AC, ID_WO);
+ return ELEM(GS(id->name), ID_MA, ID_OB, ID_AC, ID_WO, ID_NT);
}
bool ED_asset_type_is_supported(const ID *id)
diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt
index 877c2d99102..0ac572c0422 100644
--- a/source/blender/editors/curve/CMakeLists.txt
+++ b/source/blender/editors/curve/CMakeLists.txt
@@ -51,9 +51,5 @@ set(LIB
extern_curve_fit_nd
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index a034e4bb10e..a70bc1c0350 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -4953,19 +4953,22 @@ bool ed_editnurb_spin(
if ((a & 1) == 0) {
rotateflagNurb(editnurb, SELECT, cent, scalemat1);
- weightflagNurb(editnurb, SELECT, 0.25 * M_SQRT2);
+ weightflagNurb(editnurb, SELECT, 0.5 * M_SQRT2);
}
else {
rotateflagNurb(editnurb, SELECT, cent, scalemat2);
- weightflagNurb(editnurb, SELECT, 4.0 / M_SQRT2);
+ weightflagNurb(editnurb, SELECT, 2.0 / M_SQRT2);
}
}
if (ok) {
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (ED_curve_nurb_select_check(v3d, nu)) {
- nu->orderv = 4;
- nu->flagv |= CU_NURB_CYCLIC;
+ nu->orderv = 3;
+ /* It is challenging to create a good approximation of a circle with uniform knots vector
+ * (which is forced in Blender for cyclic NURBS curves). Here a NURBS circle is constructed
+ * by connecting four Bezier arcs. */
+ nu->flagv |= CU_NURB_CYCLIC | CU_NURB_BEZIER;
BKE_nurb_knot_calc_v(nu);
}
}
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index 614805a70f5..daef4a21692 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -306,9 +306,9 @@ Nurb *ED_curve_add_nurbs_primitive(
else if (cutype == CU_NURBS) { /* nurb */
nu->pntsu = 8;
nu->pntsv = 1;
- nu->orderu = 4;
+ nu->orderu = 3;
nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "addNurbprim6");
- nu->flagu = CU_NURB_CYCLIC;
+ nu->flagu = CU_NURB_CYCLIC | CU_NURB_BEZIER;
bp = nu->bp;
for (a = 0; a < 8; a++) {
@@ -322,7 +322,7 @@ Nurb *ED_curve_add_nurbs_primitive(
bp->vec[2] += 0.25f * nurbcircle[a][1] * grid;
}
if (a & 1) {
- bp->vec[3] = 0.25 * M_SQRT2;
+ bp->vec[3] = 0.5 * M_SQRT2;
}
else {
bp->vec[3] = 1.0;
diff --git a/source/blender/editors/geometry/CMakeLists.txt b/source/blender/editors/geometry/CMakeLists.txt
index 65b9633da98..8c920915937 100644
--- a/source/blender/editors/geometry/CMakeLists.txt
+++ b/source/blender/editors/geometry/CMakeLists.txt
@@ -20,9 +20,11 @@ set(INC
../../blenkernel
../../blenlib
../../depsgraph
+ ../../functions
../../makesdna
../../makesrna
../../windowmanager
+ ../../../../intern/guardedalloc
)
set(INC_SYS
diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc
index 9c0f6728701..17229c4898e 100644
--- a/source/blender/editors/geometry/geometry_attributes.cc
+++ b/source/blender/editors/geometry/geometry_attributes.cc
@@ -21,8 +21,18 @@
* \ingroup edgeometry
*/
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_scene_types.h"
+
#include "BKE_attribute.h"
#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_object_deform.h"
+#include "BKE_report.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -33,10 +43,19 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
#include "ED_object.h"
#include "geometry_intern.hh"
+namespace blender::ed::geometry {
+
+using fn::CPPType;
+using fn::GArray;
+using fn::GVArray;
+
/*********************** Attribute Operators ************************/
static bool geometry_attributes_poll(bContext *C)
@@ -76,7 +95,7 @@ static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C,
return DummyRNA_NULL_items;
}
- return rna_enum_attribute_domain_itemf(static_cast<ID *>(ob->data), r_free);
+ return rna_enum_attribute_domain_itemf(static_cast<ID *>(ob->data), false, r_free);
}
static int geometry_attribute_add_exec(bContext *C, wmOperator *op)
@@ -180,3 +199,189 @@ void GEOMETRY_OT_attribute_remove(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
+enum class ConvertAttributeMode {
+ Generic,
+ UVMap,
+ VertexGroup,
+ VertexColor,
+};
+
+static bool geometry_attribute_convert_poll(bContext *C)
+{
+ if (!geometry_attributes_poll(C)) {
+ return false;
+ }
+
+ Object *ob = ED_object_context(C);
+ ID *data = static_cast<ID *>(ob->data);
+ if (GS(data->name) != ID_ME) {
+ return false;
+ }
+ CustomDataLayer *layer = BKE_id_attributes_active_get(data);
+ if (layer == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_context(C);
+ ID *ob_data = static_cast<ID *>(ob->data);
+ CustomDataLayer *layer = BKE_id_attributes_active_get(ob_data);
+ const std::string name = layer->name;
+
+ const ConvertAttributeMode mode = static_cast<ConvertAttributeMode>(
+ RNA_enum_get(op->ptr, "mode"));
+
+ Mesh *mesh = reinterpret_cast<Mesh *>(ob_data);
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+
+ /* General conversion steps are always the same:
+ * 1. Convert old data to right domain and data type.
+ * 2. Copy the data into a new array so that it does not depend on the old attribute anymore.
+ * 3. Delete the old attribute.
+ * 4. Create a new attribute based on the previously copied data. */
+ switch (mode) {
+ case ConvertAttributeMode::Generic: {
+ const AttributeDomain dst_domain = static_cast<AttributeDomain>(
+ RNA_enum_get(op->ptr, "domain"));
+ const CustomDataType dst_type = static_cast<CustomDataType>(
+ RNA_enum_get(op->ptr, "data_type"));
+
+ if (ELEM(dst_type, CD_PROP_STRING, CD_MLOOPCOL)) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot convert to the selected type");
+ return OPERATOR_CANCELLED;
+ }
+
+ GVArray src_varray = mesh_component.attribute_get_for_read(name, dst_domain, dst_type);
+ const CPPType &cpp_type = src_varray.type();
+ void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__);
+ src_varray.materialize_to_uninitialized(new_data);
+ mesh_component.attribute_try_delete(name);
+ mesh_component.attribute_try_create(name, dst_domain, dst_type, AttributeInitMove(new_data));
+ break;
+ }
+ case ConvertAttributeMode::UVMap: {
+ MLoopUV *dst_uvs = static_cast<MLoopUV *>(
+ MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopUV), __func__));
+ VArray<float2> src_varray = mesh_component.attribute_get_for_read<float2>(
+ name, ATTR_DOMAIN_CORNER, {0.0f, 0.0f});
+ for (const int i : IndexRange(mesh->totloop)) {
+ copy_v2_v2(dst_uvs[i].uv, src_varray[i]);
+ }
+ mesh_component.attribute_try_delete(name);
+ CustomData_add_layer_named(
+ &mesh->ldata, CD_MLOOPUV, CD_ASSIGN, dst_uvs, mesh->totloop, name.c_str());
+ break;
+ }
+ case ConvertAttributeMode::VertexColor: {
+ MLoopCol *dst_colors = static_cast<MLoopCol *>(
+ MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopCol), __func__));
+ VArray<ColorGeometry4f> src_varray = mesh_component.attribute_get_for_read<ColorGeometry4f>(
+ name, ATTR_DOMAIN_CORNER, ColorGeometry4f{0.0f, 0.0f, 0.0f, 1.0f});
+ for (const int i : IndexRange(mesh->totloop)) {
+ ColorGeometry4b encoded_color = src_varray[i].encode();
+ copy_v4_v4_uchar(&dst_colors[i].r, &encoded_color.r);
+ }
+ mesh_component.attribute_try_delete(name);
+ CustomData_add_layer_named(
+ &mesh->ldata, CD_MLOOPCOL, CD_ASSIGN, dst_colors, mesh->totloop, name.c_str());
+ break;
+ }
+ case ConvertAttributeMode::VertexGroup: {
+ Array<float> src_weights(mesh->totvert);
+ VArray<float> src_varray = mesh_component.attribute_get_for_read<float>(
+ name, ATTR_DOMAIN_POINT, 0.0f);
+ src_varray.materialize(src_weights);
+ mesh_component.attribute_try_delete(name);
+
+ bDeformGroup *defgroup = BKE_object_defgroup_new(ob, name.c_str());
+ const int defgroup_index = BLI_findindex(BKE_id_defgroup_list_get(&mesh->id), defgroup);
+ MDeformVert *dverts = BKE_object_defgroup_data_create(&mesh->id);
+ for (const int i : IndexRange(mesh->totvert)) {
+ const float weight = src_weights[i];
+ if (weight > 0.0f) {
+ BKE_defvert_add_index_notest(dverts + i, defgroup_index, weight);
+ }
+ }
+ break;
+ }
+ }
+
+ int *active_index = BKE_id_attributes_active_index_p(&mesh->id);
+ if (*active_index > 0) {
+ *active_index -= 1;
+ }
+
+ DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &mesh->id);
+
+ return OPERATOR_FINISHED;
+}
+
+static void geometry_attribute_convert_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ uiItemR(layout, op->ptr, "mode", 0, nullptr, ICON_NONE);
+
+ const ConvertAttributeMode mode = static_cast<ConvertAttributeMode>(
+ RNA_enum_get(op->ptr, "mode"));
+
+ if (mode == ConvertAttributeMode::Generic) {
+ uiItemR(layout, op->ptr, "domain", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "data_type", 0, nullptr, ICON_NONE);
+ }
+}
+
+static int geometry_attribute_convert_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ return WM_operator_props_dialog_popup(C, op, 300);
+}
+
+void GEOMETRY_OT_attribute_convert(wmOperatorType *ot)
+{
+ ot->name = "Convert Attribute";
+ ot->description = "Change how the attribute is stored";
+ ot->idname = "GEOMETRY_OT_attribute_convert";
+
+ ot->invoke = geometry_attribute_convert_invoke;
+ ot->exec = geometry_attribute_convert_exec;
+ ot->poll = geometry_attribute_convert_poll;
+ ot->ui = geometry_attribute_convert_ui;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ static EnumPropertyItem mode_items[] = {
+ {int(ConvertAttributeMode::Generic), "GENERIC", 0, "Generic", ""},
+ {int(ConvertAttributeMode::UVMap), "UV_MAP", 0, "UV Map", ""},
+ {int(ConvertAttributeMode::VertexGroup), "VERTEX_GROUP", 0, "Vertex Group", ""},
+ {int(ConvertAttributeMode::VertexColor), "VERTEX_COLOR", 0, "Vertex Color", ""},
+ {0, nullptr, 0, nullptr, nullptr},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_enum(
+ ot->srna, "mode", mode_items, static_cast<int>(ConvertAttributeMode::Generic), "Mode", "");
+
+ prop = RNA_def_enum(ot->srna,
+ "domain",
+ rna_enum_attribute_domain_items,
+ ATTR_DOMAIN_POINT,
+ "Domain",
+ "Which geometry element to move the attribute to");
+ RNA_def_enum_funcs(prop, geometry_attribute_domain_itemf);
+
+ RNA_def_enum(
+ ot->srna, "data_type", rna_enum_attribute_type_items, CD_PROP_FLOAT, "Data Type", "");
+}
+
+} // namespace blender::ed::geometry
diff --git a/source/blender/editors/geometry/geometry_intern.hh b/source/blender/editors/geometry/geometry_intern.hh
index 14992476edd..30a2a1d6eb1 100644
--- a/source/blender/editors/geometry/geometry_intern.hh
+++ b/source/blender/editors/geometry/geometry_intern.hh
@@ -25,6 +25,11 @@
struct wmOperatorType;
+namespace blender::ed::geometry {
+
/* *** geometry_attributes.cc *** */
void GEOMETRY_OT_attribute_add(struct wmOperatorType *ot);
void GEOMETRY_OT_attribute_remove(struct wmOperatorType *ot);
+void GEOMETRY_OT_attribute_convert(struct wmOperatorType *ot);
+
+} // namespace blender::ed::geometry
diff --git a/source/blender/editors/geometry/geometry_ops.cc b/source/blender/editors/geometry/geometry_ops.cc
index ba8afe0e62f..8933b2a7f00 100644
--- a/source/blender/editors/geometry/geometry_ops.cc
+++ b/source/blender/editors/geometry/geometry_ops.cc
@@ -31,6 +31,9 @@
void ED_operatortypes_geometry(void)
{
+ using namespace blender::ed::geometry;
+
WM_operatortype_append(GEOMETRY_OT_attribute_add);
WM_operatortype_append(GEOMETRY_OT_attribute_remove);
+ WM_operatortype_append(GEOMETRY_OT_attribute_convert);
}
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
index aed58e31798..3871c1de77a 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
@@ -91,7 +91,7 @@ static void gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[3], f
static void gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[3], float margin[3])
{
- const float handle_size = 0.15f;
+ const float handle_size = 9.0f;
/* XXX, the scale isn't taking offset into account, we need to calculate scale per handle! */
// handle_size *= gz->scale_final;
@@ -151,7 +151,9 @@ static void cage3d_draw_box_corners(const float r[3],
immUnbindProgram();
}
-static void cage3d_draw_box_interaction(const float color[4],
+static void cage3d_draw_box_interaction(const RegionView3D *rv3d,
+ const float matrix_final[4][4],
+ const float color[4],
const int highlighted,
const float size[3],
const float margin[3])
@@ -173,13 +175,17 @@ static void cage3d_draw_box_interaction(const float color[4],
co[i] = size[i] * sign[range[i]];
}
const float rad[3] = {margin[0] / 3, margin[1] / 3, margin[2] / 3};
+ float co_test[3];
+ mul_v3_m4v3(co_test, matrix_final, co);
+ float rad_scale[3];
+ mul_v3_v3fl(rad_scale, rad, ED_view3d_pixel_size(rv3d, co_test));
{
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fv(color);
- imm_draw_cube_fill_3d(pos, co, rad);
+ imm_draw_cube_fill_3d(pos, co, rad_scale);
immUnbindProgram();
}
}
@@ -249,7 +255,7 @@ static void cage3d_draw_circle_handles(const RegionView3D *rv3d,
const float margin[3],
const float color[3],
bool solid,
- float scale)
+ const float handle_scale)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
const float rad[3] = {margin[0] / 3, margin[1] / 3, margin[2] / 3};
@@ -268,7 +274,7 @@ static void cage3d_draw_circle_handles(const RegionView3D *rv3d,
float co_test[3];
mul_v3_m4v3(co_test, matrix_final, co);
float rad_scale[3];
- mul_v3_v3fl(rad_scale, rad, ED_view3d_pixel_size(rv3d, co_test) * scale);
+ mul_v3_v3fl(rad_scale, rad, ED_view3d_pixel_size(rv3d, co_test) * handle_scale);
imm_draw_point_aspect_3d(pos, co, rad_scale, solid);
}
}
@@ -334,13 +340,13 @@ static void gizmo_cage3d_draw_intern(
continue;
}
GPU_select_load_id(select_id | i);
- cage3d_draw_box_interaction(gz->color, i, size, margin);
+ cage3d_draw_box_interaction(rv3d, matrix_final, gz->color, i, size, margin);
}
}
if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) {
const int transform_part = ED_GIZMO_CAGE3D_PART_TRANSLATE;
GPU_select_load_id(select_id | transform_part);
- cage3d_draw_box_interaction(gz->color, transform_part, size, margin);
+ cage3d_draw_box_interaction(rv3d, matrix_final, gz->color, transform_part, size, margin);
}
}
else {
@@ -375,7 +381,8 @@ static void gizmo_cage3d_draw_intern(
}
if (show) {
- cage3d_draw_box_interaction(gz->color, gz->highlight_part, size_real, margin);
+ cage3d_draw_box_interaction(
+ rv3d, matrix_final, gz->color, gz->highlight_part, size_real, margin);
}
}
else if (draw_style == ED_GIZMO_CAGE2D_STYLE_CIRCLE) {
@@ -389,10 +396,10 @@ static void gizmo_cage3d_draw_intern(
cage3d_draw_circle_wire(
size_real, margin, color, transform_flag, draw_options, gz->line_width);
- /* corner gizmos */
+ /* Corner gizmos (draw the outer & inner so there is a visible outline). */
GPU_polygon_smooth(true);
- cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, black, true, 60);
- cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, color, true, 40);
+ cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, black, true, 1.0f);
+ cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, color, true, 1.0f / 1.5f);
GPU_polygon_smooth(false);
GPU_blend(GPU_BLEND_NONE);
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index bff7310e9f7..93d17598181 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -86,9 +86,5 @@ if(WITH_POTRACE)
add_definitions(-DWITH_POTRACE)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_gpencil "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index bf414851aed..8e7a0083ded 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender
- * Operator for converting Grease Pencil data to geometry
*/
/** \file
* \ingroup edgpencil
+ * Operator for converting Grease Pencil data to geometry.
*/
#include <math.h>
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index e71a56894d0..dda36cf78d9 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender
- * Operators for editing Grease Pencil strokes
*/
/** \file
* \ingroup edgpencil
+ * Operators for editing Grease Pencil strokes.
*/
#include <math.h>
@@ -4626,6 +4626,31 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Not implemented!");
}
else {
+ /* Check if all points are selected. */
+ bool all_points_selected = true;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ all_points_selected = false;
+ break;
+ }
+ }
+
+ /* Separate the entire stroke. */
+ if (all_points_selected) {
+ /* deselect old stroke */
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ /* unlink from source frame */
+ BLI_remlink(&gpf->strokes, gps);
+ gps->prev = gps->next = NULL;
+ /* relink to destination frame */
+ BLI_addtail(&gpf_dst->strokes, gps);
+ /* Reassign material. */
+ gps->mat_nr = idx;
+
+ continue;
+ }
+
/* make copy of source stroke */
bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
diff --git a/source/blender/editors/gpencil/gpencil_edit_curve.c b/source/blender/editors/gpencil/gpencil_edit_curve.c
index 2d7497357f2..5002623fbb1 100644
--- a/source/blender/editors/gpencil/gpencil_edit_curve.c
+++ b/source/blender/editors/gpencil/gpencil_edit_curve.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender
- * Operators for editing Grease Pencil strokes
*/
/** \file
* \ingroup edgpencil
+ * Operators for editing Grease Pencil strokes.
*/
#include <math.h>
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 2023ae5fe27..2d88461fb15 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2016, Blender Foundation
* This is a new part of Blender
- * Operators for interpolating new Grease Pencil frames from existing strokes
*/
/** \file
* \ingroup edgpencil
+ * Operators for interpolating new Grease Pencil frames from existing strokes.
*/
#include <math.h>
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 925c2e1cd7f..884167d7496 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2019, Blender Foundation.
* This is a new part of Blender
- * Operators for merge Grease Pencil strokes
*/
/** \file
* \ingroup edgpencil
+ * Operators for merge Grease Pencil strokes.
*/
#include <stdio.h>
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
index efe29e852f2..adf76e79c2a 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.c
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender
- * Operator for converting Grease Pencil data to geometry
*/
/** \file
* \ingroup edgpencil
+ * Operator for converting Grease Pencil data to geometry.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c
index 45842c28dff..d9706c281ca 100644
--- a/source/blender/editors/gpencil/gpencil_ops_versioning.c
+++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2018, Blender Foundation,
* This is a new part of Blender
- * Use deprecated data to convert old 2.7x files
*/
/** \file
* \ingroup edgpencil
+ * Use deprecated data to convert old 2.7x files.
*/
/* Allow using deprecated functionality. */
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 2715491414a..fa31dfffbc1 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2017, Blender Foundation
* This is a new part of Blender
- * Operators for creating new Grease Pencil primitives (boxes, circles, ...)
*/
/** \file
* \ingroup edgpencil
+ * Operators for creating new Grease Pencil primitives (boxes, circles, ...).
*/
#include <math.h>
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 50c7202599a..0cd4efb10d6 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2015, Blender Foundation
* This is a new part of Blender
- * Brush based operators for editing Grease Pencil strokes
*/
/** \file
* \ingroup edgpencil
+ * Brush based operators for editing Grease Pencil strokes.
*/
#include <math.h>
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
index 891bd3ca5ec..d9f4cc87afc 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_ops.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2015, Blender Foundation
* This is a new part of Blender
- * Brush based operators for editing Grease Pencil strokes
*/
/** \file
* \ingroup edgpencil
+ * Brush based operators for editing Grease Pencil strokes.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index 05304f9914f..2c16894354e 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2015, Blender Foundation
* This is a new part of Blender
- * Brush based operators for editing Grease Pencil strokes
*/
/** \file
* \ingroup edgpencil
+ * Brush based operators for editing Grease Pencil strokes.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c
index 15f99d83f6d..fe4ab648581 100644
--- a/source/blender/editors/gpencil/gpencil_weight_paint.c
+++ b/source/blender/editors/gpencil/gpencil_weight_paint.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2015, Blender Foundation
* This is a new part of Blender
- * Brush based operators for editing Grease Pencil strokes
*/
/** \file
* \ingroup edgpencil
+ * Brush based operators for editing Grease Pencil strokes.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 3294316f880..04a892ab411 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -378,7 +378,7 @@ typedef enum eAnimFilter_Flags {
#define FILTER_MESH_OBJD(me) (CHECK_TYPE_INLINE(me, Mesh *), ((me->flag & ME_DS_EXPAND)))
#define FILTER_LATTICE_OBJD(lt) (CHECK_TYPE_INLINE(lt, Lattice *), ((lt->flag & LT_DS_EXPAND)))
#define FILTER_SPK_OBJD(spk) (CHECK_TYPE_INLINE(spk, Speaker *), ((spk->flag & SPK_DS_EXPAND)))
-#define FILTER_HAIR_OBJD(ha) (CHECK_TYPE_INLINE(ha, Hair *), ((ha->flag & HA_DS_EXPAND)))
+#define FILTER_CURVES_OBJD(ha) (CHECK_TYPE_INLINE(ha, Curves *), ((ha->flag & HA_DS_EXPAND)))
#define FILTER_POINTS_OBJD(pt) (CHECK_TYPE_INLINE(pt, PointCloud *), ((pt->flag & PT_DS_EXPAND)))
#define FILTER_VOLUME_OBJD(vo) (CHECK_TYPE_INLINE(vo, Volume *), ((vo->flag & VO_DS_EXPAND)))
#define FILTER_SIMULATION_OBJD(sim) \
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 7631bd35e79..0521b39ca1a 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -35,6 +35,7 @@ struct Base;
struct Bone;
struct Depsgraph;
struct EditBone;
+struct GPUSelectResult;
struct ListBase;
struct Main;
struct Mesh;
@@ -157,22 +158,22 @@ int ED_armature_join_objects_exec(struct bContext *C, struct wmOperator *op);
struct Base *ED_armature_base_and_ebone_from_select_buffer(struct Base **bases,
uint bases_len,
- int hit,
+ unsigned int select_id,
struct EditBone **r_ebone);
struct Object *ED_armature_object_and_ebone_from_select_buffer(struct Object **objects,
uint objects_len,
- int hit,
+ unsigned int select_id,
struct EditBone **r_ebone);
struct Base *ED_armature_base_and_pchan_from_select_buffer(struct Base **bases,
uint bases_len,
- int hit,
+ unsigned int select_id,
struct bPoseChannel **r_pchan);
/**
* For callers that don't need the pose channel.
*/
struct Base *ED_armature_base_and_bone_from_select_buffer(struct Base **bases,
uint bases_len,
- int hit,
+ unsigned int select_id,
struct Bone **r_bone);
bool ED_armature_edit_deselect_all(struct Object *obedit);
bool ED_armature_edit_deselect_all_visible(struct Object *obedit);
@@ -334,7 +335,7 @@ void ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer,
bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer,
struct View3D *v3d,
struct Base *base,
- const unsigned int *buffer,
+ const struct GPUSelectResult *buffer,
short hits,
bool extend,
bool deselect,
@@ -368,7 +369,8 @@ void ED_pose_bone_select_tag_update(struct Object *ob);
void ED_pose_bone_select(struct Object *ob, struct bPoseChannel *pchan, bool select);
/* meshlaplacian.c */
-void ED_mesh_deform_bind_callback(struct MeshDeformModifierData *mmd,
+void ED_mesh_deform_bind_callback(struct Object *object,
+ struct MeshDeformModifierData *mmd,
struct Mesh *cagemesh,
float *vertexcos,
int totvert,
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index c5e8c2ee096..3c1b8e4ecc1 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -38,10 +38,10 @@ struct Main;
struct ReportList;
struct Scene;
struct SpaceImage;
+struct View2D;
struct bContext;
struct wmOperator;
struct wmWindowManager;
-struct View2D;
/* image_draw.c */
float ED_space_image_zoom_level(const struct View2D *v2d, int grid_dimension);
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index c7e89030ee2..f006378658b 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -93,6 +93,13 @@ typedef enum eEditKeyframes_Snap {
SNAP_KEYS_TIME,
} eEditKeyframes_Snap;
+/* equalizing tools */
+typedef enum eEditKeyframes_Equalize {
+ EQUALIZE_HANDLES_LEFT = (1 << 0),
+ EQUALIZE_HANDLES_RIGHT = (1 << 1),
+ EQUALIZE_HANDLES_BOTH = (EQUALIZE_HANDLES_LEFT | EQUALIZE_HANDLES_RIGHT),
+} eEditKeyframes_Equalize;
+
/* mirroring tools */
typedef enum eEditKeyframes_Mirror {
MIRROR_KEYS_CURFRAME = 1,
@@ -259,6 +266,18 @@ short ANIM_fcurve_keyframes_loop(KeyframeEditData *ked,
KeyframeEditFunc key_cb,
FcuEditFunc fcu_cb);
/**
+ * Sets selected keyframes' bezier handles to an equal length and optionally makes
+ * the keyframes' handles horizontal.
+ * \param handle_length: Desired handle length, must be positive.
+ * \param flatten: Makes the keyframes' handles the same value as the keyframe,
+ * flattening the curve at that point.
+ */
+void ANIM_fcurve_equalize_keyframes_loop(struct FCurve *fcu,
+ eEditKeyframes_Equalize mode,
+ float handle_length,
+ bool flatten);
+
+/**
* Function for working with any type (i.e. one of the known types) of animation channel.
*/
short ANIM_animchannel_keyframes_loop(KeyframeEditData *ked,
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 8d89555c732..22a6bdb27ba 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -14,7 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2008, Blender Foundation
- * This is a new part of Blender (with some old code)
*/
/** \file
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index c6f02cb3bcf..0721aa21a16 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -61,7 +61,7 @@ struct wmOperator;
/**
* \param em: Edit-mesh used for generating mirror data.
- * \param use_self: Allow a vertex to point to its self (middle verts).
+ * \param use_self: Allow a vertex to point to itself (middle verts).
* \param use_select: Restrict to selected verts.
* \param respecthide: Skip hidden vertices.
* \param use_topology: Use topology mirror.
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h
index 181b6848ac7..08f4648d02b 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -57,7 +57,7 @@ typedef enum {
#define NODE_EDGE_PAN_DELAY 0.5f
#define NODE_EDGE_PAN_ZOOM_INFLUENCE 0.5f
-/* space_node.c */
+/* space_node.cc */
void ED_node_cursor_location_get(const struct SpaceNode *snode, float value[2]);
void ED_node_cursor_location_set(struct SpaceNode *snode, const float value[2]);
@@ -76,7 +76,7 @@ struct bNodeTree *ED_node_tree_get(struct SpaceNode *snode, int level);
void ED_node_set_active_viewer_key(struct SpaceNode *snode);
-/* drawnode.c */
+/* drawnode.cc */
void ED_node_init_butfuncs(void);
void ED_init_custom_node_type(struct bNodeType *ntype);
@@ -103,7 +103,7 @@ void ED_node_tag_update_id(struct ID *id);
float ED_node_grid_size(void);
-/* node_relationships.c */
+/* node_relationships.cc */
/**
* Test == 0, clear all intersect flags.
@@ -114,7 +114,7 @@ void ED_node_link_intersect_test(struct ScrArea *area, int test);
*/
void ED_node_link_insert(struct Main *bmain, struct ScrArea *area);
-/* node_edit.c */
+/* node_edit.cc */
void ED_node_set_tree_type(struct SpaceNode *snode, struct bNodeTreeType *typeinfo);
bool ED_node_is_compositor(struct SpaceNode *snode);
@@ -175,11 +175,12 @@ void ED_node_composite_job(const struct bContext *C,
struct bNodeTree *nodetree,
struct Scene *scene_owner);
-/* node_ops.c */
+/* node_ops.cc */
void ED_operatormacros_node(void);
-/* node_view.c */
+/* node_view.cc */
+
/**
* Returns mouse position in image space.
*/
diff --git a/source/blender/editors/include/ED_transverts.h b/source/blender/editors/include/ED_transverts.h
index cbcf28d53d5..dbf156d480f 100644
--- a/source/blender/editors/include/ED_transverts.h
+++ b/source/blender/editors/include/ED_transverts.h
@@ -28,6 +28,7 @@ extern "C" {
#endif
struct Object;
+struct bContext;
typedef struct TransVert {
float *loc;
@@ -42,10 +43,14 @@ typedef struct TransVertStore {
int mode;
} TransVertStore;
-void ED_transverts_create_from_obedit(TransVertStore *tvs, struct Object *obedit, int mode);
+/**
+ * \param obedit: When `mode` has the #TM_CALC_MAPLOC flag set, `obedit` must be evaluated,
+ * to access evaluated vertices.
+ */
+void ED_transverts_create_from_obedit(TransVertStore *tvs, const struct Object *obedit, int mode);
void ED_transverts_update_obedit(TransVertStore *tvs, struct Object *obedit);
void ED_transverts_free(TransVertStore *tvs);
-bool ED_transverts_check_obedit(Object *obedit);
+bool ED_transverts_check_obedit(const struct Object *obedit);
bool ED_transverts_poll(struct bContext *C);
/* currently only used for bmesh index values */
@@ -66,12 +71,16 @@ enum {
TM_SKIP_HANDLES = (1 << 1),
/** fill in normals when available */
TM_CALC_NORMALS = (1 << 2),
+ /** Calculates #TransVert.maploc where possible. */
+ TM_CALC_MAPLOC = (1 << 2),
};
enum {
/* SELECT == (1 << 0) */
+ /** Calculated when #TM_CALC_MAPLOC is set. */
TX_VERT_USE_MAPLOC = (1 << 1),
- TX_VERT_USE_NORMAL = (1 << 2), /* avoid nonzero check */
+ /** Calculated when #TM_CALC_NORMALS is set, avoid nonzero check. */
+ TX_VERT_USE_NORMAL = (1 << 2),
};
#ifdef __cplusplus
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index 6bcddfa631a..6d9691d96f4 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -33,6 +33,7 @@ extern "C" {
struct GPUBatch;
struct Main;
struct bContext;
+struct IDRemapper;
/* ed_util.c */
@@ -60,10 +61,13 @@ bool ED_editors_flush_edits(struct Main *bmain);
*
* \param new_id: may be NULL to unlink \a old_id.
*/
+void ED_spacedata_id_remap_single(struct ScrArea *area,
+ struct SpaceLink *sl,
+ struct ID *old_id,
+ struct ID *new_id);
void ED_spacedata_id_remap(struct ScrArea *area,
struct SpaceLink *sl,
- struct ID *old_id,
- struct ID *new_id);
+ const struct IDRemapper *mappings);
void ED_operatortypes_edutils(void);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 0398c209c68..3bbffc3b7c9 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -41,6 +41,7 @@ struct Camera;
struct CustomData_MeshMasks;
struct Depsgraph;
struct EditBone;
+struct GPUSelectResult;
struct ID;
struct MVert;
struct Main;
@@ -871,9 +872,14 @@ bool ED_view3d_autodist_simple(struct ARegion *region,
bool ED_view3d_depth_read_cached_seg(
const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth);
-/* select */
+/**
+ * The default value for the maximum number of elements that can be selected at once
+ * using view-port selection.
+ *
+ * \note in many cases this defines the size of fixed-size stack buffers,
+ * so take care increasing this value.
+ */
#define MAXPICKELEMS 2500
-#define MAXPICKBUF (4 * MAXPICKELEMS)
typedef enum {
/* all elements in the region, ignore depth */
@@ -912,21 +918,21 @@ void view3d_opengl_select_cache_end(void);
* \note (vc->obedit == NULL) can be set to explicitly skip edit-object selection.
*/
int view3d_opengl_select_ex(struct ViewContext *vc,
- unsigned int *buffer,
- unsigned int bufsize,
+ struct GPUSelectResult *buffer,
+ unsigned int buffer_len,
const struct rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter,
bool do_material_slot_selection);
int view3d_opengl_select(struct ViewContext *vc,
- unsigned int *buffer,
- unsigned int bufsize,
+ struct GPUSelectResult *buffer,
+ unsigned int buffer_len,
const struct rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter);
int view3d_opengl_select_with_id_filter(struct ViewContext *vc,
- unsigned int *buffer,
- unsigned int bufsize,
+ struct GPUSelectResult *buffer,
+ unsigned int buffer_len,
const struct rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter,
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index 4cf606bf98d..05353de2f92 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -752,9 +752,9 @@ DEF_ICON_BLANK(257)
DEF_ICON_BLANK(257b)
/* ADDITIONAL OBJECT TYPES */
-DEF_ICON_OBJECT(OUTLINER_OB_HAIR)
-DEF_ICON_OBJECT_DATA(OUTLINER_DATA_HAIR)
-DEF_ICON_OBJECT_DATA(HAIR_DATA)
+DEF_ICON_OBJECT(OUTLINER_OB_CURVES)
+DEF_ICON_OBJECT_DATA(OUTLINER_DATA_CURVES)
+DEF_ICON_OBJECT_DATA(CURVES_DATA)
DEF_ICON_OBJECT(OUTLINER_OB_POINTCLOUD)
DEF_ICON_OBJECT_DATA(OUTLINER_DATA_POINTCLOUD)
DEF_ICON_OBJECT_DATA(POINTCLOUD_DATA)
@@ -840,7 +840,7 @@ DEF_ICON(MATPLANE)
DEF_ICON(MATSPHERE)
DEF_ICON(MATCUBE)
DEF_ICON(MONKEY)
-DEF_ICON(HAIR)
+DEF_ICON(CURVES)
DEF_ICON(ALIASED)
DEF_ICON(ANTIALIASED)
DEF_ICON(MAT_SPHERE_SKY)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 8b972a267bc..2118a2e6b85 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -1370,6 +1370,8 @@ uiBut *uiDefIconTextButO_ptr(uiBlock *block,
/* for passing inputs to ButO buttons */
struct PointerRNA *UI_but_operator_ptr_get(uiBut *but);
+struct bContextStore *UI_but_context_get(const uiBut *but);
+
void UI_but_unit_type_set(uiBut *but, int unit_type);
int UI_but_unit_type_get(const uiBut *but);
diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh
index a52b4ebd338..8d1ca54b7a1 100644
--- a/source/blender/editors/include/UI_interface.hh
+++ b/source/blender/editors/include/UI_interface.hh
@@ -31,8 +31,8 @@ namespace blender::nodes::geometry_nodes_eval_log {
struct GeometryAttributeInfo;
}
-struct uiBlock;
struct StructRNA;
+struct uiBlock;
struct uiSearchItems;
namespace blender::ui {
diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h
index 6ffeb4134ae..ec80338b4c0 100644
--- a/source/blender/editors/include/UI_interface_icons.h
+++ b/source/blender/editors/include/UI_interface_icons.h
@@ -82,6 +82,8 @@ int UI_icon_get_height(int icon_id);
bool UI_icon_get_theme_color(int icon_id, unsigned char color[4]);
/**
+ * Render a #PreviewImage for the data block.
+ *
* Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored.
*/
void UI_icon_render_id(const struct bContext *C,
@@ -89,6 +91,17 @@ void UI_icon_render_id(const struct bContext *C,
struct ID *id,
enum eIconSizes size,
bool use_job);
+
+/**
+ * Render the data block into the provided #PreviewImage.
+ */
+void UI_icon_render_id_ex(const struct bContext *C,
+ struct Scene *scene,
+ struct ID *id_to_render,
+ const enum eIconSizes size,
+ const bool use_job,
+ struct PreviewImage *r_preview_image);
+
/**
* Render size for preview images and icons
*/
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
index 8208f8daf5d..8dee88defb3 100644
--- a/source/blender/editors/include/UI_tree_view.hh
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -40,8 +40,8 @@ struct uiBlock;
struct uiBut;
struct uiButTreeRow;
struct uiLayout;
-struct wmEvent;
struct wmDrag;
+struct wmEvent;
namespace blender::ui {
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index a3f39e1286e..0a28b1cafe1 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -15,12 +15,12 @@
*
* The Original Code is Copyright (C) 2008 Blender Foundation.
* All rights reserved.
- * Generic 2d view with should allow drawing grids,
- * panning, zooming, scrolling, ..
*/
/** \file
* \ingroup editorui
+ * Generic 2D view with should allow drawing grids,
+ * panning, zooming, scrolling, .. etc.
*/
#pragma once
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 3c701cc403b..2292bf759b7 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -4900,7 +4900,7 @@ int UI_autocomplete_end(AutoComplete *autocpl, char *autoname)
BLI_strncpy(autoname, autocpl->truncate, autocpl->maxlen);
}
else {
- if (autoname != autocpl->startname) { /* don't copy a string over its self */
+ if (autoname != autocpl->startname) { /* don't copy a string over itself */
BLI_strncpy(autoname, autocpl->startname, autocpl->maxlen);
}
}
@@ -5928,6 +5928,11 @@ PointerRNA *UI_but_operator_ptr_get(uiBut *but)
return but->opptr;
}
+bContextStore *UI_but_context_get(const uiBut *but)
+{
+ return but->context;
+}
+
void UI_but_unit_type_set(uiBut *but, const int unit_type)
{
but->unit_type = (uchar)(RNA_SUBTYPE_UNIT_VALUE(unit_type));
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 190b2d12ed9..dd5ce118d5f 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -809,12 +809,18 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
else {
if (is_array_component) {
ot = WM_operatortype_find("UI_OT_override_type_set_button", false);
- uiItemFullO_ptr(
- layout, ot, "Define Overrides", ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
+ uiItemFullO_ptr(layout,
+ ot,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Overrides"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
RNA_boolean_set(&op_ptr, "all", true);
uiItemFullO_ptr(layout,
ot,
- "Define Single Override",
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Single Override"),
ICON_NONE,
NULL,
WM_OP_INVOKE_DEFAULT,
@@ -825,7 +831,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
else {
uiItemFullO(layout,
"UI_OT_override_type_set_button",
- "Define Override",
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Override"),
ICON_NONE,
NULL,
WM_OP_INVOKE_DEFAULT,
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index fd03cc5e12c..6fa94730365 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -25,6 +25,7 @@
#include "DNA_space_types.h"
#include "BLI_math_color.h"
+#include "BLI_math_vector.h"
#include "BKE_context.h"
#include "BKE_screen.h"
@@ -107,7 +108,7 @@ wmKeyMap *eyedropper_colorband_modal_keymap(wmKeyConfig *keyconf)
/** \name Generic Shared Functions
* \{ */
-static void eyedropper_draw_cursor_text_ex(const int x, const int y, const char *name)
+static void eyedropper_draw_cursor_text_ex(const int xy[2], const char *name)
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
@@ -119,7 +120,7 @@ static void eyedropper_draw_cursor_text_ex(const int x, const int y, const char
rgba_uchar_to_float(col_fg, wcol->text);
rgba_uchar_to_float(col_bg, wcol->inner);
- UI_fontstyle_draw_simple_backdrop(fstyle, x, y + U.widget_unit, name, col_fg, col_bg);
+ UI_fontstyle_draw_simple_backdrop(fstyle, xy[0], xy[1] + U.widget_unit, name, col_fg, col_bg);
}
void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name)
@@ -128,19 +129,16 @@ void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const cha
return;
}
- const int x = window->eventstate->xy[0];
- const int y = window->eventstate->xy[1];
-
- eyedropper_draw_cursor_text_ex(x, y, name);
+ eyedropper_draw_cursor_text_ex(window->eventstate->xy, name);
}
-void eyedropper_draw_cursor_text_region(const int x, const int y, const char *name)
+void eyedropper_draw_cursor_text_region(const int xy[2], const char *name)
{
if (name[0] == '\0') {
return;
}
- eyedropper_draw_cursor_text_ex(x, y, name);
+ eyedropper_draw_cursor_text_ex(xy, name);
}
uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event)
@@ -173,8 +171,7 @@ void datadropper_win_area_find(
}
}
else if (mval != r_mval) {
- r_mval[0] = mval[0];
- r_mval[1] = mval[1];
+ copy_v2_v2_int(r_mval, mval);
}
}
diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c
index 52730096b2f..b5eed2534a3 100644
--- a/source/blender/editors/interface/interface_eyedropper_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_color.c
@@ -42,6 +42,8 @@
#include "BKE_node.h"
#include "BKE_screen.h"
+#include "NOD_composite.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
@@ -252,8 +254,10 @@ static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node,
return success;
}
-static bool eyedropper_cryptomatte_sample_fl(
- bContext *C, Eyedropper *eye, int mx, int my, float r_col[3])
+static bool eyedropper_cryptomatte_sample_fl(bContext *C,
+ Eyedropper *eye,
+ const int m_xy[2],
+ float r_col[3])
{
bNode *node = eye->crypto_node;
NodeCryptomatte *crypto = node ? ((NodeCryptomatte *)node->storage) : NULL;
@@ -263,17 +267,17 @@ static bool eyedropper_cryptomatte_sample_fl(
}
bScreen *screen = CTX_wm_screen(C);
- ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, (const int[2]){mx, my});
+ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy);
if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP)) {
return false;
}
- ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my});
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy);
if (!region) {
return false;
}
- int mval[2] = {mx - region->winrct.xmin, my - region->winrct.ymin};
+ int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin};
float fpos[2] = {-1.0f, -1.0};
switch (area->spacetype) {
case SPACE_IMAGE: {
@@ -322,7 +326,7 @@ static bool eyedropper_cryptomatte_sample_fl(
return false;
}
-void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3])
+void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3])
{
/* we could use some clever */
Main *bmain = CTX_data_main(C);
@@ -330,10 +334,10 @@ void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3])
const char *display_device = CTX_data_scene(C)->display_settings.display_device;
struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
+ int mval[2];
wmWindow *win;
ScrArea *area;
- int mval[2] = {mx, my};
- datadropper_win_area_find(C, mval, mval, &win, &area);
+ datadropper_win_area_find(C, m_xy, mval, &win, &area);
if (area) {
if (area->spacetype == SPACE_IMAGE) {
@@ -404,17 +408,17 @@ static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3
RNA_property_update(C, &eye->ptr, eye->prop);
}
-static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
+static void eyedropper_color_sample(bContext *C, Eyedropper *eye, const int m_xy[2])
{
/* Accumulate color. */
float col[3];
if (eye->crypto_node) {
- if (!eyedropper_cryptomatte_sample_fl(C, eye, mx, my, col)) {
+ if (!eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) {
return;
}
}
else {
- eyedropper_color_sample_fl(C, mx, my, col);
+ eyedropper_color_sample_fl(C, m_xy, col);
}
if (!eye->crypto_node) {
@@ -437,13 +441,13 @@ static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my
eyedropper_color_set(C, eye, accum_col);
}
-static void eyedropper_color_sample_text_update(bContext *C, Eyedropper *eye, int mx, int my)
+static void eyedropper_color_sample_text_update(bContext *C, Eyedropper *eye, const int m_xy[2])
{
float col[3];
eye->sample_text[0] = '\0';
if (eye->cryptomatte_session) {
- if (eyedropper_cryptomatte_sample_fl(C, eye, mx, my, col)) {
+ if (eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) {
BKE_cryptomatte_find_name(
eye->cryptomatte_session, col[0], eye->sample_text, sizeof(eye->sample_text));
eye->sample_text[sizeof(eye->sample_text) - 1] = '\0';
@@ -474,7 +478,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
case EYE_MODAL_SAMPLE_CONFIRM: {
const bool is_undo = eye->is_undo;
if (eye->accum_tot == 0) {
- eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_color_sample(C, eye, event->xy);
}
eyedropper_exit(C, op);
/* Could support finished & undo-skip. */
@@ -483,23 +487,23 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
case EYE_MODAL_SAMPLE_BEGIN:
/* enable accum and make first sample */
eye->accum_start = true;
- eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_color_sample(C, eye, event->xy);
break;
case EYE_MODAL_SAMPLE_RESET:
eye->accum_tot = 0;
zero_v3(eye->accum_col);
- eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_color_sample(C, eye, event->xy);
break;
}
}
else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (eye->accum_start) {
/* button is pressed so keep sampling */
- eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_color_sample(C, eye, event->xy);
}
if (eye->draw_handle_sample_text) {
- eyedropper_color_sample_text_update(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_color_sample_text_update(C, eye, event->xy);
ED_region_tag_redraw(CTX_wm_region(C));
}
}
diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c
index 22320282766..05ed4ecf601 100644
--- a/source/blender/editors/interface/interface_eyedropper_colorband.c
+++ b/source/blender/editors/interface/interface_eyedropper_colorband.c
@@ -58,7 +58,7 @@ typedef struct Colorband_RNAUpdateCb {
} Colorband_RNAUpdateCb;
typedef struct EyedropperColorband {
- int last_x, last_y;
+ int event_xy_last[2];
/* Alpha is currently fixed at 1.0, may support in future. */
float (*color_buffer)[4];
int color_buffer_alloc;
@@ -142,13 +142,12 @@ static bool eyedropper_colorband_init(bContext *C, wmOperator *op)
static void eyedropper_colorband_sample_point(bContext *C,
EyedropperColorband *eye,
- int mx,
- int my)
+ const int m_xy[2])
{
- if (eye->last_x != mx || eye->last_y != my) {
+ if (eye->event_xy_last[0] != m_xy[0] || eye->event_xy_last[1] != m_xy[1]) {
float col[4];
col[3] = 1.0f; /* TODO: sample alpha */
- eyedropper_color_sample_fl(C, mx, my, col);
+ eyedropper_color_sample_fl(C, m_xy, col);
if (eye->color_buffer_len + 1 == eye->color_buffer_alloc) {
eye->color_buffer_alloc *= 2;
eye->color_buffer = MEM_reallocN(eye->color_buffer,
@@ -156,8 +155,7 @@ static void eyedropper_colorband_sample_point(bContext *C,
}
copy_v4_v4(eye->color_buffer[eye->color_buffer_len], col);
eye->color_buffer_len += 1;
- eye->last_x = mx;
- eye->last_y = my;
+ copy_v2_v2_int(eye->event_xy_last, m_xy);
eye->is_set = true;
}
}
@@ -167,21 +165,20 @@ static bool eyedropper_colorband_sample_callback(int mx, int my, void *userdata)
struct EyedropperColorband_Context *data = userdata;
bContext *C = data->context;
EyedropperColorband *eye = data->eye;
- eyedropper_colorband_sample_point(C, eye, mx, my);
+ const int cursor[2] = {mx, my};
+ eyedropper_colorband_sample_point(C, eye, cursor);
return true;
}
static void eyedropper_colorband_sample_segment(bContext *C,
EyedropperColorband *eye,
- int mx,
- int my)
+ const int m_xy[2])
{
/* Since the mouse tends to move rather rapidly we use #BLI_bitmap_draw_2d_line_v2v2i
* to interpolate between the reported coordinates */
struct EyedropperColorband_Context userdata = {C, eye};
- const int p1[2] = {eye->last_x, eye->last_y};
- const int p2[2] = {mx, my};
- BLI_bitmap_draw_2d_line_v2v2i(p1, p2, eyedropper_colorband_sample_callback, &userdata);
+ BLI_bitmap_draw_2d_line_v2v2i(
+ eye->event_xy_last, m_xy, eyedropper_colorband_sample_callback, &userdata);
}
static void eyedropper_colorband_exit(bContext *C, wmOperator *op)
@@ -233,7 +230,7 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
case EYE_MODAL_SAMPLE_CONFIRM: {
const bool is_undo = eye->is_undo;
- eyedropper_colorband_sample_segment(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_colorband_sample_segment(C, eye, event->xy);
eyedropper_colorband_apply(C, op);
eyedropper_colorband_exit(C, op);
/* Could support finished & undo-skip. */
@@ -242,10 +239,9 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent
case EYE_MODAL_SAMPLE_BEGIN:
/* enable accum and make first sample */
eye->sample_start = true;
- eyedropper_colorband_sample_point(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_colorband_sample_point(C, eye, event->xy);
eyedropper_colorband_apply(C, op);
- eye->last_x = event->xy[0];
- eye->last_y = event->xy[1];
+ copy_v2_v2_int(eye->event_xy_last, event->xy);
break;
case EYE_MODAL_SAMPLE_RESET:
break;
@@ -253,7 +249,7 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent
}
else if (event->type == MOUSEMOVE) {
if (eye->sample_start) {
- eyedropper_colorband_sample_segment(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_colorband_sample_segment(C, eye, event->xy);
eyedropper_colorband_apply(C, op);
}
}
@@ -280,7 +276,7 @@ static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const w
}
break;
case EYE_MODAL_POINT_SAMPLE:
- eyedropper_colorband_sample_point(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_colorband_sample_point(C, eye, event->xy);
eyedropper_colorband_apply(C, op);
if (eye->color_buffer_len == MAXCOLORBAND) {
eyedropper_colorband_exit(C, op);
diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c
index cf53ef0ec75..812011101e8 100644
--- a/source/blender/editors/interface/interface_eyedropper_datablock.c
+++ b/source/blender/editors/interface/interface_eyedropper_datablock.c
@@ -32,6 +32,7 @@
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLT_translation.h"
@@ -80,7 +81,7 @@ static void datadropper_draw_cb(const struct bContext *UNUSED(C),
void *arg)
{
DataDropper *ddr = arg;
- eyedropper_draw_cursor_text_region(UNPACK2(ddr->name_pos), ddr->name);
+ eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name);
}
static int datadropper_init(bContext *C, wmOperator *op)
@@ -152,7 +153,7 @@ static void datadropper_exit(bContext *C, wmOperator *op)
* \brief get the ID from the 3D view or outliner.
*/
static void datadropper_id_sample_pt(
- bContext *C, wmWindow *win, ScrArea *area, DataDropper *ddr, int mx, int my, ID **r_id)
+ bContext *C, wmWindow *win, ScrArea *area, DataDropper *ddr, const int m_xy[2], ID **r_id)
{
wmWindow *win_prev = CTX_wm_window(C);
ScrArea *area_prev = CTX_wm_area(C);
@@ -162,9 +163,9 @@ static void datadropper_id_sample_pt(
if (area) {
if (ELEM(area->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) {
- ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my});
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy);
if (region) {
- const int mval[2] = {mx - region->winrct.xmin, my - region->winrct.ymin};
+ const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin};
Base *base;
CTX_wm_window_set(C, win);
@@ -205,8 +206,7 @@ static void datadropper_id_sample_pt(
*r_id = id;
}
- ddr->name_pos[0] = mval[0];
- ddr->name_pos[1] = mval[1];
+ copy_v2_v2_int(ddr->name_pos, mval);
}
}
}
@@ -234,17 +234,16 @@ static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id)
}
/* single point sample & set */
-static bool datadropper_id_sample(bContext *C, DataDropper *ddr, int mx, int my)
+static bool datadropper_id_sample(bContext *C, DataDropper *ddr, const int m_xy[2])
{
ID *id = NULL;
+ int mval[2];
wmWindow *win;
ScrArea *area;
+ datadropper_win_area_find(C, m_xy, mval, &win, &area);
- int mval[] = {mx, my};
- datadropper_win_area_find(C, mval, mval, &win, &area);
-
- datadropper_id_sample_pt(C, win, area, ddr, mval[0], mval[1], &id);
+ datadropper_id_sample_pt(C, win, area, ddr, mval, &id);
return datadropper_id_set(C, ddr, id);
}
@@ -292,7 +291,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
case EYE_MODAL_SAMPLE_CONFIRM: {
const bool is_undo = ddr->is_undo;
- const bool success = datadropper_id_sample(C, ddr, event->xy[0], event->xy[1]);
+ const bool success = datadropper_id_sample(C, ddr, event->xy);
datadropper_exit(C, op);
if (success) {
/* Could support finished & undo-skip. */
@@ -306,16 +305,15 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
else if (event->type == MOUSEMOVE) {
ID *id = NULL;
+ int mval[2];
wmWindow *win;
ScrArea *area;
-
- int mval[] = {event->xy[0], event->xy[1]};
- datadropper_win_area_find(C, mval, mval, &win, &area);
+ datadropper_win_area_find(C, event->xy, mval, &win, &area);
/* Set the region for eyedropper cursor text drawing */
datadropper_set_draw_callback_region(area, ddr);
- datadropper_id_sample_pt(C, win, area, ddr, mval[0], mval[1], &id);
+ datadropper_id_sample_pt(C, win, area, ddr, mval, &id);
}
return OPERATOR_RUNNING_MODAL;
diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c
index 8c6b0ac9cfe..b11001c4bf2 100644
--- a/source/blender/editors/interface/interface_eyedropper_depth.c
+++ b/source/blender/editors/interface/interface_eyedropper_depth.c
@@ -81,7 +81,7 @@ static void depthdropper_draw_cb(const struct bContext *UNUSED(C),
void *arg)
{
DepthDropper *ddr = arg;
- eyedropper_draw_cursor_text_region(UNPACK2(ddr->name_pos), ddr->name);
+ eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name);
}
static int depthdropper_init(bContext *C, wmOperator *op)
@@ -152,12 +152,14 @@ static void depthdropper_exit(bContext *C, wmOperator *op)
/**
* \brief get the ID from the screen.
*/
-static void depthdropper_depth_sample_pt(
- bContext *C, DepthDropper *ddr, int mx, int my, float *r_depth)
+static void depthdropper_depth_sample_pt(bContext *C,
+ DepthDropper *ddr,
+ const int m_xy[2],
+ float *r_depth)
{
/* we could use some clever */
bScreen *screen = CTX_wm_screen(C);
- ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, (const int[2]){mx, my});
+ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy);
Scene *scene = CTX_data_scene(C);
ScrArea *area_prev = CTX_wm_area(C);
@@ -167,14 +169,14 @@ static void depthdropper_depth_sample_pt(
if (area) {
if (area->spacetype == SPACE_VIEW3D) {
- ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my});
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy);
if (region) {
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
View3D *v3d = area->spacedata.first;
RegionView3D *rv3d = region->regiondata;
/* weak, we could pass in some reference point */
const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3];
- const int mval[2] = {mx - region->winrct.xmin, my - region->winrct.ymin};
+ const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin};
copy_v2_v2_int(ddr->name_pos, mval);
float co[3];
@@ -234,19 +236,19 @@ static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr)
}
/* single point sample & set */
-static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, int mx, int my)
+static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, const int m_xy[2])
{
float depth = -1.0f;
if (depth != -1.0f) {
- depthdropper_depth_sample_pt(C, ddr, mx, my, &depth);
+ depthdropper_depth_sample_pt(C, ddr, m_xy, &depth);
depthdropper_depth_set(C, ddr, depth);
}
}
-static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, int mx, int my)
+static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, const int m_xy[2])
{
float depth = -1.0f;
- depthdropper_depth_sample_pt(C, ddr, mx, my, &depth);
+ depthdropper_depth_sample_pt(C, ddr, m_xy, &depth);
if (depth != -1.0f) {
ddr->accum_depth += depth;
ddr->accum_tot++;
@@ -276,7 +278,7 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
case EYE_MODAL_SAMPLE_CONFIRM: {
const bool is_undo = ddr->is_undo;
if (ddr->accum_tot == 0) {
- depthdropper_depth_sample(C, ddr, event->xy[0], event->xy[1]);
+ depthdropper_depth_sample(C, ddr, event->xy);
}
else {
depthdropper_depth_set_accum(C, ddr);
@@ -288,12 +290,12 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
case EYE_MODAL_SAMPLE_BEGIN:
/* enable accum and make first sample */
ddr->accum_start = true;
- depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]);
+ depthdropper_depth_sample_accum(C, ddr, event->xy);
break;
case EYE_MODAL_SAMPLE_RESET:
ddr->accum_tot = 0;
ddr->accum_depth = 0.0f;
- depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]);
+ depthdropper_depth_sample_accum(C, ddr, event->xy);
depthdropper_depth_set_accum(C, ddr);
break;
}
@@ -301,7 +303,7 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
else if (event->type == MOUSEMOVE) {
if (ddr->accum_start) {
/* button is pressed so keep sampling */
- depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]);
+ depthdropper_depth_sample_accum(C, ddr, event->xy);
depthdropper_depth_set_accum(C, ddr);
}
}
diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
index d76ff84bcad..c1d49406818 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
@@ -265,9 +265,9 @@ static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, Eyed
}
/* Sample the color below cursor. */
-static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, int mx, int my)
+static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, const int m_xy[2])
{
- eyedropper_color_sample_fl(C, mx, my, eye->color);
+ eyedropper_color_sample_fl(C, m_xy, eye->color);
}
/* Cancel operator. */
@@ -292,7 +292,7 @@ static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *
return OPERATOR_CANCELLED;
}
case EYE_MODAL_SAMPLE_CONFIRM: {
- eyedropper_gpencil_color_sample(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_gpencil_color_sample(C, eye, event->xy);
/* Create material. */
eyedropper_gpencil_color_set(C, event, eye);
@@ -309,7 +309,7 @@ static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *
}
case MOUSEMOVE:
case INBETWEEN_MOUSEMOVE: {
- eyedropper_gpencil_color_sample(C, eye, event->xy[0], event->xy[1]);
+ eyedropper_gpencil_color_sample(C, eye, event->xy);
break;
}
default: {
diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/interface_eyedropper_intern.h
index ec448ef9b9f..335ee520791 100644
--- a/source/blender/editors/interface/interface_eyedropper_intern.h
+++ b/source/blender/editors/interface/interface_eyedropper_intern.h
@@ -25,7 +25,7 @@
/* interface_eyedropper.c */
void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name);
-void eyedropper_draw_cursor_text_region(int x, int y, const char *name);
+void eyedropper_draw_cursor_text_region(const int xy[2], const char *name);
/**
* Utility to retrieve a button representing a RNA property that is currently under the cursor.
*
@@ -51,7 +51,7 @@ void datadropper_win_area_find(const struct bContext *C,
*
* \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking.
*/
-void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3]);
+void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]);
/* Used for most eye-dropper operators. */
enum {
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 905fd452b6c..a171160d020 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -3957,7 +3957,14 @@ static void ui_do_but_textedit(
ui_textedit_delete_selection(but, data);
}
if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) {
- ui_textedit_insert_buf(but, data, ime_data->str_result, ime_data->result_len);
+ if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
+ strcmp(ime_data->str_result, "\xE3\x80\x82") == 0) {
+ /* Convert Ideographic Full Stop (U+3002) to decimal point when entering numbers. */
+ ui_textedit_insert_ascii(but, data, '.');
+ }
+ else {
+ ui_textedit_insert_buf(but, data, ime_data->str_result, ime_data->result_len);
+ }
}
}
else if (event->type == WM_IME_COMPOSITE_END) {
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 0f5b4a1a0f1..d7d3288a68d 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -1949,6 +1949,16 @@ static void ui_id_preview_image_render_size(
}
}
+void UI_icon_render_id_ex(const bContext *C,
+ Scene *scene,
+ ID *id_to_render,
+ const enum eIconSizes size,
+ const bool use_job,
+ PreviewImage *r_preview_image)
+{
+ ui_id_preview_image_render_size(C, scene, id_to_render, r_preview_image, size, use_job);
+}
+
void UI_icon_render_id(
const bContext *C, Scene *scene, ID *id, const enum eIconSizes size, const bool use_job)
{
@@ -1957,19 +1967,21 @@ void UI_icon_render_id(
return;
}
+ ID *id_to_render = id;
+
/* For objects, first try if a preview can created via the object data. */
if (GS(id->name) == ID_OB) {
Object *ob = (Object *)id;
if (ED_preview_id_is_supported(ob->data)) {
- id = ob->data;
+ id_to_render = ob->data;
}
}
- if (!ED_preview_id_is_supported(id)) {
+ if (!ED_preview_id_is_supported(id_to_render)) {
return;
}
- ui_id_preview_image_render_size(C, scene, id, pi, size, use_job);
+ UI_icon_render_id_ex(C, scene, id_to_render, size, use_job, pi);
}
static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs)
@@ -2323,8 +2335,8 @@ int UI_icon_from_idcode(const int idcode)
return ICON_TEXT;
case ID_VF:
return ICON_FONT_DATA;
- case ID_HA:
- return ICON_HAIR_DATA;
+ case ID_CV:
+ return ICON_CURVES_DATA;
case ID_PT:
return ICON_POINTCLOUD_DATA;
case ID_VO:
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index f7424066ad8..260e3dabc25 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -597,6 +597,9 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op)
opop->operation = operation;
}
+ /* Outliner e.g. has to be aware of this change. */
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+
return operator_button_property_finish(C, &ptr, prop);
}
@@ -714,6 +717,9 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
}
}
+ /* Outliner e.g. has to be aware of this change. */
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+
return operator_button_property_finish(C, &ptr, prop);
}
@@ -2067,8 +2073,9 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot)
/** \name UI Tree-View Item Rename Operator
*
* General purpose renaming operator for tree-views. Thanks to this, to add a rename button to
- * context menus for example, tree-view API users don't have to implement own renaming operators
- * with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() override.
+ * context menus for example, tree-view API users don't have to implement their own renaming
+ * operators with the same logic as they already have for their #ui::AbstractTreeViewItem::rename()
+ * override.
*
* \{ */
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index 44942d508ca..bf3fa6e62d4 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -39,9 +39,8 @@
#include "BKE_global.h"
#include "BLF_api.h"
-#ifdef WITH_INTERNATIONAL
-# include "BLT_translation.h"
-#endif
+
+#include "BLT_translation.h"
#include "UI_interface.h"
@@ -454,15 +453,6 @@ void uiStyleInit(void)
printf("%s: error, no fonts available\n", __func__);
}
}
- else {
- /* ? just for speed to initialize?
- * Yes, this build the glyph cache and create
- * the texture.
- */
- BLF_size(font->blf_id, 11.0f * U.pixelsize, U.dpi);
- BLF_size(font->blf_id, 12.0f * U.pixelsize, U.dpi);
- BLF_size(font->blf_id, 14.0f * U.pixelsize, U.dpi);
- }
}
if (style == NULL) {
@@ -486,8 +476,6 @@ void uiStyleInit(void)
blf_mono_font = BLF_load_mono_default(unique);
}
- BLF_size(blf_mono_font, 12.0f * U.pixelsize, 72);
-
/* Set default flags based on UI preferences (not render fonts) */
{
const int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT |
@@ -530,8 +518,6 @@ void uiStyleInit(void)
const bool unique = true;
blf_mono_font_render = BLF_load_mono_default(unique);
}
-
- BLF_size(blf_mono_font_render, 12.0f * U.pixelsize, 72);
}
void UI_fontstyle_set(const uiFontStyle *fs)
diff --git a/source/blender/editors/interface/interface_template_search_menu.cc b/source/blender/editors/interface/interface_template_search_menu.cc
index 7a079e01e61..0ce3a0d8af1 100644
--- a/source/blender/editors/interface/interface_template_search_menu.cc
+++ b/source/blender/editors/interface/interface_template_search_menu.cc
@@ -465,6 +465,9 @@ static MenuSearch_Data *menu_items_from_ui_create(
const char *idname_array[] = {
/* While we could include this, it's just showing filenames to load. */
"TOPBAR_MT_file_open_recent",
+ /* Showing undo history is not helpful since users may accidentally undo
+ * an action they intend to run. */
+ "TOPBAR_MT_undo_history",
};
for (int i = 0; i < ARRAY_SIZE(idname_array); i++) {
MenuType *mt = WM_menutype_find(idname_array[i], false);
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 8895d08bae4..db33141de38 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -667,6 +667,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
/* Assign new pointer, takes care of updates/notifiers */
RNA_id_pointer_create(override_id, &idptr);
+ /* Insert into override hierarchy if possible. */
+ ID *owner_id = template_ui->ptr.owner_id;
+ if (owner_id != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(owner_id)) {
+ override_id->override_library->hierarchy_root =
+ owner_id->override_library->hierarchy_root;
+ owner_id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY;
+ }
}
undo_push_label = "Make Library Override";
}
@@ -792,8 +799,8 @@ static const char *template_id_browse_tip(const StructRNA *type)
return N_("Browse Workspace to be linked");
case ID_LP:
return N_("Browse LightProbe to be linked");
- case ID_HA:
- return N_("Browse Hair Data to be linked");
+ case ID_CV:
+ return N_("Browse Hair Curves Data to be linked");
case ID_PT:
return N_("Browse Point Cloud Data to be linked");
case ID_VO:
@@ -874,7 +881,7 @@ static uiBut *template_id_def_new_but(uiBlock *block,
BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
BLT_I18NCONTEXT_ID_WORKSPACE,
BLT_I18NCONTEXT_ID_LIGHTPROBE,
- BLT_I18NCONTEXT_ID_HAIR,
+ BLT_I18NCONTEXT_ID_CURVES,
BLT_I18NCONTEXT_ID_POINTCLOUD,
BLT_I18NCONTEXT_ID_VOLUME,
BLT_I18NCONTEXT_ID_SIMULATION, );
@@ -2672,18 +2679,6 @@ static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v
static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
{
- bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
- short proxy_protected, xco = 0, yco = 0;
- // int rb_col; // UNUSED
-
- /* determine whether constraint is proxy protected or not */
- if (BKE_constraints_proxylocked_owner(ob, pchan)) {
- proxy_protected = (con->flag & CONSTRAINT_PROXY_LOCAL) == 0;
- }
- else {
- proxy_protected = 0;
- }
-
/* unless button has own callback, it adds this callback to button */
uiBlock *block = uiLayoutGetBlock(layout);
UI_block_func_set(block, constraint_active_func, ob, con);
@@ -2708,72 +2703,23 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co
uiLayout *row = uiLayoutRow(layout, true);
- if (proxy_protected == 0) {
- uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
- }
- else {
- uiItemL(row, IFACE_(con->name), ICON_NONE);
- }
+ uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
- /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
- if (proxy_protected) {
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
+ /* Enabled eye icon. */
+ uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE);
- /* draw a ghost icon (for proxy) and also a lock beside it,
- * to show that constraint is "proxy locked" */
- uiDefIconBut(block,
- UI_BTYPE_BUT,
- 0,
- ICON_GHOST_ENABLED,
- xco + 12.2f * UI_UNIT_X,
- yco,
- 0.95f * UI_UNIT_X,
- 0.95f * UI_UNIT_Y,
- NULL,
- 0.0,
- 0.0,
- 0.0,
- 0.0,
- TIP_("Proxy Protected"));
- uiDefIconBut(block,
- UI_BTYPE_BUT,
- 0,
- ICON_LOCKED,
- xco + 13.1f * UI_UNIT_X,
- yco,
- 0.95f * UI_UNIT_X,
- 0.95f * UI_UNIT_Y,
- NULL,
- 0.0,
- 0.0,
- 0.0,
- 0.0,
- TIP_("Proxy Protected"));
-
- UI_block_emboss_set(block, UI_EMBOSS);
- }
- else {
- /* Enabled eye icon. */
- uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE);
-
- /* Extra operators menu. */
- uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con);
+ /* Extra operators menu. */
+ uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con);
- /* Close 'button' - emboss calls here disable drawing of 'button' behind X */
- sub = uiLayoutRow(row, false);
- uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
- uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT);
- uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete");
- }
+ /* Close 'button' - emboss calls here disable drawing of 'button' behind X */
+ sub = uiLayoutRow(row, false);
+ uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
+ uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT);
+ uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete");
/* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
uiItemS(layout);
- /* Set but-locks for protected settings (magic numbers are used here!) */
- if (proxy_protected) {
- UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint"));
- }
-
/* clear any locks set up for proxies/lib-linking */
UI_block_lock_clear(block);
}
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index cb1c3cedf8e..5db16354124 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -84,10 +84,6 @@ if(WITH_USD)
add_definitions(-DWITH_USD)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_PUGIXML)
add_definitions(-DWITH_PUGIXML)
endif()
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index 4e2ccea36ab..49d60ede277 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -131,6 +131,11 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
const bool use_instancing = RNA_boolean_get(op->ptr, "use_instancing");
const bool evaluation_mode = RNA_enum_get(op->ptr, "evaluation_mode");
+ const bool generate_preview_surface = RNA_boolean_get(op->ptr, "generate_preview_surface");
+ const bool export_textures = RNA_boolean_get(op->ptr, "export_textures");
+ const bool overwrite_textures = RNA_boolean_get(op->ptr, "overwrite_textures");
+ const bool relative_texture_paths = RNA_boolean_get(op->ptr, "relative_texture_paths");
+
struct USDExportParams params = {
export_animation,
export_hair,
@@ -141,6 +146,10 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
visible_objects_only,
use_instancing,
evaluation_mode,
+ generate_preview_surface,
+ export_textures,
+ overwrite_textures,
+ relative_texture_paths,
};
bool ok = USD_export(C, filename, &params, as_background_job);
@@ -173,6 +182,26 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op)
uiItemR(col, ptr, "evaluation_mode", 0, NULL, ICON_NONE);
box = uiLayoutBox(layout);
+ col = uiLayoutColumnWithHeading(box, true, IFACE_("Materials"));
+ uiItemR(col, ptr, "generate_preview_surface", 0, NULL, ICON_NONE);
+ const bool export_mtl = RNA_boolean_get(ptr, "export_materials");
+ uiLayoutSetActive(col, export_mtl);
+
+ uiLayout *row = uiLayoutRow(col, true);
+ uiItemR(row, ptr, "export_textures", 0, NULL, ICON_NONE);
+ const bool preview = RNA_boolean_get(ptr, "generate_preview_surface");
+ uiLayoutSetActive(row, export_mtl && preview);
+
+ row = uiLayoutRow(col, true);
+ uiItemR(row, ptr, "overwrite_textures", 0, NULL, ICON_NONE);
+ const bool export_tex = RNA_boolean_get(ptr, "export_textures");
+ uiLayoutSetActive(row, export_mtl && preview && export_tex);
+
+ row = uiLayoutRow(col, true);
+ uiItemR(row, ptr, "relative_texture_paths", 0, NULL, ICON_NONE);
+ uiLayoutSetActive(row, export_mtl && preview);
+
+ box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Experimental"), ICON_NONE);
uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE);
}
@@ -249,6 +278,32 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
"Use Settings for",
"Determines visibility of objects, modifier settings, and other areas where there "
"are different settings for viewport and rendering");
+
+ RNA_def_boolean(ot->srna,
+ "generate_preview_surface",
+ true,
+ "To USD Preview Surface",
+ "Generate an approximate USD Preview Surface shader "
+ "representation of a Principled BSDF node network");
+
+ RNA_def_boolean(ot->srna,
+ "export_textures",
+ true,
+ "Export Textures",
+ "If exporting materials, export textures referenced by material nodes "
+ "to a 'textures' directory in the same directory as the USD file");
+
+ RNA_def_boolean(ot->srna,
+ "overwrite_textures",
+ false,
+ "Overwrite Textures",
+ "Allow overwriting existing texture files when exporting textures");
+
+ RNA_def_boolean(ot->srna,
+ "relative_texture_paths",
+ true,
+ "Relative Texture Paths",
+ "Make texture asset paths relative to the USD file");
}
/* ====== USD Import ====== */
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index 4ad2e57d266..0fb64c8a46b 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -78,10 +78,6 @@ set(LIB
bf_windowmanager
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 18fe9cf7ad3..81dc34f894f 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -57,7 +57,7 @@
#define USE_NET_ISLAND_CONNECT
/**
- * Compare selected with its self.
+ * Compare selected with itself.
*/
static int bm_face_isect_self(BMFace *f, void *UNUSED(user_data))
{
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 7e05209f79e..8383492e459 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -32,6 +32,7 @@
#include "BLI_math.h"
#include "BLI_math_bits.h"
#include "BLI_rand.h"
+#include "BLI_string.h"
#include "BLI_utildefines_stack.h"
#include "BKE_context.h"
@@ -55,6 +56,8 @@
#include "ED_transform.h"
#include "ED_view3d.h"
+#include "BLT_translation.h"
+
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -1389,6 +1392,37 @@ static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *e
return edbm_select_mode_exec(C, op);
}
+static char *edbm_select_mode_get_description(struct bContext *UNUSED(C),
+ struct wmOperatorType *UNUSED(op),
+ struct PointerRNA *values)
+{
+ const int type = RNA_enum_get(values, "type");
+
+ /* Because the special behavior for shift and ctrl click depend on user input, they may be
+ * incorrect if the operator is used from a script or from a special button. So only return the
+ * specialized descriptions if only the "type" is set, which conveys that the operator is meant
+ * to be used with the logic in the `invoke` method. */
+ if (RNA_struct_property_is_set(values, "type") &&
+ !RNA_struct_property_is_set(values, "use_extend") &&
+ !RNA_struct_property_is_set(values, "use_expand") &&
+ !RNA_struct_property_is_set(values, "action")) {
+ switch (type) {
+ case SCE_SELECT_VERTEX:
+ return BLI_strdup(TIP_(
+ "Vertex select - Shift-Click for multiple modes, Ctrl-Click contracts selection"));
+ case SCE_SELECT_EDGE:
+ return BLI_strdup(
+ TIP_("Edge select - Shift-Click for multiple modes, "
+ "Ctrl-Click expands/contracts selection depending on the current mode"));
+ case SCE_SELECT_FACE:
+ return BLI_strdup(
+ TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection"));
+ }
+ }
+
+ return NULL;
+}
+
void MESH_OT_select_mode(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -1409,6 +1443,7 @@ void MESH_OT_select_mode(wmOperatorType *ot)
ot->invoke = edbm_select_mode_invoke;
ot->exec = edbm_select_mode_exec;
ot->poll = ED_operator_editmesh;
+ ot->get_description = edbm_select_mode_get_description;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index 949b12f9a65..5ea3c0d7b32 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -68,6 +68,7 @@ static const EnumPropertyItem prop_similar_types[] = {
{SIMVERT_FACE, "FACE", 0, "Amount of Adjacent Faces", ""},
{SIMVERT_VGROUP, "VGROUP", 0, "Vertex Groups", ""},
{SIMVERT_EDGE, "EDGE", 0, "Amount of Connecting Edges", ""},
+ {SIMVERT_CREASE, "VCREASE", 0, "Vertex Crease", ""},
{SIMEDGE_LENGTH, "LENGTH", 0, "Length", ""},
{SIMEDGE_DIR, "DIR", 0, "Direction", ""},
@@ -1009,12 +1010,16 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
}
KDTree_3d *tree_3d = NULL;
+ KDTree_1d *tree_1d = NULL;
GSet *gset = NULL;
switch (type) {
case SIMVERT_NORMAL:
tree_3d = BLI_kdtree_3d_new(tot_verts_selected_all);
break;
+ case SIMVERT_CREASE:
+ tree_1d = BLI_kdtree_1d_new(tot_verts_selected_all);
+ break;
case SIMVERT_EDGE:
case SIMVERT_FACE:
gset = BLI_gset_ptr_new("Select similar vertex: edge/face");
@@ -1025,6 +1030,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
}
int normal_tree_index = 0;
+ int tree_1d_index = 0;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(ob);
@@ -1050,6 +1056,12 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
}
defbase_selected = BLI_BITMAP_NEW(defbase_len, __func__);
}
+ else if (type == SIMVERT_CREASE) {
+ if (!CustomData_has_layer(&bm->vdata, CD_CREASE)) {
+ BLI_kdtree_1d_insert(tree_1d, tree_1d_index++, (float[1]){0.0f});
+ continue;
+ }
+ }
BMVert *vert; /* Mesh vertex. */
BMIter iter; /* Selected verts iterator. */
@@ -1085,6 +1097,11 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
}
break;
}
+ case SIMVERT_CREASE: {
+ const float *value = CustomData_bmesh_get(&bm->vdata, vert->head.data, CD_CREASE);
+ BLI_kdtree_1d_insert(tree_1d, tree_1d_index++, value);
+ break;
+ }
}
}
}
@@ -1113,6 +1130,10 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
}
/* Remove duplicated entries. */
+ if (tree_1d != NULL) {
+ BLI_kdtree_1d_deduplicate(tree_1d);
+ BLI_kdtree_1d_balance(tree_1d);
+ }
if (tree_3d != NULL) {
BLI_kdtree_3d_deduplicate(tree_3d);
BLI_kdtree_3d_balance(tree_3d);
@@ -1124,6 +1145,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
BMEditMesh *em = BKE_editmesh_from_object(ob);
BMesh *bm = em->bm;
bool changed = false;
+ bool has_crease_layer = false;
int cd_dvert_offset = -1;
BLI_bitmap *defbase_selected = NULL;
int defbase_len = 0;
@@ -1158,6 +1180,17 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
continue;
}
}
+ else if (type == SIMVERT_CREASE) {
+ has_crease_layer = CustomData_has_layer(&bm->vdata, CD_CREASE);
+ if (!has_crease_layer) {
+ /* Proceed only if we have to select all the vertices that have custom data value of 0.0f.
+ * In this case we will just select all the vertices.
+ * Otherwise continue the for loop. */
+ if (!ED_select_similar_compare_float_tree(tree_1d, 0.0f, thresh, compare)) {
+ continue;
+ }
+ }
+ }
BMVert *vert; /* Mesh vertex. */
BMIter iter; /* Selected verts iterator. */
@@ -1224,6 +1257,17 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
}
break;
}
+ case SIMVERT_CREASE: {
+ if (!has_crease_layer) {
+ select = true;
+ break;
+ }
+ const float *value = CustomData_bmesh_get(&bm->vdata, vert->head.data, CD_CREASE);
+ if (ED_select_similar_compare_float_tree(tree_1d, *value, thresh, compare)) {
+ select = true;
+ }
+ break;
+ }
}
if (select) {
@@ -1249,6 +1293,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
}
MEM_freeN(objects);
+ BLI_kdtree_1d_free(tree_1d);
BLI_kdtree_3d_free(tree_3d);
if (gset != NULL) {
BLI_gset_free(gset, NULL);
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 46a056f865b..59c357aa416 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -690,12 +690,12 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *
em_tmp = BKE_editmesh_create(bm);
*em = *em_tmp;
- /* Calculate face normals and tessellation at once since it's multi-threaded.
- * The vertex normals are stored in the undo-mesh, so this doesn't need to be updated. */
- BKE_editmesh_looptri_calc_ex(em,
- &(const struct BMeshCalcTessellation_Params){
- .face_normals = true,
- });
+ /* Normals should not be stored in the undo mesh, so recalculate them. The edit
+ * mesh is expected to have valid normals and there is no tracked dirty state. */
+ BLI_assert(BKE_mesh_vertex_normals_are_dirty(&um->me));
+
+ /* Calculate face normals and tessellation at once since it's multi-threaded. */
+ BKE_editmesh_looptri_and_normals_calc(em);
em->selectmode = um->selectmode;
bm->selectmode = um->selectmode;
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 013d5e5a661..f3db8f1f0d2 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -334,9 +334,6 @@ void EDBM_mesh_clear(BMEditMesh *em)
/* clear bmesh */
BM_mesh_clear(em->bm);
- /* Free evaluated meshes & cache. */
- BKE_editmesh_free_derived_caches(em);
-
/* free tessellation data */
em->tottri = 0;
MEM_SAFE_FREE(em->looptris);
@@ -1404,8 +1401,6 @@ void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params)
BM_lnorspace_invalidate(em->bm, false);
em->bm->spacearr_dirty &= ~BM_SPACEARR_BMO_SET;
}
- /* Don't keep stale evaluated mesh data around, see: T38872. */
- BKE_editmesh_free_derived_caches(em);
#ifdef DEBUG
{
diff --git a/source/blender/editors/metaball/CMakeLists.txt b/source/blender/editors/metaball/CMakeLists.txt
index 4e600dc0277..a247920c305 100644
--- a/source/blender/editors/metaball/CMakeLists.txt
+++ b/source/blender/editors/metaball/CMakeLists.txt
@@ -20,6 +20,7 @@ set(INC
../../blenkernel
../../blenlib
../../depsgraph
+ ../../gpu
../../makesdna
../../makesrna
../../render
diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c
index bedb9d4f4f4..51cfc920d1d 100644
--- a/source/blender/editors/metaball/mball_edit.c
+++ b/source/blender/editors/metaball/mball_edit.c
@@ -47,6 +47,8 @@
#include "DEG_depsgraph.h"
+#include "GPU_select.h"
+
#include "ED_mball.h"
#include "ED_object.h"
#include "ED_screen.h"
@@ -756,15 +758,19 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese
static MetaElem *startelem = NULL;
ViewContext vc;
int a, hits;
- uint buffer[MAXPICKBUF];
+ GPUSelectResult buffer[MAXPICKELEMS];
rcti rect;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
BLI_rcti_init_pt_radius(&rect, mval, 12);
- hits = view3d_opengl_select(
- &vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP);
+ hits = view3d_opengl_select(&vc,
+ buffer,
+ ARRAY_SIZE(buffer),
+ &rect,
+ VIEW3D_SELECT_PICK_NEAREST,
+ VIEW3D_SELECT_FILTER_NOP);
FOREACH_BASE_IN_EDIT_MODE_BEGIN (vc.view_layer, vc.v3d, base) {
ED_view3d_viewcontext_init_object(&vc, base->object);
@@ -789,7 +795,7 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese
ml = startelem;
while (ml) {
for (a = 0; a < hits; a++) {
- int hitresult = buffer[(4 * a) + 3];
+ const int hitresult = buffer[a].id;
if (hitresult == -1) {
continue;
}
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 040b5cd5066..df76e605ebb 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -85,14 +85,10 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
- add_definitions(-DWITH_HAIR_NODES)
+ add_definitions(-DWITH_NEW_CURVES_TYPE)
endif()
blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 06e21f91d04..d1deb6824ea 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -62,6 +62,7 @@
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_curve.h"
+#include "BKE_curves.h"
#include "BKE_displist.h"
#include "BKE_duplilist.h"
#include "BKE_effect.h"
@@ -69,7 +70,6 @@
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
-#include "BKE_hair.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_layer.h"
@@ -1894,18 +1894,18 @@ void OBJECT_OT_speaker_add(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Add Hair Operator
+/** \name Add Hair Curves Operator
* \{ */
-static bool object_hair_add_poll(bContext *C)
+static bool object_hair_curves_add_poll(bContext *C)
{
- if (!U.experimental.use_new_hair_type) {
+ if (!U.experimental.use_new_curves_type) {
return false;
}
return ED_operator_objectmode(C);
}
-static int object_hair_add_exec(bContext *C, wmOperator *op)
+static int object_hair_curves_add_exec(bContext *C, wmOperator *op)
{
ushort local_view_bits;
float loc[3], rot[3];
@@ -1913,22 +1913,22 @@ static int object_hair_add_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- Object *object = ED_object_add_type(C, OB_HAIR, NULL, loc, rot, false, local_view_bits);
+ Object *object = ED_object_add_type(C, OB_CURVES, NULL, loc, rot, false, local_view_bits);
object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */
return OPERATOR_FINISHED;
}
-void OBJECT_OT_hair_add(wmOperatorType *ot)
+void OBJECT_OT_hair_curves_add(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Add Hair";
- ot->description = "Add a hair object to the scene";
- ot->idname = "OBJECT_OT_hair_add";
+ ot->name = "Add Hair Curves";
+ ot->description = "Add a hair curves object to the scene";
+ ot->idname = "OBJECT_OT_hair_curves_add";
/* api callbacks */
- ot->exec = object_hair_add_exec;
- ot->poll = object_hair_add_poll;
+ ot->exec = object_hair_curves_add_exec;
+ ot->poll = object_hair_curves_add_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1998,6 +1998,10 @@ void ED_object_base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
ob->id.name + 2);
return;
}
+ if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) {
+ /* Do not delete objects used by overrides of collections. */
+ return;
+ }
DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_BASE_FLAGS);
@@ -2038,10 +2042,9 @@ static int object_delete_exec(bContext *C, wmOperator *op)
}
if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) {
- /* Can this case ever happen? */
BKE_reportf(op->reports,
RPT_WARNING,
- "Cannot delete object '%s' as it used by override collections",
+ "Cannot delete object '%s' as it is used by override collections",
ob->id.name + 2);
continue;
}
@@ -2344,11 +2347,6 @@ static void make_object_duplilist_real(bContext *C,
BKE_animdata_free(&ob_dst->id, true);
ob_dst->adt = NULL;
- /* Proxies are not to be copied. */
- ob_dst->proxy_from = NULL;
- ob_dst->proxy_group = NULL;
- ob_dst->proxy = NULL;
-
ob_dst->parent = NULL;
BKE_constraints_free(&ob_dst->constraints);
ob_dst->runtime.curve_cache = NULL;
@@ -2463,13 +2461,6 @@ static void make_object_duplilist_real(bContext *C,
}
if (base->object->transflag & OB_DUPLICOLLECTION && base->object->instance_collection) {
- LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
- if (ob->proxy_group == base->object) {
- ob->proxy = NULL;
- ob->proxy_from = NULL;
- DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
- }
- }
base->object->instance_collection = NULL;
}
@@ -3731,6 +3722,7 @@ static bool object_join_poll(bContext *C)
static int object_join_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
if (ob->mode & OB_MODE_EDIT) {
@@ -3741,6 +3733,14 @@ static int object_join_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Cannot edit external library data");
return OPERATOR_CANCELLED;
}
+ if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Cannot edit object '%s' as it is used by override collections",
+ ob->id.name + 2);
+ return OPERATOR_CANCELLED;
+ }
+
if (ob->type == OB_GPENCIL) {
bGPdata *gpd = (bGPdata *)ob->data;
if ((!gpd) || GPENCIL_ANY_MODE(gpd)) {
@@ -3829,6 +3829,7 @@ static bool join_shapes_poll(bContext *C)
static int join_shapes_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
if (ob->mode & OB_MODE_EDIT) {
@@ -3839,6 +3840,13 @@ static int join_shapes_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Cannot edit external library data");
return OPERATOR_CANCELLED;
}
+ if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Cannot edit object '%s' as it is used by override collections",
+ ob->id.name + 2);
+ return OPERATOR_CANCELLED;
+ }
if (ob->type == OB_MESH) {
return ED_mesh_shapes_join_objects_exec(C, op);
diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c
index a5e6e7f0852..b903d664c9b 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -108,7 +108,7 @@ typedef struct {
ListBase data;
/** Clear the images before baking */
bool bake_clear;
- /** margin size in pixels*/
+ /** Margin size in pixels. */
int bake_margin;
/** margin type */
char bake_margin_type;
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index d56d0edd5a2..f52d2103fff 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -188,7 +188,7 @@ static bool write_internal_bake_pixels(Image *image,
const char margin_type,
const bool is_clear,
const bool is_noncolor,
- Mesh const *mesh,
+ Mesh const *mesh_eval,
char const *uv_layer)
{
ImBuf *ibuf;
@@ -285,7 +285,7 @@ static bool write_internal_bake_pixels(Image *image,
/* margins */
if (margin > 0) {
- RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh, uv_layer);
+ RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer);
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
@@ -334,7 +334,7 @@ static bool write_external_bake_pixels(const char *filepath,
const int margin_type,
ImageFormatData *im_format,
const bool is_noncolor,
- Mesh const *mesh,
+ Mesh const *mesh_eval,
char const *uv_layer)
{
ImBuf *ibuf = NULL;
@@ -392,7 +392,7 @@ static bool write_external_bake_pixels(const char *filepath,
mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask");
RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer);
- RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh, uv_layer);
+ RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer);
if (mask_buffer) {
MEM_freeN(mask_buffer);
@@ -774,10 +774,10 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr,
BakeTargets *targets,
Object *ob,
BakePixel *pixel_array,
- ReportList *reports)
+ ReportList *reports,
+ Mesh *mesh_eval)
{
bool all_ok = true;
- const Mesh *me = (Mesh *)ob->data;
for (int i = 0; i < targets->num_images; i++) {
BakeImage *bk_image = &targets->images[i];
@@ -791,7 +791,7 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr,
bkr->margin_type,
bkr->is_clear,
targets->is_noncolor,
- me,
+ mesh_eval,
bkr->uv_layer);
/* might be read by UI to set active image for display */
@@ -852,7 +852,7 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr,
BakeTargets *targets,
Object *ob,
Object *ob_eval,
- Mesh *me,
+ Mesh *mesh_eval,
BakePixel *pixel_array,
ReportList *reports)
{
@@ -886,8 +886,8 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr,
if (ob_eval->mat[i]) {
BLI_path_suffix(name, FILE_MAX, ob_eval->mat[i]->id.name + 2, "_");
}
- else if (me->mat[i]) {
- BLI_path_suffix(name, FILE_MAX, me->mat[i]->id.name + 2, "_");
+ else if (mesh_eval->mat[i]) {
+ BLI_path_suffix(name, FILE_MAX, mesh_eval->mat[i]->id.name + 2, "_");
}
else {
/* if everything else fails, use the material index */
@@ -909,7 +909,7 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr,
bkr->margin_type,
&bake->im_format,
targets->is_noncolor,
- me,
+ mesh_eval,
bkr->uv_layer);
if (!ok) {
@@ -1211,7 +1211,7 @@ static bool bake_targets_output(const BakeAPIRender *bkr,
{
if (bkr->target == R_BAKE_TARGET_IMAGE_TEXTURES) {
if (bkr->save_mode == R_BAKE_SAVE_INTERNAL) {
- return bake_targets_output_internal(bkr, targets, ob, pixel_array, reports);
+ return bake_targets_output_internal(bkr, targets, ob, pixel_array, reports, me_eval);
}
if (bkr->save_mode == R_BAKE_SAVE_EXTERNAL) {
return bake_targets_output_external(
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 91a512ae8e9..7ef17689912 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -1356,15 +1356,6 @@ void ED_object_constraint_update(Main *bmain, Object *ob)
static void object_pose_tag_update(Main *bmain, Object *ob)
{
BKE_pose_tag_recalc(bmain, ob->pose); /* Checks & sort pose channels. */
- if (ob->proxy && ob->adt) {
- /* We need to make use of ugly #POSE_ANIMATION_WORKAROUND here too,
- * else anim data are not reloaded after calling `BKE_pose_rebuild()`,
- * which causes T43872.
- * Note that this is a bit wide here, since we cannot be sure whether there are some locked
- * proxy bones or not.
- * XXX Temp hack until new depsgraph hopefully solves this. */
- DEG_id_tag_update(&ob->id, ID_RECALC_ANIMATION);
- }
}
void ED_object_constraint_dependency_update(Main *bmain, Object *ob)
@@ -2453,12 +2444,6 @@ static int constraint_add_exec(
if ((ob->type == OB_ARMATURE) && (pchan)) {
BKE_pose_tag_recalc(bmain, ob->pose); /* sort pose channels */
- if (BKE_constraints_proxylocked_owner(ob, pchan) && ob->adt) {
- /* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too,
- * else anim data are not reloaded after calling `BKE_pose_rebuild()`, which causes T43872.
- * XXX Temp hack until new depsgraph hopefully solves this. */
- DEG_id_tag_update(&ob->id, ID_RECALC_ANIMATION);
- }
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM);
}
else {
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 49149a5152f..3744cbee3a4 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -43,6 +43,8 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+#include "BLT_translation.h"
+
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -603,6 +605,20 @@ static bool data_transfer_poll_property(const bContext *UNUSED(C),
return true;
}
+static char *data_transfer_get_description(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(ot),
+ PointerRNA *ptr)
+{
+ const bool reverse_transfer = RNA_boolean_get(ptr, "use_reverse_transfer");
+
+ if (reverse_transfer) {
+ return BLI_strdup(TIP_(
+ "Transfer data layer(s) (weights, edge sharp, etc.) from selected meshes to active one"));
+ }
+
+ return NULL;
+}
+
void OBJECT_OT_data_transfer(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -619,6 +635,7 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot)
ot->invoke = WM_menu_invoke;
ot->exec = data_transfer_exec;
ot->check = data_transfer_check;
+ ot->get_description = data_transfer_get_description;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 38d0a044cb4..a38a5165c8c 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1976,8 +1976,14 @@ static void move_to_collection_menu_create(bContext *C, uiLayout *layout, void *
RNA_int_set(&menu->ptr, "collection_index", menu->index);
RNA_boolean_set(&menu->ptr, "is_new", true);
- uiItemFullO_ptr(
- layout, menu->ot, "New Collection", ICON_ADD, menu->ptr.data, WM_OP_INVOKE_DEFAULT, 0, NULL);
+ uiItemFullO_ptr(layout,
+ menu->ot,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "New Collection"),
+ ICON_ADD,
+ menu->ptr.data,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ NULL);
uiItemS(layout);
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index d517d68f1fc..ddd44fb9ded 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -65,7 +65,6 @@ void OBJECT_OT_track_set(struct wmOperatorType *ot);
void OBJECT_OT_track_clear(struct wmOperatorType *ot);
void OBJECT_OT_make_local(struct wmOperatorType *ot);
void OBJECT_OT_make_override_library(struct wmOperatorType *ot);
-void OBJECT_OT_convert_proxy_to_override(struct wmOperatorType *ot);
void OBJECT_OT_make_single_user(struct wmOperatorType *ot);
void OBJECT_OT_make_links_scene(struct wmOperatorType *ot);
void OBJECT_OT_make_links_data(struct wmOperatorType *ot);
@@ -130,7 +129,7 @@ void OBJECT_OT_light_add(struct wmOperatorType *ot);
void OBJECT_OT_effector_add(struct wmOperatorType *ot);
void OBJECT_OT_camera_add(struct wmOperatorType *ot);
void OBJECT_OT_speaker_add(struct wmOperatorType *ot);
-void OBJECT_OT_hair_add(struct wmOperatorType *ot);
+void OBJECT_OT_hair_curves_add(struct wmOperatorType *ot);
void OBJECT_OT_pointcloud_add(struct wmOperatorType *ot);
/**
* Only used as menu.
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 71ad54383a6..af428512cfd 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -53,12 +53,12 @@
#include "BKE_armature.h"
#include "BKE_context.h"
#include "BKE_curve.h"
+#include "BKE_curves.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
#include "BKE_effect.h"
#include "BKE_global.h"
#include "BKE_gpencil_modifier.h"
-#include "BKE_hair.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_lib_id.h"
@@ -84,6 +84,8 @@
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
+#include "BLT_translation.h"
+
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -130,8 +132,8 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object *
else if (ob->type == OB_GPENCIL) {
BKE_gpencil_modifiers_calc(depsgraph, scene_eval, ob_eval);
}
- else if (ob->type == OB_HAIR) {
- BKE_hair_data_update(depsgraph, scene_eval, ob);
+ else if (ob->type == OB_CURVES) {
+ BKE_curves_data_update(depsgraph, scene_eval, ob);
}
else if (ob->type == OB_POINTCLOUD) {
BKE_pointcloud_data_update(depsgraph, scene_eval, ob);
@@ -763,6 +765,12 @@ static bool modifier_apply_obdata(
BKE_object_material_from_eval_data(bmain, ob, &mesh_applied->id);
BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true);
+ /* Anonymous attributes shouldn't by available on the applied geometry. */
+ CustomData_free_layers_anonymous(&me->vdata, me->totvert);
+ CustomData_free_layers_anonymous(&me->edata, me->totedge);
+ CustomData_free_layers_anonymous(&me->pdata, me->totpoly);
+ CustomData_free_layers_anonymous(&me->ldata, me->totloop);
+
if (md_eval->type == eModifierType_Multires) {
multires_customdata_delete(me);
}
@@ -1515,7 +1523,7 @@ static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED(
bool keep = RNA_boolean_get(values, "keep_modifier");
if (keep) {
- return BLI_strdup("Apply modifier as a new shapekey and keep it in the stack");
+ return BLI_strdup(TIP_("Apply modifier as a new shapekey and keep it in the stack"));
}
return NULL;
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index b171da42522..a9a429e7e6f 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -75,7 +75,6 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_track_clear);
WM_operatortype_append(OBJECT_OT_make_local);
WM_operatortype_append(OBJECT_OT_make_override_library);
- WM_operatortype_append(OBJECT_OT_convert_proxy_to_override);
WM_operatortype_append(OBJECT_OT_make_single_user);
WM_operatortype_append(OBJECT_OT_make_links_scene);
WM_operatortype_append(OBJECT_OT_make_links_data);
@@ -106,7 +105,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_light_add);
WM_operatortype_append(OBJECT_OT_camera_add);
WM_operatortype_append(OBJECT_OT_speaker_add);
- WM_operatortype_append(OBJECT_OT_hair_add);
+ WM_operatortype_append(OBJECT_OT_hair_curves_add);
WM_operatortype_append(OBJECT_OT_pointcloud_add);
WM_operatortype_append(OBJECT_OT_volume_add);
WM_operatortype_append(OBJECT_OT_volume_import);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index a6eb35d49b9..8678bf9bd92 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -63,11 +63,11 @@
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_curve.h"
+#include "BKE_curves.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
#include "BKE_fcurve.h"
#include "BKE_gpencil.h"
-#include "BKE_hair.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
#include "BKE_lattice.h"
@@ -1872,7 +1872,7 @@ static void single_obdata_users(
ob->data,
BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS));
break;
- case OB_HAIR:
+ case OB_CURVES:
ob->data = ID_NEW_SET(
ob->data,
BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS));
@@ -2334,7 +2334,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
const bool success = BKE_lib_override_library_create(
- bmain, scene, view_layer, id_root, &obact->id, NULL);
+ bmain, scene, view_layer, NULL, id_root, &obact->id, NULL);
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
@@ -2419,63 +2419,6 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot)
ot->prop = prop;
}
-static bool convert_proxy_to_override_poll(bContext *C)
-{
- Object *obact = CTX_data_active_object(C);
-
- return obact != NULL && obact->proxy != NULL;
-}
-
-static int convert_proxy_to_override_exec(bContext *C, wmOperator *op)
-{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
-
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
-
- Object *ob_proxy = CTX_data_active_object(C);
- Object *ob_proxy_group = ob_proxy->proxy_group;
- const bool is_override_instancing_object = ob_proxy_group != NULL;
-
- const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, view_layer, ob_proxy);
-
- if (!success) {
- BKE_reportf(
- op->reports,
- RPT_ERROR_INVALID_INPUT,
- "Could not create a library override from proxy '%s' (might use already local data?)",
- ob_proxy->id.name + 2);
- return OPERATOR_CANCELLED;
- }
-
- /* Remove the instance empty from this scene, the items now have an overridden collection
- * instead. */
- if (is_override_instancing_object) {
- ED_object_base_free_and_unlink(bmain, scene, ob_proxy_group);
- }
-
- DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_WINDOW, NULL);
-
- return OPERATOR_FINISHED;
-}
-
-void OBJECT_OT_convert_proxy_to_override(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Convert Proxy to Override";
- ot->description = "Convert a proxy to a local library override";
- ot->idname = "OBJECT_OT_convert_proxy_to_override";
-
- /* api callbacks */
- ot->exec = convert_proxy_to_override_exec;
- ot->poll = convert_proxy_to_override_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
/** \} */
/* ------------------------------------------------------------------- */
diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c
index fd649854d8f..a871ddea48c 100644
--- a/source/blender/editors/object/object_shapekey.c
+++ b/source/blender/editors/object/object_shapekey.c
@@ -43,13 +43,16 @@
#include "DNA_object_types.h"
#include "BKE_context.h"
+#include "BKE_crazyspace.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_main.h"
#include "BKE_object.h"
+#include "BKE_report.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "BLI_sys_types.h" /* for intptr_t support */
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index c320313643d..c9114c7a925 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -811,8 +811,8 @@ static int apply_objects_internal(bContext *C,
/* adjust data */
BKE_mesh_transform(me, mat, true);
- /* update normals */
- BKE_mesh_calc_normals(me);
+ /* If normal layers exist, they are now dirty. */
+ BKE_mesh_normals_tag_dirty(me);
}
else if (ob->type == OB_ARMATURE) {
bArmature *arm = ob->data;
@@ -1409,7 +1409,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
mul_v3_m4v3(&pt->x, diff_mat, mpt);
}
- /* Apply transform to editcurve*/
+ /* Apply transform to edit-curve. */
if (gps->editcurve != NULL) {
for (i = 0; i < gps->editcurve->tot_curve_points; i++) {
BezTriple *bezt = &gps->editcurve->curve_points[i].bezt;
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 3e74aaeeb10..ed0f7b2fad0 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -39,6 +39,7 @@
#include "BLI_alloca.h"
#include "BLI_array.h"
+#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -63,6 +64,8 @@
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
+#include "BLT_translation.h"
+
#include "DNA_armature_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -2464,17 +2467,14 @@ void ED_vgroup_mirror(Object *ob,
sel = sel_mirr = true;
}
- /* tag verts we have used */
- for (vidx = 0, mv = me->mvert; vidx < me->totvert; vidx++, mv++) {
- mv->flag &= ~ME_VERT_TMP_TAG;
- }
+ BLI_bitmap *vert_tag = BLI_BITMAP_NEW(me->totvert, __func__);
for (vidx = 0, mv = me->mvert; vidx < me->totvert; vidx++, mv++) {
- if ((mv->flag & ME_VERT_TMP_TAG) == 0) {
+ if (!BLI_BITMAP_TEST(vert_tag, vidx)) {
if ((vidx_mirr = mesh_get_x_mirror_vert(ob, NULL, vidx, use_topology)) != -1) {
if (vidx != vidx_mirr) {
mv_mirr = &me->mvert[vidx_mirr];
- if ((mv_mirr->flag & ME_VERT_TMP_TAG) == 0) {
+ if (!BLI_BITMAP_TEST(vert_tag, vidx_mirr)) {
if (use_vert_sel) {
sel = mv->flag & SELECT;
@@ -2489,8 +2489,8 @@ void ED_vgroup_mirror(Object *ob,
totmirr++;
}
- mv->flag |= ME_VERT_TMP_TAG;
- mv_mirr->flag |= ME_VERT_TMP_TAG;
+ BLI_BITMAP_ENABLE(vert_tag, vidx);
+ BLI_BITMAP_ENABLE(vert_tag, vidx_mirr);
}
}
}
@@ -2499,6 +2499,8 @@ void ED_vgroup_mirror(Object *ob,
}
}
}
+
+ MEM_freeN(vert_tag);
}
}
else if (ob->type == OB_LATTICE) {
@@ -3427,16 +3429,16 @@ static char *vertex_group_lock_description(struct bContext *UNUSED(C),
switch (action) {
case VGROUP_LOCK:
- action_str = "Lock";
+ action_str = TIP_("Lock");
break;
case VGROUP_UNLOCK:
- action_str = "Unlock";
+ action_str = TIP_("Unlock");
break;
case VGROUP_TOGGLE:
- action_str = "Toggle locks of";
+ action_str = TIP_("Toggle locks of");
break;
case VGROUP_INVERT:
- action_str = "Invert locks of";
+ action_str = TIP_("Invert locks of");
break;
default:
return NULL;
@@ -3444,34 +3446,34 @@ static char *vertex_group_lock_description(struct bContext *UNUSED(C),
switch (mask) {
case VGROUP_MASK_ALL:
- target_str = "all";
+ target_str = TIP_("all");
break;
case VGROUP_MASK_SELECTED:
- target_str = "selected";
+ target_str = TIP_("selected");
break;
case VGROUP_MASK_UNSELECTED:
- target_str = "unselected";
+ target_str = TIP_("unselected");
break;
case VGROUP_MASK_INVERT_UNSELECTED:
switch (action) {
case VGROUP_INVERT:
- target_str = "selected";
+ target_str = TIP_("selected");
break;
case VGROUP_LOCK:
- target_str = "selected and unlock unselected";
+ target_str = TIP_("selected and unlock unselected");
break;
case VGROUP_UNLOCK:
- target_str = "selected and lock unselected";
+ target_str = TIP_("selected and lock unselected");
break;
default:
- target_str = "all and invert unselected";
+ target_str = TIP_("all and invert unselected");
}
break;
default:
return NULL;
}
- return BLI_sprintfN("%s %s vertex groups of the active object", action_str, target_str);
+ return BLI_sprintfN(TIP_("%s %s vertex groups of the active object"), action_str, target_str);
}
void OBJECT_OT_vertex_group_lock(wmOperatorType *ot)
diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt
index a607663763e..7c32a2dcf1d 100644
--- a/source/blender/editors/physics/CMakeLists.txt
+++ b/source/blender/editors/physics/CMakeLists.txt
@@ -60,10 +60,6 @@ if(WITH_MOD_FLUID)
add_definitions(-DWITH_FLUID)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_BULLET)
list(APPEND INC
../../../../intern/rigidbody
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index ea35ad6dcd0..1407ca598a7 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -1441,7 +1441,7 @@ void recalc_emitter_field(Depsgraph *UNUSED(depsgraph), Object *UNUSED(ob), Part
PTCacheEdit *edit = psys->edit;
Mesh *mesh = edit->psmd_eval->mesh_final;
float *vec, *nor;
- int i, totface /*, totvert*/;
+ int i, totface;
if (!mesh) {
return;
@@ -1454,7 +1454,7 @@ void recalc_emitter_field(Depsgraph *UNUSED(depsgraph), Object *UNUSED(ob), Part
BLI_kdtree_3d_free(edit->emitter_field);
totface = mesh->totface;
- // totvert = dm->getNumVerts(dm); /* UNUSED */
+ // int totvert = dm->getNumVerts(dm); /* UNUSED */
edit->emitter_cosnos = MEM_callocN(sizeof(float[6]) * totface, "emitter cosnos");
diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt
index 31dca83a3ab..934badd3b6f 100644
--- a/source/blender/editors/render/CMakeLists.txt
+++ b/source/blender/editors/render/CMakeLists.txt
@@ -28,6 +28,7 @@ set(INC
../../imbuf
../../makesdna
../../makesrna
+ ../../nodes
../../render
../../sequencer
../../windowmanager
@@ -66,8 +67,4 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc
index 1d0de322271..d04e45e4ccb 100644
--- a/source/blender/editors/render/render_internal.cc
+++ b/source/blender/editors/render/render_internal.cc
@@ -56,6 +56,8 @@
#include "BKE_scene.h"
#include "BKE_screen.h"
+#include "NOD_composite.h"
+
#include "DEG_depsgraph.h"
#include "WM_api.h"
@@ -88,7 +90,7 @@ struct RenderJob {
Scene *scene;
ViewLayer *single_layer;
Scene *current_scene;
- /* TODO(sergey): Should not be needed once engine will have own
+ /* TODO(sergey): Should not be needed once engine will have its own
* depsgraph and copy-on-write will be implemented.
*/
Depsgraph *depsgraph;
@@ -614,8 +616,14 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL) {
image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, &tile_rect, offset_x, offset_y, viewname);
}
- BKE_image_update_gputexture_delayed(
- ima, ibuf, offset_x, offset_y, BLI_rcti_size_x(&tile_rect), BLI_rcti_size_y(&tile_rect));
+ ImageTile *image_tile = BKE_image_get_tile(ima, 0);
+ BKE_image_update_gputexture_delayed(ima,
+ image_tile,
+ ibuf,
+ offset_x,
+ offset_y,
+ BLI_rcti_size_x(&tile_rect),
+ BLI_rcti_size_y(&tile_rect));
/* make jobs timer to send notifier */
*(rj->do_update) = true;
@@ -973,7 +981,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even
rj->scene = scene;
rj->current_scene = rj->scene;
rj->single_layer = single_layer;
- /* TODO(sergey): Render engine should be using own depsgraph.
+ /* TODO(sergey): Render engine should be using its own depsgraph.
*
* NOTE: Currently is only used by ED_update_for_newframe() at the end of the render, so no
* need to ensure evaluation here. */
diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc
index 8bd0244c899..fb6742c9fd5 100644
--- a/source/blender/editors/render/render_opengl.cc
+++ b/source/blender/editors/render/render_opengl.cc
@@ -72,8 +72,11 @@
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+
#include "RE_pipeline.h"
+#include "BLT_translation.h"
+
#include "RNA_access.h"
#include "RNA_define.h"
@@ -632,7 +635,7 @@ static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data)
case ID_MC: /* MovieClip */
case ID_MSK: /* Mask */
case ID_LP: /* LightProbe */
- case ID_HA: /* Hair */
+ case ID_CV: /* Curves */
case ID_PT: /* PointCloud */
case ID_VO: /* Volume */
case ID_SIM: /* Simulation */
@@ -1326,12 +1329,12 @@ static char *screen_opengl_render_description(struct bContext *UNUSED(C),
}
if (RNA_boolean_get(ptr, "render_keyed_only")) {
- return BLI_strdup(
+ return BLI_strdup(TIP_(
"Render the viewport for the animation range of this scene, but only render keyframes of "
- "selected objects");
+ "selected objects"));
}
- return BLI_strdup("Render the viewport for the animation range of this scene");
+ return BLI_strdup(TIP_("Render the viewport for the animation range of this scene"));
}
void RENDER_OT_opengl(wmOperatorType *ot)
diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc
index 79c3b2f7ac6..c1c75e485f7 100644
--- a/source/blender/editors/render/render_preview.cc
+++ b/source/blender/editors/render/render_preview.cc
@@ -767,7 +767,7 @@ struct ObjectPreviewData {
/* The main for the preview, not of the current file. */
Main *pr_main;
/* Copy of the object to create the preview for. The copy is for thread safety (and to insert
- * it into an own main). */
+ * it into its own main). */
Object *object;
/* Current frame. */
int cfra;
@@ -1061,11 +1061,11 @@ static void shader_preview_texture(ShaderPreview *sp, Tex *tex, Scene *sce, Rend
/* Evaluate texture at tex_coord. */
TexResult texres = {0};
BKE_texture_get_value_ex(sce, tex, tex_coord, &texres, img_pool, color_manage);
-
- rect_float[0] = texres.tr;
- rect_float[1] = texres.tg;
- rect_float[2] = texres.tb;
- rect_float[3] = texres.talpha ? texres.ta : 1.0f;
+ copy_v4_fl4(rect_float,
+ texres.trgba[0],
+ texres.trgba[1],
+ texres.trgba[2],
+ texres.talpha ? texres.trgba[3] : 1.0f);
rect_float += 4;
}
diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc
index 992bba420c1..19ab6841e22 100644
--- a/source/blender/editors/render/render_shading.cc
+++ b/source/blender/editors/render/render_shading.cc
@@ -64,6 +64,8 @@
#include "BKE_workspace.h"
#include "BKE_world.h"
+#include "NOD_composite.h"
+
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
diff --git a/source/blender/editors/render/render_update.cc b/source/blender/editors/render/render_update.cc
index b2958efde9b..b1e8e3927ef 100644
--- a/source/blender/editors/render/render_update.cc
+++ b/source/blender/editors/render/render_update.cc
@@ -49,6 +49,8 @@
#include "BKE_paint.h"
#include "BKE_scene.h"
+#include "NOD_composite.h"
+
#include "RE_engine.h"
#include "RE_pipeline.h"
diff --git a/source/blender/editors/scene/CMakeLists.txt b/source/blender/editors/scene/CMakeLists.txt
index cd59f06c6e3..ce0c2062766 100644
--- a/source/blender/editors/scene/CMakeLists.txt
+++ b/source/blender/editors/scene/CMakeLists.txt
@@ -39,8 +39,5 @@ set(LIB
bf_blenlib
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
blender_add_lib(bf_editor_scene "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt
index d194d0cdbb6..7c5ae4dcd5e 100644
--- a/source/blender/editors/screen/CMakeLists.txt
+++ b/source/blender/editors/screen/CMakeLists.txt
@@ -57,9 +57,5 @@ set(LIB
bf_editor_space_sequencer
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_screen "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 5a2b53163b8..8dd4b940491 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1662,7 +1662,7 @@ void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable)
}
/* Seek audio to ensure playback in preview range with AV sync. */
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
/* Notifier caught by top header, for button. */
WM_event_add_notifier(C, NC_SCREEN | ND_ANIMPLAY, NULL);
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 6581bffb6bd..4af965621e3 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -2608,7 +2608,7 @@ typedef struct RegionMoveData {
ARegion *region;
ScrArea *area;
int bigger, smaller, origval;
- int origx, origy;
+ int orig_xy[2];
int maxsize;
AZEdge edge;
@@ -2716,8 +2716,7 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event
}
rmd->area = sad->sa1;
rmd->edge = az->edge;
- rmd->origx = event->xy[0];
- rmd->origy = event->xy[1];
+ copy_v2_v2_int(rmd->orig_xy, event->xy);
rmd->maxsize = area_max_regionsize(rmd->area, rmd->region, rmd->edge);
/* if not set we do now, otherwise it uses type */
@@ -2804,7 +2803,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
(BLI_rcti_size_x(&rmd->region->v2d.mask) + 1);
const int snap_size_threshold = (U.widget_unit * 2) / aspect;
if (ELEM(rmd->edge, AE_LEFT_TO_TOPRIGHT, AE_RIGHT_TO_TOPLEFT)) {
- delta = event->xy[0] - rmd->origx;
+ delta = event->xy[0] - rmd->orig_xy[0];
if (rmd->edge == AE_LEFT_TO_TOPRIGHT) {
delta = -delta;
}
@@ -2837,7 +2836,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
else {
- delta = event->xy[1] - rmd->origy;
+ delta = event->xy[1] - rmd->orig_xy[1];
if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) {
delta = -delta;
}
@@ -2879,7 +2878,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
case LEFTMOUSE:
if (event->val == KM_RELEASE) {
- if (len_manhattan_v2v2_int(event->xy, &rmd->origx) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) {
+ if (len_manhattan_v2v2_int(event->xy, rmd->orig_xy) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) {
if (rmd->region->flag & RGN_FLAG_HIDDEN) {
region_scale_toggle_hidden(C, rmd);
}
@@ -2986,7 +2985,7 @@ static int frame_offset_exec(bContext *C, wmOperator *op)
areas_do_frame_follow(C, false);
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
@@ -3047,7 +3046,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op)
areas_do_frame_follow(C, true);
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
}
@@ -3161,7 +3160,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
areas_do_frame_follow(C, true);
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
@@ -3225,7 +3224,7 @@ static int marker_jump_exec(bContext *C, wmOperator *op)
areas_do_frame_follow(C, true);
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
@@ -4626,7 +4625,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con
if (scene_eval == NULL) {
/* Happens when undo/redo system is used during playback, nothing meaningful we can do here. */
}
- else if (scene_eval->id.recalc & ID_RECALC_AUDIO_SEEK) {
+ else if (scene_eval->id.recalc & ID_RECALC_FRAME_CHANGE) {
/* Ignore seek here, the audio will be updated to the scene frame after jump during next
* dependency graph update. */
}
@@ -4741,7 +4740,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con
}
if (sad->flag & ANIMPLAY_FLAG_JUMPED) {
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
#ifdef PROFILE_AUDIO_SYNCH
old_frame = CFRA;
#endif
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index 4bc9f1e2565..2b16b70d1ef 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Making screendumps.
*/
/** \file
* \ingroup edscr
+ * Making screenshots of the entire window or sub-regions.
*/
#include <errno.h>
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 5c8d7a9e9b6..907080626e0 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -27,6 +27,7 @@ set(INC
../../gpu
../../imbuf
../../makesdna
+ ../../nodes
../../makesrna
../../render
../../windowmanager
@@ -89,9 +90,5 @@ set(LIB
bf_blenlib
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 265746e27cd..4010b87a2ed 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -47,6 +47,8 @@
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "NOD_texture.h"
+
#include "WM_api.h"
#include "wm_cursors.h"
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c
index da627c6b7db..805d2221f6f 100644
--- a/source/blender/editors/sculpt_paint/paint_hide.c
+++ b/source/blender/editors/sculpt_paint/paint_hide.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2010 by Nicholas Bishop
* All rights reserved.
- * Implements the PBVH node hiding operator
*/
/** \file
* \ingroup edsculpt
+ * Implements the PBVH node hiding operator.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index dc2eaacca0c..d5aa7647603 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -14,8 +14,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
@@ -56,6 +54,8 @@
#include "BKE_paint.h"
#include "BKE_undo_system.h"
+#include "NOD_texture.h"
+
#include "DEG_depsgraph.h"
#include "UI_interface.h"
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index ca012f20f01..4d23119dd5f 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -14,8 +14,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- *
- * The Original Code is: some of this file.
*/
/** \file
@@ -1730,17 +1728,12 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps,
normalize_v3(no);
}
else {
-#if 1
/* In case the normalizing per pixel isn't optimal,
* we could cache or access from evaluated mesh. */
normal_tri_v3(no,
ps->mvert_eval[lt_vtri[0]].co,
ps->mvert_eval[lt_vtri[1]].co,
ps->mvert_eval[lt_vtri[2]].co);
-#else
- /* Don't use because some modifiers don't have normal data (subsurf for eg). */
- copy_v3_v3(no, (float *)ps->dm->getTessFaceData(ps->dm, tri_index, CD_NORMAL));
-#endif
}
if (UNLIKELY(ps->is_flip_object)) {
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index df323baa2a9..75d4237d157 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -32,6 +32,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
+#include "BLI_string.h"
#include "BLI_task.h"
#include "DNA_brush_types.h"
@@ -43,9 +44,11 @@
#include "RNA_access.h"
#include "BKE_brush.h"
+#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_layer.h"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
@@ -1470,14 +1473,48 @@ struct WPaintData {
bool precomputed_weight_ready;
};
+static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
+{
+ Scene *scene = CTX_data_scene(C);
+ Brush *brush = paint->brush;
+ int cur_brush_size = BKE_brush_size_get(scene, brush);
+
+ BLI_strncpy(
+ cache->saved_active_brush_name, brush->id.name + 2, sizeof(cache->saved_active_brush_name));
+
+ /* Switch to the blur (smooth) brush. */
+ brush = BKE_paint_toolslots_brush_get(paint, WPAINT_TOOL_BLUR);
+ if (brush) {
+ BKE_paint_brush_set(paint, brush);
+ cache->saved_smooth_size = BKE_brush_size_get(scene, brush);
+ BKE_brush_size_set(scene, brush, cur_brush_size);
+ BKE_curvemapping_init(brush->curve);
+ }
+}
+
+static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ Brush *brush = BKE_paint_brush(paint);
+ /* The current brush should match with what we have stored in the cache. */
+ BLI_assert(brush == cache->brush);
+
+ /* Try to switch back to the saved/previous brush. */
+ BKE_brush_size_set(scene, brush, cache->saved_smooth_size);
+ brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, cache->saved_active_brush_name);
+ if (brush) {
+ BKE_paint_brush_set(paint, brush);
+ }
+}
+
/* Initialize the stroke cache invariants from operator properties */
static void vwpaint_update_cache_invariants(
- bContext *C, const VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2])
+ bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2])
{
StrokeCache *cache;
Scene *scene = CTX_data_scene(C);
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
- const Brush *brush = vp->paint.brush;
ViewContext *vc = paint_stroke_view_context(op->customdata);
Object *ob = CTX_data_active_object(C);
float mat[3][3];
@@ -1513,7 +1550,12 @@ static void vwpaint_update_cache_invariants(
ups->draw_inverted = false;
}
+ if (cache->alt_smooth) {
+ smooth_brush_toggle_on(C, &vp->paint, cache);
+ }
+
copy_v2_v2(cache->mouse, cache->initial_mouse);
+ Brush *brush = vp->paint.brush;
/* Truly temporary data that isn't stored in properties */
cache->vc = vc;
cache->brush = brush;
@@ -1715,15 +1757,15 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
wpd->mirror.lock = tmpflags;
}
- if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) {
- wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__);
- }
-
/* If not previously created, create vertex/weight paint mode session data */
vertex_paint_init_stroke(depsgraph, ob);
vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
vertex_paint_init_session_data(ts, ob);
+ if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) {
+ wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__);
+ }
+
if (ob->sculpt->mode.wpaint.dvert_prev != NULL) {
MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev;
for (int i = 0; i < me->totvert; i++, dv++) {
@@ -2395,7 +2437,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
wpi.vgroup_validmap = wpd->vgroup_validmap;
wpi.vgroup_locked = wpd->vgroup_locked;
wpi.vgroup_unlocked = wpd->vgroup_unlocked;
- wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip");
+ wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip") || ss->cache->invert;
wpi.do_multipaint = wpd->do_multipaint;
wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL) &&
(wpi.do_multipaint || wpi.vgroup_validmap[wpi.active.index]));
@@ -2480,6 +2522,14 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
MEM_freeN(wpd);
}
+ SculptSession *ss = ob->sculpt;
+
+ if (ss->cache->alt_smooth) {
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ VPaint *vp = ts->wpaint;
+ smooth_brush_toggle_off(C, &vp->paint, ss->cache);
+ }
+
/* and particles too */
if (ob->particlesystem.first) {
ParticleSystem *psys;
@@ -3433,6 +3483,14 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
MEM_freeN(vpd->smear.color_curr);
}
+ SculptSession *ss = ob->sculpt;
+
+ if (ss->cache->alt_smooth) {
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ VPaint *vp = ts->vpaint;
+ smooth_brush_toggle_off(C, &vp->paint, ss->cache);
+ }
+
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
MEM_freeN(vpd);
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
index 1dfc4db6ac3..1234a56853c 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -428,9 +428,9 @@ void PAINT_OT_weight_sample_group(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_UNDO;
- /* keyingset to use (dynamic enum) */
+ /* Group to use (dynamic enum). */
prop = RNA_def_enum(
- ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
+ ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "Vertex group to set as active");
RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 5ac13ebdd93..ea3d694542c 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2006 by Nicholas Bishop
* All rights reserved.
- * Implements the Sculpt Mode tools
*/
/** \file
* \ingroup edsculpt
+ * Implements the Sculpt Mode tools.
*/
#include "MEM_guardedalloc.h"
@@ -73,6 +73,8 @@
#include "BKE_subdiv_ccg.h"
#include "BKE_subsurf.h"
+#include "NOD_texture.h"
+
#include "DEG_depsgraph.h"
#include "IMB_colormanagement.h"
@@ -176,13 +178,8 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- if (ss->shapekey_active || ss->deform_modifiers_active) {
- const float(*vert_normals)[3] = BKE_pbvh_get_vert_normals(ss->pbvh);
- copy_v3_v3(no, vert_normals[index]);
- }
- else {
- copy_v3_v3(no, ss->vert_normals[index]);
- }
+ const float(*vert_normals)[3] = BKE_pbvh_get_vert_normals(ss->pbvh);
+ copy_v3_v3(no, vert_normals[index]);
break;
}
case PBVH_BMESH:
@@ -4028,13 +4025,71 @@ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss)
}
}
+static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
+{
+ Scene *scene = CTX_data_scene(C);
+ Brush *brush = paint->brush;
+
+ if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
+ cache->saved_mask_brush_tool = brush->mask_tool;
+ brush->mask_tool = BRUSH_MASK_SMOOTH;
+ }
+ else if (ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_SLIDE_RELAX,
+ SCULPT_TOOL_DRAW_FACE_SETS,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_SMEAR)) {
+ /* Do nothing, this tool has its own smooth mode. */
+ }
+ else {
+ int cur_brush_size = BKE_brush_size_get(scene, brush);
+
+ BLI_strncpy(cache->saved_active_brush_name,
+ brush->id.name + 2,
+ sizeof(cache->saved_active_brush_name));
+
+ /* Switch to the smooth brush. */
+ brush = BKE_paint_toolslots_brush_get(paint, SCULPT_TOOL_SMOOTH);
+ if (brush) {
+ BKE_paint_brush_set(paint, brush);
+ cache->saved_smooth_size = BKE_brush_size_get(scene, brush);
+ BKE_brush_size_set(scene, brush, cur_brush_size);
+ BKE_curvemapping_init(brush->curve);
+ }
+ }
+}
+
+static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ Brush *brush = BKE_paint_brush(paint);
+
+ if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
+ brush->mask_tool = cache->saved_mask_brush_tool;
+ }
+ else if (ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_SLIDE_RELAX,
+ SCULPT_TOOL_DRAW_FACE_SETS,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_SMEAR)) {
+ /* Do nothing. */
+ }
+ else {
+ /* Try to switch back to the saved/previous brush. */
+ BKE_brush_size_set(scene, brush, cache->saved_smooth_size);
+ brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, cache->saved_active_brush_name);
+ if (brush) {
+ BKE_paint_brush_set(paint, brush);
+ }
+ }
+}
+
/* Initialize the stroke cache invariants from operator properties. */
static void sculpt_update_cache_invariants(
bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2])
{
StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
Brush *brush = BKE_paint_brush(&sd->paint);
ViewContext *vc = paint_stroke_view_context(op->customdata);
@@ -4099,35 +4154,9 @@ static void sculpt_update_cache_invariants(
/* Alt-Smooth. */
if (cache->alt_smooth) {
- if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
- cache->saved_mask_brush_tool = brush->mask_tool;
- brush->mask_tool = BRUSH_MASK_SMOOTH;
- }
- else if (ELEM(brush->sculpt_tool,
- SCULPT_TOOL_SLIDE_RELAX,
- SCULPT_TOOL_DRAW_FACE_SETS,
- SCULPT_TOOL_PAINT,
- SCULPT_TOOL_SMEAR)) {
- /* Do nothing, this tool has its own smooth mode. */
- }
- else {
- Paint *p = &sd->paint;
- Brush *br;
- int size = BKE_brush_size_get(scene, brush);
-
- BLI_strncpy(cache->saved_active_brush_name,
- brush->id.name + 2,
- sizeof(cache->saved_active_brush_name));
-
- br = (Brush *)BKE_libblock_find_name(bmain, ID_BR, "Smooth");
- if (br) {
- BKE_paint_brush_set(p, br);
- brush = br;
- cache->saved_smooth_size = BKE_brush_size_get(scene, brush);
- BKE_brush_size_set(scene, brush, size);
- BKE_curvemapping_init(brush->curve);
- }
- }
+ smooth_brush_toggle_on(C, &sd->paint, cache);
+ /* Refresh the brush pointer in case we switched brush in the toggle function. */
+ brush = BKE_paint_brush(&sd->paint);
}
copy_v2_v2(cache->mouse, cache->initial_mouse);
@@ -4135,9 +4164,7 @@ static void sculpt_update_cache_invariants(
copy_v2_v2(ups->tex_mouse, cache->initial_mouse);
/* Truly temporary data that isn't stored in properties. */
-
cache->vc = vc;
-
cache->brush = brush;
/* Cache projection matrix. */
@@ -5255,9 +5282,7 @@ static void sculpt_brush_exit_tex(Sculpt *sd)
static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(stroke))
{
- Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
- Scene *scene = CTX_data_scene(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@@ -5275,23 +5300,9 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
/* Alt-Smooth. */
if (ss->cache->alt_smooth) {
- if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
- brush->mask_tool = ss->cache->saved_mask_brush_tool;
- }
- else if (ELEM(brush->sculpt_tool,
- SCULPT_TOOL_SLIDE_RELAX,
- SCULPT_TOOL_DRAW_FACE_SETS,
- SCULPT_TOOL_PAINT,
- SCULPT_TOOL_SMEAR)) {
- /* Do nothing. */
- }
- else {
- BKE_brush_size_set(scene, brush, ss->cache->saved_smooth_size);
- brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, ss->cache->saved_active_brush_name);
- if (brush) {
- BKE_paint_brush_set(&sd->paint, brush);
- }
- }
+ smooth_brush_toggle_off(C, &sd->paint, ss->cache);
+ /* Refresh the brush pointer in case we switched brush in the toggle function. */
+ brush = BKE_paint_brush(&sd->paint);
}
if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.c b/source/blender/editors/sculpt_paint/sculpt_brush_types.c
index c2acc361a79..0d2c5641183 100644
--- a/source/blender/editors/sculpt_paint/sculpt_brush_types.c
+++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2006 by Nicholas Bishop
* All rights reserved.
- * Implements the Sculpt Mode tools
*/
/** \file
* \ingroup edsculpt
+ * Implements the Sculpt Mode tools.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index f84380b4f64..8de9fa3763b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -376,14 +376,6 @@ typedef enum SculptFilterOrientation {
SCULPT_FILTER_ORIENTATION_VIEW = 2,
} SculptFilterOrientation;
-/* Defines how transform tools are going to apply its displacement. */
-typedef enum SculptTransformDisplacementMode {
- /* Displaces the elements from their original coordinates. */
- SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL = 0,
- /* Displaces the elements incrementally from their previous position. */
- SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL = 1,
-} SculptTransformDisplacementMode;
-
#define SCULPT_CLAY_STABILIZER_LEN 10
typedef struct AutomaskingSettings {
@@ -452,9 +444,6 @@ typedef struct FilterCache {
int active_face_set;
- /* Transform. */
- SculptTransformDisplacementMode transform_displacement_mode;
-
/* Auto-masking. */
AutomaskingCache *automasking;
} FilterCache;
diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c
index 119d246a770..f50775f8a32 100644
--- a/source/blender/editors/sculpt_paint/sculpt_ops.c
+++ b/source/blender/editors/sculpt_paint/sculpt_ops.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2006 by Nicholas Bishop
* All rights reserved.
- * Implements the Sculpt Mode tools
*/
/** \file
* \ingroup edsculpt
+ * Implements the Sculpt Mode tools.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index bfbe545d1ef..b91e05f226e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -70,10 +70,6 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob)
copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
copy_v3_v3(ss->init_pivot_scale, ss->pivot_scale);
- copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos);
- copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
- copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
-
SCULPT_undo_push_begin(ob, "Transform");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
@@ -81,13 +77,10 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob)
SCULPT_vertex_random_access_ensure(ss);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
-
- ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL;
}
static void sculpt_transform_matrices_init(SculptSession *ss,
const char symm,
- const SculptTransformDisplacementMode t_mode,
float r_transform_mats[8][4][4])
{
@@ -96,18 +89,9 @@ static void sculpt_transform_matrices_init(SculptSession *ss,
transform_mat[4][4];
float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3];
- switch (t_mode) {
- case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL:
- copy_v3_v3(start_pivot_pos, ss->init_pivot_pos);
- copy_v4_v4(start_pivot_rot, ss->init_pivot_rot);
- copy_v3_v3(start_pivot_scale, ss->init_pivot_scale);
- break;
- case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL:
- copy_v3_v3(start_pivot_pos, ss->prev_pivot_pos);
- copy_v4_v4(start_pivot_rot, ss->prev_pivot_rot);
- copy_v3_v3(start_pivot_scale, ss->prev_pivot_scale);
- break;
- }
+ copy_v3_v3(start_pivot_pos, ss->init_pivot_pos);
+ copy_v4_v4(start_pivot_rot, ss->init_pivot_rot);
+ copy_v3_v3(start_pivot_scale, ss->init_pivot_scale);
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
ePaintSymmetryAreas v_symm = i;
@@ -167,25 +151,15 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
float transformed_co[3], orig_co[3], disp[3];
- float *start_co;
float fade = vd.mask ? *vd.mask : 0.0f;
copy_v3_v3(orig_co, orig_data.co);
char symm_area = SCULPT_get_vertex_symm_area(orig_co);
- switch (ss->filter_cache->transform_displacement_mode) {
- case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL:
- start_co = orig_co;
- break;
- case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL:
- start_co = vd.co;
- break;
- }
-
- copy_v3_v3(transformed_co, start_co);
+ copy_v3_v3(transformed_co, orig_co);
mul_m4_v3(data->transform_mats[(int)symm_area], transformed_co);
- sub_v3_v3v3(disp, transformed_co, start_co);
+ sub_v3_v3v3(disp, transformed_co, orig_co);
mul_v3_fl(disp, 1.0f - fade);
- add_v3_v3v3(vd.co, start_co, disp);
+ add_v3_v3v3(vd.co, orig_co, disp);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@@ -207,8 +181,7 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob)
.nodes = ss->filter_cache->nodes,
};
- sculpt_transform_matrices_init(
- ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats);
+ sculpt_transform_matrices_init(ss, symm, data.transform_mats);
/* Regular transform applies all symmetry passes at once as it is split by symmetry areas
* (each vertex can only be transformed once by the transform matrix of its area). */
@@ -229,10 +202,6 @@ void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob)
sculpt_transform_all_vertices(sd, ob);
- copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos);
- copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
- copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
-
if (ss->deform_modifiers_active || ss->shapekey_active) {
SCULPT_flush_stroke_deform(sd, ob, true);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 8819496c168..0b64d1f8a35 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2006 by Nicholas Bishop
* All rights reserved.
- * Implements the Sculpt Mode tools
*/
/** \file
* \ingroup edsculpt
+ * Implements the Sculpt Mode tools.
*/
#include <stddef.h>
diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c
index e5ca5e4defd..3e05b8d7a7f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_uv.c
+++ b/source/blender/editors/sculpt_paint/sculpt_uv.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) Blender Foundation, 2002-2009
* All rights reserved.
- * UV Sculpt tools
*/
/** \file
* \ingroup edsculpt
+ * UV Sculpt tools.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 184d715a347..ba4ba04d548 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -658,7 +658,7 @@ static char *actkeys_paste_description(bContext *UNUSED(C),
{
/* Custom description if the 'flipped' option is used. */
if (RNA_boolean_get(ptr, "flipped")) {
- return BLI_strdup("Paste keyframes from mirrored bones if they exist");
+ return BLI_strdup(TIP_("Paste keyframes from mirrored bones if they exist"));
}
/* Use the default description in the other cases. */
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 4463856f40a..ba96ac52f1f 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -36,6 +36,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
+#include "BKE_lib_remap.h"
#include "BKE_nla.h"
#include "BKE_screen.h"
@@ -814,20 +815,15 @@ static void action_refresh(const bContext *C, ScrArea *area)
/* XXX re-sizing y-extents of tot should go here? */
}
-static void action_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void action_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
{
SpaceAction *sact = (SpaceAction *)slink;
- if ((ID *)sact->action == old_id) {
- sact->action = (bAction *)new_id;
- }
-
- if ((ID *)sact->ads.filter_grp == old_id) {
- sact->ads.filter_grp = (Collection *)new_id;
- }
- if ((ID *)sact->ads.source == old_id) {
- sact->ads.source = new_id;
- }
+ BKE_id_remapper_apply(mappings, (ID **)&sact->action, ID_REMAP_APPLY_DEFAULT);
+ BKE_id_remapper_apply(mappings, (ID **)&sact->ads.filter_grp, ID_REMAP_APPLY_DEFAULT);
+ BKE_id_remapper_apply(mappings, &sact->ads.source, ID_REMAP_APPLY_DEFAULT);
}
/**
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index b5f6874fcfc..14cc03e3120 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -40,10 +40,6 @@ set(SRC
set(LIB
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
@@ -52,7 +48,7 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
- add_definitions(-DWITH_HAIR_NODES)
+ add_definitions(-DWITH_NEW_CURVES_TYPE)
endif()
blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index f5107cb13fd..b83396b10d9 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -273,8 +273,8 @@ static bool buttons_context_path_data(ButsContextPath *path, int type)
if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) {
return true;
}
-#ifdef WITH_HAIR_NODES
- if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (ELEM(type, -1, OB_HAIR))) {
+#ifdef WITH_NEW_CURVES_TYPE
+ if (RNA_struct_is_a(ptr->type, &RNA_Curves) && (ELEM(type, -1, OB_CURVES))) {
return true;
}
#endif
@@ -314,7 +314,7 @@ static bool buttons_context_path_modifier(ButsContextPath *path)
OB_SURF,
OB_LATTICE,
OB_GPENCIL,
- OB_HAIR,
+ OB_CURVES,
OB_POINTCLOUD,
OB_VOLUME)) {
ModifierData *md = BKE_object_active_modifier(ob);
@@ -845,8 +845,8 @@ const char *buttons_context_dir[] = {
"line_style",
"collection",
"gpencil",
-#ifdef WITH_HAIR_NODES
- "hair",
+#ifdef WITH_NEW_CURVES_TYPE
+ "curves",
#endif
#ifdef WITH_POINT_CLOUD
"pointcloud",
@@ -941,9 +941,9 @@ int /*eContextResult*/ buttons_context(const bContext *C,
set_pointer_type(path, result, &RNA_LightProbe);
return CTX_RESULT_OK;
}
-#ifdef WITH_HAIR_NODES
- if (CTX_data_equals(member, "hair")) {
- set_pointer_type(path, result, &RNA_Hair);
+#ifdef WITH_NEW_CURVES_TYPE
+ if (CTX_data_equals(member, "curves")) {
+ set_pointer_type(path, result, &RNA_Curves);
return CTX_RESULT_OK;
}
#endif
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 007a9105c76..cf1e7788ff8 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -32,6 +32,7 @@
#include "BKE_context.h"
#include "BKE_gpencil_modifier.h" /* Types for registering panels. */
+#include "BKE_lib_remap.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "BKE_shader_fx.h"
@@ -860,54 +861,53 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params)
}
}
-static void buttons_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void buttons_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
{
SpaceProperties *sbuts = (SpaceProperties *)slink;
- if (sbuts->pinid == old_id) {
- sbuts->pinid = new_id;
- if (new_id == NULL) {
- sbuts->flag &= ~SB_PIN_CONTEXT;
- }
+ if (BKE_id_remapper_apply(mappings, &sbuts->pinid, ID_REMAP_APPLY_DEFAULT) ==
+ ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
+ sbuts->flag &= ~SB_PIN_CONTEXT;
}
if (sbuts->path) {
ButsContextPath *path = sbuts->path;
+ for (int i = 0; i < path->len; i++) {
+ switch (BKE_id_remapper_apply(mappings, &path->ptr[i].owner_id, ID_REMAP_APPLY_DEFAULT)) {
+ case ID_REMAP_RESULT_SOURCE_UNASSIGNED: {
+ if (i == 0) {
+ MEM_SAFE_FREE(sbuts->path);
+ }
+ else {
+ memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
+ path->len = i;
+ }
+ break;
+ }
+ case ID_REMAP_RESULT_SOURCE_REMAPPED: {
+ RNA_id_pointer_create(path->ptr[i].owner_id, &path->ptr[i]);
+ /* There is no easy way to check/make path downwards valid, just nullify it.
+ * Next redraw will rebuild this anyway. */
+ i++;
+ memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
+ path->len = i;
+ break;
+ }
- int i;
- for (i = 0; i < path->len; i++) {
- if (path->ptr[i].owner_id == old_id) {
- break;
- }
- }
-
- if (i == path->len) {
- /* pass */
- }
- else if (new_id == NULL) {
- if (i == 0) {
- MEM_SAFE_FREE(sbuts->path);
- }
- else {
- memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
- path->len = i;
+ case ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE:
+ case ID_REMAP_RESULT_SOURCE_UNAVAILABLE: {
+ /* Nothing to do. */
+ break;
+ }
}
}
- else {
- RNA_id_pointer_create(new_id, &path->ptr[i]);
- /* There is no easy way to check/make path downwards valid, just nullify it.
- * Next redraw will rebuild this anyway. */
- i++;
- memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
- path->len = i;
- }
}
if (sbuts->texuser) {
ButsContextTexture *ct = sbuts->texuser;
- if ((ID *)ct->texture == old_id) {
- ct->texture = (Tex *)new_id;
- }
+ BKE_id_remapper_apply(mappings, (ID **)&ct->texture, ID_REMAP_APPLY_DEFAULT);
BLI_freelistN(&ct->users);
ct->user = NULL;
}
diff --git a/source/blender/editors/space_clip/CMakeLists.txt b/source/blender/editors/space_clip/CMakeLists.txt
index db881dafa6b..8f1a2c3c81e 100644
--- a/source/blender/editors/space_clip/CMakeLists.txt
+++ b/source/blender/editors/space_clip/CMakeLists.txt
@@ -68,11 +68,6 @@ set(LIB
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
-
blender_add_lib(bf_editor_space_clip "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# Needed so we can use dna_type_offsets.h for defaults initialization.
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index ef522e57d02..b45eabdc7c3 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -1082,7 +1082,7 @@ static void change_frame_apply(bContext *C, wmOperator *op)
SUBFRA = 0.0f;
/* do updates */
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
}
@@ -1560,7 +1560,8 @@ static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
clip->proxy.build_size_flag,
clip->proxy.quality,
true,
- NULL);
+ NULL,
+ false);
}
WM_jobs_customdata_set(wm_job, pj, proxy_freejob);
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index b6dbda79a2d..da1d2dea653 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -39,6 +39,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_remap.h"
#include "BKE_movieclip.h"
#include "BKE_screen.h"
#include "BKE_tracking.h"
@@ -1317,23 +1318,18 @@ static void clip_properties_region_listener(const wmRegionListenerParams *params
/********************* registration ********************/
-static void clip_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void clip_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
{
SpaceClip *sclip = (SpaceClip *)slink;
- if (!ELEM(GS(old_id->name), ID_MC, ID_MSK)) {
+ if (!BKE_id_remapper_has_mapping_for(mappings, FILTER_ID_MC | FILTER_ID_MSK)) {
return;
}
- if ((ID *)sclip->clip == old_id) {
- sclip->clip = (MovieClip *)new_id;
- id_us_ensure_real(new_id);
- }
-
- if ((ID *)sclip->mask_info.mask == old_id) {
- sclip->mask_info.mask = (Mask *)new_id;
- id_us_ensure_real(new_id);
- }
+ BKE_id_remapper_apply(mappings, (ID **)&sclip->clip, ID_REMAP_APPLY_ENSURE_REAL);
+ BKE_id_remapper_apply(mappings, (ID **)&sclip->mask_info.mask, ID_REMAP_APPLY_ENSURE_REAL);
}
void ED_spacetype_clip(void)
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index ee0406cde30..313887ec8fd 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -1385,7 +1385,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op)
if (CFRA != sc->user.framenr) {
CFRA = sc->user.framenr;
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
}
diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt
index cbeb2e6f529..1df0c9c4409 100644
--- a/source/blender/editors/space_file/CMakeLists.txt
+++ b/source/blender/editors/space_file/CMakeLists.txt
@@ -92,10 +92,6 @@ if(WITH_IMAGE_HDR)
add_definitions(-DWITH_HDR)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 9f18f6d1443..14c786e5dea 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -906,7 +906,8 @@ void file_draw_list(const bContext *C, ARegion *region)
* since it's filelist_file_cache_block() and filelist_cache_previews_update()
* which controls previews task. */
{
- const bool previews_running = filelist_cache_previews_running(files);
+ const bool previews_running = filelist_cache_previews_running(files) &&
+ !filelist_cache_previews_done(files);
// printf("%s: preview task: %d\n", __func__, previews_running);
if (previews_running && !sfile->previews_timer) {
sfile->previews_timer = WM_event_add_timer_notifier(
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index 4334beea196..bd55e6f78ab 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -34,11 +34,11 @@ extern "C" {
struct ARegion;
struct ARegionType;
struct AssetLibrary;
-struct FileSelectParams;
struct FileAssetSelectParams;
+struct FileSelectParams;
struct SpaceFile;
-struct uiLayout;
struct View2D;
+struct uiLayout;
/* file_draw.c */
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index b5eb7905fd8..6be17bbd355 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -339,7 +339,10 @@ typedef struct FileListEntryCache {
/* Previews handling. */
TaskPool *previews_pool;
ThreadQueue *previews_done;
- size_t previews_todo_count;
+ /** Counter for previews that are not fully loaded and ready to display yet. So includes all
+ * previews either in `previews_pool` or `previews_done`. #filelist_cache_previews_update() makes
+ * previews in `preview_done` ready for display, so the counter is decremented there. */
+ int previews_todo_count;
} FileListEntryCache;
/* FileListCache.flags */
@@ -521,7 +524,7 @@ static int compare_apply_inverted(int val, const struct FileSortData *sort_data)
* 2) If not possible (file names match) and both represent local IDs, sort by ID-type.
* 3) If not possible and only one is a local ID, place files representing local IDs first.
*
- * TODO (not actually implemented, but should be):
+ * TODO: (not actually implemented, but should be):
* 4) If no file represents a local ID, sort by file path, so that files higher up the file system
* hierarchy are placed first.
*/
@@ -1647,7 +1650,6 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
preview_taskdata->preview = NULL;
BLI_thread_queue_push(cache->previews_done, preview);
- atomic_fetch_and_sub_z(&cache->previews_todo_count, 1);
// printf("%s: End (%d)...\n", __func__, threadid);
}
@@ -1689,6 +1691,7 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache)
}
MEM_freeN(preview);
}
+ cache->previews_todo_count = 0;
}
}
@@ -1759,7 +1762,6 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
preview->icon_id = BKE_icon_imbuf_create(imbuf);
}
BLI_thread_queue_push(cache->previews_done, preview);
- atomic_fetch_and_sub_z(&cache->previews_todo_count, 1);
}
else {
if (entry->redirection_path) {
@@ -1780,6 +1782,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
true,
filelist_cache_preview_freef);
}
+ cache->previews_todo_count++;
}
static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
@@ -2693,6 +2696,7 @@ bool filelist_cache_previews_update(FileList *filelist)
}
MEM_freeN(preview);
+ cache->previews_todo_count--;
}
return changed;
@@ -2714,7 +2718,7 @@ bool filelist_cache_previews_done(FileList *filelist)
}
return (cache->previews_pool == NULL) || (cache->previews_done == NULL) ||
- (cache->previews_todo_count == (size_t)BLI_thread_queue_len(cache->previews_done));
+ (cache->previews_todo_count == 0);
}
/* would recognize .blend as well */
@@ -2779,7 +2783,8 @@ int ED_path_extension_type(const char *path)
NULL)) {
return FILE_TYPE_TEXT;
}
- if (BLI_path_extension_check_n(path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", NULL)) {
+ if (BLI_path_extension_check_n(
+ path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", ".woff", ".woff2", NULL)) {
return FILE_TYPE_FTFONT;
}
if (BLI_path_extension_check(path, ".btx")) {
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index 14f596ae7bf..0a69d0f9b39 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -745,14 +745,17 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
N_("Fonts"),
ICON_FILE_FONT,
FS_INSERT_LAST);
+ fsmenu_add_windows_folder(fsmenu,
+ FS_CATEGORY_SYSTEM_BOOKMARKS,
+ &FOLDERID_SkyDrive,
+ N_("OneDrive"),
+ ICON_URL,
+ FS_INSERT_LAST);
/* These items are just put in path cache for thumbnail views and if bookmarked. */
fsmenu_add_windows_folder(
fsmenu, FS_CATEGORY_OTHER, &FOLDERID_UserProfiles, NULL, ICON_COMMUNITY, FS_INSERT_LAST);
-
- fsmenu_add_windows_folder(
- fsmenu, FS_CATEGORY_OTHER, &FOLDERID_SkyDrive, NULL, ICON_URL, FS_INSERT_LAST);
}
}
#elif defined(__APPLE__)
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 97a5f173ffd..470128f61bd 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -33,6 +33,7 @@
#include "BKE_appdir.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_screen.h"
@@ -989,7 +990,7 @@ static int /*eContextResult*/ file_context(const bContext *C,
return CTX_RESULT_MEMBER_NOT_FOUND;
}
-static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *UNUSED(new_id))
+static void file_id_remap(ScrArea *area, SpaceLink *sl, const struct IDRemapper *UNUSED(mappings))
{
SpaceFile *sfile = (SpaceFile *)sl;
diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt
index 2a795dd954c..2e6e6971ce9 100644
--- a/source/blender/editors/space_graph/CMakeLists.txt
+++ b/source/blender/editors/space_graph/CMakeLists.txt
@@ -61,9 +61,5 @@ if(WITH_AUDASPACE)
add_definitions(-DWITH_AUDASPACE)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_space_graph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index ed5993c77a7..3a2593cc543 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -437,7 +437,6 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu)
for (sel = 0; sel < 2; sel++) {
BezTriple *bezt = fcu->bezt, *prevbezt = NULL;
int basecol = (sel) ? TH_HANDLE_SEL_FREE : TH_HANDLE_FREE;
- const float *fp;
uchar col[4];
for (b = 0; b < fcu->totvert; b++, prevbezt = bezt, bezt++) {
@@ -452,17 +451,15 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu)
/* draw handle with appropriate set of colors if selection is ok */
if ((bezt->f2 & SELECT) == sel) {
- fp = bezt->vec[0];
-
/* only draw first handle if previous segment had handles */
if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
(prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) {
UI_GetThemeColor3ubv(basecol + bezt->h1, col);
col[3] = fcurve_display_alpha(fcu) * 255;
immAttr4ubv(color, col);
- immVertex2fv(pos, fp);
+ immVertex2fv(pos, bezt->vec[0]);
immAttr4ubv(color, col);
- immVertex2fv(pos, fp + 3);
+ immVertex2fv(pos, bezt->vec[1]);
}
/* only draw second handle if this segment is bezier */
@@ -470,33 +467,31 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu)
UI_GetThemeColor3ubv(basecol + bezt->h2, col);
col[3] = fcurve_display_alpha(fcu) * 255;
immAttr4ubv(color, col);
- immVertex2fv(pos, fp + 3);
+ immVertex2fv(pos, bezt->vec[1]);
immAttr4ubv(color, col);
- immVertex2fv(pos, fp + 6);
+ immVertex2fv(pos, bezt->vec[2]);
}
}
else {
/* only draw first handle if previous segment was had handles, and selection is ok */
if (((bezt->f1 & SELECT) == sel) && ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
(prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)))) {
- fp = bezt->vec[0];
UI_GetThemeColor3ubv(basecol + bezt->h1, col);
col[3] = fcurve_display_alpha(fcu) * 255;
immAttr4ubv(color, col);
- immVertex2fv(pos, fp);
+ immVertex2fv(pos, bezt->vec[0]);
immAttr4ubv(color, col);
- immVertex2fv(pos, fp + 3);
+ immVertex2fv(pos, bezt->vec[1]);
}
/* only draw second handle if this segment is bezier, and selection is ok */
if (((bezt->f3 & SELECT) == sel) && (bezt->ipo == BEZT_IPO_BEZ)) {
- fp = bezt->vec[1];
UI_GetThemeColor3ubv(basecol + bezt->h2, col);
col[3] = fcurve_display_alpha(fcu) * 255;
immAttr4ubv(color, col);
- immVertex2fv(pos, fp);
+ immVertex2fv(pos, bezt->vec[0]);
immAttr4ubv(color, col);
- immVertex2fv(pos, fp + 3);
+ immVertex2fv(pos, bezt->vec[1]);
}
}
}
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 2afee277847..63de7fb570e 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -586,7 +586,7 @@ static char *graphkeys_paste_description(bContext *UNUSED(C),
{
/* Custom description if the 'flipped' option is used. */
if (RNA_boolean_get(ptr, "flipped")) {
- return BLI_strdup("Paste keyframes from mirrored bones if they exist");
+ return BLI_strdup(TIP_("Paste keyframes from mirrored bones if they exist"));
}
/* Use the default description in the other cases. */
@@ -2353,6 +2353,103 @@ void GRAPH_OT_snap(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Equalize Handles Operator
+ * \{ */
+
+/* Defines for equalize handles tool. */
+static const EnumPropertyItem prop_graphkeys_equalize_handles_sides[] = {
+ {GRAPHKEYS_EQUALIZE_LEFT, "LEFT", 0, "Left", "Equalize selected keyframes' left handles"},
+ {GRAPHKEYS_EQUALIZE_RIGHT, "RIGHT", 0, "Right", "Equalize selected keyframes' right handles"},
+ {GRAPHKEYS_EQUALIZE_BOTH, "BOTH", 0, "Both", "Equalize both of a keyframe's handles"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+/* ------------------- */
+
+/* Equalize selected keyframes' bezier handles. */
+static void equalize_graph_keys(bAnimContext *ac, int mode, float handle_length, bool flatten)
+{
+ /* Filter data. */
+ const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_NODUPLIS);
+ ListBase anim_data = {NULL, NULL};
+ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+
+ /* Equalize keyframes. */
+ LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
+ ANIM_fcurve_equalize_keyframes_loop(ale->key_data, mode, handle_length, flatten);
+ ale->update |= ANIM_UPDATE_DEFAULT;
+ }
+
+ ANIM_animdata_update(ac, &anim_data);
+ ANIM_animdata_freelist(&anim_data);
+}
+
+static int graphkeys_equalize_handles_exec(bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ /* Get editor data. */
+ if (ANIM_animdata_get_context(C, &ac) == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Get equalize mode. */
+ int mode = RNA_enum_get(op->ptr, "side");
+ float handle_length = RNA_float_get(op->ptr, "handle_length");
+ bool flatten = RNA_boolean_get(op->ptr, "flatten");
+
+ /* Equalize graph keyframes. */
+ equalize_graph_keys(&ac, mode, handle_length, flatten);
+
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GRAPH_OT_equalize_handles(wmOperatorType *ot)
+{
+ /* Identifiers */
+ ot->name = "Equalize Handles";
+ ot->idname = "GRAPH_OT_equalize_handles";
+ ot->description =
+ "Ensure selected keyframes' handles have equal length, optionally making them horizontal";
+
+ /* API callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = graphkeys_equalize_handles_exec;
+ ot->poll = graphop_editable_keyframes_poll;
+
+ /* Flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* Properties */
+ ot->prop = RNA_def_enum(ot->srna,
+ "side",
+ prop_graphkeys_equalize_handles_sides,
+ 0,
+ "Side",
+ "Side of the keyframes' bezier handles to affect");
+ RNA_def_float(ot->srna,
+ "handle_length",
+ 5.0f,
+ 0.1f,
+ FLT_MAX,
+ "Handle Length",
+ "Length to make selected keyframes' bezier handles",
+ 1.0f,
+ 50.0f);
+ RNA_def_boolean(
+ ot->srna,
+ "flatten",
+ false,
+ "Flatten",
+ "Make the values of the selected keyframes' handles the same as their respective keyframes");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Mirror Keyframes Operator
* \{ */
diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h
index 243a4ee4b95..a59fb63dd22 100644
--- a/source/blender/editors/space_graph/graph_intern.h
+++ b/source/blender/editors/space_graph/graph_intern.h
@@ -144,6 +144,7 @@ void GRAPH_OT_easing_type(struct wmOperatorType *ot);
void GRAPH_OT_frame_jump(struct wmOperatorType *ot);
void GRAPH_OT_snap_cursor_value(struct wmOperatorType *ot);
void GRAPH_OT_snap(struct wmOperatorType *ot);
+void GRAPH_OT_equalize_handles(struct wmOperatorType *ot);
void GRAPH_OT_mirror(struct wmOperatorType *ot);
/* defines for snap keyframes
@@ -158,6 +159,15 @@ enum eGraphKeys_Snap_Mode {
GRAPHKEYS_SNAP_VALUE,
};
+/* Defines for equalize keyframe handles.
+ * NOTE: Keep in sync with eEditKeyframes_Equalize (in ED_keyframes_edit.h).
+ */
+enum eGraphKeys_Equalize_Mode {
+ GRAPHKEYS_EQUALIZE_LEFT = 1,
+ GRAPHKEYS_EQUALIZE_RIGHT,
+ GRAPHKEYS_EQUALIZE_BOTH,
+};
+
/* defines for mirror keyframes
* NOTE: keep in sync with eEditKeyframes_Mirror (in ED_keyframes_edit.h)
*/
diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c
index fe4cffcb3b8..0cebc6eb586 100644
--- a/source/blender/editors/space_graph/graph_ops.c
+++ b/source/blender/editors/space_graph/graph_ops.c
@@ -103,7 +103,7 @@ static void graphview_cursor_apply(bContext *C, wmOperator *op)
}
SUBFRA = 0.0f;
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
}
/* set the cursor value */
@@ -456,6 +456,7 @@ void graphedit_operatortypes(void)
/* editing */
WM_operatortype_append(GRAPH_OT_snap);
+ WM_operatortype_append(GRAPH_OT_equalize_handles);
WM_operatortype_append(GRAPH_OT_mirror);
WM_operatortype_append(GRAPH_OT_frame_jump);
WM_operatortype_append(GRAPH_OT_snap_cursor_value);
diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c
index 4b8c983a761..cfaea33605a 100644
--- a/source/blender/editors/space_graph/graph_slider_ops.c
+++ b/source/blender/editors/space_graph/graph_slider_ops.c
@@ -483,7 +483,7 @@ static char *decimate_desc(bContext *UNUSED(C), wmOperatorType *UNUSED(op), Poin
if (RNA_enum_get(ptr, "mode") == DECIM_ERROR) {
return BLI_strdup(
- "Decimate F-Curves by specifying how much it can deviate from the original curve");
+ TIP_("Decimate F-Curves by specifying how much they can deviate from the original curve"));
}
/* Use default description. */
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 40c95d4f382..7d5e8836490 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -36,6 +36,7 @@
#include "BKE_context.h"
#include "BKE_fcurve.h"
+#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "ED_anim_api.h"
@@ -796,18 +797,17 @@ static void graph_refresh(const bContext *C, ScrArea *area)
graph_refresh_fcurve_colors(C);
}
-static void graph_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void graph_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
{
SpaceGraph *sgraph = (SpaceGraph *)slink;
-
- if (sgraph->ads) {
- if ((ID *)sgraph->ads->filter_grp == old_id) {
- sgraph->ads->filter_grp = (Collection *)new_id;
- }
- if ((ID *)sgraph->ads->source == old_id) {
- sgraph->ads->source = new_id;
- }
+ if (!sgraph->ads) {
+ return;
}
+
+ BKE_id_remapper_apply(mappings, (ID **)&sgraph->ads->filter_grp, ID_REMAP_APPLY_DEFAULT);
+ BKE_id_remapper_apply(mappings, (ID **)&sgraph->ads->source, ID_REMAP_APPLY_DEFAULT);
}
static int graph_space_subtype_get(ScrArea *area)
diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt
index 7a1bab0ef14..25d0e02ecc0 100644
--- a/source/blender/editors/space_image/CMakeLists.txt
+++ b/source/blender/editors/space_image/CMakeLists.txt
@@ -53,10 +53,6 @@ set(LIB
bf_editor_uvedit
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_OPENIMAGEIO)
add_definitions(-DWITH_OPENIMAGEIO)
endif()
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index ade5993cdb9..8858df3323f 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -591,7 +591,7 @@ float ED_space_image_zoom_level(const View2D *v2d, const int grid_dimension)
* - Default grid size on startup, which is 256x256 pixels
* - How blend factor for grid lines is set up in the fragment shader `grid_frag.glsl`. */
float zoom_factor;
- zoom_factor = (xzoom + yzoom) / 2.0f; /* Average for accuracy. */
+ zoom_factor = (xzoom + yzoom) / 2.0f; /* Average for accuracy. */
zoom_factor *= 256.0f / (powf(grid_dimension, 2));
return zoom_factor;
}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 23d07c9b45b..b1ca8505b8a 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -3605,7 +3605,7 @@ static void change_frame_apply(bContext *C, wmOperator *op)
SUBFRA = 0.0f;
/* do updates */
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
}
diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c
index e81f3b6a490..73993606a14 100644
--- a/source/blender/editors/space_image/image_undo.c
+++ b/source/blender/editors/space_image/image_undo.c
@@ -1000,7 +1000,7 @@ void ED_image_undosys_type(UndoType *ut)
ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref;
- /* NOTE this is actually a confusing case, since it expects a valid context, but only in a
+ /* NOTE: this is actually a confusing case, since it expects a valid context, but only in a
* specific case, see `image_undosys_step_encode` code. We cannot specify
* `UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE` though, as it can be called with a NULL context by
* current code. */
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index 52cb36b1b8f..eb5b6104a79 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -37,6 +37,7 @@
#include "BKE_context.h"
#include "BKE_image.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "RNA_access.h"
@@ -983,29 +984,19 @@ static void image_header_region_listener(const wmRegionListenerParams *params)
}
}
-static void image_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void image_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
{
SpaceImage *simg = (SpaceImage *)slink;
- if (!ELEM(GS(old_id->name), ID_IM, ID_GD, ID_MSK)) {
+ if (!BKE_id_remapper_has_mapping_for(mappings, FILTER_ID_IM | FILTER_ID_GD | FILTER_ID_MSK)) {
return;
}
- if ((ID *)simg->image == old_id) {
- simg->image = (Image *)new_id;
- id_us_ensure_real(new_id);
- }
-
- if ((ID *)simg->gpd == old_id) {
- simg->gpd = (bGPdata *)new_id;
- id_us_min(old_id);
- id_us_plus(new_id);
- }
-
- if ((ID *)simg->mask_info.mask == old_id) {
- simg->mask_info.mask = (Mask *)new_id;
- id_us_ensure_real(new_id);
- }
+ BKE_id_remapper_apply(mappings, (ID **)&simg->image, ID_REMAP_APPLY_ENSURE_REAL);
+ BKE_id_remapper_apply(mappings, (ID **)&simg->gpd, ID_REMAP_APPLY_UPDATE_REFCOUNT);
+ BKE_id_remapper_apply(mappings, (ID **)&simg->mask_info.mask, ID_REMAP_APPLY_ENSURE_REAL);
}
/**
diff --git a/source/blender/editors/space_info/CMakeLists.txt b/source/blender/editors/space_info/CMakeLists.txt
index 144b21fb9b8..fb17a6c9709 100644
--- a/source/blender/editors/space_info/CMakeLists.txt
+++ b/source/blender/editors/space_info/CMakeLists.txt
@@ -48,9 +48,5 @@ set(SRC
set(LIB
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_space_info "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc
index 005ae0214cd..a1eacc2bc3a 100644
--- a/source/blender/editors/space_info/info_stats.cc
+++ b/source/blender/editors/space_info/info_stats.cc
@@ -219,7 +219,7 @@ static void stats_object(Object *ob,
}
break;
}
- case OB_HAIR:
+ case OB_CURVES:
case OB_POINTCLOUD:
case OB_VOLUME: {
break;
diff --git a/source/blender/editors/space_nla/CMakeLists.txt b/source/blender/editors/space_nla/CMakeLists.txt
index 9a94d28c604..5326f1cce2b 100644
--- a/source/blender/editors/space_nla/CMakeLists.txt
+++ b/source/blender/editors/space_nla/CMakeLists.txt
@@ -47,9 +47,5 @@ set(LIB
bf_blenlib
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_space_nla "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index 12f0011b499..7dfe0e89e90 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -324,7 +324,7 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
- /* Fully opaque line on selected strips. */
+ /* Fully opaque line on selected strips. */
if (strip->flag & NLASTRIP_FLAG_SELECT) {
/* TODO: Use theme setting. */
immUniformColor3f(1.0f, 1.0f, 1.0f);
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index 0771153c5f5..962b5151661 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -33,6 +33,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
+#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "ED_anim_api.h"
@@ -577,18 +578,17 @@ static void nla_listener(const wmSpaceTypeListenerParams *params)
}
}
-static void nla_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void nla_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
{
SpaceNla *snla = (SpaceNla *)slink;
- if (snla->ads) {
- if ((ID *)snla->ads->filter_grp == old_id) {
- snla->ads->filter_grp = (Collection *)new_id;
- }
- if ((ID *)snla->ads->source == old_id) {
- snla->ads->source = new_id;
- }
+ if (snla->ads == NULL) {
+ return;
}
+ BKE_id_remapper_apply(mappings, (ID **)&snla->ads->filter_grp, ID_REMAP_APPLY_DEFAULT);
+ BKE_id_remapper_apply(mappings, (ID **)&snla->ads->source, ID_REMAP_APPLY_DEFAULT);
}
void ED_spacetype_nla(void)
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index 41d6388c947..15e0d04c8fa 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -63,10 +63,6 @@ set(LIB
bf_editor_screen
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_COMPOSITOR)
add_definitions(-DWITH_COMPOSITOR)
endif()
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 8474192ca23..30f2cc970bd 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -79,7 +79,7 @@
#include "NOD_texture.h"
#include "node_intern.hh" /* own include */
-using blender::float2;
+namespace blender::ed::space_node {
/* Default flags for uiItemR(). Name is kept short since this is used a lot in this file. */
#define DEFAULT_FLAGS UI_ITEM_R_SPLIT_EMPTY_NAME
@@ -160,6 +160,8 @@ static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerR
uiTemplateCurveMapping(layout, ptr, "mapping", 0, false, false, false, false);
}
+} // namespace blender::ed::space_node
+
#define SAMPLE_FLT_ISNONE FLT_MAX
/* Bad bad, 2.5 will do better? ... no it won't! */
static float _sample_col[4] = {SAMPLE_FLT_ISNONE};
@@ -173,6 +175,8 @@ void ED_node_sample_set(const float col[4])
}
}
+namespace blender::ed::space_node {
+
static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
@@ -227,7 +231,6 @@ static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt
NodeResizeDirection node_get_resize_direction(const bNode *node, const int x, const int y)
{
if (node->type == NODE_FRAME) {
- const float size = 10.0f;
NodeFrame *data = (NodeFrame *)node->storage;
/* shrinking frame size is determined by child nodes */
@@ -238,7 +241,9 @@ NodeResizeDirection node_get_resize_direction(const bNode *node, const int x, co
NodeResizeDirection dir = NODE_RESIZE_NONE;
const rctf &totr = node->totr;
- if (x >= totr.xmax - size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) {
+ const float size = NODE_RESIZE_MARGIN;
+
+ if (x > totr.xmax - size && x <= totr.xmax && y >= totr.ymin && y < totr.ymax) {
dir |= NODE_RESIZE_RIGHT;
}
if (x >= totr.xmin && x < totr.xmin + size && y >= totr.ymin && y < totr.ymax) {
@@ -1101,8 +1106,12 @@ static void node_socket_undefined_interface_draw_color(bContext *UNUSED(C),
/** \} */
+} // namespace blender::ed::space_node
+
void ED_node_init_butfuncs()
{
+ using namespace blender::ed::space_node;
+
/* Fallback types for undefined tree, nodes, sockets
* Defined in blenkernel, but not registered in type hashes.
*/
@@ -1135,9 +1144,11 @@ void ED_init_custom_node_type(bNodeType *UNUSED(ntype))
void ED_init_custom_node_socket_type(bNodeSocketType *stype)
{
- stype->draw = node_socket_button_label;
+ stype->draw = blender::ed::space_node::node_socket_button_label;
}
+namespace blender::ed::space_node {
+
static const float virtual_node_socket_color[4] = {0.2, 0.2, 0.2, 1.0};
/* maps standard socket integer type to a color */
@@ -1420,14 +1431,6 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout
uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, nullptr, 0);
}
-void ED_init_standard_node_socket_type(bNodeSocketType *stype)
-{
- stype->draw = std_node_socket_draw;
- stype->draw_color = std_node_socket_draw_color;
- stype->interface_draw = std_node_socket_interface_draw;
- stype->interface_draw_color = std_node_socket_interface_draw_color;
-}
-
static void node_socket_virtual_draw_color(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
PointerRNA *UNUSED(node_ptr),
@@ -1436,12 +1439,26 @@ static void node_socket_virtual_draw_color(bContext *UNUSED(C),
copy_v4_v4(r_color, virtual_node_socket_color);
}
+} // namespace blender::ed::space_node
+
+void ED_init_standard_node_socket_type(bNodeSocketType *stype)
+{
+ using namespace blender::ed::space_node;
+ stype->draw = std_node_socket_draw;
+ stype->draw_color = std_node_socket_draw_color;
+ stype->interface_draw = std_node_socket_interface_draw;
+ stype->interface_draw_color = std_node_socket_interface_draw_color;
+}
+
void ED_init_node_socket_type_virtual(bNodeSocketType *stype)
{
+ using namespace blender::ed::space_node;
stype->draw = node_socket_button_label;
stype->draw_color = node_socket_virtual_draw_color;
}
+namespace blender::ed::space_node {
+
/* ************** Generic drawing ************** */
void draw_nodespace_back_pix(const bContext &C,
@@ -2148,6 +2165,8 @@ void node_draw_link(const bContext &C,
node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3);
}
+} // namespace blender::ed::space_node
+
void ED_node_draw_snap(View2D *v2d, const float cent[2], float size, NodeBorder border, uint pos)
{
immBegin(GPU_PRIM_LINES, 4);
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index be29c125e5a..8e88f1fad5a 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -62,6 +62,8 @@
/** \name Utilities
* \{ */
+namespace blender::ed::space_node {
+
bNode *node_add_node(const bContext &C, const char *idname, int type, float locx, float locy)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
@@ -1046,3 +1048,5 @@ void NODE_OT_new_node_tree(wmOperatorType *ot)
}
/** \} */
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc
index 2f3855fd654..cab7cbf10be 100644
--- a/source/blender/editors/space_node/node_context_path.cc
+++ b/source/blender/editors/space_node/node_context_path.cc
@@ -45,11 +45,11 @@
#include "node_intern.hh"
-struct Mesh;
struct Curve;
struct Light;
-struct World;
struct Material;
+struct Mesh;
+struct World;
namespace blender::ed::space_node {
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index ee6dbc15c15..e290316af1f 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -88,13 +88,6 @@
#include "node_intern.hh" /* own include */
-using blender::Array;
-using blender::float2;
-using blender::Map;
-using blender::Set;
-using blender::Span;
-using blender::Vector;
-using blender::VectorSet;
using blender::fn::CPPType;
using blender::fn::FieldCPPType;
using blender::fn::FieldInput;
@@ -115,6 +108,8 @@ float ED_node_grid_size()
void ED_node_tree_update(const bContext *C)
{
+ using namespace blender::ed::space_node;
+
SpaceNode *snode = CTX_wm_space_node(C);
if (snode) {
snode_set_context(*C);
@@ -176,6 +171,8 @@ void ED_node_tag_update_id(ID *id)
}
}
+namespace blender::ed::space_node {
+
static bool compare_nodes(const bNode *a, const bNode *b)
{
/* These tell if either the node or any of the parent nodes is selected.
@@ -400,7 +397,7 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
/* Round the socket location to stop it from jiggling. */
nsock->locx = round(loc.x + NODE_WIDTH(node));
- nsock->locy = round(0.5f * (dy + buty));
+ nsock->locy = round(dy - NODE_DYS);
dy = buty;
if (nsock->next) {
@@ -530,7 +527,7 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
nsock->locx = loc.x;
/* Round the socket vertical position to stop it from jiggling. */
- nsock->locy = round(0.5f * (dy + buty));
+ nsock->locy = round(dy - NODE_DYS);
dy = buty - multi_input_socket_offset * 0.5;
if (nsock->next) {
@@ -629,7 +626,9 @@ static void node_update_hidden(bNode &node, uiBlock &block)
static int node_get_colorid(const bNode &node)
{
- switch (node.typeinfo->nclass) {
+ const int nclass = (node.typeinfo->ui_class == nullptr) ? node.typeinfo->nclass :
+ node.typeinfo->ui_class(&node);
+ switch (nclass) {
case NODE_CLASS_INPUT:
return TH_NODE_INPUT;
case NODE_CLASS_OUTPUT:
@@ -1071,8 +1070,12 @@ static void node_socket_draw_nested(const bContext &C,
UI_block_emboss_set(&block, old_emboss);
}
+} // namespace blender::ed::space_node
+
void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale)
{
+ using namespace blender::ed::space_node;
+
const float size = 2.25f * NODE_SOCKSIZE * scale;
rcti draw_rect = *rect;
float outline_color[4] = {0};
@@ -1119,6 +1122,8 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[
GPU_blend(state);
}
+namespace blender::ed::space_node {
+
/* ************** Socket callbacks *********** */
static void node_draw_preview_background(rctf *rect)
@@ -2025,7 +2030,7 @@ static void node_draw_basis(const bContext &C,
}
UI_draw_roundbox_corner_set(UI_CNR_ALL);
- UI_draw_roundbox_4fv(&rect, false, BASIS_RAD, color_outline);
+ UI_draw_roundbox_4fv(&rect, false, BASIS_RAD + outline_width, color_outline);
}
float scale;
@@ -2267,6 +2272,13 @@ void node_set_cursor(wmWindow &win, SpaceNode &snode, const float2 &cursor)
if (node) {
NodeResizeDirection dir = node_get_resize_direction(node, cursor[0], cursor[1]);
wmcursor = node_get_resize_cursor(dir);
+ /* We want to indicate that Frame nodes can be moved/selected on their borders. */
+ if (node->type == NODE_FRAME && dir == NODE_RESIZE_NONE) {
+ const rctf frame_inside = node_frame_rect_inside(*node);
+ if (!BLI_rctf_isect_pt(&frame_inside, cursor[0], cursor[1])) {
+ wmcursor = WM_CURSOR_NSEW_SCROLL;
+ }
+ }
}
WM_cursor_set(&win, wmcursor);
@@ -2879,3 +2891,5 @@ void node_draw_space(const bContext &C, ARegion &region)
/* Scrollers. */
UI_view2d_scrollers_draw(&v2d, nullptr);
}
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index 35cd74552ec..a513f5df7a0 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -76,10 +76,9 @@
#include "NOD_texture.h"
#include "node_intern.hh" /* own include */
-#define USE_ESC_COMPO
+namespace blender::ed::space_node {
-using blender::float2;
-using blender::Map;
+#define USE_ESC_COMPO
/* ***************** composite job manager ********************** */
@@ -320,8 +319,12 @@ static void compo_startjob(void *cjv,
ntree->progress = nullptr;
}
+} // namespace blender::ed::space_node
+
void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner)
{
+ using namespace blender::ed::space_node;
+
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -361,6 +364,8 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
+namespace blender::ed::space_node {
+
/* ***************************************** */
bool composite_node_active(bContext *C)
@@ -389,7 +394,7 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree)
{
WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr);
- if (ntree->type == NTREE_SHADER) {
+ if (ntree->type == NTREE_SHADER && id != nullptr) {
if (GS(id->name) == ID_MA) {
WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id);
}
@@ -411,18 +416,20 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree)
}
}
+} // namespace blender::ed::space_node
+
void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree)
{
if (C != nullptr) {
SpaceNode *snode = CTX_wm_space_node(C);
if (snode != nullptr && root_ntree != nullptr) {
- send_notifiers_after_tree_change(snode->id, root_ntree);
+ blender::ed::space_node::send_notifiers_after_tree_change(snode->id, root_ntree);
}
}
NodeTreeUpdateExtraParams params = {nullptr};
params.tree_changed_fn = [](ID *id, bNodeTree *ntree, void *UNUSED(user_data)) {
- send_notifiers_after_tree_change(id, ntree);
+ blender::ed::space_node::send_notifiers_after_tree_change(id, ntree);
DEG_id_tag_update(&ntree->id, ID_RECALC_COPY_ON_WRITE);
};
params.tree_output_changed_fn = [](ID *UNUSED(id), bNodeTree *ntree, void *UNUSED(user_data)) {
@@ -589,6 +596,8 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
BKE_ntree_update_main_tree(CTX_data_main(C), tex->nodetree, nullptr);
}
+namespace blender::ed::space_node {
+
/**
* Here we set the active tree(s), even called for each redraw now, so keep it fast :)
*/
@@ -631,6 +640,8 @@ void snode_set_context(const bContext &C)
}
}
+} // namespace blender::ed::space_node
+
void ED_node_set_active(
Main *bmain, SpaceNode *snode, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed)
{
@@ -796,6 +807,8 @@ void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree))
// node_update_nodetree(C, ntree, 0.0f, 0.0f);
}
+namespace blender::ed::space_node {
+
/* ***************** generic operator functions for nodes ***************** */
#if 0 /* UNUSED */
@@ -2929,3 +2942,4 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index 6f96e08d749..542e6fd748f 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -46,13 +46,11 @@
#include "node_intern.hh"
-using blender::IndexRange;
-using blender::Map;
-using blender::Set;
-using blender::StringRef;
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
using geo_log::GeometryAttributeInfo;
+namespace blender::ed::space_node {
+
struct AttributeSearchData {
const bNodeTree *tree;
const bNode *node;
@@ -139,3 +137,5 @@ void node_geometry_add_attribute_search_button(const bContext &UNUSED(C),
attribute_search_exec_fn,
nullptr);
}
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_gizmo.cc b/source/blender/editors/space_node/node_gizmo.cc
index 4e5c5694aff..8c60d100b26 100644
--- a/source/blender/editors/space_node/node_gizmo.cc
+++ b/source/blender/editors/space_node/node_gizmo.cc
@@ -43,6 +43,8 @@
#include "node_intern.hh"
+namespace blender::ed::space_node {
+
/* -------------------------------------------------------------------- */
/** \name Local Utilities
* \{ */
@@ -636,3 +638,5 @@ void NODE_GGT_backdrop_corner_pin(wmGizmoGroupType *gzgt)
}
/** \} */
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc
index 02d68189997..3d3f8378916 100644
--- a/source/blender/editors/space_node/node_group.cc
+++ b/source/blender/editors/space_node/node_group.cc
@@ -62,9 +62,7 @@
#include "NOD_socket.h"
#include "node_intern.hh" /* own include */
-using blender::float2;
-using blender::Map;
-using blender::Vector;
+namespace blender::ed::space_node {
/* -------------------------------------------------------------------- */
/** \name Local Utilities
@@ -778,6 +776,18 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
ListBase anim_basepaths = {nullptr, nullptr};
+ /* Detach unselected nodes inside frames when the frame is put into the group. Otherwise the
+ * `parent` pointer becomes dangling. */
+ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
+ if (node->parent == nullptr) {
+ continue;
+ }
+ if (node_group_make_use_node(*node->parent, gnode) &&
+ !node_group_make_use_node(*node, gnode)) {
+ nodeDetachNode(node);
+ }
+ }
+
/* move nodes over */
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree.nodes) {
if (node_group_make_use_node(*node, gnode)) {
@@ -1109,3 +1119,5 @@ void NODE_OT_group_insert(wmOperatorType *ot)
}
/** \} */
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 6016bf5da71..51825f5bd39 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -45,10 +45,15 @@ struct wmGizmoGroupType;
struct wmKeyConfig;
struct wmWindow;
+/* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */
+extern const char *node_context_dir[];
+
+namespace blender::ed::space_node {
+
/** Temporary data used in node link drag modal operator. */
struct bNodeLinkDrag {
/** Links dragged by the operator. */
- blender::Vector<bNodeLink *> links;
+ Vector<bNodeLink *> links;
bool from_multi_input_socket;
eNodeSocketInOut in_out;
@@ -82,7 +87,7 @@ struct SpaceNode_Runtime {
float aspect;
/** Mouse position for drawing socket-less links and adding nodes. */
- blender::float2 cursor;
+ float2 cursor;
/** For auto compositing. */
bool recalc;
@@ -104,18 +109,30 @@ enum NodeResizeDirection {
};
ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT);
+/* Nodes draw without dpi - the view zoom is flexible. */
+#define HIDDEN_RAD (0.75f * U.widget_unit)
+#define BASIS_RAD (0.2f * U.widget_unit)
+#define NODE_DYS (U.widget_unit / 2)
+#define NODE_DY U.widget_unit
+#define NODE_SOCKDY (0.1f * U.widget_unit)
+#define NODE_WIDTH(node) (node.width * UI_DPI_FAC)
+#define NODE_HEIGHT(node) (node.height * UI_DPI_FAC)
+#define NODE_MARGIN_X (1.2f * U.widget_unit)
+#define NODE_SOCKSIZE (0.25f * U.widget_unit)
+#define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit)
+#define NODE_RESIZE_MARGIN (0.20f * U.widget_unit)
+#define NODE_LINK_RESOL 12
+
+/* space_node.cc */
+
/**
* Transform between View2Ds in the tree path.
*/
-blender::float2 space_node_group_offset(const SpaceNode &snode);
+float2 space_node_group_offset(const SpaceNode &snode);
-float node_socket_calculate_height(const bNodeSocket &socket);
-blender::float2 node_link_calculate_multi_input_position(const blender::float2 &socket_position,
- int index,
- int total_inputs);
+rctf node_frame_rect_inside(const bNode &node);
int node_get_resize_cursor(NodeResizeDirection directions);
-NodeResizeDirection node_get_resize_direction(const bNode *node, int x, int y);
/**
* Usual convention here would be #node_socket_get_color(),
* but that's already used (for setting a color property socket).
@@ -125,6 +142,9 @@ void node_socket_color_get(const bContext &C,
PointerRNA &node_ptr,
const bNodeSocket &sock,
float r_color[4]);
+
+/* node_draw.cc */
+
void node_draw_space(const bContext &C, ARegion &region);
/**
@@ -133,15 +153,19 @@ void node_draw_space(const bContext &C, ARegion &region);
*/
void node_sort(bNodeTree &ntree);
-void node_set_cursor(wmWindow &win, SpaceNode &snode, const blender::float2 &cursor);
+void node_set_cursor(wmWindow &win, SpaceNode &snode, const float2 &cursor);
/* DPI scaled coords */
-blender::float2 node_to_view(const bNode &node, const blender::float2 &co);
+float2 node_to_view(const bNode &node, const float2 &co);
void node_to_updated_rect(const bNode &node, rctf &r_rect);
-blender::float2 node_from_view(const bNode &node, const blender::float2 &co);
+float2 node_from_view(const bNode &node, const float2 &co);
+
+/* node_ops.cc */
void node_operatortypes();
void node_keymap(wmKeyConfig *keyconf);
+/* node_select.cc */
+
void node_deselect_all(SpaceNode &snode);
void node_socket_select(bNode *node, bNodeSocket &sock);
void node_socket_deselect(bNode *node, bNodeSocket &sock, bool deselect_node);
@@ -160,6 +184,8 @@ void NODE_OT_select_grouped(wmOperatorType *ot);
void NODE_OT_select_same_type_step(wmOperatorType *ot);
void NODE_OT_find_node(wmOperatorType *ot);
+/* node_view.cc */
+
bool space_node_view_flag(
bContext &C, SpaceNode &snode, ARegion &region, int node_flag, int smooth_viewtx);
@@ -172,6 +198,10 @@ void NODE_OT_backimage_zoom(wmOperatorType *ot);
void NODE_OT_backimage_fit(wmOperatorType *ot);
void NODE_OT_backimage_sample(wmOperatorType *ot);
+/* drawnode.cc */
+
+NodeResizeDirection node_get_resize_direction(const bNode *node, int x, int y);
+
void nodelink_batch_start(SpaceNode &snode);
void nodelink_batch_end(SpaceNode &snode);
@@ -210,7 +240,7 @@ void draw_nodespace_back_pix(const bContext &C,
SpaceNode &snode,
bNodeInstanceKey parent_key);
-void node_select_all(ListBase *lb, int action);
+/* node_add.cc */
/**
* XXX Does some additional initialization on top of #nodeAddNode
@@ -227,6 +257,8 @@ void NODE_OT_add_file(wmOperatorType *ot);
void NODE_OT_add_mask(wmOperatorType *ot);
void NODE_OT_new_node_tree(wmOperatorType *ot);
+/* node_group.cc */
+
const char *node_group_idname(bContext *C);
void NODE_OT_group_make(wmOperatorType *ot);
void NODE_OT_group_insert(wmOperatorType *ot);
@@ -234,10 +266,12 @@ void NODE_OT_group_ungroup(wmOperatorType *ot);
void NODE_OT_group_separate(wmOperatorType *ot);
void NODE_OT_group_edit(wmOperatorType *ot);
+/* node_relationships.cc */
+
void sort_multi_input_socket_links(SpaceNode &snode,
bNode &node,
bNodeLink *drag_link,
- const blender::float2 *cursor);
+ const float2 *cursor);
void NODE_OT_link(wmOperatorType *ot);
void NODE_OT_link_make(wmOperatorType *ot);
@@ -254,6 +288,16 @@ void NODE_OT_link_viewer(wmOperatorType *ot);
void NODE_OT_insert_offset(wmOperatorType *ot);
+/* node_edit.cc */
+
+float2 node_link_calculate_multi_input_position(const float2 &socket_position,
+ int index,
+ int total_inputs);
+
+void node_select_all(ListBase *lb, int action);
+
+float node_socket_calculate_height(const bNodeSocket &socket);
+
void snode_set_context(const bContext &C);
bool composite_node_active(bContext *C);
@@ -267,7 +311,7 @@ int node_render_changed_exec(bContext *, wmOperator *);
bool node_find_indicated_socket(SpaceNode &snode,
bNode **nodep,
bNodeSocket **sockp,
- const blender::float2 &cursor,
+ const float2 &cursor,
eNodeSocketInOut in_out);
float node_link_dim_factor(const View2D &v2d, const bNodeLink &link);
bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link);
@@ -309,13 +353,17 @@ void NODE_OT_shader_script_update(wmOperatorType *ot);
void NODE_OT_viewer_border(wmOperatorType *ot);
void NODE_OT_clear_viewer_border(wmOperatorType *ot);
+void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot);
+void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot);
+
+/* node_gizmo.cc */
+
void NODE_GGT_backdrop_transform(wmGizmoGroupType *gzgt);
void NODE_GGT_backdrop_crop(wmGizmoGroupType *gzgt);
void NODE_GGT_backdrop_sun_beams(wmGizmoGroupType *gzgt);
void NODE_GGT_backdrop_corner_pin(wmGizmoGroupType *gzgt);
-void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot);
-void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot);
+/* node_geometry_attribute_search.cc */
void node_geometry_add_attribute_search_button(const bContext &C,
const bNodeTree &node_tree,
@@ -323,26 +371,12 @@ void node_geometry_add_attribute_search_button(const bContext &C,
PointerRNA &socket_ptr,
uiLayout &layout);
-extern const char *node_context_dir[];
-
-/* Nodes draw without dpi - the view zoom is flexible. */
-#define HIDDEN_RAD (0.75f * U.widget_unit)
-#define BASIS_RAD (0.2f * U.widget_unit)
-#define NODE_DYS (U.widget_unit / 2)
-#define NODE_DY U.widget_unit
-#define NODE_SOCKDY (0.1f * U.widget_unit)
-#define NODE_WIDTH(node) (node.width * UI_DPI_FAC)
-#define NODE_HEIGHT(node) (node.height * UI_DPI_FAC)
-#define NODE_MARGIN_X (1.2f * U.widget_unit)
-#define NODE_SOCKSIZE (0.25f * U.widget_unit)
-#define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit)
-#define NODE_RESIZE_MARGIN (0.20f * U.widget_unit)
-#define NODE_LINK_RESOL 12
-
-namespace blender::ed::space_node {
+/* node_context_path.c */
Vector<ui::ContextPathItem> context_path_for_space_node(const bContext &C);
+/* link_drag_search.cc */
+
void invoke_node_link_drag_add_menu(bContext &C,
bNode &node,
bNodeSocket &socket,
diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc
index a6264f151e4..2ca475f6948 100644
--- a/source/blender/editors/space_node/node_ops.cc
+++ b/source/blender/editors/space_node/node_ops.cc
@@ -35,6 +35,8 @@
#include "node_intern.hh" /* own include */
+namespace blender::ed::space_node {
+
void node_operatortypes()
{
WM_operatortype_append(NODE_OT_select);
@@ -127,6 +129,17 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_cryptomatte_layer_remove);
}
+void node_keymap(struct wmKeyConfig *keyconf)
+{
+ /* Entire Editor only ----------------- */
+ WM_keymap_ensure(keyconf, "Node Generic", SPACE_NODE, 0);
+
+ /* Main Region only ----------------- */
+ WM_keymap_ensure(keyconf, "Node Editor", SPACE_NODE, 0);
+}
+
+} // namespace blender::ed::space_node
+
void ED_operatormacros_node()
{
wmOperatorType *ot;
@@ -203,12 +216,3 @@ void ED_operatormacros_node()
WM_operatortype_macro_define(ot, "NODE_OT_links_detach");
WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
}
-
-void node_keymap(struct wmKeyConfig *keyconf)
-{
- /* Entire Editor only ----------------- */
- WM_keymap_ensure(keyconf, "Node Generic", SPACE_NODE, 0);
-
- /* Main Region only ----------------- */
- WM_keymap_ensure(keyconf, "Node Editor", SPACE_NODE, 0);
-}
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index 45b2dbda48b..d92f86c2cfc 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -68,10 +68,6 @@
#include "node_intern.hh" /* own include */
using namespace blender::nodes::node_tree_ref_types;
-using blender::float2;
-using blender::StringRef;
-using blender::StringRefNull;
-using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name Add Node
@@ -100,6 +96,8 @@ static void clear_picking_highlight(ListBase *links)
}
}
+namespace blender::ed::space_node {
+
static bNodeLink *create_drag_link(bNode &node, bNodeSocket &sock)
{
bNodeLink *oplink = MEM_cnew<bNodeLink>(__func__);
@@ -367,10 +365,7 @@ void sort_multi_input_socket_links(SpaceNode &snode,
}
}
-static void snode_autoconnect(Main &bmain,
- SpaceNode &snode,
- const bool allow_multiple,
- const bool replace)
+static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace)
{
bNodeTree *ntree = snode.edittree;
Vector<bNode *> sorted_nodes;
@@ -443,10 +438,6 @@ static void snode_autoconnect(Main &bmain,
}
}
}
-
- if (numlinks > 0) {
- BKE_ntree_update_main_tree(&bmain, ntree, nullptr);
- }
}
/** \} */
@@ -455,7 +446,7 @@ static void snode_autoconnect(Main &bmain,
/** \name Link Viewer Operator
* \{ */
-namespace blender::ed::nodes::viewer_linking {
+namespace viewer_linking {
/* Depending on the node tree type, different socket types are supported by viewer nodes. */
static bool socket_can_be_viewed(const OutputSocketRef &socket)
@@ -722,7 +713,7 @@ static int node_link_viewer(const bContext &C, bNode &bnode_to_view)
return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view);
}
-} // namespace blender::ed::nodes::viewer_linking
+} // namespace viewer_linking
static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -735,7 +726,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- if (blender::ed::nodes::viewer_linking::node_link_viewer(*C, *node) == OPERATOR_CANCELLED) {
+ if (viewer_linking::node_link_viewer(*C, *node) == OPERATOR_CANCELLED) {
return OPERATOR_CANCELLED;
}
@@ -1076,12 +1067,10 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (should_create_drag_link_search_menu(*snode.edittree, *nldrag)) {
bNodeLink &link = *nldrag->links.first();
if (nldrag->in_out == SOCK_OUT) {
- blender::ed::space_node::invoke_node_link_drag_add_menu(
- *C, *link.fromnode, *link.fromsock, cursor);
+ invoke_node_link_drag_add_menu(*C, *link.fromnode, *link.fromsock, cursor);
}
else {
- blender::ed::space_node::invoke_node_link_drag_add_menu(
- *C, *link.tonode, *link.tosock, cursor);
+ invoke_node_link_drag_add_menu(*C, *link.tonode, *link.tosock, cursor);
}
}
@@ -1308,13 +1297,13 @@ static int node_make_link_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
- snode_autoconnect(bmain, snode, true, replace);
+ snode_autoconnect(snode, true, replace);
/* deselect sockets after linking */
node_deselect_all_input_sockets(snode, false);
node_deselect_all_output_sockets(snode, false);
- ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
+ ED_node_tree_propagate_change(C, &bmain, snode.edittree);
return OPERATOR_FINISHED;
}
@@ -1949,8 +1938,12 @@ static bool ed_node_link_conditions(ScrArea *area,
return true;
}
+} // namespace blender::ed::space_node
+
void ED_node_link_intersect_test(ScrArea *area, int test)
{
+ using namespace blender::ed::space_node;
+
bNode *select;
SpaceNode *snode;
if (!ed_node_link_conditions(area, test, &snode, &select)) {
@@ -2010,6 +2003,8 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
}
}
+namespace blender::ed::space_node {
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -2049,20 +2044,17 @@ static int get_main_socket_priority(const bNodeSocket *socket)
/** Get the "main" socket based on the node declaration or an heuristic. */
static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out)
{
- using namespace blender;
- using namespace blender::nodes;
-
ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs;
/* Try to get the main socket based on the socket declaration. */
nodeDeclarationEnsure(&ntree, &node);
- const NodeDeclaration *node_decl = node.declaration;
+ const nodes::NodeDeclaration *node_decl = node.declaration;
if (node_decl != nullptr) {
- Span<SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs() :
- node_decl->outputs();
+ Span<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs() :
+ node_decl->outputs();
int index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) {
- const SocketDeclaration &socket_decl = *socket_decls[index];
+ const nodes::SocketDeclaration &socket_decl = *socket_decls[index];
if (nodeSocketIsHidden(socket)) {
continue;
}
@@ -2426,60 +2418,83 @@ void NODE_OT_insert_offset(wmOperatorType *ot)
/** \} */
+} // namespace blender::ed::space_node
+
/* -------------------------------------------------------------------- */
/** \name Note Link Insert
* \{ */
void ED_node_link_insert(Main *bmain, ScrArea *area)
{
- bNode *select;
+ using namespace blender::ed::space_node;
+
+ bNode *node_to_insert;
SpaceNode *snode;
- if (!ed_node_link_conditions(area, true, &snode, &select)) {
+ if (!ed_node_link_conditions(area, true, &snode, &node_to_insert)) {
return;
}
- /* get the link */
- bNodeLink *link;
- for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
+ /* Find link to insert on. */
+ bNodeTree &ntree = *snode->edittree;
+ bNodeLink *old_link = nullptr;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
if (link->flag & NODE_LINKFLAG_HILITE) {
+ old_link = link;
break;
}
}
+ if (old_link == nullptr) {
+ return;
+ }
- if (link) {
- bNodeSocket *best_input = get_main_socket(*snode->edittree, *select, SOCK_IN);
- bNodeSocket *best_output = get_main_socket(*snode->edittree, *select, SOCK_OUT);
+ old_link->flag &= ~NODE_LINKFLAG_HILITE;
- if (best_input && best_output) {
- bNode *node = link->tonode;
- bNodeSocket *sockto = link->tosock;
+ bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN);
+ bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT);
- link->tonode = select;
- link->tosock = best_input;
- node_remove_extra_links(*snode, *link);
- link->flag &= ~NODE_LINKFLAG_HILITE;
+ /* Ignore main sockets when the types don't match. */
+ if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr &&
+ !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type),
+ static_cast<eNodeSocketDatatype>(best_input->type))) {
+ best_input = nullptr;
+ }
+ if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr &&
+ !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type),
+ static_cast<eNodeSocketDatatype>(old_link->tosock->type))) {
+ best_output = nullptr;
+ }
- bNodeLink *new_link = nodeAddLink(snode->edittree, select, best_output, node, sockto);
+ bNode *from_node = old_link->fromnode;
+ bNodeSocket *from_socket = old_link->fromsock;
+ bNode *to_node = old_link->tonode;
- /* Copy the socket index for the new link, and reset it for the old link. This way the
- * relative order of links is preserved, and the links get drawn in the right place. */
- new_link->multi_input_socket_index = link->multi_input_socket_index;
- link->multi_input_socket_index = 0;
+ if (best_output != nullptr) {
+ /* Relink the "start" of the existing link to the newly inserted node. */
+ old_link->fromnode = node_to_insert;
+ old_link->fromsock = best_output;
+ BKE_ntree_update_tag_link_changed(&ntree);
+ }
+ else {
+ nodeRemLink(&ntree, old_link);
+ }
- /* set up insert offset data, it needs stuff from here */
- if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
- NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__);
+ if (best_input != nullptr) {
+ /* Add a new link that connects the node on the left to the newly inserted node. */
+ nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input);
+ }
- iofsd->insert = select;
- iofsd->prev = link->fromnode;
- iofsd->next = node;
+ /* Set up insert offset data, it needs stuff from here. */
+ if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
+ NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__);
- snode->runtime->iofsd = iofsd;
- }
+ iofsd->insert = node_to_insert;
+ iofsd->prev = from_node;
+ iofsd->next = to_node;
- ED_node_tree_propagate_change(nullptr, bmain, snode->edittree);
- }
+ snode->runtime->iofsd = iofsd;
}
+
+ ED_node_tree_propagate_change(nullptr, bmain, snode->edittree);
}
/** \} */
diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc
index d0197252588..6b0fa2cc37c 100644
--- a/source/blender/editors/space_node/node_select.cc
+++ b/source/blender/editors/space_node/node_select.cc
@@ -62,7 +62,7 @@
#include "node_intern.hh" /* own include */
-using blender::float2;
+namespace blender::ed::space_node {
/**
* Function to detect if there is a visible view3d that uses workbench in texture mode.
@@ -99,11 +99,51 @@ static bool has_workbench_in_texture_color(const wmWindowManager *wm,
/** \name Public Node Selection API
* \{ */
+rctf node_frame_rect_inside(const bNode &node)
+{
+ const float margin = 1.5f * U.widget_unit;
+ rctf frame_inside = {
+ node.totr.xmin,
+ node.totr.xmax,
+ node.totr.ymin,
+ node.totr.ymax,
+ };
+
+ BLI_rctf_pad(&frame_inside, -margin, -margin);
+
+ return frame_inside;
+}
+
+static bool node_frame_select_isect_mouse(bNode *node, const float2 &mouse)
+{
+ /* Frame nodes are selectable by their borders (including their whole rect - as for other nodes -
+ * would prevent e.g. box selection of nodes inside that frame). */
+ const rctf frame_inside = node_frame_rect_inside(*node);
+ if (BLI_rctf_isect_pt(&node->totr, mouse.x, mouse.y) &&
+ !BLI_rctf_isect_pt(&frame_inside, mouse.x, mouse.y)) {
+ return true;
+ }
+
+ return false;
+}
+
static bNode *node_under_mouse_select(bNodeTree &ntree, int mx, int my)
{
LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) {
- if (BLI_rctf_isect_pt(&node->totr, mx, my)) {
- return node;
+ switch (node->type) {
+ case NODE_FRAME: {
+ const float2 mouse{(float)mx, (float)my};
+ if (node_frame_select_isect_mouse(node, mouse)) {
+ return node;
+ }
+ break;
+ }
+ default: {
+ if (BLI_rctf_isect_pt(&node->totr, mx, my)) {
+ return node;
+ }
+ break;
+ }
}
}
return nullptr;
@@ -114,15 +154,27 @@ static bNode *node_under_mouse_tweak(bNodeTree &ntree, const float2 &mouse)
using namespace blender::math;
LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) {
- if (node->type == NODE_REROUTE) {
- bNodeSocket *socket = (bNodeSocket *)node->inputs.first;
- const float2 location{socket->locx, socket->locy};
- if (distance(mouse, location) < 24.0f) {
- return node;
+ switch (node->type) {
+ case NODE_REROUTE: {
+ bNodeSocket *socket = (bNodeSocket *)node->inputs.first;
+ const float2 location{socket->locx, socket->locy};
+ if (distance(mouse, location) < 24.0f) {
+ return node;
+ }
+ break;
+ }
+ case NODE_FRAME: {
+ if (node_frame_select_isect_mouse(node, mouse)) {
+ return node;
+ }
+ break;
+ }
+ default: {
+ if (BLI_rctf_isect_pt(&node->totr, mouse.x, mouse.y)) {
+ return node;
+ }
+ break;
}
- }
- if (BLI_rctf_isect_pt(&node->totr, mouse.x, mouse.y)) {
- return node;
}
}
return nullptr;
@@ -687,12 +739,24 @@ static int node_box_select_exec(bContext *C, wmOperator *op)
}
LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
- bool is_inside;
- if (node->type == NODE_FRAME) {
- is_inside = BLI_rctf_inside_rctf(&rectf, &node->totr);
- }
- else {
- is_inside = BLI_rctf_isect(&rectf, &node->totr, nullptr);
+ bool is_inside = false;
+
+ switch (node->type) {
+ case NODE_FRAME: {
+ /* Frame nodes are selectable by their borders (including their whole rect - as for other
+ * nodes - would prevent selection of other nodes inside that frame. */
+ const rctf frame_inside = node_frame_rect_inside(*node);
+ if (BLI_rctf_isect(&rectf, &node->totr, NULL) &&
+ !BLI_rctf_inside_rctf(&frame_inside, &rectf)) {
+ nodeSetSelected(node, select);
+ is_inside = true;
+ }
+ break;
+ }
+ default: {
+ is_inside = BLI_rctf_isect(&rectf, &node->totr, nullptr);
+ break;
+ }
}
if (is_inside) {
@@ -781,8 +845,25 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
- if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) {
- nodeSetSelected(node, select);
+ switch (node->type) {
+ case NODE_FRAME: {
+ /* Frame nodes are selectable by their borders (including their whole rect - as for other
+ * nodes - would prevent selection of _only_ other nodes inside that frame. */
+ rctf frame_inside = node_frame_rect_inside(*node);
+ const float radius_adjusted = (float)radius / zoom;
+ BLI_rctf_pad(&frame_inside, -2.0f * radius_adjusted, -2.0f * radius_adjusted);
+ if (BLI_rctf_isect_circle(&node->totr, offset, radius_adjusted) &&
+ !BLI_rctf_isect_circle(&frame_inside, offset, radius_adjusted)) {
+ nodeSetSelected(node, select);
+ }
+ break;
+ }
+ default: {
+ if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) {
+ nodeSetSelected(node, select);
+ }
+ break;
+ }
}
}
@@ -859,16 +940,35 @@ static bool do_lasso_select_node(bContext *C,
continue;
}
- int screen_co[2];
- const float cent[2] = {BLI_rctf_cent_x(&node->totr), BLI_rctf_cent_y(&node->totr)};
-
- /* marker in screen coords */
- if (UI_view2d_view_to_region_clip(
- &region->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) &&
- BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
- BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) {
- nodeSetSelected(node, select);
- changed = true;
+ switch (node->type) {
+ case NODE_FRAME: {
+ /* Frame nodes are selectable by their borders (including their whole rect - as for other
+ * nodes - would prevent selection of other nodes inside that frame. */
+ rctf rectf;
+ BLI_rctf_rcti_copy(&rectf, &rect);
+ UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
+ const rctf frame_inside = node_frame_rect_inside(*node);
+ if (BLI_rctf_isect(&rectf, &node->totr, NULL) &&
+ !BLI_rctf_inside_rctf(&frame_inside, &rectf)) {
+ nodeSetSelected(node, select);
+ changed = true;
+ }
+ break;
+ }
+ default: {
+ int screen_co[2];
+ const float cent[2] = {BLI_rctf_cent_x(&node->totr), BLI_rctf_cent_y(&node->totr)};
+
+ /* marker in screen coords */
+ if (UI_view2d_view_to_region_clip(
+ &region->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) &&
+ BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
+ BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) {
+ nodeSetSelected(node, select);
+ changed = true;
+ }
+ break;
+ }
}
}
@@ -1309,3 +1409,5 @@ void NODE_OT_find_node(wmOperatorType *ot)
}
/** \} */
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc
index 74c0d2124cd..113e2bd3bb3 100644
--- a/source/blender/editors/space_node/node_templates.cc
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -53,9 +53,10 @@
#include "ED_undo.h"
-using blender::Vector;
using blender::nodes::NodeDeclaration;
+namespace blender::ed::space_node {
+
/************************* Node Socket Manipulation **************************/
/* describes an instance of a node type and a specific socket to link */
@@ -716,9 +717,13 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
ui_node_menu_column(arg, NODE_CLASS_GROUP, N_("Group"));
}
+} // namespace blender::ed::space_node
+
void uiTemplateNodeLink(
uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
{
+ using namespace blender::ed::space_node;
+
uiBlock *block = uiLayoutGetBlock(layout);
NodeLinkArg *arg;
uiBut *but;
@@ -760,6 +765,8 @@ void uiTemplateNodeLink(
}
}
+namespace blender::ed::space_node {
+
/**************************** Node Tree Layout *******************************/
static void ui_node_draw_input(
@@ -912,9 +919,13 @@ static void ui_node_draw_input(
node->flag &= ~NODE_TEST;
}
+} // namespace blender::ed::space_node
+
void uiTemplateNodeView(
uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
{
+ using namespace blender::ed::space_node;
+
bNode *tnode;
if (!ntree) {
diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc
index 12f390c47cd..9d99709a780 100644
--- a/source/blender/editors/space_node/node_view.cc
+++ b/source/blender/editors/space_node/node_view.cc
@@ -55,7 +55,7 @@
#include "node_intern.hh" /* own include */
-using blender::StringRef;
+namespace blender::ed::space_node {
/* -------------------------------------------------------------------- */
/** \name View All Operator
@@ -444,6 +444,8 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
}
}
+} // namespace blender::ed::space_node
+
bool ED_space_node_get_position(
Main *bmain, SpaceNode *snode, struct ARegion *region, const int mval[2], float fpos[2])
{
@@ -526,6 +528,8 @@ bool ED_space_node_color_sample(
return ret;
}
+namespace blender::ed::space_node {
+
static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
{
Main *bmain = CTX_data_main(C);
@@ -783,3 +787,5 @@ void NODE_OT_geometry_node_view_legacy(wmOperatorType *ot)
}
/** \} */
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index 8e986217ffe..4d0c88a5343 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -31,6 +31,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_remap.h"
#include "BKE_node.h"
#include "BKE_screen.h"
@@ -206,6 +207,18 @@ void ED_node_set_active_viewer_key(SpaceNode *snode)
}
}
+void ED_node_cursor_location_get(const SpaceNode *snode, float value[2])
+{
+ copy_v2_v2(value, snode->runtime->cursor);
+}
+
+void ED_node_cursor_location_set(SpaceNode *snode, const float value[2])
+{
+ copy_v2_v2(snode->runtime->cursor, value);
+}
+
+namespace blender::ed::space_node {
+
float2 space_node_group_offset(const SpaceNode &snode)
{
const bNodeTreePath *path = (bNodeTreePath *)snode.treepath.last;
@@ -556,16 +569,6 @@ static void node_toolbar_region_draw(const bContext *C, ARegion *region)
ED_region_panels(C, region);
}
-void ED_node_cursor_location_get(const SpaceNode *snode, float value[2])
-{
- copy_v2_v2(value, snode->runtime->cursor);
-}
-
-void ED_node_cursor_location_set(SpaceNode *snode, const float value[2])
-{
- copy_v2_v2(snode->runtime->cursor, value);
-}
-
static void node_cursor(wmWindow *win, ScrArea *area, ARegion *region)
{
SpaceNode *snode = (SpaceNode *)area->spacedata.first;
@@ -814,8 +817,14 @@ static void node_region_listener(const wmRegionListenerParams *params)
}
}
+} // namespace blender::ed::space_node
+
+/* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */
const char *node_context_dir[] = {
"selected_nodes", "active_node", "light", "material", "world", nullptr};
+
+namespace blender::ed::space_node {
+
static int /*eContextResult*/ node_context(const bContext *C,
const char *member,
bContextDataResult *result)
@@ -888,9 +897,9 @@ static void node_widgets()
WM_gizmogrouptype_append_and_link(gzmap_type, NODE_GGT_backdrop_corner_pin);
}
-static void node_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void node_id_remap_cb(ID *old_id, ID *new_id, void *user_data)
{
- SpaceNode *snode = (SpaceNode *)slink;
+ SpaceNode *snode = static_cast<SpaceNode *>(user_data);
if (snode->id == old_id) {
/* nasty DNA logic for SpaceNode:
@@ -956,6 +965,24 @@ static void node_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, I
}
}
+static void node_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
+{
+ /* Although we should be able to perform all the mappings in a single go this lead to issues when
+ * running the python test cases. Somehow the nodetree/edittree weren't updated to the new
+ * pointers that generated a SEGFAULT.
+ *
+ * To move forward we should perhaps remove snode->edittree and snode->nodetree as they are just
+ * copies of pointers. All usages should be calling a function that will receive the appropriate
+ * instance.
+ *
+ * We could also move a remap address at a time to use the IDRemapper as that should get closer
+ * to cleaner code. See {D13615} for more information about this topic.
+ */
+ BKE_id_remapper_iter(mappings, node_id_remap_cb, slink);
+}
+
static int node_space_subtype_get(ScrArea *area)
{
SpaceNode *snode = (SpaceNode *)area->spacedata.first;
@@ -978,8 +1005,12 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
}
}
+} // namespace blender::ed::space_node
+
void ED_spacetype_node()
{
+ using namespace blender::ed::space_node;
+
SpaceType *st = MEM_cnew<SpaceType>("spacetype node");
ARegionType *art;
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index cac9d4131f8..d97f48bcb68 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -65,7 +65,9 @@ set(SRC
tree/tree_element_id_scene.cc
tree/tree_element_nla.cc
tree/tree_element_overrides.cc
+ tree/tree_element_rna.cc
tree/tree_element_scene_objects.cc
+ tree/tree_element_seq.cc
tree/tree_element_view_layer.cc
outliner_intern.hh
@@ -81,7 +83,9 @@ set(SRC
tree/tree_element_id_scene.hh
tree/tree_element_nla.hh
tree/tree_element_overrides.hh
+ tree/tree_element_rna.hh
tree/tree_element_scene_objects.hh
+ tree/tree_element_seq.hh
tree/tree_element_view_layer.hh
)
@@ -91,9 +95,5 @@ set(LIB
bf_editor_undo
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_space_outliner "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc
index 6de8d9539a9..13c273d1ec9 100644
--- a/source/blender/editors/space_outliner/outliner_draw.cc
+++ b/source/blender/editors/space_outliner/outliner_draw.cc
@@ -81,6 +81,7 @@
#include "outliner_intern.hh"
#include "tree/tree_display.hh"
#include "tree/tree_element.hh"
+#include "tree/tree_element_rna.hh"
using namespace blender::ed::outliner;
@@ -1909,20 +1910,20 @@ static void outliner_draw_rnacols(ARegion *region, int sizex)
static void outliner_draw_rnabuts(
uiBlock *block, ARegion *region, SpaceOutliner *space_outliner, int sizex, ListBase *lb)
{
- PointerRNA *ptr;
+ PointerRNA ptr;
PropertyRNA *prop;
LISTBASE_FOREACH (TreeElement *, te, lb) {
TreeStoreElem *tselem = TREESTORE(te);
if (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin && te->ys <= region->v2d.cur.ymax) {
- if (tselem->type == TSE_RNA_PROPERTY) {
- ptr = &te->rnaptr;
- prop = reinterpret_cast<PropertyRNA *>(te->directdata);
+ if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) {
+ ptr = te_rna_prop->getPointerRNA();
+ prop = te_rna_prop->getPropertyRNA();
if (!TSELEM_OPEN(tselem, space_outliner)) {
if (RNA_property_type(prop) == PROP_POINTER) {
uiBut *but = uiDefAutoButR(block,
- ptr,
+ &ptr,
prop,
-1,
"",
@@ -1935,7 +1936,7 @@ static void outliner_draw_rnabuts(
}
else if (RNA_property_type(prop) == PROP_ENUM) {
uiDefAutoButR(block,
- ptr,
+ &ptr,
prop,
-1,
nullptr,
@@ -1947,7 +1948,7 @@ static void outliner_draw_rnabuts(
}
else {
uiDefAutoButR(block,
- ptr,
+ &ptr,
prop,
-1,
"",
@@ -1959,12 +1960,13 @@ static void outliner_draw_rnabuts(
}
}
}
- else if (tselem->type == TSE_RNA_ARRAY_ELEM) {
- ptr = &te->rnaptr;
- prop = reinterpret_cast<PropertyRNA *>(te->directdata);
+ else if (TreeElementRNAArrayElement *te_rna_array_elem =
+ tree_element_cast<TreeElementRNAArrayElement>(te)) {
+ ptr = te_rna_array_elem->getPointerRNA();
+ prop = te_rna_array_elem->getPropertyRNA();
uiDefAutoButR(block,
- ptr,
+ &ptr,
prop,
te->index,
"",
@@ -2478,9 +2480,6 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case TSE_POSE_CHANNEL:
data.icon = ICON_BONE_DATA;
break;
- case TSE_PROXY:
- data.icon = ICON_GHOST_ENABLED;
- break;
case TSE_R_LAYER_BASE:
data.icon = ICON_RENDERLAYERS;
break;
@@ -2554,15 +2553,19 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case TSE_SEQUENCE_DUP:
data.icon = ICON_SEQ_STRIP_DUPLICATE;
break;
- case TSE_RNA_STRUCT:
- if (RNA_struct_is_ID(te->rnaptr.type)) {
- data.drag_id = (ID *)te->rnaptr.data;
- data.icon = RNA_struct_ui_icon(te->rnaptr.type);
+ case TSE_RNA_STRUCT: {
+ const TreeElementRNAStruct *te_rna_struct = tree_element_cast<TreeElementRNAStruct>(te);
+ const PointerRNA &ptr = te_rna_struct->getPointerRNA();
+
+ if (RNA_struct_is_ID(ptr.type)) {
+ data.drag_id = reinterpret_cast<ID *>(ptr.data);
+ data.icon = RNA_struct_ui_icon(ptr.type);
}
else {
- data.icon = RNA_struct_ui_icon(te->rnaptr.type);
+ data.icon = RNA_struct_ui_icon(ptr.type);
}
break;
+ }
case TSE_LAYER_COLLECTION:
case TSE_SCENE_COLLECTION_BASE:
case TSE_VIEW_COLLECTION_BASE: {
@@ -2629,8 +2632,8 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case OB_LIGHTPROBE:
data.icon = ICON_OUTLINER_OB_LIGHTPROBE;
break;
- case OB_HAIR:
- data.icon = ICON_OUTLINER_OB_HAIR;
+ case OB_CURVES:
+ data.icon = ICON_OUTLINER_OB_CURVES;
break;
case OB_POINTCLOUD:
data.icon = ICON_OUTLINER_OB_POINTCLOUD;
@@ -2743,8 +2746,8 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case ID_GR:
data.icon = ICON_OUTLINER_COLLECTION;
break;
- case ID_HA:
- data.icon = ICON_OUTLINER_DATA_HAIR;
+ case ID_CV:
+ data.icon = ICON_OUTLINER_DATA_CURVES;
break;
case ID_PT:
data.icon = ICON_OUTLINER_DATA_POINTCLOUD;
@@ -3319,8 +3322,9 @@ static void outliner_draw_tree_element(bContext *C,
offsx += 2 * ufac;
}
+ const TreeElementRNAStruct *te_rna_struct = tree_element_cast<TreeElementRNAStruct>(te);
if (ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION) ||
- ((tselem->type == TSE_RNA_STRUCT) && RNA_struct_is_ID(te->rnaptr.type))) {
+ (te_rna_struct && RNA_struct_is_ID(te_rna_struct->getPointerRNA().type))) {
const BIFIconID lib_icon = (BIFIconID)UI_icon_from_library(tselem->id);
if (lib_icon != ICON_NONE) {
UI_icon_draw_alpha(
diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc
index a10dbc94b34..b41b260b14a 100644
--- a/source/blender/editors/space_outliner/outliner_edit.cc
+++ b/source/blender/editors/space_outliner/outliner_edit.cc
@@ -74,6 +74,9 @@
#include "GPU_material.h"
#include "outliner_intern.hh"
+#include "tree/tree_element_rna.hh"
+
+using namespace blender::ed::outliner;
static void outliner_show_active(SpaceOutliner *space_outliner,
ARegion *region,
@@ -1714,11 +1717,6 @@ static void tree_element_to_path(TreeElement *te,
short *UNUSED(groupmode))
{
ListBase hierarchy = {nullptr, nullptr};
- LinkData *ld;
- TreeElement *tem, *temnext;
- TreeStoreElem *tse /* , *tsenext */ /* UNUSED */;
- PointerRNA *ptr, *nextptr;
- PropertyRNA *prop;
char *newpath = nullptr;
/* optimize tricks:
@@ -1738,20 +1736,19 @@ static void tree_element_to_path(TreeElement *te,
*/
/* step 1: flatten out hierarchy of parents into a flat chain */
- for (tem = te->parent; tem; tem = tem->parent) {
- ld = MEM_cnew<LinkData>("LinkData for tree_element_to_path()");
+ for (TreeElement *tem = te->parent; tem; tem = tem->parent) {
+ LinkData *ld = MEM_cnew<LinkData>("LinkData for tree_element_to_path()");
ld->data = tem;
BLI_addhead(&hierarchy, ld);
}
/* step 2: step down hierarchy building the path
* (NOTE: addhead in previous loop was needed so that we can loop like this) */
- for (ld = reinterpret_cast<LinkData *>(hierarchy.first); ld; ld = ld->next) {
+ LISTBASE_FOREACH (LinkData *, ld, &hierarchy) {
/* get data */
- tem = (TreeElement *)ld->data;
- tse = TREESTORE(tem);
- ptr = &tem->rnaptr;
- prop = reinterpret_cast<PropertyRNA *>(tem->directdata);
+ TreeElement *tem = (TreeElement *)ld->data;
+ TreeElementRNACommon *tem_rna = tree_element_cast<TreeElementRNACommon>(tem);
+ PointerRNA ptr = tem_rna->getPointerRNA();
/* check if we're looking for first ID, or appending to path */
if (*id) {
@@ -1759,19 +1756,19 @@ static void tree_element_to_path(TreeElement *te,
* - to prevent memory leaks, we must write to newpath not path,
* then free old path + swap them.
*/
- if (tse->type == TSE_RNA_PROPERTY) {
+ if (TreeElementRNAProperty *tem_rna_prop = tree_element_cast<TreeElementRNAProperty>(tem)) {
+ PropertyRNA *prop = tem_rna_prop->getPropertyRNA();
+
if (RNA_property_type(prop) == PROP_POINTER) {
/* for pointer we just append property name */
- newpath = RNA_path_append(*path, ptr, prop, 0, nullptr);
+ newpath = RNA_path_append(*path, &ptr, prop, 0, nullptr);
}
else if (RNA_property_type(prop) == PROP_COLLECTION) {
char buf[128], *name;
- temnext = (TreeElement *)(ld->next->data);
- // tsenext = TREESTORE(temnext); /* UNUSED */
-
- nextptr = &temnext->rnaptr;
- name = RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf), nullptr);
+ TreeElement *temnext = (TreeElement *)(ld->next->data);
+ PointerRNA nextptr = tree_element_cast<TreeElementRNACommon>(temnext)->getPointerRNA();
+ name = RNA_struct_name_get_alloc(&nextptr, buf, sizeof(buf), nullptr);
if (name) {
/* if possible, use name as a key in the path */
@@ -1789,6 +1786,7 @@ static void tree_element_to_path(TreeElement *te,
if (temsub == temnext) {
break;
}
+ index++;
}
newpath = RNA_path_append(*path, nullptr, prop, index, nullptr);
}
@@ -1808,11 +1806,11 @@ static void tree_element_to_path(TreeElement *te,
else {
/* no ID, so check if entry is RNA-struct,
* and if that RNA-struct is an ID datablock to extract info from. */
- if (tse->type == TSE_RNA_STRUCT) {
+ if (tree_element_cast<TreeElementRNAStruct>(tem)) {
/* ptr->data not ptr->owner_id seems to be the one we want,
* since ptr->data is sometimes the owner of this ID? */
- if (RNA_struct_is_ID(ptr->type)) {
- *id = reinterpret_cast<ID *>(ptr->data);
+ if (RNA_struct_is_ID(ptr.type)) {
+ *id = reinterpret_cast<ID *>(ptr.data);
/* clear path */
if (*path) {
@@ -1827,8 +1825,7 @@ static void tree_element_to_path(TreeElement *te,
/* step 3: if we've got an ID, add the current item to the path */
if (*id) {
/* add the active property to the path */
- ptr = &te->rnaptr;
- prop = reinterpret_cast<PropertyRNA *>(te->directdata);
+ PropertyRNA *prop = tree_element_cast<TreeElementRNACommon>(te)->getPropertyRNA();
/* array checks */
if (tselem->type == TSE_RNA_ARRAY_ELEM) {
@@ -1886,9 +1883,12 @@ static void do_outliner_drivers_editop(SpaceOutliner *space_outliner,
short flag = 0;
short groupmode = KSP_GROUP_KSNAME;
+ TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te);
+ PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL;
+ PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr;
+
/* check if RNA-property described by this selected element is an animatable prop */
- if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) &&
- RNA_property_animateable(&te->rnaptr, reinterpret_cast<PropertyRNA *>(te->directdata))) {
+ if (prop && RNA_property_animateable(&ptr, prop)) {
/* get id + path + index info from the selected element */
tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode);
}
@@ -1901,8 +1901,7 @@ static void do_outliner_drivers_editop(SpaceOutliner *space_outliner,
/* array checks */
if (flag & KSP_FLAG_WHOLE_ARRAY) {
/* entire array was selected, so add drivers for all */
- arraylen = RNA_property_array_length(&te->rnaptr,
- reinterpret_cast<PropertyRNA *>(te->directdata));
+ arraylen = RNA_property_array_length(&ptr, prop);
}
else {
arraylen = array_index;
@@ -2084,8 +2083,10 @@ static void do_outliner_keyingset_editop(SpaceOutliner *space_outliner,
short groupmode = KSP_GROUP_KSNAME;
/* check if RNA-property described by this selected element is an animatable prop */
- if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) &&
- RNA_property_animateable(&te->rnaptr, reinterpret_cast<PropertyRNA *>(te->directdata))) {
+ const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te);
+ PointerRNA ptr = te_rna->getPointerRNA();
+ if (te_rna && te_rna->getPropertyRNA() &&
+ RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) {
/* get id + path + index info from the selected element */
tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode);
}
diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh
index 9065f7f9cc1..efbd8a32716 100644
--- a/source/blender/editors/space_outliner/outliner_intern.hh
+++ b/source/blender/editors/space_outliner/outliner_intern.hh
@@ -27,6 +27,9 @@
#include "RNA_types.h"
+/* Needed for `tree_element_cast()`. */
+#include "tree/tree_element.hh"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -54,10 +57,12 @@ class AbstractTreeDisplay;
class AbstractTreeElement;
} // namespace blender::ed::outliner
+namespace outliner = blender::ed::outliner;
+
struct SpaceOutliner_Runtime {
/** Object to create and manage the tree for a specific display type (View Layers, Scenes,
* Blender File, etc.). */
- std::unique_ptr<blender::ed::outliner::AbstractTreeDisplay> tree_display;
+ std::unique_ptr<outliner::AbstractTreeDisplay> tree_display;
/** Pointers to tree-store elements, grouped by `(id, type, nr)`
* in hash-table for faster searching. */
@@ -90,11 +95,12 @@ typedef struct TreeElement {
struct TreeElement *next, *prev, *parent;
/**
- * Handle to the new C++ object (a derived type of base #AbstractTreeElement) that should replace
- * #TreeElement. Step by step, data should be moved to it and operations based on the type should
- * become virtual methods of the class hierarchy.
+ * The new inheritance based representation of the element (a derived type of base
+ * #AbstractTreeElement) that should eventually replace #TreeElement. Step by step, data should
+ * be moved to it and operations based on the type should become virtual methods of the class
+ * hierarchy.
*/
- std::unique_ptr<blender::ed::outliner::AbstractTreeElement> type;
+ std::unique_ptr<outliner::AbstractTreeElement> abstract_element;
ListBase subtree;
int xs, ys; /* Do selection. */
@@ -104,8 +110,7 @@ typedef struct TreeElement {
short idcode; /* From TreeStore id. */
short xend; /* Width of item display, for select. */
const char *name;
- void *directdata; /* Armature Bones, Base, Sequence, Strip... */
- PointerRNA rnaptr; /* RNA Pointer. */
+ void *directdata; /* Armature Bones, Base, ... */
} TreeElement;
typedef struct TreeElementIcon {
@@ -140,7 +145,7 @@ typedef struct TreeElementIcon {
ID_GD, \
ID_LS, \
ID_LP, \
- ID_HA, \
+ ID_CV, \
ID_PT, \
ID_VO, \
ID_SIM) || /* Only in 'blendfile' mode ... :/ */ \
@@ -225,7 +230,7 @@ typedef enum {
* - not searching into RNA items helps but isn't the complete solution
*/
-#define SEARCHING_OUTLINER(sov) (sov->search_flags & SO_SEARCH_RECURSIVE)
+#define SEARCHING_OUTLINER(sov) ((sov)->search_flags & SO_SEARCH_RECURSIVE)
/* is the current element open? if so we also show children */
#define TSELEM_OPEN(telm, sv) \
@@ -686,3 +691,19 @@ int outliner_context(const struct bContext *C,
#ifdef __cplusplus
}
#endif
+
+namespace blender::ed::outliner {
+
+/**
+ * Helper to safely "cast" a #TreeElement to its new C++ #AbstractTreeElement, if possible.
+ * \return nullptr if the tree-element doesn't match the requested type \a TreeElementT or the
+ * element doesn't hold a C++ #AbstractTreeElement pendant yet.
+ */
+template<typename TreeElementT> TreeElementT *tree_element_cast(const TreeElement *te)
+{
+ static_assert(std::is_base_of_v<AbstractTreeElement, TreeElementT>,
+ "Requested tree-element type must be an AbstractTreeElement");
+ return dynamic_cast<TreeElementT *>(te->abstract_element.get());
+}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc
index c2a7bfb9b37..f256475c0da 100644
--- a/source/blender/editors/space_outliner/outliner_select.cc
+++ b/source/blender/editors/space_outliner/outliner_select.cc
@@ -80,6 +80,9 @@
#include "RNA_define.h"
#include "outliner_intern.hh"
+#include "tree/tree_element_seq.hh"
+
+using namespace blender::ed::outliner;
/**
* \note changes to selection are by convention and not essential.
@@ -676,7 +679,8 @@ static void tree_element_sequence_activate(bContext *C,
TreeElement *te,
const eOLSetState set)
{
- Sequence *seq = (Sequence *)te->directdata;
+ const TreeElementSequence *te_seq = tree_element_cast<TreeElementSequence>(te);
+ Sequence *seq = &te_seq->getSequence();
Editing *ed = SEQ_editing_get(scene);
if (BLI_findindex(ed->seqbasep, seq) != -1) {
@@ -954,7 +958,8 @@ static eOLDrawState tree_element_posegroup_state_get(const ViewLayer *view_layer
static eOLDrawState tree_element_sequence_state_get(const Scene *scene, const TreeElement *te)
{
- const Sequence *seq = (const Sequence *)te->directdata;
+ const TreeElementSequence *te_seq = tree_element_cast<TreeElementSequence>(te);
+ const Sequence *seq = &te_seq->getSequence();
const Editing *ed = scene->ed;
if (ed && ed->act_seq == seq && seq->flag & SELECT) {
@@ -965,7 +970,9 @@ static eOLDrawState tree_element_sequence_state_get(const Scene *scene, const Tr
static eOLDrawState tree_element_sequence_dup_state_get(const TreeElement *te)
{
- const Sequence *seq = (const Sequence *)te->directdata;
+ const TreeElementSequenceStripDuplicate *te_dup =
+ tree_element_cast<TreeElementSequenceStripDuplicate>(te);
+ const Sequence *seq = &te_dup->getSequence();
if (seq->flag & SELECT) {
return OL_DRAWSEL_NORMAL;
}
@@ -1191,7 +1198,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
case ID_AR:
case ID_GD:
case ID_LP:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
context = BCONTEXT_DATA;
@@ -1604,8 +1611,7 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
if (!(te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]))) {
if (deselect_all) {
- outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false);
- changed = true;
+ changed |= outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false);
}
}
/* Don't allow toggle on scene collection */
@@ -1653,17 +1659,19 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
changed = true;
}
- if (changed) {
- if (rebuild_tree) {
- ED_region_tag_redraw(region);
- }
- else {
- ED_region_tag_redraw_no_rebuild(region);
- }
+ if (!changed) {
+ return OPERATOR_CANCELLED;
+ }
- ED_outliner_select_sync_from_outliner(C, space_outliner);
+ if (rebuild_tree) {
+ ED_region_tag_redraw(region);
+ }
+ else {
+ ED_region_tag_redraw_no_rebuild(region);
}
+ ED_outliner_select_sync_from_outliner(C, space_outliner);
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 1c1a4f6f4c2..337649834a4 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -29,8 +29,8 @@
#include "DNA_armature_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
+#include "DNA_curves_types.h"
#include "DNA_gpencil_types.h"
-#include "DNA_hair_types.h"
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
@@ -96,9 +96,13 @@
#include "SEQ_sequencer.h"
#include "outliner_intern.hh"
+#include "tree/tree_element_rna.hh"
+#include "tree/tree_element_seq.hh"
static CLG_LogRef LOG = {"ed.outliner.tools"};
+using namespace blender::ed::outliner;
+
/* -------------------------------------------------------------------- */
/** \name ID/Library/Data Set/Un-link Utilities
* \{ */
@@ -160,7 +164,7 @@ static void get_element_operation_type(
case ID_CF:
case ID_WS:
case ID_LP:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
case ID_SIM:
@@ -258,10 +262,10 @@ static void unlink_material_fn(bContext *UNUSED(C),
totcol = mb->totcol;
matar = mb->mat;
}
- else if (GS(tsep->id->name) == ID_HA) {
- Hair *hair = (Hair *)tsep->id;
- totcol = hair->totcol;
- matar = hair->mat;
+ else if (GS(tsep->id->name) == ID_CV) {
+ Curves *curves = (Curves *)tsep->id;
+ totcol = curves->totcol;
+ matar = curves->mat;
}
else if (GS(tsep->id->name) == ID_PT) {
PointCloud *pointcloud = (PointCloud *)tsep->id;
@@ -760,38 +764,6 @@ static void id_local_fn(bContext *C,
}
}
-static void object_proxy_to_override_convert_fn(bContext *C,
- ReportList *reports,
- Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *UNUSED(user_data))
-{
- BLI_assert(TSE_IS_REAL_ID(tselem));
- ID *id_proxy = tselem->id;
- BLI_assert(GS(id_proxy->name) == ID_OB);
- Object *ob_proxy = (Object *)id_proxy;
- Scene *scene = CTX_data_scene(C);
-
- if (ob_proxy->proxy == nullptr) {
- return;
- }
-
- if (!BKE_lib_override_library_proxy_convert(
- CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy)) {
- BKE_reportf(
- reports,
- RPT_ERROR_INVALID_INPUT,
- "Could not create a library override from proxy '%s' (might use already local data?)",
- ob_proxy->id.name + 2);
- return;
- }
-
- DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_WINDOW, nullptr);
-}
-
struct OutlinerLibOverrideData {
bool do_hierarchy;
/**
@@ -855,8 +827,9 @@ static void id_override_library_create_fn(bContext *C,
if (!ID_IS_LINKED(te->store_elem->id)) {
break;
}
- /* If we'd need to override that aren't ID, but it is not overridable, abort. */
- if (!ID_IS_OVERRIDABLE_LIBRARY(te->store_elem->id)) {
+ /* If some element in the tree needs to be overridden, but its ID is not overridable,
+ * abort. */
+ if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(te->store_elem->id)) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
@@ -869,8 +842,13 @@ static void id_override_library_create_fn(bContext *C,
te->store_elem->id->tag |= LIB_TAG_DOIT;
}
- success = BKE_lib_override_library_create(
- bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, nullptr);
+ success = BKE_lib_override_library_create(bmain,
+ CTX_data_scene(C),
+ CTX_data_view_layer(C),
+ nullptr,
+ id_root,
+ id_reference,
+ nullptr);
}
else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != nullptr;
@@ -1285,7 +1263,8 @@ static void ebone_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem),
static void sequence_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *scene_ptr)
{
- Sequence *seq = (Sequence *)te->directdata;
+ TreeElementSequence *te_seq = tree_element_cast<TreeElementSequence>(te);
+ Sequence *seq = &te_seq->getSequence();
Scene *scene = (Scene *)scene_ptr;
Editing *ed = SEQ_editing_get(scene);
if (BLI_findindex(ed->seqbasep, seq) != -1) {
@@ -1336,10 +1315,16 @@ static void data_select_linked_fn(int event,
TreeStoreElem *UNUSED(tselem),
void *C_v)
{
+ const TreeElementRNAStruct *te_rna_struct = tree_element_cast<TreeElementRNAStruct>(te);
+ if (!te_rna_struct) {
+ return;
+ }
+
if (event == OL_DOP_SELECT_LINKED) {
- if (RNA_struct_is_ID(te->rnaptr.type)) {
+ const PointerRNA &ptr = te_rna_struct->getPointerRNA();
+ if (RNA_struct_is_ID(ptr.type)) {
bContext *C = (bContext *)C_v;
- ID *id = reinterpret_cast<ID *>(te->rnaptr.data);
+ ID *id = reinterpret_cast<ID *>(ptr.data);
ED_object_select_linked_by_id(C, id);
}
@@ -1520,7 +1505,6 @@ enum {
OL_OP_SELECT_HIERARCHY,
OL_OP_REMAP,
OL_OP_RENAME,
- OL_OP_PROXY_TO_OVERRIDE_CONVERT,
};
static const EnumPropertyItem prop_object_op_types[] = {
@@ -1533,11 +1517,6 @@ static const EnumPropertyItem prop_object_op_types[] = {
"Remap Users",
"Make all users of selected data-blocks to use instead a new chosen one"},
{OL_OP_RENAME, "RENAME", 0, "Rename", ""},
- {OL_OP_PROXY_TO_OVERRIDE_CONVERT,
- "OBJECT_PROXY_TO_OVERRIDE",
- 0,
- "Convert Proxy to Override",
- "Convert a Proxy object to a full library override, including all its dependencies"},
{0, nullptr, 0, nullptr, nullptr},
};
@@ -1602,15 +1581,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn);
str = "Rename Object";
}
- else if (event == OL_OP_PROXY_TO_OVERRIDE_CONVERT) {
- outliner_do_object_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- object_proxy_to_override_convert_fn);
- str = "Convert Proxy to Override";
- }
else {
BLI_assert(0);
return OPERATOR_CANCELLED;
@@ -1782,7 +1752,6 @@ enum eOutlinerIdOpTypes {
OUTLINER_IDOP_LOCAL,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT,
OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
@@ -1824,11 +1793,6 @@ static const EnumPropertyItem prop_id_op_types[] = {
0,
"Make Library Override Hierarchy",
"Make a local override of this linked data-block, and its hierarchy of dependencies"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT,
- "OVERRIDE_LIBRARY_PROXY_CONVERT",
- 0,
- "Convert Proxy to Override",
- "Convert a Proxy object to a full library override, including all its dependencies"},
{OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
"OVERRIDE_LIBRARY_RESET",
0,
@@ -1901,16 +1865,6 @@ static bool outliner_id_operation_item_poll(bContext *C,
return true;
}
return false;
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT: {
- if (GS(tselem->id->name) == ID_OB) {
- Object *ob = (Object *)tselem->id;
-
- if ((ob != nullptr) && (ob->proxy != nullptr)) {
- return true;
- }
- }
- return false;
- }
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET:
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY:
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY:
@@ -2087,16 +2041,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
ED_undo_push(C, "Overridden Data Hierarchy");
break;
}
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT: {
- outliner_do_object_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- object_proxy_to_override_convert_fn);
- ED_undo_push(C, "Convert Proxy to Override");
- break;
- }
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: {
OutlinerLibOverrideData override_data{};
outliner_do_libdata_operation(C,
diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc
index edf9abdd0b8..60f5437ad88 100644
--- a/source/blender/editors/space_outliner/outliner_tree.cc
+++ b/source/blender/editors/space_outliner/outliner_tree.cc
@@ -32,9 +32,9 @@
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
+#include "DNA_curves_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
-#include "DNA_hair_types.h"
#include "DNA_key_types.h"
#include "DNA_light_types.h"
#include "DNA_lightprobe_types.h"
@@ -217,7 +217,7 @@ void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree)
if (element->flag & TE_FREE_NAME) {
MEM_freeN((void *)element->name);
}
- element->type = nullptr;
+ element->abstract_element = nullptr;
MEM_delete(element);
}
@@ -302,10 +302,6 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner,
/* FIXME: add a special type for this. */
outliner_add_element(space_outliner, &te->subtree, ob->poselib, te, TSE_SOME_ID, 0);
- if (ob->proxy && !ID_IS_LINKED(ob)) {
- outliner_add_element(space_outliner, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
- }
-
outliner_add_element(space_outliner, &te->subtree, ob->data, te, TSE_SOME_ID, 0);
if (ob->pose) {
@@ -777,10 +773,10 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
}
break;
}
- case ID_HA: {
- Hair *hair = (Hair *)id;
- if (outliner_animdata_test(hair->adt)) {
- outliner_add_element(space_outliner, &te->subtree, hair, te, TSE_ANIM_DATA, 0);
+ case ID_CV: {
+ Curves *curves = (Curves *)id;
+ if (outliner_animdata_test(curves->adt)) {
+ outliner_add_element(space_outliner, &te->subtree, curves, te, TSE_ANIM_DATA, 0);
}
break;
}
@@ -828,7 +824,7 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
}
}
else if (type == TSE_GP_LAYER) {
- /* idv is the layer its self */
+ /* idv is the layer itself */
id = TREESTORE(parent)->id;
}
@@ -860,10 +856,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
te->parent = parent;
te->index = index; /* For data arrays. */
- /* New C++ based type handle. Only some support this, eventually this should replace
- * `TreeElement` entirely. */
- te->type = AbstractTreeElement::createFromType(type, *te, idv);
- if (te->type) {
+ /* New inheritance based element representation. Not all element types support this yet,
+ * eventually it should replace #TreeElement entirely. */
+ te->abstract_element = AbstractTreeElement::createFromType(type, *te, idv);
+ if (te->abstract_element) {
/* Element types ported to the new design are expected to have their name set at this point! */
BLI_assert(te->name != nullptr);
}
@@ -887,12 +883,12 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
/* pass */
}
else if (type == TSE_SOME_ID) {
- if (!te->type) {
+ if (!te->abstract_element) {
BLI_assert_msg(0, "Expected this ID type to be ported to new Outliner tree-element design");
}
}
else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) {
- if (!te->type) {
+ if (!te->abstract_element) {
BLI_assert_msg(0,
"Expected override types to be ported to new Outliner tree-element design");
}
@@ -903,20 +899,20 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
/* The new type design sets the name already, don't override that here. We need to figure out
* how to deal with the idcode for non-TSE_SOME_ID types still. Some rely on it... */
- if (!te->type) {
+ if (!te->abstract_element) {
te->name = id->name + 2; /* Default, can be overridden by Library or non-ID data. */
}
te->idcode = GS(id->name);
}
- if (te->type && te->type->isExpandValid()) {
- tree_element_expand(*te->type, *space_outliner);
+ if (te->abstract_element && te->abstract_element->isExpandValid()) {
+ tree_element_expand(*te->abstract_element, *space_outliner);
}
else if (type == TSE_SOME_ID) {
/* ID types not (fully) ported to new design yet. */
- if (te->type->expandPoll(*space_outliner)) {
+ if (te->abstract_element->expandPoll(*space_outliner)) {
outliner_add_id_contents(space_outliner, te, tselem, id);
- te->type->postExpand(*space_outliner);
+ te->abstract_element->postExpand(*space_outliner);
}
}
else if (ELEM(type,
@@ -925,195 +921,14 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
TSE_NLA,
TSE_NLA_ACTION,
TSE_NLA_TRACK,
- TSE_GP_LAYER)) {
- /* Should already use new AbstractTreeElement design. */
- BLI_assert(0);
- }
- else if (type == TSE_SEQUENCE) {
- Sequence *seq = (Sequence *)idv;
-
- /*
- * The idcode is a little hack, but the outliner
- * only check te->idcode if te->type is equal to zero,
- * so this is "safe".
- */
- te->idcode = seq->type;
- te->directdata = seq;
- te->name = seq->name + 2;
-
- if (!(seq->type & SEQ_TYPE_EFFECT)) {
- /*
- * This work like the sequence.
- * If the sequence have a name (not default name)
- * show it, in other case put the filename.
- */
-
- if (seq->type == SEQ_TYPE_META) {
- LISTBASE_FOREACH (Sequence *, p, &seq->seqbase) {
- outliner_add_element(space_outliner, &te->subtree, (void *)p, te, TSE_SEQUENCE, index);
- }
- }
- else {
- outliner_add_element(
- space_outliner, &te->subtree, (void *)seq->strip, te, TSE_SEQ_STRIP, index);
- }
- }
- }
- else if (type == TSE_SEQ_STRIP) {
- Strip *strip = (Strip *)idv;
-
- if (strip->dir[0] != '\0') {
- te->name = strip->dir;
- }
- else {
- te->name = IFACE_("Strip None");
- }
- te->directdata = strip;
- }
- else if (type == TSE_SEQUENCE_DUP) {
- Sequence *seq = (Sequence *)idv;
-
- te->idcode = seq->type;
- te->directdata = seq;
- te->name = seq->strip->stripdata->name;
- }
- else if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
- PointerRNA *ptr = (PointerRNA *)idv;
-
- /* Don't display arrays larger, weak but index is stored as a short,
- * also the outliner isn't intended for editing such large data-sets. */
- BLI_STATIC_ASSERT(sizeof(te->index) == 2, "Index is no longer short!")
- const int tot_limit = SHRT_MAX;
-
- /* we do lazy build, for speed and to avoid infinite recursion */
-
- if (ptr->data == nullptr) {
- te->name = IFACE_("(empty)");
- }
- else if (type == TSE_RNA_STRUCT) {
- /* struct */
- te->name = RNA_struct_name_get_alloc(ptr, nullptr, 0, nullptr);
-
- if (te->name) {
- te->flag |= TE_FREE_NAME;
- }
- else {
- te->name = RNA_struct_ui_name(ptr->type);
- }
-
- /* If searching don't expand RNA entries */
- if (SEARCHING_OUTLINER(space_outliner) && BLI_strcasecmp("RNA", te->name) == 0) {
- tselem->flag &= ~TSE_CHILDSEARCH;
- }
-
- PropertyRNA *iterprop = RNA_struct_iterator_property(ptr->type);
- int tot = RNA_property_collection_length(ptr, iterprop);
- CLAMP_MAX(tot, tot_limit);
-
- /* auto open these cases */
- if (!parent || (RNA_property_type(reinterpret_cast<PropertyRNA *>(parent->directdata))) ==
- PROP_POINTER) {
- if (!tselem->used) {
- tselem->flag &= ~TSE_CLOSED;
- }
- }
-
- if (TSELEM_OPEN(tselem, space_outliner)) {
- for (int a = 0; a < tot; a++) {
- PointerRNA propptr;
- RNA_property_collection_lookup_int(ptr, iterprop, a, &propptr);
- if (!(RNA_property_flag(reinterpret_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) {
- outliner_add_element(
- space_outliner, &te->subtree, (void *)ptr, te, TSE_RNA_PROPERTY, a);
- }
- }
- }
- else if (tot) {
- te->flag |= TE_LAZY_CLOSED;
- }
-
- te->rnaptr = *ptr;
- }
- else if (type == TSE_RNA_PROPERTY) {
- /* property */
- PointerRNA propptr;
- PropertyRNA *iterprop = RNA_struct_iterator_property(ptr->type);
- RNA_property_collection_lookup_int(ptr, iterprop, index, &propptr);
-
- PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(propptr.data);
- PropertyType proptype = RNA_property_type(prop);
-
- te->name = RNA_property_ui_name(prop);
- te->directdata = prop;
- te->rnaptr = *ptr;
-
- /* If searching don't expand RNA entries */
- if (SEARCHING_OUTLINER(space_outliner) && BLI_strcasecmp("RNA", te->name) == 0) {
- tselem->flag &= ~TSE_CHILDSEARCH;
- }
-
- if (proptype == PROP_POINTER) {
- PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
-
- if (pptr.data) {
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_add_element(
- space_outliner, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, -1);
- }
- else {
- te->flag |= TE_LAZY_CLOSED;
- }
- }
- }
- else if (proptype == PROP_COLLECTION) {
- int tot = RNA_property_collection_length(ptr, prop);
- CLAMP_MAX(tot, tot_limit);
-
- if (TSELEM_OPEN(tselem, space_outliner)) {
- for (int a = 0; a < tot; a++) {
- PointerRNA pptr;
- RNA_property_collection_lookup_int(ptr, prop, a, &pptr);
- outliner_add_element(
- space_outliner, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, a);
- }
- }
- else if (tot) {
- te->flag |= TE_LAZY_CLOSED;
- }
- }
- else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
- int tot = RNA_property_array_length(ptr, prop);
- CLAMP_MAX(tot, tot_limit);
-
- if (TSELEM_OPEN(tselem, space_outliner)) {
- for (int a = 0; a < tot; a++) {
- outliner_add_element(
- space_outliner, &te->subtree, (void *)ptr, te, TSE_RNA_ARRAY_ELEM, a);
- }
- }
- else if (tot) {
- te->flag |= TE_LAZY_CLOSED;
- }
- }
- }
- else if (type == TSE_RNA_ARRAY_ELEM) {
- PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(parent->directdata);
-
- te->directdata = prop;
- te->rnaptr = *ptr;
- te->index = index;
-
- char c = RNA_property_array_item_char(prop, index);
-
- te->name = reinterpret_cast<char *>(MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName"));
- if (c) {
- sprintf((char *)te->name, " %c", c);
- }
- else {
- sprintf((char *)te->name, " %d", index + 1);
- }
- te->flag |= TE_FREE_NAME;
- }
+ TSE_GP_LAYER,
+ TSE_RNA_STRUCT,
+ TSE_RNA_PROPERTY,
+ TSE_RNA_ARRAY_ELEM,
+ TSE_SEQUENCE,
+ TSE_SEQ_STRIP,
+ TSE_SEQUENCE_DUP)) {
+ BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design");
}
if (tree_element_warnings_get(te, nullptr, nullptr)) {
diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc
index ea07f1d4611..0fb17fa3f47 100644
--- a/source/blender/editors/space_outliner/space_outliner.cc
+++ b/source/blender/editors/space_outliner/space_outliner.cc
@@ -31,6 +31,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
+#include "BKE_lib_remap.h"
#include "BKE_outliner_treehash.h"
#include "BKE_screen.h"
@@ -405,45 +406,49 @@ static SpaceLink *outliner_duplicate(SpaceLink *sl)
return (SpaceLink *)space_outliner_new;
}
-static void outliner_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id)
+static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRemapper *mappings)
{
SpaceOutliner *space_outliner = (SpaceOutliner *)slink;
- /* Some early out checks. */
- if (!TREESTORE_ID_TYPE(old_id)) {
- return; /* ID type is not used by outliner. */
- }
+ BKE_id_remapper_apply(mappings, (ID **)&space_outliner->search_tse.id, ID_REMAP_APPLY_DEFAULT);
- if (space_outliner->search_tse.id == old_id) {
- space_outliner->search_tse.id = new_id;
+ if (!space_outliner->treestore) {
+ return;
}
- if (space_outliner->treestore) {
- TreeStoreElem *tselem;
- BLI_mempool_iter iter;
- bool changed = false;
-
- BLI_mempool_iternew(space_outliner->treestore, &iter);
- while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
- if (tselem->id == old_id) {
- tselem->id = new_id;
+ TreeStoreElem *tselem;
+ BLI_mempool_iter iter;
+ bool changed = false;
+ bool unassigned = false;
+
+ BLI_mempool_iternew(space_outliner->treestore, &iter);
+ while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
+ switch (BKE_id_remapper_apply(mappings, &tselem->id, ID_REMAP_APPLY_DEFAULT)) {
+ case ID_REMAP_RESULT_SOURCE_REMAPPED:
changed = true;
- }
+ break;
+ case ID_REMAP_RESULT_SOURCE_UNASSIGNED:
+ changed = true;
+ unassigned = true;
+ break;
+ case ID_REMAP_RESULT_SOURCE_UNAVAILABLE:
+ case ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE:
+ break;
}
+ }
- /* Note that the Outliner may not be the active editor of the area, and hence not initialized.
- * So runtime data might not have been created yet. */
- if (space_outliner->runtime && space_outliner->runtime->treehash && changed) {
- /* rebuild hash table, because it depends on ids too */
- /* postpone a full rebuild because this can be called many times on-free */
- space_outliner->storeflag |= SO_TREESTORE_REBUILD;
-
- if (new_id == nullptr) {
- /* Redraw is needed when removing data for multiple outlines show the same data.
- * without this, the stale data won't get fully flushed when this outliner
- * is not the active outliner the user is interacting with. See T85976. */
- ED_area_tag_redraw(area);
- }
+ /* Note that the Outliner may not be the active editor of the area, and hence not initialized.
+ * So runtime data might not have been created yet. */
+ if (space_outliner->runtime && space_outliner->runtime->treehash && changed) {
+ /* rebuild hash table, because it depends on ids too */
+ /* postpone a full rebuild because this can be called many times on-free */
+ space_outliner->storeflag |= SO_TREESTORE_REBUILD;
+
+ if (unassigned) {
+ /* Redraw is needed when removing data for multiple outlines show the same data.
+ * without this, the stale data won't get fully flushed when this outliner
+ * is not the active outliner the user is interacting with. See T85976. */
+ ED_area_tag_redraw(area);
}
}
}
diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh
index 8005b7d17dd..68f0f9c562d 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.hh
+++ b/source/blender/editors/space_outliner/tree/tree_display.hh
@@ -38,8 +38,8 @@
struct ID;
struct LayerCollection;
-struct ListBase;
struct Library;
+struct ListBase;
struct Main;
struct Scene;
struct Sequence;
diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc
index ea78f70f86d..5685d8964f5 100644
--- a/source/blender/editors/space_outliner/tree/tree_element.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element.cc
@@ -33,7 +33,9 @@
#include "tree_element_id.hh"
#include "tree_element_nla.hh"
#include "tree_element_overrides.hh"
+#include "tree_element_rna.hh"
#include "tree_element_scene_objects.hh"
+#include "tree_element_seq.hh"
#include "tree_element_view_layer.hh"
#include "../outliner_intern.hh"
@@ -86,6 +88,23 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i
case TSE_LIBRARY_OVERRIDE:
return std::make_unique<TreeElementOverridesProperty>(
legacy_te, *static_cast<TreeElementOverridesData *>(idv));
+ case TSE_RNA_STRUCT:
+ return std::make_unique<TreeElementRNAStruct>(legacy_te,
+ *reinterpret_cast<PointerRNA *>(idv));
+ case TSE_RNA_PROPERTY:
+ return std::make_unique<TreeElementRNAProperty>(
+ legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index);
+ case TSE_RNA_ARRAY_ELEM:
+ return std::make_unique<TreeElementRNAArrayElement>(
+ legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index);
+ case TSE_SEQUENCE:
+ return std::make_unique<TreeElementSequence>(legacy_te, *reinterpret_cast<Sequence *>(idv));
+ case TSE_SEQ_STRIP:
+ return std::make_unique<TreeElementSequenceStrip>(legacy_te,
+ *reinterpret_cast<Strip *>(idv));
+ case TSE_SEQUENCE_DUP:
+ return std::make_unique<TreeElementSequenceStripDuplicate>(
+ legacy_te, *reinterpret_cast<Sequence *>(idv));
default:
break;
}
diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc
index afbbd171cf4..3289cb8ac76 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_id.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc
@@ -69,7 +69,7 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
case ID_LP:
case ID_GD:
case ID_WS:
- case ID_HA:
+ case ID_CV:
case ID_PT:
case ID_VO:
case ID_SIM:
diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.cc b/source/blender/editors/space_outliner/tree/tree_element_rna.cc
new file mode 100644
index 00000000000..7a9f1b6f0fa
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_element_rna.cc
@@ -0,0 +1,268 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include <climits>
+#include <iostream>
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_outliner_types.h"
+#include "DNA_space_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "../outliner_intern.hh"
+
+#include "tree_element_rna.hh"
+
+namespace blender::ed::outliner {
+
+/* Don't display arrays larger, weak but index is stored as a short,
+ * also the outliner isn't intended for editing such large data-sets. */
+BLI_STATIC_ASSERT(sizeof(TreeElement::index) == 2, "Index is no longer short!")
+
+/* -------------------------------------------------------------------- */
+/* Common functionality (#TreeElementRNACommon Base Class) */
+
+TreeElementRNACommon::TreeElementRNACommon(TreeElement &legacy_te, PointerRNA &rna_ptr)
+ : AbstractTreeElement(legacy_te), rna_ptr_(rna_ptr)
+{
+ /* Create an empty tree-element. */
+ if (!isRNAValid()) {
+ legacy_te_.name = IFACE_("(empty)");
+ return;
+ }
+}
+
+bool TreeElementRNACommon::isExpandValid() const
+{
+ return true;
+}
+
+bool TreeElementRNACommon::isRNAValid() const
+{
+ return rna_ptr_.data != nullptr;
+}
+
+bool TreeElementRNACommon::expandPoll(const SpaceOutliner &) const
+{
+ return isRNAValid();
+}
+
+const PointerRNA &TreeElementRNACommon::getPointerRNA() const
+{
+ return rna_ptr_;
+}
+
+PropertyRNA *TreeElementRNACommon::getPropertyRNA() const
+{
+ return nullptr;
+}
+
+/* -------------------------------------------------------------------- */
+/* RNA Struct */
+
+TreeElementRNAStruct::TreeElementRNAStruct(TreeElement &legacy_te, PointerRNA &rna_ptr)
+ : TreeElementRNACommon(legacy_te, rna_ptr)
+{
+ BLI_assert(legacy_te.store_elem->type == TSE_RNA_STRUCT);
+
+ if (!isRNAValid()) {
+ return;
+ }
+
+ legacy_te_.name = RNA_struct_name_get_alloc(&rna_ptr, nullptr, 0, nullptr);
+ if (legacy_te_.name) {
+ legacy_te_.flag |= TE_FREE_NAME;
+ }
+ else {
+ legacy_te_.name = RNA_struct_ui_name(rna_ptr.type);
+ }
+}
+
+void TreeElementRNAStruct::expand(SpaceOutliner &space_outliner) const
+{
+ TreeStoreElem &tselem = *TREESTORE(&legacy_te_);
+ PointerRNA ptr = rna_ptr_;
+
+ /* If searching don't expand RNA entries */
+ if (SEARCHING_OUTLINER(&space_outliner) && BLI_strcasecmp("RNA", legacy_te_.name) == 0) {
+ tselem.flag &= ~TSE_CHILDSEARCH;
+ }
+
+ PropertyRNA *iterprop = RNA_struct_iterator_property(ptr.type);
+ int tot = RNA_property_collection_length(&ptr, iterprop);
+ CLAMP_MAX(tot, max_index);
+
+ TreeElementRNAProperty *parent_prop_te = legacy_te_.parent ?
+ tree_element_cast<TreeElementRNAProperty>(
+ legacy_te_.parent) :
+ nullptr;
+ /* auto open these cases */
+ if (!parent_prop_te || (RNA_property_type(parent_prop_te->getPropertyRNA()) == PROP_POINTER)) {
+ if (!tselem.used) {
+ tselem.flag &= ~TSE_CLOSED;
+ }
+ }
+
+ if (TSELEM_OPEN(&tselem, &space_outliner)) {
+ for (int index = 0; index < tot; index++) {
+ PointerRNA propptr;
+ RNA_property_collection_lookup_int(&ptr, iterprop, index, &propptr);
+ if (!(RNA_property_flag(reinterpret_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) {
+ outliner_add_element(
+ &space_outliner, &legacy_te_.subtree, &ptr, &legacy_te_, TSE_RNA_PROPERTY, index);
+ }
+ }
+ }
+ else if (tot) {
+ legacy_te_.flag |= TE_LAZY_CLOSED;
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/* RNA Property */
+
+TreeElementRNAProperty::TreeElementRNAProperty(TreeElement &legacy_te,
+ PointerRNA &rna_ptr,
+ const int index)
+ : TreeElementRNACommon(legacy_te, rna_ptr)
+{
+ BLI_assert(legacy_te.store_elem->type == TSE_RNA_PROPERTY);
+
+ if (!isRNAValid()) {
+ return;
+ }
+
+ PointerRNA propptr;
+ PropertyRNA *iterprop = RNA_struct_iterator_property(rna_ptr.type);
+ RNA_property_collection_lookup_int(&rna_ptr, iterprop, index, &propptr);
+
+ PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(propptr.data);
+
+ legacy_te_.name = RNA_property_ui_name(prop);
+ rna_prop_ = prop;
+}
+
+void TreeElementRNAProperty::expand(SpaceOutliner &space_outliner) const
+{
+ TreeStoreElem &tselem = *TREESTORE(&legacy_te_);
+ PointerRNA rna_ptr = rna_ptr_;
+ PropertyType proptype = RNA_property_type(rna_prop_);
+
+ /* If searching don't expand RNA entries */
+ if (SEARCHING_OUTLINER(&space_outliner) && BLI_strcasecmp("RNA", legacy_te_.name) == 0) {
+ tselem.flag &= ~TSE_CHILDSEARCH;
+ }
+
+ if (proptype == PROP_POINTER) {
+ PointerRNA pptr = RNA_property_pointer_get(&rna_ptr, rna_prop_);
+
+ if (pptr.data) {
+ if (TSELEM_OPEN(&tselem, &space_outliner)) {
+ outliner_add_element(
+ &space_outliner, &legacy_te_.subtree, &pptr, &legacy_te_, TSE_RNA_STRUCT, -1);
+ }
+ else {
+ legacy_te_.flag |= TE_LAZY_CLOSED;
+ }
+ }
+ }
+ else if (proptype == PROP_COLLECTION) {
+ int tot = RNA_property_collection_length(&rna_ptr, rna_prop_);
+ CLAMP_MAX(tot, max_index);
+
+ if (TSELEM_OPEN(&tselem, &space_outliner)) {
+ for (int index = 0; index < tot; index++) {
+ PointerRNA pptr;
+ RNA_property_collection_lookup_int(&rna_ptr, rna_prop_, index, &pptr);
+ outliner_add_element(
+ &space_outliner, &legacy_te_.subtree, &pptr, &legacy_te_, TSE_RNA_STRUCT, index);
+ }
+ }
+ else if (tot) {
+ legacy_te_.flag |= TE_LAZY_CLOSED;
+ }
+ }
+ else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
+ int tot = RNA_property_array_length(&rna_ptr, rna_prop_);
+ CLAMP_MAX(tot, max_index);
+
+ if (TSELEM_OPEN(&tselem, &space_outliner)) {
+ for (int index = 0; index < tot; index++) {
+ outliner_add_element(&space_outliner,
+ &legacy_te_.subtree,
+ &rna_ptr,
+ &legacy_te_,
+ TSE_RNA_ARRAY_ELEM,
+ index);
+ }
+ }
+ else if (tot) {
+ legacy_te_.flag |= TE_LAZY_CLOSED;
+ }
+ }
+}
+
+PropertyRNA *TreeElementRNAProperty::getPropertyRNA() const
+{
+ return rna_prop_;
+}
+
+/* -------------------------------------------------------------------- */
+/* RNA Array Element */
+
+TreeElementRNAArrayElement::TreeElementRNAArrayElement(TreeElement &legacy_te,
+ PointerRNA &rna_ptr,
+ const int index)
+ : TreeElementRNACommon(legacy_te, rna_ptr)
+{
+ BLI_assert(legacy_te.store_elem->type == TSE_RNA_ARRAY_ELEM);
+
+ BLI_assert(legacy_te.parent && (legacy_te.parent->store_elem->type == TSE_RNA_PROPERTY));
+ legacy_te_.index = index;
+
+ char c = RNA_property_array_item_char(TreeElementRNAArrayElement::getPropertyRNA(), index);
+
+ legacy_te_.name = reinterpret_cast<char *>(
+ MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName"));
+ if (c) {
+ sprintf((char *)legacy_te_.name, " %c", c);
+ }
+ else {
+ sprintf((char *)legacy_te_.name, " %d", index + 1);
+ }
+ legacy_te_.flag |= TE_FREE_NAME;
+}
+
+PropertyRNA *TreeElementRNAArrayElement::getPropertyRNA() const
+{
+ /* Forward query to the parent (which is expected to be a #TreeElementRNAProperty). */
+ const TreeElementRNAProperty *parent_prop_te = tree_element_cast<TreeElementRNAProperty>(
+ legacy_te_.parent);
+ return parent_prop_te ? parent_prop_te->getPropertyRNA() : nullptr;
+}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.hh b/source/blender/editors/space_outliner/tree/tree_element_rna.hh
new file mode 100644
index 00000000000..1f107ddbf88
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_element_rna.hh
@@ -0,0 +1,86 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#pragma once
+
+#include <limits>
+
+#include "RNA_types.h"
+
+#include "tree_element.hh"
+
+struct PointerRNA;
+
+namespace blender::ed::outliner {
+
+/**
+ * Base class for common behavior of RNA tree elements.
+ */
+class TreeElementRNACommon : public AbstractTreeElement {
+ protected:
+ constexpr static int max_index = std::numeric_limits<short>::max();
+ PointerRNA rna_ptr_;
+
+ public:
+ TreeElementRNACommon(TreeElement &legacy_te, PointerRNA &rna_ptr);
+ bool isExpandValid() const override;
+ bool expandPoll(const SpaceOutliner &) const override;
+
+ const PointerRNA &getPointerRNA() const;
+ /**
+ * If this element represents a property or is part of a property (array element), this returns
+ * the property. Otherwise nullptr.
+ */
+ virtual PropertyRNA *getPropertyRNA() const;
+
+ bool isRNAValid() const;
+};
+
+/* -------------------------------------------------------------------- */
+
+class TreeElementRNAStruct : public TreeElementRNACommon {
+ public:
+ TreeElementRNAStruct(TreeElement &legacy_te, PointerRNA &rna_ptr);
+ void expand(SpaceOutliner &space_outliner) const override;
+};
+
+/* -------------------------------------------------------------------- */
+
+class TreeElementRNAProperty : public TreeElementRNACommon {
+ private:
+ PropertyRNA *rna_prop_ = nullptr;
+
+ public:
+ TreeElementRNAProperty(TreeElement &legacy_te, PointerRNA &rna_ptr, int index);
+ void expand(SpaceOutliner &space_outliner) const override;
+
+ PropertyRNA *getPropertyRNA() const override;
+};
+
+/* -------------------------------------------------------------------- */
+
+class TreeElementRNAArrayElement : public TreeElementRNACommon {
+ public:
+ TreeElementRNAArrayElement(TreeElement &legacy_te, PointerRNA &rna_ptr, int index);
+
+ PropertyRNA *getPropertyRNA() const override;
+};
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_seq.cc b/source/blender/editors/space_outliner/tree/tree_element_seq.cc
new file mode 100644
index 00000000000..8d0b4c140c7
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_element_seq.cc
@@ -0,0 +1,111 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include "DNA_outliner_types.h"
+#include "DNA_sequence_types.h"
+
+#include "BLI_listbase.h"
+
+#include "BLT_translation.h"
+
+#include "../outliner_intern.hh"
+#include "tree_element_seq.hh"
+
+namespace blender::ed::outliner {
+
+TreeElementSequence::TreeElementSequence(TreeElement &legacy_te, Sequence &sequence)
+ : AbstractTreeElement(legacy_te), sequence_(sequence)
+{
+ BLI_assert(legacy_te.store_elem->type == TSE_SEQUENCE);
+
+ /*
+ * The idcode is a little hack, but the outliner
+ * only check te->idcode if te->type is equal to zero,
+ * so this is "safe".
+ */
+ legacy_te.idcode = sequence_.type;
+ legacy_te.name = sequence_.name + 2;
+}
+
+bool TreeElementSequence::expandPoll(const SpaceOutliner & /*space_outliner*/) const
+{
+ return !(sequence_.type & SEQ_TYPE_EFFECT);
+}
+
+void TreeElementSequence::expand(SpaceOutliner &space_outliner) const
+{
+ /*
+ * This work like the sequence.
+ * If the sequence have a name (not default name)
+ * show it, in other case put the filename.
+ */
+
+ if (sequence_.type == SEQ_TYPE_META) {
+ LISTBASE_FOREACH (Sequence *, child, &sequence_.seqbase) {
+ outliner_add_element(
+ &space_outliner, &legacy_te_.subtree, child, &legacy_te_, TSE_SEQUENCE, 0);
+ }
+ }
+ else {
+ outliner_add_element(
+ &space_outliner, &legacy_te_.subtree, sequence_.strip, &legacy_te_, TSE_SEQ_STRIP, 0);
+ }
+}
+
+Sequence &TreeElementSequence::getSequence() const
+{
+ return sequence_;
+}
+
+/* -------------------------------------------------------------------- */
+/* Strip */
+
+TreeElementSequenceStrip::TreeElementSequenceStrip(TreeElement &legacy_te, Strip &strip)
+ : AbstractTreeElement(legacy_te)
+{
+ BLI_assert(legacy_te.store_elem->type == TSE_SEQ_STRIP);
+
+ if (strip.dir[0] != '\0') {
+ legacy_te_.name = strip.dir;
+ }
+ else {
+ legacy_te_.name = IFACE_("Strip None");
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/* Strip Duplicate */
+
+TreeElementSequenceStripDuplicate::TreeElementSequenceStripDuplicate(TreeElement &legacy_te,
+ Sequence &sequence)
+ : AbstractTreeElement(legacy_te), sequence_(sequence)
+{
+ BLI_assert(legacy_te.store_elem->type == TSE_SEQUENCE_DUP);
+
+ legacy_te_.idcode = sequence.type;
+ legacy_te_.name = sequence.strip->stripdata->name;
+}
+
+Sequence &TreeElementSequenceStripDuplicate::getSequence() const
+{
+ return sequence_;
+}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_seq.hh b/source/blender/editors/space_outliner/tree/tree_element_seq.hh
new file mode 100644
index 00000000000..2b334b5b7fa
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_element_seq.hh
@@ -0,0 +1,60 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#pragma once
+
+#include "tree_element.hh"
+
+struct Sequence;
+struct Strip;
+
+namespace blender::ed::outliner {
+
+class TreeElementSequence : public AbstractTreeElement {
+ Sequence &sequence_;
+
+ public:
+ TreeElementSequence(TreeElement &legacy_te, Sequence &sequence);
+
+ bool expandPoll(const SpaceOutliner &) const override;
+ void expand(SpaceOutliner &) const override;
+
+ Sequence &getSequence() const;
+};
+
+/* -------------------------------------------------------------------- */
+
+class TreeElementSequenceStrip : public AbstractTreeElement {
+ public:
+ TreeElementSequenceStrip(TreeElement &legacy_te, Strip &strip);
+};
+
+/* -------------------------------------------------------------------- */
+
+class TreeElementSequenceStripDuplicate : public AbstractTreeElement {
+ Sequence &sequence_;
+
+ public:
+ TreeElementSequenceStripDuplicate(TreeElement &legacy_te, Sequence &sequence);
+
+ Sequence &getSequence() const;
+};
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt
index bf8cf89699d..d93dc0ac0c0 100644
--- a/source/blender/editors/space_sequencer/CMakeLists.txt
+++ b/source/blender/editors/space_sequencer/CMakeLists.txt
@@ -70,9 +70,5 @@ if(WITH_AUDASPACE)
)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_space_sequencer "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 9f31e55439d..439a37631e0 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -59,6 +59,7 @@
#include "SEQ_add.h"
#include "SEQ_effects.h"
+#include "SEQ_iterator.h"
#include "SEQ_proxy.h"
#include "SEQ_relations.h"
#include "SEQ_render.h"
@@ -601,29 +602,28 @@ static IMB_Proxy_Size seq_get_proxy_size_flags(bContext *C)
return proxy_sizes;
}
-static void seq_build_proxy(bContext *C, Sequence *seq)
+static void seq_build_proxy(bContext *C, SeqCollection *movie_strips)
{
if (U.sequencer_proxy_setup != USER_SEQ_PROXY_SETUP_AUTOMATIC) {
return;
}
- /* Enable and set proxy size. */
- SEQ_proxy_set(seq, true);
- seq->strip->proxy->build_size_flags = seq_get_proxy_size_flags(C);
- seq->strip->proxy->build_flags |= SEQ_PROXY_SKIP_EXISTING;
-
- /* Build proxy. */
- GSet *file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list");
wmJob *wm_job = ED_seq_proxy_wm_job_get(C);
ProxyJob *pj = ED_seq_proxy_job_get(C, wm_job);
- SEQ_proxy_rebuild_context(pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue);
- BLI_gset_free(file_list, MEM_freeN);
+
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, movie_strips) {
+ /* Enable and set proxy size. */
+ SEQ_proxy_set(seq, true);
+ seq->strip->proxy->build_size_flags = seq_get_proxy_size_flags(C);
+ seq->strip->proxy->build_flags |= SEQ_PROXY_SKIP_EXISTING;
+ SEQ_proxy_rebuild_context(pj->main, pj->depsgraph, pj->scene, seq, NULL, &pj->queue, true);
+ }
if (!WM_jobs_is_running(wm_job)) {
G.is_break = false;
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
-
ED_area_tag_redraw(CTX_wm_area(C));
}
@@ -632,17 +632,19 @@ static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene,
Sequence *seq_movie,
Sequence *seq_sound)
{
- if (ELEM(NULL, seq_movie, seq_sound) || seq_sound->len <= seq_movie->len) {
+ if (ELEM(NULL, seq_movie, seq_sound)) {
return;
}
SEQ_transform_set_right_handle_frame(seq_sound, SEQ_transform_get_right_handle_frame(seq_movie));
+ SEQ_transform_set_left_handle_frame(seq_sound, SEQ_transform_get_left_handle_frame(seq_movie));
SEQ_time_update_sequence(scene, seqbase, seq_sound);
}
static void sequencer_add_movie_multiple_strips(bContext *C,
wmOperator *op,
- SeqLoadData *load_data)
+ SeqLoadData *load_data,
+ SeqCollection *r_movie_strips)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -657,61 +659,32 @@ static void sequencer_add_movie_multiple_strips(bContext *C,
BLI_strncpy(load_data->name, file_only, sizeof(load_data->name));
Sequence *seq_movie = NULL;
Sequence *seq_sound = NULL;
- double video_start_offset = -1;
- double audio_start_offset = 0;
-
- if (RNA_boolean_get(op->ptr, "sound")) {
- SoundStreamInfo sound_info;
- if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) {
- audio_start_offset = video_start_offset = sound_info.start;
- }
- }
load_data->channel++;
- seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset);
+ seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data);
load_data->channel--;
if (seq_movie == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
}
else {
if (RNA_boolean_get(op->ptr, "sound")) {
- int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS;
-
- int video_frame_offset = video_start_offset * FPS;
- int audio_frame_offset = audio_start_offset * FPS;
-
- double video_frame_remainder = video_start_offset * FPS - video_frame_offset;
- double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset;
-
- double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS;
-
- video_frame_offset -= minimum_frame_offset;
- audio_frame_offset -= minimum_frame_offset;
-
- load_data->start_frame += audio_frame_offset;
- seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip);
-
- int min_startdisp = 0, max_enddisp = 0;
- if (seq_sound != NULL) {
- min_startdisp = MIN2(seq_movie->startdisp, seq_sound->startdisp);
- max_enddisp = MAX2(seq_movie->enddisp, seq_sound->enddisp);
- }
-
- load_data->start_frame += max_enddisp - min_startdisp - audio_frame_offset;
+ seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
+ sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound);
}
- else {
- load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp;
- }
- sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound);
+
+ load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp;
seq_load_apply_generic_options(C, op, seq_sound);
seq_load_apply_generic_options(C, op, seq_movie);
- seq_build_proxy(C, seq_movie);
+ SEQ_collection_append_strip(seq_movie, r_movie_strips);
}
}
RNA_END;
}
-static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoadData *load_data)
+static bool sequencer_add_movie_single_strip(bContext *C,
+ wmOperator *op,
+ SeqLoadData *load_data,
+ SeqCollection *r_movie_strips)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -719,18 +692,9 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa
Sequence *seq_movie = NULL;
Sequence *seq_sound = NULL;
- double video_start_offset = -1;
- double audio_start_offset = 0;
-
- if (RNA_boolean_get(op->ptr, "sound")) {
- SoundStreamInfo sound_info;
- if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) {
- audio_start_offset = video_start_offset = sound_info.start;
- }
- }
load_data->channel++;
- seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset);
+ seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data);
load_data->channel--;
if (seq_movie == NULL) {
@@ -738,26 +702,12 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa
return false;
}
if (RNA_boolean_get(op->ptr, "sound")) {
- int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS;
-
- int video_frame_offset = video_start_offset * FPS;
- int audio_frame_offset = audio_start_offset * FPS;
-
- double video_frame_remainder = video_start_offset * FPS - video_frame_offset;
- double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset;
-
- double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS;
-
- video_frame_offset -= minimum_frame_offset;
- audio_frame_offset -= minimum_frame_offset;
-
- load_data->start_frame += audio_frame_offset;
- seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip);
+ seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
+ sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound);
}
- sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound);
seq_load_apply_generic_options(C, op, seq_sound);
seq_load_apply_generic_options(C, op, seq_movie);
- seq_build_proxy(C, seq_movie);
+ SEQ_collection_append_strip(seq_movie, r_movie_strips);
return true;
}
@@ -774,25 +724,30 @@ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op)
ED_sequencer_deselect_all(scene);
}
+ SeqCollection *movie_strips = SEQ_collection_create(__func__);
const int tot_files = RNA_property_collection_length(op->ptr,
RNA_struct_find_property(op->ptr, "files"));
if (tot_files > 1) {
- sequencer_add_movie_multiple_strips(C, op, &load_data);
+ sequencer_add_movie_multiple_strips(C, op, &load_data, movie_strips);
}
else {
- if (!sequencer_add_movie_single_strip(C, op, &load_data)) {
- sequencer_add_cancel(C, op);
- return OPERATOR_CANCELLED;
- }
+ sequencer_add_movie_single_strip(C, op, &load_data, movie_strips);
}
- /* Free custom data. */
- sequencer_add_cancel(C, op);
+ if (SEQ_collection_len(movie_strips) == 0) {
+ SEQ_collection_free(movie_strips);
+ return OPERATOR_CANCELLED;
+ }
+ seq_build_proxy(C, movie_strips);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+ /* Free custom data. */
+ sequencer_add_cancel(C, op);
+ SEQ_collection_free(movie_strips);
+
return OPERATOR_FINISHED;
}
@@ -896,7 +851,7 @@ static void sequencer_add_sound_multiple_strips(bContext *C,
RNA_string_get(&itemptr, "name", file_only);
BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only);
BLI_strncpy(load_data->name, file_only, sizeof(load_data->name));
- Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f);
+ Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
if (seq == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
}
@@ -914,7 +869,7 @@ static bool sequencer_add_sound_single_strip(bContext *C, wmOperator *op, SeqLoa
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_ensure(scene);
- Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f);
+ Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
if (seq == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
return false;
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 3b5e16d84a9..b2f13c612ac 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -2213,7 +2213,7 @@ static int sequencer_strip_jump_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 8f7ab2ea881..7143a1fa1ca 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -33,13 +33,13 @@ struct ARegionType;
struct Depsgraph;
struct Main;
struct Scene;
-struct Sequence;
struct SeqCollection;
+struct Sequence;
struct SpaceSeq;
struct StripElem;
+struct View2D;
struct bContext;
struct rctf;
-struct View2D;
struct wmOperator;
#define OVERLAP_ALPHA 180
diff --git a/source/blender/editors/space_sequencer/sequencer_proxy.c b/source/blender/editors/space_sequencer/sequencer_proxy.c
index 0a8eb7cb88f..fb561025da2 100644
--- a/source/blender/editors/space_sequencer/sequencer_proxy.c
+++ b/source/blender/editors/space_sequencer/sequencer_proxy.c
@@ -85,7 +85,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
}
bool success = SEQ_proxy_rebuild_context(
- pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue);
+ pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue, false);
if (!success && (seq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) != 0) {
BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping", seq->name);
@@ -137,7 +137,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
short stop = 0, do_update;
float progress;
- SEQ_proxy_rebuild_context(bmain, depsgraph, scene, seq, file_list, &queue);
+ SEQ_proxy_rebuild_context(bmain, depsgraph, scene, seq, file_list, &queue, false);
for (link = queue.first; link; link = link->next) {
struct SeqIndexBuildContext *context = link->data;
diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c
index 5d857f62b47..fd341fc9c0d 100644
--- a/source/blender/editors/space_sequencer/sequencer_scopes.c
+++ b/source/blender/editors/space_sequencer/sequencer_scopes.c
@@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Author: Peter Schlaile < peter [at] schlaile [dot] de >
+ * Copyright 2006-2008 Peter Schlaile < peter [at] schlaile [dot] de >
*/
/** \file
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index b93f421ff5c..b294fdf4820 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -39,6 +39,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "BKE_sequencer_offscreen.h"
@@ -988,19 +989,12 @@ static void sequencer_buttons_region_listener(const wmRegionListenerParams *para
}
}
-static void sequencer_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void sequencer_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
{
SpaceSeq *sseq = (SpaceSeq *)slink;
-
- if (!ELEM(GS(old_id->name), ID_GD)) {
- return;
- }
-
- if ((ID *)sseq->gpd == old_id) {
- sseq->gpd = (bGPdata *)new_id;
- id_us_min(old_id);
- id_us_plus(new_id);
- }
+ BKE_id_remapper_apply(mappings, (ID **)&sseq->gpd, ID_REMAP_APPLY_DEFAULT);
}
/* ************************************* */
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index 02f7f1d71c4..357b3e0a8b4 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -18,6 +18,7 @@
#include "BLI_listbase.h"
+#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "ED_screen.h"
@@ -171,21 +172,23 @@ static void spreadsheet_keymap(wmKeyConfig *keyconf)
WM_keymap_ensure(keyconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0);
}
-static void spreadsheet_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void spreadsheet_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const IDRemapper *mappings)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink;
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
- if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
- SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
- if ((ID *)object_context->object == old_id) {
- if (new_id && GS(new_id->name) == ID_OB) {
- object_context->object = (Object *)new_id;
- }
- else {
- object_context->object = nullptr;
- }
- }
+ if (context->type != SPREADSHEET_CONTEXT_OBJECT) {
+ continue;
}
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+
+ if (object_context->object != nullptr && GS(object_context->object->id.name) != ID_OB) {
+ object_context->object = nullptr;
+ continue;
+ }
+
+ BKE_id_remapper_apply(mappings, ((ID **)&object_context->object), ID_REMAP_APPLY_DEFAULT);
}
}
@@ -551,7 +554,7 @@ static void spreadsheet_footer_region_draw(const bContext *C, ARegion *region)
UI_LAYOUT_HEADER,
UI_HEADER_OFFSET,
region->winy - (region->winy - UI_UNIT_Y) / 2.0f,
- region->sizex,
+ region->winx,
1,
0,
style);
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index b9b03732a40..83302f94c85 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -474,6 +474,11 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet,
r_fields.add("Viewer", std::move(field));
}
}
+ if (const geo_log::GenericValueLog *generic_value_log =
+ dynamic_cast<const geo_log::GenericValueLog *>(value_log)) {
+ fn::GPointer value = generic_value_log->value();
+ r_fields.add("Viewer", fn::make_constant_field(*value.type(), value.get()));
+ }
}
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
index 64672b919cd..df23a27aa22 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
@@ -36,8 +36,8 @@ struct SpaceSpreadsheet_Runtime {
}
};
-struct bContext;
struct ARegionType;
+struct bContext;
void spreadsheet_operatortypes();
void spreadsheet_update_context_path(const bContext *C);
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
index f4b5ff819ed..d9837b7c1a6 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -127,6 +127,28 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
}
+ if (data.type().is<int8_t>()) {
+ const int8_t value = data.get<int8_t>(real_index);
+ const std::string value_str = std::to_string(value);
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ value_str.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align Integers. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ }
else if (data.type().is<float>()) {
const float value = data.get<float>(real_index);
std::stringstream ss;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
index 56722104b4f..c409defd3f4 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
@@ -243,7 +243,7 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, filter_ptr, "value_string", 0, IFACE_("Value"), ICON_NONE);
break;
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
- uiItemL(layout, IFACE_("Unkown column type"), ICON_ERROR);
+ uiItemL(layout, IFACE_("Unknown column type"), ICON_ERROR);
break;
}
}
diff --git a/source/blender/editors/space_text/CMakeLists.txt b/source/blender/editors/space_text/CMakeLists.txt
index abd7620ea2b..a85c69caa50 100644
--- a/source/blender/editors/space_text/CMakeLists.txt
+++ b/source/blender/editors/space_text/CMakeLists.txt
@@ -61,8 +61,5 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
blender_add_lib(bf_editor_space_text "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index f449ce50ae3..7339d8248c8 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -32,6 +32,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "ED_screen.h"
@@ -401,18 +402,12 @@ static void text_properties_region_draw(const bContext *C, ARegion *region)
}
}
-static void text_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+static void text_id_remap(ScrArea *UNUSED(area),
+ SpaceLink *slink,
+ const struct IDRemapper *mappings)
{
SpaceText *stext = (SpaceText *)slink;
-
- if (!ELEM(GS(old_id->name), ID_TXT)) {
- return;
- }
-
- if ((ID *)stext->text == old_id) {
- stext->text = (Text *)new_id;
- id_us_ensure_real(new_id);
- }
+ BKE_id_remapper_apply(mappings, (ID **)&stext->text, ID_REMAP_APPLY_ENSURE_REAL);
}
/********************* registration ********************/
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index 8fb55ed9b46..27941b881b8 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -79,10 +79,8 @@ static void text_font_end(const TextDrawContext *UNUSED(tdc))
static int text_font_draw(const TextDrawContext *tdc, int x, int y, const char *str)
{
- int columns;
-
BLF_position(tdc->font_id, x, y, 0);
- columns = BLF_draw_mono(tdc->font_id, str, BLF_DRAW_STR_DUMMY_MAX, tdc->cwidth_px);
+ const int columns = BLF_draw_mono(tdc->font_id, str, BLF_DRAW_STR_DUMMY_MAX, tdc->cwidth_px);
return tdc->cwidth_px * columns;
}
@@ -90,18 +88,17 @@ static int text_font_draw(const TextDrawContext *tdc, int x, int y, const char *
static int text_font_draw_character(const TextDrawContext *tdc, int x, int y, char c)
{
BLF_position(tdc->font_id, x, y, 0);
- BLF_draw(tdc->font_id, &c, 1);
+ BLF_draw_mono(tdc->font_id, &c, 1, tdc->cwidth_px);
return tdc->cwidth_px;
}
-static int text_font_draw_character_utf8(const TextDrawContext *tdc, int x, int y, const char *c)
+static int text_font_draw_character_utf8(
+ const TextDrawContext *tdc, int x, int y, const char *c, const int c_len)
{
- int columns;
-
- const size_t len = BLI_str_utf8_size_safe(c);
+ BLI_assert(c_len == BLI_str_utf8_size_safe(c));
BLF_position(tdc->font_id, x, y, 0);
- columns = BLF_draw_mono(tdc->font_id, c, len, tdc->cwidth_px);
+ const int columns = BLF_draw_mono(tdc->font_id, c, c_len, tdc->cwidth_px);
return tdc->cwidth_px * columns;
}
@@ -463,13 +460,15 @@ static int text_draw_wrapped(const SpaceText *st,
}
/* Draw the visible portion of text on the overshot line */
- for (a = fstart, ma = mstart; ma < mend; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
+ for (a = fstart, ma = mstart; ma < mend; a++) {
if (use_syntax) {
if (fmt_prev != format[a]) {
format_draw_color(tdc, fmt_prev = format[a]);
}
}
- x += text_font_draw_character_utf8(tdc, x, y, str + ma);
+ const int c_len = BLI_str_utf8_size_safe(str + ma);
+ x += text_font_draw_character_utf8(tdc, x, y, str + ma, c_len);
+ ma += c_len;
fpos++;
}
y -= TXT_LINE_HEIGHT(st);
@@ -491,15 +490,16 @@ static int text_draw_wrapped(const SpaceText *st,
}
/* Draw the remaining text */
- for (a = fstart, ma = mstart; str[ma] && y > clip_min_y;
- a++, ma += BLI_str_utf8_size_safe(str + ma)) {
+ for (a = fstart, ma = mstart; str[ma] && y > clip_min_y; a++) {
if (use_syntax) {
if (fmt_prev != format[a]) {
format_draw_color(tdc, fmt_prev = format[a]);
}
}
- x += text_font_draw_character_utf8(tdc, x, y, str + ma);
+ const int c_len = BLI_str_utf8_size_safe(str + ma);
+ x += text_font_draw_character_utf8(tdc, x, y, str + ma, c_len);
+ ma += c_len;
}
flatten_string_free(&fs);
@@ -559,8 +559,9 @@ static void text_draw(const SpaceText *st,
if (format[a] != fmt_prev) {
format_draw_color(tdc, fmt_prev = format[a]);
}
- x += text_font_draw_character_utf8(tdc, x, y, in + str_shift);
- str_shift += BLI_str_utf8_size_safe(in + str_shift);
+ const int c_len = BLI_str_utf8_size_safe(in + str_shift);
+ x += text_font_draw_character_utf8(tdc, x, y, in + str_shift, c_len);
+ str_shift += c_len;
}
}
else {
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 3c0ffa29bbd..430ffe6d56f 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -796,7 +796,7 @@ static int text_run_script(bContext *C, ReportList *reports)
/* Don't report error messages while live editing */
if (!is_live) {
- /* text may have freed its self */
+ /* text may have freed itself */
if (CTX_data_edit_text(C) == text) {
if (text->curl != curl_prev || curc_prev != text->curc) {
text_update_cursor_moved(C);
diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c
index d40229332fd..e4c11bc8668 100644
--- a/source/blender/editors/space_userpref/userpref_ops.c
+++ b/source/blender/editors/space_userpref/userpref_ops.c
@@ -30,6 +30,7 @@
#ifdef WIN32
# include "BLI_winstuff.h"
#endif
+#include "BLI_path_util.h"
#include "BKE_context.h"
#include "BKE_main.h"
@@ -142,16 +143,20 @@ static void PREFERENCES_OT_autoexec_path_remove(wmOperatorType *ot)
static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *op)
{
- char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL);
+ char *path = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL);
+ char dirname[FILE_MAXFILE];
+
+ BLI_path_slash_rstrip(path);
+ BLI_split_file_part(path, dirname, sizeof(dirname));
/* NULL is a valid directory path here. A library without path will be created then. */
- BKE_preferences_asset_library_add(&U, NULL, directory);
+ BKE_preferences_asset_library_add(&U, dirname, path);
U.runtime.is_dirty = true;
/* There's no dedicated notifier for the Preferences. */
WM_main_add_notifier(NC_WINDOW, NULL);
- MEM_freeN(directory);
+ MEM_freeN(path);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt
index 19f869ed50b..7e8b013192f 100644
--- a/source/blender/editors/space_view3d/CMakeLists.txt
+++ b/source/blender/editors/space_view3d/CMakeLists.txt
@@ -60,8 +60,17 @@ set(SRC
view3d_gizmo_tool_generic.c
view3d_header.c
view3d_iterators.c
+ view3d_navigate.c
+ view3d_navigate_dolly.c
view3d_navigate_fly.c
+ view3d_navigate_move.c
+ view3d_navigate_ndof.c
+ view3d_navigate_roll.c
+ view3d_navigate_rotate.c
+ view3d_navigate_smoothview.c
view3d_navigate_walk.c
+ view3d_navigate_zoom.c
+ view3d_navigate_zoom_border.c
view3d_ops.c
view3d_placement.c
view3d_project.c
@@ -71,6 +80,7 @@ set(SRC
view3d_view.c
view3d_intern.h
+ view3d_navigate.h
)
set(LIB
@@ -83,11 +93,6 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 8822ea6af3b..51107499d3f 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -48,6 +48,7 @@
#include "BKE_idprop.h"
#include "BKE_lattice.h"
#include "BKE_layer.h"
+#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_mball.h"
#include "BKE_mesh.h"
@@ -88,6 +89,7 @@
#include "DEG_depsgraph_build.h"
#include "view3d_intern.h" /* own include */
+#include "view3d_navigate.h"
/* ******************** manage regions ********************* */
@@ -242,7 +244,6 @@ void ED_view3d_shade_update(Main *bmain, View3D *v3d, ScrArea *area)
for (region = area->regionbase.first; region; region = region->next) {
if ((region->regiontype == RGN_TYPE_WINDOW) && region->regiondata) {
ED_view3d_stop_render_preview(wm, region);
- break;
}
}
}
@@ -1813,50 +1814,54 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
return CTX_RESULT_MEMBER_NOT_FOUND;
}
-static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id)
+static void view3d_id_remap_v3d_ob_centers(View3D *v3d, const struct IDRemapper *mappings)
{
- View3D *v3d;
- ARegion *region;
- bool is_local = false;
-
- if (!ELEM(GS(old_id->name), ID_OB, ID_MA, ID_IM, ID_MC)) {
- return;
+ if (BKE_id_remapper_apply(mappings, (ID **)&v3d->ob_center, ID_REMAP_APPLY_DEFAULT) ==
+ ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
+ /* Otherwise, bonename may remain valid...
+ * We could be smart and check this, too? */
+ v3d->ob_center_bone[0] = '\0';
}
+}
- for (v3d = (View3D *)slink; v3d; v3d = v3d->localvd, is_local = true) {
- if ((ID *)v3d->camera == old_id) {
- v3d->camera = (Object *)new_id;
- if (!new_id) {
- /* 3D view might be inactive, in that case needs to use slink->regionbase */
- ListBase *regionbase = (slink == area->spacedata.first) ? &area->regionbase :
- &slink->regionbase;
- for (region = regionbase->first; region; region = region->next) {
- if (region->regiontype == RGN_TYPE_WINDOW) {
- RegionView3D *rv3d = is_local ? ((RegionView3D *)region->regiondata)->localvd :
- region->regiondata;
- if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
- rv3d->persp = RV3D_PERSP;
- }
- }
+static void view3d_id_remap_v3d(ScrArea *area,
+ SpaceLink *slink,
+ View3D *v3d,
+ const struct IDRemapper *mappings,
+ const bool is_local)
+{
+ ARegion *region;
+ if (BKE_id_remapper_apply(mappings, (ID **)&v3d->camera, ID_REMAP_APPLY_DEFAULT) ==
+ ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
+ /* 3D view might be inactive, in that case needs to use slink->regionbase */
+ ListBase *regionbase = (slink == area->spacedata.first) ? &area->regionbase :
+ &slink->regionbase;
+ for (region = regionbase->first; region; region = region->next) {
+ if (region->regiontype == RGN_TYPE_WINDOW) {
+ RegionView3D *rv3d = is_local ? ((RegionView3D *)region->regiondata)->localvd :
+ region->regiondata;
+ if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
+ rv3d->persp = RV3D_PERSP;
}
}
}
+ }
+}
- /* Values in local-view aren't used, see: T52663 */
- if (is_local == false) {
- if ((ID *)v3d->ob_center == old_id) {
- v3d->ob_center = (Object *)new_id;
- /* Otherwise, bonename may remain valid...
- * We could be smart and check this, too? */
- if (new_id == NULL) {
- v3d->ob_center_bone[0] = '\0';
- }
- }
- }
+static void view3d_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRemapper *mappings)
+{
- if (is_local) {
- break;
- }
+ if (!BKE_id_remapper_has_mapping_for(
+ mappings, FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_IM | FILTER_ID_MC)) {
+ return;
+ }
+
+ View3D *view3d = (View3D *)slink;
+ view3d_id_remap_v3d(area, slink, view3d, mappings, false);
+ view3d_id_remap_v3d_ob_centers(view3d, mappings);
+ if (view3d->localvd != NULL) {
+ /* Object centers in local-view aren't used, see: T52663 */
+ view3d_id_remap_v3d(area, slink, view3d->localvd, mappings, true);
}
}
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index 243d4033cbc..6edb0a070c0 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1764,7 +1764,8 @@ static void view3d_panel_transform(const bContext *C, Panel *panel)
v3d_transform_butsR(col, &obptr);
/* Dimensions and editmode are mostly the same check. */
- if (OB_TYPE_SUPPORT_EDITMODE(ob->type) || ELEM(ob->type, OB_VOLUME, OB_HAIR, OB_POINTCLOUD)) {
+ if (OB_TYPE_SUPPORT_EDITMODE(ob->type) ||
+ ELEM(ob->type, OB_VOLUME, OB_CURVES, OB_POINTCLOUD)) {
View3D *v3d = CTX_wm_view3d(C);
v3d_object_dimension_buts(NULL, col, v3d, ob);
}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index b1f19581543..3f639e8ee1a 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1036,7 +1036,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d)
negate_v3_v3(o, rv3d->ofs);
GPU_blend(GPU_BLEND_ALPHA);
- GPU_depth_mask(false); /* don't overwrite zbuf */
+ GPU_depth_mask(false); /* Don't overwrite the Z-buffer. */
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
@@ -1258,11 +1258,7 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y
/* 6 is the maximum size of the axis roll text. */
/* increase size for unicode languages (Chinese in utf-8...) */
-#ifdef WITH_INTERNATIONAL
char tmpstr[96 + 6];
-#else
- char tmpstr[32 + 6];
-#endif
BLF_enable(font_id, BLF_SHADOW);
BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f});
@@ -1689,9 +1685,9 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
G.f |= G_FLAG_RENDER_VIEWPORT;
{
- /* free images which can have changed on frame-change
- * warning! can be slow so only free animated images - campbell */
- BKE_image_free_anim_gputextures(G.main); /* XXX :((( */
+ /* Free images which can have changed on frame-change.
+ * WARNING(@campbellbarton): can be slow so only free animated images. */
+ BKE_image_free_anim_gputextures(G.main);
}
GPU_matrix_push_projection();
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 80089815284..4c7a7cb4c61 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -23,70 +23,39 @@
* 3D view manipulation/operators.
*/
-#include <float.h>
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
-
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
-#include "DNA_curve_types.h"
-#include "DNA_gpencil_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
#include "DNA_world_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
-#include "BLI_dial_2d.h"
#include "BLI_math.h"
-#include "BLI_utildefines.h"
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_camera.h"
-#include "BKE_context.h"
-#include "BKE_gpencil_geom.h"
-#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_object.h"
-#include "BKE_paint.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
-#include "BKE_vfont.h"
-#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "WM_api.h"
#include "WM_message.h"
-#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
-#include "ED_armature.h"
-#include "ED_mesh.h"
-#include "ED_particle.h"
#include "ED_screen.h"
#include "ED_transform.h"
#include "ED_transform_snap_object_context.h"
-#include "ED_view3d.h"
-
-#include "UI_resources.h"
-
-#include "PIL_time.h"
#include "view3d_intern.h" /* own include */
-enum {
- HAS_TRANSLATE = (1 << 0),
- HAS_ROTATE = (1 << 0),
-};
-
/* test for unlocked camera view in quad view */
static bool view3d_camera_user_poll(bContext *C)
{
@@ -115,3052 +84,6 @@ static bool view3d_lock_poll(bContext *C)
return false;
}
-static bool view3d_pan_poll(bContext *C)
-{
- if (ED_operator_region_view3d_active(C)) {
- const RegionView3D *rv3d = CTX_wm_region_view3d(C);
- return !(RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_LOCATION);
- }
- return false;
-}
-
-static bool view3d_zoom_or_dolly_poll(bContext *C)
-{
- if (ED_operator_region_view3d_active(C)) {
- const RegionView3D *rv3d = CTX_wm_region_view3d(C);
- return !(RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ZOOM_AND_DOLLY);
- }
- return false;
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Generic View Operator Properties
- * \{ */
-
-enum eV3D_OpPropFlag {
- V3D_OP_PROP_MOUSE_CO = (1 << 0),
- V3D_OP_PROP_DELTA = (1 << 1),
- V3D_OP_PROP_USE_ALL_REGIONS = (1 << 2),
- V3D_OP_PROP_USE_MOUSE_INIT = (1 << 3),
-};
-
-static void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPropFlag flag)
-{
- if (flag & V3D_OP_PROP_MOUSE_CO) {
- PropertyRNA *prop;
- prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Region Position X", "", 0, INT_MAX);
- RNA_def_property_flag(prop, PROP_HIDDEN);
- prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Region Position Y", "", 0, INT_MAX);
- RNA_def_property_flag(prop, PROP_HIDDEN);
- }
- if (flag & V3D_OP_PROP_DELTA) {
- RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
- }
- if (flag & V3D_OP_PROP_USE_ALL_REGIONS) {
- PropertyRNA *prop;
- prop = RNA_def_boolean(
- ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- }
- if (flag & V3D_OP_PROP_USE_MOUSE_INIT) {
- WM_operator_properties_use_cursor_init(ot);
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Generic View Operator Custom-Data
- * \{ */
-
-typedef struct ViewOpsData {
- /** Context pointers (assigned by #viewops_data_alloc). */
- Main *bmain;
- Scene *scene;
- ScrArea *area;
- ARegion *region;
- View3D *v3d;
- RegionView3D *rv3d;
- Depsgraph *depsgraph;
-
- /** Needed for continuous zoom. */
- wmTimer *timer;
-
- /** Viewport state on initialization, don't change afterwards. */
- struct {
- float dist;
- float camzoom;
- float quat[4];
- /** #wmEvent.xy. */
- int event_xy[2];
- /** Offset to use when #VIEWOPS_FLAG_USE_MOUSE_INIT is not set.
- * so we can simulate pressing in the middle of the screen. */
- int event_xy_offset[2];
- /** #wmEvent.type that triggered the operator. */
- int event_type;
- float ofs[3];
- /** Initial distance to 'ofs'. */
- float zfac;
-
- /** Trackball rotation only. */
- float trackvec[3];
- /** Dolly only. */
- float mousevec[3];
-
- /**
- * #RegionView3D.persp set after auto-perspective is applied.
- * If we want the value before running the operator, add a separate member.
- */
- char persp;
-
- /** Used for roll */
- Dial *dial;
- } init;
-
- /** Previous state (previous modal event handled). */
- struct {
- int event_xy[2];
- /** For operators that use time-steps (continuous zoom). */
- double time;
- } prev;
-
- /** Current state. */
- struct {
- /** Working copy of #RegionView3D.viewquat, needed for rotation calculation
- * so we can apply snap to the 3D Viewport while keeping the unsnapped rotation
- * here to use when snap is disabled and for continued calculation. */
- float viewquat[4];
- } curr;
-
- float reverse;
- bool axis_snap; /* view rotate only */
-
- /** Use for orbit selection and auto-dist. */
- float dyn_ofs[3];
- bool use_dyn_ofs;
-} ViewOpsData;
-
-/**
- * Size of the sphere being dragged for trackball rotation within the view bounds.
- * also affects speed (smaller is faster).
- */
-#define TRACKBALLSIZE (1.1f)
-
-static void calctrackballvec(const rcti *rect, const int event_xy[2], float r_dir[3])
-{
- const float radius = TRACKBALLSIZE;
- const float t = radius / (float)M_SQRT2;
- const float size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)};
- /* Aspect correct so dragging in a non-square view doesn't squash the direction.
- * So diagonal motion rotates the same direction the cursor is moving. */
- const float size_min = min_ff(size[0], size[1]);
- const float aspect[2] = {size_min / size[0], size_min / size[1]};
-
- /* Normalize x and y. */
- r_dir[0] = (event_xy[0] - BLI_rcti_cent_x(rect)) / ((size[0] * aspect[0]) / 2.0);
- r_dir[1] = (event_xy[1] - BLI_rcti_cent_y(rect)) / ((size[1] * aspect[1]) / 2.0);
- const float d = len_v2(r_dir);
- if (d < t) {
- /* Inside sphere. */
- r_dir[2] = sqrtf(square_f(radius) - square_f(d));
- }
- else {
- /* On hyperbola. */
- r_dir[2] = square_f(t) / d;
- }
-}
-
-/**
- * Allocate and fill in context pointers for #ViewOpsData
- */
-static void viewops_data_alloc(bContext *C, wmOperator *op)
-{
- ViewOpsData *vod = MEM_callocN(sizeof(ViewOpsData), "viewops data");
-
- /* store data */
- op->customdata = vod;
- vod->bmain = CTX_data_main(C);
- vod->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- vod->scene = CTX_data_scene(C);
- vod->area = CTX_wm_area(C);
- vod->region = CTX_wm_region(C);
- vod->v3d = vod->area->spacedata.first;
- vod->rv3d = vod->region->regiondata;
-}
-
-void view3d_orbit_apply_dyn_ofs(float r_ofs[3],
- const float ofs_old[3],
- const float viewquat_old[4],
- const float viewquat_new[4],
- const float dyn_ofs[3])
-{
- float q[4];
- invert_qt_qt_normalized(q, viewquat_old);
- mul_qt_qtqt(q, q, viewquat_new);
-
- invert_qt_normalized(q);
-
- sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs);
- mul_qt_v3(q, r_ofs);
- add_v3_v3(r_ofs, dyn_ofs);
-}
-
-static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
-{
- static float lastofs[3] = {0, 0, 0};
- bool is_set = false;
-
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
- View3D *v3d = CTX_wm_view3d(C);
- Object *ob_act_eval = OBACT(view_layer_eval);
- Object *ob_act = DEG_get_original_object(ob_act_eval);
-
- if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
- /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */
- ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0) {
- /* in case of sculpting use last average stroke position as a rotation
- * center, in other cases it's not clear what rotation center shall be
- * so just rotate around object origin
- */
- if (ob_act->mode &
- (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
- float stroke[3];
- BKE_paint_stroke_get_average(scene, ob_act_eval, stroke);
- copy_v3_v3(lastofs, stroke);
- }
- else {
- copy_v3_v3(lastofs, ob_act_eval->obmat[3]);
- }
- is_set = true;
- }
- else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
- Curve *cu = ob_act_eval->data;
- EditFont *ef = cu->editfont;
-
- zero_v3(lastofs);
- for (int i = 0; i < 4; i++) {
- add_v2_v2(lastofs, ef->textcurs[i]);
- }
- mul_v2_fl(lastofs, 1.0f / 4.0f);
-
- mul_m4_v3(ob_act_eval->obmat, lastofs);
-
- is_set = true;
- }
- else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) {
- /* object mode use boundbox centers */
- Base *base_eval;
- uint tot = 0;
- float select_center[3];
-
- zero_v3(select_center);
- for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) {
- if (BASE_SELECTED(v3d, base_eval)) {
- /* use the boundbox if we can */
- Object *ob_eval = base_eval->object;
-
- if (ob_eval->runtime.bb && !(ob_eval->runtime.bb->flag & BOUNDBOX_DIRTY)) {
- float cent[3];
-
- BKE_boundbox_calc_center_aabb(ob_eval->runtime.bb, cent);
-
- mul_m4_v3(ob_eval->obmat, cent);
- add_v3_v3(select_center, cent);
- }
- else {
- add_v3_v3(select_center, ob_eval->obmat[3]);
- }
- tot++;
- }
- }
- if (tot) {
- mul_v3_fl(select_center, 1.0f / (float)tot);
- copy_v3_v3(lastofs, select_center);
- is_set = true;
- }
- }
- else {
- /* If there's no selection, lastofs is unmodified and last value since static */
- is_set = calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, lastofs, NULL);
- }
-
- copy_v3_v3(r_dyn_ofs, lastofs);
-
- return is_set;
-}
-
-enum eViewOpsFlag {
- /** When enabled, rotate around the selection. */
- VIEWOPS_FLAG_ORBIT_SELECT = (1 << 0),
- /** When enabled, use the depth under the cursor for navigation. */
- VIEWOPS_FLAG_DEPTH_NAVIGATE = (1 << 1),
- /**
- * When enabled run #ED_view3d_persp_ensure this may switch out of camera view
- * when orbiting or switch from orthographic to perspective when auto-perspective is enabled.
- * Some operations don't require this (view zoom/pan or NDOF where subtle rotation is common
- * so we don't want it to trigger auto-perspective). */
- VIEWOPS_FLAG_PERSP_ENSURE = (1 << 2),
- /** When set, ignore any options that depend on initial cursor location. */
- VIEWOPS_FLAG_USE_MOUSE_INIT = (1 << 3),
-};
-
-static enum eViewOpsFlag viewops_flag_from_args(bool use_select, bool use_depth)
-{
- enum eViewOpsFlag flag = 0;
- if (use_select) {
- flag |= VIEWOPS_FLAG_ORBIT_SELECT;
- }
- if (use_depth) {
- flag |= VIEWOPS_FLAG_DEPTH_NAVIGATE;
- }
-
- return flag;
-}
-
-static enum eViewOpsFlag viewops_flag_from_prefs(void)
-{
- return viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0,
- (U.uiflag & USER_DEPTH_NAVIGATE) != 0);
-}
-
-/**
- * Calculate the values for #ViewOpsData
- */
-static void viewops_data_create(bContext *C,
- wmOperator *op,
- const wmEvent *event,
- enum eViewOpsFlag viewops_flag)
-{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewOpsData *vod = op->customdata;
- RegionView3D *rv3d = vod->rv3d;
-
- /* Could do this more nicely. */
- if ((viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) == 0) {
- viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE;
- }
-
- /* we need the depth info before changing any viewport options */
- if (viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE) {
- float fallback_depth_pt[3];
-
- view3d_operator_needs_opengl(C); /* needed for zbuf drawing */
-
- negate_v3_v3(fallback_depth_pt, rv3d->ofs);
-
- vod->use_dyn_ofs = ED_view3d_autodist(
- depsgraph, vod->region, vod->v3d, event->mval, vod->dyn_ofs, true, fallback_depth_pt);
- }
- else {
- vod->use_dyn_ofs = false;
- }
-
- if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) {
- if (ED_view3d_persp_ensure(depsgraph, vod->v3d, vod->region)) {
- /* If we're switching from camera view to the perspective one,
- * need to tag viewport update, so camera view and borders are properly updated. */
- ED_region_tag_redraw(vod->region);
- }
- }
-
- /* set the view from the camera, if view locking is enabled.
- * we may want to make this optional but for now its needed always */
- ED_view3d_camera_lock_init(depsgraph, vod->v3d, vod->rv3d);
-
- vod->init.persp = rv3d->persp;
- vod->init.dist = rv3d->dist;
- vod->init.camzoom = rv3d->camzoom;
- copy_qt_qt(vod->init.quat, rv3d->viewquat);
- copy_v2_v2_int(vod->init.event_xy, event->xy);
- copy_v2_v2_int(vod->prev.event_xy, event->xy);
-
- if (viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) {
- zero_v2_int(vod->init.event_xy_offset);
- }
- else {
- /* Simulate the event starting in the middle of the region. */
- vod->init.event_xy_offset[0] = BLI_rcti_cent_x(&vod->region->winrct) - event->xy[0];
- vod->init.event_xy_offset[1] = BLI_rcti_cent_y(&vod->region->winrct) - event->xy[1];
- }
-
- vod->init.event_type = event->type;
- copy_v3_v3(vod->init.ofs, rv3d->ofs);
-
- copy_qt_qt(vod->curr.viewquat, rv3d->viewquat);
-
- if (viewops_flag & VIEWOPS_FLAG_ORBIT_SELECT) {
- float ofs[3];
- if (view3d_orbit_calc_center(C, ofs) || (vod->use_dyn_ofs == false)) {
- vod->use_dyn_ofs = true;
- negate_v3_v3(vod->dyn_ofs, ofs);
- viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE;
- }
- }
-
- if (viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE) {
- if (vod->use_dyn_ofs) {
- if (rv3d->is_persp) {
- float my_origin[3]; /* original G.vd->ofs */
- float my_pivot[3]; /* view */
- float dvec[3];
-
- /* locals for dist correction */
- float mat[3][3];
- float upvec[3];
-
- negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
-
- /* Set the dist value to be the distance from this 3d point this means you'll
- * always be able to zoom into it and panning won't go bad when dist was zero. */
-
- /* remove dist value */
- upvec[0] = upvec[1] = 0;
- upvec[2] = rv3d->dist;
- copy_m3_m4(mat, rv3d->viewinv);
-
- mul_m3_v3(mat, upvec);
- sub_v3_v3v3(my_pivot, rv3d->ofs, upvec);
- negate_v3(my_pivot); /* ofs is flipped */
-
- /* find a new ofs value that is along the view axis
- * (rather than the mouse location) */
- closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin);
- vod->init.dist = rv3d->dist = len_v3v3(my_pivot, dvec);
-
- negate_v3_v3(rv3d->ofs, dvec);
- }
- else {
- const float mval_region_mid[2] = {(float)vod->region->winx / 2.0f,
- (float)vod->region->winy / 2.0f};
-
- ED_view3d_win_to_3d(vod->v3d, vod->region, vod->dyn_ofs, mval_region_mid, rv3d->ofs);
- negate_v3(rv3d->ofs);
- }
- negate_v3(vod->dyn_ofs);
- copy_v3_v3(vod->init.ofs, rv3d->ofs);
- }
- }
-
- /* For dolly */
- ED_view3d_win_to_vector(vod->region, (const float[2]){UNPACK2(event->mval)}, vod->init.mousevec);
-
- {
- int event_xy_offset[2];
- add_v2_v2v2_int(event_xy_offset, event->xy, vod->init.event_xy_offset);
-
- /* For rotation with trackball rotation. */
- calctrackballvec(&vod->region->winrct, event_xy_offset, vod->init.trackvec);
- }
-
- {
- float tvec[3];
- negate_v3_v3(tvec, rv3d->ofs);
- vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL);
- }
-
- vod->reverse = 1.0f;
- if (rv3d->persmat[2][1] < 0.0f) {
- vod->reverse = -1.0f;
- }
-
- rv3d->rflag |= RV3D_NAVIGATING;
-}
-
-static void viewops_data_free(bContext *C, wmOperator *op)
-{
- ARegion *region;
- if (op->customdata) {
- ViewOpsData *vod = op->customdata;
- region = vod->region;
- vod->rv3d->rflag &= ~RV3D_NAVIGATING;
-
- if (vod->timer) {
- WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
- }
-
- if (vod->init.dial) {
- MEM_freeN(vod->init.dial);
- }
-
- MEM_freeN(vod);
- op->customdata = NULL;
- }
- else {
- region = CTX_wm_region(C);
- }
-
- /* Need to redraw because drawing code uses RV3D_NAVIGATING to draw
- * faster while navigation operator runs. */
- ED_region_tag_redraw(region);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Rotate Operator
- * \{ */
-
-enum {
- VIEW_PASS = 0,
- VIEW_APPLY,
- VIEW_CONFIRM,
-};
-
-/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
-enum {
- VIEW_MODAL_CONFIRM = 1, /* used for all view operations */
- VIEWROT_MODAL_AXIS_SNAP_ENABLE = 2,
- VIEWROT_MODAL_AXIS_SNAP_DISABLE = 3,
- VIEWROT_MODAL_SWITCH_ZOOM = 4,
- VIEWROT_MODAL_SWITCH_MOVE = 5,
- VIEWROT_MODAL_SWITCH_ROTATE = 6,
-};
-
-void viewrotate_modal_keymap(wmKeyConfig *keyconf)
-{
- static const EnumPropertyItem modal_items[] = {
- {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
-
- {VIEWROT_MODAL_AXIS_SNAP_ENABLE, "AXIS_SNAP_ENABLE", 0, "Axis Snap", ""},
- {VIEWROT_MODAL_AXIS_SNAP_DISABLE, "AXIS_SNAP_DISABLE", 0, "Axis Snap (Off)", ""},
-
- {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
- {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
-
- {0, NULL, 0, NULL, NULL},
- };
-
- wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Rotate Modal");
-
- /* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items) {
- return;
- }
-
- keymap = WM_modalkeymap_ensure(keyconf, "View3D Rotate Modal", modal_items);
-
- /* disabled mode switching for now, can re-implement better, later on */
-#if 0
- WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
- WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
- WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
-#endif
-
- /* assign map to operators */
- WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate");
-}
-
-static void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
-{
- if (vod->use_dyn_ofs) {
- RegionView3D *rv3d = vod->rv3d;
- view3d_orbit_apply_dyn_ofs(
- rv3d->ofs, vod->init.ofs, vod->init.quat, viewquat_new, vod->dyn_ofs);
- }
-}
-
-static void viewrotate_apply_snap(ViewOpsData *vod)
-{
- const float axis_limit = DEG2RADF(45 / 3);
-
- RegionView3D *rv3d = vod->rv3d;
-
- float viewquat_inv[4];
- float zaxis[3] = {0, 0, 1};
- float zaxis_best[3];
- int x, y, z;
- bool found = false;
-
- invert_qt_qt_normalized(viewquat_inv, vod->curr.viewquat);
-
- mul_qt_v3(viewquat_inv, zaxis);
- normalize_v3(zaxis);
-
- for (x = -1; x < 2; x++) {
- for (y = -1; y < 2; y++) {
- for (z = -1; z < 2; z++) {
- if (x || y || z) {
- float zaxis_test[3] = {x, y, z};
-
- normalize_v3(zaxis_test);
-
- if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) {
- copy_v3_v3(zaxis_best, zaxis_test);
- found = true;
- }
- }
- }
- }
- }
-
- if (found) {
-
- /* find the best roll */
- float quat_roll[4], quat_final[4], quat_best[4], quat_snap[4];
- float viewquat_align[4]; /* viewquat aligned to zaxis_best */
- float viewquat_align_inv[4]; /* viewquat aligned to zaxis_best */
- float best_angle = axis_limit;
- int j;
-
- /* viewquat_align is the original viewquat aligned to the snapped axis
- * for testing roll */
- rotation_between_vecs_to_quat(viewquat_align, zaxis_best, zaxis);
- normalize_qt(viewquat_align);
- mul_qt_qtqt(viewquat_align, vod->curr.viewquat, viewquat_align);
- normalize_qt(viewquat_align);
- invert_qt_qt_normalized(viewquat_align_inv, viewquat_align);
-
- vec_to_quat(quat_snap, zaxis_best, OB_NEGZ, OB_POSY);
- normalize_qt(quat_snap);
- invert_qt_normalized(quat_snap);
-
- /* check if we can find the roll */
- found = false;
-
- /* find best roll */
- for (j = 0; j < 8; j++) {
- float angle;
- float xaxis1[3] = {1, 0, 0};
- float xaxis2[3] = {1, 0, 0};
- float quat_final_inv[4];
-
- axis_angle_to_quat(quat_roll, zaxis_best, (float)j * DEG2RADF(45.0f));
- normalize_qt(quat_roll);
-
- mul_qt_qtqt(quat_final, quat_snap, quat_roll);
- normalize_qt(quat_final);
-
- /* compare 2 vector angles to find the least roll */
- invert_qt_qt_normalized(quat_final_inv, quat_final);
- mul_qt_v3(viewquat_align_inv, xaxis1);
- mul_qt_v3(quat_final_inv, xaxis2);
- angle = angle_v3v3(xaxis1, xaxis2);
-
- if (angle <= best_angle) {
- found = true;
- best_angle = angle;
- copy_qt_qt(quat_best, quat_final);
- }
- }
-
- if (found) {
- /* lock 'quat_best' to an axis view if we can */
- ED_view3d_quat_to_axis_view(quat_best, 0.01f, &rv3d->view, &rv3d->view_axis_roll);
- if (rv3d->view != RV3D_VIEW_USER) {
- ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_best);
- }
- }
- else {
- copy_qt_qt(quat_best, viewquat_align);
- }
-
- copy_qt_qt(rv3d->viewquat, quat_best);
-
- viewrotate_apply_dyn_ofs(vod, rv3d->viewquat);
-
- if (U.uiflag & USER_AUTOPERSP) {
- if (RV3D_VIEW_IS_AXIS(rv3d->view)) {
- if (rv3d->persp == RV3D_PERSP) {
- rv3d->persp = RV3D_ORTHO;
- }
- }
- }
- }
- else if (U.uiflag & USER_AUTOPERSP) {
- rv3d->persp = vod->init.persp;
- }
-}
-
-static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2])
-{
- RegionView3D *rv3d = vod->rv3d;
-
- rv3d->view = RV3D_VIEW_USER; /* need to reset every time because of view snapping */
-
- if (U.flag & USER_TRACKBALL) {
- float axis[3], q1[4], dvec[3], newvec[3];
- float angle;
-
- {
- const int event_xy_offset[2] = {
- event_xy[0] + vod->init.event_xy_offset[0],
- event_xy[1] + vod->init.event_xy_offset[1],
- };
- calctrackballvec(&vod->region->winrct, event_xy_offset, newvec);
- }
-
- sub_v3_v3v3(dvec, newvec, vod->init.trackvec);
-
- angle = (len_v3(dvec) / (2.0f * TRACKBALLSIZE)) * (float)M_PI;
-
- /* Before applying the sensitivity this is rotating 1:1,
- * where the cursor would match the surface of a sphere in the view. */
- angle *= U.view_rotate_sensitivity_trackball;
-
- /* Allow for rotation beyond the interval [-pi, pi] */
- angle = angle_wrap_rad(angle);
-
- /* This relation is used instead of the actual angle between vectors
- * so that the angle of rotation is linearly proportional to
- * the distance that the mouse is dragged. */
-
- cross_v3_v3v3(axis, vod->init.trackvec, newvec);
- axis_angle_to_quat(q1, axis, angle);
-
- mul_qt_qtqt(vod->curr.viewquat, q1, vod->init.quat);
-
- viewrotate_apply_dyn_ofs(vod, vod->curr.viewquat);
- }
- else {
- float quat_local_x[4], quat_global_z[4];
- float m[3][3];
- float m_inv[3][3];
- const float zvec_global[3] = {0.0f, 0.0f, 1.0f};
- float xaxis[3];
-
- /* Radians per-pixel. */
- const float sensitivity = U.view_rotate_sensitivity_turntable / U.dpi_fac;
-
- /* Get the 3x3 matrix and its inverse from the quaternion */
- quat_to_mat3(m, vod->curr.viewquat);
- invert_m3_m3(m_inv, m);
-
- /* Avoid Gimbal Lock
- *
- * Even though turn-table mode is in use, this can occur when the user exits the camera view
- * or when aligning the view to a rotated object.
- *
- * We have gimbal lock when the user's view is rotated +/- 90 degrees along the view axis.
- * In this case the vertical rotation is the same as the sideways turntable motion.
- * Making it impossible to get out of the gimbal locked state without resetting the view.
- *
- * The logic below lets the user exit out of this state without any abrupt 'fix'
- * which would be disorienting.
- *
- * This works by blending two horizons:
- * - Rotated-horizon: `cross_v3_v3v3(xaxis, zvec_global, m_inv[2])`
- * When only this is used, this turntable rotation works - but it's side-ways
- * (as if the entire turn-table has been placed on its side)
- * While there is no gimbal lock, it's also awkward to use.
- * - Un-rotated-horizon: `m_inv[0]`
- * When only this is used, the turntable rotation can have gimbal lock.
- *
- * The solution used here is to blend between these two values,
- * so the severity of the gimbal lock is used to blend the rotated horizon.
- * Blending isn't essential, it just makes the transition smoother.
- *
- * This allows sideways turn-table rotation on a Z axis that isn't world-space Z,
- * While up-down turntable rotation eventually corrects gimbal lock. */
-#if 1
- if (len_squared_v3v3(zvec_global, m_inv[2]) > 0.001f) {
- float fac;
- cross_v3_v3v3(xaxis, zvec_global, m_inv[2]);
- if (dot_v3v3(xaxis, m_inv[0]) < 0) {
- negate_v3(xaxis);
- }
- fac = angle_normalized_v3v3(zvec_global, m_inv[2]) / (float)M_PI;
- fac = fabsf(fac - 0.5f) * 2;
- fac = fac * fac;
- interp_v3_v3v3(xaxis, xaxis, m_inv[0], fac);
- }
- else {
- copy_v3_v3(xaxis, m_inv[0]);
- }
-#else
- copy_v3_v3(xaxis, m_inv[0]);
-#endif
-
- /* Determine the direction of the x vector (for rotating up and down) */
- /* This can likely be computed directly from the quaternion. */
-
- /* Perform the up/down rotation */
- axis_angle_to_quat(quat_local_x, xaxis, sensitivity * -(event_xy[1] - vod->prev.event_xy[1]));
- mul_qt_qtqt(quat_local_x, vod->curr.viewquat, quat_local_x);
-
- /* Perform the orbital rotation */
- axis_angle_to_quat_single(
- quat_global_z, 'Z', sensitivity * vod->reverse * (event_xy[0] - vod->prev.event_xy[0]));
- mul_qt_qtqt(vod->curr.viewquat, quat_local_x, quat_global_z);
-
- viewrotate_apply_dyn_ofs(vod, vod->curr.viewquat);
- }
-
- /* avoid precision loss over time */
- normalize_qt(vod->curr.viewquat);
-
- /* use a working copy so view rotation locking doesn't overwrite the locked
- * rotation back into the view we calculate with */
- copy_qt_qt(rv3d->viewquat, vod->curr.viewquat);
-
- /* Check for view snap,
- * NOTE: don't apply snap to `vod->viewquat` so the view won't jam up. */
- if (vod->axis_snap) {
- viewrotate_apply_snap(vod);
- }
- vod->prev.event_xy[0] = event_xy[0];
- vod->prev.event_xy[1] = event_xy[1];
-
- ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, rv3d);
-
- ED_region_tag_redraw(vod->region);
-}
-
-static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod = op->customdata;
- short event_code = VIEW_PASS;
- bool use_autokey = false;
- int ret = OPERATOR_RUNNING_MODAL;
-
- /* execute the events */
- if (event->type == MOUSEMOVE) {
- event_code = VIEW_APPLY;
- }
- else if (event->type == EVT_MODAL_MAP) {
- switch (event->val) {
- case VIEW_MODAL_CONFIRM:
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
- vod->axis_snap = true;
- event_code = VIEW_APPLY;
- break;
- case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
- vod->rv3d->persp = vod->init.persp;
- vod->axis_snap = false;
- event_code = VIEW_APPLY;
- break;
- case VIEWROT_MODAL_SWITCH_ZOOM:
- WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_MOVE:
- WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- }
- }
- else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
- event_code = VIEW_CONFIRM;
- }
-
- if (event_code == VIEW_APPLY) {
- viewrotate_apply(vod, event->xy);
- if (ED_screen_animation_playing(CTX_wm_manager(C))) {
- use_autokey = true;
- }
- }
- else if (event_code == VIEW_CONFIRM) {
- use_autokey = true;
- ret = OPERATOR_FINISHED;
- }
-
- if (use_autokey) {
- ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, true);
- }
-
- if (ret & OPERATOR_FINISHED) {
- viewops_data_free(C, op);
- }
-
- return ret;
-}
-
-static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod;
-
- const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
-
- /* makes op->customdata */
- viewops_data_alloc(C, op);
- vod = op->customdata;
-
- /* poll should check but in some cases fails, see poll func for details */
- if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_ROTATION) {
- viewops_data_free(C, op);
- return OPERATOR_PASS_THROUGH;
- }
-
- ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
-
- viewops_data_create(C,
- op,
- event,
- viewops_flag_from_prefs() | VIEWOPS_FLAG_PERSP_ENSURE |
- (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
-
- if (ELEM(event->type, MOUSEPAN, MOUSEROTATE)) {
- /* Rotate direction we keep always same */
- int event_xy[2];
-
- if (event->type == MOUSEPAN) {
- if (event->is_direction_inverted) {
- event_xy[0] = 2 * event->xy[0] - event->prev_xy[0];
- event_xy[1] = 2 * event->xy[1] - event->prev_xy[1];
- }
- else {
- copy_v2_v2_int(event_xy, event->prev_xy);
- }
- }
- else {
- /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
- copy_v2_v2_int(event_xy, event->prev_xy);
- }
-
- viewrotate_apply(vod, event_xy);
-
- viewops_data_free(C, op);
-
- return OPERATOR_FINISHED;
- }
-
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static void viewrotate_cancel(bContext *C, wmOperator *op)
-{
- viewops_data_free(C, op);
-}
-
-void VIEW3D_OT_rotate(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Rotate View";
- ot->description = "Rotate the view";
- ot->idname = "VIEW3D_OT_rotate";
-
- /* api callbacks */
- ot->invoke = viewrotate_invoke;
- ot->modal = viewrotate_modal;
- ot->poll = ED_operator_region_view3d_active;
- ot->cancel = viewrotate_cancel;
-
- /* flags */
- ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
-
- view3d_operator_properties_common(ot, V3D_OP_PROP_USE_MOUSE_INIT);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name NDOF Utility Functions
- * \{ */
-
-#ifdef WITH_INPUT_NDOF
-static bool ndof_has_translate(const wmNDOFMotionData *ndof,
- const View3D *v3d,
- const RegionView3D *rv3d)
-{
- return !is_zero_v3(ndof->tvec) && (!ED_view3d_offset_lock_check(v3d, rv3d));
-}
-
-static bool ndof_has_rotate(const wmNDOFMotionData *ndof, const RegionView3D *rv3d)
-{
- return !is_zero_v3(ndof->rvec) && ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0);
-}
-
-/**
- * \param depth_pt: A point to calculate the depth (in perspective mode)
- */
-static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth_pt[3])
-{
- float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND;
-
- if (rv3d->is_persp) {
- speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL);
- }
-
- return speed;
-}
-
-static float view3d_ndof_pan_speed_calc_from_dist(RegionView3D *rv3d, const float dist)
-{
- float viewinv[4];
- float tvec[3];
-
- BLI_assert(dist >= 0.0f);
-
- copy_v3_fl3(tvec, 0.0f, 0.0f, dist);
- /* rv3d->viewinv isn't always valid */
-# if 0
- mul_mat3_m4_v3(rv3d->viewinv, tvec);
-# else
- invert_qt_qt_normalized(viewinv, rv3d->viewquat);
- mul_qt_v3(viewinv, tvec);
-# endif
-
- return view3d_ndof_pan_speed_calc_ex(rv3d, tvec);
-}
-
-static float view3d_ndof_pan_speed_calc(RegionView3D *rv3d)
-{
- float tvec[3];
- negate_v3_v3(tvec, rv3d->ofs);
-
- return view3d_ndof_pan_speed_calc_ex(rv3d, tvec);
-}
-
-/**
- * Zoom and pan in the same function since sometimes zoom is interpreted as dolly (pan forward).
- *
- * \param has_zoom: zoom, otherwise dolly,
- * often `!rv3d->is_persp` since it doesn't make sense to dolly in ortho.
- */
-static void view3d_ndof_pan_zoom(const struct wmNDOFMotionData *ndof,
- ScrArea *area,
- ARegion *region,
- const bool has_translate,
- const bool has_zoom)
-{
- RegionView3D *rv3d = region->regiondata;
- float view_inv[4];
- float pan_vec[3];
-
- if (has_translate == false && has_zoom == false) {
- return;
- }
-
- WM_event_ndof_pan_get(ndof, pan_vec, false);
-
- if (has_zoom) {
- /* zoom with Z */
-
- /* Zoom!
- * velocity should be proportional to the linear velocity attained by rotational motion
- * of same strength [got that?] proportional to `arclength = radius * angle`.
- */
-
- pan_vec[2] = 0.0f;
-
- /* "zoom in" or "translate"? depends on zoom mode in user settings? */
- if (ndof->tvec[2]) {
- float zoom_distance = rv3d->dist * ndof->dt * ndof->tvec[2];
-
- if (U.ndof_flag & NDOF_ZOOM_INVERT) {
- zoom_distance = -zoom_distance;
- }
-
- rv3d->dist += zoom_distance;
- }
- }
- else {
- /* dolly with Z */
-
- /* all callers must check */
- if (has_translate) {
- BLI_assert(ED_view3d_offset_lock_check((View3D *)area->spacedata.first, rv3d) == false);
- }
- }
-
- if (has_translate) {
- const float speed = view3d_ndof_pan_speed_calc(rv3d);
-
- mul_v3_fl(pan_vec, speed * ndof->dt);
-
- /* transform motion from view to world coordinates */
- invert_qt_qt_normalized(view_inv, rv3d->viewquat);
- mul_qt_v3(view_inv, pan_vec);
-
- /* move center of view opposite of hand motion (this is camera mode, not object mode) */
- sub_v3_v3(rv3d->ofs, pan_vec);
-
- if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_sync(area, region);
- }
- }
-}
-
-static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof,
- ScrArea *area,
- ARegion *region,
- ViewOpsData *vod,
- const bool apply_dyn_ofs)
-{
- View3D *v3d = area->spacedata.first;
- RegionView3D *rv3d = region->regiondata;
-
- float view_inv[4];
-
- BLI_assert((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0);
-
- ED_view3d_persp_ensure(vod->depsgraph, v3d, region);
-
- rv3d->view = RV3D_VIEW_USER;
-
- invert_qt_qt_normalized(view_inv, rv3d->viewquat);
-
- if (U.ndof_flag & NDOF_TURNTABLE) {
- float rot[3];
-
- /* Turntable view code adapted for 3D mouse use. */
- float angle, quat[4];
- float xvec[3] = {1, 0, 0};
-
- /* only use XY, ignore Z */
- WM_event_ndof_rotate_get(ndof, rot);
-
- /* Determine the direction of the x vector (for rotating up and down) */
- mul_qt_v3(view_inv, xvec);
-
- /* Perform the up/down rotation */
- angle = ndof->dt * rot[0];
- axis_angle_to_quat(quat, xvec, angle);
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
-
- /* Perform the orbital rotation */
- angle = ndof->dt * rot[1];
-
- /* update the onscreen doo-dad */
- rv3d->rot_angle = angle;
- rv3d->rot_axis[0] = 0;
- rv3d->rot_axis[1] = 0;
- rv3d->rot_axis[2] = 1;
-
- axis_angle_to_quat_single(quat, 'Z', angle);
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
- }
- else {
- float quat[4];
- float axis[3];
- float angle = WM_event_ndof_to_axis_angle(ndof, axis);
-
- /* transform rotation axis from view to world coordinates */
- mul_qt_v3(view_inv, axis);
-
- /* update the onscreen doo-dad */
- rv3d->rot_angle = angle;
- copy_v3_v3(rv3d->rot_axis, axis);
-
- axis_angle_to_quat(quat, axis, angle);
-
- /* apply rotation */
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
- }
-
- if (apply_dyn_ofs) {
- viewrotate_apply_dyn_ofs(vod, rv3d->viewquat);
- }
-}
-
-void view3d_ndof_fly(const wmNDOFMotionData *ndof,
- View3D *v3d,
- RegionView3D *rv3d,
- const bool use_precision,
- const short protectflag,
- bool *r_has_translate,
- bool *r_has_rotate)
-{
- bool has_translate = ndof_has_translate(ndof, v3d, rv3d);
- bool has_rotate = ndof_has_rotate(ndof, rv3d);
-
- float view_inv[4];
- invert_qt_qt_normalized(view_inv, rv3d->viewquat);
-
- rv3d->rot_angle = 0.0f; /* disable onscreen rotation doo-dad */
-
- if (has_translate) {
- /* ignore real 'dist' since fly has its own speed settings,
- * also its overwritten at this point. */
- float speed = view3d_ndof_pan_speed_calc_from_dist(rv3d, 1.0f);
- float trans[3], trans_orig_y;
-
- if (use_precision) {
- speed *= 0.2f;
- }
-
- WM_event_ndof_pan_get(ndof, trans, false);
- mul_v3_fl(trans, speed * ndof->dt);
- trans_orig_y = trans[1];
-
- if (U.ndof_flag & NDOF_FLY_HELICOPTER) {
- trans[1] = 0.0f;
- }
-
- /* transform motion from view to world coordinates */
- mul_qt_v3(view_inv, trans);
-
- if (U.ndof_flag & NDOF_FLY_HELICOPTER) {
- /* replace world z component with device y (yes it makes sense) */
- trans[2] = trans_orig_y;
- }
-
- if (rv3d->persp == RV3D_CAMOB) {
- /* respect camera position locks */
- if (protectflag & OB_LOCK_LOCX) {
- trans[0] = 0.0f;
- }
- if (protectflag & OB_LOCK_LOCY) {
- trans[1] = 0.0f;
- }
- if (protectflag & OB_LOCK_LOCZ) {
- trans[2] = 0.0f;
- }
- }
-
- if (!is_zero_v3(trans)) {
- /* move center of view opposite of hand motion
- * (this is camera mode, not object mode) */
- sub_v3_v3(rv3d->ofs, trans);
- has_translate = true;
- }
- else {
- has_translate = false;
- }
- }
-
- if (has_rotate) {
- const float turn_sensitivity = 1.0f;
-
- float rotation[4];
- float axis[3];
- float angle = turn_sensitivity * WM_event_ndof_to_axis_angle(ndof, axis);
-
- if (fabsf(angle) > 0.0001f) {
- has_rotate = true;
-
- if (use_precision) {
- angle *= 0.2f;
- }
-
- /* transform rotation axis from view to world coordinates */
- mul_qt_v3(view_inv, axis);
-
- /* apply rotation to view */
- axis_angle_to_quat(rotation, axis, angle);
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
-
- if (U.ndof_flag & NDOF_LOCK_HORIZON) {
- /* force an upright viewpoint
- * TODO: make this less... sudden */
- float view_horizon[3] = {1.0f, 0.0f, 0.0f}; /* view +x */
- float view_direction[3] = {0.0f, 0.0f, -1.0f}; /* view -z (into screen) */
-
- /* find new inverse since viewquat has changed */
- invert_qt_qt_normalized(view_inv, rv3d->viewquat);
- /* could apply reverse rotation to existing view_inv to save a few cycles */
-
- /* transform view vectors to world coordinates */
- mul_qt_v3(view_inv, view_horizon);
- mul_qt_v3(view_inv, view_direction);
-
- /* find difference between view & world horizons
- * true horizon lives in world xy plane, so look only at difference in z */
- angle = -asinf(view_horizon[2]);
-
- /* rotate view so view horizon = world horizon */
- axis_angle_to_quat(rotation, view_direction, angle);
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
- }
-
- rv3d->view = RV3D_VIEW_USER;
- }
- else {
- has_rotate = false;
- }
- }
-
- *r_has_translate = has_translate;
- *r_has_rotate = has_rotate;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name NDOF Orbit/Translate Operator
- * \{ */
-
-static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- if (event->type != NDOF_MOTION) {
- return OPERATOR_CANCELLED;
- }
-
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewOpsData *vod;
- View3D *v3d;
- RegionView3D *rv3d;
- char xform_flag = 0;
-
- const wmNDOFMotionData *ndof = event->customdata;
-
- viewops_data_alloc(C, op);
- viewops_data_create(
- C, op, event, viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0, false));
- vod = op->customdata;
-
- ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
-
- v3d = vod->v3d;
- rv3d = vod->rv3d;
-
- /* off by default, until changed later this function */
- rv3d->rot_angle = 0.0f;
-
- ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false);
-
- if (ndof->progress != P_FINISHING) {
- const bool has_rotation = ndof_has_rotate(ndof, rv3d);
- /* if we can't rotate, fallback to translate (locked axis views) */
- const bool has_translate = ndof_has_translate(ndof, v3d, rv3d) &&
- (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION);
- const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
-
- if (has_translate || has_zoom) {
- view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, has_zoom);
- xform_flag |= HAS_TRANSLATE;
- }
-
- if (has_rotation) {
- view3d_ndof_orbit(ndof, vod->area, vod->region, vod, true);
- xform_flag |= HAS_ROTATE;
- }
- }
-
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- if (xform_flag) {
- ED_view3d_camera_lock_autokey(
- v3d, rv3d, C, xform_flag & HAS_ROTATE, xform_flag & HAS_TRANSLATE);
- }
-
- ED_region_tag_redraw(vod->region);
-
- viewops_data_free(C, op);
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "NDOF Orbit View";
- ot->description = "Orbit the view using the 3D mouse";
- ot->idname = "VIEW3D_OT_ndof_orbit";
-
- /* api callbacks */
- ot->invoke = ndof_orbit_invoke;
- ot->poll = ED_operator_view3d_active;
-
- /* flags */
- ot->flag = 0;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name NDOF Orbit/Zoom Operator
- * \{ */
-
-static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- if (event->type != NDOF_MOTION) {
- return OPERATOR_CANCELLED;
- }
-
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewOpsData *vod;
- View3D *v3d;
- RegionView3D *rv3d;
- char xform_flag = 0;
-
- const wmNDOFMotionData *ndof = event->customdata;
-
- viewops_data_alloc(C, op);
- viewops_data_create(
- C, op, event, viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0, false));
-
- vod = op->customdata;
-
- ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
-
- v3d = vod->v3d;
- rv3d = vod->rv3d;
-
- /* off by default, until changed later this function */
- rv3d->rot_angle = 0.0f;
-
- ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false);
-
- if (ndof->progress == P_FINISHING) {
- /* pass */
- }
- else if ((rv3d->persp == RV3D_ORTHO) && RV3D_VIEW_IS_AXIS(rv3d->view)) {
- /* if we can't rotate, fallback to translate (locked axis views) */
- const bool has_translate = ndof_has_translate(ndof, v3d, rv3d);
- const bool has_zoom = (ndof->tvec[2] != 0.0f) && ED_view3d_offset_lock_check(v3d, rv3d);
-
- if (has_translate || has_zoom) {
- view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, true);
- xform_flag |= HAS_TRANSLATE;
- }
- }
- else {
- /* NOTE: based on feedback from T67579, users want to have pan and orbit enabled at once.
- * It's arguable that orbit shouldn't pan (since we have a pan only operator),
- * so if there are users who like to separate orbit/pan operations - it can be a preference. */
- const bool is_orbit_around_pivot = (U.ndof_flag & NDOF_MODE_ORBIT) ||
- ED_view3d_offset_lock_check(v3d, rv3d);
- const bool has_rotation = ndof_has_rotate(ndof, rv3d);
- bool has_translate, has_zoom;
-
- if (is_orbit_around_pivot) {
- /* Orbit preference or forced lock (Z zooms). */
- has_translate = !is_zero_v2(ndof->tvec) && ndof_has_translate(ndof, v3d, rv3d);
- has_zoom = (ndof->tvec[2] != 0.0f);
- }
- else {
- /* Free preference (Z translates). */
- has_translate = ndof_has_translate(ndof, v3d, rv3d);
- has_zoom = false;
- }
-
- /* Rotation first because dynamic offset resets offset otherwise (and disables panning). */
- if (has_rotation) {
- const float dist_backup = rv3d->dist;
- if (!is_orbit_around_pivot) {
- ED_view3d_distance_set(rv3d, 0.0f);
- }
- view3d_ndof_orbit(ndof, vod->area, vod->region, vod, is_orbit_around_pivot);
- xform_flag |= HAS_ROTATE;
- if (!is_orbit_around_pivot) {
- ED_view3d_distance_set(rv3d, dist_backup);
- }
- }
-
- if (has_translate || has_zoom) {
- view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, has_zoom);
- xform_flag |= HAS_TRANSLATE;
- }
- }
-
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- if (xform_flag) {
- ED_view3d_camera_lock_autokey(
- v3d, rv3d, C, xform_flag & HAS_ROTATE, xform_flag & HAS_TRANSLATE);
- }
-
- ED_region_tag_redraw(vod->region);
-
- viewops_data_free(C, op);
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "NDOF Orbit View with Zoom";
- ot->description = "Orbit and zoom the view using the 3D mouse";
- ot->idname = "VIEW3D_OT_ndof_orbit_zoom";
-
- /* api callbacks */
- ot->invoke = ndof_orbit_zoom_invoke;
- ot->poll = ED_operator_view3d_active;
-
- /* flags */
- ot->flag = 0;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name NDOF Pan/Zoom Operator
- * \{ */
-
-static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
-{
- if (event->type != NDOF_MOTION) {
- return OPERATOR_CANCELLED;
- }
-
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- const wmNDOFMotionData *ndof = event->customdata;
- char xform_flag = 0;
-
- const bool has_translate = ndof_has_translate(ndof, v3d, rv3d);
- const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
-
- /* we're panning here! so erase any leftover rotation from other operators */
- rv3d->rot_angle = 0.0f;
-
- if (!(has_translate || has_zoom)) {
- return OPERATOR_CANCELLED;
- }
-
- ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false);
-
- if (ndof->progress != P_FINISHING) {
- ScrArea *area = CTX_wm_area(C);
- ARegion *region = CTX_wm_region(C);
-
- if (has_translate || has_zoom) {
- view3d_ndof_pan_zoom(ndof, area, region, has_translate, has_zoom);
- xform_flag |= HAS_TRANSLATE;
- }
- }
-
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- if (xform_flag) {
- ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, xform_flag & HAS_TRANSLATE);
- }
-
- ED_region_tag_redraw(CTX_wm_region(C));
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "NDOF Pan View";
- ot->description = "Pan the view with the 3D mouse";
- ot->idname = "VIEW3D_OT_ndof_pan";
-
- /* api callbacks */
- ot->invoke = ndof_pan_invoke;
- ot->poll = ED_operator_view3d_active;
-
- /* flags */
- ot->flag = 0;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name NDOF Transform All Operator
- * \{ */
-
-/**
- * wraps #ndof_orbit_zoom but never restrict to orbit.
- */
-static int ndof_all_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- /* weak!, but it works */
- const int ndof_flag = U.ndof_flag;
- int ret;
-
- U.ndof_flag &= ~NDOF_MODE_ORBIT;
-
- ret = ndof_orbit_zoom_invoke(C, op, event);
-
- U.ndof_flag = ndof_flag;
-
- return ret;
-}
-
-void VIEW3D_OT_ndof_all(struct wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "NDOF Transform View";
- ot->description = "Pan and rotate the view with the 3D mouse";
- ot->idname = "VIEW3D_OT_ndof_all";
-
- /* api callbacks */
- ot->invoke = ndof_all_invoke;
- ot->poll = ED_operator_view3d_active;
-
- /* flags */
- ot->flag = 0;
-}
-
-#endif /* WITH_INPUT_NDOF */
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Move (Pan) Operator
- * \{ */
-
-/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
-
-void viewmove_modal_keymap(wmKeyConfig *keyconf)
-{
- static const EnumPropertyItem modal_items[] = {
- {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
-
- {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
- {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
-
- {0, NULL, 0, NULL, NULL},
- };
-
- wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Move Modal");
-
- /* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items) {
- return;
- }
-
- keymap = WM_modalkeymap_ensure(keyconf, "View3D Move Modal", modal_items);
-
- /* items for modal map */
- WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
- WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
-
- /* disabled mode switching for now, can re-implement better, later on */
-#if 0
- WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
- WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
- WM_modalkeymap_add_item(
- keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
-#endif
-
- /* assign map to operators */
- WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
-}
-
-static void viewmove_apply(ViewOpsData *vod, int x, int y)
-{
- if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) {
- vod->rv3d->ofs_lock[0] -= ((vod->prev.event_xy[0] - x) * 2.0f) / (float)vod->region->winx;
- vod->rv3d->ofs_lock[1] -= ((vod->prev.event_xy[1] - y) * 2.0f) / (float)vod->region->winy;
- }
- else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
- const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
- vod->rv3d->camdx += (vod->prev.event_xy[0] - x) / (vod->region->winx * zoomfac);
- vod->rv3d->camdy += (vod->prev.event_xy[1] - y) / (vod->region->winy * zoomfac);
- CLAMP(vod->rv3d->camdx, -1.0f, 1.0f);
- CLAMP(vod->rv3d->camdy, -1.0f, 1.0f);
- }
- else {
- float dvec[3];
- float mval_f[2];
-
- mval_f[0] = x - vod->prev.event_xy[0];
- mval_f[1] = y - vod->prev.event_xy[1];
- ED_view3d_win_to_delta(vod->region, mval_f, dvec, vod->init.zfac);
-
- add_v3_v3(vod->rv3d->ofs, dvec);
-
- if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_sync(vod->area, vod->region);
- }
- }
-
- vod->prev.event_xy[0] = x;
- vod->prev.event_xy[1] = y;
-
- ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
-
- ED_region_tag_redraw(vod->region);
-}
-
-static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
-
- ViewOpsData *vod = op->customdata;
- short event_code = VIEW_PASS;
- bool use_autokey = false;
- int ret = OPERATOR_RUNNING_MODAL;
-
- /* execute the events */
- if (event->type == MOUSEMOVE) {
- event_code = VIEW_APPLY;
- }
- else if (event->type == EVT_MODAL_MAP) {
- switch (event->val) {
- case VIEW_MODAL_CONFIRM:
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_ZOOM:
- WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_ROTATE:
- WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- }
- }
- else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
- event_code = VIEW_CONFIRM;
- }
-
- if (event_code == VIEW_APPLY) {
- viewmove_apply(vod, event->xy[0], event->xy[1]);
- if (ED_screen_animation_playing(CTX_wm_manager(C))) {
- use_autokey = true;
- }
- }
- else if (event_code == VIEW_CONFIRM) {
- use_autokey = true;
- ret = OPERATOR_FINISHED;
- }
-
- if (use_autokey) {
- ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
- }
-
- if (ret & OPERATOR_FINISHED) {
- viewops_data_free(C, op);
- }
-
- return ret;
-}
-
-static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod;
-
- const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
-
- /* makes op->customdata */
- viewops_data_alloc(C, op);
- vod = op->customdata;
- if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_LOCATION) {
- viewops_data_free(C, op);
- return OPERATOR_PASS_THROUGH;
- }
-
- viewops_data_create(C,
- op,
- event,
- (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
- (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
-
- ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
-
- if (event->type == MOUSEPAN) {
- /* invert it, trackpad scroll follows same principle as 2d windows this way */
- viewmove_apply(
- vod, 2 * event->xy[0] - event->prev_xy[0], 2 * event->xy[1] - event->prev_xy[1]);
-
- viewops_data_free(C, op);
-
- return OPERATOR_FINISHED;
- }
-
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static void viewmove_cancel(bContext *C, wmOperator *op)
-{
- viewops_data_free(C, op);
-}
-
-void VIEW3D_OT_move(wmOperatorType *ot)
-{
-
- /* identifiers */
- ot->name = "Pan View";
- ot->description = "Move the view";
- ot->idname = "VIEW3D_OT_move";
-
- /* api callbacks */
- ot->invoke = viewmove_invoke;
- ot->modal = viewmove_modal;
- ot->poll = ED_operator_region_view3d_active;
- ot->cancel = viewmove_cancel;
-
- /* flags */
- ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
-
- /* properties */
- view3d_operator_properties_common(ot, V3D_OP_PROP_USE_MOUSE_INIT);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Zoom Operator
- * \{ */
-
-/* #viewdolly_modal_keymap has an exact copy of this, apply fixes to both. */
-void viewzoom_modal_keymap(wmKeyConfig *keyconf)
-{
- static const EnumPropertyItem modal_items[] = {
- {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
-
- {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
- {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
-
- {0, NULL, 0, NULL, NULL},
- };
-
- wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Zoom Modal");
-
- /* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items) {
- return;
- }
-
- keymap = WM_modalkeymap_ensure(keyconf, "View3D Zoom Modal", modal_items);
-
- /* disabled mode switching for now, can re-implement better, later on */
-#if 0
- WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
- WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
- WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
-#endif
-
- /* assign map to operators */
- WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
-}
-
-/**
- * \param zoom_xy: Optionally zoom to window location
- * (coords compatible w/ #wmEvent.xy). Use when not NULL.
- */
-static void view_zoom_to_window_xy_camera(Scene *scene,
- Depsgraph *depsgraph,
- View3D *v3d,
- ARegion *region,
- float dfac,
- const int zoom_xy[2])
-{
- RegionView3D *rv3d = region->regiondata;
- const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom);
- const float zoomfac_new = clamp_f(
- zoomfac * (1.0f / dfac), RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR);
- const float camzoom_new = BKE_screen_view3d_zoom_from_fac(zoomfac_new);
-
- if (zoom_xy != NULL) {
- float zoomfac_px;
- rctf camera_frame_old;
- rctf camera_frame_new;
-
- const float pt_src[2] = {zoom_xy[0], zoom_xy[1]};
- float pt_dst[2];
- float delta_px[2];
-
- ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_old, false);
- BLI_rctf_translate(&camera_frame_old, region->winrct.xmin, region->winrct.ymin);
-
- rv3d->camzoom = camzoom_new;
- CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
-
- ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_new, false);
- BLI_rctf_translate(&camera_frame_new, region->winrct.xmin, region->winrct.ymin);
-
- BLI_rctf_transform_pt_v(&camera_frame_new, &camera_frame_old, pt_dst, pt_src);
- sub_v2_v2v2(delta_px, pt_dst, pt_src);
-
- /* translate the camera offset using pixel space delta
- * mapped back to the camera (same logic as panning in camera view) */
- zoomfac_px = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f;
-
- rv3d->camdx += delta_px[0] / (region->winx * zoomfac_px);
- rv3d->camdy += delta_px[1] / (region->winy * zoomfac_px);
- CLAMP(rv3d->camdx, -1.0f, 1.0f);
- CLAMP(rv3d->camdy, -1.0f, 1.0f);
- }
- else {
- rv3d->camzoom = camzoom_new;
- CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
- }
-}
-
-/**
- * \param zoom_xy: Optionally zoom to window location
- * (coords compatible w/ #wmEvent.xy). Use when not NULL.
- */
-static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoom_xy[2])
-{
- RegionView3D *rv3d = region->regiondata;
- const float dist_new = rv3d->dist * dfac;
-
- if (zoom_xy != NULL) {
- float dvec[3];
- float tvec[3];
- float tpos[3];
- float mval_f[2];
-
- float zfac;
-
- negate_v3_v3(tpos, rv3d->ofs);
-
- mval_f[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f;
- mval_f[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f;
-
- /* Project cursor position into 3D space */
- zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL);
- ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
-
- /* Calculate view target position for dolly */
- add_v3_v3v3(tvec, tpos, dvec);
- negate_v3(tvec);
-
- /* Offset to target position and dolly */
- copy_v3_v3(rv3d->ofs, tvec);
- rv3d->dist = dist_new;
-
- /* Calculate final offset */
- madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
- }
- else {
- rv3d->dist = dist_new;
- }
-}
-
-static float viewzoom_scale_value(const rcti *winrct,
- const eViewZoom_Style viewzoom,
- const bool zoom_invert,
- const bool zoom_invert_force,
- const int xy_curr[2],
- const int xy_init[2],
- const float val,
- const float val_orig,
- double *r_timer_lastdraw)
-{
- float zfac;
-
- if (viewzoom == USER_ZOOM_CONTINUE) {
- double time = PIL_check_seconds_timer();
- float time_step = (float)(time - *r_timer_lastdraw);
- float fac;
-
- if (U.uiflag & USER_ZOOM_HORIZ) {
- fac = (float)(xy_init[0] - xy_curr[0]);
- }
- else {
- fac = (float)(xy_init[1] - xy_curr[1]);
- }
-
- fac /= U.dpi_fac;
-
- if (zoom_invert != zoom_invert_force) {
- fac = -fac;
- }
-
- zfac = 1.0f + ((fac / 20.0f) * time_step);
- *r_timer_lastdraw = time;
- }
- else if (viewzoom == USER_ZOOM_SCALE) {
- /* method which zooms based on how far you move the mouse */
-
- const int ctr[2] = {
- BLI_rcti_cent_x(winrct),
- BLI_rcti_cent_y(winrct),
- };
- float len_new = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_curr) / U.dpi_fac);
- float len_old = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_init) / U.dpi_fac);
-
- /* intentionally ignore 'zoom_invert' for scale */
- if (zoom_invert_force) {
- SWAP(float, len_new, len_old);
- }
-
- zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val;
- }
- else { /* USER_ZOOM_DOLLY */
- float len_new = 5 * U.dpi_fac;
- float len_old = 5 * U.dpi_fac;
-
- if (U.uiflag & USER_ZOOM_HORIZ) {
- len_new += (winrct->xmax - (xy_curr[0])) / U.dpi_fac;
- len_old += (winrct->xmax - (xy_init[0])) / U.dpi_fac;
- }
- else {
- len_new += (winrct->ymax - (xy_curr[1])) / U.dpi_fac;
- len_old += (winrct->ymax - (xy_init[1])) / U.dpi_fac;
- }
-
- if (zoom_invert != zoom_invert_force) {
- SWAP(float, len_new, len_old);
- }
-
- zfac = val_orig * (2.0f * ((len_new / max_ff(len_old, 1.0f)) - 1.0f) + 1.0f) / val;
- }
-
- return zfac;
-}
-
-static float viewzoom_scale_value_offset(const rcti *winrct,
- const eViewZoom_Style viewzoom,
- const bool zoom_invert,
- const bool zoom_invert_force,
- const int xy_curr[2],
- const int xy_init[2],
- const int xy_offset[2],
- const float val,
- const float val_orig,
- double *r_timer_lastdraw)
-{
- const int xy_curr_offset[2] = {
- xy_curr[0] + xy_offset[0],
- xy_curr[1] + xy_offset[1],
- };
- const int xy_init_offset[2] = {
- xy_init[0] + xy_offset[0],
- xy_init[1] + xy_offset[1],
- };
- return viewzoom_scale_value(winrct,
- viewzoom,
- zoom_invert,
- zoom_invert_force,
- xy_curr_offset,
- xy_init_offset,
- val,
- val_orig,
- r_timer_lastdraw);
-}
-
-static void viewzoom_apply_camera(ViewOpsData *vod,
- const int xy[2],
- const eViewZoom_Style viewzoom,
- const bool zoom_invert,
- const bool zoom_to_pos)
-{
- float zfac;
- float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->init.camzoom) * 2.0f;
- float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
-
- zfac = viewzoom_scale_value_offset(&vod->region->winrct,
- viewzoom,
- zoom_invert,
- true,
- xy,
- vod->init.event_xy,
- vod->init.event_xy_offset,
- zoomfac,
- zoomfac_prev,
- &vod->prev.time);
-
- if (!ELEM(zfac, 1.0f, 0.0f)) {
- /* calculate inverted, then invert again (needed because of camera zoom scaling) */
- zfac = 1.0f / zfac;
- view_zoom_to_window_xy_camera(vod->scene,
- vod->depsgraph,
- vod->v3d,
- vod->region,
- zfac,
- zoom_to_pos ? vod->prev.event_xy : NULL);
- }
-
- ED_region_tag_redraw(vod->region);
-}
-
-static void viewzoom_apply_3d(ViewOpsData *vod,
- const int xy[2],
- const eViewZoom_Style viewzoom,
- const bool zoom_invert,
- const bool zoom_to_pos)
-{
- float zfac;
- float dist_range[2];
-
- ED_view3d_dist_range_get(vod->v3d, dist_range);
-
- zfac = viewzoom_scale_value_offset(&vod->region->winrct,
- viewzoom,
- zoom_invert,
- false,
- xy,
- vod->init.event_xy,
- vod->init.event_xy_offset,
- vod->rv3d->dist,
- vod->init.dist,
- &vod->prev.time);
-
- if (zfac != 1.0f) {
- const float zfac_min = dist_range[0] / vod->rv3d->dist;
- const float zfac_max = dist_range[1] / vod->rv3d->dist;
- CLAMP(zfac, zfac_min, zfac_max);
-
- view_zoom_to_window_xy_3d(vod->region, zfac, zoom_to_pos ? vod->prev.event_xy : NULL);
- }
-
- /* these limits were in old code too */
- CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]);
-
- if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_sync(vod->area, vod->region);
- }
-
- ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
-
- ED_region_tag_redraw(vod->region);
-}
-
-static void viewzoom_apply(ViewOpsData *vod,
- const int xy[2],
- const eViewZoom_Style viewzoom,
- const bool zoom_invert,
- const bool zoom_to_pos)
-{
- if ((vod->rv3d->persp == RV3D_CAMOB) &&
- (vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0) {
- viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert, zoom_to_pos);
- }
- else {
- viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert, zoom_to_pos);
- }
-}
-
-static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod = op->customdata;
- short event_code = VIEW_PASS;
- bool use_autokey = false;
- int ret = OPERATOR_RUNNING_MODAL;
-
- /* execute the events */
- if (event->type == TIMER && event->customdata == vod->timer) {
- /* continuous zoom */
- event_code = VIEW_APPLY;
- }
- else if (event->type == MOUSEMOVE) {
- event_code = VIEW_APPLY;
- }
- else if (event->type == EVT_MODAL_MAP) {
- switch (event->val) {
- case VIEW_MODAL_CONFIRM:
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_MOVE:
- WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_ROTATE:
- WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- }
- }
- else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
- event_code = VIEW_CONFIRM;
- }
-
- if (event_code == VIEW_APPLY) {
- const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
- viewzoom_apply(vod,
- event->xy,
- (eViewZoom_Style)U.viewzoom,
- (U.uiflag & USER_ZOOM_INVERT) != 0,
- (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
- if (ED_screen_animation_playing(CTX_wm_manager(C))) {
- use_autokey = true;
- }
- }
- else if (event_code == VIEW_CONFIRM) {
- use_autokey = true;
- ret = OPERATOR_FINISHED;
- }
-
- if (use_autokey) {
- ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
- }
-
- if (ret & OPERATOR_FINISHED) {
- viewops_data_free(C, op);
- }
-
- return ret;
-}
-
-static int viewzoom_exec(bContext *C, wmOperator *op)
-{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene = CTX_data_scene(C);
- View3D *v3d;
- RegionView3D *rv3d;
- ScrArea *area;
- ARegion *region;
- bool use_cam_zoom;
- float dist_range[2];
-
- const int delta = RNA_int_get(op->ptr, "delta");
- const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
-
- if (op->customdata) {
- ViewOpsData *vod = op->customdata;
-
- area = vod->area;
- region = vod->region;
- }
- else {
- area = CTX_wm_area(C);
- region = CTX_wm_region(C);
- }
-
- v3d = area->spacedata.first;
- rv3d = region->regiondata;
-
- use_cam_zoom = (rv3d->persp == RV3D_CAMOB) &&
- !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d));
-
- int zoom_xy_buf[2];
- const int *zoom_xy = NULL;
- if (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) {
- zoom_xy_buf[0] = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") :
- region->winx / 2;
- zoom_xy_buf[1] = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") :
- region->winy / 2;
- zoom_xy = zoom_xy_buf;
- }
-
- ED_view3d_dist_range_get(v3d, dist_range);
-
- if (delta < 0) {
- const float step = 1.2f;
- /* this min and max is also in viewmove() */
- if (use_cam_zoom) {
- view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy);
- }
- else {
- if (rv3d->dist < dist_range[1]) {
- view_zoom_to_window_xy_3d(region, step, zoom_xy);
- }
- }
- }
- else {
- const float step = 1.0f / 1.2f;
- if (use_cam_zoom) {
- view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy);
- }
- else {
- if (rv3d->dist > dist_range[0]) {
- view_zoom_to_window_xy_3d(region, step, zoom_xy);
- }
- }
- }
-
- if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_sync(area, region);
- }
-
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true);
-
- ED_region_tag_redraw(region);
-
- viewops_data_free(C, op);
-
- return OPERATOR_FINISHED;
-}
-
-/* viewdolly_invoke() copied this function, changes here may apply there */
-static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod;
-
- const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
-
- /* makes op->customdata */
- viewops_data_alloc(C, op);
- viewops_data_create(C,
- op,
- event,
- (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
- (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
- vod = op->customdata;
-
- ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
-
- /* if one or the other zoom position aren't set, set from event */
- if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
- RNA_int_set(op->ptr, "mx", event->xy[0]);
- RNA_int_set(op->ptr, "my", event->xy[1]);
- }
-
- if (RNA_struct_property_is_set(op->ptr, "delta")) {
- viewzoom_exec(C, op);
- }
- else {
- if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
-
- if (U.uiflag & USER_ZOOM_HORIZ) {
- vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
- }
- else {
- /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
- vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] -
- event->prev_xy[0];
- }
- viewzoom_apply(vod,
- event->prev_xy,
- USER_ZOOM_DOLLY,
- (U.uiflag & USER_ZOOM_INVERT) != 0,
- (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
- ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
-
- viewops_data_free(C, op);
- return OPERATOR_FINISHED;
- }
-
- if (U.viewzoom == USER_ZOOM_CONTINUE) {
- /* needs a timer to continue redrawing */
- vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
- vod->prev.time = PIL_check_seconds_timer();
- }
-
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
-
- return OPERATOR_RUNNING_MODAL;
- }
- return OPERATOR_FINISHED;
-}
-
-static void viewzoom_cancel(bContext *C, wmOperator *op)
-{
- viewops_data_free(C, op);
-}
-
-void VIEW3D_OT_zoom(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Zoom View";
- ot->description = "Zoom in/out in the view";
- ot->idname = "VIEW3D_OT_zoom";
-
- /* api callbacks */
- ot->invoke = viewzoom_invoke;
- ot->exec = viewzoom_exec;
- ot->modal = viewzoom_modal;
- ot->poll = view3d_zoom_or_dolly_poll;
- ot->cancel = viewzoom_cancel;
-
- /* flags */
- ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
-
- /* properties */
- view3d_operator_properties_common(
- ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Dolly Operator
- *
- * Like zoom but translates the view offset along the view direction
- * which avoids #RegionView3D.dist approaching zero.
- * \{ */
-
-/* This is an exact copy of #viewzoom_modal_keymap. */
-void viewdolly_modal_keymap(wmKeyConfig *keyconf)
-{
- static const EnumPropertyItem modal_items[] = {
- {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
-
- {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
- {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
-
- {0, NULL, 0, NULL, NULL},
- };
-
- wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Dolly Modal");
-
- /* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items) {
- return;
- }
-
- keymap = WM_modalkeymap_ensure(keyconf, "View3D Dolly Modal", modal_items);
-
- /* disabled mode switching for now, can re-implement better, later on */
-#if 0
- WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
- WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
- WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
-#endif
-
- /* assign map to operators */
- WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly");
-}
-
-static bool viewdolly_offset_lock_check(bContext *C, wmOperator *op)
-{
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- if (ED_view3d_offset_lock_check(v3d, rv3d)) {
- BKE_report(op->reports, RPT_WARNING, "Cannot dolly when the view offset is locked");
- return true;
- }
- return false;
-}
-
-static void view_dolly_to_vector_3d(ARegion *region,
- const float orig_ofs[3],
- const float dvec[3],
- float dfac)
-{
- RegionView3D *rv3d = region->regiondata;
- madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac));
-}
-
-static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert)
-{
- float zfac = 1.0;
-
- {
- float len1, len2;
-
- if (U.uiflag & USER_ZOOM_HORIZ) {
- len1 = (vod->region->winrct.xmax - xy[0]) + 5;
- len2 = (vod->region->winrct.xmax - vod->init.event_xy[0]) + 5;
- }
- else {
- len1 = (vod->region->winrct.ymax - xy[1]) + 5;
- len2 = (vod->region->winrct.ymax - vod->init.event_xy[1]) + 5;
- }
- if (zoom_invert) {
- SWAP(float, len1, len2);
- }
-
- zfac = 1.0f + ((len1 - len2) * 0.01f * vod->rv3d->dist);
- }
-
- if (zfac != 1.0f) {
- view_dolly_to_vector_3d(vod->region, vod->init.ofs, vod->init.mousevec, zfac);
- }
-
- if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_sync(vod->area, vod->region);
- }
-
- ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
-
- ED_region_tag_redraw(vod->region);
-}
-
-static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod = op->customdata;
- short event_code = VIEW_PASS;
- bool use_autokey = false;
- int ret = OPERATOR_RUNNING_MODAL;
-
- /* execute the events */
- if (event->type == MOUSEMOVE) {
- event_code = VIEW_APPLY;
- }
- else if (event->type == EVT_MODAL_MAP) {
- switch (event->val) {
- case VIEW_MODAL_CONFIRM:
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_MOVE:
- WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_ROTATE:
- WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- }
- }
- else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
- event_code = VIEW_CONFIRM;
- }
-
- if (event_code == VIEW_APPLY) {
- viewdolly_apply(vod, event->xy, (U.uiflag & USER_ZOOM_INVERT) != 0);
- if (ED_screen_animation_playing(CTX_wm_manager(C))) {
- use_autokey = true;
- }
- }
- else if (event_code == VIEW_CONFIRM) {
- use_autokey = true;
- ret = OPERATOR_FINISHED;
- }
-
- if (use_autokey) {
- ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
- }
-
- if (ret & OPERATOR_FINISHED) {
- viewops_data_free(C, op);
- }
-
- return ret;
-}
-
-static int viewdolly_exec(bContext *C, wmOperator *op)
-{
- View3D *v3d;
- RegionView3D *rv3d;
- ScrArea *area;
- ARegion *region;
- float mousevec[3];
-
- const int delta = RNA_int_get(op->ptr, "delta");
-
- if (op->customdata) {
- ViewOpsData *vod = op->customdata;
-
- area = vod->area;
- region = vod->region;
- copy_v3_v3(mousevec, vod->init.mousevec);
- }
- else {
- area = CTX_wm_area(C);
- region = CTX_wm_region(C);
- negate_v3_v3(mousevec, ((RegionView3D *)region->regiondata)->viewinv[2]);
- normalize_v3(mousevec);
- }
-
- v3d = area->spacedata.first;
- rv3d = region->regiondata;
-
- const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
-
- /* overwrite the mouse vector with the view direction (zoom into the center) */
- if ((use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) == 0) {
- normalize_v3_v3(mousevec, rv3d->viewinv[2]);
- negate_v3(mousevec);
- }
-
- view_dolly_to_vector_3d(region, rv3d->ofs, mousevec, delta < 0 ? 1.8f : 0.2f);
-
- if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_sync(area, region);
- }
-
- ED_view3d_camera_lock_sync(CTX_data_ensure_evaluated_depsgraph(C), v3d, rv3d);
-
- ED_region_tag_redraw(region);
-
- viewops_data_free(C, op);
-
- return OPERATOR_FINISHED;
-}
-
-/* copied from viewzoom_invoke(), changes here may apply there */
-static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod;
-
- if (viewdolly_offset_lock_check(C, op)) {
- return OPERATOR_CANCELLED;
- }
-
- /* makes op->customdata */
- viewops_data_alloc(C, op);
- vod = op->customdata;
-
- /* poll should check but in some cases fails, see poll func for details */
- if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_ROTATION) {
- viewops_data_free(C, op);
- return OPERATOR_PASS_THROUGH;
- }
-
- ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
-
- /* needs to run before 'viewops_data_create' so the backup 'rv3d->ofs' is correct */
- /* switch from camera view when: */
- if (vod->rv3d->persp != RV3D_PERSP) {
- if (vod->rv3d->persp == RV3D_CAMOB) {
- /* ignore rv3d->lpersp because dolly only makes sense in perspective mode */
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_persp_switch_from_camera(depsgraph, vod->v3d, vod->rv3d, RV3D_PERSP);
- }
- else {
- vod->rv3d->persp = RV3D_PERSP;
- }
- ED_region_tag_redraw(vod->region);
- }
-
- const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
-
- viewops_data_create(C,
- op,
- event,
- (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
- (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
-
- /* if one or the other zoom position aren't set, set from event */
- if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
- RNA_int_set(op->ptr, "mx", event->xy[0]);
- RNA_int_set(op->ptr, "my", event->xy[1]);
- }
-
- if (RNA_struct_property_is_set(op->ptr, "delta")) {
- viewdolly_exec(C, op);
- }
- else {
- /* overwrite the mouse vector with the view direction (zoom into the center) */
- if ((use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) == 0) {
- negate_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]);
- normalize_v3(vod->init.mousevec);
- }
-
- if (event->type == MOUSEZOOM) {
- /* Bypass Zoom invert flag for track pads (pass false always) */
-
- if (U.uiflag & USER_ZOOM_HORIZ) {
- vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
- }
- else {
- /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
- vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] -
- event->prev_xy[0];
- }
- viewdolly_apply(vod, event->prev_xy, (U.uiflag & USER_ZOOM_INVERT) == 0);
-
- viewops_data_free(C, op);
- return OPERATOR_FINISHED;
- }
-
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
- return OPERATOR_FINISHED;
-}
-
-static void viewdolly_cancel(bContext *C, wmOperator *op)
-{
- viewops_data_free(C, op);
-}
-
-void VIEW3D_OT_dolly(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Dolly View";
- ot->description = "Dolly in/out in the view";
- ot->idname = "VIEW3D_OT_dolly";
-
- /* api callbacks */
- ot->invoke = viewdolly_invoke;
- ot->exec = viewdolly_exec;
- ot->modal = viewdolly_modal;
- ot->poll = ED_operator_region_view3d_active;
- ot->cancel = viewdolly_cancel;
-
- /* flags */
- ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
-
- /* properties */
- view3d_operator_properties_common(
- ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View All Operator
- *
- * Move & Zoom the view to fit all of its contents.
- * \{ */
-
-static bool view3d_object_skip_minmax(const View3D *v3d,
- const RegionView3D *rv3d,
- const Object *ob,
- const bool skip_camera,
- bool *r_only_center)
-{
- BLI_assert(ob->id.orig_id == NULL);
- *r_only_center = false;
-
- if (skip_camera && (ob == v3d->camera)) {
- return true;
- }
-
- if ((ob->type == OB_EMPTY) && (ob->empty_drawtype == OB_EMPTY_IMAGE) &&
- !BKE_object_empty_image_frame_is_visible_in_view3d(ob, rv3d)) {
- *r_only_center = true;
- return false;
- }
-
- return false;
-}
-
-static void view3d_object_calc_minmax(Depsgraph *depsgraph,
- Scene *scene,
- Object *ob_eval,
- const bool only_center,
- float min[3],
- float max[3])
-{
- /* Account for duplis. */
- if (BKE_object_minmax_dupli(depsgraph, scene, ob_eval, min, max, false) == 0) {
- /* Use if duplis aren't found. */
- if (only_center) {
- minmax_v3v3_v3(min, max, ob_eval->obmat[3]);
- }
- else {
- BKE_object_minmax(ob_eval, min, max, false);
- }
- }
-}
-
-static void view3d_from_minmax(bContext *C,
- View3D *v3d,
- ARegion *region,
- const float min[3],
- const float max[3],
- bool ok_dist,
- const int smooth_viewtx)
-{
- RegionView3D *rv3d = region->regiondata;
- float afm[3];
- float size;
-
- ED_view3d_smooth_view_force_finish(C, v3d, region);
-
- /* SMOOTHVIEW */
- float new_ofs[3];
- float new_dist;
-
- sub_v3_v3v3(afm, max, min);
- size = max_fff(afm[0], afm[1], afm[2]);
-
- if (ok_dist) {
- char persp;
-
- if (rv3d->is_persp) {
- if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) {
- persp = RV3D_CAMOB;
- }
- else {
- persp = RV3D_PERSP;
- }
- }
- else { /* ortho */
- if (size < 0.0001f) {
- /* bounding box was a single point so do not zoom */
- ok_dist = false;
- }
- else {
- /* adjust zoom so it looks nicer */
- persp = RV3D_ORTHO;
- }
- }
-
- if (ok_dist) {
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- new_dist = ED_view3d_radius_to_dist(
- v3d, region, depsgraph, persp, true, (size / 2) * VIEW3D_MARGIN);
- if (rv3d->is_persp) {
- /* don't zoom closer than the near clipping plane */
- new_dist = max_ff(new_dist, v3d->clip_start * 1.5f);
- }
- }
- }
-
- mid_v3_v3v3(new_ofs, min, max);
- negate_v3(new_ofs);
-
- if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) {
- rv3d->persp = RV3D_PERSP;
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .camera_old = v3d->camera,
- .ofs = new_ofs,
- .dist = ok_dist ? &new_dist : NULL,
- });
- }
- else {
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .ofs = new_ofs,
- .dist = ok_dist ? &new_dist : NULL,
- });
- }
-
- /* Smooth-view does view-lock #RV3D_BOXVIEW copy. */
-}
-
-/**
- * Same as #view3d_from_minmax but for all regions (except cameras).
- */
-static void view3d_from_minmax_multi(bContext *C,
- View3D *v3d,
- const float min[3],
- const float max[3],
- const bool ok_dist,
- const int smooth_viewtx)
-{
- ScrArea *area = CTX_wm_area(C);
- ARegion *region;
- for (region = area->regionbase.first; region; region = region->next) {
- if (region->regiontype == RGN_TYPE_WINDOW) {
- RegionView3D *rv3d = region->regiondata;
- /* when using all regions, don't jump out of camera view,
- * but _do_ allow locked cameras to be moved */
- if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
- view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx);
- }
- }
- }
-}
-
-static int view3d_all_exec(bContext *C, wmOperator *op)
-{
- ARegion *region = CTX_wm_region(C);
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- Scene *scene = CTX_data_scene(C);
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
- Base *base_eval;
- const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
- const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) ||
- /* any one of the regions may be locked */
- (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
- const bool center = RNA_boolean_get(op->ptr, "center");
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- float min[3], max[3];
- bool changed = false;
-
- if (center) {
- /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */
- View3DCursor *cursor = &scene->cursor;
- zero_v3(min);
- zero_v3(max);
- zero_v3(cursor->location);
- float mat3[3][3];
- unit_m3(mat3);
- BKE_scene_cursor_mat3_to_rot(cursor, mat3, false);
- }
- else {
- INIT_MINMAX(min, max);
- }
-
- for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) {
- if (BASE_VISIBLE(v3d, base_eval)) {
- bool only_center = false;
- Object *ob = DEG_get_original_object(base_eval->object);
- if (view3d_object_skip_minmax(v3d, rv3d, ob, skip_camera, &only_center)) {
- continue;
- }
- view3d_object_calc_minmax(depsgraph, scene, base_eval->object, only_center, min, max);
- changed = true;
- }
- }
-
- if (center) {
- struct wmMsgBus *mbus = CTX_wm_message_bus(C);
- WM_msg_publish_rna_prop(mbus, &scene->id, &scene->cursor, View3DCursor, location);
-
- DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
- }
-
- if (!changed) {
- ED_region_tag_redraw(region);
- /* TODO: should this be cancel?
- * I think no, because we always move the cursor, with or without
- * object, but in this case there is no change in the scene,
- * only the cursor so I choice a ED_region_tag like
- * view3d_smooth_view do for the center_cursor.
- * See bug T22640.
- */
- return OPERATOR_FINISHED;
- }
-
- if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
- /* This is an approximation, see function documentation for details. */
- ED_view3d_clipping_clamp_minmax(rv3d, min, max);
- }
-
- if (use_all_regions) {
- view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx);
- }
- else {
- view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx);
- }
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_view_all(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Frame All";
- ot->description = "View all objects in scene";
- ot->idname = "VIEW3D_OT_view_all";
-
- /* api callbacks */
- ot->exec = view3d_all_exec;
- ot->poll = ED_operator_region_view3d_active;
-
- /* flags */
- ot->flag = 0;
-
- /* properties */
- view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS);
- RNA_def_boolean(ot->srna, "center", 0, "Center", "");
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Frame Selected Operator
- *
- * Move & Zoom the view to fit selected contents.
- * \{ */
-
-static int viewselected_exec(bContext *C, wmOperator *op)
-{
- ARegion *region = CTX_wm_region(C);
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- Scene *scene = CTX_data_scene(C);
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
- Object *ob_eval = OBACT(view_layer_eval);
- Object *obedit = CTX_data_edit_object(C);
- const bGPdata *gpd_eval = ob_eval && (ob_eval->type == OB_GPENCIL) ? ob_eval->data : NULL;
- const bool is_gp_edit = gpd_eval ? GPENCIL_ANY_MODE(gpd_eval) : false;
- const bool is_face_map = ((is_gp_edit == false) && region->gizmo_map &&
- WM_gizmomap_is_any_selected(region->gizmo_map));
- float min[3], max[3];
- bool ok = false, ok_dist = true;
- const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
- const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) ||
- /* any one of the regions may be locked */
- (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- INIT_MINMAX(min, max);
- if (is_face_map) {
- ob_eval = NULL;
- }
-
- if (ob_eval && (ob_eval->mode & OB_MODE_WEIGHT_PAINT)) {
- /* hard-coded exception, we look for the one selected armature */
- /* this is weak code this way, we should make a generic
- * active/selection callback interface once... */
- Base *base_eval;
- for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) {
- if (BASE_SELECTED_EDITABLE(v3d, base_eval)) {
- if (base_eval->object->type == OB_ARMATURE) {
- if (base_eval->object->mode & OB_MODE_POSE) {
- break;
- }
- }
- }
- }
- if (base_eval) {
- ob_eval = base_eval->object;
- }
- }
-
- if (is_gp_edit) {
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- /* we're only interested in selected points here... */
- if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) {
- ok |= BKE_gpencil_stroke_minmax(gps, true, min, max);
- }
- if (gps->editcurve != NULL) {
- for (int i = 0; i < gps->editcurve->tot_curve_points; i++) {
- BezTriple *bezt = &gps->editcurve->curve_points[i].bezt;
- if ((bezt->f1 & SELECT)) {
- minmax_v3v3_v3(min, max, bezt->vec[0]);
- ok = true;
- }
- if ((bezt->f2 & SELECT)) {
- minmax_v3v3_v3(min, max, bezt->vec[1]);
- ok = true;
- }
- if ((bezt->f3 & SELECT)) {
- minmax_v3v3_v3(min, max, bezt->vec[2]);
- ok = true;
- }
- }
- }
- }
- CTX_DATA_END;
-
- if ((ob_eval) && (ok)) {
- mul_m4_v3(ob_eval->obmat, min);
- mul_m4_v3(ob_eval->obmat, max);
- }
- }
- else if (is_face_map) {
- ok = WM_gizmomap_minmax(region->gizmo_map, true, true, min, max);
- }
- else if (obedit) {
- /* only selected */
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer_eval, v3d, obedit->type, obedit->mode, ob_eval_iter) {
- ok |= ED_view3d_minmax_verts(ob_eval_iter, min, max);
- }
- FOREACH_OBJECT_IN_MODE_END;
- }
- else if (ob_eval && (ob_eval->mode & OB_MODE_POSE)) {
- FOREACH_OBJECT_IN_MODE_BEGIN (
- view_layer_eval, v3d, ob_eval->type, ob_eval->mode, ob_eval_iter) {
- ok |= BKE_pose_minmax(ob_eval_iter, min, max, true, true);
- }
- FOREACH_OBJECT_IN_MODE_END;
- }
- else if (BKE_paint_select_face_test(ob_eval)) {
- ok = paintface_minmax(ob_eval, min, max);
- }
- else if (ob_eval && (ob_eval->mode & OB_MODE_PARTICLE_EDIT)) {
- ok = PE_minmax(depsgraph, scene, CTX_data_view_layer(C), min, max);
- }
- else if (ob_eval && (ob_eval->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT |
- OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
- BKE_paint_stroke_get_average(scene, ob_eval, min);
- copy_v3_v3(max, min);
- ok = true;
- ok_dist = 0; /* don't zoom */
- }
- else {
- Base *base_eval;
- for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) {
- if (BASE_SELECTED(v3d, base_eval)) {
- bool only_center = false;
- Object *ob = DEG_get_original_object(base_eval->object);
- if (view3d_object_skip_minmax(v3d, rv3d, ob, skip_camera, &only_center)) {
- continue;
- }
- view3d_object_calc_minmax(depsgraph, scene, base_eval->object, only_center, min, max);
- ok = 1;
- }
- }
- }
-
- if (ok == 0) {
- return OPERATOR_FINISHED;
- }
-
- if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
- /* This is an approximation, see function documentation for details. */
- ED_view3d_clipping_clamp_minmax(rv3d, min, max);
- }
-
- if (use_all_regions) {
- view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx);
- }
- else {
- view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx);
- }
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_view_selected(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Frame Selected";
- ot->description = "Move the view to the selection center";
- ot->idname = "VIEW3D_OT_view_selected";
-
- /* api callbacks */
- ot->exec = viewselected_exec;
- ot->poll = view3d_zoom_or_dolly_poll;
-
- /* flags */
- ot->flag = 0;
-
- /* properties */
- view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS);
-}
-
-/** \} */
-
/* -------------------------------------------------------------------- */
/** \name View Lock Clear Operator
* \{ */
@@ -3256,103 +179,6 @@ void VIEW3D_OT_view_lock_to_active(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name View Center Cursor Operator
- * \{ */
-
-static int viewcenter_cursor_exec(bContext *C, wmOperator *op)
-{
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- Scene *scene = CTX_data_scene(C);
-
- if (rv3d) {
- ARegion *region = CTX_wm_region(C);
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- ED_view3d_smooth_view_force_finish(C, v3d, region);
-
- /* non camera center */
- float new_ofs[3];
- negate_v3_v3(new_ofs, scene->cursor.location);
- ED_view3d_smooth_view(
- C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs});
-
- /* Smooth view does view-lock #RV3D_BOXVIEW copy. */
- }
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_view_center_cursor(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Center View to Cursor";
- ot->description = "Center the view so that the cursor is in the middle of the view";
- ot->idname = "VIEW3D_OT_view_center_cursor";
-
- /* api callbacks */
- ot->exec = viewcenter_cursor_exec;
- ot->poll = view3d_pan_poll;
-
- /* flags */
- ot->flag = 0;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Center Pick Operator
- * \{ */
-
-static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- ARegion *region = CTX_wm_region(C);
-
- if (rv3d) {
- struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- float new_ofs[3];
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- ED_view3d_smooth_view_force_finish(C, v3d, region);
-
- view3d_operator_needs_opengl(C);
-
- if (ED_view3d_autodist(depsgraph, region, v3d, event->mval, new_ofs, false, NULL)) {
- /* pass */
- }
- else {
- /* fallback to simple pan */
- negate_v3_v3(new_ofs, rv3d->ofs);
- ED_view3d_win_to_3d_int(v3d, region, new_ofs, event->mval, new_ofs);
- }
- negate_v3(new_ofs);
- ED_view3d_smooth_view(
- C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs});
- }
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_view_center_pick(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Center View to Mouse";
- ot->description = "Center the view to the Z-depth position under the mouse cursor";
- ot->idname = "VIEW3D_OT_view_center_pick";
-
- /* api callbacks */
- ot->invoke = viewcenter_pick_invoke;
- ot->poll = view3d_pan_poll;
-
- /* flags */
- ot->flag = 0;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Frame Camera Bounds Operator
* \{ */
@@ -3591,189 +417,6 @@ void VIEW3D_OT_clear_render_border(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Border Zoom Operator
- * \{ */
-
-static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
-{
- ARegion *region = CTX_wm_region(C);
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- /* Zooms in on a border drawn by the user */
- rcti rect;
- float dvec[3], vb[2], xscale, yscale;
- float dist_range[2];
-
- /* SMOOTHVIEW */
- float new_dist;
- float new_ofs[3];
-
- /* ZBuffer depth vars */
- float depth_close = FLT_MAX;
- float cent[2], p[3];
-
- /* NOTE: otherwise opengl won't work. */
- view3d_operator_needs_opengl(C);
-
- /* get box select values using rna */
- WM_operator_properties_border_to_rcti(op, &rect);
-
- /* check if zooming in/out view */
- const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out");
-
- ED_view3d_dist_range_get(v3d, dist_range);
-
- ED_view3d_depth_override(
- CTX_data_ensure_evaluated_depsgraph(C), region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL);
- {
- /* avoid allocating the whole depth buffer */
- ViewDepths depth_temp = {0};
-
- /* avoid view3d_update_depths() for speed. */
- view3d_depths_rect_create(region, &rect, &depth_temp);
-
- /* find the closest Z pixel */
- depth_close = view3d_depth_near(&depth_temp);
-
- MEM_SAFE_FREE(depth_temp.depths);
- }
-
- /* Resize border to the same ratio as the window. */
- {
- const float region_aspect = (float)region->winx / (float)region->winy;
- if (((float)BLI_rcti_size_x(&rect) / (float)BLI_rcti_size_y(&rect)) < region_aspect) {
- BLI_rcti_resize_x(&rect, (int)(BLI_rcti_size_y(&rect) * region_aspect));
- }
- else {
- BLI_rcti_resize_y(&rect, (int)(BLI_rcti_size_x(&rect) / region_aspect));
- }
- }
-
- cent[0] = (((float)rect.xmin) + ((float)rect.xmax)) / 2;
- cent[1] = (((float)rect.ymin) + ((float)rect.ymax)) / 2;
-
- if (rv3d->is_persp) {
- float p_corner[3];
-
- /* no depths to use, we can't do anything! */
- if (depth_close == FLT_MAX) {
- BKE_report(op->reports, RPT_ERROR, "Depth too large");
- return OPERATOR_CANCELLED;
- }
- /* convert border to 3d coordinates */
- if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) ||
- (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) {
- return OPERATOR_CANCELLED;
- }
-
- sub_v3_v3v3(dvec, p, p_corner);
- negate_v3_v3(new_ofs, p);
-
- new_dist = len_v3(dvec);
-
- /* Account for the lens, without this a narrow lens zooms in too close. */
- new_dist *= (v3d->lens / DEFAULT_SENSOR_WIDTH);
-
- /* ignore dist_range min */
- dist_range[0] = v3d->clip_start * 1.5f;
- }
- else { /* orthographic */
- /* find the current window width and height */
- vb[0] = region->winx;
- vb[1] = region->winy;
-
- new_dist = rv3d->dist;
-
- /* convert the drawn rectangle into 3d space */
- if (depth_close != FLT_MAX &&
- ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) {
- negate_v3_v3(new_ofs, p);
- }
- else {
- float mval_f[2];
- float zfac;
-
- /* We can't use the depth, fallback to the old way that doesn't set the center depth */
- copy_v3_v3(new_ofs, rv3d->ofs);
-
- {
- float tvec[3];
- negate_v3_v3(tvec, new_ofs);
- zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL);
- }
-
- mval_f[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f;
- mval_f[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f;
- ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
- /* center the view to the center of the rectangle */
- sub_v3_v3(new_ofs, dvec);
- }
-
- /* work out the ratios, so that everything selected fits when we zoom */
- xscale = (BLI_rcti_size_x(&rect) / vb[0]);
- yscale = (BLI_rcti_size_y(&rect) / vb[1]);
- new_dist *= max_ff(xscale, yscale);
- }
-
- if (!zoom_in) {
- sub_v3_v3v3(dvec, new_ofs, rv3d->ofs);
- new_dist = rv3d->dist * (rv3d->dist / new_dist);
- add_v3_v3v3(new_ofs, rv3d->ofs, dvec);
- }
-
- /* clamp after because we may have been zooming out */
- CLAMP(new_dist, dist_range[0], dist_range[1]);
-
- /* TODO(campbell): 'is_camera_lock' not currently working well. */
- const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d);
- if ((rv3d->persp == RV3D_CAMOB) && (is_camera_lock == false)) {
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP);
- }
-
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .ofs = new_ofs,
- .dist = &new_dist,
- });
-
- if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_sync(CTX_wm_area(C), region);
- }
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_zoom_border(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Zoom to Border";
- ot->description = "Zoom in the view to the nearest object contained in the border";
- ot->idname = "VIEW3D_OT_zoom_border";
-
- /* api callbacks */
- ot->invoke = WM_gesture_box_invoke;
- ot->exec = view3d_zoom_border_exec;
- ot->modal = WM_gesture_box_modal;
- ot->cancel = WM_gesture_box_cancel;
-
- ot->poll = view3d_zoom_or_dolly_poll;
-
- /* flags */
- ot->flag = 0;
-
- /* properties */
- WM_operator_properties_gesture_box_zoom(ot);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Set Camera Zoom 1:1 Operator
*
* Sets the view to 1:1 camera/render-pixel.
@@ -3830,838 +473,6 @@ void VIEW3D_OT_zoom_camera_1_to_1(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name View Axis Operator
- * \{ */
-
-static const EnumPropertyItem prop_view_items[] = {
- {RV3D_VIEW_LEFT, "LEFT", ICON_TRIA_LEFT, "Left", "View from the left"},
- {RV3D_VIEW_RIGHT, "RIGHT", ICON_TRIA_RIGHT, "Right", "View from the right"},
- {RV3D_VIEW_BOTTOM, "BOTTOM", ICON_TRIA_DOWN, "Bottom", "View from the bottom"},
- {RV3D_VIEW_TOP, "TOP", ICON_TRIA_UP, "Top", "View from the top"},
- {RV3D_VIEW_FRONT, "FRONT", 0, "Front", "View from the front"},
- {RV3D_VIEW_BACK, "BACK", 0, "Back", "View from the back"},
- {0, NULL, 0, NULL, NULL},
-};
-
-/* would like to make this a generic function - outside of transform */
-
-/**
- * \param align_to_quat: When not NULL, set the axis relative to this rotation.
- */
-static void axis_set_view(bContext *C,
- View3D *v3d,
- ARegion *region,
- const float quat_[4],
- char view,
- char view_axis_roll,
- int perspo,
- const float *align_to_quat,
- const int smooth_viewtx)
-{
- RegionView3D *rv3d = region->regiondata; /* no NULL check is needed, poll checks */
- float quat[4];
- const short orig_persp = rv3d->persp;
-
- normalize_qt_qt(quat, quat_);
-
- if (align_to_quat) {
- mul_qt_qtqt(quat, quat, align_to_quat);
- rv3d->view = view = RV3D_VIEW_USER;
- rv3d->view_axis_roll = RV3D_VIEW_AXIS_ROLL_0;
- }
-
- if (align_to_quat == NULL) {
- rv3d->view = view;
- rv3d->view_axis_roll = view_axis_roll;
- }
-
- if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) {
- ED_region_tag_redraw(region);
- return;
- }
-
- if (U.uiflag & USER_AUTOPERSP) {
- rv3d->persp = RV3D_VIEW_IS_AXIS(view) ? RV3D_ORTHO : perspo;
- }
- else if (rv3d->persp == RV3D_CAMOB) {
- rv3d->persp = perspo;
- }
-
- if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
- /* to camera */
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .camera_old = v3d->camera,
- .ofs = rv3d->ofs,
- .quat = quat,
- });
- }
- else if (orig_persp == RV3D_CAMOB && v3d->camera) {
- /* from camera */
- float ofs[3], dist;
-
- copy_v3_v3(ofs, rv3d->ofs);
- dist = rv3d->dist;
-
- /* so we animate _from_ the camera location */
- Object *camera_eval = DEG_get_evaluated_object(CTX_data_ensure_evaluated_depsgraph(C),
- v3d->camera);
- ED_view3d_from_object(camera_eval, rv3d->ofs, NULL, &rv3d->dist, NULL);
-
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .camera_old = camera_eval,
- .ofs = ofs,
- .quat = quat,
- .dist = &dist,
- });
- }
- else {
- /* rotate around selection */
- const float *dyn_ofs_pt = NULL;
- float dyn_ofs[3];
-
- if (U.uiflag & USER_ORBIT_SELECTION) {
- if (view3d_orbit_calc_center(C, dyn_ofs)) {
- negate_v3(dyn_ofs);
- dyn_ofs_pt = dyn_ofs;
- }
- }
-
- /* no camera involved */
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .quat = quat,
- .dyn_ofs = dyn_ofs_pt,
- });
- }
-}
-
-static int view_axis_exec(bContext *C, wmOperator *op)
-{
- View3D *v3d;
- ARegion *region;
- RegionView3D *rv3d;
- static int perspo = RV3D_PERSP;
- int viewnum;
- int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0;
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- /* no NULL check is needed, poll checks */
- ED_view3d_context_user_region(C, &v3d, &region);
- rv3d = region->regiondata;
-
- ED_view3d_smooth_view_force_finish(C, v3d, region);
-
- viewnum = RNA_enum_get(op->ptr, "type");
-
- float align_quat_buf[4];
- float *align_quat = NULL;
-
- if (RNA_boolean_get(op->ptr, "align_active")) {
- /* align to active object */
- Object *obact = CTX_data_active_object(C);
- if (obact != NULL) {
- float twmat[3][3];
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = CTX_data_edit_object(C);
- /* same as transform gizmo when normal is set */
- ED_getTransformOrientationMatrix(view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat);
- align_quat = align_quat_buf;
- mat3_to_quat(align_quat, twmat);
- invert_qt_normalized(align_quat);
- }
- }
-
- if (RNA_boolean_get(op->ptr, "relative")) {
- float quat_rotate[4];
- float quat_test[4];
-
- if (viewnum == RV3D_VIEW_LEFT) {
- axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], -M_PI / 2.0f);
- }
- else if (viewnum == RV3D_VIEW_RIGHT) {
- axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], M_PI / 2.0f);
- }
- else if (viewnum == RV3D_VIEW_TOP) {
- axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], -M_PI / 2.0f);
- }
- else if (viewnum == RV3D_VIEW_BOTTOM) {
- axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI / 2.0f);
- }
- else if (viewnum == RV3D_VIEW_FRONT) {
- unit_qt(quat_rotate);
- }
- else if (viewnum == RV3D_VIEW_BACK) {
- axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI);
- }
- else {
- BLI_assert(0);
- }
-
- mul_qt_qtqt(quat_test, rv3d->viewquat, quat_rotate);
-
- float angle_best = FLT_MAX;
- int view_best = -1;
- int view_axis_roll_best = -1;
- for (int i = RV3D_VIEW_FRONT; i <= RV3D_VIEW_BOTTOM; i++) {
- for (int j = RV3D_VIEW_AXIS_ROLL_0; j <= RV3D_VIEW_AXIS_ROLL_270; j++) {
- float quat_axis[4];
- ED_view3d_quat_from_axis_view(i, j, quat_axis);
- if (align_quat) {
- mul_qt_qtqt(quat_axis, quat_axis, align_quat);
- }
- const float angle_test = fabsf(angle_signed_qtqt(quat_axis, quat_test));
- if (angle_best > angle_test) {
- angle_best = angle_test;
- view_best = i;
- view_axis_roll_best = j;
- }
- }
- }
- if (view_best == -1) {
- view_best = RV3D_VIEW_FRONT;
- view_axis_roll_best = RV3D_VIEW_AXIS_ROLL_0;
- }
-
- /* Disallow non-upright views in turn-table modes,
- * it's too difficult to navigate out of them. */
- if ((U.flag & USER_TRACKBALL) == 0) {
- if (!ELEM(view_best, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
- view_axis_roll_best = RV3D_VIEW_AXIS_ROLL_0;
- }
- }
-
- viewnum = view_best;
- view_axis_roll = view_axis_roll_best;
- }
-
- /* Use this to test if we started out with a camera */
- const int nextperspo = (rv3d->persp == RV3D_CAMOB) ? rv3d->lpersp : perspo;
- float quat[4];
- ED_view3d_quat_from_axis_view(viewnum, view_axis_roll, quat);
- axis_set_view(
- C, v3d, region, quat, viewnum, view_axis_roll, nextperspo, align_quat, smooth_viewtx);
-
- perspo = rv3d->persp;
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_view_axis(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "View Axis";
- ot->description = "Use a preset viewpoint";
- ot->idname = "VIEW3D_OT_view_axis";
-
- /* api callbacks */
- ot->exec = view_axis_exec;
- ot->poll = ED_operator_rv3d_user_region_poll;
-
- /* flags */
- ot->flag = 0;
-
- ot->prop = RNA_def_enum(ot->srna, "type", prop_view_items, 0, "View", "Preset viewpoint to use");
- RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(
- ot->srna, "align_active", 0, "Align Active", "Align to the active object's axis");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(
- ot->srna, "relative", 0, "Relative", "Rotate relative to the current orientation");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Camera Operator
- * \{ */
-
-static int view_camera_exec(bContext *C, wmOperator *op)
-{
- View3D *v3d;
- ARegion *region;
- RegionView3D *rv3d;
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- /* no NULL check is needed, poll checks */
- ED_view3d_context_user_region(C, &v3d, &region);
- rv3d = region->regiondata;
-
- ED_view3d_smooth_view_force_finish(C, v3d, region);
-
- if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) == 0) {
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Scene *scene = CTX_data_scene(C);
-
- if (rv3d->persp != RV3D_CAMOB) {
- Object *ob = OBACT(view_layer);
-
- if (!rv3d->smooth_timer) {
- /* store settings of current view before allowing overwriting with camera view
- * only if we're not currently in a view transition */
-
- ED_view3d_lastview_store(rv3d);
- }
-
- /* first get the default camera for the view lock type */
- if (v3d->scenelock) {
- /* sets the camera view if available */
- v3d->camera = scene->camera;
- }
- else {
- /* use scene camera if one is not set (even though we're unlocked) */
- if (v3d->camera == NULL) {
- v3d->camera = scene->camera;
- }
- }
-
- /* if the camera isn't found, check a number of options */
- if (v3d->camera == NULL && ob && ob->type == OB_CAMERA) {
- v3d->camera = ob;
- }
-
- if (v3d->camera == NULL) {
- v3d->camera = BKE_view_layer_camera_find(view_layer);
- }
-
- /* couldn't find any useful camera, bail out */
- if (v3d->camera == NULL) {
- return OPERATOR_CANCELLED;
- }
-
- /* important these don't get out of sync for locked scenes */
- if (v3d->scenelock && scene->camera != v3d->camera) {
- scene->camera = v3d->camera;
- DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
- }
-
- /* finally do snazzy view zooming */
- rv3d->persp = RV3D_CAMOB;
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .camera = v3d->camera,
- .ofs = rv3d->ofs,
- .quat = rv3d->viewquat,
- .dist = &rv3d->dist,
- .lens = &v3d->lens,
- });
- }
- else {
- /* return to settings of last view */
- /* does view3d_smooth_view too */
- axis_set_view(C,
- v3d,
- region,
- rv3d->lviewquat,
- rv3d->lview,
- rv3d->lview_axis_roll,
- rv3d->lpersp,
- NULL,
- smooth_viewtx);
- }
- }
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_view_camera(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "View Camera";
- ot->description = "Toggle the camera view";
- ot->idname = "VIEW3D_OT_view_camera";
-
- /* api callbacks */
- ot->exec = view_camera_exec;
- ot->poll = ED_operator_rv3d_user_region_poll;
-
- /* flags */
- ot->flag = 0;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Orbit Operator
- *
- * Rotate (orbit) in incremental steps. For interactive orbit see #VIEW3D_OT_rotate.
- * \{ */
-
-enum {
- V3D_VIEW_STEPLEFT = 1,
- V3D_VIEW_STEPRIGHT,
- V3D_VIEW_STEPDOWN,
- V3D_VIEW_STEPUP,
-};
-
-static const EnumPropertyItem prop_view_orbit_items[] = {
- {V3D_VIEW_STEPLEFT, "ORBITLEFT", 0, "Orbit Left", "Orbit the view around to the left"},
- {V3D_VIEW_STEPRIGHT, "ORBITRIGHT", 0, "Orbit Right", "Orbit the view around to the right"},
- {V3D_VIEW_STEPUP, "ORBITUP", 0, "Orbit Up", "Orbit the view up"},
- {V3D_VIEW_STEPDOWN, "ORBITDOWN", 0, "Orbit Down", "Orbit the view down"},
- {0, NULL, 0, NULL, NULL},
-};
-
-static int vieworbit_exec(bContext *C, wmOperator *op)
-{
- View3D *v3d;
- ARegion *region;
- RegionView3D *rv3d;
- int orbitdir;
- char view_opposite;
- PropertyRNA *prop_angle = RNA_struct_find_property(op->ptr, "angle");
- float angle = RNA_property_is_set(op->ptr, prop_angle) ?
- RNA_property_float_get(op->ptr, prop_angle) :
- DEG2RADF(U.pad_rot_angle);
-
- /* no NULL check is needed, poll checks */
- v3d = CTX_wm_view3d(C);
- region = CTX_wm_region(C);
- rv3d = region->regiondata;
-
- /* support for switching to the opposite view (even when in locked views) */
- view_opposite = (fabsf(angle) == (float)M_PI) ? ED_view3d_axis_view_opposite(rv3d->view) :
- RV3D_VIEW_USER;
- orbitdir = RNA_enum_get(op->ptr, "type");
-
- if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) && (view_opposite == RV3D_VIEW_USER)) {
- /* no NULL check is needed, poll checks */
- ED_view3d_context_user_region(C, &v3d, &region);
- rv3d = region->regiondata;
- }
-
- ED_view3d_smooth_view_force_finish(C, v3d, region);
-
- if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0 || (view_opposite != RV3D_VIEW_USER)) {
- if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
- int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
- float quat_mul[4];
- float quat_new[4];
-
- if (view_opposite == RV3D_VIEW_USER) {
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_persp_ensure(depsgraph, v3d, region);
- }
-
- if (ELEM(orbitdir, V3D_VIEW_STEPLEFT, V3D_VIEW_STEPRIGHT)) {
- if (orbitdir == V3D_VIEW_STEPRIGHT) {
- angle = -angle;
- }
-
- /* z-axis */
- axis_angle_to_quat_single(quat_mul, 'Z', angle);
- }
- else {
-
- if (orbitdir == V3D_VIEW_STEPDOWN) {
- angle = -angle;
- }
-
- /* horizontal axis */
- axis_angle_to_quat(quat_mul, rv3d->viewinv[0], angle);
- }
-
- mul_qt_qtqt(quat_new, rv3d->viewquat, quat_mul);
-
- /* avoid precision loss over time */
- normalize_qt(quat_new);
-
- if (view_opposite != RV3D_VIEW_USER) {
- rv3d->view = view_opposite;
- /* avoid float in-precision, just get a new orientation */
- ED_view3d_quat_from_axis_view(view_opposite, rv3d->view_axis_roll, quat_new);
- }
- else {
- rv3d->view = RV3D_VIEW_USER;
- }
-
- float dyn_ofs[3], *dyn_ofs_pt = NULL;
-
- if (U.uiflag & USER_ORBIT_SELECTION) {
- if (view3d_orbit_calc_center(C, dyn_ofs)) {
- negate_v3(dyn_ofs);
- dyn_ofs_pt = dyn_ofs;
- }
- }
-
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .quat = quat_new,
- .dyn_ofs = dyn_ofs_pt,
- });
-
- return OPERATOR_FINISHED;
- }
- }
-
- return OPERATOR_CANCELLED;
-}
-
-void VIEW3D_OT_view_orbit(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "View Orbit";
- ot->description = "Orbit the view";
- ot->idname = "VIEW3D_OT_view_orbit";
-
- /* api callbacks */
- ot->exec = vieworbit_exec;
- ot->poll = ED_operator_rv3d_user_region_poll;
-
- /* flags */
- ot->flag = 0;
-
- /* properties */
- prop = RNA_def_float(ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-
- ot->prop = RNA_def_enum(
- ot->srna, "type", prop_view_orbit_items, 0, "Orbit", "Direction of View Orbit");
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Roll Operator
- * \{ */
-
-static void view_roll_angle(
- ARegion *region, float quat[4], const float orig_quat[4], const float dvec[3], float angle)
-{
- RegionView3D *rv3d = region->regiondata;
- float quat_mul[4];
-
- /* camera axis */
- axis_angle_normalized_to_quat(quat_mul, dvec, angle);
-
- mul_qt_qtqt(quat, orig_quat, quat_mul);
-
- /* avoid precision loss over time */
- normalize_qt(quat);
-
- rv3d->view = RV3D_VIEW_USER;
-}
-
-static void viewroll_apply(ViewOpsData *vod, int x, int y)
-{
- float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y});
-
- if (angle != 0.0f) {
- view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle);
- }
-
- if (vod->use_dyn_ofs) {
- view3d_orbit_apply_dyn_ofs(
- vod->rv3d->ofs, vod->init.ofs, vod->init.quat, vod->rv3d->viewquat, vod->dyn_ofs);
- }
-
- if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_sync(vod->area, vod->region);
- }
-
- ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
-
- ED_region_tag_redraw(vod->region);
-}
-
-static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod = op->customdata;
- short event_code = VIEW_PASS;
- bool use_autokey = false;
- int ret = OPERATOR_RUNNING_MODAL;
-
- /* execute the events */
- if (event->type == MOUSEMOVE) {
- event_code = VIEW_APPLY;
- }
- else if (event->type == EVT_MODAL_MAP) {
- switch (event->val) {
- case VIEW_MODAL_CONFIRM:
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_MOVE:
- WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- case VIEWROT_MODAL_SWITCH_ROTATE:
- WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
- event_code = VIEW_CONFIRM;
- break;
- }
- }
- else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
- /* Note this does not remove auto-keys on locked cameras. */
- copy_qt_qt(vod->rv3d->viewquat, vod->init.quat);
- ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
- viewops_data_free(C, op);
- return OPERATOR_CANCELLED;
- }
- else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
- event_code = VIEW_CONFIRM;
- }
-
- if (event_code == VIEW_APPLY) {
- viewroll_apply(vod, event->xy[0], event->xy[1]);
- if (ED_screen_animation_playing(CTX_wm_manager(C))) {
- use_autokey = true;
- }
- }
- else if (event_code == VIEW_CONFIRM) {
- use_autokey = true;
- ret = OPERATOR_FINISHED;
- }
-
- if (use_autokey) {
- ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, false);
- }
-
- if (ret & OPERATOR_FINISHED) {
- viewops_data_free(C, op);
- }
-
- return ret;
-}
-
-static const EnumPropertyItem prop_view_roll_items[] = {
- {0, "ANGLE", 0, "Roll Angle", "Roll the view using an angle value"},
- {V3D_VIEW_STEPLEFT, "LEFT", 0, "Roll Left", "Roll the view around to the left"},
- {V3D_VIEW_STEPRIGHT, "RIGHT", 0, "Roll Right", "Roll the view around to the right"},
- {0, NULL, 0, NULL, NULL},
-};
-
-static int viewroll_exec(bContext *C, wmOperator *op)
-{
- View3D *v3d;
- RegionView3D *rv3d;
- ARegion *region;
-
- if (op->customdata) {
- ViewOpsData *vod = op->customdata;
- region = vod->region;
- v3d = vod->v3d;
- }
- else {
- ED_view3d_context_user_region(C, &v3d, &region);
- }
-
- rv3d = region->regiondata;
- if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
-
- ED_view3d_smooth_view_force_finish(C, v3d, region);
-
- int type = RNA_enum_get(op->ptr, "type");
- float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle);
- float mousevec[3];
- float quat_new[4];
-
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- if (type == V3D_VIEW_STEPLEFT) {
- angle = -angle;
- }
-
- normalize_v3_v3(mousevec, rv3d->viewinv[2]);
- negate_v3(mousevec);
- view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle);
-
- const float *dyn_ofs_pt = NULL;
- float dyn_ofs[3];
- if (U.uiflag & USER_ORBIT_SELECTION) {
- if (view3d_orbit_calc_center(C, dyn_ofs)) {
- negate_v3(dyn_ofs);
- dyn_ofs_pt = dyn_ofs;
- }
- }
-
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .quat = quat_new,
- .dyn_ofs = dyn_ofs_pt,
- });
-
- viewops_data_free(C, op);
- return OPERATOR_FINISHED;
- }
-
- viewops_data_free(C, op);
- return OPERATOR_CANCELLED;
-}
-
-static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewOpsData *vod;
-
- bool use_angle = RNA_enum_get(op->ptr, "type") != 0;
-
- if (use_angle || RNA_struct_property_is_set(op->ptr, "angle")) {
- viewroll_exec(C, op);
- }
- else {
- /* makes op->customdata */
- viewops_data_alloc(C, op);
- viewops_data_create(C, op, event, viewops_flag_from_prefs());
- vod = op->customdata;
- vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct),
- BLI_rcti_cent_y(&vod->region->winrct)},
- FLT_EPSILON);
-
- ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
-
- /* overwrite the mouse vector with the view direction */
- normalize_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]);
- negate_v3(vod->init.mousevec);
-
- if (event->type == MOUSEROTATE) {
- vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
- viewroll_apply(vod, event->prev_xy[0], event->prev_xy[1]);
-
- viewops_data_free(C, op);
- return OPERATOR_FINISHED;
- }
-
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
- return OPERATOR_FINISHED;
-}
-
-static void viewroll_cancel(bContext *C, wmOperator *op)
-{
- viewops_data_free(C, op);
-}
-
-void VIEW3D_OT_view_roll(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "View Roll";
- ot->description = "Roll the view";
- ot->idname = "VIEW3D_OT_view_roll";
-
- /* api callbacks */
- ot->invoke = viewroll_invoke;
- ot->exec = viewroll_exec;
- ot->modal = viewroll_modal;
- ot->poll = ED_operator_rv3d_user_region_poll;
- ot->cancel = viewroll_cancel;
-
- /* flags */
- ot->flag = 0;
-
- /* properties */
- ot->prop = prop = RNA_def_float(
- ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_enum(ot->srna,
- "type",
- prop_view_roll_items,
- 0,
- "Roll Angle Source",
- "How roll angle is calculated");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-}
-
-enum {
- V3D_VIEW_PANLEFT = 1,
- V3D_VIEW_PANRIGHT,
- V3D_VIEW_PANDOWN,
- V3D_VIEW_PANUP,
-};
-
-static const EnumPropertyItem prop_view_pan_items[] = {
- {V3D_VIEW_PANLEFT, "PANLEFT", 0, "Pan Left", "Pan the view to the left"},
- {V3D_VIEW_PANRIGHT, "PANRIGHT", 0, "Pan Right", "Pan the view to the right"},
- {V3D_VIEW_PANUP, "PANUP", 0, "Pan Up", "Pan the view up"},
- {V3D_VIEW_PANDOWN, "PANDOWN", 0, "Pan Down", "Pan the view down"},
- {0, NULL, 0, NULL, NULL},
-};
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Pan Operator
- *
- * Move (pan) in incremental steps. For interactive pan see #VIEW3D_OT_move.
- * \{ */
-
-static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- int x = 0, y = 0;
- int pandir = RNA_enum_get(op->ptr, "type");
-
- if (pandir == V3D_VIEW_PANRIGHT) {
- x = -32;
- }
- else if (pandir == V3D_VIEW_PANLEFT) {
- x = 32;
- }
- else if (pandir == V3D_VIEW_PANUP) {
- y = -25;
- }
- else if (pandir == V3D_VIEW_PANDOWN) {
- y = 25;
- }
-
- viewops_data_alloc(C, op);
- viewops_data_create(C, op, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT));
- ViewOpsData *vod = op->customdata;
-
- viewmove_apply(vod, vod->prev.event_xy[0] + x, vod->prev.event_xy[1] + y);
-
- viewops_data_free(C, op);
-
- return OPERATOR_FINISHED;
-}
-
-void VIEW3D_OT_view_pan(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Pan View Direction";
- ot->description = "Pan the view in a given direction";
- ot->idname = "VIEW3D_OT_view_pan";
-
- /* api callbacks */
- ot->invoke = viewpan_invoke;
- ot->poll = view3d_pan_poll;
-
- /* flags */
- ot->flag = 0;
-
- /* Properties */
- ot->prop = RNA_def_enum(
- ot->srna, "type", prop_view_pan_items, 0, "Pan", "Direction of View Pan");
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name View Toggle Perspective/Orthographic Operator
* \{ */
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index 607ca110d0f..7f872c9b1af 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -52,8 +52,6 @@
#include "view3d_intern.h"
-static void do_view3d_header_buttons(bContext *C, void *arg, int event);
-
#define B_SEL_VERT 110
#define B_SEL_EDGE 111
#define B_SEL_FACE 112
@@ -98,101 +96,45 @@ void VIEW3D_OT_toggle_matcap_flip(wmOperatorType *ot)
/** \name UI Templates
* \{ */
-static void do_view3d_header_buttons(bContext *C, void *UNUSED(arg), int event)
-{
- wmWindow *win = CTX_wm_window(C);
- const int ctrl = win->eventstate->ctrl, shift = win->eventstate->shift;
-
- /* watch it: if area->win does not exist, check that when calling direct drawing routines */
-
- switch (event) {
- case B_SEL_VERT:
- if (EDBM_selectmode_toggle_multi(C, SCE_SELECT_VERTEX, -1, shift, ctrl)) {
- ED_undo_push(C, "Selectmode Set: Vertex");
- }
- break;
- case B_SEL_EDGE:
- if (EDBM_selectmode_toggle_multi(C, SCE_SELECT_EDGE, -1, shift, ctrl)) {
- ED_undo_push(C, "Selectmode Set: Edge");
- }
- break;
- case B_SEL_FACE:
- if (EDBM_selectmode_toggle_multi(C, SCE_SELECT_FACE, -1, shift, ctrl)) {
- ED_undo_push(C, "Selectmode Set: Face");
- }
- break;
- default:
- break;
- }
-}
-
void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C)
{
Object *obedit = CTX_data_edit_object(C);
- uiBlock *block = uiLayoutGetBlock(layout);
-
- UI_block_func_handle_set(block, do_view3d_header_buttons, NULL);
-
- if (obedit && (obedit->type == OB_MESH)) {
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
- uiLayout *row;
- uiBut *but;
-
- row = uiLayoutRow(layout, true);
- block = uiLayoutGetBlock(row);
- but = uiDefIconButBitS(
- block,
- UI_BTYPE_TOGGLE,
- SCE_SELECT_VERTEX,
- B_SEL_VERT,
- ICON_VERTEXSEL,
- 0,
- 0,
- UI_UNIT_X,
- UI_UNIT_Y,
- &em->selectmode,
- 1.0,
- 0.0,
- 0,
- 0,
- TIP_("Vertex select - Shift-Click for multiple modes, Ctrl-Click contracts selection"));
- UI_but_flag_disable(but, UI_BUT_UNDO);
- but = uiDefIconButBitS(
- block,
- UI_BTYPE_TOGGLE,
- SCE_SELECT_EDGE,
- B_SEL_EDGE,
- ICON_EDGESEL,
- 0,
- 0,
- ceilf(UI_UNIT_X - U.pixelsize),
- UI_UNIT_Y,
- &em->selectmode,
- 1.0,
- 0.0,
- 0,
- 0,
- TIP_("Edge select - Shift-Click for multiple modes, "
- "Ctrl-Click expands/contracts selection depending on the current mode"));
- UI_but_flag_disable(but, UI_BUT_UNDO);
- but = uiDefIconButBitS(
- block,
- UI_BTYPE_TOGGLE,
- SCE_SELECT_FACE,
- B_SEL_FACE,
- ICON_FACESEL,
- 0,
- 0,
- ceilf(UI_UNIT_X - U.pixelsize),
- UI_UNIT_Y,
- &em->selectmode,
- 1.0,
- 0.0,
- 0,
- 0,
- TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection"));
- UI_but_flag_disable(but, UI_BUT_UNDO);
+ if (!obedit || obedit->type != OB_MESH) {
+ return;
}
+
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ uiLayout *row = uiLayoutRow(layout, true);
+
+ PointerRNA op_ptr;
+ wmOperatorType *ot = WM_operatortype_find("MESH_OT_select_mode", true);
+ uiItemFullO_ptr(row,
+ ot,
+ "",
+ ICON_VERTEXSEL,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ (em->selectmode & SCE_SELECT_VERTEX) ? UI_ITEM_O_DEPRESS : 0,
+ &op_ptr);
+ RNA_enum_set(&op_ptr, "type", SCE_SELECT_VERTEX);
+ uiItemFullO_ptr(row,
+ ot,
+ "",
+ ICON_EDGESEL,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ (em->selectmode & SCE_SELECT_EDGE) ? UI_ITEM_O_DEPRESS : 0,
+ &op_ptr);
+ RNA_enum_set(&op_ptr, "type", SCE_SELECT_EDGE);
+ uiItemFullO_ptr(row,
+ ot,
+ "",
+ ICON_FACESEL,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ (em->selectmode & SCE_SELECT_FACE) ? UI_ITEM_O_DEPRESS : 0,
+ &op_ptr);
+ RNA_enum_set(&op_ptr, "type", SCE_SELECT_FACE);
}
static void uiTemplatePaintModeSelection(uiLayout *layout, struct bContext *C)
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 6a1a09df316..b443ebeed94 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -32,6 +32,8 @@ struct ARegionType;
struct BoundBox;
struct Depsgraph;
struct Object;
+struct Scene;
+struct ViewContext;
struct ViewLayer;
struct bContext;
struct wmGizmoGroupType;
@@ -40,13 +42,6 @@ struct wmKeyConfig;
struct wmOperatorType;
struct wmWindowManager;
-/* drawing flags: */
-enum {
- DRAW_PICKING = (1 << 0),
- DRAW_CONSTCOLOR = (1 << 1),
- DRAW_SCENESET = (1 << 2),
-};
-
/* view3d_header.c */
void VIEW3D_OT_toggle_matcap_flip(struct wmOperatorType *ot);
@@ -54,84 +49,24 @@ void VIEW3D_OT_toggle_matcap_flip(struct wmOperatorType *ot);
void view3d_operatortypes(void);
/* view3d_edit.c */
-void VIEW3D_OT_zoom(struct wmOperatorType *ot);
-void VIEW3D_OT_dolly(struct wmOperatorType *ot);
void VIEW3D_OT_zoom_camera_1_to_1(struct wmOperatorType *ot);
-void VIEW3D_OT_move(struct wmOperatorType *ot);
-void VIEW3D_OT_rotate(struct wmOperatorType *ot);
-#ifdef WITH_INPUT_NDOF
-void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot);
-void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot);
-void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot);
-void VIEW3D_OT_ndof_all(struct wmOperatorType *ot);
-#endif /* WITH_INPUT_NDOF */
-void VIEW3D_OT_view_all(struct wmOperatorType *ot);
-void VIEW3D_OT_view_axis(struct wmOperatorType *ot);
-void VIEW3D_OT_view_camera(struct wmOperatorType *ot);
-void VIEW3D_OT_view_selected(struct wmOperatorType *ot);
void VIEW3D_OT_view_lock_clear(struct wmOperatorType *ot);
void VIEW3D_OT_view_lock_to_active(struct wmOperatorType *ot);
-void VIEW3D_OT_view_center_cursor(struct wmOperatorType *ot);
-void VIEW3D_OT_view_center_pick(struct wmOperatorType *ot);
void VIEW3D_OT_view_center_camera(struct wmOperatorType *ot);
void VIEW3D_OT_view_center_lock(struct wmOperatorType *ot);
-void VIEW3D_OT_view_pan(struct wmOperatorType *ot);
void VIEW3D_OT_view_persportho(struct wmOperatorType *ot);
void VIEW3D_OT_navigate(struct wmOperatorType *ot);
void VIEW3D_OT_background_image_add(struct wmOperatorType *ot);
void VIEW3D_OT_background_image_remove(struct wmOperatorType *ot);
void VIEW3D_OT_drop_world(struct wmOperatorType *ot);
-void VIEW3D_OT_view_orbit(struct wmOperatorType *ot);
-void VIEW3D_OT_view_roll(struct wmOperatorType *ot);
void VIEW3D_OT_clip_border(struct wmOperatorType *ot);
void VIEW3D_OT_cursor3d(struct wmOperatorType *ot);
void VIEW3D_OT_render_border(struct wmOperatorType *ot);
void VIEW3D_OT_clear_render_border(struct wmOperatorType *ot);
-void VIEW3D_OT_zoom_border(struct wmOperatorType *ot);
void VIEW3D_OT_toggle_shading(struct wmOperatorType *ot);
void VIEW3D_OT_toggle_xray(struct wmOperatorType *ot);
-/**
- * For home, center etc.
- */
-void view3d_boxview_copy(struct ScrArea *area, struct ARegion *region);
-/**
- * Sync center/zoom view of region to others, for view transforms.
- */
-void view3d_boxview_sync(struct ScrArea *area, struct ARegion *region);
-
-void view3d_orbit_apply_dyn_ofs(float r_ofs[3],
- const float ofs_old[3],
- const float viewquat_old[4],
- const float viewquat_new[4],
- const float dyn_ofs[3]);
-
-#ifdef WITH_INPUT_NDOF
-struct wmNDOFMotionData;
-
-/**
- * Called from both fly mode and walk mode,
- */
-void view3d_ndof_fly(const struct wmNDOFMotionData *ndof,
- struct View3D *v3d,
- struct RegionView3D *rv3d,
- bool use_precision,
- short protectflag,
- bool *r_has_translate,
- bool *r_has_rotate);
-#endif /* WITH_INPUT_NDOF */
-
-/* view3d_navigate_fly.c */
-
-void view3d_keymap(struct wmKeyConfig *keyconf);
-void VIEW3D_OT_fly(struct wmOperatorType *ot);
-
-/* view3d_navigate_walk.c */
-
-void VIEW3D_OT_walk(struct wmOperatorType *ot);
-
/* view3d_draw.c */
-
void view3d_main_region_draw(const struct bContext *C, struct ARegion *region);
/**
* Information drawn on top of the solid plates and composed data.
@@ -141,16 +76,16 @@ void view3d_draw_region_info(const struct bContext *C, struct ARegion *region);
/* view3d_draw_legacy.c */
void ED_view3d_draw_select_loop(struct Depsgraph *depsgraph,
- ViewContext *vc,
- Scene *scene,
+ struct ViewContext *vc,
+ struct Scene *scene,
struct ViewLayer *view_layer,
- View3D *v3d,
+ struct View3D *v3d,
struct ARegion *region,
bool use_obedit_skip,
bool use_nearest);
void ED_view3d_draw_depth_loop(struct Depsgraph *depsgraph,
- Scene *scene,
+ struct Scene *scene,
struct ARegion *region,
View3D *v3d);
@@ -168,57 +103,27 @@ void VIEW3D_OT_select_lasso(struct wmOperatorType *ot);
void VIEW3D_OT_select_menu(struct wmOperatorType *ot);
void VIEW3D_OT_bone_select_menu(struct wmOperatorType *ot);
-/* view3d_view.c */
-void VIEW3D_OT_smoothview(struct wmOperatorType *ot);
-void VIEW3D_OT_camera_to_view(struct wmOperatorType *ot);
-void VIEW3D_OT_camera_to_view_selected(struct wmOperatorType *ot);
-void VIEW3D_OT_object_as_camera(struct wmOperatorType *ot);
-void VIEW3D_OT_localview(struct wmOperatorType *ot);
-void VIEW3D_OT_localview_remove_from(struct wmOperatorType *ot);
+/* view3d_utils.c */
+/**
+ * For home, center etc.
+ */
+void view3d_boxview_copy(struct ScrArea *area, struct ARegion *region);
+/**
+ * Sync center/zoom view of region to others, for view transforms.
+ */
+void view3d_boxview_sync(struct ScrArea *area, struct ARegion *region);
bool ED_view3d_boundbox_clip_ex(const RegionView3D *rv3d,
const struct BoundBox *bb,
float obmat[4][4]);
bool ED_view3d_boundbox_clip(RegionView3D *rv3d, const struct BoundBox *bb);
-/**
- * Parameters for setting the new 3D Viewport state.
- *
- * Each of the struct members may be NULL to signify they aren't to be adjusted.
- */
-typedef struct V3D_SmoothParams {
- struct Object *camera_old, *camera;
- const float *ofs, *quat, *dist, *lens;
-
- /** Alternate rotation center, when set `ofs` must be NULL. */
- const float *dyn_ofs;
-} V3D_SmoothParams;
-
-/**
- * The arguments are the desired situation.
- */
-void ED_view3d_smooth_view_ex(const struct Depsgraph *depsgraph,
- struct wmWindowManager *wm,
- struct wmWindow *win,
- struct ScrArea *area,
- struct View3D *v3d,
- struct ARegion *region,
- int smooth_viewtx,
- const V3D_SmoothParams *sview);
-
-void ED_view3d_smooth_view(struct bContext *C,
- struct View3D *v3d,
- struct ARegion *region,
- int smooth_viewtx,
- const V3D_SmoothParams *sview);
-
-/**
- * Apply the smooth-view immediately, use when we need to start a new view operation.
- * (so we don't end up half-applying a view operation when pressing keys quickly).
- */
-void ED_view3d_smooth_view_force_finish(struct bContext *C,
- struct View3D *v3d,
- struct ARegion *region);
+/* view3d_view.c */
+void VIEW3D_OT_camera_to_view(struct wmOperatorType *ot);
+void VIEW3D_OT_camera_to_view_selected(struct wmOperatorType *ot);
+void VIEW3D_OT_object_as_camera(struct wmOperatorType *ot);
+void VIEW3D_OT_localview(struct wmOperatorType *ot);
+void VIEW3D_OT_localview_remove_from(struct wmOperatorType *ot);
/**
* \param rect: optional for picking (can be NULL).
@@ -247,12 +152,7 @@ void view3d_viewmatrix_set(struct Depsgraph *depsgraph,
/* Called in transform_ops.c, on each regeneration of key-maps. */
-void fly_modal_keymap(struct wmKeyConfig *keyconf);
-void walk_modal_keymap(struct wmKeyConfig *keyconf);
-void viewrotate_modal_keymap(struct wmKeyConfig *keyconf);
-void viewmove_modal_keymap(struct wmKeyConfig *keyconf);
-void viewzoom_modal_keymap(struct wmKeyConfig *keyconf);
-void viewdolly_modal_keymap(struct wmKeyConfig *keyconf);
+/* view3d_placement.c */
void viewplace_modal_keymap(struct wmKeyConfig *keyconf);
/* view3d_buttons.c */
@@ -267,7 +167,7 @@ void view3d_buttons_register(struct ARegionType *art);
* the view for first-person style navigation.
*/
struct View3DCameraControl *ED_view3d_cameracontrol_acquire(struct Depsgraph *depsgraph,
- Scene *scene,
+ struct Scene *scene,
View3D *v3d,
RegionView3D *rv3d);
/**
diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c
new file mode 100644
index 00000000000..98eef94d5fb
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate.c
@@ -0,0 +1,1593 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "DNA_curve_types.h"
+#include "DNA_gpencil_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_rect.h"
+
+#include "BKE_armature.h"
+#include "BKE_context.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_layer.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_vfont.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "ED_mesh.h"
+#include "ED_particle.h"
+#include "ED_screen.h"
+#include "ED_transform.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_resources.h"
+
+#include "view3d_intern.h"
+
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Navigation Polls
+ * \{ */
+
+static bool view3d_navigation_poll_impl(bContext *C, const char viewlock)
+{
+ if (!ED_operator_region_view3d_active(C)) {
+ return false;
+ }
+
+ const RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ return !(RV3D_LOCK_FLAGS(rv3d) & viewlock);
+}
+
+bool view3d_location_poll(bContext *C)
+{
+ return view3d_navigation_poll_impl(C, RV3D_LOCK_LOCATION);
+}
+
+bool view3d_rotation_poll(bContext *C)
+{
+ return view3d_navigation_poll_impl(C, RV3D_LOCK_ROTATION);
+}
+
+bool view3d_zoom_or_dolly_poll(bContext *C)
+{
+ return view3d_navigation_poll_impl(C, RV3D_LOCK_ZOOM_AND_DOLLY);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic View Operator Properties
+ * \{ */
+
+void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPropFlag flag)
+{
+ if (flag & V3D_OP_PROP_MOUSE_CO) {
+ PropertyRNA *prop;
+ prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Region Position X", "", 0, INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Region Position Y", "", 0, INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ }
+ if (flag & V3D_OP_PROP_DELTA) {
+ RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
+ }
+ if (flag & V3D_OP_PROP_USE_ALL_REGIONS) {
+ PropertyRNA *prop;
+ prop = RNA_def_boolean(
+ ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ }
+ if (flag & V3D_OP_PROP_USE_MOUSE_INIT) {
+ WM_operator_properties_use_cursor_init(ot);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic View Operator Custom-Data
+ * \{ */
+
+void calctrackballvec(const rcti *rect, const int event_xy[2], float r_dir[3])
+{
+ const float radius = V3D_OP_TRACKBALLSIZE;
+ const float t = radius / (float)M_SQRT2;
+ const float size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)};
+ /* Aspect correct so dragging in a non-square view doesn't squash the direction.
+ * So diagonal motion rotates the same direction the cursor is moving. */
+ const float size_min = min_ff(size[0], size[1]);
+ const float aspect[2] = {size_min / size[0], size_min / size[1]};
+
+ /* Normalize x and y. */
+ r_dir[0] = (event_xy[0] - BLI_rcti_cent_x(rect)) / ((size[0] * aspect[0]) / 2.0);
+ r_dir[1] = (event_xy[1] - BLI_rcti_cent_y(rect)) / ((size[1] * aspect[1]) / 2.0);
+ const float d = len_v2(r_dir);
+ if (d < t) {
+ /* Inside sphere. */
+ r_dir[2] = sqrtf(square_f(radius) - square_f(d));
+ }
+ else {
+ /* On hyperbola. */
+ r_dir[2] = square_f(t) / d;
+ }
+}
+
+void view3d_orbit_apply_dyn_ofs(float r_ofs[3],
+ const float ofs_old[3],
+ const float viewquat_old[4],
+ const float viewquat_new[4],
+ const float dyn_ofs[3])
+{
+ float q[4];
+ invert_qt_qt_normalized(q, viewquat_old);
+ mul_qt_qtqt(q, q, viewquat_new);
+
+ invert_qt_normalized(q);
+
+ sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs);
+ mul_qt_v3(q, r_ofs);
+ add_v3_v3(r_ofs, dyn_ofs);
+}
+
+void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
+{
+ if (vod->use_dyn_ofs) {
+ RegionView3D *rv3d = vod->rv3d;
+ view3d_orbit_apply_dyn_ofs(
+ rv3d->ofs, vod->init.ofs, vod->init.quat, viewquat_new, vod->dyn_ofs);
+ }
+}
+
+bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
+{
+ static float lastofs[3] = {0, 0, 0};
+ bool is_set = false;
+
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
+ View3D *v3d = CTX_wm_view3d(C);
+ Object *ob_act_eval = OBACT(view_layer_eval);
+ Object *ob_act = DEG_get_original_object(ob_act_eval);
+
+ if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
+ /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */
+ ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0) {
+ /* in case of sculpting use last average stroke position as a rotation
+ * center, in other cases it's not clear what rotation center shall be
+ * so just rotate around object origin
+ */
+ if (ob_act->mode &
+ (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
+ float stroke[3];
+ BKE_paint_stroke_get_average(scene, ob_act_eval, stroke);
+ copy_v3_v3(lastofs, stroke);
+ }
+ else {
+ copy_v3_v3(lastofs, ob_act_eval->obmat[3]);
+ }
+ is_set = true;
+ }
+ else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
+ Curve *cu = ob_act_eval->data;
+ EditFont *ef = cu->editfont;
+
+ zero_v3(lastofs);
+ for (int i = 0; i < 4; i++) {
+ add_v2_v2(lastofs, ef->textcurs[i]);
+ }
+ mul_v2_fl(lastofs, 1.0f / 4.0f);
+
+ mul_m4_v3(ob_act_eval->obmat, lastofs);
+
+ is_set = true;
+ }
+ else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) {
+ /* object mode use boundbox centers */
+ Base *base_eval;
+ uint tot = 0;
+ float select_center[3];
+
+ zero_v3(select_center);
+ for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) {
+ if (BASE_SELECTED(v3d, base_eval)) {
+ /* use the boundbox if we can */
+ Object *ob_eval = base_eval->object;
+
+ if (ob_eval->runtime.bb && !(ob_eval->runtime.bb->flag & BOUNDBOX_DIRTY)) {
+ float cent[3];
+
+ BKE_boundbox_calc_center_aabb(ob_eval->runtime.bb, cent);
+
+ mul_m4_v3(ob_eval->obmat, cent);
+ add_v3_v3(select_center, cent);
+ }
+ else {
+ add_v3_v3(select_center, ob_eval->obmat[3]);
+ }
+ tot++;
+ }
+ }
+ if (tot) {
+ mul_v3_fl(select_center, 1.0f / (float)tot);
+ copy_v3_v3(lastofs, select_center);
+ is_set = true;
+ }
+ }
+ else {
+ /* If there's no selection, `lastofs` is unmodified and last value since static. */
+ is_set = calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, lastofs, NULL);
+ }
+
+ copy_v3_v3(r_dyn_ofs, lastofs);
+
+ return is_set;
+}
+
+static enum eViewOpsFlag viewops_flag_from_args(bool use_select, bool use_depth)
+{
+ enum eViewOpsFlag flag = 0;
+ if (use_select) {
+ flag |= VIEWOPS_FLAG_ORBIT_SELECT;
+ }
+ if (use_depth) {
+ flag |= VIEWOPS_FLAG_DEPTH_NAVIGATE;
+ }
+
+ return flag;
+}
+
+enum eViewOpsFlag viewops_flag_from_prefs(void)
+{
+ return viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0,
+ (U.uiflag & USER_DEPTH_NAVIGATE) != 0);
+}
+
+ViewOpsData *viewops_data_create(bContext *C, const wmEvent *event, enum eViewOpsFlag viewops_flag)
+{
+ ViewOpsData *vod = MEM_callocN(sizeof(ViewOpsData), __func__);
+
+ /* Store data. */
+ vod->bmain = CTX_data_main(C);
+ vod->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ vod->scene = CTX_data_scene(C);
+ vod->area = CTX_wm_area(C);
+ vod->region = CTX_wm_region(C);
+ vod->v3d = vod->area->spacedata.first;
+ vod->rv3d = vod->region->regiondata;
+
+ Depsgraph *depsgraph = vod->depsgraph;
+ RegionView3D *rv3d = vod->rv3d;
+
+ /* Could do this more nicely. */
+ if ((viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) == 0) {
+ viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE;
+ }
+
+ /* we need the depth info before changing any viewport options */
+ if (viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE) {
+ float fallback_depth_pt[3];
+
+ view3d_operator_needs_opengl(C); /* Needed for Z-buffer drawing. */
+
+ negate_v3_v3(fallback_depth_pt, rv3d->ofs);
+
+ vod->use_dyn_ofs = ED_view3d_autodist(
+ depsgraph, vod->region, vod->v3d, event->mval, vod->dyn_ofs, true, fallback_depth_pt);
+ }
+ else {
+ vod->use_dyn_ofs = false;
+ }
+
+ if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) {
+ if (ED_view3d_persp_ensure(depsgraph, vod->v3d, vod->region)) {
+ /* If we're switching from camera view to the perspective one,
+ * need to tag viewport update, so camera view and borders are properly updated. */
+ ED_region_tag_redraw(vod->region);
+ }
+ }
+
+ /* set the view from the camera, if view locking is enabled.
+ * we may want to make this optional but for now its needed always */
+ ED_view3d_camera_lock_init(depsgraph, vod->v3d, vod->rv3d);
+
+ vod->init.persp = rv3d->persp;
+ vod->init.dist = rv3d->dist;
+ vod->init.camzoom = rv3d->camzoom;
+ copy_qt_qt(vod->init.quat, rv3d->viewquat);
+ copy_v2_v2_int(vod->init.event_xy, event->xy);
+ copy_v2_v2_int(vod->prev.event_xy, event->xy);
+
+ if (viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) {
+ zero_v2_int(vod->init.event_xy_offset);
+ }
+ else {
+ /* Simulate the event starting in the middle of the region. */
+ vod->init.event_xy_offset[0] = BLI_rcti_cent_x(&vod->region->winrct) - event->xy[0];
+ vod->init.event_xy_offset[1] = BLI_rcti_cent_y(&vod->region->winrct) - event->xy[1];
+ }
+
+ vod->init.event_type = event->type;
+ copy_v3_v3(vod->init.ofs, rv3d->ofs);
+
+ copy_qt_qt(vod->curr.viewquat, rv3d->viewquat);
+
+ if (viewops_flag & VIEWOPS_FLAG_ORBIT_SELECT) {
+ float ofs[3];
+ if (view3d_orbit_calc_center(C, ofs) || (vod->use_dyn_ofs == false)) {
+ vod->use_dyn_ofs = true;
+ negate_v3_v3(vod->dyn_ofs, ofs);
+ viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE;
+ }
+ }
+
+ if (viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE) {
+ if (vod->use_dyn_ofs) {
+ if (rv3d->is_persp) {
+ float my_origin[3]; /* Original #RegionView3D.ofs. */
+ float my_pivot[3]; /* View pivot. */
+ float dvec[3];
+
+ /* locals for dist correction */
+ float mat[3][3];
+ float upvec[3];
+
+ negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
+
+ /* Set the dist value to be the distance from this 3d point this means you'll
+ * always be able to zoom into it and panning won't go bad when dist was zero. */
+
+ /* remove dist value */
+ upvec[0] = upvec[1] = 0;
+ upvec[2] = rv3d->dist;
+ copy_m3_m4(mat, rv3d->viewinv);
+
+ mul_m3_v3(mat, upvec);
+ sub_v3_v3v3(my_pivot, rv3d->ofs, upvec);
+ negate_v3(my_pivot); /* ofs is flipped */
+
+ /* find a new ofs value that is along the view axis
+ * (rather than the mouse location) */
+ closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin);
+ vod->init.dist = rv3d->dist = len_v3v3(my_pivot, dvec);
+
+ negate_v3_v3(rv3d->ofs, dvec);
+ }
+ else {
+ const float mval_region_mid[2] = {(float)vod->region->winx / 2.0f,
+ (float)vod->region->winy / 2.0f};
+
+ ED_view3d_win_to_3d(vod->v3d, vod->region, vod->dyn_ofs, mval_region_mid, rv3d->ofs);
+ negate_v3(rv3d->ofs);
+ }
+ negate_v3(vod->dyn_ofs);
+ copy_v3_v3(vod->init.ofs, rv3d->ofs);
+ }
+ }
+
+ /* For dolly */
+ ED_view3d_win_to_vector(vod->region, (const float[2]){UNPACK2(event->mval)}, vod->init.mousevec);
+
+ {
+ int event_xy_offset[2];
+ add_v2_v2v2_int(event_xy_offset, event->xy, vod->init.event_xy_offset);
+
+ /* For rotation with trackball rotation. */
+ calctrackballvec(&vod->region->winrct, event_xy_offset, vod->init.trackvec);
+ }
+
+ {
+ float tvec[3];
+ negate_v3_v3(tvec, rv3d->ofs);
+ vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL);
+ }
+
+ vod->reverse = 1.0f;
+ if (rv3d->persmat[2][1] < 0.0f) {
+ vod->reverse = -1.0f;
+ }
+
+ rv3d->rflag |= RV3D_NAVIGATING;
+
+ return vod;
+}
+
+void viewops_data_free(bContext *C, ViewOpsData *vod)
+{
+ ARegion *region;
+ if (vod) {
+ region = vod->region;
+ vod->rv3d->rflag &= ~RV3D_NAVIGATING;
+
+ if (vod->timer) {
+ WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
+ }
+
+ if (vod->init.dial) {
+ MEM_freeN(vod->init.dial);
+ }
+
+ MEM_freeN(vod);
+ }
+ else {
+ region = CTX_wm_region(C);
+ }
+
+ /* Need to redraw because drawing code uses RV3D_NAVIGATING to draw
+ * faster while navigation operator runs. */
+ ED_region_tag_redraw(region);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic View Operator Utilities
+ * \{ */
+
+/**
+ * \param align_to_quat: When not NULL, set the axis relative to this rotation.
+ */
+static void axis_set_view(bContext *C,
+ View3D *v3d,
+ ARegion *region,
+ const float quat_[4],
+ char view,
+ char view_axis_roll,
+ int perspo,
+ const float *align_to_quat,
+ const int smooth_viewtx)
+{
+ RegionView3D *rv3d = region->regiondata; /* no NULL check is needed, poll checks */
+ float quat[4];
+ const short orig_persp = rv3d->persp;
+
+ normalize_qt_qt(quat, quat_);
+
+ if (align_to_quat) {
+ mul_qt_qtqt(quat, quat, align_to_quat);
+ rv3d->view = view = RV3D_VIEW_USER;
+ rv3d->view_axis_roll = RV3D_VIEW_AXIS_ROLL_0;
+ }
+
+ if (align_to_quat == NULL) {
+ rv3d->view = view;
+ rv3d->view_axis_roll = view_axis_roll;
+ }
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) {
+ ED_region_tag_redraw(region);
+ return;
+ }
+
+ if (U.uiflag & USER_AUTOPERSP) {
+ rv3d->persp = RV3D_VIEW_IS_AXIS(view) ? RV3D_ORTHO : perspo;
+ }
+ else if (rv3d->persp == RV3D_CAMOB) {
+ rv3d->persp = perspo;
+ }
+
+ if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
+ /* to camera */
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .camera_old = v3d->camera,
+ .ofs = rv3d->ofs,
+ .quat = quat,
+ });
+ }
+ else if (orig_persp == RV3D_CAMOB && v3d->camera) {
+ /* from camera */
+ float ofs[3], dist;
+
+ copy_v3_v3(ofs, rv3d->ofs);
+ dist = rv3d->dist;
+
+ /* so we animate _from_ the camera location */
+ Object *camera_eval = DEG_get_evaluated_object(CTX_data_ensure_evaluated_depsgraph(C),
+ v3d->camera);
+ ED_view3d_from_object(camera_eval, rv3d->ofs, NULL, &rv3d->dist, NULL);
+
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .camera_old = camera_eval,
+ .ofs = ofs,
+ .quat = quat,
+ .dist = &dist,
+ });
+ }
+ else {
+ /* rotate around selection */
+ const float *dyn_ofs_pt = NULL;
+ float dyn_ofs[3];
+
+ if (U.uiflag & USER_ORBIT_SELECTION) {
+ if (view3d_orbit_calc_center(C, dyn_ofs)) {
+ negate_v3(dyn_ofs);
+ dyn_ofs_pt = dyn_ofs;
+ }
+ }
+
+ /* no camera involved */
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .quat = quat,
+ .dyn_ofs = dyn_ofs_pt,
+ });
+ }
+}
+
+void viewmove_apply(ViewOpsData *vod, int x, int y)
+{
+ if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) {
+ vod->rv3d->ofs_lock[0] -= ((vod->prev.event_xy[0] - x) * 2.0f) / (float)vod->region->winx;
+ vod->rv3d->ofs_lock[1] -= ((vod->prev.event_xy[1] - y) * 2.0f) / (float)vod->region->winy;
+ }
+ else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
+ const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
+ vod->rv3d->camdx += (vod->prev.event_xy[0] - x) / (vod->region->winx * zoomfac);
+ vod->rv3d->camdy += (vod->prev.event_xy[1] - y) / (vod->region->winy * zoomfac);
+ CLAMP(vod->rv3d->camdx, -1.0f, 1.0f);
+ CLAMP(vod->rv3d->camdy, -1.0f, 1.0f);
+ }
+ else {
+ float dvec[3];
+ float mval_f[2];
+
+ mval_f[0] = x - vod->prev.event_xy[0];
+ mval_f[1] = y - vod->prev.event_xy[1];
+ ED_view3d_win_to_delta(vod->region, mval_f, dvec, vod->init.zfac);
+
+ add_v3_v3(vod->rv3d->ofs, dvec);
+
+ if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(vod->area, vod->region);
+ }
+ }
+
+ vod->prev.event_xy[0] = x;
+ vod->prev.event_xy[1] = y;
+
+ ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
+
+ ED_region_tag_redraw(vod->region);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View All Operator
+ *
+ * Move & Zoom the view to fit all of its contents.
+ * \{ */
+
+static bool view3d_object_skip_minmax(const View3D *v3d,
+ const RegionView3D *rv3d,
+ const Object *ob,
+ const bool skip_camera,
+ bool *r_only_center)
+{
+ BLI_assert(ob->id.orig_id == NULL);
+ *r_only_center = false;
+
+ if (skip_camera && (ob == v3d->camera)) {
+ return true;
+ }
+
+ if ((ob->type == OB_EMPTY) && (ob->empty_drawtype == OB_EMPTY_IMAGE) &&
+ !BKE_object_empty_image_frame_is_visible_in_view3d(ob, rv3d)) {
+ *r_only_center = true;
+ return false;
+ }
+
+ return false;
+}
+
+static void view3d_object_calc_minmax(Depsgraph *depsgraph,
+ Scene *scene,
+ Object *ob_eval,
+ const bool only_center,
+ float min[3],
+ float max[3])
+{
+ /* Account for duplis. */
+ if (BKE_object_minmax_dupli(depsgraph, scene, ob_eval, min, max, false) == 0) {
+ /* Use if duplis aren't found. */
+ if (only_center) {
+ minmax_v3v3_v3(min, max, ob_eval->obmat[3]);
+ }
+ else {
+ BKE_object_minmax(ob_eval, min, max, false);
+ }
+ }
+}
+
+static void view3d_from_minmax(bContext *C,
+ View3D *v3d,
+ ARegion *region,
+ const float min[3],
+ const float max[3],
+ bool ok_dist,
+ const int smooth_viewtx)
+{
+ RegionView3D *rv3d = region->regiondata;
+ float afm[3];
+ float size;
+
+ ED_view3d_smooth_view_force_finish(C, v3d, region);
+
+ /* SMOOTHVIEW */
+ float new_ofs[3];
+ float new_dist;
+
+ sub_v3_v3v3(afm, max, min);
+ size = max_fff(afm[0], afm[1], afm[2]);
+
+ if (ok_dist) {
+ char persp;
+
+ if (rv3d->is_persp) {
+ if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) {
+ persp = RV3D_CAMOB;
+ }
+ else {
+ persp = RV3D_PERSP;
+ }
+ }
+ else { /* ortho */
+ if (size < 0.0001f) {
+ /* bounding box was a single point so do not zoom */
+ ok_dist = false;
+ }
+ else {
+ /* adjust zoom so it looks nicer */
+ persp = RV3D_ORTHO;
+ }
+ }
+
+ if (ok_dist) {
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ new_dist = ED_view3d_radius_to_dist(
+ v3d, region, depsgraph, persp, true, (size / 2) * VIEW3D_MARGIN);
+ if (rv3d->is_persp) {
+ /* don't zoom closer than the near clipping plane */
+ new_dist = max_ff(new_dist, v3d->clip_start * 1.5f);
+ }
+ }
+ }
+
+ mid_v3_v3v3(new_ofs, min, max);
+ negate_v3(new_ofs);
+
+ if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) {
+ rv3d->persp = RV3D_PERSP;
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .camera_old = v3d->camera,
+ .ofs = new_ofs,
+ .dist = ok_dist ? &new_dist : NULL,
+ });
+ }
+ else {
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .ofs = new_ofs,
+ .dist = ok_dist ? &new_dist : NULL,
+ });
+ }
+
+ /* Smooth-view does view-lock #RV3D_BOXVIEW copy. */
+}
+
+/**
+ * Same as #view3d_from_minmax but for all regions (except cameras).
+ */
+static void view3d_from_minmax_multi(bContext *C,
+ View3D *v3d,
+ const float min[3],
+ const float max[3],
+ const bool ok_dist,
+ const int smooth_viewtx)
+{
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region;
+ for (region = area->regionbase.first; region; region = region->next) {
+ if (region->regiontype == RGN_TYPE_WINDOW) {
+ RegionView3D *rv3d = region->regiondata;
+ /* when using all regions, don't jump out of camera view,
+ * but _do_ allow locked cameras to be moved */
+ if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
+ view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx);
+ }
+ }
+ }
+}
+
+static int view3d_all_exec(bContext *C, wmOperator *op)
+{
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
+ Base *base_eval;
+ const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
+ const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) ||
+ /* any one of the regions may be locked */
+ (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
+ const bool center = RNA_boolean_get(op->ptr, "center");
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ float min[3], max[3];
+ bool changed = false;
+
+ if (center) {
+ /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */
+ View3DCursor *cursor = &scene->cursor;
+ zero_v3(min);
+ zero_v3(max);
+ zero_v3(cursor->location);
+ float mat3[3][3];
+ unit_m3(mat3);
+ BKE_scene_cursor_mat3_to_rot(cursor, mat3, false);
+ }
+ else {
+ INIT_MINMAX(min, max);
+ }
+
+ for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) {
+ if (BASE_VISIBLE(v3d, base_eval)) {
+ bool only_center = false;
+ Object *ob = DEG_get_original_object(base_eval->object);
+ if (view3d_object_skip_minmax(v3d, rv3d, ob, skip_camera, &only_center)) {
+ continue;
+ }
+ view3d_object_calc_minmax(depsgraph, scene, base_eval->object, only_center, min, max);
+ changed = true;
+ }
+ }
+
+ if (center) {
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ WM_msg_publish_rna_prop(mbus, &scene->id, &scene->cursor, View3DCursor, location);
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+ }
+
+ if (!changed) {
+ ED_region_tag_redraw(region);
+ /* TODO: should this be cancel?
+ * I think no, because we always move the cursor, with or without
+ * object, but in this case there is no change in the scene,
+ * only the cursor so I choice a ED_region_tag like
+ * view3d_smooth_view do for the center_cursor.
+ * See bug T22640.
+ */
+ return OPERATOR_FINISHED;
+ }
+
+ if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
+ /* This is an approximation, see function documentation for details. */
+ ED_view3d_clipping_clamp_minmax(rv3d, min, max);
+ }
+
+ if (use_all_regions) {
+ view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx);
+ }
+ else {
+ view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_view_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Frame All";
+ ot->description = "View all objects in scene";
+ ot->idname = "VIEW3D_OT_view_all";
+
+ /* api callbacks */
+ ot->exec = view3d_all_exec;
+ ot->poll = ED_operator_region_view3d_active;
+
+ /* flags */
+ ot->flag = 0;
+
+ /* properties */
+ view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS);
+ RNA_def_boolean(ot->srna, "center", 0, "Center", "");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Frame Selected Operator
+ *
+ * Move & Zoom the view to fit selected contents.
+ * \{ */
+
+static int viewselected_exec(bContext *C, wmOperator *op)
+{
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
+ Object *ob_eval = OBACT(view_layer_eval);
+ Object *obedit = CTX_data_edit_object(C);
+ const bGPdata *gpd_eval = ob_eval && (ob_eval->type == OB_GPENCIL) ? ob_eval->data : NULL;
+ const bool is_gp_edit = gpd_eval ? GPENCIL_ANY_MODE(gpd_eval) : false;
+ const bool is_face_map = ((is_gp_edit == false) && region->gizmo_map &&
+ WM_gizmomap_is_any_selected(region->gizmo_map));
+ float min[3], max[3];
+ bool ok = false, ok_dist = true;
+ const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
+ const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) ||
+ /* any one of the regions may be locked */
+ (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ INIT_MINMAX(min, max);
+ if (is_face_map) {
+ ob_eval = NULL;
+ }
+
+ if (ob_eval && (ob_eval->mode & OB_MODE_WEIGHT_PAINT)) {
+ /* hard-coded exception, we look for the one selected armature */
+ /* this is weak code this way, we should make a generic
+ * active/selection callback interface once... */
+ Base *base_eval;
+ for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) {
+ if (BASE_SELECTED_EDITABLE(v3d, base_eval)) {
+ if (base_eval->object->type == OB_ARMATURE) {
+ if (base_eval->object->mode & OB_MODE_POSE) {
+ break;
+ }
+ }
+ }
+ }
+ if (base_eval) {
+ ob_eval = base_eval->object;
+ }
+ }
+
+ if (is_gp_edit) {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ /* we're only interested in selected points here... */
+ if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) {
+ ok |= BKE_gpencil_stroke_minmax(gps, true, min, max);
+ }
+ if (gps->editcurve != NULL) {
+ for (int i = 0; i < gps->editcurve->tot_curve_points; i++) {
+ BezTriple *bezt = &gps->editcurve->curve_points[i].bezt;
+ if ((bezt->f1 & SELECT)) {
+ minmax_v3v3_v3(min, max, bezt->vec[0]);
+ ok = true;
+ }
+ if ((bezt->f2 & SELECT)) {
+ minmax_v3v3_v3(min, max, bezt->vec[1]);
+ ok = true;
+ }
+ if ((bezt->f3 & SELECT)) {
+ minmax_v3v3_v3(min, max, bezt->vec[2]);
+ ok = true;
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ if ((ob_eval) && (ok)) {
+ mul_m4_v3(ob_eval->obmat, min);
+ mul_m4_v3(ob_eval->obmat, max);
+ }
+ }
+ else if (is_face_map) {
+ ok = WM_gizmomap_minmax(region->gizmo_map, true, true, min, max);
+ }
+ else if (obedit) {
+ /* only selected */
+ FOREACH_OBJECT_IN_MODE_BEGIN (view_layer_eval, v3d, obedit->type, obedit->mode, ob_eval_iter) {
+ ok |= ED_view3d_minmax_verts(ob_eval_iter, min, max);
+ }
+ FOREACH_OBJECT_IN_MODE_END;
+ }
+ else if (ob_eval && (ob_eval->mode & OB_MODE_POSE)) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (
+ view_layer_eval, v3d, ob_eval->type, ob_eval->mode, ob_eval_iter) {
+ ok |= BKE_pose_minmax(ob_eval_iter, min, max, true, true);
+ }
+ FOREACH_OBJECT_IN_MODE_END;
+ }
+ else if (BKE_paint_select_face_test(ob_eval)) {
+ ok = paintface_minmax(ob_eval, min, max);
+ }
+ else if (ob_eval && (ob_eval->mode & OB_MODE_PARTICLE_EDIT)) {
+ ok = PE_minmax(depsgraph, scene, CTX_data_view_layer(C), min, max);
+ }
+ else if (ob_eval && (ob_eval->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT |
+ OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
+ BKE_paint_stroke_get_average(scene, ob_eval, min);
+ copy_v3_v3(max, min);
+ ok = true;
+ ok_dist = 0; /* don't zoom */
+ }
+ else {
+ Base *base_eval;
+ for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) {
+ if (BASE_SELECTED(v3d, base_eval)) {
+ bool only_center = false;
+ Object *ob = DEG_get_original_object(base_eval->object);
+ if (view3d_object_skip_minmax(v3d, rv3d, ob, skip_camera, &only_center)) {
+ continue;
+ }
+ view3d_object_calc_minmax(depsgraph, scene, base_eval->object, only_center, min, max);
+ ok = 1;
+ }
+ }
+ }
+
+ if (ok == 0) {
+ return OPERATOR_FINISHED;
+ }
+
+ if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
+ /* This is an approximation, see function documentation for details. */
+ ED_view3d_clipping_clamp_minmax(rv3d, min, max);
+ }
+
+ if (use_all_regions) {
+ view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx);
+ }
+ else {
+ view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_view_selected(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Frame Selected";
+ ot->description = "Move the view to the selection center";
+ ot->idname = "VIEW3D_OT_view_selected";
+
+ /* api callbacks */
+ ot->exec = viewselected_exec;
+ ot->poll = view3d_zoom_or_dolly_poll;
+
+ /* flags */
+ ot->flag = 0;
+
+ /* properties */
+ view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Center Cursor Operator
+ * \{ */
+
+static int viewcenter_cursor_exec(bContext *C, wmOperator *op)
+{
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ Scene *scene = CTX_data_scene(C);
+
+ if (rv3d) {
+ ARegion *region = CTX_wm_region(C);
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ ED_view3d_smooth_view_force_finish(C, v3d, region);
+
+ /* non camera center */
+ float new_ofs[3];
+ negate_v3_v3(new_ofs, scene->cursor.location);
+ ED_view3d_smooth_view(
+ C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs});
+
+ /* Smooth view does view-lock #RV3D_BOXVIEW copy. */
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_view_center_cursor(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Center View to Cursor";
+ ot->description = "Center the view so that the cursor is in the middle of the view";
+ ot->idname = "VIEW3D_OT_view_center_cursor";
+
+ /* api callbacks */
+ ot->exec = viewcenter_cursor_exec;
+ ot->poll = view3d_location_poll;
+
+ /* flags */
+ ot->flag = 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Center Pick Operator
+ * \{ */
+
+static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+
+ if (rv3d) {
+ struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ float new_ofs[3];
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ ED_view3d_smooth_view_force_finish(C, v3d, region);
+
+ view3d_operator_needs_opengl(C);
+
+ if (ED_view3d_autodist(depsgraph, region, v3d, event->mval, new_ofs, false, NULL)) {
+ /* pass */
+ }
+ else {
+ /* fallback to simple pan */
+ negate_v3_v3(new_ofs, rv3d->ofs);
+ ED_view3d_win_to_3d_int(v3d, region, new_ofs, event->mval, new_ofs);
+ }
+ negate_v3(new_ofs);
+ ED_view3d_smooth_view(
+ C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs});
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_view_center_pick(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Center View to Mouse";
+ ot->description = "Center the view to the Z-depth position under the mouse cursor";
+ ot->idname = "VIEW3D_OT_view_center_pick";
+
+ /* api callbacks */
+ ot->invoke = viewcenter_pick_invoke;
+ ot->poll = view3d_location_poll;
+
+ /* flags */
+ ot->flag = 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Axis Operator
+ * \{ */
+
+static const EnumPropertyItem prop_view_items[] = {
+ {RV3D_VIEW_LEFT, "LEFT", ICON_TRIA_LEFT, "Left", "View from the left"},
+ {RV3D_VIEW_RIGHT, "RIGHT", ICON_TRIA_RIGHT, "Right", "View from the right"},
+ {RV3D_VIEW_BOTTOM, "BOTTOM", ICON_TRIA_DOWN, "Bottom", "View from the bottom"},
+ {RV3D_VIEW_TOP, "TOP", ICON_TRIA_UP, "Top", "View from the top"},
+ {RV3D_VIEW_FRONT, "FRONT", 0, "Front", "View from the front"},
+ {RV3D_VIEW_BACK, "BACK", 0, "Back", "View from the back"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int view_axis_exec(bContext *C, wmOperator *op)
+{
+ View3D *v3d;
+ ARegion *region;
+ RegionView3D *rv3d;
+ static int perspo = RV3D_PERSP;
+ int viewnum;
+ int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0;
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ /* no NULL check is needed, poll checks */
+ ED_view3d_context_user_region(C, &v3d, &region);
+ rv3d = region->regiondata;
+
+ ED_view3d_smooth_view_force_finish(C, v3d, region);
+
+ viewnum = RNA_enum_get(op->ptr, "type");
+
+ float align_quat_buf[4];
+ float *align_quat = NULL;
+
+ if (RNA_boolean_get(op->ptr, "align_active")) {
+ /* align to active object */
+ Object *obact = CTX_data_active_object(C);
+ if (obact != NULL) {
+ float twmat[3][3];
+ struct ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *obedit = CTX_data_edit_object(C);
+ /* same as transform gizmo when normal is set */
+ ED_getTransformOrientationMatrix(view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat);
+ align_quat = align_quat_buf;
+ mat3_to_quat(align_quat, twmat);
+ invert_qt_normalized(align_quat);
+ }
+ }
+
+ if (RNA_boolean_get(op->ptr, "relative")) {
+ float quat_rotate[4];
+ float quat_test[4];
+
+ if (viewnum == RV3D_VIEW_LEFT) {
+ axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], -M_PI / 2.0f);
+ }
+ else if (viewnum == RV3D_VIEW_RIGHT) {
+ axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], M_PI / 2.0f);
+ }
+ else if (viewnum == RV3D_VIEW_TOP) {
+ axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], -M_PI / 2.0f);
+ }
+ else if (viewnum == RV3D_VIEW_BOTTOM) {
+ axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI / 2.0f);
+ }
+ else if (viewnum == RV3D_VIEW_FRONT) {
+ unit_qt(quat_rotate);
+ }
+ else if (viewnum == RV3D_VIEW_BACK) {
+ axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI);
+ }
+ else {
+ BLI_assert(0);
+ }
+
+ mul_qt_qtqt(quat_test, rv3d->viewquat, quat_rotate);
+
+ float angle_best = FLT_MAX;
+ int view_best = -1;
+ int view_axis_roll_best = -1;
+ for (int i = RV3D_VIEW_FRONT; i <= RV3D_VIEW_BOTTOM; i++) {
+ for (int j = RV3D_VIEW_AXIS_ROLL_0; j <= RV3D_VIEW_AXIS_ROLL_270; j++) {
+ float quat_axis[4];
+ ED_view3d_quat_from_axis_view(i, j, quat_axis);
+ if (align_quat) {
+ mul_qt_qtqt(quat_axis, quat_axis, align_quat);
+ }
+ const float angle_test = fabsf(angle_signed_qtqt(quat_axis, quat_test));
+ if (angle_best > angle_test) {
+ angle_best = angle_test;
+ view_best = i;
+ view_axis_roll_best = j;
+ }
+ }
+ }
+ if (view_best == -1) {
+ view_best = RV3D_VIEW_FRONT;
+ view_axis_roll_best = RV3D_VIEW_AXIS_ROLL_0;
+ }
+
+ /* Disallow non-upright views in turn-table modes,
+ * it's too difficult to navigate out of them. */
+ if ((U.flag & USER_TRACKBALL) == 0) {
+ if (!ELEM(view_best, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
+ view_axis_roll_best = RV3D_VIEW_AXIS_ROLL_0;
+ }
+ }
+
+ viewnum = view_best;
+ view_axis_roll = view_axis_roll_best;
+ }
+
+ /* Use this to test if we started out with a camera */
+ const int nextperspo = (rv3d->persp == RV3D_CAMOB) ? rv3d->lpersp : perspo;
+ float quat[4];
+ ED_view3d_quat_from_axis_view(viewnum, view_axis_roll, quat);
+ axis_set_view(
+ C, v3d, region, quat, viewnum, view_axis_roll, nextperspo, align_quat, smooth_viewtx);
+
+ perspo = rv3d->persp;
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_view_axis(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "View Axis";
+ ot->description = "Use a preset viewpoint";
+ ot->idname = "VIEW3D_OT_view_axis";
+
+ /* api callbacks */
+ ot->exec = view_axis_exec;
+ ot->poll = ED_operator_rv3d_user_region_poll;
+
+ /* flags */
+ ot->flag = 0;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", prop_view_items, 0, "View", "Preset viewpoint to use");
+ RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(
+ ot->srna, "align_active", 0, "Align Active", "Align to the active object's axis");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(
+ ot->srna, "relative", 0, "Relative", "Rotate relative to the current orientation");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Camera Operator
+ * \{ */
+
+static int view_camera_exec(bContext *C, wmOperator *op)
+{
+ View3D *v3d;
+ ARegion *region;
+ RegionView3D *rv3d;
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ /* no NULL check is needed, poll checks */
+ ED_view3d_context_user_region(C, &v3d, &region);
+ rv3d = region->regiondata;
+
+ ED_view3d_smooth_view_force_finish(C, v3d, region);
+
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) == 0) {
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Scene *scene = CTX_data_scene(C);
+
+ if (rv3d->persp != RV3D_CAMOB) {
+ Object *ob = OBACT(view_layer);
+
+ if (!rv3d->smooth_timer) {
+ /* store settings of current view before allowing overwriting with camera view
+ * only if we're not currently in a view transition */
+
+ ED_view3d_lastview_store(rv3d);
+ }
+
+ /* first get the default camera for the view lock type */
+ if (v3d->scenelock) {
+ /* sets the camera view if available */
+ v3d->camera = scene->camera;
+ }
+ else {
+ /* use scene camera if one is not set (even though we're unlocked) */
+ if (v3d->camera == NULL) {
+ v3d->camera = scene->camera;
+ }
+ }
+
+ /* if the camera isn't found, check a number of options */
+ if (v3d->camera == NULL && ob && ob->type == OB_CAMERA) {
+ v3d->camera = ob;
+ }
+
+ if (v3d->camera == NULL) {
+ v3d->camera = BKE_view_layer_camera_find(view_layer);
+ }
+
+ /* couldn't find any useful camera, bail out */
+ if (v3d->camera == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* important these don't get out of sync for locked scenes */
+ if (v3d->scenelock && scene->camera != v3d->camera) {
+ scene->camera = v3d->camera;
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+ }
+
+ /* finally do snazzy view zooming */
+ rv3d->persp = RV3D_CAMOB;
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .camera = v3d->camera,
+ .ofs = rv3d->ofs,
+ .quat = rv3d->viewquat,
+ .dist = &rv3d->dist,
+ .lens = &v3d->lens,
+ });
+ }
+ else {
+ /* return to settings of last view */
+ /* does view3d_smooth_view too */
+ axis_set_view(C,
+ v3d,
+ region,
+ rv3d->lviewquat,
+ rv3d->lview,
+ rv3d->lview_axis_roll,
+ rv3d->lpersp,
+ NULL,
+ smooth_viewtx);
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_view_camera(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "View Camera";
+ ot->description = "Toggle the camera view";
+ ot->idname = "VIEW3D_OT_view_camera";
+
+ /* api callbacks */
+ ot->exec = view_camera_exec;
+ ot->poll = ED_operator_rv3d_user_region_poll;
+
+ /* flags */
+ ot->flag = 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Orbit Operator
+ *
+ * Rotate (orbit) in incremental steps. For interactive orbit see #VIEW3D_OT_rotate.
+ * \{ */
+
+enum {
+ V3D_VIEW_STEPLEFT = 1,
+ V3D_VIEW_STEPRIGHT,
+ V3D_VIEW_STEPDOWN,
+ V3D_VIEW_STEPUP,
+};
+
+static const EnumPropertyItem prop_view_orbit_items[] = {
+ {V3D_VIEW_STEPLEFT, "ORBITLEFT", 0, "Orbit Left", "Orbit the view around to the left"},
+ {V3D_VIEW_STEPRIGHT, "ORBITRIGHT", 0, "Orbit Right", "Orbit the view around to the right"},
+ {V3D_VIEW_STEPUP, "ORBITUP", 0, "Orbit Up", "Orbit the view up"},
+ {V3D_VIEW_STEPDOWN, "ORBITDOWN", 0, "Orbit Down", "Orbit the view down"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int vieworbit_exec(bContext *C, wmOperator *op)
+{
+ View3D *v3d;
+ ARegion *region;
+ RegionView3D *rv3d;
+ int orbitdir;
+ char view_opposite;
+ PropertyRNA *prop_angle = RNA_struct_find_property(op->ptr, "angle");
+ float angle = RNA_property_is_set(op->ptr, prop_angle) ?
+ RNA_property_float_get(op->ptr, prop_angle) :
+ DEG2RADF(U.pad_rot_angle);
+
+ /* no NULL check is needed, poll checks */
+ v3d = CTX_wm_view3d(C);
+ region = CTX_wm_region(C);
+ rv3d = region->regiondata;
+
+ /* support for switching to the opposite view (even when in locked views) */
+ view_opposite = (fabsf(angle) == (float)M_PI) ? ED_view3d_axis_view_opposite(rv3d->view) :
+ RV3D_VIEW_USER;
+ orbitdir = RNA_enum_get(op->ptr, "type");
+
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) && (view_opposite == RV3D_VIEW_USER)) {
+ /* no NULL check is needed, poll checks */
+ ED_view3d_context_user_region(C, &v3d, &region);
+ rv3d = region->regiondata;
+ }
+
+ ED_view3d_smooth_view_force_finish(C, v3d, region);
+
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0 || (view_opposite != RV3D_VIEW_USER)) {
+ if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
+ int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ float quat_mul[4];
+ float quat_new[4];
+
+ if (view_opposite == RV3D_VIEW_USER) {
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_persp_ensure(depsgraph, v3d, region);
+ }
+
+ if (ELEM(orbitdir, V3D_VIEW_STEPLEFT, V3D_VIEW_STEPRIGHT)) {
+ if (orbitdir == V3D_VIEW_STEPRIGHT) {
+ angle = -angle;
+ }
+
+ /* z-axis */
+ axis_angle_to_quat_single(quat_mul, 'Z', angle);
+ }
+ else {
+
+ if (orbitdir == V3D_VIEW_STEPDOWN) {
+ angle = -angle;
+ }
+
+ /* horizontal axis */
+ axis_angle_to_quat(quat_mul, rv3d->viewinv[0], angle);
+ }
+
+ mul_qt_qtqt(quat_new, rv3d->viewquat, quat_mul);
+
+ /* avoid precision loss over time */
+ normalize_qt(quat_new);
+
+ if (view_opposite != RV3D_VIEW_USER) {
+ rv3d->view = view_opposite;
+ /* avoid float in-precision, just get a new orientation */
+ ED_view3d_quat_from_axis_view(view_opposite, rv3d->view_axis_roll, quat_new);
+ }
+ else {
+ rv3d->view = RV3D_VIEW_USER;
+ }
+
+ float dyn_ofs[3], *dyn_ofs_pt = NULL;
+
+ if (U.uiflag & USER_ORBIT_SELECTION) {
+ if (view3d_orbit_calc_center(C, dyn_ofs)) {
+ negate_v3(dyn_ofs);
+ dyn_ofs_pt = dyn_ofs;
+ }
+ }
+
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .quat = quat_new,
+ .dyn_ofs = dyn_ofs_pt,
+ });
+
+ return OPERATOR_FINISHED;
+ }
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+void VIEW3D_OT_view_orbit(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "View Orbit";
+ ot->description = "Orbit the view";
+ ot->idname = "VIEW3D_OT_view_orbit";
+
+ /* api callbacks */
+ ot->exec = vieworbit_exec;
+ ot->poll = ED_operator_rv3d_user_region_poll;
+
+ /* flags */
+ ot->flag = 0;
+
+ /* properties */
+ prop = RNA_def_float(ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ ot->prop = RNA_def_enum(
+ ot->srna, "type", prop_view_orbit_items, 0, "Orbit", "Direction of View Orbit");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Pan Operator
+ *
+ * Move (pan) in incremental steps. For interactive pan see #VIEW3D_OT_move.
+ * \{ */
+
+enum {
+ V3D_VIEW_PANLEFT = 1,
+ V3D_VIEW_PANRIGHT,
+ V3D_VIEW_PANDOWN,
+ V3D_VIEW_PANUP,
+};
+
+static const EnumPropertyItem prop_view_pan_items[] = {
+ {V3D_VIEW_PANLEFT, "PANLEFT", 0, "Pan Left", "Pan the view to the left"},
+ {V3D_VIEW_PANRIGHT, "PANRIGHT", 0, "Pan Right", "Pan the view to the right"},
+ {V3D_VIEW_PANUP, "PANUP", 0, "Pan Up", "Pan the view up"},
+ {V3D_VIEW_PANDOWN, "PANDOWN", 0, "Pan Down", "Pan the view down"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int x = 0, y = 0;
+ int pandir = RNA_enum_get(op->ptr, "type");
+
+ if (pandir == V3D_VIEW_PANRIGHT) {
+ x = -32;
+ }
+ else if (pandir == V3D_VIEW_PANLEFT) {
+ x = 32;
+ }
+ else if (pandir == V3D_VIEW_PANUP) {
+ y = -25;
+ }
+ else if (pandir == V3D_VIEW_PANDOWN) {
+ y = 25;
+ }
+
+ ViewOpsData *vod = viewops_data_create(
+ C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT));
+
+ viewmove_apply(vod, vod->prev.event_xy[0] + x, vod->prev.event_xy[1] + y);
+
+ viewops_data_free(C, vod);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_view_pan(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Pan View Direction";
+ ot->description = "Pan the view in a given direction";
+ ot->idname = "VIEW3D_OT_view_pan";
+
+ /* api callbacks */
+ ot->invoke = viewpan_invoke;
+ ot->poll = view3d_location_poll;
+
+ /* flags */
+ ot->flag = 0;
+
+ /* Properties */
+ ot->prop = RNA_def_enum(
+ ot->srna, "type", prop_view_pan_items, 0, "Pan", "Direction of View Pan");
+}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h
new file mode 100644
index 00000000000..792d2a83bc2
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate.h
@@ -0,0 +1,282 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#pragma once
+
+/**
+ * Size of the sphere being dragged for trackball rotation within the view bounds.
+ * also affects speed (smaller is faster).
+ */
+#define V3D_OP_TRACKBALLSIZE (1.1f)
+
+struct ARegion;
+struct bContext;
+struct Depsgraph;
+struct Dial;
+struct Main;
+struct rcti;
+struct RegionView3D;
+struct Scene;
+struct ScrArea;
+struct View3D;
+struct wmEvent;
+struct wmOperator;
+
+enum eV3D_OpPropFlag {
+ V3D_OP_PROP_MOUSE_CO = (1 << 0),
+ V3D_OP_PROP_DELTA = (1 << 1),
+ V3D_OP_PROP_USE_ALL_REGIONS = (1 << 2),
+ V3D_OP_PROP_USE_MOUSE_INIT = (1 << 3),
+};
+
+enum {
+ VIEW_PASS = 0,
+ VIEW_APPLY,
+ VIEW_CONFIRM,
+};
+
+/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
+enum {
+ VIEW_MODAL_CONFIRM = 1, /* used for all view operations */
+ VIEWROT_MODAL_AXIS_SNAP_ENABLE = 2,
+ VIEWROT_MODAL_AXIS_SNAP_DISABLE = 3,
+ VIEWROT_MODAL_SWITCH_ZOOM = 4,
+ VIEWROT_MODAL_SWITCH_MOVE = 5,
+ VIEWROT_MODAL_SWITCH_ROTATE = 6,
+};
+
+enum eViewOpsFlag {
+ /** When enabled, rotate around the selection. */
+ VIEWOPS_FLAG_ORBIT_SELECT = (1 << 0),
+ /** When enabled, use the depth under the cursor for navigation. */
+ VIEWOPS_FLAG_DEPTH_NAVIGATE = (1 << 1),
+ /**
+ * When enabled run #ED_view3d_persp_ensure this may switch out of camera view
+ * when orbiting or switch from orthographic to perspective when auto-perspective is enabled.
+ * Some operations don't require this (view zoom/pan or NDOF where subtle rotation is common
+ * so we don't want it to trigger auto-perspective). */
+ VIEWOPS_FLAG_PERSP_ENSURE = (1 << 2),
+ /** When set, ignore any options that depend on initial cursor location. */
+ VIEWOPS_FLAG_USE_MOUSE_INIT = (1 << 3),
+};
+
+/** Generic View Operator Custom-Data */
+typedef struct ViewOpsData {
+ /** Context pointers (assigned by #viewops_data_create). */
+ struct Main *bmain;
+ struct Scene *scene;
+ struct ScrArea *area;
+ struct ARegion *region;
+ struct View3D *v3d;
+ struct RegionView3D *rv3d;
+ struct Depsgraph *depsgraph;
+
+ /** Needed for continuous zoom. */
+ struct wmTimer *timer;
+
+ /** Viewport state on initialization, don't change afterwards. */
+ struct {
+ float dist;
+ float camzoom;
+ float quat[4];
+ /** #wmEvent.xy. */
+ int event_xy[2];
+ /** Offset to use when #VIEWOPS_FLAG_USE_MOUSE_INIT is not set.
+ * so we can simulate pressing in the middle of the screen. */
+ int event_xy_offset[2];
+ /** #wmEvent.type that triggered the operator. */
+ int event_type;
+ float ofs[3];
+ /** Initial distance to 'ofs'. */
+ float zfac;
+
+ /** Trackball rotation only. */
+ float trackvec[3];
+ /** Dolly only. */
+ float mousevec[3];
+
+ /**
+ * #RegionView3D.persp set after auto-perspective is applied.
+ * If we want the value before running the operator, add a separate member.
+ */
+ char persp;
+
+ /** Used for roll */
+ struct Dial *dial;
+ } init;
+
+ /** Previous state (previous modal event handled). */
+ struct {
+ int event_xy[2];
+ /** For operators that use time-steps (continuous zoom). */
+ double time;
+ } prev;
+
+ /** Current state. */
+ struct {
+ /** Working copy of #RegionView3D.viewquat, needed for rotation calculation
+ * so we can apply snap to the 3D Viewport while keeping the unsnapped rotation
+ * here to use when snap is disabled and for continued calculation. */
+ float viewquat[4];
+ } curr;
+
+ float reverse;
+ bool axis_snap; /* view rotate only */
+
+ /** Use for orbit selection and auto-dist. */
+ float dyn_ofs[3];
+ bool use_dyn_ofs;
+} ViewOpsData;
+
+/* view3d_navigate.c */
+bool view3d_location_poll(struct bContext *C);
+bool view3d_rotation_poll(struct bContext *C);
+bool view3d_zoom_or_dolly_poll(struct bContext *C);
+
+enum eViewOpsFlag viewops_flag_from_prefs(void);
+void calctrackballvec(const struct rcti *rect, const int event_xy[2], float r_dir[3]);
+void viewmove_apply(ViewOpsData *vod, int x, int y);
+void view3d_orbit_apply_dyn_ofs(float r_ofs[3],
+ const float ofs_old[3],
+ const float viewquat_old[4],
+ const float viewquat_new[4],
+ const float dyn_ofs[3]);
+void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4]);
+bool view3d_orbit_calc_center(struct bContext *C, float r_dyn_ofs[3]);
+
+void view3d_operator_properties_common(struct wmOperatorType *ot, const enum eV3D_OpPropFlag flag);
+
+/**
+ * Allocate and fill in context pointers for #ViewOpsData
+ */
+void viewops_data_free(struct bContext *C, ViewOpsData *vod);
+
+/**
+ * Allocate, fill in context pointers and calculate the values for #ViewOpsData
+ */
+ViewOpsData *viewops_data_create(struct bContext *C,
+ const struct wmEvent *event,
+ enum eViewOpsFlag viewops_flag);
+
+void VIEW3D_OT_view_all(struct wmOperatorType *ot);
+void VIEW3D_OT_view_selected(struct wmOperatorType *ot);
+void VIEW3D_OT_view_center_cursor(struct wmOperatorType *ot);
+void VIEW3D_OT_view_center_pick(struct wmOperatorType *ot);
+void VIEW3D_OT_view_axis(struct wmOperatorType *ot);
+void VIEW3D_OT_view_camera(struct wmOperatorType *ot);
+void VIEW3D_OT_view_orbit(struct wmOperatorType *ot);
+void VIEW3D_OT_view_pan(struct wmOperatorType *ot);
+
+/* view3d_navigate_dolly.c */
+void viewdolly_modal_keymap(struct wmKeyConfig *keyconf);
+void VIEW3D_OT_dolly(struct wmOperatorType *ot);
+
+/* view3d_navigate_fly.c */
+void fly_modal_keymap(struct wmKeyConfig *keyconf);
+void view3d_keymap(struct wmKeyConfig *keyconf);
+void VIEW3D_OT_fly(struct wmOperatorType *ot);
+
+/* view3d_navigate_move.c */
+void viewmove_modal_keymap(struct wmKeyConfig *keyconf);
+void VIEW3D_OT_move(struct wmOperatorType *ot);
+
+/* view3d_navigate_ndof.c */
+#ifdef WITH_INPUT_NDOF
+struct wmNDOFMotionData;
+
+/**
+ * Called from both fly mode and walk mode,
+ */
+void view3d_ndof_fly(const struct wmNDOFMotionData *ndof,
+ struct View3D *v3d,
+ struct RegionView3D *rv3d,
+ bool use_precision,
+ short protectflag,
+ bool *r_has_translate,
+ bool *r_has_rotate);
+void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot);
+void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot);
+void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot);
+void VIEW3D_OT_ndof_all(struct wmOperatorType *ot);
+#endif /* WITH_INPUT_NDOF */
+
+/* view3d_navigate_roll.c */
+void VIEW3D_OT_view_roll(struct wmOperatorType *ot);
+
+/* view3d_navigate_rotate.c */
+void viewrotate_modal_keymap(struct wmKeyConfig *keyconf);
+void VIEW3D_OT_rotate(struct wmOperatorType *ot);
+
+/* view3d_navigate_smoothview.c */
+
+/**
+ * Parameters for setting the new 3D Viewport state.
+ *
+ * Each of the struct members may be NULL to signify they aren't to be adjusted.
+ */
+typedef struct V3D_SmoothParams {
+ struct Object *camera_old, *camera;
+ const float *ofs, *quat, *dist, *lens;
+
+ /** Alternate rotation center, when set `ofs` must be NULL. */
+ const float *dyn_ofs;
+} V3D_SmoothParams;
+
+/**
+ * The arguments are the desired situation.
+ */
+void ED_view3d_smooth_view_ex(const struct Depsgraph *depsgraph,
+ struct wmWindowManager *wm,
+ struct wmWindow *win,
+ struct ScrArea *area,
+ struct View3D *v3d,
+ struct ARegion *region,
+ int smooth_viewtx,
+ const V3D_SmoothParams *sview);
+
+void ED_view3d_smooth_view(struct bContext *C,
+ struct View3D *v3d,
+ struct ARegion *region,
+ int smooth_viewtx,
+ const V3D_SmoothParams *sview);
+
+/**
+ * Apply the smooth-view immediately, use when we need to start a new view operation.
+ * (so we don't end up half-applying a view operation when pressing keys quickly).
+ */
+void ED_view3d_smooth_view_force_finish(struct bContext *C,
+ struct View3D *v3d,
+ struct ARegion *region);
+
+void VIEW3D_OT_smoothview(struct wmOperatorType *ot);
+
+/* view3d_navigate_walk.c */
+void walk_modal_keymap(struct wmKeyConfig *keyconf);
+void VIEW3D_OT_walk(struct wmOperatorType *ot);
+
+/* view3d_navigate_zoom.c */
+void viewzoom_modal_keymap(struct wmKeyConfig *keyconf);
+void VIEW3D_OT_zoom(struct wmOperatorType *ot);
+
+/* view3d_navigate_zoom_border.c */
+void VIEW3D_OT_zoom_border(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_dolly.c b/source/blender/editors/space_view3d/view3d_navigate_dolly.c
new file mode 100644
index 00000000000..02783c2a244
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_dolly.c
@@ -0,0 +1,337 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_report.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+
+#include "RNA_access.h"
+
+#include "ED_screen.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name View Dolly Operator
+ *
+ * Like zoom but translates the view offset along the view direction
+ * which avoids #RegionView3D.dist approaching zero.
+ * \{ */
+
+/* This is an exact copy of #viewzoom_modal_keymap. */
+void viewdolly_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const EnumPropertyItem modal_items[] = {
+ {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+
+ {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
+ {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
+
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Dolly Modal");
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items) {
+ return;
+ }
+
+ keymap = WM_modalkeymap_ensure(keyconf, "View3D Dolly Modal", modal_items);
+
+ /* disabled mode switching for now, can re-implement better, later on */
+#if 0
+ WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
+#endif
+
+ /* assign map to operators */
+ WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly");
+}
+
+static bool viewdolly_offset_lock_check(bContext *C, wmOperator *op)
+{
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ if (ED_view3d_offset_lock_check(v3d, rv3d)) {
+ BKE_report(op->reports, RPT_WARNING, "Cannot dolly when the view offset is locked");
+ return true;
+ }
+ return false;
+}
+
+static void view_dolly_to_vector_3d(ARegion *region,
+ const float orig_ofs[3],
+ const float dvec[3],
+ float dfac)
+{
+ RegionView3D *rv3d = region->regiondata;
+ madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac));
+}
+
+static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert)
+{
+ float zfac = 1.0;
+
+ {
+ float len1, len2;
+
+ if (U.uiflag & USER_ZOOM_HORIZ) {
+ len1 = (vod->region->winrct.xmax - xy[0]) + 5;
+ len2 = (vod->region->winrct.xmax - vod->init.event_xy[0]) + 5;
+ }
+ else {
+ len1 = (vod->region->winrct.ymax - xy[1]) + 5;
+ len2 = (vod->region->winrct.ymax - vod->init.event_xy[1]) + 5;
+ }
+ if (zoom_invert) {
+ SWAP(float, len1, len2);
+ }
+
+ zfac = 1.0f + ((len1 - len2) * 0.01f * vod->rv3d->dist);
+ }
+
+ if (zfac != 1.0f) {
+ view_dolly_to_vector_3d(vod->region, vod->init.ofs, vod->init.mousevec, zfac);
+ }
+
+ if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(vod->area, vod->region);
+ }
+
+ ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
+
+ ED_region_tag_redraw(vod->region);
+}
+
+static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod = op->customdata;
+ short event_code = VIEW_PASS;
+ bool use_autokey = false;
+ int ret = OPERATOR_RUNNING_MODAL;
+
+ /* execute the events */
+ if (event->type == MOUSEMOVE) {
+ event_code = VIEW_APPLY;
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case VIEW_MODAL_CONFIRM:
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_MOVE:
+ WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_ROTATE:
+ WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ }
+ }
+ else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
+ event_code = VIEW_CONFIRM;
+ }
+
+ if (event_code == VIEW_APPLY) {
+ viewdolly_apply(vod, event->xy, (U.uiflag & USER_ZOOM_INVERT) != 0);
+ if (ED_screen_animation_playing(CTX_wm_manager(C))) {
+ use_autokey = true;
+ }
+ }
+ else if (event_code == VIEW_CONFIRM) {
+ use_autokey = true;
+ ret = OPERATOR_FINISHED;
+ }
+
+ if (use_autokey) {
+ ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
+ }
+
+ if (ret & OPERATOR_FINISHED) {
+ viewops_data_free(C, vod);
+ op->customdata = NULL;
+ }
+
+ return ret;
+}
+
+static int viewdolly_exec(bContext *C, wmOperator *op)
+{
+ View3D *v3d;
+ RegionView3D *rv3d;
+ ScrArea *area;
+ ARegion *region;
+ float mousevec[3];
+
+ const int delta = RNA_int_get(op->ptr, "delta");
+
+ if (op->customdata) {
+ ViewOpsData *vod = op->customdata;
+
+ area = vod->area;
+ region = vod->region;
+ copy_v3_v3(mousevec, vod->init.mousevec);
+ }
+ else {
+ area = CTX_wm_area(C);
+ region = CTX_wm_region(C);
+ negate_v3_v3(mousevec, ((RegionView3D *)region->regiondata)->viewinv[2]);
+ normalize_v3(mousevec);
+ }
+
+ v3d = area->spacedata.first;
+ rv3d = region->regiondata;
+
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+
+ /* overwrite the mouse vector with the view direction (zoom into the center) */
+ if ((use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) == 0) {
+ normalize_v3_v3(mousevec, rv3d->viewinv[2]);
+ negate_v3(mousevec);
+ }
+
+ view_dolly_to_vector_3d(region, rv3d->ofs, mousevec, delta < 0 ? 1.8f : 0.2f);
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(area, region);
+ }
+
+ ED_view3d_camera_lock_sync(CTX_data_ensure_evaluated_depsgraph(C), v3d, rv3d);
+
+ ED_region_tag_redraw(region);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+
+ return OPERATOR_FINISHED;
+}
+
+/* copied from viewzoom_invoke(), changes here may apply there */
+static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod;
+
+ if (viewdolly_offset_lock_check(C, op)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+
+ vod = op->customdata = viewops_data_create(
+ C,
+ event,
+ (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
+ (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
+
+ ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
+
+ /* needs to run before 'viewops_data_create' so the backup 'rv3d->ofs' is correct */
+ /* switch from camera view when: */
+ if (vod->rv3d->persp != RV3D_PERSP) {
+ if (vod->rv3d->persp == RV3D_CAMOB) {
+ /* ignore rv3d->lpersp because dolly only makes sense in perspective mode */
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_persp_switch_from_camera(depsgraph, vod->v3d, vod->rv3d, RV3D_PERSP);
+ }
+ else {
+ vod->rv3d->persp = RV3D_PERSP;
+ }
+ ED_region_tag_redraw(vod->region);
+ }
+
+ /* if one or the other zoom position aren't set, set from event */
+ if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
+ RNA_int_set(op->ptr, "mx", event->xy[0]);
+ RNA_int_set(op->ptr, "my", event->xy[1]);
+ }
+
+ if (RNA_struct_property_is_set(op->ptr, "delta")) {
+ viewdolly_exec(C, op);
+ }
+ else {
+ /* overwrite the mouse vector with the view direction (zoom into the center) */
+ if ((use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) == 0) {
+ negate_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]);
+ normalize_v3(vod->init.mousevec);
+ }
+
+ if (event->type == MOUSEZOOM) {
+ /* Bypass Zoom invert flag for track pads (pass false always) */
+
+ if (U.uiflag & USER_ZOOM_HORIZ) {
+ vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
+ }
+ else {
+ /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
+ vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] -
+ event->prev_xy[0];
+ }
+ viewdolly_apply(vod, event->prev_xy, (U.uiflag & USER_ZOOM_INVERT) == 0);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ return OPERATOR_FINISHED;
+ }
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_FINISHED;
+}
+
+static void viewdolly_cancel(bContext *C, wmOperator *op)
+{
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+}
+
+void VIEW3D_OT_dolly(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Dolly View";
+ ot->description = "Dolly in/out in the view";
+ ot->idname = "VIEW3D_OT_dolly";
+
+ /* api callbacks */
+ ot->invoke = viewdolly_invoke;
+ ot->exec = viewdolly_exec;
+ ot->modal = viewdolly_modal;
+ ot->poll = view3d_rotation_poll;
+ ot->cancel = viewdolly_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
+
+ /* properties */
+ view3d_operator_properties_common(
+ ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT);
+}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_navigate_fly.c b/source/blender/editors/space_view3d/view3d_navigate_fly.c
index 2e9cb419e2e..940616eec1f 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_fly.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_fly.c
@@ -58,6 +58,7 @@
#include "DEG_depsgraph.h"
#include "view3d_intern.h" /* own include */
+#include "view3d_navigate.h"
/* -------------------------------------------------------------------- */
/** \name Modal Key-map
diff --git a/source/blender/editors/space_view3d/view3d_navigate_move.c b/source/blender/editors/space_view3d/view3d_navigate_move.c
new file mode 100644
index 00000000000..90acf20d24f
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_move.c
@@ -0,0 +1,188 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "BKE_context.h"
+
+#include "WM_api.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "ED_screen.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name View Move (Pan) Operator
+ * \{ */
+
+/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
+
+void viewmove_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const EnumPropertyItem modal_items[] = {
+ {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+
+ {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
+ {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
+
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Move Modal");
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items) {
+ return;
+ }
+
+ keymap = WM_modalkeymap_ensure(keyconf, "View3D Move Modal", modal_items);
+
+ /* items for modal map */
+ WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
+
+ /* disabled mode switching for now, can re-implement better, later on */
+#if 0
+ WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
+ WM_modalkeymap_add_item(
+ keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
+#endif
+
+ /* assign map to operators */
+ WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
+}
+
+static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+
+ ViewOpsData *vod = op->customdata;
+ short event_code = VIEW_PASS;
+ bool use_autokey = false;
+ int ret = OPERATOR_RUNNING_MODAL;
+
+ /* execute the events */
+ if (event->type == MOUSEMOVE) {
+ event_code = VIEW_APPLY;
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case VIEW_MODAL_CONFIRM:
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_ZOOM:
+ WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_ROTATE:
+ WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ }
+ }
+ else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
+ event_code = VIEW_CONFIRM;
+ }
+
+ if (event_code == VIEW_APPLY) {
+ viewmove_apply(vod, event->xy[0], event->xy[1]);
+ if (ED_screen_animation_playing(CTX_wm_manager(C))) {
+ use_autokey = true;
+ }
+ }
+ else if (event_code == VIEW_CONFIRM) {
+ use_autokey = true;
+ ret = OPERATOR_FINISHED;
+ }
+
+ if (use_autokey) {
+ ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
+ }
+
+ if (ret & OPERATOR_FINISHED) {
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ }
+
+ return ret;
+}
+
+static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod;
+
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+
+ vod = op->customdata = viewops_data_create(
+ C,
+ event,
+ (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
+ (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
+ vod = op->customdata;
+
+ ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
+
+ if (event->type == MOUSEPAN) {
+ /* invert it, trackpad scroll follows same principle as 2d windows this way */
+ viewmove_apply(
+ vod, 2 * event->xy[0] - event->prev_xy[0], 2 * event->xy[1] - event->prev_xy[1]);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+
+ return OPERATOR_FINISHED;
+ }
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void viewmove_cancel(bContext *C, wmOperator *op)
+{
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+}
+
+void VIEW3D_OT_move(wmOperatorType *ot)
+{
+
+ /* identifiers */
+ ot->name = "Pan View";
+ ot->description = "Move the view";
+ ot->idname = "VIEW3D_OT_move";
+
+ /* api callbacks */
+ ot->invoke = viewmove_invoke;
+ ot->modal = viewmove_modal;
+ ot->poll = view3d_location_poll;
+ ot->cancel = viewmove_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
+
+ /* properties */
+ view3d_operator_properties_common(ot, V3D_OP_PROP_USE_MOUSE_INIT);
+}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_navigate_ndof.c b/source/blender/editors/space_view3d/view3d_navigate_ndof.c
new file mode 100644
index 00000000000..285d5c02db2
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_ndof.c
@@ -0,0 +1,661 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+
+#include "ED_screen.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name NDOF Utility Functions
+ * \{ */
+
+#ifdef WITH_INPUT_NDOF
+
+enum {
+ HAS_TRANSLATE = (1 << 0),
+ HAS_ROTATE = (1 << 0),
+};
+
+static bool ndof_has_translate(const wmNDOFMotionData *ndof,
+ const View3D *v3d,
+ const RegionView3D *rv3d)
+{
+ return !is_zero_v3(ndof->tvec) && (!ED_view3d_offset_lock_check(v3d, rv3d));
+}
+
+static bool ndof_has_rotate(const wmNDOFMotionData *ndof, const RegionView3D *rv3d)
+{
+ return !is_zero_v3(ndof->rvec) && ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0);
+}
+
+/**
+ * \param depth_pt: A point to calculate the depth (in perspective mode)
+ */
+static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth_pt[3])
+{
+ float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND;
+
+ if (rv3d->is_persp) {
+ speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL);
+ }
+
+ return speed;
+}
+
+static float view3d_ndof_pan_speed_calc_from_dist(RegionView3D *rv3d, const float dist)
+{
+ float viewinv[4];
+ float tvec[3];
+
+ BLI_assert(dist >= 0.0f);
+
+ copy_v3_fl3(tvec, 0.0f, 0.0f, dist);
+ /* rv3d->viewinv isn't always valid */
+# if 0
+ mul_mat3_m4_v3(rv3d->viewinv, tvec);
+# else
+ invert_qt_qt_normalized(viewinv, rv3d->viewquat);
+ mul_qt_v3(viewinv, tvec);
+# endif
+
+ return view3d_ndof_pan_speed_calc_ex(rv3d, tvec);
+}
+
+static float view3d_ndof_pan_speed_calc(RegionView3D *rv3d)
+{
+ float tvec[3];
+ negate_v3_v3(tvec, rv3d->ofs);
+
+ return view3d_ndof_pan_speed_calc_ex(rv3d, tvec);
+}
+
+/**
+ * Zoom and pan in the same function since sometimes zoom is interpreted as dolly (pan forward).
+ *
+ * \param has_zoom: zoom, otherwise dolly,
+ * often `!rv3d->is_persp` since it doesn't make sense to dolly in ortho.
+ */
+static void view3d_ndof_pan_zoom(const struct wmNDOFMotionData *ndof,
+ ScrArea *area,
+ ARegion *region,
+ const bool has_translate,
+ const bool has_zoom)
+{
+ RegionView3D *rv3d = region->regiondata;
+ float view_inv[4];
+ float pan_vec[3];
+
+ if (has_translate == false && has_zoom == false) {
+ return;
+ }
+
+ WM_event_ndof_pan_get(ndof, pan_vec, false);
+
+ if (has_zoom) {
+ /* zoom with Z */
+
+ /* Zoom!
+ * velocity should be proportional to the linear velocity attained by rotational motion
+ * of same strength [got that?] proportional to `arclength = radius * angle`.
+ */
+
+ pan_vec[2] = 0.0f;
+
+ /* "zoom in" or "translate"? depends on zoom mode in user settings? */
+ if (ndof->tvec[2]) {
+ float zoom_distance = rv3d->dist * ndof->dt * ndof->tvec[2];
+
+ if (U.ndof_flag & NDOF_ZOOM_INVERT) {
+ zoom_distance = -zoom_distance;
+ }
+
+ rv3d->dist += zoom_distance;
+ }
+ }
+ else {
+ /* dolly with Z */
+
+ /* all callers must check */
+ if (has_translate) {
+ BLI_assert(ED_view3d_offset_lock_check((View3D *)area->spacedata.first, rv3d) == false);
+ }
+ }
+
+ if (has_translate) {
+ const float speed = view3d_ndof_pan_speed_calc(rv3d);
+
+ mul_v3_fl(pan_vec, speed * ndof->dt);
+
+ /* transform motion from view to world coordinates */
+ invert_qt_qt_normalized(view_inv, rv3d->viewquat);
+ mul_qt_v3(view_inv, pan_vec);
+
+ /* move center of view opposite of hand motion (this is camera mode, not object mode) */
+ sub_v3_v3(rv3d->ofs, pan_vec);
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(area, region);
+ }
+ }
+}
+
+static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof,
+ ScrArea *area,
+ ARegion *region,
+ ViewOpsData *vod,
+ const bool apply_dyn_ofs)
+{
+ View3D *v3d = area->spacedata.first;
+ RegionView3D *rv3d = region->regiondata;
+
+ float view_inv[4];
+
+ BLI_assert((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0);
+
+ ED_view3d_persp_ensure(vod->depsgraph, v3d, region);
+
+ rv3d->view = RV3D_VIEW_USER;
+
+ invert_qt_qt_normalized(view_inv, rv3d->viewquat);
+
+ if (U.ndof_flag & NDOF_TURNTABLE) {
+ float rot[3];
+
+ /* Turntable view code adapted for 3D mouse use. */
+ float angle, quat[4];
+ float xvec[3] = {1, 0, 0};
+
+ /* only use XY, ignore Z */
+ WM_event_ndof_rotate_get(ndof, rot);
+
+ /* Determine the direction of the x vector (for rotating up and down) */
+ mul_qt_v3(view_inv, xvec);
+
+ /* Perform the up/down rotation */
+ angle = ndof->dt * rot[0];
+ axis_angle_to_quat(quat, xvec, angle);
+ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
+
+ /* Perform the orbital rotation */
+ angle = ndof->dt * rot[1];
+
+ /* Update the onscreen axis-angle indicator. */
+ rv3d->rot_angle = angle;
+ rv3d->rot_axis[0] = 0;
+ rv3d->rot_axis[1] = 0;
+ rv3d->rot_axis[2] = 1;
+
+ axis_angle_to_quat_single(quat, 'Z', angle);
+ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
+ }
+ else {
+ float quat[4];
+ float axis[3];
+ float angle = WM_event_ndof_to_axis_angle(ndof, axis);
+
+ /* transform rotation axis from view to world coordinates */
+ mul_qt_v3(view_inv, axis);
+
+ /* Update the onscreen axis-angle indicator. */
+ rv3d->rot_angle = angle;
+ copy_v3_v3(rv3d->rot_axis, axis);
+
+ axis_angle_to_quat(quat, axis, angle);
+
+ /* apply rotation */
+ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
+ }
+
+ if (apply_dyn_ofs) {
+ viewrotate_apply_dyn_ofs(vod, rv3d->viewquat);
+ }
+}
+
+void view3d_ndof_fly(const wmNDOFMotionData *ndof,
+ View3D *v3d,
+ RegionView3D *rv3d,
+ const bool use_precision,
+ const short protectflag,
+ bool *r_has_translate,
+ bool *r_has_rotate)
+{
+ bool has_translate = ndof_has_translate(ndof, v3d, rv3d);
+ bool has_rotate = ndof_has_rotate(ndof, rv3d);
+
+ float view_inv[4];
+ invert_qt_qt_normalized(view_inv, rv3d->viewquat);
+
+ rv3d->rot_angle = 0.0f; /* Disable onscreen rotation indicator. */
+
+ if (has_translate) {
+ /* ignore real 'dist' since fly has its own speed settings,
+ * also its overwritten at this point. */
+ float speed = view3d_ndof_pan_speed_calc_from_dist(rv3d, 1.0f);
+ float trans[3], trans_orig_y;
+
+ if (use_precision) {
+ speed *= 0.2f;
+ }
+
+ WM_event_ndof_pan_get(ndof, trans, false);
+ mul_v3_fl(trans, speed * ndof->dt);
+ trans_orig_y = trans[1];
+
+ if (U.ndof_flag & NDOF_FLY_HELICOPTER) {
+ trans[1] = 0.0f;
+ }
+
+ /* transform motion from view to world coordinates */
+ mul_qt_v3(view_inv, trans);
+
+ if (U.ndof_flag & NDOF_FLY_HELICOPTER) {
+ /* replace world z component with device y (yes it makes sense) */
+ trans[2] = trans_orig_y;
+ }
+
+ if (rv3d->persp == RV3D_CAMOB) {
+ /* respect camera position locks */
+ if (protectflag & OB_LOCK_LOCX) {
+ trans[0] = 0.0f;
+ }
+ if (protectflag & OB_LOCK_LOCY) {
+ trans[1] = 0.0f;
+ }
+ if (protectflag & OB_LOCK_LOCZ) {
+ trans[2] = 0.0f;
+ }
+ }
+
+ if (!is_zero_v3(trans)) {
+ /* move center of view opposite of hand motion
+ * (this is camera mode, not object mode) */
+ sub_v3_v3(rv3d->ofs, trans);
+ has_translate = true;
+ }
+ else {
+ has_translate = false;
+ }
+ }
+
+ if (has_rotate) {
+ const float turn_sensitivity = 1.0f;
+
+ float rotation[4];
+ float axis[3];
+ float angle = turn_sensitivity * WM_event_ndof_to_axis_angle(ndof, axis);
+
+ if (fabsf(angle) > 0.0001f) {
+ has_rotate = true;
+
+ if (use_precision) {
+ angle *= 0.2f;
+ }
+
+ /* transform rotation axis from view to world coordinates */
+ mul_qt_v3(view_inv, axis);
+
+ /* apply rotation to view */
+ axis_angle_to_quat(rotation, axis, angle);
+ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
+
+ if (U.ndof_flag & NDOF_LOCK_HORIZON) {
+ /* force an upright viewpoint
+ * TODO: make this less... sudden */
+ float view_horizon[3] = {1.0f, 0.0f, 0.0f}; /* view +x */
+ float view_direction[3] = {0.0f, 0.0f, -1.0f}; /* view -z (into screen) */
+
+ /* find new inverse since viewquat has changed */
+ invert_qt_qt_normalized(view_inv, rv3d->viewquat);
+ /* could apply reverse rotation to existing view_inv to save a few cycles */
+
+ /* transform view vectors to world coordinates */
+ mul_qt_v3(view_inv, view_horizon);
+ mul_qt_v3(view_inv, view_direction);
+
+ /* find difference between view & world horizons
+ * true horizon lives in world xy plane, so look only at difference in z */
+ angle = -asinf(view_horizon[2]);
+
+ /* rotate view so view horizon = world horizon */
+ axis_angle_to_quat(rotation, view_direction, angle);
+ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
+ }
+
+ rv3d->view = RV3D_VIEW_USER;
+ }
+ else {
+ has_rotate = false;
+ }
+ }
+
+ *r_has_translate = has_translate;
+ *r_has_rotate = has_rotate;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name NDOF Orbit/Translate Operator
+ * \{ */
+
+static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (event->type != NDOF_MOTION) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewOpsData *vod;
+ View3D *v3d;
+ RegionView3D *rv3d;
+ char xform_flag = 0;
+
+ const wmNDOFMotionData *ndof = event->customdata;
+
+ vod = op->customdata = viewops_data_create(
+ C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_DEPTH_NAVIGATE));
+
+ ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
+
+ v3d = vod->v3d;
+ rv3d = vod->rv3d;
+
+ /* off by default, until changed later this function */
+ rv3d->rot_angle = 0.0f;
+
+ ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false);
+
+ if (ndof->progress != P_FINISHING) {
+ const bool has_rotation = ndof_has_rotate(ndof, rv3d);
+ /* if we can't rotate, fallback to translate (locked axis views) */
+ const bool has_translate = ndof_has_translate(ndof, v3d, rv3d) &&
+ (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION);
+ const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
+
+ if (has_translate || has_zoom) {
+ view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, has_zoom);
+ xform_flag |= HAS_TRANSLATE;
+ }
+
+ if (has_rotation) {
+ view3d_ndof_orbit(ndof, vod->area, vod->region, vod, true);
+ xform_flag |= HAS_ROTATE;
+ }
+ }
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ if (xform_flag) {
+ ED_view3d_camera_lock_autokey(
+ v3d, rv3d, C, xform_flag & HAS_ROTATE, xform_flag & HAS_TRANSLATE);
+ }
+
+ ED_region_tag_redraw(vod->region);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "NDOF Orbit View";
+ ot->description = "Orbit the view using the 3D mouse";
+ ot->idname = "VIEW3D_OT_ndof_orbit";
+
+ /* api callbacks */
+ ot->invoke = ndof_orbit_invoke;
+ ot->poll = ED_operator_view3d_active;
+
+ /* flags */
+ ot->flag = 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name NDOF Orbit/Zoom Operator
+ * \{ */
+
+static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (event->type != NDOF_MOTION) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewOpsData *vod;
+ View3D *v3d;
+ RegionView3D *rv3d;
+ char xform_flag = 0;
+
+ const wmNDOFMotionData *ndof = event->customdata;
+
+ vod = op->customdata = viewops_data_create(
+ C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_DEPTH_NAVIGATE));
+
+ ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
+
+ v3d = vod->v3d;
+ rv3d = vod->rv3d;
+
+ /* off by default, until changed later this function */
+ rv3d->rot_angle = 0.0f;
+
+ ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false);
+
+ if (ndof->progress == P_FINISHING) {
+ /* pass */
+ }
+ else if ((rv3d->persp == RV3D_ORTHO) && RV3D_VIEW_IS_AXIS(rv3d->view)) {
+ /* if we can't rotate, fallback to translate (locked axis views) */
+ const bool has_translate = ndof_has_translate(ndof, v3d, rv3d);
+ const bool has_zoom = (ndof->tvec[2] != 0.0f) && ED_view3d_offset_lock_check(v3d, rv3d);
+
+ if (has_translate || has_zoom) {
+ view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, true);
+ xform_flag |= HAS_TRANSLATE;
+ }
+ }
+ else {
+ /* NOTE: based on feedback from T67579, users want to have pan and orbit enabled at once.
+ * It's arguable that orbit shouldn't pan (since we have a pan only operator),
+ * so if there are users who like to separate orbit/pan operations - it can be a preference. */
+ const bool is_orbit_around_pivot = (U.ndof_flag & NDOF_MODE_ORBIT) ||
+ ED_view3d_offset_lock_check(v3d, rv3d);
+ const bool has_rotation = ndof_has_rotate(ndof, rv3d);
+ bool has_translate, has_zoom;
+
+ if (is_orbit_around_pivot) {
+ /* Orbit preference or forced lock (Z zooms). */
+ has_translate = !is_zero_v2(ndof->tvec) && ndof_has_translate(ndof, v3d, rv3d);
+ has_zoom = (ndof->tvec[2] != 0.0f);
+ }
+ else {
+ /* Free preference (Z translates). */
+ has_translate = ndof_has_translate(ndof, v3d, rv3d);
+ has_zoom = false;
+ }
+
+ /* Rotation first because dynamic offset resets offset otherwise (and disables panning). */
+ if (has_rotation) {
+ const float dist_backup = rv3d->dist;
+ if (!is_orbit_around_pivot) {
+ ED_view3d_distance_set(rv3d, 0.0f);
+ }
+ view3d_ndof_orbit(ndof, vod->area, vod->region, vod, is_orbit_around_pivot);
+ xform_flag |= HAS_ROTATE;
+ if (!is_orbit_around_pivot) {
+ ED_view3d_distance_set(rv3d, dist_backup);
+ }
+ }
+
+ if (has_translate || has_zoom) {
+ view3d_ndof_pan_zoom(ndof, vod->area, vod->region, has_translate, has_zoom);
+ xform_flag |= HAS_TRANSLATE;
+ }
+ }
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ if (xform_flag) {
+ ED_view3d_camera_lock_autokey(
+ v3d, rv3d, C, xform_flag & HAS_ROTATE, xform_flag & HAS_TRANSLATE);
+ }
+
+ ED_region_tag_redraw(vod->region);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "NDOF Orbit View with Zoom";
+ ot->description = "Orbit and zoom the view using the 3D mouse";
+ ot->idname = "VIEW3D_OT_ndof_orbit_zoom";
+
+ /* api callbacks */
+ ot->invoke = ndof_orbit_zoom_invoke;
+ ot->poll = ED_operator_view3d_active;
+
+ /* flags */
+ ot->flag = 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name NDOF Pan/Zoom Operator
+ * \{ */
+
+static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ if (event->type != NDOF_MOTION) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ const wmNDOFMotionData *ndof = event->customdata;
+ char xform_flag = 0;
+
+ const bool has_translate = ndof_has_translate(ndof, v3d, rv3d);
+ const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
+
+ /* we're panning here! so erase any leftover rotation from other operators */
+ rv3d->rot_angle = 0.0f;
+
+ if (!(has_translate || has_zoom)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, false);
+
+ if (ndof->progress != P_FINISHING) {
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+
+ if (has_translate || has_zoom) {
+ view3d_ndof_pan_zoom(ndof, area, region, has_translate, has_zoom);
+ xform_flag |= HAS_TRANSLATE;
+ }
+ }
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ if (xform_flag) {
+ ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, xform_flag & HAS_TRANSLATE);
+ }
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "NDOF Pan View";
+ ot->description = "Pan the view with the 3D mouse";
+ ot->idname = "VIEW3D_OT_ndof_pan";
+
+ /* api callbacks */
+ ot->invoke = ndof_pan_invoke;
+ ot->poll = ED_operator_view3d_active;
+
+ /* flags */
+ ot->flag = 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name NDOF Transform All Operator
+ * \{ */
+
+/**
+ * wraps #ndof_orbit_zoom but never restrict to orbit.
+ */
+static int ndof_all_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ /* weak!, but it works */
+ const int ndof_flag = U.ndof_flag;
+ int ret;
+
+ U.ndof_flag &= ~NDOF_MODE_ORBIT;
+
+ ret = ndof_orbit_zoom_invoke(C, op, event);
+
+ U.ndof_flag = ndof_flag;
+
+ return ret;
+}
+
+void VIEW3D_OT_ndof_all(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "NDOF Transform View";
+ ot->description = "Pan and rotate the view with the 3D mouse";
+ ot->idname = "VIEW3D_OT_ndof_all";
+
+ /* api callbacks */
+ ot->invoke = ndof_all_invoke;
+ ot->poll = ED_operator_view3d_active;
+
+ /* flags */
+ ot->flag = 0;
+}
+
+#endif /* WITH_INPUT_NDOF */
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c
new file mode 100644
index 00000000000..c9bfdc4412a
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c
@@ -0,0 +1,292 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "BLI_blenlib.h"
+#include "BLI_dial_2d.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+
+#include "WM_api.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "ED_screen.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name View Roll Operator
+ * \{ */
+
+static void view_roll_angle(
+ ARegion *region, float quat[4], const float orig_quat[4], const float dvec[3], float angle)
+{
+ RegionView3D *rv3d = region->regiondata;
+ float quat_mul[4];
+
+ /* camera axis */
+ axis_angle_normalized_to_quat(quat_mul, dvec, angle);
+
+ mul_qt_qtqt(quat, orig_quat, quat_mul);
+
+ /* avoid precision loss over time */
+ normalize_qt(quat);
+
+ rv3d->view = RV3D_VIEW_USER;
+}
+
+static void viewroll_apply(ViewOpsData *vod, int x, int y)
+{
+ float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y});
+
+ if (angle != 0.0f) {
+ view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle);
+ }
+
+ if (vod->use_dyn_ofs) {
+ view3d_orbit_apply_dyn_ofs(
+ vod->rv3d->ofs, vod->init.ofs, vod->init.quat, vod->rv3d->viewquat, vod->dyn_ofs);
+ }
+
+ if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(vod->area, vod->region);
+ }
+
+ ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
+
+ ED_region_tag_redraw(vod->region);
+}
+
+static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod = op->customdata;
+ short event_code = VIEW_PASS;
+ bool use_autokey = false;
+ int ret = OPERATOR_RUNNING_MODAL;
+
+ /* execute the events */
+ if (event->type == MOUSEMOVE) {
+ event_code = VIEW_APPLY;
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case VIEW_MODAL_CONFIRM:
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_MOVE:
+ WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_ROTATE:
+ WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ }
+ }
+ else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
+ /* Note this does not remove auto-keys on locked cameras. */
+ copy_qt_qt(vod->rv3d->viewquat, vod->init.quat);
+ ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ return OPERATOR_CANCELLED;
+ }
+ else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
+ event_code = VIEW_CONFIRM;
+ }
+
+ if (event_code == VIEW_APPLY) {
+ viewroll_apply(vod, event->xy[0], event->xy[1]);
+ if (ED_screen_animation_playing(CTX_wm_manager(C))) {
+ use_autokey = true;
+ }
+ }
+ else if (event_code == VIEW_CONFIRM) {
+ use_autokey = true;
+ ret = OPERATOR_FINISHED;
+ }
+
+ if (use_autokey) {
+ ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, false);
+ }
+
+ if (ret & OPERATOR_FINISHED) {
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ }
+
+ return ret;
+}
+
+enum {
+ V3D_VIEW_STEPLEFT = 1,
+ V3D_VIEW_STEPRIGHT,
+};
+
+static const EnumPropertyItem prop_view_roll_items[] = {
+ {0, "ANGLE", 0, "Roll Angle", "Roll the view using an angle value"},
+ {V3D_VIEW_STEPLEFT, "LEFT", 0, "Roll Left", "Roll the view around to the left"},
+ {V3D_VIEW_STEPRIGHT, "RIGHT", 0, "Roll Right", "Roll the view around to the right"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int viewroll_exec(bContext *C, wmOperator *op)
+{
+ View3D *v3d;
+ RegionView3D *rv3d;
+ ARegion *region;
+
+ if (op->customdata) {
+ ViewOpsData *vod = op->customdata;
+ region = vod->region;
+ v3d = vod->v3d;
+ }
+ else {
+ ED_view3d_context_user_region(C, &v3d, &region);
+ }
+
+ rv3d = region->regiondata;
+ if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
+
+ ED_view3d_smooth_view_force_finish(C, v3d, region);
+
+ int type = RNA_enum_get(op->ptr, "type");
+ float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle);
+ float mousevec[3];
+ float quat_new[4];
+
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ if (type == V3D_VIEW_STEPLEFT) {
+ angle = -angle;
+ }
+
+ normalize_v3_v3(mousevec, rv3d->viewinv[2]);
+ negate_v3(mousevec);
+ view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle);
+
+ const float *dyn_ofs_pt = NULL;
+ float dyn_ofs[3];
+ if (U.uiflag & USER_ORBIT_SELECTION) {
+ if (view3d_orbit_calc_center(C, dyn_ofs)) {
+ negate_v3(dyn_ofs);
+ dyn_ofs_pt = dyn_ofs;
+ }
+ }
+
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .quat = quat_new,
+ .dyn_ofs = dyn_ofs_pt,
+ });
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ return OPERATOR_FINISHED;
+ }
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ return OPERATOR_CANCELLED;
+}
+
+static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod;
+
+ bool use_angle = RNA_enum_get(op->ptr, "type") != 0;
+
+ if (use_angle || RNA_struct_property_is_set(op->ptr, "angle")) {
+ viewroll_exec(C, op);
+ }
+ else {
+ /* makes op->customdata */
+ vod = op->customdata = viewops_data_create(C, event, viewops_flag_from_prefs());
+ vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct),
+ BLI_rcti_cent_y(&vod->region->winrct)},
+ FLT_EPSILON);
+
+ ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
+
+ /* overwrite the mouse vector with the view direction */
+ normalize_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]);
+ negate_v3(vod->init.mousevec);
+
+ if (event->type == MOUSEROTATE) {
+ vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
+ viewroll_apply(vod, event->prev_xy[0], event->prev_xy[1]);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ return OPERATOR_FINISHED;
+ }
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_FINISHED;
+}
+
+static void viewroll_cancel(bContext *C, wmOperator *op)
+{
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+}
+
+void VIEW3D_OT_view_roll(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "View Roll";
+ ot->description = "Roll the view";
+ ot->idname = "VIEW3D_OT_view_roll";
+
+ /* api callbacks */
+ ot->invoke = viewroll_invoke;
+ ot->exec = viewroll_exec;
+ ot->modal = viewroll_modal;
+ ot->poll = ED_operator_rv3d_user_region_poll;
+ ot->cancel = viewroll_cancel;
+
+ /* flags */
+ ot->flag = 0;
+
+ /* properties */
+ ot->prop = prop = RNA_def_float(
+ ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_enum(ot->srna,
+ "type",
+ prop_view_roll_items,
+ 0,
+ "Roll Angle Source",
+ "How roll angle is calculated");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c
new file mode 100644
index 00000000000..c3730b3b3b1
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c
@@ -0,0 +1,452 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+
+#include "WM_api.h"
+
+#include "RNA_access.h"
+
+#include "ED_screen.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name View Rotate Operator
+ * \{ */
+
+void viewrotate_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const EnumPropertyItem modal_items[] = {
+ {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+
+ {VIEWROT_MODAL_AXIS_SNAP_ENABLE, "AXIS_SNAP_ENABLE", 0, "Axis Snap", ""},
+ {VIEWROT_MODAL_AXIS_SNAP_DISABLE, "AXIS_SNAP_DISABLE", 0, "Axis Snap (Off)", ""},
+
+ {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
+ {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
+
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Rotate Modal");
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items) {
+ return;
+ }
+
+ keymap = WM_modalkeymap_ensure(keyconf, "View3D Rotate Modal", modal_items);
+
+ /* disabled mode switching for now, can re-implement better, later on */
+#if 0
+ WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
+#endif
+
+ /* assign map to operators */
+ WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate");
+}
+
+static void viewrotate_apply_snap(ViewOpsData *vod)
+{
+ const float axis_limit = DEG2RADF(45 / 3);
+
+ RegionView3D *rv3d = vod->rv3d;
+
+ float viewquat_inv[4];
+ float zaxis[3] = {0, 0, 1};
+ float zaxis_best[3];
+ int x, y, z;
+ bool found = false;
+
+ invert_qt_qt_normalized(viewquat_inv, vod->curr.viewquat);
+
+ mul_qt_v3(viewquat_inv, zaxis);
+ normalize_v3(zaxis);
+
+ for (x = -1; x < 2; x++) {
+ for (y = -1; y < 2; y++) {
+ for (z = -1; z < 2; z++) {
+ if (x || y || z) {
+ float zaxis_test[3] = {x, y, z};
+
+ normalize_v3(zaxis_test);
+
+ if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) {
+ copy_v3_v3(zaxis_best, zaxis_test);
+ found = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (found) {
+
+ /* find the best roll */
+ float quat_roll[4], quat_final[4], quat_best[4], quat_snap[4];
+ float viewquat_align[4]; /* viewquat aligned to zaxis_best */
+ float viewquat_align_inv[4]; /* viewquat aligned to zaxis_best */
+ float best_angle = axis_limit;
+ int j;
+
+ /* viewquat_align is the original viewquat aligned to the snapped axis
+ * for testing roll */
+ rotation_between_vecs_to_quat(viewquat_align, zaxis_best, zaxis);
+ normalize_qt(viewquat_align);
+ mul_qt_qtqt(viewquat_align, vod->curr.viewquat, viewquat_align);
+ normalize_qt(viewquat_align);
+ invert_qt_qt_normalized(viewquat_align_inv, viewquat_align);
+
+ vec_to_quat(quat_snap, zaxis_best, OB_NEGZ, OB_POSY);
+ normalize_qt(quat_snap);
+ invert_qt_normalized(quat_snap);
+
+ /* check if we can find the roll */
+ found = false;
+
+ /* find best roll */
+ for (j = 0; j < 8; j++) {
+ float angle;
+ float xaxis1[3] = {1, 0, 0};
+ float xaxis2[3] = {1, 0, 0};
+ float quat_final_inv[4];
+
+ axis_angle_to_quat(quat_roll, zaxis_best, (float)j * DEG2RADF(45.0f));
+ normalize_qt(quat_roll);
+
+ mul_qt_qtqt(quat_final, quat_snap, quat_roll);
+ normalize_qt(quat_final);
+
+ /* compare 2 vector angles to find the least roll */
+ invert_qt_qt_normalized(quat_final_inv, quat_final);
+ mul_qt_v3(viewquat_align_inv, xaxis1);
+ mul_qt_v3(quat_final_inv, xaxis2);
+ angle = angle_v3v3(xaxis1, xaxis2);
+
+ if (angle <= best_angle) {
+ found = true;
+ best_angle = angle;
+ copy_qt_qt(quat_best, quat_final);
+ }
+ }
+
+ if (found) {
+ /* lock 'quat_best' to an axis view if we can */
+ ED_view3d_quat_to_axis_view(quat_best, 0.01f, &rv3d->view, &rv3d->view_axis_roll);
+ if (rv3d->view != RV3D_VIEW_USER) {
+ ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_best);
+ }
+ }
+ else {
+ copy_qt_qt(quat_best, viewquat_align);
+ }
+
+ copy_qt_qt(rv3d->viewquat, quat_best);
+
+ viewrotate_apply_dyn_ofs(vod, rv3d->viewquat);
+
+ if (U.uiflag & USER_AUTOPERSP) {
+ if (RV3D_VIEW_IS_AXIS(rv3d->view)) {
+ if (rv3d->persp == RV3D_PERSP) {
+ rv3d->persp = RV3D_ORTHO;
+ }
+ }
+ }
+ }
+ else if (U.uiflag & USER_AUTOPERSP) {
+ rv3d->persp = vod->init.persp;
+ }
+}
+
+static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2])
+{
+ RegionView3D *rv3d = vod->rv3d;
+
+ rv3d->view = RV3D_VIEW_USER; /* need to reset every time because of view snapping */
+
+ if (U.flag & USER_TRACKBALL) {
+ float axis[3], q1[4], dvec[3], newvec[3];
+ float angle;
+
+ {
+ const int event_xy_offset[2] = {
+ event_xy[0] + vod->init.event_xy_offset[0],
+ event_xy[1] + vod->init.event_xy_offset[1],
+ };
+ calctrackballvec(&vod->region->winrct, event_xy_offset, newvec);
+ }
+
+ sub_v3_v3v3(dvec, newvec, vod->init.trackvec);
+
+ angle = (len_v3(dvec) / (2.0f * V3D_OP_TRACKBALLSIZE)) * (float)M_PI;
+
+ /* Before applying the sensitivity this is rotating 1:1,
+ * where the cursor would match the surface of a sphere in the view. */
+ angle *= U.view_rotate_sensitivity_trackball;
+
+ /* Allow for rotation beyond the interval [-pi, pi] */
+ angle = angle_wrap_rad(angle);
+
+ /* This relation is used instead of the actual angle between vectors
+ * so that the angle of rotation is linearly proportional to
+ * the distance that the mouse is dragged. */
+
+ cross_v3_v3v3(axis, vod->init.trackvec, newvec);
+ axis_angle_to_quat(q1, axis, angle);
+
+ mul_qt_qtqt(vod->curr.viewquat, q1, vod->init.quat);
+
+ viewrotate_apply_dyn_ofs(vod, vod->curr.viewquat);
+ }
+ else {
+ float quat_local_x[4], quat_global_z[4];
+ float m[3][3];
+ float m_inv[3][3];
+ const float zvec_global[3] = {0.0f, 0.0f, 1.0f};
+ float xaxis[3];
+
+ /* Radians per-pixel. */
+ const float sensitivity = U.view_rotate_sensitivity_turntable / U.dpi_fac;
+
+ /* Get the 3x3 matrix and its inverse from the quaternion */
+ quat_to_mat3(m, vod->curr.viewquat);
+ invert_m3_m3(m_inv, m);
+
+ /* Avoid Gimbal Lock
+ *
+ * Even though turn-table mode is in use, this can occur when the user exits the camera view
+ * or when aligning the view to a rotated object.
+ *
+ * We have gimbal lock when the user's view is rotated +/- 90 degrees along the view axis.
+ * In this case the vertical rotation is the same as the sideways turntable motion.
+ * Making it impossible to get out of the gimbal locked state without resetting the view.
+ *
+ * The logic below lets the user exit out of this state without any abrupt 'fix'
+ * which would be disorienting.
+ *
+ * This works by blending two horizons:
+ * - Rotated-horizon: `cross_v3_v3v3(xaxis, zvec_global, m_inv[2])`
+ * When only this is used, this turntable rotation works - but it's side-ways
+ * (as if the entire turn-table has been placed on its side)
+ * While there is no gimbal lock, it's also awkward to use.
+ * - Un-rotated-horizon: `m_inv[0]`
+ * When only this is used, the turntable rotation can have gimbal lock.
+ *
+ * The solution used here is to blend between these two values,
+ * so the severity of the gimbal lock is used to blend the rotated horizon.
+ * Blending isn't essential, it just makes the transition smoother.
+ *
+ * This allows sideways turn-table rotation on a Z axis that isn't world-space Z,
+ * While up-down turntable rotation eventually corrects gimbal lock. */
+#if 1
+ if (len_squared_v3v3(zvec_global, m_inv[2]) > 0.001f) {
+ float fac;
+ cross_v3_v3v3(xaxis, zvec_global, m_inv[2]);
+ if (dot_v3v3(xaxis, m_inv[0]) < 0) {
+ negate_v3(xaxis);
+ }
+ fac = angle_normalized_v3v3(zvec_global, m_inv[2]) / (float)M_PI;
+ fac = fabsf(fac - 0.5f) * 2;
+ fac = fac * fac;
+ interp_v3_v3v3(xaxis, xaxis, m_inv[0], fac);
+ }
+ else {
+ copy_v3_v3(xaxis, m_inv[0]);
+ }
+#else
+ copy_v3_v3(xaxis, m_inv[0]);
+#endif
+
+ /* Determine the direction of the x vector (for rotating up and down) */
+ /* This can likely be computed directly from the quaternion. */
+
+ /* Perform the up/down rotation */
+ axis_angle_to_quat(quat_local_x, xaxis, sensitivity * -(event_xy[1] - vod->prev.event_xy[1]));
+ mul_qt_qtqt(quat_local_x, vod->curr.viewquat, quat_local_x);
+
+ /* Perform the orbital rotation */
+ axis_angle_to_quat_single(
+ quat_global_z, 'Z', sensitivity * vod->reverse * (event_xy[0] - vod->prev.event_xy[0]));
+ mul_qt_qtqt(vod->curr.viewquat, quat_local_x, quat_global_z);
+
+ viewrotate_apply_dyn_ofs(vod, vod->curr.viewquat);
+ }
+
+ /* avoid precision loss over time */
+ normalize_qt(vod->curr.viewquat);
+
+ /* use a working copy so view rotation locking doesn't overwrite the locked
+ * rotation back into the view we calculate with */
+ copy_qt_qt(rv3d->viewquat, vod->curr.viewquat);
+
+ /* Check for view snap,
+ * NOTE: don't apply snap to `vod->viewquat` so the view won't jam up. */
+ if (vod->axis_snap) {
+ viewrotate_apply_snap(vod);
+ }
+ vod->prev.event_xy[0] = event_xy[0];
+ vod->prev.event_xy[1] = event_xy[1];
+
+ ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, rv3d);
+
+ ED_region_tag_redraw(vod->region);
+}
+
+static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod = op->customdata;
+ short event_code = VIEW_PASS;
+ bool use_autokey = false;
+ int ret = OPERATOR_RUNNING_MODAL;
+
+ /* execute the events */
+ if (event->type == MOUSEMOVE) {
+ event_code = VIEW_APPLY;
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case VIEW_MODAL_CONFIRM:
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
+ vod->axis_snap = true;
+ event_code = VIEW_APPLY;
+ break;
+ case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
+ vod->rv3d->persp = vod->init.persp;
+ vod->axis_snap = false;
+ event_code = VIEW_APPLY;
+ break;
+ case VIEWROT_MODAL_SWITCH_ZOOM:
+ WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_MOVE:
+ WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ }
+ }
+ else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
+ event_code = VIEW_CONFIRM;
+ }
+
+ if (event_code == VIEW_APPLY) {
+ viewrotate_apply(vod, event->xy);
+ if (ED_screen_animation_playing(CTX_wm_manager(C))) {
+ use_autokey = true;
+ }
+ }
+ else if (event_code == VIEW_CONFIRM) {
+ use_autokey = true;
+ ret = OPERATOR_FINISHED;
+ }
+
+ if (use_autokey) {
+ ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, true);
+ }
+
+ if (ret & OPERATOR_FINISHED) {
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ }
+
+ return ret;
+}
+
+static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod;
+
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+
+ /* makes op->customdata */
+ vod = op->customdata = viewops_data_create(
+ C,
+ event,
+ viewops_flag_from_prefs() | VIEWOPS_FLAG_PERSP_ENSURE |
+ (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
+
+ ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
+
+ if (ELEM(event->type, MOUSEPAN, MOUSEROTATE)) {
+ /* Rotate direction we keep always same */
+ int event_xy[2];
+
+ if (event->type == MOUSEPAN) {
+ if (event->is_direction_inverted) {
+ event_xy[0] = 2 * event->xy[0] - event->prev_xy[0];
+ event_xy[1] = 2 * event->xy[1] - event->prev_xy[1];
+ }
+ else {
+ copy_v2_v2_int(event_xy, event->prev_xy);
+ }
+ }
+ else {
+ /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
+ copy_v2_v2_int(event_xy, event->prev_xy);
+ }
+
+ viewrotate_apply(vod, event_xy);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+
+ return OPERATOR_FINISHED;
+ }
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void viewrotate_cancel(bContext *C, wmOperator *op)
+{
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+}
+
+void VIEW3D_OT_rotate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Rotate View";
+ ot->description = "Rotate the view";
+ ot->idname = "VIEW3D_OT_rotate";
+
+ /* api callbacks */
+ ot->invoke = viewrotate_invoke;
+ ot->modal = viewrotate_modal;
+ ot->poll = view3d_rotation_poll;
+ ot->cancel = viewrotate_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
+
+ view3d_operator_properties_common(ot, V3D_OP_PROP_USE_MOUSE_INIT);
+}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
new file mode 100644
index 00000000000..aeffb520b0a
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
@@ -0,0 +1,406 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "DNA_camera_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+
+#include "ED_screen.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Smooth View Operator & Utilities
+ *
+ * Use for view transitions to have smooth (animated) transitions.
+ * \{ */
+
+/* This operator is one of the 'timer refresh' ones like animation playback */
+
+struct SmoothView3DState {
+ float dist;
+ float lens;
+ float quat[4];
+ float ofs[3];
+};
+
+struct SmoothView3DStore {
+ /* Source. */
+ struct SmoothView3DState src; /* source */
+ struct SmoothView3DState dst; /* destination */
+ struct SmoothView3DState org; /* original */
+
+ bool to_camera;
+
+ bool use_dyn_ofs;
+ float dyn_ofs[3];
+
+ /* When smooth-view is enabled, store the 'rv3d->view' here,
+ * assign back when the view motion is completed. */
+ char org_view;
+
+ double time_allowed;
+};
+
+static void view3d_smooth_view_state_backup(struct SmoothView3DState *sms_state,
+ const View3D *v3d,
+ const RegionView3D *rv3d)
+{
+ copy_v3_v3(sms_state->ofs, rv3d->ofs);
+ copy_qt_qt(sms_state->quat, rv3d->viewquat);
+ sms_state->dist = rv3d->dist;
+ sms_state->lens = v3d->lens;
+}
+
+static void view3d_smooth_view_state_restore(const struct SmoothView3DState *sms_state,
+ View3D *v3d,
+ RegionView3D *rv3d)
+{
+ copy_v3_v3(rv3d->ofs, sms_state->ofs);
+ copy_qt_qt(rv3d->viewquat, sms_state->quat);
+ rv3d->dist = sms_state->dist;
+ v3d->lens = sms_state->lens;
+}
+
+/* will start timer if appropriate */
+void ED_view3d_smooth_view_ex(
+ /* avoid passing in the context */
+ const Depsgraph *depsgraph,
+ wmWindowManager *wm,
+ wmWindow *win,
+ ScrArea *area,
+ View3D *v3d,
+ ARegion *region,
+ const int smooth_viewtx,
+ const V3D_SmoothParams *sview)
+{
+ RegionView3D *rv3d = region->regiondata;
+ struct SmoothView3DStore sms = {{0}};
+
+ /* initialize sms */
+ view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d);
+ view3d_smooth_view_state_backup(&sms.src, v3d, rv3d);
+ /* If smooth-view runs multiple times. */
+ if (rv3d->sms == NULL) {
+ view3d_smooth_view_state_backup(&sms.org, v3d, rv3d);
+ }
+ else {
+ sms.org = rv3d->sms->org;
+ }
+ sms.org_view = rv3d->view;
+
+ /* sms.to_camera = false; */ /* initialized to zero anyway */
+
+ /* note on camera locking, this is a little confusing but works ok.
+ * we may be changing the view 'as if' there is no active camera, but in fact
+ * there is an active camera which is locked to the view.
+ *
+ * In the case where smooth view is moving _to_ a camera we don't want that
+ * camera to be moved or changed, so only when the camera is not being set should
+ * we allow camera option locking to initialize the view settings from the camera.
+ */
+ if (sview->camera == NULL && sview->camera_old == NULL) {
+ ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
+ }
+
+ /* store the options we want to end with */
+ if (sview->ofs) {
+ copy_v3_v3(sms.dst.ofs, sview->ofs);
+ }
+ if (sview->quat) {
+ copy_qt_qt(sms.dst.quat, sview->quat);
+ }
+ if (sview->dist) {
+ sms.dst.dist = *sview->dist;
+ }
+ if (sview->lens) {
+ sms.dst.lens = *sview->lens;
+ }
+
+ if (sview->dyn_ofs) {
+ BLI_assert(sview->ofs == NULL);
+ BLI_assert(sview->quat != NULL);
+
+ copy_v3_v3(sms.dyn_ofs, sview->dyn_ofs);
+ sms.use_dyn_ofs = true;
+
+ /* calculate the final destination offset */
+ view3d_orbit_apply_dyn_ofs(sms.dst.ofs, sms.src.ofs, sms.src.quat, sms.dst.quat, sms.dyn_ofs);
+ }
+
+ if (sview->camera) {
+ Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera);
+ if (sview->ofs != NULL) {
+ sms.dst.dist = ED_view3d_offset_distance(
+ ob_camera_eval->obmat, sview->ofs, VIEW3D_DIST_FALLBACK);
+ }
+ ED_view3d_from_object(ob_camera_eval, sms.dst.ofs, sms.dst.quat, &sms.dst.dist, &sms.dst.lens);
+ sms.to_camera = true; /* restore view3d values in end */
+ }
+
+ if ((sview->camera_old == sview->camera) && /* Camera. */
+ (sms.dst.dist == rv3d->dist) && /* Distance. */
+ (sms.dst.lens == v3d->lens) && /* Lens. */
+ equals_v3v3(sms.dst.ofs, rv3d->ofs) && /* Offset. */
+ equals_v4v4(sms.dst.quat, rv3d->viewquat) /* Rotation. */
+ ) {
+ /* Early return if nothing changed. */
+ return;
+ }
+
+ /* Skip smooth viewing for external render engine draw. */
+ if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) {
+
+ /* original values */
+ if (sview->camera_old) {
+ Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old);
+ if (sview->ofs != NULL) {
+ sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f);
+ }
+ ED_view3d_from_object(
+ ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens);
+ }
+ /* grid draw as floor */
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
+ /* use existing if exists, means multiple calls to smooth view
+ * won't lose the original 'view' setting */
+ rv3d->view = RV3D_VIEW_USER;
+ }
+
+ sms.time_allowed = (double)smooth_viewtx / 1000.0;
+
+ /* If this is view rotation only we can decrease the time allowed by the angle between quats
+ * this means small rotations won't lag. */
+ if (sview->quat && !sview->ofs && !sview->dist) {
+ /* scale the time allowed by the rotation */
+ /* 180deg == 1.0 */
+ sms.time_allowed *= (double)fabsf(angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) /
+ M_PI;
+ }
+
+ /* ensure it shows correct */
+ if (sms.to_camera) {
+ /* use ortho if we move from an ortho view to an ortho camera */
+ Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera);
+ rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) &&
+ (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ?
+ RV3D_ORTHO :
+ RV3D_PERSP);
+ }
+
+ rv3d->rflag |= RV3D_NAVIGATING;
+
+ /* not essential but in some cases the caller will tag the area for redraw, and in that
+ * case we can get a flicker of the 'org' user view but we want to see 'src' */
+ view3d_smooth_view_state_restore(&sms.src, v3d, rv3d);
+
+ /* keep track of running timer! */
+ if (rv3d->sms == NULL) {
+ rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d");
+ }
+ *rv3d->sms = sms;
+ if (rv3d->smooth_timer) {
+ WM_event_remove_timer(wm, win, rv3d->smooth_timer);
+ }
+ /* #TIMER1 is hard-coded in key-map. */
+ rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0);
+ }
+ else {
+ /* Animation is disabled, apply immediately. */
+ if (sms.to_camera == false) {
+ copy_v3_v3(rv3d->ofs, sms.dst.ofs);
+ copy_qt_qt(rv3d->viewquat, sms.dst.quat);
+ rv3d->dist = sms.dst.dist;
+ v3d->lens = sms.dst.lens;
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ }
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_copy(area, region);
+ }
+
+ ED_region_tag_redraw(region);
+
+ WM_event_add_mousemove(win);
+ }
+}
+
+void ED_view3d_smooth_view(bContext *C,
+ View3D *v3d,
+ ARegion *region,
+ const int smooth_viewtx,
+ const struct V3D_SmoothParams *sview)
+{
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+ ScrArea *area = CTX_wm_area(C);
+
+ ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview);
+}
+
+/* only meant for timer usage */
+static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ RegionView3D *rv3d = region->regiondata;
+ struct SmoothView3DStore *sms = rv3d->sms;
+ float step, step_inv;
+
+ if (sms->time_allowed != 0.0) {
+ step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed);
+ }
+ else {
+ step = 1.0f;
+ }
+
+ /* end timer */
+ if (step >= 1.0f) {
+ wmWindow *win = CTX_wm_window(C);
+
+ /* if we went to camera, store the original */
+ if (sms->to_camera) {
+ rv3d->persp = RV3D_CAMOB;
+ view3d_smooth_view_state_restore(&sms->org, v3d, rv3d);
+ }
+ else {
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+
+ view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d);
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
+ }
+
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
+ rv3d->view = sms->org_view;
+ }
+
+ MEM_freeN(rv3d->sms);
+ rv3d->sms = NULL;
+
+ WM_event_remove_timer(wm, win, rv3d->smooth_timer);
+ rv3d->smooth_timer = NULL;
+ rv3d->rflag &= ~RV3D_NAVIGATING;
+
+ /* Event handling won't know if a UI item has been moved under the pointer. */
+ WM_event_add_mousemove(win);
+ }
+ else {
+ /* ease in/out */
+ step = (3.0f * step * step - 2.0f * step * step * step);
+
+ step_inv = 1.0f - step;
+
+ interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step);
+
+ if (sms->use_dyn_ofs) {
+ view3d_orbit_apply_dyn_ofs(
+ rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs);
+ }
+ else {
+ interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step);
+ }
+
+ rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv;
+ v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv;
+
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ if (ED_screen_animation_playing(wm)) {
+ ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
+ }
+ }
+
+ if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) {
+ view3d_boxview_copy(CTX_wm_area(C), region);
+ }
+
+ /* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636,
+ * when switching camera in quad-view the other ortho views would zoom & reset.
+ *
+ * For now only redraw all regions when smooth-view finishes.
+ */
+ if (step >= 1.0f) {
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+ }
+ else {
+ ED_region_tag_redraw(region);
+ }
+}
+
+static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ View3D *v3d = CTX_wm_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = region->regiondata;
+
+ /* escape if not our timer */
+ if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ view3d_smoothview_apply(C, v3d, region, true);
+
+ return OPERATOR_FINISHED;
+}
+
+void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region)
+{
+ RegionView3D *rv3d = region->regiondata;
+
+ if (rv3d && rv3d->sms) {
+ rv3d->sms->time_allowed = 0.0; /* force finishing */
+ view3d_smoothview_apply(C, v3d, region, false);
+
+ /* force update of view matrix so tools that run immediately after
+ * can use them without redrawing first */
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false);
+ }
+}
+
+void VIEW3D_OT_smoothview(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Smooth View";
+ ot->idname = "VIEW3D_OT_smoothview";
+
+ /* api callbacks */
+ ot->invoke = view3d_smoothview_invoke;
+
+ /* flags */
+ ot->flag = OPTYPE_INTERNAL;
+
+ ot->poll = ED_operator_view3d_active;
+}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c
index ed76b10c95a..d72fa3cb90f 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_walk.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c
@@ -58,6 +58,7 @@
#include "DEG_depsgraph.h"
#include "view3d_intern.h" /* own include */
+#include "view3d_navigate.h"
#ifdef WITH_INPUT_NDOF
//# define NDOF_WALK_DEBUG
diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c
new file mode 100644
index 00000000000..a6c7d06c079
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c
@@ -0,0 +1,598 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "BLI_math.h"
+#include "BLI_rect.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+
+#include "RNA_access.h"
+
+#include "ED_screen.h"
+
+#include "PIL_time.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name View Zoom Operator
+ * \{ */
+
+/* #viewdolly_modal_keymap has an exact copy of this, apply fixes to both. */
+void viewzoom_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const EnumPropertyItem modal_items[] = {
+ {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+
+ {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
+ {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
+
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Zoom Modal");
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items) {
+ return;
+ }
+
+ keymap = WM_modalkeymap_ensure(keyconf, "View3D Zoom Modal", modal_items);
+
+ /* disabled mode switching for now, can re-implement better, later on */
+#if 0
+ WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
+#endif
+
+ /* assign map to operators */
+ WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
+}
+
+/**
+ * \param zoom_xy: Optionally zoom to window location
+ * (coords compatible w/ #wmEvent.xy). Use when not NULL.
+ */
+static void view_zoom_to_window_xy_camera(Scene *scene,
+ Depsgraph *depsgraph,
+ View3D *v3d,
+ ARegion *region,
+ float dfac,
+ const int zoom_xy[2])
+{
+ RegionView3D *rv3d = region->regiondata;
+ const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom);
+ const float zoomfac_new = clamp_f(
+ zoomfac * (1.0f / dfac), RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR);
+ const float camzoom_new = BKE_screen_view3d_zoom_from_fac(zoomfac_new);
+
+ if (zoom_xy != NULL) {
+ float zoomfac_px;
+ rctf camera_frame_old;
+ rctf camera_frame_new;
+
+ const float pt_src[2] = {zoom_xy[0], zoom_xy[1]};
+ float pt_dst[2];
+ float delta_px[2];
+
+ ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_old, false);
+ BLI_rctf_translate(&camera_frame_old, region->winrct.xmin, region->winrct.ymin);
+
+ rv3d->camzoom = camzoom_new;
+ CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
+
+ ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_new, false);
+ BLI_rctf_translate(&camera_frame_new, region->winrct.xmin, region->winrct.ymin);
+
+ BLI_rctf_transform_pt_v(&camera_frame_new, &camera_frame_old, pt_dst, pt_src);
+ sub_v2_v2v2(delta_px, pt_dst, pt_src);
+
+ /* translate the camera offset using pixel space delta
+ * mapped back to the camera (same logic as panning in camera view) */
+ zoomfac_px = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f;
+
+ rv3d->camdx += delta_px[0] / (region->winx * zoomfac_px);
+ rv3d->camdy += delta_px[1] / (region->winy * zoomfac_px);
+ CLAMP(rv3d->camdx, -1.0f, 1.0f);
+ CLAMP(rv3d->camdy, -1.0f, 1.0f);
+ }
+ else {
+ rv3d->camzoom = camzoom_new;
+ CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
+ }
+}
+
+/**
+ * \param zoom_xy: Optionally zoom to window location
+ * (coords compatible w/ #wmEvent.xy). Use when not NULL.
+ */
+static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoom_xy[2])
+{
+ RegionView3D *rv3d = region->regiondata;
+ const float dist_new = rv3d->dist * dfac;
+
+ if (zoom_xy != NULL) {
+ float dvec[3];
+ float tvec[3];
+ float tpos[3];
+ float mval_f[2];
+
+ float zfac;
+
+ negate_v3_v3(tpos, rv3d->ofs);
+
+ mval_f[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f;
+ mval_f[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f;
+
+ /* Project cursor position into 3D space */
+ zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL);
+ ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+
+ /* Calculate view target position for dolly */
+ add_v3_v3v3(tvec, tpos, dvec);
+ negate_v3(tvec);
+
+ /* Offset to target position and dolly */
+ copy_v3_v3(rv3d->ofs, tvec);
+ rv3d->dist = dist_new;
+
+ /* Calculate final offset */
+ madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
+ }
+ else {
+ rv3d->dist = dist_new;
+ }
+}
+
+static float viewzoom_scale_value(const rcti *winrct,
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_invert_force,
+ const int xy_curr[2],
+ const int xy_init[2],
+ const float val,
+ const float val_orig,
+ double *r_timer_lastdraw)
+{
+ float zfac;
+
+ if (viewzoom == USER_ZOOM_CONTINUE) {
+ double time = PIL_check_seconds_timer();
+ float time_step = (float)(time - *r_timer_lastdraw);
+ float fac;
+
+ if (U.uiflag & USER_ZOOM_HORIZ) {
+ fac = (float)(xy_init[0] - xy_curr[0]);
+ }
+ else {
+ fac = (float)(xy_init[1] - xy_curr[1]);
+ }
+
+ fac /= U.dpi_fac;
+
+ if (zoom_invert != zoom_invert_force) {
+ fac = -fac;
+ }
+
+ zfac = 1.0f + ((fac / 20.0f) * time_step);
+ *r_timer_lastdraw = time;
+ }
+ else if (viewzoom == USER_ZOOM_SCALE) {
+ /* method which zooms based on how far you move the mouse */
+
+ const int ctr[2] = {
+ BLI_rcti_cent_x(winrct),
+ BLI_rcti_cent_y(winrct),
+ };
+ float len_new = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_curr) / U.dpi_fac);
+ float len_old = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_init) / U.dpi_fac);
+
+ /* intentionally ignore 'zoom_invert' for scale */
+ if (zoom_invert_force) {
+ SWAP(float, len_new, len_old);
+ }
+
+ zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val;
+ }
+ else { /* USER_ZOOM_DOLLY */
+ float len_new = 5 * U.dpi_fac;
+ float len_old = 5 * U.dpi_fac;
+
+ if (U.uiflag & USER_ZOOM_HORIZ) {
+ len_new += (winrct->xmax - (xy_curr[0])) / U.dpi_fac;
+ len_old += (winrct->xmax - (xy_init[0])) / U.dpi_fac;
+ }
+ else {
+ len_new += (winrct->ymax - (xy_curr[1])) / U.dpi_fac;
+ len_old += (winrct->ymax - (xy_init[1])) / U.dpi_fac;
+ }
+
+ if (zoom_invert != zoom_invert_force) {
+ SWAP(float, len_new, len_old);
+ }
+
+ zfac = val_orig * (2.0f * ((len_new / max_ff(len_old, 1.0f)) - 1.0f) + 1.0f) / val;
+ }
+
+ return zfac;
+}
+
+static float viewzoom_scale_value_offset(const rcti *winrct,
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_invert_force,
+ const int xy_curr[2],
+ const int xy_init[2],
+ const int xy_offset[2],
+ const float val,
+ const float val_orig,
+ double *r_timer_lastdraw)
+{
+ const int xy_curr_offset[2] = {
+ xy_curr[0] + xy_offset[0],
+ xy_curr[1] + xy_offset[1],
+ };
+ const int xy_init_offset[2] = {
+ xy_init[0] + xy_offset[0],
+ xy_init[1] + xy_offset[1],
+ };
+ return viewzoom_scale_value(winrct,
+ viewzoom,
+ zoom_invert,
+ zoom_invert_force,
+ xy_curr_offset,
+ xy_init_offset,
+ val,
+ val_orig,
+ r_timer_lastdraw);
+}
+
+static void viewzoom_apply_camera(ViewOpsData *vod,
+ const int xy[2],
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_to_pos)
+{
+ float zfac;
+ float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->init.camzoom) * 2.0f;
+ float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
+
+ zfac = viewzoom_scale_value_offset(&vod->region->winrct,
+ viewzoom,
+ zoom_invert,
+ true,
+ xy,
+ vod->init.event_xy,
+ vod->init.event_xy_offset,
+ zoomfac,
+ zoomfac_prev,
+ &vod->prev.time);
+
+ if (!ELEM(zfac, 1.0f, 0.0f)) {
+ /* calculate inverted, then invert again (needed because of camera zoom scaling) */
+ zfac = 1.0f / zfac;
+ view_zoom_to_window_xy_camera(vod->scene,
+ vod->depsgraph,
+ vod->v3d,
+ vod->region,
+ zfac,
+ zoom_to_pos ? vod->prev.event_xy : NULL);
+ }
+
+ ED_region_tag_redraw(vod->region);
+}
+
+static void viewzoom_apply_3d(ViewOpsData *vod,
+ const int xy[2],
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_to_pos)
+{
+ float zfac;
+ float dist_range[2];
+
+ ED_view3d_dist_range_get(vod->v3d, dist_range);
+
+ zfac = viewzoom_scale_value_offset(&vod->region->winrct,
+ viewzoom,
+ zoom_invert,
+ false,
+ xy,
+ vod->init.event_xy,
+ vod->init.event_xy_offset,
+ vod->rv3d->dist,
+ vod->init.dist,
+ &vod->prev.time);
+
+ if (zfac != 1.0f) {
+ const float zfac_min = dist_range[0] / vod->rv3d->dist;
+ const float zfac_max = dist_range[1] / vod->rv3d->dist;
+ CLAMP(zfac, zfac_min, zfac_max);
+
+ view_zoom_to_window_xy_3d(vod->region, zfac, zoom_to_pos ? vod->prev.event_xy : NULL);
+ }
+
+ /* these limits were in old code too */
+ CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]);
+
+ if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(vod->area, vod->region);
+ }
+
+ ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
+
+ ED_region_tag_redraw(vod->region);
+}
+
+static void viewzoom_apply(ViewOpsData *vod,
+ const int xy[2],
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_to_pos)
+{
+ if ((vod->rv3d->persp == RV3D_CAMOB) &&
+ (vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0) {
+ viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert, zoom_to_pos);
+ }
+ else {
+ viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert, zoom_to_pos);
+ }
+}
+
+static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod = op->customdata;
+ short event_code = VIEW_PASS;
+ bool use_autokey = false;
+ int ret = OPERATOR_RUNNING_MODAL;
+
+ /* execute the events */
+ if (event->type == TIMER && event->customdata == vod->timer) {
+ /* continuous zoom */
+ event_code = VIEW_APPLY;
+ }
+ else if (event->type == MOUSEMOVE) {
+ event_code = VIEW_APPLY;
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case VIEW_MODAL_CONFIRM:
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_MOVE:
+ WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_ROTATE:
+ WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ }
+ }
+ else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
+ event_code = VIEW_CONFIRM;
+ }
+
+ if (event_code == VIEW_APPLY) {
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+ viewzoom_apply(vod,
+ event->xy,
+ (eViewZoom_Style)U.viewzoom,
+ (U.uiflag & USER_ZOOM_INVERT) != 0,
+ (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
+ if (ED_screen_animation_playing(CTX_wm_manager(C))) {
+ use_autokey = true;
+ }
+ }
+ else if (event_code == VIEW_CONFIRM) {
+ use_autokey = true;
+ ret = OPERATOR_FINISHED;
+ }
+
+ if (use_autokey) {
+ ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
+ }
+
+ if (ret & OPERATOR_FINISHED) {
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ }
+
+ return ret;
+}
+
+static int viewzoom_exec(bContext *C, wmOperator *op)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d;
+ RegionView3D *rv3d;
+ ScrArea *area;
+ ARegion *region;
+ bool use_cam_zoom;
+ float dist_range[2];
+
+ const int delta = RNA_int_get(op->ptr, "delta");
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+
+ if (op->customdata) {
+ ViewOpsData *vod = op->customdata;
+
+ area = vod->area;
+ region = vod->region;
+ }
+ else {
+ area = CTX_wm_area(C);
+ region = CTX_wm_region(C);
+ }
+
+ v3d = area->spacedata.first;
+ rv3d = region->regiondata;
+
+ use_cam_zoom = (rv3d->persp == RV3D_CAMOB) &&
+ !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d));
+
+ int zoom_xy_buf[2];
+ const int *zoom_xy = NULL;
+ if (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) {
+ zoom_xy_buf[0] = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") :
+ region->winx / 2;
+ zoom_xy_buf[1] = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") :
+ region->winy / 2;
+ zoom_xy = zoom_xy_buf;
+ }
+
+ ED_view3d_dist_range_get(v3d, dist_range);
+
+ if (delta < 0) {
+ const float step = 1.2f;
+ if (use_cam_zoom) {
+ view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy);
+ }
+ else {
+ if (rv3d->dist < dist_range[1]) {
+ view_zoom_to_window_xy_3d(region, step, zoom_xy);
+ }
+ }
+ }
+ else {
+ const float step = 1.0f / 1.2f;
+ if (use_cam_zoom) {
+ view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy);
+ }
+ else {
+ if (rv3d->dist > dist_range[0]) {
+ view_zoom_to_window_xy_3d(region, step, zoom_xy);
+ }
+ }
+ }
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(area, region);
+ }
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true);
+
+ ED_region_tag_redraw(region);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+
+ return OPERATOR_FINISHED;
+}
+
+/* viewdolly_invoke() copied this function, changes here may apply there */
+static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod;
+
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+
+ vod = op->customdata = viewops_data_create(
+ C,
+ event,
+ (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
+ (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
+
+ ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
+
+ /* if one or the other zoom position aren't set, set from event */
+ if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
+ RNA_int_set(op->ptr, "mx", event->xy[0]);
+ RNA_int_set(op->ptr, "my", event->xy[1]);
+ }
+
+ if (RNA_struct_property_is_set(op->ptr, "delta")) {
+ viewzoom_exec(C, op);
+ }
+ else {
+ if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
+
+ if (U.uiflag & USER_ZOOM_HORIZ) {
+ vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
+ }
+ else {
+ /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
+ vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] -
+ event->prev_xy[0];
+ }
+ viewzoom_apply(vod,
+ event->prev_xy,
+ USER_ZOOM_DOLLY,
+ (U.uiflag & USER_ZOOM_INVERT) != 0,
+ (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
+ ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ return OPERATOR_FINISHED;
+ }
+
+ if (U.viewzoom == USER_ZOOM_CONTINUE) {
+ /* needs a timer to continue redrawing */
+ vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
+ vod->prev.time = PIL_check_seconds_timer();
+ }
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_FINISHED;
+}
+
+static void viewzoom_cancel(bContext *C, wmOperator *op)
+{
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+}
+
+void VIEW3D_OT_zoom(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Zoom View";
+ ot->description = "Zoom in/out in the view";
+ ot->idname = "VIEW3D_OT_zoom";
+
+ /* api callbacks */
+ ot->invoke = viewzoom_invoke;
+ ot->exec = viewzoom_exec;
+ ot->modal = viewzoom_modal;
+ ot->poll = view3d_zoom_or_dolly_poll;
+ ot->cancel = viewzoom_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
+
+ /* properties */
+ view3d_operator_properties_common(
+ ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT);
+}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
new file mode 100644
index 00000000000..38c3e37bac6
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
@@ -0,0 +1,221 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "DNA_camera_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_rect.h"
+
+#include "BKE_context.h"
+#include "BKE_report.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+
+#include "RNA_access.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Border Zoom Operator
+ * \{ */
+
+static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
+{
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ /* Zooms in on a border drawn by the user */
+ rcti rect;
+ float dvec[3], vb[2], xscale, yscale;
+ float dist_range[2];
+
+ /* SMOOTHVIEW */
+ float new_dist;
+ float new_ofs[3];
+
+ /* ZBuffer depth vars */
+ float depth_close = FLT_MAX;
+ float cent[2], p[3];
+
+ /* NOTE: otherwise opengl won't work. */
+ view3d_operator_needs_opengl(C);
+
+ /* get box select values using rna */
+ WM_operator_properties_border_to_rcti(op, &rect);
+
+ /* check if zooming in/out view */
+ const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out");
+
+ ED_view3d_dist_range_get(v3d, dist_range);
+
+ ED_view3d_depth_override(
+ CTX_data_ensure_evaluated_depsgraph(C), region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL);
+ {
+ /* avoid allocating the whole depth buffer */
+ ViewDepths depth_temp = {0};
+
+ /* avoid view3d_update_depths() for speed. */
+ view3d_depths_rect_create(region, &rect, &depth_temp);
+
+ /* find the closest Z pixel */
+ depth_close = view3d_depth_near(&depth_temp);
+
+ MEM_SAFE_FREE(depth_temp.depths);
+ }
+
+ /* Resize border to the same ratio as the window. */
+ {
+ const float region_aspect = (float)region->winx / (float)region->winy;
+ if (((float)BLI_rcti_size_x(&rect) / (float)BLI_rcti_size_y(&rect)) < region_aspect) {
+ BLI_rcti_resize_x(&rect, (int)(BLI_rcti_size_y(&rect) * region_aspect));
+ }
+ else {
+ BLI_rcti_resize_y(&rect, (int)(BLI_rcti_size_x(&rect) / region_aspect));
+ }
+ }
+
+ cent[0] = (((float)rect.xmin) + ((float)rect.xmax)) / 2;
+ cent[1] = (((float)rect.ymin) + ((float)rect.ymax)) / 2;
+
+ if (rv3d->is_persp) {
+ float p_corner[3];
+
+ /* no depths to use, we can't do anything! */
+ if (depth_close == FLT_MAX) {
+ BKE_report(op->reports, RPT_ERROR, "Depth too large");
+ return OPERATOR_CANCELLED;
+ }
+ /* convert border to 3d coordinates */
+ if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) ||
+ (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) {
+ return OPERATOR_CANCELLED;
+ }
+
+ sub_v3_v3v3(dvec, p, p_corner);
+ negate_v3_v3(new_ofs, p);
+
+ new_dist = len_v3(dvec);
+
+ /* Account for the lens, without this a narrow lens zooms in too close. */
+ new_dist *= (v3d->lens / DEFAULT_SENSOR_WIDTH);
+
+ /* ignore dist_range min */
+ dist_range[0] = v3d->clip_start * 1.5f;
+ }
+ else { /* orthographic */
+ /* find the current window width and height */
+ vb[0] = region->winx;
+ vb[1] = region->winy;
+
+ new_dist = rv3d->dist;
+
+ /* convert the drawn rectangle into 3d space */
+ if (depth_close != FLT_MAX &&
+ ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) {
+ negate_v3_v3(new_ofs, p);
+ }
+ else {
+ float mval_f[2];
+ float zfac;
+
+ /* We can't use the depth, fallback to the old way that doesn't set the center depth */
+ copy_v3_v3(new_ofs, rv3d->ofs);
+
+ {
+ float tvec[3];
+ negate_v3_v3(tvec, new_ofs);
+ zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL);
+ }
+
+ mval_f[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f;
+ mval_f[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f;
+ ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+ /* center the view to the center of the rectangle */
+ sub_v3_v3(new_ofs, dvec);
+ }
+
+ /* work out the ratios, so that everything selected fits when we zoom */
+ xscale = (BLI_rcti_size_x(&rect) / vb[0]);
+ yscale = (BLI_rcti_size_y(&rect) / vb[1]);
+ new_dist *= max_ff(xscale, yscale);
+ }
+
+ if (!zoom_in) {
+ sub_v3_v3v3(dvec, new_ofs, rv3d->ofs);
+ new_dist = rv3d->dist * (rv3d->dist / new_dist);
+ add_v3_v3v3(new_ofs, rv3d->ofs, dvec);
+ }
+
+ /* clamp after because we may have been zooming out */
+ CLAMP(new_dist, dist_range[0], dist_range[1]);
+
+ /* TODO(campbell): 'is_camera_lock' not currently working well. */
+ const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d);
+ if ((rv3d->persp == RV3D_CAMOB) && (is_camera_lock == false)) {
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP);
+ }
+
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .ofs = new_ofs,
+ .dist = &new_dist,
+ });
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(CTX_wm_area(C), region);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_zoom_border(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Zoom to Border";
+ ot->description = "Zoom in the view to the nearest object contained in the border";
+ ot->idname = "VIEW3D_OT_zoom_border";
+
+ /* api callbacks */
+ ot->invoke = WM_gesture_box_invoke;
+ ot->exec = view3d_zoom_border_exec;
+ ot->modal = WM_gesture_box_modal;
+ ot->cancel = WM_gesture_box_cancel;
+
+ ot->poll = view3d_zoom_or_dolly_poll;
+
+ /* flags */
+ ot->flag = 0;
+
+ /* properties */
+ WM_operator_properties_gesture_box_zoom(ot);
+}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c
index 823aa3b6643..52db8526937 100644
--- a/source/blender/editors/space_view3d/view3d_ops.c
+++ b/source/blender/editors/space_view3d/view3d_ops.c
@@ -50,6 +50,7 @@
#include "ED_transform.h"
#include "view3d_intern.h"
+#include "view3d_navigate.h"
#ifdef WIN32
# include "BLI_math_base.h" /* M_PI */
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 45899880b41..34aa24a1eef 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -99,6 +99,7 @@
#include "UI_resources.h"
#include "GPU_matrix.h"
+#include "GPU_select.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -1566,8 +1567,8 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot)
static Base *object_mouse_select_menu(bContext *C,
ViewContext *vc,
- const uint *buffer,
- int hits,
+ const GPUSelectResult *buffer,
+ const int hits,
const int mval[2],
bool extend,
bool deselect,
@@ -1585,7 +1586,7 @@ static Base *object_mouse_select_menu(bContext *C,
if (buffer) {
for (int a = 0; a < hits; a++) {
/* index was converted */
- if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & ~0xFFFF0000)) {
+ if (base->object->runtime.select_id == (buffer[a].id & ~0xFFFF0000)) {
ok = true;
break;
}
@@ -1742,7 +1743,7 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
static bool bone_mouse_select_menu(bContext *C,
- const uint *buffer,
+ const GPUSelectResult *buffer,
const int hits,
const bool is_editmode,
const bool extend,
@@ -1760,7 +1761,7 @@ static bool bone_mouse_select_menu(bContext *C,
for (int a = 0; a < hits; a++) {
void *bone_ptr = NULL;
Base *bone_base = NULL;
- uint hitresult = buffer[3 + (a * 4)];
+ uint hitresult = buffer[a].id;
if (!(hitresult & BONESEL_ANY)) {
/* To avoid including objects in selection. */
@@ -1874,10 +1875,10 @@ static bool bone_mouse_select_menu(bContext *C,
return true;
}
-static bool selectbuffer_has_bones(const uint *buffer, const uint hits)
+static bool selectbuffer_has_bones(const GPUSelectResult *buffer, const uint hits)
{
for (uint i = 0; i < hits; i++) {
- if (buffer[(4 * i) + 3] & 0xFFFF0000) {
+ if (buffer[i].id & 0xFFFF0000) {
return true;
}
}
@@ -1885,25 +1886,25 @@ static bool selectbuffer_has_bones(const uint *buffer, const uint hits)
}
/* utility function for mixed_bones_object_selectbuffer */
-static int selectbuffer_ret_hits_15(uint *UNUSED(buffer), const int hits15)
+static int selectbuffer_ret_hits_15(GPUSelectResult *UNUSED(buffer), const int hits15)
{
return hits15;
}
-static int selectbuffer_ret_hits_9(uint *buffer, const int hits15, const int hits9)
+static int selectbuffer_ret_hits_9(GPUSelectResult *buffer, const int hits15, const int hits9)
{
- const int ofs = 4 * hits15;
- memcpy(buffer, buffer + ofs, 4 * hits9 * sizeof(uint));
+ const int ofs = hits15;
+ memcpy(buffer, buffer + ofs, hits9 * sizeof(GPUSelectResult));
return hits9;
}
-static int selectbuffer_ret_hits_5(uint *buffer,
+static int selectbuffer_ret_hits_5(GPUSelectResult *buffer,
const int hits15,
const int hits9,
const int hits5)
{
- const int ofs = 4 * hits15 + 4 * hits9;
- memcpy(buffer, buffer + ofs, 4 * hits5 * sizeof(uint));
+ const int ofs = hits15 + hits9;
+ memcpy(buffer, buffer + ofs, hits5 * sizeof(GPUSelectResult));
return hits5;
}
@@ -1916,7 +1917,8 @@ static int selectbuffer_ret_hits_5(uint *buffer,
* Needed so we can step to the next, non-active object when it's already selected, see: T76445.
*/
static int mixed_bones_object_selectbuffer(ViewContext *vc,
- uint *buffer,
+ GPUSelectResult *buffer,
+ const int buffer_len,
const int mval[2],
eV3DSelectObjectFilter select_filter,
bool do_nearest,
@@ -1941,7 +1943,7 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc,
BLI_rcti_init_pt_radius(&rect, mval, 14);
hits15 = view3d_opengl_select_ex(
- vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter, do_material_slot_selection);
+ vc, buffer, buffer_len, &rect, select_mode, select_filter, do_material_slot_selection);
if (hits15 == 1) {
hits = selectbuffer_ret_hits_15(buffer, hits15);
goto finally;
@@ -1950,10 +1952,10 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc,
int ofs;
has_bones15 = selectbuffer_has_bones(buffer, hits15);
- ofs = 4 * hits15;
+ ofs = hits15;
BLI_rcti_init_pt_radius(&rect, mval, 9);
hits9 = view3d_opengl_select(
- vc, buffer + ofs, MAXPICKBUF - ofs, &rect, select_mode, select_filter);
+ vc, buffer + ofs, buffer_len - ofs, &rect, select_mode, select_filter);
if (hits9 == 1) {
hits = selectbuffer_ret_hits_9(buffer, hits15, hits9);
goto finally;
@@ -1961,10 +1963,10 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc,
else if (hits9 > 0) {
has_bones9 = selectbuffer_has_bones(buffer + ofs, hits9);
- ofs += 4 * hits9;
+ ofs += hits9;
BLI_rcti_init_pt_radius(&rect, mval, 5);
hits5 = view3d_opengl_select(
- vc, buffer + ofs, MAXPICKBUF - ofs, &rect, select_mode, select_filter);
+ vc, buffer + ofs, buffer_len - ofs, &rect, select_mode, select_filter);
if (hits5 == 1) {
hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5);
goto finally;
@@ -2007,7 +2009,8 @@ finally:
}
static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
- uint *buffer,
+ GPUSelectResult *buffer,
+ const int buffer_len,
const int mval[2],
eV3DSelectObjectFilter select_filter,
bool use_cycle,
@@ -2038,7 +2041,7 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
do_nearest = do_nearest && !enumerate;
int hits = mixed_bones_object_selectbuffer(
- vc, buffer, mval, select_filter, do_nearest, true, false);
+ vc, buffer, buffer_len, mval, select_filter, do_nearest, true, false);
return hits;
}
@@ -2051,7 +2054,7 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
* \return the active base or NULL.
*/
static Base *mouse_select_eval_buffer(ViewContext *vc,
- const uint *buffer,
+ const GPUSelectResult *buffer,
int hits,
Base *startbase,
bool has_bones,
@@ -2071,10 +2074,10 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
if (has_bones) {
/* we skip non-bone hits */
for (a = 0; a < hits; a++) {
- if (min > buffer[4 * a + 1] && (buffer[4 * a + 3] & 0xFFFF0000)) {
- min = buffer[4 * a + 1];
- selcol = buffer[4 * a + 3] & 0xFFFF;
- sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16;
+ if (min > buffer[a].depth && (buffer[a].id & 0xFFFF0000)) {
+ min = buffer[a].depth;
+ selcol = buffer[a].id & 0xFFFF;
+ sub_selection_id = (buffer[a].id & 0xFFFF0000) >> 16;
}
}
}
@@ -2085,10 +2088,10 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
}
for (a = 0; a < hits; a++) {
- if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) {
- min = buffer[4 * a + 1];
- selcol = buffer[4 * a + 3] & 0xFFFF;
- sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16;
+ if (min > buffer[a].depth && notcol != (buffer[a].id & 0xFFFF)) {
+ min = buffer[a].depth;
+ selcol = buffer[a].id & 0xFFFF;
+ sub_selection_id = (buffer[a].id & 0xFFFF0000) >> 16;
}
}
}
@@ -2127,14 +2130,14 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
for (a = 0; a < hits; a++) {
if (has_bones) {
/* skip non-bone objects */
- if (buffer[4 * a + 3] & 0xFFFF0000) {
- if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) {
+ if (buffer[a].id & 0xFFFF0000) {
+ if (base->object->runtime.select_id == (buffer[a].id & 0xFFFF)) {
basact = base;
}
}
}
else {
- if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) {
+ if (base->object->runtime.select_id == (buffer[a].id & 0xFFFF)) {
basact = base;
}
}
@@ -2169,7 +2172,7 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
Base *basact = NULL;
- uint buffer[MAXPICKBUF];
+ GPUSelectResult buffer[MAXPICKELEMS];
/* setup view context for argument to callbacks */
view3d_operator_needs_opengl(C);
@@ -2179,8 +2182,14 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
const bool do_material_slot_selection = r_material_slot != NULL;
- const int hits = mixed_bones_object_selectbuffer(
- &vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false, do_material_slot_selection);
+ const int hits = mixed_bones_object_selectbuffer(&vc,
+ buffer,
+ ARRAY_SIZE(buffer),
+ mval,
+ VIEW3D_SELECT_FILTER_NOP,
+ do_nearest,
+ false,
+ do_material_slot_selection);
if (hits > 0) {
const bool has_bones = (r_material_slot == NULL) && selectbuffer_has_bones(buffer, hits);
@@ -2342,7 +2351,7 @@ static bool ed_object_select_pick(bContext *C,
}
}
else {
- uint buffer[MAXPICKBUF];
+ GPUSelectResult buffer[MAXPICKELEMS];
bool do_nearest;
// TIMEIT_START(select_time);
@@ -2353,7 +2362,7 @@ static bool ed_object_select_pick(bContext *C,
vc.obact) :
VIEW3D_SELECT_FILTER_NOP);
hits = mixed_bones_object_selectbuffer_extended(
- &vc, buffer, mval, select_filter, true, enumerate, &do_nearest);
+ &vc, buffer, ARRAY_SIZE(buffer), mval, select_filter, true, enumerate, &do_nearest);
// TIMEIT_END(select_time);
@@ -2383,7 +2392,7 @@ static bool ed_object_select_pick(bContext *C,
bool changed = false;
for (int i = 0; i < hits; i++) {
- int hitresult = buffer[3 + (i * 4)];
+ const int hitresult = buffer[i].id;
/* if there's bundles in buffer select bundles first,
* so non-camera elements should be ignored in buffer */
@@ -2394,7 +2403,7 @@ static bool ed_object_select_pick(bContext *C,
/* index of bundle is 1<<16-based. if there's no "bone" index
* in height word, this buffer value belongs to camera. not to bundle
*/
- if (buffer[4 * i + 3] & 0xFFFF0000) {
+ if (hitresult & 0xFFFF0000) {
MovieTracking *tracking = &clip->tracking;
ListBase *tracksbase;
MovieTrackingTrack *track;
@@ -2674,9 +2683,15 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
ViewContext vc;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
- uint buffer[MAXPICKBUF];
- const int hits = mixed_bones_object_selectbuffer(
- &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true, false);
+ GPUSelectResult buffer[MAXPICKELEMS];
+ const int hits = mixed_bones_object_selectbuffer(&vc,
+ buffer,
+ ARRAY_SIZE(buffer),
+ location,
+ VIEW3D_SELECT_FILTER_NOP,
+ false,
+ true,
+ false);
retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle);
}
if (!retval) {
@@ -3256,11 +3271,11 @@ static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectO
int a;
bool changed = false;
- uint buffer[MAXPICKBUF];
+ GPUSelectResult buffer[MAXPICKELEMS];
int hits;
hits = view3d_opengl_select(
- vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP);
+ vc, buffer, MAXPICKELEMS, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
changed |= BKE_mball_deselect_all(mb);
@@ -3272,7 +3287,7 @@ static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectO
bool is_inside_stiff = false;
for (a = 0; a < hits; a++) {
- int hitresult = buffer[(4 * a) + 3];
+ const int hitresult = buffer[a].id;
if (hitresult == -1) {
continue;
@@ -3323,11 +3338,11 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel
bool changed = false;
int a;
- uint buffer[MAXPICKBUF];
+ GPUSelectResult buffer[MAXPICKELEMS];
int hits;
hits = view3d_opengl_select(
- vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP);
+ vc, buffer, MAXPICKELEMS, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
@@ -3347,7 +3362,7 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel
/* first we only check points inside the border */
for (a = 0; a < hits; a++) {
- int select_id = buffer[(4 * a) + 3];
+ const int select_id = buffer[a].id;
if (select_id != -1) {
if ((select_id & 0xFFFF0000) == 0) {
continue;
@@ -3375,14 +3390,13 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel
}
/**
- * Compare result of 'GPU_select': 'uint[4]',
+ * Compare result of 'GPU_select': 'GPUSelectResult',
* needed for when we need to align with object draw-order.
*/
static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p)
{
- /* 4th element is select id */
- uint sel_a = ((uint *)sel_a_p)[3];
- uint sel_b = ((uint *)sel_b_p)[3];
+ uint sel_a = ((GPUSelectResult *)sel_a_p)->id;
+ uint sel_b = ((GPUSelectResult *)sel_b_p)->id;
#ifdef __BIG_ENDIAN__
BLI_endian_switch_uint32(&sel_a);
@@ -3401,14 +3415,15 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_
static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
{
View3D *v3d = vc->v3d;
- int totobj = MAXPICKBUF; /* XXX solve later */
+ int totobj = MAXPICKELEMS; /* XXX solve later */
- /* selection buffer now has bones potentially too, so we add MAXPICKBUF */
- uint *vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer");
+ /* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */
+ GPUSelectResult *buffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult),
+ "selection buffer");
const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene,
vc->obact);
const int hits = view3d_opengl_select(
- vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
+ vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
LISTBASE_FOREACH (Base *, base, &vc->view_layer->object_bases) {
base->object->id.tag &= ~LIB_TAG_DOIT;
@@ -3435,12 +3450,13 @@ static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const
}
/* The draw order doesn't always match the order we populate the engine, see: T51695. */
- qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp);
+ qsort(buffer, hits, sizeof(GPUSelectResult), opengl_bone_select_buffer_cmp);
- for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) {
+ for (const GPUSelectResult *buf_iter = buffer, *buf_end = buf_iter + hits; buf_iter < buf_end;
+ buf_iter++) {
bPoseChannel *pchan_dummy;
Base *base = ED_armature_base_and_pchan_from_select_buffer(
- bases, BLI_array_len(bases), *col, &pchan_dummy);
+ bases, BLI_array_len(bases), buf_iter->id, &pchan_dummy);
if (base != NULL) {
base->object->id.tag |= LIB_TAG_DOIT;
}
@@ -3463,7 +3479,7 @@ finally:
MEM_freeN(bases);
}
- MEM_freeN(vbuffer);
+ MEM_freeN(buffer);
if (changed) {
DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
@@ -3477,14 +3493,15 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e
uint bases_len;
Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len);
- int totobj = MAXPICKBUF; /* XXX solve later */
+ int totobj = MAXPICKELEMS; /* XXX solve later */
- /* selection buffer now has bones potentially too, so we add MAXPICKBUF */
- uint *vbuffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer");
+ /* Selection buffer has bones potentially too, so add #MAXPICKELEMS. */
+ GPUSelectResult *buffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult),
+ "selection buffer");
const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene,
vc->obact);
const int hits = view3d_opengl_select(
- vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
+ vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
/*
* LOGIC NOTES (theeth):
* The buffer and ListBase have the same relative order, which makes the selection
@@ -3498,18 +3515,20 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e
/* no need to loop if there's no hit */
/* The draw order doesn't always match the order we populate the engine, see: T51695. */
- qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp);
+ qsort(buffer, hits, sizeof(GPUSelectResult), opengl_bone_select_buffer_cmp);
- for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) {
+ for (const GPUSelectResult *buf_iter = buffer, *buf_end = buf_iter + hits; buf_iter < buf_end;
+ buf_iter++) {
Bone *bone;
- Base *base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, *col, &bone);
+ Base *base = ED_armature_base_and_bone_from_select_buffer(
+ bases, bases_len, buf_iter->id, &bone);
if (base == NULL) {
continue;
}
/* Loop over contiguous bone hits for 'base'. */
- for (; col != col_end; col += 4) {
+ for (; buf_iter != buf_end; buf_iter++) {
/* should never fail */
if (bone != NULL) {
base->object->id.tag |= LIB_TAG_DOIT;
@@ -3517,12 +3536,13 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e
}
/* Select the next bone if we're not switching bases. */
- if (col + 4 != col_end) {
- if ((base->object->runtime.select_id & 0x0000FFFF) != (col[4] & 0x0000FFFF)) {
+ if (buf_iter + 1 != buf_end) {
+ const GPUSelectResult *col_next = buf_iter + 1;
+ if ((base->object->runtime.select_id & 0x0000FFFF) != (col_next->id & 0x0000FFFF)) {
break;
}
if (base->object->pose != NULL) {
- const uint hit_bone = (col[4] & ~BONESEL_ANY) >> 16;
+ const uint hit_bone = (col_next->id & ~BONESEL_ANY) >> 16;
bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone);
bone = pchan ? pchan->bone : NULL;
}
@@ -3543,7 +3563,7 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e
if (bases != NULL) {
MEM_freeN(bases);
}
- MEM_freeN(vbuffer);
+ MEM_freeN(buffer);
return changed_multi;
}
diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c
index 53bd181f544..4334ede0a06 100644
--- a/source/blender/editors/space_view3d/view3d_snap.c
+++ b/source/blender/editors/space_view3d/view3d_snap.c
@@ -1033,7 +1033,7 @@ bool ED_view3d_minmax_verts(Object *obedit, float r_min[3], float r_max[3])
}
if (ED_transverts_check_obedit(obedit)) {
- ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS);
+ ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS | TM_CALC_MAPLOC);
}
if (tvs.transverts_tot == 0) {
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index 165f931394d..ddd5cc640bb 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -21,21 +21,14 @@
* \ingroup spview3d
*/
-#include "DNA_camera_types.h"
-#include "DNA_gpencil_modifier_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-
#include "MEM_guardedalloc.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
-#include "BLI_utildefines.h"
#include "BKE_action.h"
-#include "BKE_camera.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_gpencil_modifier.h"
@@ -47,7 +40,6 @@
#include "BKE_report.h"
#include "BKE_scene.h"
-#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "UI_resources.h"
@@ -57,7 +49,6 @@
#include "GPU_state.h"
#include "WM_api.h"
-#include "WM_types.h"
#include "ED_object.h"
#include "ED_screen.h"
@@ -68,376 +59,7 @@
#include "RNA_define.h"
#include "view3d_intern.h" /* own include */
-
-/* -------------------------------------------------------------------- */
-/** \name Smooth View Operator & Utilities
- *
- * Use for view transitions to have smooth (animated) transitions.
- * \{ */
-
-/* This operator is one of the 'timer refresh' ones like animation playback */
-
-struct SmoothView3DState {
- float dist;
- float lens;
- float quat[4];
- float ofs[3];
-};
-
-struct SmoothView3DStore {
- /* Source. */
- struct SmoothView3DState src; /* source */
- struct SmoothView3DState dst; /* destination */
- struct SmoothView3DState org; /* original */
-
- bool to_camera;
-
- bool use_dyn_ofs;
- float dyn_ofs[3];
-
- /* When smooth-view is enabled, store the 'rv3d->view' here,
- * assign back when the view motion is completed. */
- char org_view;
-
- double time_allowed;
-};
-
-static void view3d_smooth_view_state_backup(struct SmoothView3DState *sms_state,
- const View3D *v3d,
- const RegionView3D *rv3d)
-{
- copy_v3_v3(sms_state->ofs, rv3d->ofs);
- copy_qt_qt(sms_state->quat, rv3d->viewquat);
- sms_state->dist = rv3d->dist;
- sms_state->lens = v3d->lens;
-}
-
-static void view3d_smooth_view_state_restore(const struct SmoothView3DState *sms_state,
- View3D *v3d,
- RegionView3D *rv3d)
-{
- copy_v3_v3(rv3d->ofs, sms_state->ofs);
- copy_qt_qt(rv3d->viewquat, sms_state->quat);
- rv3d->dist = sms_state->dist;
- v3d->lens = sms_state->lens;
-}
-
-/* will start timer if appropriate */
-void ED_view3d_smooth_view_ex(
- /* avoid passing in the context */
- const Depsgraph *depsgraph,
- wmWindowManager *wm,
- wmWindow *win,
- ScrArea *area,
- View3D *v3d,
- ARegion *region,
- const int smooth_viewtx,
- const V3D_SmoothParams *sview)
-{
- RegionView3D *rv3d = region->regiondata;
- struct SmoothView3DStore sms = {{0}};
-
- /* initialize sms */
- view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d);
- view3d_smooth_view_state_backup(&sms.src, v3d, rv3d);
- /* If smooth-view runs multiple times. */
- if (rv3d->sms == NULL) {
- view3d_smooth_view_state_backup(&sms.org, v3d, rv3d);
- }
- else {
- sms.org = rv3d->sms->org;
- }
- sms.org_view = rv3d->view;
-
- /* sms.to_camera = false; */ /* initialized to zero anyway */
-
- /* note on camera locking, this is a little confusing but works ok.
- * we may be changing the view 'as if' there is no active camera, but in fact
- * there is an active camera which is locked to the view.
- *
- * In the case where smooth view is moving _to_ a camera we don't want that
- * camera to be moved or changed, so only when the camera is not being set should
- * we allow camera option locking to initialize the view settings from the camera.
- */
- if (sview->camera == NULL && sview->camera_old == NULL) {
- ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
- }
-
- /* store the options we want to end with */
- if (sview->ofs) {
- copy_v3_v3(sms.dst.ofs, sview->ofs);
- }
- if (sview->quat) {
- copy_qt_qt(sms.dst.quat, sview->quat);
- }
- if (sview->dist) {
- sms.dst.dist = *sview->dist;
- }
- if (sview->lens) {
- sms.dst.lens = *sview->lens;
- }
-
- if (sview->dyn_ofs) {
- BLI_assert(sview->ofs == NULL);
- BLI_assert(sview->quat != NULL);
-
- copy_v3_v3(sms.dyn_ofs, sview->dyn_ofs);
- sms.use_dyn_ofs = true;
-
- /* calculate the final destination offset */
- view3d_orbit_apply_dyn_ofs(sms.dst.ofs, sms.src.ofs, sms.src.quat, sms.dst.quat, sms.dyn_ofs);
- }
-
- if (sview->camera) {
- Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera);
- if (sview->ofs != NULL) {
- sms.dst.dist = ED_view3d_offset_distance(
- ob_camera_eval->obmat, sview->ofs, VIEW3D_DIST_FALLBACK);
- }
- ED_view3d_from_object(ob_camera_eval, sms.dst.ofs, sms.dst.quat, &sms.dst.dist, &sms.dst.lens);
- sms.to_camera = true; /* restore view3d values in end */
- }
-
- if ((sview->camera_old == sview->camera) && /* Camera. */
- (sms.dst.dist == rv3d->dist) && /* Distance. */
- (sms.dst.lens == v3d->lens) && /* Lens. */
- equals_v3v3(sms.dst.ofs, rv3d->ofs) && /* Offset. */
- equals_v4v4(sms.dst.quat, rv3d->viewquat) /* Rotation. */
- ) {
- /* Early return if nothing changed. */
- return;
- }
-
- /* Skip smooth viewing for external render engine draw. */
- if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) {
-
- /* original values */
- if (sview->camera_old) {
- Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old);
- if (sview->ofs != NULL) {
- sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f);
- }
- ED_view3d_from_object(
- ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens);
- }
- /* grid draw as floor */
- if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
- /* use existing if exists, means multiple calls to smooth view
- * won't lose the original 'view' setting */
- rv3d->view = RV3D_VIEW_USER;
- }
-
- sms.time_allowed = (double)smooth_viewtx / 1000.0;
-
- /* If this is view rotation only we can decrease the time allowed by the angle between quats
- * this means small rotations won't lag. */
- if (sview->quat && !sview->ofs && !sview->dist) {
- /* scale the time allowed by the rotation */
- /* 180deg == 1.0 */
- sms.time_allowed *= (double)fabsf(angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) /
- M_PI;
- }
-
- /* ensure it shows correct */
- if (sms.to_camera) {
- /* use ortho if we move from an ortho view to an ortho camera */
- Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera);
- rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) &&
- (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ?
- RV3D_ORTHO :
- RV3D_PERSP);
- }
-
- rv3d->rflag |= RV3D_NAVIGATING;
-
- /* not essential but in some cases the caller will tag the area for redraw, and in that
- * case we can get a flicker of the 'org' user view but we want to see 'src' */
- view3d_smooth_view_state_restore(&sms.src, v3d, rv3d);
-
- /* keep track of running timer! */
- if (rv3d->sms == NULL) {
- rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d");
- }
- *rv3d->sms = sms;
- if (rv3d->smooth_timer) {
- WM_event_remove_timer(wm, win, rv3d->smooth_timer);
- }
- /* #TIMER1 is hard-coded in key-map. */
- rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0);
- }
- else {
- /* Animation is disabled, apply immediately. */
- if (sms.to_camera == false) {
- copy_v3_v3(rv3d->ofs, sms.dst.ofs);
- copy_qt_qt(rv3d->viewquat, sms.dst.quat);
- rv3d->dist = sms.dst.dist;
- v3d->lens = sms.dst.lens;
-
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- }
-
- if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
- view3d_boxview_copy(area, region);
- }
-
- ED_region_tag_redraw(region);
-
- WM_event_add_mousemove(win);
- }
-}
-
-void ED_view3d_smooth_view(bContext *C,
- View3D *v3d,
- ARegion *region,
- const int smooth_viewtx,
- const struct V3D_SmoothParams *sview)
-{
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- wmWindowManager *wm = CTX_wm_manager(C);
- wmWindow *win = CTX_wm_window(C);
- ScrArea *area = CTX_wm_area(C);
-
- ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview);
-}
-
-/* only meant for timer usage */
-static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview)
-{
- wmWindowManager *wm = CTX_wm_manager(C);
- RegionView3D *rv3d = region->regiondata;
- struct SmoothView3DStore *sms = rv3d->sms;
- float step, step_inv;
-
- if (sms->time_allowed != 0.0) {
- step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed);
- }
- else {
- step = 1.0f;
- }
-
- /* end timer */
- if (step >= 1.0f) {
- wmWindow *win = CTX_wm_window(C);
-
- /* if we went to camera, store the original */
- if (sms->to_camera) {
- rv3d->persp = RV3D_CAMOB;
- view3d_smooth_view_state_restore(&sms->org, v3d, rv3d);
- }
- else {
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
-
- view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d);
-
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
- }
-
- if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
- rv3d->view = sms->org_view;
- }
-
- MEM_freeN(rv3d->sms);
- rv3d->sms = NULL;
-
- WM_event_remove_timer(wm, win, rv3d->smooth_timer);
- rv3d->smooth_timer = NULL;
- rv3d->rflag &= ~RV3D_NAVIGATING;
-
- /* Event handling won't know if a UI item has been moved under the pointer. */
- WM_event_add_mousemove(win);
- }
- else {
- /* ease in/out */
- step = (3.0f * step * step - 2.0f * step * step * step);
-
- step_inv = 1.0f - step;
-
- interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step);
-
- if (sms->use_dyn_ofs) {
- view3d_orbit_apply_dyn_ofs(
- rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs);
- }
- else {
- interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step);
- }
-
- rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv;
- v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv;
-
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- if (ED_screen_animation_playing(wm)) {
- ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
- }
- }
-
- if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) {
- view3d_boxview_copy(CTX_wm_area(C), region);
- }
-
- /* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636,
- * when switching camera in quad-view the other ortho views would zoom & reset.
- *
- * For now only redraw all regions when smooth-view finishes.
- */
- if (step >= 1.0f) {
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
- }
- else {
- ED_region_tag_redraw(region);
- }
-}
-
-static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
-{
- View3D *v3d = CTX_wm_view3d(C);
- ARegion *region = CTX_wm_region(C);
- RegionView3D *rv3d = region->regiondata;
-
- /* escape if not our timer */
- if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) {
- return OPERATOR_PASS_THROUGH;
- }
-
- view3d_smoothview_apply(C, v3d, region, true);
-
- return OPERATOR_FINISHED;
-}
-
-void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region)
-{
- RegionView3D *rv3d = region->regiondata;
-
- if (rv3d && rv3d->sms) {
- rv3d->sms->time_allowed = 0.0; /* force finishing */
- view3d_smoothview_apply(C, v3d, region, false);
-
- /* force update of view matrix so tools that run immediately after
- * can use them without redrawing first */
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene = CTX_data_scene(C);
- ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false);
- }
-}
-
-void VIEW3D_OT_smoothview(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Smooth View";
- ot->idname = "VIEW3D_OT_smoothview";
-
- /* api callbacks */
- ot->invoke = view3d_smoothview_invoke;
-
- /* flags */
- ot->flag = OPTYPE_INTERNAL;
-
- ot->poll = ED_operator_view3d_active;
-}
-
-/** \} */
+#include "view3d_navigate.h"
/* -------------------------------------------------------------------- */
/** \name Camera to View Operator
@@ -863,10 +485,10 @@ void view3d_opengl_select_cache_end(void)
struct DrawSelectLoopUserData {
uint pass;
uint hits;
- uint *buffer;
+ GPUSelectResult *buffer;
uint buffer_len;
const rcti *rect;
- char gpu_select_mode;
+ eGPUSelectMode gpu_select_mode;
};
static bool drw_select_loop_pass(eDRWSelectStage stage, void *user_data)
@@ -927,8 +549,8 @@ static bool drw_select_filter_object_mode_lock_for_weight_paint(Object *ob, void
}
int view3d_opengl_select_ex(ViewContext *vc,
- uint *buffer,
- uint bufsize,
+ GPUSelectResult *buffer,
+ uint buffer_len,
const rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter,
@@ -950,7 +572,7 @@ int view3d_opengl_select_ex(ViewContext *vc,
const bool use_nearest = (is_pick_select && select_mode == VIEW3D_SELECT_PICK_NEAREST);
bool draw_surface = true;
- char gpu_select_mode;
+ eGPUSelectMode gpu_select_mode;
/* case not a box select */
if (input->xmin == input->xmax) {
@@ -981,6 +603,15 @@ int view3d_opengl_select_ex(ViewContext *vc,
}
}
+ /* Re-use cache (rect must be smaller than the cached)
+ * other context is assumed to be unchanged */
+ if (GPU_select_is_cached()) {
+ GPU_select_begin(buffer, buffer_len, &rect, gpu_select_mode, 0);
+ GPU_select_cache_load_id();
+ hits = GPU_select_end();
+ goto finally;
+ }
+
/* Important to use 'vc->obact', not 'OBACT(vc->view_layer)' below,
* so it will be NULL when hidden. */
struct {
@@ -1040,15 +671,6 @@ int view3d_opengl_select_ex(ViewContext *vc,
UI_Theme_Store(&theme_state);
UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
- /* Re-use cache (rect must be smaller than the cached)
- * other context is assumed to be unchanged */
- if (GPU_select_is_cached()) {
- GPU_select_begin(buffer, bufsize, &rect, gpu_select_mode, 0);
- GPU_select_cache_load_id();
- hits = GPU_select_end();
- goto finally;
- }
-
/* All of the queries need to be perform on the drawing context. */
DRW_opengl_context_enable();
@@ -1071,7 +693,7 @@ int view3d_opengl_select_ex(ViewContext *vc,
.pass = 0,
.hits = 0,
.buffer = buffer,
- .buffer_len = bufsize,
+ .buffer_len = buffer_len,
.rect = &rect,
.gpu_select_mode = gpu_select_mode,
};
@@ -1101,7 +723,7 @@ int view3d_opengl_select_ex(ViewContext *vc,
.pass = 0,
.hits = 0,
.buffer = buffer,
- .buffer_len = bufsize,
+ .buffer_len = buffer_len,
.rect = &rect,
.gpu_select_mode = gpu_select_mode,
};
@@ -1132,36 +754,36 @@ int view3d_opengl_select_ex(ViewContext *vc,
DRW_opengl_context_disable();
+ UI_Theme_Restore(&theme_state);
+
finally:
if (hits < 0) {
printf("Too many objects in select buffer\n"); /* XXX make error message */
}
- UI_Theme_Restore(&theme_state);
-
return hits;
}
int view3d_opengl_select(ViewContext *vc,
- uint *buffer,
- uint bufsize,
+ GPUSelectResult *buffer,
+ uint buffer_len,
const rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter)
{
- return view3d_opengl_select_ex(vc, buffer, bufsize, input, select_mode, select_filter, false);
+ return view3d_opengl_select_ex(vc, buffer, buffer_len, input, select_mode, select_filter, false);
}
int view3d_opengl_select_with_id_filter(ViewContext *vc,
- uint *buffer,
- uint bufsize,
+ GPUSelectResult *buffer,
+ const uint buffer_len,
const rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter,
uint select_id)
{
- int hits = view3d_opengl_select(vc, buffer, bufsize, input, select_mode, select_filter);
+ int hits = view3d_opengl_select(vc, buffer, buffer_len, input, select_mode, select_filter);
/* Selection sometimes uses -1 for an invalid selection ID, remove these as they
* interfere with detection of actual number of hits in the selection. */
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index 64a720322c1..09c53d7196c 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -125,9 +125,5 @@ set(LIB
bf_gpu
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_transform "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 642de550812..8d91f90ea29 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -580,11 +580,11 @@ typedef struct TransInfo {
/** Mouse side of the current frame, 'L', 'R' or 'B' */
char frame_side;
- /** copy from G.vd, prevents feedback. */
+ /** copy from #RegionView3D, prevents feedback. */
float viewmat[4][4];
/** and to make sure we don't have to. */
float viewinv[4][4];
- /** access G.vd from other space types. */
+ /** Access #RegionView3D from other space types. */
float persmat[4][4];
float persinv[4][4];
short persp;
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index c40f3c28a79..90f78d4abf1 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -124,10 +124,8 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t);
* Sets transform flags in the bones.
* Returns total number of bones with #BONE_TRANSFORM.
*/
-int transform_convert_pose_transflags_update(Object *ob,
- int mode,
- short around,
- bool has_translate_rotate[2]);
+void transform_convert_pose_transflags_update(Object *ob, int mode, short around);
+
/**
* When objects array is NULL, use 't->data_container' as is.
*/
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 5d0a3bd9dd1..04a8d462924 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -739,9 +739,43 @@ void createTransPose(TransInfo *t)
const bool mirror = ((pose->flag & POSE_MIRROR_EDIT) != 0);
- /* set flags and count total */
- tc->data_len = transform_convert_pose_transflags_update(
- ob, t->mode, t->around, has_translate_rotate);
+ /* Set flags. */
+ transform_convert_pose_transflags_update(ob, t->mode, t->around);
+
+ /* Now count, and check if we have autoIK or have to switch from translate to rotate. */
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ Bone *bone = pchan->bone;
+ if (!(bone->flag & BONE_TRANSFORM)) {
+ continue;
+ }
+
+ tc->data_len++;
+
+ if (has_translate_rotate != NULL) {
+ if (has_translate_rotate[0] && has_translate_rotate[1]) {
+ continue;
+ }
+
+ if (has_targetless_ik(pchan) == NULL) {
+ if (pchan->parent && (bone->flag & BONE_CONNECTED)) {
+ if (bone->flag & BONE_HINGE_CHILD_TRANSFORM) {
+ has_translate_rotate[0] = true;
+ }
+ }
+ else {
+ if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) {
+ has_translate_rotate[0] = true;
+ }
+ }
+ if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) {
+ has_translate_rotate[1] = true;
+ }
+ }
+ else {
+ has_translate_rotate[0] = true;
+ }
+ }
+ }
if (tc->data_len == 0) {
continue;
@@ -1499,15 +1533,11 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb)
}
}
-int transform_convert_pose_transflags_update(Object *ob,
- const int mode,
- const short around,
- bool has_translate_rotate[2])
+void transform_convert_pose_transflags_update(Object *ob, const int mode, const short around)
{
bArmature *arm = ob->data;
bPoseChannel *pchan;
Bone *bone;
- int total = 0;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
bone = pchan->bone;
@@ -1537,36 +1567,6 @@ int transform_convert_pose_transflags_update(Object *ob,
}
}
}
- /* now count, and check if we have autoIK or have to switch from translate to rotate */
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- bone = pchan->bone;
- if (bone->flag & BONE_TRANSFORM) {
- total++;
-
- if (has_translate_rotate != NULL) {
- if (has_targetless_ik(pchan) == NULL) {
- if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) {
- if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) {
- has_translate_rotate[0] = true;
- }
- }
- else {
- if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) {
- has_translate_rotate[0] = true;
- }
- }
- if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) {
- has_translate_rotate[1] = true;
- }
- }
- else {
- has_translate_rotate[0] = true;
- }
- }
- }
- }
-
- return total;
}
static short apply_targetless_ik(Object *ob)
@@ -1733,7 +1733,7 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t)
/* Set BONE_TRANSFORM flags for auto-key, gizmo draw might have changed them. */
if (!canceled && (t->mode != TFM_DUMMY)) {
- transform_convert_pose_transflags_update(ob, t->mode, t->around, NULL);
+ transform_convert_pose_transflags_update(ob, t->mode, t->around);
}
/* if target-less IK grabbing, we calculate the pchan transforms and clear flag */
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 55f2bfd37db..e00522f0f88 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -1650,7 +1650,7 @@ void createTransEditVerts(TransInfo *t)
else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
float *bweight = (cd_vert_bweight_offset != -1) ?
BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) :
- (cd_vert_crease_offset != -1) ?
+ (cd_vert_crease_offset != -1) ?
BM_ELEM_CD_GET_VOID_P(eve, cd_vert_crease_offset) :
NULL;
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 9bd55d78039..c0572478481 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -953,32 +953,25 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
- const bool use_mat_local = (ob_iter != ob);
- bPoseChannel *pchan;
-
+ const bool use_mat_local = params->use_local_axis && (ob_iter != ob);
/* mislead counting bones... bah. We don't know the gizmo mode, could be mixed */
const int mode = TFM_ROTATION;
- const int totsel_iter = transform_convert_pose_transflags_update(
- ob_iter, mode, V3D_AROUND_CENTER_BOUNDS, NULL);
+ transform_convert_pose_transflags_update(ob_iter, mode, V3D_AROUND_CENTER_BOUNDS);
- if (totsel_iter) {
- float mat_local[4][4];
- if (params->use_local_axis) {
- if (use_mat_local) {
- mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat);
- }
- }
+ float mat_local[4][4];
+ if (use_mat_local) {
+ mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat);
+ }
- /* use channels to get stats */
- for (pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) {
- Bone *bone = pchan->bone;
- if (bone && (bone->flag & BONE_TRANSFORM)) {
- calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local);
- protectflag_to_drawflags_pchan(rv3d, pchan, orient_index);
- }
+ /* Use channels to get stats. */
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ if (!(pchan->bone->flag & BONE_TRANSFORM)) {
+ continue;
}
- totsel += totsel_iter;
+ calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local);
+ protectflag_to_drawflags_pchan(rv3d, pchan, orient_index);
+ totsel++;
}
}
MEM_freeN(objects);
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index 7a0f2743a98..223f2511c72 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -195,7 +195,7 @@ static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, const float plane_no[3], flo
/* allow some overlap to avoid missing the intersection because of float precision */
if ((fac > -FLT_EPSILON) && (fac < 1.0f + FLT_EPSILON)) {
/* likelihood of multiple intersections per ngon is quite low,
- * it would have to loop back on its self, but better support it
+ * it would have to loop back on itself, but better support it
* so check for the closest opposite edge */
const float tdist = len_v3v3(l_tmp->v->co, tvec);
if (tdist < dist) {
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index e96c43e0d02..9dc8b6cf69d 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -153,19 +153,21 @@ static Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *
return NULL;
}
- BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
- if ((edit_mode_type == SNAP_GEOM_FINAL) && em_eval->mesh_eval_final) {
- if (em_eval->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval);
+ Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval);
+
+ if ((edit_mode_type == SNAP_GEOM_FINAL) && editmesh_eval_final) {
+ if (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
return NULL;
}
- me_eval = em_eval->mesh_eval_final;
+ me_eval = editmesh_eval_final;
use_hide = true;
}
- else if ((edit_mode_type == SNAP_GEOM_CAGE) && em_eval->mesh_eval_cage) {
- if (em_eval->mesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ else if ((edit_mode_type == SNAP_GEOM_CAGE) && editmesh_eval_cage) {
+ if (editmesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
return NULL;
}
- me_eval = em_eval->mesh_eval_cage;
+ me_eval = editmesh_eval_cage;
use_hide = true;
}
}
@@ -345,12 +347,14 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx,
static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob_eval)
{
- BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
- if (em_eval->mesh_eval_final) {
- return &em_eval->mesh_eval_final->runtime;
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval);
+ if (editmesh_eval_final) {
+ return &editmesh_eval_final->runtime;
}
- if (em_eval->mesh_eval_cage) {
- return &em_eval->mesh_eval_cage->runtime;
+
+ Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval);
+ if (editmesh_eval_cage) {
+ return &editmesh_eval_cage->runtime;
}
return &((Mesh *)ob_eval->data)->runtime;
diff --git a/source/blender/editors/undo/CMakeLists.txt b/source/blender/editors/undo/CMakeLists.txt
index 0f4152c9128..6f659e383fe 100644
--- a/source/blender/editors/undo/CMakeLists.txt
+++ b/source/blender/editors/undo/CMakeLists.txt
@@ -46,8 +46,4 @@ set(LIB
bf_editor_physics
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_undo "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 90a09c87cc6..66cda0fc3f8 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -115,10 +115,6 @@ set(LIB
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
list(APPEND INC
diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c
index 705dfff7260..b9e90670a4d 100644
--- a/source/blender/editors/util/ed_transverts.c
+++ b/source/blender/editors/util/ed_transverts.c
@@ -41,6 +41,7 @@
#include "BKE_editmesh.h"
#include "BKE_lattice.h"
#include "BKE_mesh_iterators.h"
+#include "BKE_object.h"
#include "DEG_depsgraph.h"
@@ -194,12 +195,12 @@ static void set_mapped_co(void *vuserdata, int index, const float co[3], const f
}
}
-bool ED_transverts_check_obedit(Object *obedit)
+bool ED_transverts_check_obedit(const Object *obedit)
{
return (ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL));
}
-void ED_transverts_create_from_obedit(TransVertStore *tvs, Object *obedit, const int mode)
+void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, const int mode)
{
Nurb *nu;
BezTriple *bezt;
@@ -213,7 +214,7 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, Object *obedit, const
tvs->transverts_tot = 0;
if (obedit->type == OB_MESH) {
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMEditMesh *em = BKE_editmesh_from_object((Object *)obedit);
BMesh *bm = em->bm;
BMIter iter;
void *userdata[2] = {em, NULL};
@@ -311,9 +312,13 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, Object *obedit, const
userdata[1] = tvs->transverts;
}
- if (tvs->transverts && em->mesh_eval_cage) {
- BM_mesh_elem_table_ensure(bm, BM_VERT);
- BKE_mesh_foreach_mapped_vert(em->mesh_eval_cage, set_mapped_co, userdata, MESH_FOREACH_NOP);
+ if (mode & TM_CALC_MAPLOC) {
+ struct Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(obedit);
+ if (tvs->transverts && editmesh_eval_cage) {
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+ BKE_mesh_foreach_mapped_vert(
+ editmesh_eval_cage, set_mapped_co, userdata, MESH_FOREACH_NOP);
+ }
}
}
else if (obedit->type == OB_ARMATURE) {
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 882f140c063..e86392e47ab 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -35,6 +35,7 @@
#include "BKE_collection.h"
#include "BKE_global.h"
+#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_multires.h"
@@ -46,6 +47,8 @@
#include "DEG_depsgraph.h"
+#include "DNA_gpencil_types.h"
+
#include "ED_armature.h"
#include "ED_asset.h"
#include "ED_image.h"
@@ -116,6 +119,10 @@ void ED_editors_init(bContext *C)
/* For multi-edit mode we may already have mode data (grease pencil does not need it).
* However we may have a non-active object stuck in a grease-pencil edit mode. */
if (ob != obact) {
+ bGPdata *gpd = (bGPdata *)ob->data;
+ gpd->flag &= ~(GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE |
+ GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE |
+ GP_DATA_STROKE_VERTEXMODE);
ob->mode = OB_MODE_OBJECT;
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
@@ -434,11 +441,27 @@ void unpack_menu(bContext *C,
UI_popup_menu_end(C, pup);
}
-void ED_spacedata_id_remap(struct ScrArea *area, struct SpaceLink *sl, ID *old_id, ID *new_id)
+void ED_spacedata_id_remap(struct ScrArea *area,
+ struct SpaceLink *sl,
+ const struct IDRemapper *mappings)
+{
+ SpaceType *st = BKE_spacetype_from_id(sl->spacetype);
+ if (st && st->id_remap) {
+ st->id_remap(area, sl, mappings);
+ }
+}
+
+void ED_spacedata_id_remap_single(struct ScrArea *area,
+ struct SpaceLink *sl,
+ ID *old_id,
+ ID *new_id)
{
SpaceType *st = BKE_spacetype_from_id(sl->spacetype);
if (st && st->id_remap) {
- st->id_remap(area, sl, old_id, new_id);
+ struct IDRemapper *mappings = BKE_id_remapper_create();
+ BKE_id_remapper_add(mappings, old_id, new_id);
+ st->id_remap(area, sl, mappings);
+ BKE_id_remapper_free(mappings);
}
}
diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc
index a1b17d799bc..ae37dab7bb4 100644
--- a/source/blender/editors/util/ed_util_ops.cc
+++ b/source/blender/editors/util/ed_util_ops.cc
@@ -121,6 +121,22 @@ static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot)
FILE_SORT_DEFAULT);
}
+static bool lib_id_generate_preview_poll(bContext *C)
+{
+ if (!lib_id_preview_editing_poll(C)) {
+ return false;
+ }
+
+ const PointerRNA idptr = CTX_data_pointer_get(C, "id");
+ const ID *id = (ID *)idptr.data;
+ if (GS(id->name) == ID_NT) {
+ CTX_wm_operator_poll_msg_set(C, TIP_("Can't generate automatic preview for node group"));
+ return false;
+ }
+
+ return true;
+}
+
static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op))
{
PointerRNA idptr = CTX_data_pointer_get(C, "id");
@@ -148,13 +164,57 @@ static void ED_OT_lib_id_generate_preview(wmOperatorType *ot)
ot->idname = "ED_OT_lib_id_generate_preview";
/* api callbacks */
- ot->poll = lib_id_preview_editing_poll;
+ ot->poll = lib_id_generate_preview_poll;
ot->exec = lib_id_generate_preview_exec;
/* flags */
ot->flag = OPTYPE_INTERNAL | OPTYPE_REGISTER | OPTYPE_UNDO;
}
+static bool lib_id_generate_preview_from_object_poll(bContext *C)
+{
+ if (!lib_id_preview_editing_poll(C)) {
+ return false;
+ }
+ if (CTX_data_active_object(C) == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+static int lib_id_generate_preview_from_object_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA idptr = CTX_data_pointer_get(C, "id");
+ ID *id = (ID *)idptr.data;
+
+ ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
+
+ Object *object_to_render = CTX_data_active_object(C);
+
+ BKE_previewimg_id_free(id);
+ PreviewImage *preview_image = BKE_previewimg_id_ensure(id);
+ UI_icon_render_id_ex(C, nullptr, &object_to_render->id, ICON_SIZE_PREVIEW, true, preview_image);
+
+ WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr);
+ ED_assetlist_storage_tag_main_data_dirty();
+
+ return OPERATOR_FINISHED;
+}
+
+static void ED_OT_lib_id_generate_preview_from_object(wmOperatorType *ot)
+{
+ ot->name = "Generate Preview from Object";
+ ot->description = "Create a preview for this asset by rendering the active object";
+ ot->idname = "ED_OT_lib_id_generate_preview_from_object";
+
+ /* api callbacks */
+ ot->poll = lib_id_generate_preview_from_object_poll;
+ ot->exec = lib_id_generate_preview_from_object_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_INTERNAL | OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -280,6 +340,7 @@ void ED_operatortypes_edutils()
{
WM_operatortype_append(ED_OT_lib_id_load_custom_preview);
WM_operatortype_append(ED_OT_lib_id_generate_preview);
+ WM_operatortype_append(ED_OT_lib_id_generate_preview_from_object);
WM_operatortype_append(ED_OT_lib_id_fake_user_toggle);
WM_operatortype_append(ED_OT_lib_id_unlink);
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt
index 1c8a56e0608..a3b29f29354 100644
--- a/source/blender/editors/uvedit/CMakeLists.txt
+++ b/source/blender/editors/uvedit/CMakeLists.txt
@@ -52,9 +52,5 @@ set(LIB
bf_bmesh
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_editor_uvedit "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/freestyle/CMakeLists.txt b/source/blender/freestyle/CMakeLists.txt
index d16787714c9..948e68e52da 100644
--- a/source/blender/freestyle/CMakeLists.txt
+++ b/source/blender/freestyle/CMakeLists.txt
@@ -583,10 +583,6 @@ if(WITH_PYTHON_SAFETY)
add_definitions(-DWITH_PYTHON_SAFETY)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WIN32)
list(APPEND INC_SYS
${PTHREADS_INC}
diff --git a/source/blender/freestyle/intern/geometry/matrix_util.cpp b/source/blender/freestyle/intern/geometry/matrix_util.cpp
index 95a24d85677..c917e9c87fd 100644
--- a/source/blender/freestyle/intern/geometry/matrix_util.cpp
+++ b/source/blender/freestyle/intern/geometry/matrix_util.cpp
@@ -15,14 +15,7 @@
*
* The Original Code is:
* GXML/Graphite: Geometry and Graphics Programming Library + Utilities
- * Copyright (C) 2000 Bruno Levy
- * Contact: Bruno Levy
- * <levy@loria.fr>
- * ISA Project
- * LORIA, INRIA Lorraine,
- * Campus Scientifique, BP 239
- * 54506 VANDOEUVRE LES NANCY CEDEX
- * FRANCE
+ * Copyright 2000 Bruno Levy <levy@loria.fr>
*/
/** \file
diff --git a/source/blender/freestyle/intern/geometry/matrix_util.h b/source/blender/freestyle/intern/geometry/matrix_util.h
index 8c2eb799d13..147e25e9608 100644
--- a/source/blender/freestyle/intern/geometry/matrix_util.h
+++ b/source/blender/freestyle/intern/geometry/matrix_util.h
@@ -15,14 +15,7 @@
*
* The Original Code is:
* GXML/Graphite: Geometry and Graphics Programming Library + Utilities
- * Copyright (C) 2000 Bruno Levy
- * Contact: Bruno Levy
- * <levy@loria.fr>
- * ISA Project
- * LORIA, INRIA Lorraine,
- * Campus Scientifique, BP 239
- * 54506 VANDOEUVRE LES NANCY CEDEX
- * FRANCE
+ * Copyright 2000 Bruno Levy <levy@loria.fr>
*/
#pragma once
diff --git a/source/blender/freestyle/intern/geometry/normal_cycle.cpp b/source/blender/freestyle/intern/geometry/normal_cycle.cpp
index 77a80e63b77..fbcbeb220df 100644
--- a/source/blender/freestyle/intern/geometry/normal_cycle.cpp
+++ b/source/blender/freestyle/intern/geometry/normal_cycle.cpp
@@ -15,14 +15,7 @@
*
* The Original Code is:
* OGF/Graphite: Geometry and Graphics Programming Library + Utilities
- * Copyright (C) 2000 Bruno Levy
- * Contact: Bruno Levy
- * <levy@loria.fr>
- * ISA Project
- * LORIA, INRIA Lorraine,
- * Campus Scientifique, BP 239
- * 54506 VANDOEUVRE LES NANCY CEDEX
- * FRANCE
+ * Copyright 2000 Bruno Levy <levy@loria.fr>
*/
/** \file
diff --git a/source/blender/freestyle/intern/geometry/normal_cycle.h b/source/blender/freestyle/intern/geometry/normal_cycle.h
index 949675e9d8d..1fb59bcad19 100644
--- a/source/blender/freestyle/intern/geometry/normal_cycle.h
+++ b/source/blender/freestyle/intern/geometry/normal_cycle.h
@@ -15,14 +15,7 @@
*
* The Original Code is:
* OGF/Graphite: Geometry and Graphics Programming Library + Utilities
- * Copyright (C) 2000 Bruno Levy
- * Contact: Bruno Levy
- * <levy@loria.fr>
- * ISA Project
- * LORIA, INRIA Lorraine,
- * Campus Scientifique, BP 239
- * 54506 VANDOEUVRE LES NANCY CEDEX
- * FRANCE
+ * Copyright 2000 Bruno Levy <levy@loria.fr>
*/
#pragma once
diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.cpp b/source/blender/freestyle/intern/winged_edge/Curvature.cpp
index ec07a124808..571f21ac309 100644
--- a/source/blender/freestyle/intern/winged_edge/Curvature.cpp
+++ b/source/blender/freestyle/intern/winged_edge/Curvature.cpp
@@ -18,13 +18,7 @@
* Copyright (C) 1999 Stephane Popinet
* and:
* OGF/Graphite: Geometry and Graphics Programming Library + Utilities
- * Copyright (C) 2000-2003 Bruno Levy
- * Contact: Bruno Levy <levy@loria.fr>
- * ISA Project
- * LORIA, INRIA Lorraine,
- * Campus Scientifique, BP 239
- * 54506 VANDOEUVRE LES NANCY CEDEX
- * FRANCE
+ * Copyright 2000-2003 Bruno Levy <levy@loria.fr>
*/
/** \file
diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.h b/source/blender/freestyle/intern/winged_edge/Curvature.h
index acbe4e8daf6..c87134a0c14 100644
--- a/source/blender/freestyle/intern/winged_edge/Curvature.h
+++ b/source/blender/freestyle/intern/winged_edge/Curvature.h
@@ -15,16 +15,10 @@
*
* The Original Code is:
* GTS - Library for the manipulation of triangulated surfaces
- * Copyright (C) 1999 Stephane Popinet
+ * Copyright 1999 Stephane Popinet
* and:
* OGF/Graphite: Geometry and Graphics Programming Library + Utilities
- * Copyright (C) 2000-2003 Bruno Levy
- * Contact: Bruno Levy <levy@loria.fr>
- * ISA Project
- * LORIA, INRIA Lorraine,
- * Campus Scientifique, BP 239
- * 54506 VANDOEUVRE LES NANCY CEDEX
- * FRANCE
+ * Copyright (C) 2000-2003 Bruno Levy <levy@loria.fr>
*/
#pragma once
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index e869927c33b..e42feac1644 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -87,8 +87,7 @@ class FieldNode {
public:
FieldNode(FieldNodeType node_type);
-
- virtual ~FieldNode() = default;
+ virtual ~FieldNode();
virtual const CPPType &output_cpp_type(int output_index) const = 0;
@@ -230,6 +229,7 @@ class FieldOperation : public FieldNode {
public:
FieldOperation(std::shared_ptr<const MultiFunction> function, Vector<GField> inputs = {});
FieldOperation(const MultiFunction &function, Vector<GField> inputs = {});
+ ~FieldOperation();
Span<GField> inputs() const;
const MultiFunction &multi_function() const;
@@ -259,6 +259,7 @@ class FieldInput : public FieldNode {
public:
FieldInput(const CPPType &type, std::string debug_name = "");
+ ~FieldInput();
/**
* Get the value of this specific input based on the given context. The returned virtual array,
diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc
index 0bbfbc8cb10..7f1eb8bc1a7 100644
--- a/source/blender/functions/intern/cpp_types.cc
+++ b/source/blender/functions/intern/cpp_types.cc
@@ -31,6 +31,7 @@ MAKE_CPP_TYPE(float3, blender::float3, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(float4x4, blender::float4x4, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(int32, int32_t, CPPTypeFlags::BasicType)
+MAKE_CPP_TYPE(int8, int8_t, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(uint32, uint32_t, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(uint8, uint8_t, CPPTypeFlags::BasicType)
@@ -44,6 +45,7 @@ MAKE_FIELD_CPP_TYPE(Float2Field, float2);
MAKE_FIELD_CPP_TYPE(Float3Field, float3);
MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f);
MAKE_FIELD_CPP_TYPE(BoolField, bool);
+MAKE_FIELD_CPP_TYPE(Int8Field, int8_t);
MAKE_FIELD_CPP_TYPE(Int32Field, int32_t);
MAKE_FIELD_CPP_TYPE(StringField, std::string);
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index d6b83c42294..fe3041b8602 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -571,6 +571,13 @@ bool IndexFieldInput::is_equal_to(const fn::FieldNode &other) const
}
/* --------------------------------------------------------------------
+ * FieldNode.
+ */
+
+/* Avoid generating the destructor in every translation unit. */
+FieldNode::~FieldNode() = default;
+
+/* --------------------------------------------------------------------
* FieldOperation.
*/
@@ -581,6 +588,9 @@ FieldOperation::FieldOperation(std::shared_ptr<const MultiFunction> function,
owned_function_ = std::move(function);
}
+/* Avoid generating the destructor in every translation unit. */
+FieldOperation::~FieldOperation() = default;
+
/**
* Returns the field inputs used by all the provided fields.
* This tries to reuse an existing #FieldInputs whenever possible to avoid copying it.
@@ -655,6 +665,9 @@ FieldInput::FieldInput(const CPPType &type, std::string debug_name)
field_inputs_ = std::move(field_inputs);
}
+/* Avoid generating the destructor in every translation unit. */
+FieldInput::~FieldInput() = default;
+
/* --------------------------------------------------------------------
* FieldConstant.
*/
diff --git a/source/blender/functions/intern/multi_function_procedure_optimization.cc b/source/blender/functions/intern/multi_function_procedure_optimization.cc
index f220c85e535..9ad428dcbd8 100644
--- a/source/blender/functions/intern/multi_function_procedure_optimization.cc
+++ b/source/blender/functions/intern/multi_function_procedure_optimization.cc
@@ -81,7 +81,7 @@ void move_destructs_up(MFProcedure &procedure, MFInstruction &block_end_instr)
const MFInstructionCursor &prev_cursor = prev_cursors[0];
current_instr = prev_cursor.instruction();
if (current_instr == nullptr) {
- /* Stop when there is no previous instruction. E.g. when this is the first instruction. */
+ /* Stop when there is no previous instruction. E.g. when this is the first instruction. */
break;
}
}
diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt
index de508ddc540..110092e4f5f 100644
--- a/source/blender/geometry/CMakeLists.txt
+++ b/source/blender/geometry/CMakeLists.txt
@@ -14,7 +14,7 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
-# The Original Code is Copyright (C) 2006, Blender Foundation
+# The Original Code is Copyright (C) 2006 Blender Foundation
# All rights reserved.
set(INC
@@ -30,10 +30,14 @@ set(INC
)
set(SRC
+ intern/mesh_merge_by_distance.cc
intern/mesh_to_curve_convert.cc
+ intern/point_merge_by_distance.cc
intern/realize_instances.cc
+ GEO_mesh_merge_by_distance.hh
GEO_mesh_to_curve.hh
+ GEO_point_merge_by_distance.hh
GEO_realize_instances.hh
)
diff --git a/source/blender/geometry/GEO_mesh_merge_by_distance.hh b/source/blender/geometry/GEO_mesh_merge_by_distance.hh
new file mode 100644
index 00000000000..1d64680a02b
--- /dev/null
+++ b/source/blender/geometry/GEO_mesh_merge_by_distance.hh
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "BLI_index_mask.hh"
+#include "BLI_span.hh"
+
+struct Mesh;
+
+/** \file
+ * \ingroup geo
+ */
+
+namespace blender::geometry {
+
+/**
+ * Merge selected vertices into other selected vertices within the \a merge_distance. The merged
+ * indices favor speed over accuracy, since the results will depend on the order of the vertices.
+ *
+ * \returns #std::nullopt if the mesh should not be changed (no vertices are merged), in order to
+ * avoid copying the input. Otherwise returns the new mesh with merged geometry.
+ */
+std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh,
+ IndexMask selection,
+ float merge_distance);
+
+/**
+ * Merge selected vertices along edges to other selected vertices. Only vertices connected by edges
+ * are considered for merging.
+ *
+ * \returns #std::nullopt if the mesh should not be changed (no vertices are merged), in order to
+ * avoid copying the input. Otherwise returns the new mesh with merged geometry.
+ */
+std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
+ Span<bool> selection,
+ float merge_distance,
+ bool only_loose_edges);
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_point_merge_by_distance.hh b/source/blender/geometry/GEO_point_merge_by_distance.hh
new file mode 100644
index 00000000000..6766f3c559d
--- /dev/null
+++ b/source/blender/geometry/GEO_point_merge_by_distance.hh
@@ -0,0 +1,38 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_index_mask.hh"
+
+#pragma once
+
+struct PointCloud;
+class PointCloudComponent;
+
+/** \file
+ * \ingroup geo
+ */
+
+namespace blender::geometry {
+
+/**
+ * Merge selected points into other selected points within the \a merge_distance. The merged
+ * indices favor speed over accuracy, since the results will depend on the order of the points.
+ */
+PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
+ const float merge_distance,
+ const IndexMask selection);
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc
new file mode 100644
index 00000000000..1a07ebf31f6
--- /dev/null
+++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc
@@ -0,0 +1,1729 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_index_mask.hh"
+#include "BLI_kdtree.h"
+#include "BLI_math_vector.hh"
+#include "BLI_vector.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+
+#include "GEO_mesh_merge_by_distance.hh"
+
+//#define USE_WELD_DEBUG
+//#define USE_WELD_NORMALS
+
+namespace blender::geometry {
+
+/* Indicates when the element was not computed. */
+#define OUT_OF_CONTEXT (int)(-1)
+/* Indicates if the edge or face will be collapsed. */
+#define ELEM_COLLAPSED (int)(-2)
+/* indicates whether an edge or vertex in groups_map will be merged. */
+#define ELEM_MERGED (int)(-2)
+
+/* Used to indicate a range in an array specifying a group. */
+struct WeldGroup {
+ int len;
+ int ofs;
+};
+
+/* Edge groups that will be merged. Final vertices are also indicated. */
+struct WeldGroupEdge {
+ struct WeldGroup group;
+ int v1;
+ int v2;
+};
+
+struct WeldVert {
+ /* Indexes relative to the original Mesh. */
+ int vert_dest;
+ int vert_orig;
+};
+
+struct WeldEdge {
+ union {
+ int flag;
+ struct {
+ /* Indexes relative to the original Mesh. */
+ int edge_dest;
+ int edge_orig;
+ int vert_a;
+ int vert_b;
+ };
+ };
+};
+
+struct WeldLoop {
+ union {
+ int flag;
+ struct {
+ /* Indexes relative to the original Mesh. */
+ int vert;
+ int edge;
+ int loop_orig;
+ int loop_skip_to;
+ };
+ };
+};
+
+struct WeldPoly {
+ union {
+ int flag;
+ struct {
+ /* Indexes relative to the original Mesh. */
+ int poly_dst;
+ int poly_orig;
+ int loop_start;
+ int loop_end;
+ /* Final Polygon Size. */
+ int len;
+ /* Group of loops that will be affected. */
+ struct WeldGroup loops;
+ };
+ };
+};
+
+struct WeldMesh {
+ /* Group of vertices to be merged. */
+ Array<WeldGroup> vert_groups;
+ Array<int> vert_groups_buffer;
+
+ /* Group of edges to be merged. */
+ Array<WeldGroupEdge> edge_groups;
+ Array<int> edge_groups_buffer;
+ /* From the original index of the vertex, this indicates which group it is or is going to be
+ * merged. */
+ Array<int> edge_groups_map;
+
+ /* References all polygons and loops that will be affected. */
+ Vector<WeldLoop> wloop;
+ Vector<WeldPoly> wpoly;
+ WeldPoly *wpoly_new;
+ int wloop_len;
+ int wpoly_len;
+ int wpoly_new_len;
+
+ /* From the actual index of the element in the mesh, it indicates what is the index of the Weld
+ * element above. */
+ Array<int> loop_map;
+ Array<int> poly_map;
+
+ int vert_kill_len;
+ int edge_kill_len;
+ int loop_kill_len;
+ int poly_kill_len; /* Including the new polygons. */
+
+ /* Size of the affected polygon with more sides. */
+ int max_poly_len;
+};
+
+struct WeldLoopOfPolyIter {
+ int loop_start;
+ int loop_end;
+ Span<WeldLoop> wloop;
+ Span<MLoop> mloop;
+ Span<int> loop_map;
+ /* Weld group. */
+ int *group;
+
+ int l_curr;
+ int l_next;
+
+ /* Return */
+ int group_len;
+ int v;
+ int e;
+ char type;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name Debug Utils
+ * \{ */
+
+#ifdef USE_WELD_DEBUG
+static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter,
+ const WeldPoly &wp,
+ Span<WeldLoop> wloop,
+ Span<MLoop> mloop,
+ Span<int> loop_map,
+ int *group_buffer);
+
+static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter);
+
+static void weld_assert_edge_kill_len(Span<WeldEdge> wedge, const int supposed_kill_len)
+{
+ int kills = 0;
+ const WeldEdge *we = &wedge[0];
+ for (int i = wedge.size(); i--; we++) {
+ int edge_dest = we->edge_dest;
+ /* Magically includes collapsed edges. */
+ if (edge_dest != OUT_OF_CONTEXT) {
+ kills++;
+ }
+ }
+ BLI_assert(kills == supposed_kill_len);
+}
+
+static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly,
+ Span<WeldPoly> wpoly_new,
+ Span<WeldLoop> wloop,
+ Span<MLoop> mloop,
+ Span<int> loop_map,
+ Span<int> poly_map,
+ Span<MPoly> mpoly,
+ const int supposed_poly_kill_len,
+ const int supposed_loop_kill_len)
+{
+ int poly_kills = 0;
+ int loop_kills = mloop.size();
+ const MPoly *mp = &mpoly[0];
+ for (int i = 0; i < mpoly.size(); i++, mp++) {
+ int poly_ctx = poly_map[i];
+ if (poly_ctx != OUT_OF_CONTEXT) {
+ const WeldPoly *wp = &wpoly[poly_ctx];
+ WeldLoopOfPolyIter iter;
+ if (!weld_iter_loop_of_poly_begin(&iter, *wp, wloop, mloop, loop_map, nullptr)) {
+ poly_kills++;
+ continue;
+ }
+ else {
+ if (wp->poly_dst != OUT_OF_CONTEXT) {
+ poly_kills++;
+ continue;
+ }
+ int remain = wp->len;
+ int l = wp->loop_start;
+ while (remain) {
+ int l_next = l + 1;
+ int loop_ctx = loop_map[l];
+ if (loop_ctx != OUT_OF_CONTEXT) {
+ const WeldLoop *wl = &wloop[loop_ctx];
+ if (wl->loop_skip_to != OUT_OF_CONTEXT) {
+ l_next = wl->loop_skip_to;
+ }
+ if (wl->flag != ELEM_COLLAPSED) {
+ loop_kills--;
+ remain--;
+ }
+ }
+ else {
+ loop_kills--;
+ remain--;
+ }
+ l = l_next;
+ }
+ }
+ }
+ else {
+ loop_kills -= mp->totloop;
+ }
+ }
+
+ const WeldPoly *wp = wpoly_new.data();
+ for (int i = wpoly_new.size(); i--; wp++) {
+ if (wp->poly_dst != OUT_OF_CONTEXT) {
+ poly_kills++;
+ continue;
+ }
+ int remain = wp->len;
+ int l = wp->loop_start;
+ while (remain) {
+ int l_next = l + 1;
+ int loop_ctx = loop_map[l];
+ if (loop_ctx != OUT_OF_CONTEXT) {
+ const WeldLoop *wl = &wloop[loop_ctx];
+ if (wl->loop_skip_to != OUT_OF_CONTEXT) {
+ l_next = wl->loop_skip_to;
+ }
+ if (wl->flag != ELEM_COLLAPSED) {
+ loop_kills--;
+ remain--;
+ }
+ }
+ else {
+ loop_kills--;
+ remain--;
+ }
+ l = l_next;
+ }
+ }
+
+ BLI_assert(poly_kills == supposed_poly_kill_len);
+ BLI_assert(loop_kills == supposed_loop_kill_len);
+}
+
+static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp,
+ Span<WeldLoop> wloop,
+ Span<MLoop> mloop,
+ Span<int> loop_map)
+{
+ const int len = wp.len;
+ Array<int, 64> verts(len);
+ WeldLoopOfPolyIter iter;
+ if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) {
+ return;
+ }
+ else {
+ int i = 0;
+ while (weld_iter_loop_of_poly_next(iter)) {
+ verts[i++] = iter.v;
+ }
+ }
+ for (int i = 0; i < len; i++) {
+ int va = verts[i];
+ for (int j = i + 1; j < len; j++) {
+ int vb = verts[j];
+ BLI_assert(va != vb);
+ }
+ }
+}
+
+static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop)
+{
+ if (wp->flag == ELEM_COLLAPSED) {
+ return;
+ }
+
+ int len = wp->len;
+ const WeldLoop *wl = &wloop[wp->loops.ofs];
+ BLI_assert(wp->loop_start <= wl->loop_orig);
+
+ int end_wloop = wp->loops.ofs + wp->loops.len;
+ const WeldLoop *wl_end = &wloop[end_wloop - 1];
+
+ int min_len = 0;
+ for (; wl <= wl_end; wl++) {
+ BLI_assert(wl->loop_skip_to == OUT_OF_CONTEXT); /* Not for this case. */
+ if (wl->flag != ELEM_COLLAPSED) {
+ min_len++;
+ }
+ }
+ BLI_assert(len >= min_len);
+
+ int max_len = wp->loop_end - wp->loop_start + 1;
+ BLI_assert(len <= max_len);
+}
+
+#endif /* USE_WELD_DEBUG */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vert API
+ * \{ */
+
+static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map,
+ const int vert_kill_len)
+{
+ Vector<WeldVert> wvert;
+ wvert.reserve(std::min<int>(2 * vert_kill_len, vert_dest_map.size()));
+
+ for (const int i : vert_dest_map.index_range()) {
+ if (vert_dest_map[i] != OUT_OF_CONTEXT) {
+ WeldVert wv{};
+ wv.vert_dest = vert_dest_map[i];
+ wv.vert_orig = i;
+ wvert.append(wv);
+ }
+ }
+ return wvert;
+}
+
+static void weld_vert_groups_setup(Span<WeldVert> wvert,
+ Span<int> vert_dest_map,
+ MutableSpan<int> r_vert_groups_map,
+ Array<int> &r_vert_groups_buffer,
+ Array<WeldGroup> &r_vert_groups)
+{
+ /* Get weld vert groups. */
+
+ int wgroups_len = 0;
+ for (const int i : vert_dest_map.index_range()) {
+ const int vert_dest = vert_dest_map[i];
+ if (vert_dest != OUT_OF_CONTEXT) {
+ if (vert_dest != i) {
+ r_vert_groups_map[i] = ELEM_MERGED;
+ }
+ else {
+ r_vert_groups_map[i] = wgroups_len;
+ wgroups_len++;
+ }
+ }
+ else {
+ r_vert_groups_map[i] = OUT_OF_CONTEXT;
+ }
+ }
+
+ r_vert_groups.reinitialize(wgroups_len);
+ r_vert_groups.fill({0, 0});
+ MutableSpan<WeldGroup> wgroups = r_vert_groups;
+
+ for (const WeldVert &wv : wvert) {
+ int group_index = r_vert_groups_map[wv.vert_dest];
+ wgroups[group_index].len++;
+ }
+
+ int ofs = 0;
+ for (WeldGroup &wg : wgroups) {
+ wg.ofs = ofs;
+ ofs += wg.len;
+ }
+
+ BLI_assert(ofs == wvert.size());
+
+ r_vert_groups_buffer.reinitialize(ofs);
+ for (const WeldVert &wv : wvert) {
+ int group_index = r_vert_groups_map[wv.vert_dest];
+ r_vert_groups_buffer[wgroups[group_index].ofs++] = wv.vert_orig;
+ }
+
+ for (WeldGroup &wg : wgroups) {
+ wg.ofs -= wg.len;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Edge API
+ * \{ */
+
+static Vector<WeldEdge> weld_edge_ctx_alloc(Span<MEdge> medge,
+ Span<int> vert_dest_map,
+ MutableSpan<int> r_edge_dest_map,
+ MutableSpan<int> r_edge_ctx_map)
+{
+ /* Edge Context. */
+ int wedge_len = 0;
+
+ Vector<WeldEdge> wedge;
+ wedge.reserve(medge.size());
+
+ for (const int i : medge.index_range()) {
+ int v1 = medge[i].v1;
+ int v2 = medge[i].v2;
+ int v_dest_1 = vert_dest_map[v1];
+ int v_dest_2 = vert_dest_map[v2];
+ if ((v_dest_1 != OUT_OF_CONTEXT) || (v_dest_2 != OUT_OF_CONTEXT)) {
+ WeldEdge we{};
+ we.vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1;
+ we.vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2;
+ we.edge_dest = OUT_OF_CONTEXT;
+ we.edge_orig = i;
+ wedge.append(we);
+ r_edge_dest_map[i] = i;
+ r_edge_ctx_map[i] = wedge_len++;
+ }
+ else {
+ r_edge_dest_map[i] = OUT_OF_CONTEXT;
+ r_edge_ctx_map[i] = OUT_OF_CONTEXT;
+ }
+ }
+
+ return wedge;
+}
+
+static void weld_edge_ctx_setup(MutableSpan<WeldGroup> r_vlinks,
+ MutableSpan<int> r_edge_dest_map,
+ MutableSpan<WeldEdge> r_wedge,
+ int *r_edge_kiil_len)
+{
+ /* Setup Edge Overlap. */
+ int edge_kill_len = 0;
+
+ MutableSpan<WeldGroup> v_links = r_vlinks;
+
+ for (WeldEdge &we : r_wedge) {
+ int dst_vert_a = we.vert_a;
+ int dst_vert_b = we.vert_b;
+
+ if (dst_vert_a == dst_vert_b) {
+ BLI_assert(we.edge_dest == OUT_OF_CONTEXT);
+ r_edge_dest_map[we.edge_orig] = ELEM_COLLAPSED;
+ we.flag = ELEM_COLLAPSED;
+ edge_kill_len++;
+ continue;
+ }
+
+ v_links[dst_vert_a].len++;
+ v_links[dst_vert_b].len++;
+ }
+
+ int link_len = 0;
+ for (WeldGroup &vl : r_vlinks) {
+ vl.ofs = link_len;
+ link_len += vl.len;
+ }
+
+ if (link_len > 0) {
+ Array<int> link_edge_buffer(link_len);
+
+ for (const int i : r_wedge.index_range()) {
+ const WeldEdge &we = r_wedge[i];
+ if (we.flag == ELEM_COLLAPSED) {
+ continue;
+ }
+
+ int dst_vert_a = we.vert_a;
+ int dst_vert_b = we.vert_b;
+
+ link_edge_buffer[v_links[dst_vert_a].ofs++] = i;
+ link_edge_buffer[v_links[dst_vert_b].ofs++] = i;
+ }
+
+ for (WeldGroup &vl : r_vlinks) {
+ /* Fix offset */
+ vl.ofs -= vl.len;
+ }
+
+ for (const int i : r_wedge.index_range()) {
+ const WeldEdge &we = r_wedge[i];
+ if (we.edge_dest != OUT_OF_CONTEXT) {
+ /* No need to retest edges.
+ * (Already includes collapsed edges). */
+ continue;
+ }
+
+ int dst_vert_a = we.vert_a;
+ int dst_vert_b = we.vert_b;
+
+ struct WeldGroup *link_a = &v_links[dst_vert_a];
+ struct WeldGroup *link_b = &v_links[dst_vert_b];
+
+ int edges_len_a = link_a->len;
+ int edges_len_b = link_b->len;
+
+ if (edges_len_a <= 1 || edges_len_b <= 1) {
+ continue;
+ }
+
+ int *edges_ctx_a = &link_edge_buffer[link_a->ofs];
+ int *edges_ctx_b = &link_edge_buffer[link_b->ofs];
+ int edge_orig = we.edge_orig;
+
+ for (; edges_len_a--; edges_ctx_a++) {
+ int e_ctx_a = *edges_ctx_a;
+ if (e_ctx_a == i) {
+ continue;
+ }
+ while (edges_len_b && *edges_ctx_b < e_ctx_a) {
+ edges_ctx_b++;
+ edges_len_b--;
+ }
+ if (edges_len_b == 0) {
+ break;
+ }
+ int e_ctx_b = *edges_ctx_b;
+ if (e_ctx_a == e_ctx_b) {
+ WeldEdge *we_b = &r_wedge[e_ctx_b];
+ BLI_assert(ELEM(we_b->vert_a, dst_vert_a, dst_vert_b));
+ BLI_assert(ELEM(we_b->vert_b, dst_vert_a, dst_vert_b));
+ BLI_assert(we_b->edge_dest == OUT_OF_CONTEXT);
+ BLI_assert(we_b->edge_orig != edge_orig);
+ r_edge_dest_map[we_b->edge_orig] = edge_orig;
+ we_b->edge_dest = edge_orig;
+ edge_kill_len++;
+ }
+ }
+ }
+
+#ifdef USE_WELD_DEBUG
+ weld_assert_edge_kill_len(r_wedge, edge_kill_len);
+#endif
+ }
+
+ *r_edge_kiil_len = edge_kill_len;
+}
+
+static void weld_edge_groups_setup(const int medge_len,
+ const int edge_kill_len,
+ MutableSpan<WeldEdge> wedge,
+ Span<int> wedge_map,
+ MutableSpan<int> r_edge_groups_map,
+ Array<int> &r_edge_groups_buffer,
+ Array<WeldGroupEdge> &r_edge_groups)
+{
+ /* Get weld edge groups. */
+ int wgroups_len = wedge.size() - edge_kill_len;
+ r_edge_groups.reinitialize(wgroups_len);
+ r_edge_groups.fill({{0}});
+ MutableSpan<WeldGroupEdge> wegroups = r_edge_groups;
+
+ wgroups_len = 0;
+ for (const int i : IndexRange(medge_len)) {
+ int edge_ctx = wedge_map[i];
+ if (edge_ctx != OUT_OF_CONTEXT) {
+ WeldEdge *we = &wedge[edge_ctx];
+ int edge_dest = we->edge_dest;
+ if (edge_dest != OUT_OF_CONTEXT) {
+ BLI_assert(edge_dest != we->edge_orig);
+ r_edge_groups_map[i] = ELEM_MERGED;
+ }
+ else {
+ we->edge_dest = we->edge_orig;
+ wegroups[wgroups_len].v1 = we->vert_a;
+ wegroups[wgroups_len].v2 = we->vert_b;
+ r_edge_groups_map[i] = wgroups_len;
+ wgroups_len++;
+ }
+ }
+ else {
+ r_edge_groups_map[i] = OUT_OF_CONTEXT;
+ }
+ }
+
+ BLI_assert(wgroups_len == wedge.size() - edge_kill_len);
+
+ if (wgroups_len == 0) {
+ /* All edges in the context are collapsed. */
+ return;
+ }
+
+ for (const WeldEdge &we : wedge) {
+ if (we.flag == ELEM_COLLAPSED) {
+ continue;
+ }
+ int group_index = r_edge_groups_map[we.edge_dest];
+ wegroups[group_index].group.len++;
+ }
+
+ int ofs = 0;
+ for (WeldGroupEdge &wegrp : wegroups) {
+ wegrp.group.ofs = ofs;
+ ofs += wegrp.group.len;
+ }
+
+ r_edge_groups_buffer.reinitialize(ofs);
+ for (const WeldEdge &we : wedge) {
+ if (we.flag == ELEM_COLLAPSED) {
+ continue;
+ }
+ int group_index = r_edge_groups_map[we.edge_dest];
+ r_edge_groups_buffer[wegroups[group_index].group.ofs++] = we.edge_orig;
+ }
+
+ for (WeldGroupEdge &wegrp : wegroups) {
+ wegrp.group.ofs -= wegrp.group.len;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Poly and Loop API
+ * \{ */
+
+static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter &iter,
+ const WeldPoly &wp,
+ Span<WeldLoop> wloop,
+ Span<MLoop> mloop,
+ Span<int> loop_map,
+ int *group_buffer)
+{
+ if (wp.flag == ELEM_COLLAPSED) {
+ return false;
+ }
+
+ iter.loop_start = wp.loop_start;
+ iter.loop_end = wp.loop_end;
+ iter.wloop = wloop;
+ iter.mloop = mloop;
+ iter.loop_map = loop_map;
+ iter.group = group_buffer;
+
+ int group_len = 0;
+ if (group_buffer) {
+ /* First loop group needs more attention. */
+ int loop_start, loop_end, l;
+ loop_start = iter.loop_start;
+ loop_end = l = iter.loop_end;
+ while (l >= loop_start) {
+ const int loop_ctx = loop_map[l];
+ if (loop_ctx != OUT_OF_CONTEXT) {
+ const WeldLoop *wl = &wloop[loop_ctx];
+ if (wl->flag == ELEM_COLLAPSED) {
+ l--;
+ continue;
+ }
+ }
+ break;
+ }
+ if (l != loop_end) {
+ group_len = loop_end - l;
+ int i = 0;
+ while (l < loop_end) {
+ iter.group[i++] = ++l;
+ }
+ }
+ }
+ iter.group_len = group_len;
+
+ iter.l_next = iter.loop_start;
+#ifdef USE_WELD_DEBUG
+ iter.v = OUT_OF_CONTEXT;
+#endif
+ return true;
+}
+
+static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter)
+{
+ const int loop_end = iter.loop_end;
+ Span<WeldLoop> wloop = iter.wloop;
+ Span<int> loop_map = iter.loop_map;
+ int l = iter.l_curr = iter.l_next;
+ if (l == iter.loop_start) {
+ /* `grupo_len` is already calculated in the first loop */
+ }
+ else {
+ iter.group_len = 0;
+ }
+ while (l <= loop_end) {
+ int l_next = l + 1;
+ const int loop_ctx = loop_map[l];
+ if (loop_ctx != OUT_OF_CONTEXT) {
+ const WeldLoop *wl = &wloop[loop_ctx];
+ if (wl->loop_skip_to != OUT_OF_CONTEXT) {
+ l_next = wl->loop_skip_to;
+ }
+ if (wl->flag == ELEM_COLLAPSED) {
+ if (iter.group) {
+ iter.group[iter.group_len++] = l;
+ }
+ l = l_next;
+ continue;
+ }
+#ifdef USE_WELD_DEBUG
+ BLI_assert(iter.v != wl->vert);
+#endif
+ iter.v = wl->vert;
+ iter.e = wl->edge;
+ iter.type = 1;
+ }
+ else {
+ const MLoop &ml = iter.mloop[l];
+#ifdef USE_WELD_DEBUG
+ BLI_assert((uint)iter.v != ml.v);
+#endif
+ iter.v = ml.v;
+ iter.e = ml.e;
+ iter.type = 0;
+ }
+ if (iter.group) {
+ iter.group[iter.group_len++] = l;
+ }
+ iter.l_next = l_next;
+ return true;
+ }
+
+ return false;
+}
+
+static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly,
+ Span<MLoop> mloop,
+ Span<int> vert_dest_map,
+ Span<int> edge_dest_map,
+ WeldMesh *r_weld_mesh)
+{
+ /* Loop/Poly Context. */
+ Array<int> loop_map(mloop.size());
+ Array<int> poly_map(mpoly.size());
+ int wloop_len = 0;
+ int wpoly_len = 0;
+ int max_ctx_poly_len = 4;
+
+ Vector<WeldLoop> wloop;
+ wloop.reserve(mloop.size());
+
+ Vector<WeldPoly> wpoly;
+ wpoly.reserve(mpoly.size());
+
+ int maybe_new_poly = 0;
+
+ for (const int i : mpoly.index_range()) {
+ const MPoly &mp = mpoly[i];
+ const int loopstart = mp.loopstart;
+ const int totloop = mp.totloop;
+
+ int vert_ctx_len = 0;
+
+ int prev_wloop_len = wloop_len;
+ for (const int i_loop : mloop.index_range().slice(loopstart, totloop)) {
+ int v = mloop[i_loop].v;
+ int e = mloop[i_loop].e;
+ int v_dest = vert_dest_map[v];
+ int e_dest = edge_dest_map[e];
+ bool is_vert_ctx = v_dest != OUT_OF_CONTEXT;
+ bool is_edge_ctx = e_dest != OUT_OF_CONTEXT;
+ if (is_vert_ctx) {
+ vert_ctx_len++;
+ }
+ if (is_vert_ctx || is_edge_ctx) {
+ WeldLoop wl{};
+ wl.vert = is_vert_ctx ? v_dest : v;
+ wl.edge = is_edge_ctx ? e_dest : e;
+ wl.loop_orig = i_loop;
+ wl.loop_skip_to = OUT_OF_CONTEXT;
+ wloop.append(wl);
+
+ loop_map[i_loop] = wloop_len++;
+ }
+ else {
+ loop_map[i_loop] = OUT_OF_CONTEXT;
+ }
+ }
+ if (wloop_len != prev_wloop_len) {
+ int loops_len = wloop_len - prev_wloop_len;
+ WeldPoly wp{};
+ wp.poly_dst = OUT_OF_CONTEXT;
+ wp.poly_orig = i;
+ wp.loops.len = loops_len;
+ wp.loops.ofs = prev_wloop_len;
+ wp.loop_start = loopstart;
+ wp.loop_end = loopstart + totloop - 1;
+ wp.len = totloop;
+ wpoly.append(wp);
+
+ poly_map[i] = wpoly_len++;
+ if (totloop > 5 && vert_ctx_len > 1) {
+ int max_new = (totloop / 3) - 1;
+ vert_ctx_len /= 2;
+ maybe_new_poly += MIN2(max_new, vert_ctx_len);
+ CLAMP_MIN(max_ctx_poly_len, totloop);
+ }
+ }
+ else {
+ poly_map[i] = OUT_OF_CONTEXT;
+ }
+ }
+
+ if (mpoly.size() < (wpoly_len + maybe_new_poly)) {
+ wpoly.resize(wpoly_len + maybe_new_poly);
+ }
+
+ WeldPoly *poly_new = wpoly.data() + wpoly_len;
+
+ r_weld_mesh->wloop = std::move(wloop);
+ r_weld_mesh->wpoly = std::move(wpoly);
+ r_weld_mesh->wpoly_new = poly_new;
+ r_weld_mesh->wloop_len = wloop_len;
+ r_weld_mesh->wpoly_len = wpoly_len;
+ r_weld_mesh->wpoly_new_len = 0;
+ r_weld_mesh->loop_map = std::move(loop_map);
+ r_weld_mesh->poly_map = std::move(poly_map);
+ r_weld_mesh->max_poly_len = max_ctx_poly_len;
+}
+
+static void weld_poly_split_recursive(Span<int> vert_dest_map,
+#ifdef USE_WELD_DEBUG
+ const Span<MLoop> mloop,
+#endif
+ int ctx_verts_len,
+ WeldPoly *r_wp,
+ WeldMesh *r_weld_mesh,
+ int *r_poly_kill,
+ int *r_loop_kill)
+{
+ int poly_len = r_wp->len;
+ if (poly_len > 3 && ctx_verts_len > 1) {
+ const int ctx_loops_len = r_wp->loops.len;
+ const int ctx_loops_ofs = r_wp->loops.ofs;
+ MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
+ WeldPoly *wpoly_new = r_weld_mesh->wpoly_new;
+
+ int loop_kill = 0;
+
+ WeldLoop *poly_loops = &wloop[ctx_loops_ofs];
+ WeldLoop *wla = &poly_loops[0];
+ WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1];
+ while (wla_prev->flag == ELEM_COLLAPSED) {
+ wla_prev--;
+ }
+ const int la_len = ctx_loops_len - 1;
+ for (int la = 0; la < la_len; la++, wla++) {
+ wa_continue:
+ if (wla->flag == ELEM_COLLAPSED) {
+ continue;
+ }
+ int vert_a = wla->vert;
+ /* Only test vertices that will be merged. */
+ if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) {
+ int lb = la + 1;
+ WeldLoop *wlb = wla + 1;
+ WeldLoop *wlb_prev = wla;
+ int killed_ab = 0;
+ ctx_verts_len = 1;
+ for (; lb < ctx_loops_len; lb++, wlb++) {
+ BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT);
+ if (wlb->flag == ELEM_COLLAPSED) {
+ killed_ab++;
+ continue;
+ }
+ int vert_b = wlb->vert;
+ if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) {
+ ctx_verts_len++;
+ }
+ if (vert_a == vert_b) {
+ const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab;
+ const int dist_b = poly_len - dist_a;
+
+ BLI_assert(dist_a != 0 && dist_b != 0);
+ if (dist_a == 1 || dist_b == 1) {
+ BLI_assert(dist_a != dist_b);
+ BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED));
+ }
+ else {
+ WeldLoop *wl_tmp = nullptr;
+ if (dist_a == 2) {
+ wl_tmp = wlb_prev;
+ BLI_assert(wla->flag != ELEM_COLLAPSED);
+ BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
+ wla->flag = ELEM_COLLAPSED;
+ wl_tmp->flag = ELEM_COLLAPSED;
+ loop_kill += 2;
+ poly_len -= 2;
+ }
+ if (dist_b == 2) {
+ if (wl_tmp != nullptr) {
+ r_wp->flag = ELEM_COLLAPSED;
+ *r_poly_kill += 1;
+ }
+ else {
+ wl_tmp = wla_prev;
+ BLI_assert(wlb->flag != ELEM_COLLAPSED);
+ BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
+ wlb->flag = ELEM_COLLAPSED;
+ wl_tmp->flag = ELEM_COLLAPSED;
+ }
+ loop_kill += 2;
+ poly_len -= 2;
+ }
+ if (wl_tmp == nullptr) {
+ const int new_loops_len = lb - la;
+ const int new_loops_ofs = ctx_loops_ofs + la;
+
+ WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++];
+ new_wp->poly_dst = OUT_OF_CONTEXT;
+ new_wp->poly_orig = r_wp->poly_orig;
+ new_wp->loops.len = new_loops_len;
+ new_wp->loops.ofs = new_loops_ofs;
+ new_wp->loop_start = wla->loop_orig;
+ new_wp->loop_end = wlb_prev->loop_orig;
+ new_wp->len = dist_a;
+ weld_poly_split_recursive(vert_dest_map,
+#ifdef USE_WELD_DEBUG
+ mloop,
+#endif
+ ctx_verts_len,
+ new_wp,
+ r_weld_mesh,
+ r_poly_kill,
+ r_loop_kill);
+ BLI_assert(dist_b == poly_len - dist_a);
+ poly_len = dist_b;
+ if (wla_prev->loop_orig > wla->loop_orig) {
+ /* New start. */
+ r_wp->loop_start = wlb->loop_orig;
+ }
+ else {
+ /* The `loop_start` doesn't change but some loops must be skipped. */
+ wla_prev->loop_skip_to = wlb->loop_orig;
+ }
+ wla = wlb;
+ la = lb;
+ goto wa_continue;
+ }
+ break;
+ }
+ }
+ if (wlb->flag != ELEM_COLLAPSED) {
+ wlb_prev = wlb;
+ }
+ }
+ }
+ if (wla->flag != ELEM_COLLAPSED) {
+ wla_prev = wla;
+ }
+ }
+ r_wp->len = poly_len;
+ *r_loop_kill += loop_kill;
+
+#ifdef USE_WELD_DEBUG
+ weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map);
+#endif
+ }
+}
+
+static void weld_poly_loop_ctx_setup(Span<MLoop> mloop,
+#ifdef USE_WELD_DEBUG
+ Span<MPoly> mpoly,
+#endif
+ const int mvert_len,
+ Span<int> vert_dest_map,
+ const int remain_edge_ctx_len,
+ MutableSpan<WeldGroup> r_vlinks,
+ WeldMesh *r_weld_mesh)
+{
+ MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly;
+ MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
+ WeldPoly *wpoly_new = r_weld_mesh->wpoly_new;
+ int wpoly_len = r_weld_mesh->wpoly_len;
+ int wpoly_new_len = 0;
+ int poly_kill_len = 0;
+ int loop_kill_len = 0;
+
+ Span<int> loop_map = r_weld_mesh->loop_map;
+
+ if (remain_edge_ctx_len) {
+
+ /* Setup Poly/Loop. Note that `wpoly_len` may be different than `wpoly.size()` here. */
+ for (const int i : IndexRange(wpoly_len)) {
+ WeldPoly &wp = wpoly[i];
+ const int ctx_loops_len = wp.loops.len;
+ const int ctx_loops_ofs = wp.loops.ofs;
+
+ int poly_len = wp.len;
+ int ctx_verts_len = 0;
+ WeldLoop *wl = &wloop[ctx_loops_ofs];
+ for (int l = ctx_loops_len; l--; wl++) {
+ const int edge_dest = wl->edge;
+ if (edge_dest == ELEM_COLLAPSED) {
+ wl->flag = ELEM_COLLAPSED;
+ if (poly_len == 3) {
+ wp.flag = ELEM_COLLAPSED;
+ poly_kill_len++;
+ loop_kill_len += 3;
+ poly_len = 0;
+ break;
+ }
+ loop_kill_len++;
+ poly_len--;
+ }
+ else {
+ const int vert_dst = wl->vert;
+ if (vert_dest_map[vert_dst] != OUT_OF_CONTEXT) {
+ ctx_verts_len++;
+ }
+ }
+ }
+
+ if (poly_len) {
+ wp.len = poly_len;
+#ifdef USE_WELD_DEBUG
+ weld_assert_poly_len(wp, wloop);
+#endif
+
+ weld_poly_split_recursive(vert_dest_map,
+#ifdef USE_WELD_DEBUG
+ mloop,
+#endif
+ ctx_verts_len,
+ &wp,
+ r_weld_mesh,
+ &poly_kill_len,
+ &loop_kill_len);
+
+ wpoly_new_len = r_weld_mesh->wpoly_new_len;
+ }
+ }
+
+#ifdef USE_WELD_DEBUG
+ weld_assert_poly_and_loop_kill_len(wpoly,
+ {wpoly_new, wpoly_new_len},
+ wloop,
+ mloop,
+ loop_map,
+ r_weld_mesh->poly_map,
+ mpoly,
+ poly_kill_len,
+ loop_kill_len);
+#endif
+
+ /* Setup Polygon Overlap. */
+
+ const int wpoly_and_new_len = wpoly_len + wpoly_new_len;
+
+ r_vlinks.fill({0, 0});
+ MutableSpan<WeldGroup> v_links = r_vlinks;
+
+ for (const int i : IndexRange(wpoly_and_new_len)) {
+ const WeldPoly &wp = wpoly[i];
+ WeldLoopOfPolyIter iter;
+ if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) {
+ while (weld_iter_loop_of_poly_next(iter)) {
+ v_links[iter.v].len++;
+ }
+ }
+ }
+
+ int link_len = 0;
+ for (const int i : IndexRange(mvert_len)) {
+ v_links[i].ofs = link_len;
+ link_len += v_links[i].len;
+ }
+
+ if (link_len) {
+ Array<int> link_poly_buffer(link_len);
+
+ for (const int i : IndexRange(wpoly_and_new_len)) {
+ const WeldPoly &wp = wpoly[i];
+ WeldLoopOfPolyIter iter;
+ if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) {
+ while (weld_iter_loop_of_poly_next(iter)) {
+ link_poly_buffer[v_links[iter.v].ofs++] = i;
+ }
+ }
+ }
+
+ for (WeldGroup &vl : r_vlinks) {
+ /* Fix offset */
+ vl.ofs -= vl.len;
+ }
+
+ int polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b;
+ polys_len_b = p_ctx_b = 0; /* silence warnings */
+
+ for (const int i : IndexRange(wpoly_and_new_len)) {
+ const WeldPoly &wp = wpoly[i];
+ if (wp.poly_dst != OUT_OF_CONTEXT) {
+ /* No need to retest poly.
+ * (Already includes collapsed polygons). */
+ continue;
+ }
+
+ WeldLoopOfPolyIter iter;
+ weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr);
+ weld_iter_loop_of_poly_next(iter);
+ struct WeldGroup *link_a = &v_links[iter.v];
+ polys_len_a = link_a->len;
+ if (polys_len_a == 1) {
+ BLI_assert(link_poly_buffer[link_a->ofs] == i);
+ continue;
+ }
+ int wp_len = wp.len;
+ polys_ctx_a = &link_poly_buffer[link_a->ofs];
+ for (; polys_len_a--; polys_ctx_a++) {
+ p_ctx_a = *polys_ctx_a;
+ if (p_ctx_a == i) {
+ continue;
+ }
+
+ WeldPoly *wp_tmp = &wpoly[p_ctx_a];
+ if (wp_tmp->len != wp_len) {
+ continue;
+ }
+
+ WeldLoopOfPolyIter iter_b = iter;
+ while (weld_iter_loop_of_poly_next(iter_b)) {
+ struct WeldGroup *link_b = &v_links[iter_b.v];
+ polys_len_b = link_b->len;
+ if (polys_len_b == 1) {
+ BLI_assert(link_poly_buffer[link_b->ofs] == i);
+ polys_len_b = 0;
+ break;
+ }
+
+ polys_ctx_b = &link_poly_buffer[link_b->ofs];
+ for (; polys_len_b; polys_len_b--, polys_ctx_b++) {
+ p_ctx_b = *polys_ctx_b;
+ if (p_ctx_b < p_ctx_a) {
+ continue;
+ }
+ if (p_ctx_b >= p_ctx_a) {
+ if (p_ctx_b > p_ctx_a) {
+ polys_len_b = 0;
+ }
+ break;
+ }
+ }
+ if (polys_len_b == 0) {
+ break;
+ }
+ }
+ if (polys_len_b == 0) {
+ continue;
+ }
+ BLI_assert(p_ctx_a > i);
+ BLI_assert(p_ctx_a == p_ctx_b);
+ BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT);
+ BLI_assert(wp_tmp != &wp);
+ wp_tmp->poly_dst = wp.poly_orig;
+ loop_kill_len += wp_tmp->len;
+ poly_kill_len++;
+ }
+ }
+ }
+ }
+ else {
+ poly_kill_len = r_weld_mesh->wpoly_len;
+ loop_kill_len = r_weld_mesh->wloop_len;
+
+ for (WeldPoly &wp : wpoly) {
+ wp.flag = ELEM_COLLAPSED;
+ }
+ }
+
+#ifdef USE_WELD_DEBUG
+ weld_assert_poly_and_loop_kill_len(wpoly,
+ {wpoly_new, wpoly_new_len},
+ wloop,
+ mloop,
+ loop_map,
+ r_weld_mesh->poly_map,
+ mpoly,
+ poly_kill_len,
+ loop_kill_len);
+#endif
+
+ r_weld_mesh->wpoly_new = wpoly_new;
+ r_weld_mesh->poly_kill_len = poly_kill_len;
+ r_weld_mesh->loop_kill_len = loop_kill_len;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh API
+ * \{ */
+
+static void weld_mesh_context_create(const Mesh &mesh,
+ MutableSpan<int> vert_dest_map,
+ const int vert_kill_len,
+ WeldMesh *r_weld_mesh)
+{
+ Span<MEdge> medge{mesh.medge, mesh.totedge};
+ Span<MPoly> mpoly{mesh.mpoly, mesh.totpoly};
+ Span<MLoop> mloop{mesh.mloop, mesh.totloop};
+ const int mvert_len = mesh.totvert;
+
+ Vector<WeldVert> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map, vert_kill_len);
+ r_weld_mesh->vert_kill_len = vert_kill_len;
+
+ Array<int> edge_dest_map(medge.size());
+ Array<int> edge_ctx_map(medge.size());
+ Vector<WeldEdge> wedge = weld_edge_ctx_alloc(medge, vert_dest_map, edge_dest_map, edge_ctx_map);
+
+ Array<WeldGroup> v_links(mvert_len, {0, 0});
+ weld_edge_ctx_setup(v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len);
+
+ weld_poly_loop_ctx_alloc(mpoly, mloop, vert_dest_map, edge_dest_map, r_weld_mesh);
+
+ weld_poly_loop_ctx_setup(mloop,
+#ifdef USE_WELD_DEBUG
+ mpoly,
+
+#endif
+ mvert_len,
+ vert_dest_map,
+ wedge.size() - r_weld_mesh->edge_kill_len,
+ v_links,
+ r_weld_mesh);
+
+ weld_vert_groups_setup(wvert,
+ vert_dest_map,
+ vert_dest_map,
+ r_weld_mesh->vert_groups_buffer,
+ r_weld_mesh->vert_groups);
+
+ weld_edge_groups_setup(medge.size(),
+ r_weld_mesh->edge_kill_len,
+ wedge,
+ edge_ctx_map,
+ edge_dest_map,
+ r_weld_mesh->edge_groups_buffer,
+ r_weld_mesh->edge_groups);
+
+ r_weld_mesh->edge_groups_map = std::move(edge_dest_map);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData
+ * \{ */
+
+static void customdata_weld(
+ const CustomData *source, CustomData *dest, const int *src_indices, int count, int dest_index)
+{
+ if (count == 1) {
+ CustomData_copy_data(source, dest, src_indices[0], dest_index, 1);
+ return;
+ }
+
+ CustomData_interp(source, dest, (const int *)src_indices, nullptr, nullptr, count, dest_index);
+
+ int src_i, dest_i;
+ int j;
+
+ float co[3] = {0.0f, 0.0f, 0.0f};
+#ifdef USE_WELD_NORMALS
+ float no[3] = {0.0f, 0.0f, 0.0f};
+#endif
+ int crease = 0;
+ int bweight = 0;
+ short flag = 0;
+
+ /* interpolates a layer at a time */
+ dest_i = 0;
+ for (src_i = 0; src_i < source->totlayer; src_i++) {
+ const int type = source->layers[src_i].type;
+
+ /* find the first dest layer with type >= the source type
+ * (this should work because layers are ordered by type)
+ */
+ while (dest_i < dest->totlayer && dest->layers[dest_i].type < type) {
+ dest_i++;
+ }
+
+ /* if there are no more dest layers, we're done */
+ if (dest_i == dest->totlayer) {
+ break;
+ }
+
+ /* if we found a matching layer, add the data */
+ if (dest->layers[dest_i].type == type) {
+ void *src_data = source->layers[src_i].data;
+
+ if (type == CD_MVERT) {
+ for (j = 0; j < count; j++) {
+ MVert *mv_src = &((MVert *)src_data)[src_indices[j]];
+ add_v3_v3(co, mv_src->co);
+#ifdef USE_WELD_NORMALS
+ short *mv_src_no = mv_src->no;
+ no[0] += mv_src_no[0];
+ no[1] += mv_src_no[1];
+ no[2] += mv_src_no[2];
+#endif
+ bweight += mv_src->bweight;
+ flag |= mv_src->flag;
+ }
+ }
+ else if (type == CD_MEDGE) {
+ for (j = 0; j < count; j++) {
+ MEdge *me_src = &((MEdge *)src_data)[src_indices[j]];
+ crease += me_src->crease;
+ bweight += me_src->bweight;
+ flag |= me_src->flag;
+ }
+ }
+ else if (CustomData_layer_has_interp(dest, dest_i)) {
+ /* Already calculated.
+ * TODO: Optimize by exposing `typeInfo->interp`. */
+ }
+ else if (CustomData_layer_has_math(dest, dest_i)) {
+ const int size = CustomData_sizeof(type);
+ void *dst_data = dest->layers[dest_i].data;
+ void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size);
+ for (j = 0; j < count; j++) {
+ CustomData_data_add(
+ type, v_dst, POINTER_OFFSET(src_data, (size_t)src_indices[j] * size));
+ }
+ }
+ else {
+ CustomData_copy_layer_type_data(source, dest, type, src_indices[0], dest_index, 1);
+ }
+
+ /* if there are multiple source & dest layers of the same type,
+ * we don't want to copy all source layers to the same dest, so
+ * increment dest_i
+ */
+ dest_i++;
+ }
+ }
+
+ float fac = 1.0f / count;
+
+ for (dest_i = 0; dest_i < dest->totlayer; dest_i++) {
+ CustomDataLayer *layer_dst = &dest->layers[dest_i];
+ const int type = layer_dst->type;
+ if (type == CD_MVERT) {
+ MVert *mv = &((MVert *)layer_dst->data)[dest_index];
+ mul_v3_fl(co, fac);
+ bweight *= fac;
+ CLAMP_MAX(bweight, 255);
+
+ copy_v3_v3(mv->co, co);
+#ifdef USE_WELD_NORMALS
+ mul_v3_fl(no, fac);
+ short *mv_no = mv->no;
+ mv_no[0] = (short)no[0];
+ mv_no[1] = (short)no[1];
+ mv_no[2] = (short)no[2];
+#endif
+
+ mv->flag = (char)flag;
+ mv->bweight = (char)bweight;
+ }
+ else if (type == CD_MEDGE) {
+ MEdge *me = &((MEdge *)layer_dst->data)[dest_index];
+ crease *= fac;
+ bweight *= fac;
+ CLAMP_MAX(crease, 255);
+ CLAMP_MAX(bweight, 255);
+
+ me->crease = (char)crease;
+ me->bweight = (char)bweight;
+ me->flag = flag;
+ }
+ else if (CustomData_layer_has_interp(dest, dest_i)) {
+ /* Already calculated. */
+ }
+ else if (CustomData_layer_has_math(dest, dest_i)) {
+ const int size = CustomData_sizeof(type);
+ void *dst_data = layer_dst->data;
+ void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size);
+ CustomData_data_multiply(type, v_dst, fac);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh Vertex Merging
+ * \{ */
+
+static Mesh *create_merged_mesh(const Mesh &mesh,
+ MutableSpan<int> vert_dest_map,
+ const int removed_vertex_count)
+{
+ Span<MPoly> mpoly{mesh.mpoly, mesh.totpoly};
+ Span<MLoop> mloop{mesh.mloop, mesh.totloop};
+ const int totvert = mesh.totvert;
+ const int totedge = mesh.totedge;
+ const int totloop = mesh.totloop;
+ const int totpoly = mesh.totpoly;
+
+ WeldMesh weld_mesh;
+ weld_mesh_context_create(mesh, vert_dest_map, removed_vertex_count, &weld_mesh);
+
+ const int result_nverts = totvert - weld_mesh.vert_kill_len;
+ const int result_nedges = totedge - weld_mesh.edge_kill_len;
+ const int result_nloops = totloop - weld_mesh.loop_kill_len;
+ const int result_npolys = totpoly - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len;
+
+ Mesh *result = BKE_mesh_new_nomain_from_template(
+ &mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys);
+
+ /* Vertices. */
+
+ /* Be careful when editing this array, to avoid new allocations it uses the same buffer as
+ * #vert_dest_map. This map will be used to adjust the edges, polys and loops. */
+ MutableSpan<int> vert_final = vert_dest_map;
+
+ int dest_index = 0;
+ for (int i = 0; i < totvert; i++) {
+ int source_index = i;
+ int count = 0;
+ while (i < totvert && vert_dest_map[i] == OUT_OF_CONTEXT) {
+ vert_final[i] = dest_index + count;
+ count++;
+ i++;
+ }
+ if (count) {
+ CustomData_copy_data(&mesh.vdata, &result->vdata, source_index, dest_index, count);
+ dest_index += count;
+ }
+ if (i == totvert) {
+ break;
+ }
+ if (vert_dest_map[i] != ELEM_MERGED) {
+ struct WeldGroup *wgroup = &weld_mesh.vert_groups[vert_dest_map[i]];
+ customdata_weld(&mesh.vdata,
+ &result->vdata,
+ &weld_mesh.vert_groups_buffer[wgroup->ofs],
+ wgroup->len,
+ dest_index);
+ vert_final[i] = dest_index;
+ dest_index++;
+ }
+ }
+
+ BLI_assert(dest_index == result_nverts);
+
+ /* Edges. */
+
+ /* Be careful when editing this array, to avoid new allocations it uses the same buffer as
+ * #edge_groups_map. This map will be used to adjust the polys and loops. */
+ MutableSpan<int> edge_final = weld_mesh.edge_groups_map;
+
+ dest_index = 0;
+ for (int i = 0; i < totedge; i++) {
+ const int source_index = i;
+ int count = 0;
+ while (i < totedge && weld_mesh.edge_groups_map[i] == OUT_OF_CONTEXT) {
+ edge_final[i] = dest_index + count;
+ count++;
+ i++;
+ }
+ if (count) {
+ CustomData_copy_data(&mesh.edata, &result->edata, source_index, dest_index, count);
+ MEdge *me = &result->medge[dest_index];
+ dest_index += count;
+ for (; count--; me++) {
+ me->v1 = vert_final[me->v1];
+ me->v2 = vert_final[me->v2];
+ }
+ }
+ if (i == totedge) {
+ break;
+ }
+ if (weld_mesh.edge_groups_map[i] != ELEM_MERGED) {
+ struct WeldGroupEdge *wegrp = &weld_mesh.edge_groups[weld_mesh.edge_groups_map[i]];
+ customdata_weld(&mesh.edata,
+ &result->edata,
+ &weld_mesh.edge_groups_buffer[wegrp->group.ofs],
+ wegrp->group.len,
+ dest_index);
+ MEdge *me = &result->medge[dest_index];
+ me->v1 = vert_final[wegrp->v1];
+ me->v2 = vert_final[wegrp->v2];
+ /* "For now, assume that all merged edges are loose. This flag will be cleared in the
+ * Polys/Loops step". */
+ me->flag |= ME_LOOSEEDGE;
+
+ edge_final[i] = dest_index;
+ dest_index++;
+ }
+ }
+
+ BLI_assert(dest_index == result_nedges);
+
+ /* Polys/Loops. */
+
+ MPoly *r_mp = &result->mpoly[0];
+ MLoop *r_ml = &result->mloop[0];
+ int r_i = 0;
+ int loop_cur = 0;
+ Array<int, 64> group_buffer(weld_mesh.max_poly_len);
+ for (const int i : mpoly.index_range()) {
+ const MPoly &mp = mpoly[i];
+ const int loop_start = loop_cur;
+ const int poly_ctx = weld_mesh.poly_map[i];
+ if (poly_ctx == OUT_OF_CONTEXT) {
+ int mp_loop_len = mp.totloop;
+ CustomData_copy_data(&mesh.ldata, &result->ldata, mp.loopstart, loop_cur, mp_loop_len);
+ loop_cur += mp_loop_len;
+ for (; mp_loop_len--; r_ml++) {
+ r_ml->v = vert_final[r_ml->v];
+ r_ml->e = edge_final[r_ml->e];
+ }
+ }
+ else {
+ const WeldPoly &wp = weld_mesh.wpoly[poly_ctx];
+ WeldLoopOfPolyIter iter;
+ if (!weld_iter_loop_of_poly_begin(
+ iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) {
+ continue;
+ }
+
+ if (wp.poly_dst != OUT_OF_CONTEXT) {
+ continue;
+ }
+ while (weld_iter_loop_of_poly_next(iter)) {
+ customdata_weld(
+ &mesh.ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur);
+ int v = vert_final[iter.v];
+ int e = edge_final[iter.e];
+ r_ml->v = v;
+ r_ml->e = e;
+ r_ml++;
+ loop_cur++;
+ if (iter.type) {
+ result->medge[e].flag &= ~ME_LOOSEEDGE;
+ }
+ BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0);
+ }
+ }
+
+ CustomData_copy_data(&mesh.pdata, &result->pdata, i, r_i, 1);
+ r_mp->loopstart = loop_start;
+ r_mp->totloop = loop_cur - loop_start;
+ r_mp++;
+ r_i++;
+ }
+
+ for (const int i : IndexRange(weld_mesh.wpoly_new_len)) {
+ const WeldPoly &wp = weld_mesh.wpoly_new[i];
+ const int loop_start = loop_cur;
+ WeldLoopOfPolyIter iter;
+ if (!weld_iter_loop_of_poly_begin(
+ iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) {
+ continue;
+ }
+
+ if (wp.poly_dst != OUT_OF_CONTEXT) {
+ continue;
+ }
+ while (weld_iter_loop_of_poly_next(iter)) {
+ customdata_weld(&mesh.ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur);
+ int v = vert_final[iter.v];
+ int e = edge_final[iter.e];
+ r_ml->v = v;
+ r_ml->e = e;
+ r_ml++;
+ loop_cur++;
+ if (iter.type) {
+ result->medge[e].flag &= ~ME_LOOSEEDGE;
+ }
+ BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0);
+ }
+
+ r_mp->loopstart = loop_start;
+ r_mp->totloop = loop_cur - loop_start;
+ r_mp++;
+ r_i++;
+ }
+
+ BLI_assert((int)r_i == result_npolys);
+ BLI_assert(loop_cur == result_nloops);
+
+ /* We could only update the normals of the elements in context, but the next modifier can make it
+ * dirty anyway which would make the work useless. */
+ BKE_mesh_normals_tag_dirty(result);
+
+ return result;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Merge Map Creation
+ * \{ */
+
+std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh,
+ const IndexMask selection,
+ const float merge_distance)
+{
+ Array<int> vert_dest_map(mesh.totvert, OUT_OF_CONTEXT);
+
+ KDTree_3d *tree = BLI_kdtree_3d_new(selection.size());
+
+ for (const int i : selection) {
+ BLI_kdtree_3d_insert(tree, i, mesh.mvert[i].co);
+ }
+
+ BLI_kdtree_3d_balance(tree);
+ const int vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast(
+ tree, merge_distance, false, vert_dest_map.data());
+ BLI_kdtree_3d_free(tree);
+
+ if (vert_kill_len == 0) {
+ return std::nullopt;
+ }
+
+ return create_merged_mesh(mesh, vert_dest_map, vert_kill_len);
+}
+
+struct WeldVertexCluster {
+ float co[3];
+ int merged_verts;
+};
+
+std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
+ Span<bool> selection,
+ const float merge_distance,
+ const bool only_loose_edges)
+{
+ Span<MVert> verts{mesh.mvert, mesh.totvert};
+ Span<MEdge> edges{mesh.medge, mesh.totedge};
+
+ int vert_kill_len = 0;
+
+ /* From the original index of the vertex.
+ * This indicates which vert it is or is going to be merged. */
+ Array<int> vert_dest_map(mesh.totvert, OUT_OF_CONTEXT);
+
+ Array<WeldVertexCluster> vert_clusters(mesh.totvert);
+
+ for (const int i : verts.index_range()) {
+ WeldVertexCluster &vc = vert_clusters[i];
+ copy_v3_v3(vc.co, verts[i].co);
+ vc.merged_verts = 0;
+ }
+ const float merge_dist_sq = square_f(merge_distance);
+
+ range_vn_i(vert_dest_map.data(), mesh.totvert, 0);
+
+ /* Collapse Edges that are shorter than the threshold. */
+ for (const int i : edges.index_range()) {
+ int v1 = edges[i].v1;
+ int v2 = edges[i].v2;
+
+ if (only_loose_edges && (edges[i].flag & ME_LOOSEEDGE) == 0) {
+ continue;
+ }
+ while (v1 != vert_dest_map[v1]) {
+ v1 = vert_dest_map[v1];
+ }
+ while (v2 != vert_dest_map[v2]) {
+ v2 = vert_dest_map[v2];
+ }
+ if (v1 == v2) {
+ continue;
+ }
+ if (!selection.is_empty() && (!selection[v1] || !selection[v2])) {
+ continue;
+ }
+ if (v1 > v2) {
+ SWAP(int, v1, v2);
+ }
+ WeldVertexCluster *v1_cluster = &vert_clusters[v1];
+ WeldVertexCluster *v2_cluster = &vert_clusters[v2];
+
+ float edgedir[3];
+ sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co);
+ const float dist_sq = len_squared_v3(edgedir);
+ if (dist_sq <= merge_dist_sq) {
+ float influence = (v2_cluster->merged_verts + 1) /
+ (float)(v1_cluster->merged_verts + v2_cluster->merged_verts + 2);
+ madd_v3_v3fl(v1_cluster->co, edgedir, influence);
+
+ v1_cluster->merged_verts += v2_cluster->merged_verts + 1;
+ vert_dest_map[v2] = v1;
+ vert_kill_len++;
+ }
+ }
+
+ if (vert_kill_len == 0) {
+ return std::nullopt;
+ }
+
+ for (const int i : IndexRange(mesh.totvert)) {
+ if (i == vert_dest_map[i]) {
+ vert_dest_map[i] = OUT_OF_CONTEXT;
+ }
+ else {
+ int v = i;
+ while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) {
+ v = vert_dest_map[v];
+ }
+ vert_dest_map[v] = v;
+ vert_dest_map[i] = v;
+ }
+ }
+
+ return create_merged_mesh(mesh, vert_dest_map, vert_kill_len);
+}
+
+/** \} */
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc
new file mode 100644
index 00000000000..daa08a3bce6
--- /dev/null
+++ b/source/blender/geometry/intern/point_merge_by_distance.cc
@@ -0,0 +1,183 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_kdtree.h"
+#include "BLI_task.hh"
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_pointcloud.h"
+
+#include "GEO_point_merge_by_distance.hh"
+
+namespace blender::geometry {
+
+PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
+ const float merge_distance,
+ const IndexMask selection)
+{
+ const PointCloud &src_pointcloud = *src_points.get_for_read();
+ const int src_size = src_pointcloud.totpoint;
+ Span<float3> positions{reinterpret_cast<float3 *>(src_pointcloud.co), src_size};
+
+ /* Create the KD tree based on only the selected points, to speed up merge detection and
+ * balancing. */
+ KDTree_3d *tree = BLI_kdtree_3d_new(selection.size());
+ for (const int i : selection.index_range()) {
+ BLI_kdtree_3d_insert(tree, i, positions[selection[i]]);
+ }
+ BLI_kdtree_3d_balance(tree);
+
+ /* Find the duplicates in the KD tree. Because the tree only contains the selected points, the
+ * resulting indices are indices into the selection, rather than indices of the source point
+ * cloud. */
+ Array<int> selection_merge_indices(selection.size(), -1);
+ const int duplicate_count = BLI_kdtree_3d_calc_duplicates_fast(
+ tree, merge_distance, false, selection_merge_indices.data());
+ BLI_kdtree_3d_free(tree);
+
+ /* Create the new point cloud and add it to a temporary component for the attribute API. */
+ const int dst_size = src_size - duplicate_count;
+ PointCloud *dst_pointcloud = BKE_pointcloud_new_nomain(dst_size);
+ PointCloudComponent dst_points;
+ dst_points.replace(dst_pointcloud, GeometryOwnershipType::Editable);
+
+ /* By default, every point is just "merged" with itself. Then fill in the results of the merge
+ * finding, converting from indices into the selection to indices into the full input point
+ * cloud. */
+ Array<int> merge_indices(src_size);
+ for (const int i : merge_indices.index_range()) {
+ merge_indices[i] = i;
+ }
+ for (const int i : selection_merge_indices.index_range()) {
+ const int merge_index = selection_merge_indices[i];
+ if (merge_index != -1) {
+ const int src_merge_index = selection[merge_index];
+ const int src_index = selection[i];
+ merge_indices[src_index] = src_merge_index;
+ }
+ }
+
+ /* For every source index, find the corresponding index in the result by iterating through the
+ * source indices and counting how many merges happened before that point. */
+ int merged_points = 0;
+ Array<int> src_to_dst_indices(src_size);
+ for (const int i : IndexRange(src_size)) {
+ src_to_dst_indices[i] = i - merged_points;
+ if (merge_indices[i] != i) {
+ merged_points++;
+ }
+ }
+
+ /* In order to use a contiguous array as the storage for every destination point's source
+ * indices, first the number of source points must be counted for every result point. */
+ Array<int> point_merge_counts(dst_size, 0);
+ for (const int i : IndexRange(src_size)) {
+ const int merge_index = merge_indices[i];
+ const int dst_index = src_to_dst_indices[merge_index];
+ point_merge_counts[dst_index]++;
+ }
+
+ /* This array stores an offset into `merge_map` for every result point. */
+ Array<int> map_offsets(dst_size + 1);
+ int offset = 0;
+ for (const int i : IndexRange(dst_size)) {
+ map_offsets[i] = offset;
+ offset += point_merge_counts[i];
+ }
+ map_offsets.last() = offset;
+
+ point_merge_counts.fill(0);
+
+ /* This array stores all of the source indices for every result point. The size is the source
+ * size because every input point is either merged with another or copied directly. */
+ Array<int> merge_map(src_size);
+ for (const int i : IndexRange(src_size)) {
+ const int merge_index = merge_indices[i];
+ const int dst_index = src_to_dst_indices[merge_index];
+
+ const IndexRange point_range(map_offsets[dst_index],
+ map_offsets[dst_index + 1] - map_offsets[dst_index]);
+ MutableSpan<int> point_merge_indices = merge_map.as_mutable_span().slice(point_range);
+ point_merge_indices[point_merge_counts[dst_index]] = i;
+ point_merge_counts[dst_index]++;
+ }
+
+ Set<bke::AttributeIDRef> attributes = src_points.attribute_ids();
+
+ /* Transfer the ID attribute if it exists, using the ID of the first merged point. */
+ if (attributes.contains("id")) {
+ VArray<int> src = src_points.attribute_get_for_read<int>("id", ATTR_DOMAIN_POINT, 0);
+ bke::OutputAttribute_Typed<int> dst = dst_points.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ Span<int> src_ids = src.get_internal_span();
+ MutableSpan<int> dst_ids = dst.as_span();
+
+ threading::parallel_for(IndexRange(dst_size), 1024, [&](IndexRange range) {
+ for (const int i_dst : range) {
+ const IndexRange point_range(map_offsets[i_dst],
+ map_offsets[i_dst + 1] - map_offsets[i_dst]);
+ dst_ids[i_dst] = src_ids[point_range.first()];
+ }
+ });
+
+ dst.save();
+ attributes.remove_contained("id");
+ }
+
+ /* Transfer all other attributes. */
+ for (const bke::AttributeIDRef &id : attributes) {
+ if (!id.should_be_kept()) {
+ continue;
+ }
+
+ bke::ReadAttributeLookup src_attribute = src_points.attribute_try_get_for_read(id);
+ attribute_math::convert_to_static_type(src_attribute.varray.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ bke::OutputAttribute_Typed<T> dst_attribute =
+ dst_points.attribute_try_get_for_output_only<T>(id, ATTR_DOMAIN_POINT);
+ Span<T> src = src_attribute.varray.get_internal_span().typed<T>();
+ MutableSpan<T> dst = dst_attribute.as_span();
+
+ threading::parallel_for(IndexRange(dst_size), 1024, [&](IndexRange range) {
+ for (const int i_dst : range) {
+ /* Create a separate mixer for every point to avoid allocating temporary buffers
+ * in the mixer the size of the result point cloud and to improve memory locality. */
+ attribute_math::DefaultMixer<T> mixer{dst.slice(i_dst, 1)};
+
+ const IndexRange point_range(map_offsets[i_dst],
+ map_offsets[i_dst + 1] - map_offsets[i_dst]);
+ Span<int> src_merge_indices = merge_map.as_span().slice(point_range);
+ for (const int i_src : src_merge_indices) {
+ mixer.mix_in(0, src[i_src]);
+ }
+
+ mixer.finalize();
+ }
+ });
+
+ dst_attribute.save();
+ }
+ });
+ }
+
+ return dst_pointcloud;
+}
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc
index 4022794d53f..18dd74cb688 100644
--- a/source/blender/geometry/intern/realize_instances.cc
+++ b/source/blender/geometry/intern/realize_instances.cc
@@ -1269,7 +1269,7 @@ static void remove_id_attribute_from_instances(GeometrySet &geometry_set)
{
geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) {
if (sub_geometry.has<InstancesComponent>()) {
- InstancesComponent &component = geometry_set.get_component_for_write<InstancesComponent>();
+ InstancesComponent &component = sub_geometry.get_component_for_write<InstancesComponent>();
component.attributes().remove("id");
}
});
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index 5ee75619259..a3f468a20dc 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -92,10 +92,6 @@ set(SRC
set(LIB
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index 59937bb0757..6a3a27a6630 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -409,6 +409,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE);
uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE);
+ uiItemR(col, ptr, "use_back_face_culling", 0, NULL, ICON_NONE);
}
static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -580,6 +581,7 @@ static void face_mark_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "use_face_mark_invert", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "use_face_mark_boundaries", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_face_mark_keep_contour", 0, NULL, ICON_NONE);
}
static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -607,6 +609,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(col, ptr, "use_fuzzy_all", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_loose_edge_chain", 0, IFACE_("Loose Edges"), ICON_NONE);
uiItemR(col, ptr, "use_loose_as_contour", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_detail_preserve", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_geometry_space_chain", 0, IFACE_("Geometry Space"), ICON_NONE);
uiItemR(layout,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 8c5820c9667..cb17d1d5f1a 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -300,14 +300,18 @@ typedef struct LineartRenderBuffer {
bool use_loose_edge_chain;
bool use_geometry_space_chain;
bool use_image_boundary_trimming;
+ bool use_back_face_culling;
bool filter_face_mark;
bool filter_face_mark_invert;
bool filter_face_mark_boundaries;
+ bool filter_face_mark_keep_contour;
bool force_crease;
bool sharp_as_crease;
+ bool chain_preserve_details;
+
/* Keep an copy of these data so when line art is running it's self-contained. */
bool cam_is_persp;
float cam_obmat[4][4];
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index 0deb8b1c335..78ad895c93e 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -571,6 +571,57 @@ static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartEdg
}
}
+static bool lineart_chain_fix_ambiguous_segments(LineartEdgeChain *ec,
+ LineartEdgeChainItem *last_matching_eci,
+ float distance_threshold,
+ bool preserve_details,
+ LineartEdgeChainItem **r_next_eci)
+{
+ float dist_accum = 0;
+
+ int fixed_occ = last_matching_eci->occlusion;
+ unsigned char fixed_mask = last_matching_eci->material_mask_bits;
+
+ LineartEdgeChainItem *can_skip_to = NULL;
+ LineartEdgeChainItem *last_eci = last_matching_eci;
+ for (LineartEdgeChainItem *eci = last_matching_eci->next; eci; eci = eci->next) {
+ dist_accum += len_v2v2(last_eci->pos, eci->pos);
+ if (dist_accum > distance_threshold) {
+ break;
+ }
+ last_eci = eci;
+ /* The reason for this is because we don't want visible segments to be "skipped" into
+ * connecting with invisible segments. */
+ if (eci->occlusion < fixed_occ) {
+ break;
+ }
+ if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ) {
+ can_skip_to = eci;
+ }
+ }
+ if (can_skip_to) {
+ /* Either mark all in-between segments with the same occlusion and mask or delete those
+ * different ones. */
+ LineartEdgeChainItem *next_eci;
+ for (LineartEdgeChainItem *eci = last_matching_eci->next; eci != can_skip_to; eci = next_eci) {
+ next_eci = eci->next;
+ if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ) {
+ continue;
+ }
+ if (preserve_details) {
+ eci->material_mask_bits = fixed_mask;
+ eci->occlusion = fixed_occ;
+ }
+ else {
+ BLI_remlink(&ec->chain, eci);
+ }
+ }
+ *r_next_eci = can_skip_to;
+ return true;
+ }
+ return false;
+}
+
void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
{
LineartEdgeChain *ec, *new_ec;
@@ -597,6 +648,13 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
if (lineart_point_overlapping(next_eci, eci->pos[0], eci->pos[1], 1e-5)) {
continue;
}
+ if (lineart_chain_fix_ambiguous_segments(ec,
+ eci->prev,
+ rb->chaining_image_threshold,
+ rb->chain_preserve_details,
+ &next_eci)) {
+ continue;
+ }
}
else {
/* Set the same occlusion level for the end vertex, so when further connection is needed
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 5461b80cad1..5434f7768b2 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -1346,6 +1346,10 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
/* Select the triangle in the array. */
tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i);
+ if (tri->flags & LRT_CULL_DISCARD) {
+ continue;
+ }
+
LRT_CULL_DECIDE_INSIDE
LRT_CULL_ENSURE_MEMORY
lineart_triangle_cull_single(rb,
@@ -1489,6 +1493,7 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
FreestyleEdge *fel, *fer;
bool face_mark_filtered = false;
uint16_t edge_flag_result = 0;
+ bool only_contour = false;
if (use_freestyle_face && rb->filter_face_mark) {
fel = CustomData_bmesh_get(&bm_if_freestyle->pdata, ll->f->head.data, CD_FREESTYLE_FACE);
@@ -1513,7 +1518,12 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
face_mark_filtered = !face_mark_filtered;
}
if (!face_mark_filtered) {
- return 0;
+ if (rb->filter_face_mark_keep_contour) {
+ only_contour = true;
+ }
+ else {
+ return 0;
+ }
}
}
@@ -1536,8 +1546,31 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
double dot_1 = 0, dot_2 = 0;
double result;
- if (rb->cam_is_persp) {
- sub_v3_v3v3_db(view_vector, l->gloc, rb->camera_pos);
+ if (rb->use_contour || rb->use_back_face_culling) {
+
+ if (rb->cam_is_persp) {
+ sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc);
+ }
+ else {
+ view_vector = rb->view_vector;
+ }
+
+ dot_1 = dot_v3v3_db(view_vector, tri1->gn);
+ dot_2 = dot_v3v3_db(view_vector, tri2->gn);
+
+ if (rb->use_contour && (result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) {
+ edge_flag_result |= LRT_EDGE_FLAG_CONTOUR;
+ }
+
+ /* Because the ray points towards the camera, so back-face is when dot value being negative. */
+ if (rb->use_back_face_culling) {
+ if (dot_1 < 0) {
+ tri1->flags |= LRT_CULL_DISCARD;
+ }
+ if (dot_2 < 0) {
+ tri2->flags |= LRT_CULL_DISCARD;
+ }
+ }
}
else {
view_vector = rb->view_vector;
@@ -1550,6 +1583,12 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
edge_flag_result |= LRT_EDGE_FLAG_CONTOUR;
}
+ /* For when face mark filtering decided that we discard the face but keep_contour option is on.
+ * so we still have correct full contour around the object. */
+ if (only_contour) {
+ return edge_flag_result;
+ }
+
if (rb->use_crease) {
if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) {
edge_flag_result |= LRT_EDGE_FLAG_CREASE;
@@ -1875,7 +1914,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu
bm);
if (eflag) {
/* Only allocate for feature lines (instead of all lines) to save memory.
- * If allow duplicated edges, one edge gets added multiple times if it has multiple types. */
+ * If allow duplicated edges, one edge gets added multiple times if it has multiple types.
+ */
allocate_la_e += rb->allow_duplicated_types ? lineart_edge_type_duplication_count(eflag) : 1;
}
/* Here we just use bm's flag for when loading actual lines, then we don't need to call
@@ -2069,8 +2109,8 @@ static bool lineart_geometry_check_visible(double (*model_view_proj)[4],
}
bool cond[6] = {true, true, true, true, true, true};
- /* Because for a point to be inside clip space, it must satisfy `-Wc <= XYCc <= Wc`, here if all
- * verts falls to the same side of the clip space border, we know it's outside view. */
+ /* Because for a point to be inside clip space, it must satisfy `-Wc <= XYCc <= Wc`, here if
+ * all verts falls to the same side of the clip space border, we know it's outside view. */
for (int i = 0; i < 8; i++) {
cond[0] &= (co[i][0] < -co[i][3]);
cond[1] &= (co[i][0] > co[i][3]);
@@ -2145,7 +2185,8 @@ static void lineart_main_load_geometries(
int thread_count = rb->thread_count;
- /* This memory is in render buffer memory pool. so we don't need to free those after loading. */
+ /* This memory is in render buffer memory pool. so we don't need to free those after loading.
+ */
LineartObjectLoadTaskInfo *olti = lineart_mem_acquire(
&rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count);
@@ -2428,8 +2469,9 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
dot_f = dot_v3v3_db(Cv, tri->gn);
/* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_
- * faces in perspective mode would get erroneously caught in this condition where they really are
- * legit faces that would produce occlusion, but haven't encountered those yet in my test files.
+ * faces in perspective mode would get erroneously caught in this condition where they really
+ * are legit faces that would produce occlusion, but haven't encountered those yet in my test
+ * files.
*/
if (fabs(dot_f) < FLT_EPSILON) {
return false;
@@ -2496,8 +2538,9 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
return false; \
}
- /* Determine the pair of edges that the line has crossed. The "|" symbol in the comment indicates
- * triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision tolerance. */
+ /* Determine the pair of edges that the line has crossed. The "|" symbol in the comment
+ * indicates triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision
+ * tolerance. */
if (st_l == 2) {
/* Left side is in the triangle. */
@@ -3158,6 +3201,14 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->force_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SMOOTH_SURFACES) != 0;
rb->sharp_as_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SHARP_EDGES) != 0;
+ rb->chain_preserve_details = (lmd->calculation_flags & LRT_CHAIN_PRESERVE_DETAILS) != 0;
+
+ /* This is used to limit calculation to a certain level to save time, lines who have higher
+ * occlusion levels will get ignored. */
+ rb->max_occlusion_level = lmd->level_end_override;
+
+ rb->use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0;
+
int16_t edge_types = lmd->edge_types_override;
rb->use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0;
@@ -3171,6 +3222,8 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->filter_face_mark = (lmd->calculation_flags & LRT_FILTER_FACE_MARK) != 0;
rb->filter_face_mark_boundaries = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_BOUNDARIES) !=
0;
+ rb->filter_face_mark_keep_contour = (lmd->calculation_flags &
+ LRT_FILTER_FACE_MARK_KEEP_CONTOUR) != 0;
rb->chain_data_pool = &lc->chain_data_pool;
@@ -4176,12 +4229,6 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
* See definition of LineartTriangleThread for details. */
rb->triangle_size = lineart_triangle_size_get(scene, rb);
- /* This is used to limit calculation to a certain level to save time, lines who have higher
- * occlusion levels will get ignored. */
- rb->max_occlusion_level = (lmd->flags & LRT_GPENCIL_USE_CACHE) ?
- lmd->level_end_override :
- (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start);
-
/* FIXME(Yiming): See definition of int #LineartRenderBuffer::_source_type for detailed. */
rb->_source_type = lmd->source_type;
rb->_source_collection = lmd->source_collection;
@@ -4253,8 +4300,8 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
if (rb->chain_smooth_tolerance > FLT_EPSILON) {
/* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best
- * effective range in image-space (Coordinate only goes from -1 to 1). This value is somewhat
- * arbitrary, but works best for the moment. */
+ * effective range in image-space (Coordinate only goes from -1 to 1). This value is
+ * somewhat arbitrary, but works best for the moment. */
MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50);
}
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index bb7e9e8b26f..cffea5e9dcc 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -323,6 +323,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_output_material.glsl
shaders/material/gpu_shader_material_output_world.glsl
shaders/material/gpu_shader_material_particle_info.glsl
+ shaders/material/gpu_shader_material_point_info.glsl
shaders/material/gpu_shader_material_principled.glsl
shaders/material/gpu_shader_material_refraction.glsl
shaders/material/gpu_shader_material_rgb_curves.glsl
@@ -374,7 +375,7 @@ set(GLSL_SRC
shaders/gpu_shader_common_obinfos_lib.glsl
- intern/gpu_shader_shared_utils.h
+ GPU_shader_shared_utils.h
)
set(GLSL_C)
@@ -392,7 +393,7 @@ set(GLSL_SOURCE_CONTENT "")
foreach(GLSL_FILE ${GLSL_SRC})
get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME)
string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME})
- string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\"\)\n")
+ string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n")
endforeach()
set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_gpu_source_list.h")
@@ -401,48 +402,58 @@ list(APPEND SRC ${glsl_source_list_file})
list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
set(SHADER_CREATE_INFOS
-#../draw/engines/workbench/shaders/workbench_effect_cavity_info.hh
-#../draw/engines/workbench/shaders/workbench_prepass_info.hh
-../draw/intern/shaders/draw_fullscreen_info.hh
-../draw/intern/shaders/draw_view_info.hh
-../draw/intern/shaders/draw_object_infos_info.hh
-
-shaders/infos/gpu_clip_planes_info.hh
-shaders/infos/gpu_srgb_to_framebuffer_space_info.hh
-shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
-shaders/infos/gpu_shader_2D_checker_info.hh
-shaders/infos/gpu_shader_2D_diag_stripes_info.hh
-shaders/infos/gpu_shader_2D_uniform_color_info.hh
-shaders/infos/gpu_shader_2D_flat_color_info.hh
-shaders/infos/gpu_shader_2D_smooth_color_info.hh
-shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh
-shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh
-shaders/infos/gpu_shader_2D_image_info.hh
-shaders/infos/gpu_shader_2D_image_color_info.hh
-shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh
-shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh
-shaders/infos/gpu_shader_2D_image_rect_color_info.hh
-shaders/infos/gpu_shader_text_info.hh
-shaders/infos/gpu_shader_keyframe_shape_info.hh
-shaders/infos/gpu_shader_3D_flat_color_info.hh
-shaders/infos/gpu_shader_3D_uniform_color_info.hh
-shaders/infos/gpu_shader_3D_smooth_color_info.hh
-shaders/infos/gpu_shader_3D_depth_only_info.hh
-shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh
-shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh
-shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh
-shaders/infos/gpu_shader_2D_area_borders_info.hh
-shaders/infos/gpu_shader_2D_image_multi_rect_color_info.hh
-shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh
-shaders/infos/gpu_shader_3D_point_info.hh
-shaders/infos/gpu_shader_2D_nodelink_info.hh
-shaders/infos/gpu_shader_gpencil_stroke_info.hh
-shaders/infos/gpu_shader_simple_lighting_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_composite_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_effect_dof_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_effect_outline_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_merge_infront_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_prepass_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_shadow_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh
+ ../draw/engines/workbench/shaders/infos/workbench_volume_info.hh
+ ../draw/engines/image/shaders/infos/engine_image_info.hh
+ ../draw/intern/shaders/draw_fullscreen_info.hh
+ ../draw/intern/shaders/draw_object_infos_info.hh
+ ../draw/intern/shaders/draw_view_info.hh
+ ../draw/intern/shaders/draw_hair_refine_info.hh
+
+ shaders/infos/gpu_clip_planes_info.hh
+ shaders/infos/gpu_shader_2D_area_borders_info.hh
+ shaders/infos/gpu_shader_2D_checker_info.hh
+ shaders/infos/gpu_shader_2D_diag_stripes_info.hh
+ shaders/infos/gpu_shader_2D_flat_color_info.hh
+ shaders/infos/gpu_shader_2D_image_color_info.hh
+ shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh
+ shaders/infos/gpu_shader_2D_image_info.hh
+ shaders/infos/gpu_shader_2D_image_multi_rect_color_info.hh
+ shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh
+ shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh
+ shaders/infos/gpu_shader_2D_image_rect_color_info.hh
+ shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh
+ shaders/infos/gpu_shader_2D_nodelink_info.hh
+ shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh
+ shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh
+ shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh
+ shaders/infos/gpu_shader_2D_smooth_color_info.hh
+ shaders/infos/gpu_shader_2D_uniform_color_info.hh
+ shaders/infos/gpu_shader_3D_depth_only_info.hh
+ shaders/infos/gpu_shader_3D_flat_color_info.hh
+ shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
+ shaders/infos/gpu_shader_3D_point_info.hh
+ shaders/infos/gpu_shader_3D_smooth_color_info.hh
+ shaders/infos/gpu_shader_3D_uniform_color_info.hh
+ shaders/infos/gpu_shader_gpencil_stroke_info.hh
+ shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh
+ shaders/infos/gpu_shader_keyframe_shape_info.hh
+ shaders/infos/gpu_shader_simple_lighting_info.hh
+ shaders/infos/gpu_shader_text_info.hh
+ shaders/infos/gpu_srgb_to_framebuffer_space_info.hh
)
set(SHADER_CREATE_INFOS_CONTENT "")
foreach(DESCRIPTOR_FILE ${SHADER_CREATE_INFOS})
-string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n")
+ string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n")
endforeach()
set(shader_create_info_list_file "${CMAKE_CURRENT_BINARY_DIR}/gpu_shader_create_info_list.hh")
diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h
index 9796ac63272..18a35f9c71a 100644
--- a/source/blender/gpu/GPU_debug.h
+++ b/source/blender/gpu/GPU_debug.h
@@ -31,6 +31,8 @@
extern "C" {
#endif
+#define GPU_DEBUG_SHADER_COMPILATION_GROUP "Shader Compilation"
+
void GPU_debug_group_begin(const char *name);
void GPU_debug_group_end(void);
/**
diff --git a/source/blender/gpu/GPU_select.h b/source/blender/gpu/GPU_select.h
index c02af763311..3076058c075 100644
--- a/source/blender/gpu/GPU_select.h
+++ b/source/blender/gpu/GPU_select.h
@@ -32,7 +32,7 @@ extern "C" {
struct rcti;
/** Flags for mode of operation. */
-enum {
+typedef enum eGPUSelectMode {
GPU_SELECT_ALL = 1,
/* gpu_select_query */
GPU_SELECT_NEAREST_FIRST_PASS = 2,
@@ -40,13 +40,32 @@ enum {
/* gpu_select_pick */
GPU_SELECT_PICK_ALL = 4,
GPU_SELECT_PICK_NEAREST = 5,
-};
+} eGPUSelectMode;
+
+/**
+ * The result of calling #GPU_select_begin & #GPU_select_end.
+ */
+typedef struct GPUSelectResult {
+ /** The selection identifier matching the value passed in by #GPU_select_load_id. */
+ unsigned int id;
+ /**
+ * The nearest depth.
+ * - Only supported by picking modes (#GPU_SELECT_PICK_ALL and #GPU_SELECT_PICK_NEAREST)
+ * since occlusion quires don't provide a convenient way of accessing the depth-buffer.
+ * - OpenGL's `GL_SELECT` supported both near and far depths,
+ * this has not been included as Blender doesn't need this however support could be added.
+ */
+ unsigned int depth;
+} GPUSelectResult;
/**
* Initialize and provide buffer for results.
*/
-void GPU_select_begin(
- unsigned int *buffer, unsigned int bufsize, const struct rcti *input, char mode, int oldhits);
+void GPU_select_begin(GPUSelectResult *buffer,
+ unsigned int buffer_len,
+ const struct rcti *input,
+ eGPUSelectMode mode,
+ int oldhits);
/**
* Loads a new selection id and ends previous query, if any.
* In second pass of selection it also returns
@@ -79,8 +98,8 @@ void GPU_select_cache_end(void);
*
* Note that comparing depth as uint is fine.
*/
-const uint *GPU_select_buffer_near(const uint *buffer, int hits);
-uint GPU_select_buffer_remove_by_id(uint *buffer, int hits, uint select_id);
+const GPUSelectResult *GPU_select_buffer_near(const GPUSelectResult *buffer, int hits);
+uint GPU_select_buffer_remove_by_id(GPUSelectResult *buffer, int hits, uint select_id);
/**
* Part of the solution copied from `rect_subregion_stride_calc`.
*/
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 97b9b26ba2a..05c992274eb 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -69,6 +69,9 @@ GPUShader *GPU_shader_create_ex(const char *vertcode,
int tf_count,
const char *shname);
GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info);
+GPUShader *GPU_shader_create_from_info_name(const char *info_name);
+
+const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name);
struct GPU_ShaderCreateFromArray_Params {
const char **vert, **geom, **frag, **defs;
@@ -148,9 +151,14 @@ typedef enum {
} GPUUniformBuiltin;
typedef enum {
+ /** Deprecated */
GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */
GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */
GPU_UNIFORM_BLOCK_INFO, /* infoBlock */
+ /** New ones */
+ GPU_UNIFORM_BLOCK_DRW_VIEW,
+ GPU_UNIFORM_BLOCK_DRW_MODEL,
+ GPU_UNIFORM_BLOCK_DRW_INFOS,
GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
} GPUUniformBlockBuiltin;
diff --git a/source/blender/gpu/GPU_shader_shared.h b/source/blender/gpu/GPU_shader_shared.h
index 334b974acd8..636a8f53ba6 100644
--- a/source/blender/gpu/GPU_shader_shared.h
+++ b/source/blender/gpu/GPU_shader_shared.h
@@ -22,14 +22,7 @@
*/
#ifndef USE_GPU_SHADER_CREATE_INFO
-# include "intern/gpu_shader_shared_utils.h"
-#endif
-
-#ifdef __cplusplus
-using blender::float2;
-using blender::float3;
-using blender::float4;
-using blender::float4x4;
+# include "GPU_shader_shared_utils.h"
#endif
struct NodeLinkData {
diff --git a/source/blender/gpu/intern/gpu_shader_shared_utils.h b/source/blender/gpu/GPU_shader_shared_utils.h
index f061f0f968a..0d283fb1e66 100644
--- a/source/blender/gpu/intern/gpu_shader_shared_utils.h
+++ b/source/blender/gpu/GPU_shader_shared_utils.h
@@ -27,11 +27,10 @@
* Some preprocessing is done by the GPU back-end to make it GLSL compatible.
*
* IMPORTANT:
- * - Don't add trailing comma at the end of the enum. Our custom pre-processor will now trim it
- * for GLSL.
* - Always use `u` suffix for enum values. GLSL do not support implicit cast.
* - Define all values. This is in order to simplify custom pre-processor code.
- * - Always use uint32_t as underlying type.
+ * - (C++ only) Always use `uint32_t` as underlying type (`enum eMyEnum : uint32_t`).
+ * - (C only) do NOT use the enum type inside UBO/SSBO structs and use `uint` instead.
* - Use float suffix by default for float literals to avoid double promotion in C++.
* - Pack one float or int after a vec3/ivec3 to fulfill alignment rules.
*
@@ -74,31 +73,44 @@
# define bool3 bvec3
# define bool4 bvec4
-#else /* C */
+#else /* C / C++ */
# pragma once
# include "BLI_assert.h"
# ifdef __cplusplus
# include "BLI_float4x4.hh"
-# else
+# include "BLI_math_vec_types.hh"
+using blender::float2;
+using blender::float3;
+using blender::float4;
+using blender::float4x4;
+using blender::int2;
+using blender::int3;
+using blender::int4;
+using blender::uint2;
+using blender::uint3;
+using blender::uint4;
+using bool1 = int;
+using bool2 = blender::int2;
+using bool3 = blender::int3;
+using bool4 = blender::int4;
+
+# else /* C */
typedef float float2[2];
typedef float float3[3];
typedef float float4[4];
typedef float float4x4[4][4];
-# endif
typedef int int2[2];
typedef int int3[2];
typedef int int4[4];
typedef uint uint2[2];
typedef uint uint3[3];
typedef uint uint4[4];
-typedef int int2[2];
-typedef int int3[2];
-typedef int int4[4];
typedef int bool1;
typedef int bool2[2];
typedef int bool3[2];
typedef int bool4[4];
+# endif
#endif
diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h
index 22cd7eab927..bd52b8321bb 100644
--- a/source/blender/gpu/GPU_state.h
+++ b/source/blender/gpu/GPU_state.h
@@ -37,11 +37,14 @@ ENUM_OPERATORS(eGPUWriteMask, GPU_WRITE_COLOR)
typedef enum eGPUBarrier {
GPU_BARRIER_NONE = 0,
- GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0),
- GPU_BARRIER_TEXTURE_FETCH = (1 << 1),
- GPU_BARRIER_SHADER_STORAGE = (1 << 2),
- GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 3),
- GPU_BARRIER_ELEMENT_ARRAY = (1 << 4),
+ GPU_BARRIER_COMMAND = (1 << 0),
+ GPU_BARRIER_FRAMEBUFFER = (1 << 1),
+ GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 2),
+ GPU_BARRIER_SHADER_STORAGE = (1 << 3),
+ GPU_BARRIER_TEXTURE_FETCH = (1 << 4),
+ GPU_BARRIER_TEXTURE_UPDATE = (1 << 5),
+ GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 6),
+ GPU_BARRIER_ELEMENT_ARRAY = (1 << 7),
} eGPUBarrier;
ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY)
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index d65115541fa..7812c7a7257 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -273,6 +273,7 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl);
* \warning Only work for 2D texture for now.
* \warning Only clears the mip 0 of the texture.
* \param data_format: data format of the pixel data.
+ * \note The format is float for unorm textures.
* \param data: 1 pixel worth of data to fill the texture with.
*/
void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data);
diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h
index c4948d68298..917b87efeaa 100644
--- a/source/blender/gpu/GPU_viewport.h
+++ b/source/blender/gpu/GPU_viewport.h
@@ -40,10 +40,10 @@ extern "C" {
typedef struct GHash GHash;
typedef struct GPUViewport GPUViewport;
-struct GPUFrameBuffer;
+struct DRWData;
struct DefaultFramebufferList;
struct DefaultTextureList;
-struct DRWData;
+struct GPUFrameBuffer;
GPUViewport *GPU_viewport_create(void);
GPUViewport *GPU_viewport_stereo_create(void);
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index 568462ec2a6..e1c6901470a 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -711,10 +711,10 @@ static char *code_generate_vertex(GPUNodeGraph *graph,
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
const char *type_str = gpu_data_type_to_string(attr->gputype);
const char *prefix = attr_prefix_get(attr->type);
- /* XXX FIXME : see notes in mesh_render_data_create() */
- /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */
+ /* XXX FIXME: see notes in mesh_render_data_create() */
+ /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */
if (attr->type == CD_ORCO) {
- /* OPTI : orco is computed from local positions, but only if no modifier is present. */
+ /* OPTI: orco is computed from local positions, but only if no modifier is present. */
BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n");
}
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index 5b1eac2e82f..28f5b8b02b0 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -437,7 +437,7 @@ void GPU_framebuffer_blit(GPUFrameBuffer *gpufb_read,
fb_read->blit_to(blit_buffers, read_slot, fb_write, write_slot, 0, 0);
- /* FIXME(fclem) sRGB is not saved. */
+ /* FIXME(@fclem): sRGB is not saved. */
prev_fb->bind(true);
}
diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh
index a936e889e57..e7505258d2a 100644
--- a/source/blender/gpu/intern/gpu_framebuffer_private.hh
+++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh
@@ -43,8 +43,9 @@ typedef enum GPUAttachmentType : int {
GPU_FB_COLOR_ATTACHMENT3,
GPU_FB_COLOR_ATTACHMENT4,
GPU_FB_COLOR_ATTACHMENT5,
- /* Number of maximum output slots.
- * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */
+ GPU_FB_COLOR_ATTACHMENT6,
+ GPU_FB_COLOR_ATTACHMENT7,
+ /* Number of maximum output slots. */
/* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to
* the maximum number of COLOR attachments specified by glDrawBuffers. */
GPU_FB_MAX_ATTACHMENT,
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 6451086f5fc..11c61e4cf72 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -44,6 +44,8 @@
#include "BKE_node.h"
#include "BKE_scene.h"
+#include "NOD_shader.h"
+
#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c
index 74e0270c42a..712ab44adb3 100644
--- a/source/blender/gpu/intern/gpu_material_library.c
+++ b/source/blender/gpu/intern/gpu_material_library.c
@@ -91,6 +91,7 @@ extern char datatoc_gpu_shader_material_output_aov_glsl[];
extern char datatoc_gpu_shader_material_output_material_glsl[];
extern char datatoc_gpu_shader_material_output_world_glsl[];
extern char datatoc_gpu_shader_material_particle_info_glsl[];
+extern char datatoc_gpu_shader_material_point_info_glsl[];
extern char datatoc_gpu_shader_material_principled_glsl[];
extern char datatoc_gpu_shader_material_refraction_glsl[];
extern char datatoc_gpu_shader_material_rgb_curves_glsl[];
@@ -295,7 +296,7 @@ static GPUMaterialLibrary gpu_shader_material_glass_library = {
static GPUMaterialLibrary gpu_shader_material_hair_info_library = {
.code = datatoc_gpu_shader_material_hair_info_glsl,
- .dependencies = {NULL},
+ .dependencies = {&gpu_shader_material_hash_library, NULL},
};
static GPUMaterialLibrary gpu_shader_material_holdout_library = {
@@ -388,6 +389,11 @@ static GPUMaterialLibrary gpu_shader_material_particle_info_library = {
.dependencies = {NULL},
};
+static GPUMaterialLibrary gpu_shader_material_point_info_library = {
+ .code = datatoc_gpu_shader_material_point_info_glsl,
+ .dependencies = {&gpu_shader_material_hash_library, NULL},
+};
+
static GPUMaterialLibrary gpu_shader_material_principled_library = {
.code = datatoc_gpu_shader_material_principled_glsl,
.dependencies = {NULL},
@@ -644,6 +650,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = {
&gpu_shader_material_output_material_library,
&gpu_shader_material_output_world_library,
&gpu_shader_material_particle_info_library,
+ &gpu_shader_material_point_info_library,
&gpu_shader_material_principled_library,
&gpu_shader_material_refraction_library,
&gpu_shader_material_rgb_curves_library,
diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c
index aadb52fb49c..afef0e70ccc 100644
--- a/source/blender/gpu/intern/gpu_select.c
+++ b/source/blender/gpu/intern/gpu_select.c
@@ -43,24 +43,33 @@
* \{ */
/* Internal algorithm used */
-enum {
+typedef enum eGPUSelectAlgo {
/** glBegin/EndQuery(GL_SAMPLES_PASSED... ), `gpu_select_query.c`
* Only sets 4th component (ID) correctly. */
ALGO_GL_QUERY = 1,
/** Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.c`
* Only sets 4th component (ID) correctly. */
ALGO_GL_PICK = 2,
-};
+} eGPUSelectAlgo;
typedef struct GPUSelectState {
/* To ignore selection id calls when not initialized */
bool select_is_active;
/* mode of operation */
- char mode;
+ eGPUSelectMode mode;
/* internal algorithm for selection */
- char algorithm;
+ eGPUSelectAlgo algorithm;
/* allow GPU_select_begin/end without drawing */
bool use_cache;
+ /**
+ * Signifies that #GPU_select_cache_begin has been called,
+ * future calls to #GPU_select_begin should initialize the cache.
+ *
+ * \note #GPU_select_cache_begin could perform initialization but doesn't as it's inconvenient
+ * for callers making the cache begin/end calls outside lower level selection logic
+ * where the `mode` to pass to #GPU_select_begin yet isn't known.
+ */
+ bool use_cache_needs_init;
} GPUSelectState;
static GPUSelectState g_select_state = {0};
@@ -71,7 +80,11 @@ static GPUSelectState g_select_state = {0};
/** \name Public API
* \{ */
-void GPU_select_begin(uint *buffer, uint bufsize, const rcti *input, char mode, int oldhits)
+void GPU_select_begin(GPUSelectResult *buffer,
+ const uint buffer_len,
+ const rcti *input,
+ eGPUSelectMode mode,
+ int oldhits)
{
if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
/* In the case hits was '-1',
@@ -90,15 +103,32 @@ void GPU_select_begin(uint *buffer, uint bufsize, const rcti *input, char mode,
g_select_state.algorithm = ALGO_GL_QUERY;
}
+ /* This function is called when cache has already been initialized,
+ * so only manipulate cache values when cache is pending. */
+ if (g_select_state.use_cache_needs_init) {
+ g_select_state.use_cache_needs_init = false;
+
+ switch (g_select_state.algorithm) {
+ case ALGO_GL_QUERY: {
+ g_select_state.use_cache = false;
+ break;
+ }
+ default: {
+ g_select_state.use_cache = true;
+ gpu_select_pick_cache_begin();
+ break;
+ }
+ }
+ }
+
switch (g_select_state.algorithm) {
case ALGO_GL_QUERY: {
- g_select_state.use_cache = false;
- gpu_select_query_begin((uint(*)[4])buffer, bufsize / 4, input, mode, oldhits);
+ gpu_select_query_begin(buffer, buffer_len, input, mode, oldhits);
break;
}
default: /* ALGO_GL_PICK */
{
- gpu_select_pick_begin((uint(*)[4])buffer, bufsize / 4, input, mode);
+ gpu_select_pick_begin(buffer, buffer_len, input, mode);
break;
}
}
@@ -154,12 +184,13 @@ uint GPU_select_end(void)
void GPU_select_cache_begin(void)
{
- /* validate on GPU_select_begin, clear if not supported */
- BLI_assert(g_select_state.use_cache == false);
- g_select_state.use_cache = true;
- if (g_select_state.algorithm == ALGO_GL_PICK) {
- gpu_select_pick_cache_begin();
- }
+ BLI_assert(g_select_state.select_is_active == false);
+ /* Ensure #GPU_select_cache_end is always called. */
+ BLI_assert(g_select_state.use_cache_needs_init == false);
+
+ /* Signal that cache should be used, instead of calling the algorithms cache-begin function.
+ * This is more convenient as the exact method of selection may not be known by the caller. */
+ g_select_state.use_cache_needs_init = true;
}
void GPU_select_cache_load_id(void)
@@ -173,9 +204,12 @@ void GPU_select_cache_load_id(void)
void GPU_select_cache_end(void)
{
if (g_select_state.algorithm == ALGO_GL_PICK) {
+ BLI_assert(g_select_state.use_cache == true);
gpu_select_pick_cache_end();
}
g_select_state.use_cache = false;
+ /* Paranoid assignment, should already be false. */
+ g_select_state.use_cache_needs_init = false;
}
bool GPU_select_is_cached(void)
@@ -189,35 +223,35 @@ bool GPU_select_is_cached(void)
/** \name Utilities
* \{ */
-const uint *GPU_select_buffer_near(const uint *buffer, int hits)
+const GPUSelectResult *GPU_select_buffer_near(const GPUSelectResult *buffer, int hits)
{
- const uint *buffer_near = NULL;
+ const GPUSelectResult *buffer_near = NULL;
uint depth_min = (uint)-1;
for (int i = 0; i < hits; i++) {
- if (buffer[1] < depth_min) {
- BLI_assert(buffer[3] != -1);
- depth_min = buffer[1];
+ if (buffer->depth < depth_min) {
+ BLI_assert(buffer->id != -1);
+ depth_min = buffer->depth;
buffer_near = buffer;
}
- buffer += 4;
+ buffer++;
}
return buffer_near;
}
-uint GPU_select_buffer_remove_by_id(uint *buffer, int hits, uint select_id)
+uint GPU_select_buffer_remove_by_id(GPUSelectResult *buffer, int hits, uint select_id)
{
- uint *buffer_src = buffer;
- uint *buffer_dst = buffer;
+ GPUSelectResult *buffer_src = buffer;
+ GPUSelectResult *buffer_dst = buffer;
int hits_final = 0;
for (int i = 0; i < hits; i++) {
- if (buffer_src[3] != select_id) {
+ if (buffer_src->id != select_id) {
if (buffer_dst != buffer_src) {
- memcpy(buffer_dst, buffer_src, sizeof(int[4]));
+ memcpy(buffer_dst, buffer_src, sizeof(GPUSelectResult));
}
- buffer_dst += 4;
+ buffer_dst++;
hits_final += 1;
}
- buffer_src += 4;
+ buffer_src++;
}
return hits_final;
}
diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c
index 737e4c6e874..a5c0b2b1a14 100644
--- a/source/blender/gpu/intern/gpu_select_pick.c
+++ b/source/blender/gpu/intern/gpu_select_pick.c
@@ -55,15 +55,19 @@
/** \name #SubRectStride
* \{ */
-/* For looping over a sub-region of a rect, could be moved into 'rct.c'. */
+/** For looping over a sub-region of a #rcti, could be moved into 'rct.c'. */
typedef struct SubRectStride {
- uint start; /* start here */
- uint span; /* read these */
- uint span_len; /* len times (read span 'len' times). */
- uint skip; /* skip those */
+ /** Start here. */
+ uint start;
+ /** Read these. */
+ uint span;
+ /** `len` times (read span 'len' times). */
+ uint span_len;
+ /** Skip those. */
+ uint skip;
} SubRectStride;
-/* we may want to change back to float if uint isn't well supported */
+/** We may want to change back to float if `uint` isn't well supported. */
typedef uint depth_t;
/**
@@ -104,11 +108,11 @@ BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr)
/* -------------------------------------------------------------------- */
/** \name #DepthBufCache
*
- * Result of reading #glReadPixels,
+ * Result of reading #GPU_framebuffer_read_depth,
* use for both cache and non-cached storage.
* \{ */
-/** Store result of #glReadPixels. */
+/** Store result of #GPU_framebuffer_read_depth. */
typedef struct DepthBufCache {
struct DepthBufCache *next, *prev;
uint id;
@@ -174,7 +178,7 @@ static bool depth_buf_subrect_depth_any_filled(const DepthBufCache *rect_src,
const DepthBufCache *rect_dst,
const SubRectStride *sub_rect)
{
- /* same as above but different rect sizes */
+ /* Same as above but different rectangle sizes. */
const depth_t *prev = rect_src->buf + sub_rect->start;
const depth_t *curr = rect_dst->buf + sub_rect->start;
for (uint i = 0; i < sub_rect->span_len; i++) {
@@ -235,66 +239,68 @@ static int depth_cmp(const void *v1, const void *v2)
/** \name Main Selection Begin/End/Load API
* \{ */
-/* depth sorting */
+/** Depth sorting. */
typedef struct GPUPickState {
- /* cache on initialization */
- uint (*buffer)[4];
+ /** Cache on initialization. */
+ GPUSelectResult *buffer;
+ uint buffer_len;
+ /** Mode of this operation. */
+ eGPUSelectMode mode;
- /* Buffer size (stores number of integers, for actual size multiply by sizeof integer). */
- uint bufsize;
- /* mode of operation */
- char mode;
-
- /* OpenGL drawing, never use when (is_cached == true). */
+ /** GPU drawing, never use when `is_cached == true`. */
struct {
- /* The current depth, accumulated as we draw */
+ /** The current depth, accumulated while drawing. */
DepthBufCache *rect_depth;
- /* Scratch buffer, avoid allocs every time (when not caching) */
+ /** Scratch buffer, avoid allocations every time (when not caching). */
DepthBufCache *rect_depth_test;
- /* Pass to glReadPixels (x, y, w, h) */
+ /** Pass to `GPU_framebuffer_read_depth(x, y, w, h)`. */
int clip_readpixels[4];
- /* Set after first draw */
+ /** Set after first draw. */
bool is_init;
uint prev_id;
- } gl;
+ } gpu;
- /* src: data stored in 'cache' and 'gl',
- * dst: use when cached region is smaller (where src -> dst isn't 1:1) */
+ /**
+ * `src`: data stored in 'cache' and 'gpu',
+ * `dst`: use when cached region is smaller (where `src` -> `dst` isn't 1:1).
+ */
struct {
rcti clip_rect;
uint rect_len;
} src, dst;
- /* Store cache between `GPU_select_cache_begin/end` */
+ /** Store cache between `GPU_select_cache_begin/end` */
bool use_cache;
bool is_cached;
struct {
- /* Cleanup used for iterating over both source and destination buffers:
- * src.clip_rect -> dst.clip_rect */
+ /**
+ * Cleanup used for iterating over both source and destination buffers:
+ * `src.clip_rect` -> `dst.clip_rect`.
+ */
SubRectStride sub_rect;
- /* List of DepthBufCache, sized of 'src.clip_rect' */
+ /** List of #DepthBufCache, sized of 'src.clip_rect'. */
ListBase bufs;
} cache;
- /* Picking methods. */
+ /** Picking methods. */
union {
- /* GPU_SELECT_PICK_ALL */
+ /** #GPU_SELECT_PICK_ALL */
struct {
DepthID *hits;
uint hits_len;
uint hits_len_alloc;
} all;
- /* GPU_SELECT_PICK_NEAREST */
+ /** #GPU_SELECT_PICK_NEAREST */
struct {
uint *rect_id;
} nearest;
};
- /* Previous state to restore after drawing. */
+ /** Previous state to restore after drawing. */
int viewport[4];
int scissor[4];
eGPUWriteMask write_mask;
@@ -303,37 +309,44 @@ typedef struct GPUPickState {
static GPUPickState g_pick_state = {0};
-void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, char mode)
+void gpu_select_pick_begin(GPUSelectResult *buffer,
+ const uint buffer_len,
+ const rcti *input,
+ eGPUSelectMode mode)
{
GPUPickState *ps = &g_pick_state;
#ifdef DEBUG_PRINT
- printf("%s: mode=%d, use_cache=%d, is_cache=%d\n", __func__, mode, ps->use_cache, ps->is_cached);
+ printf("%s: mode=%d, use_cache=%d, is_cache=%d\n",
+ __func__,
+ (int)mode,
+ ps->use_cache,
+ ps->is_cached);
#endif
GPU_debug_group_begin("Selection Pick");
- ps->bufsize = bufsize;
ps->buffer = buffer;
+ ps->buffer_len = buffer_len;
ps->mode = mode;
const uint rect_len = (uint)(BLI_rcti_size_x(input) * BLI_rcti_size_y(input));
ps->dst.clip_rect = *input;
ps->dst.rect_len = rect_len;
- /* Restrict OpenGL operations for when we don't have cache */
+ /* Avoids unnecessary GPU operations when cache is available and they are unnecessary. */
if (ps->is_cached == false) {
ps->write_mask = GPU_write_mask_get();
ps->depth_test = GPU_depth_test_get();
GPU_scissor_get(ps->scissor);
- /* disable writing to the framebuffer */
+ /* Disable writing to the frame-buffer. */
GPU_color_mask(false, false, false, false);
GPU_depth_mask(true);
- /* Always use #GL_LEQUAL even though GPU_SELECT_PICK_ALL always clears the buffer. This is
- * because individual objects themselves might have sections that overlap and we need these
- * to have the correct distance information. */
+ /* Always use #GPU_DEPTH_LESS_EQUAL even though #GPU_SELECT_PICK_ALL always clears the buffer.
+ * This is because individual objects themselves might have sections that overlap and we need
+ * these to have the correct distance information. */
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
float viewport[4];
@@ -342,35 +355,35 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c
ps->src.clip_rect = *input;
ps->src.rect_len = rect_len;
- ps->gl.clip_readpixels[0] = (int)viewport[0];
- ps->gl.clip_readpixels[1] = (int)viewport[1];
- ps->gl.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect);
- ps->gl.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect);
+ ps->gpu.clip_readpixels[0] = (int)viewport[0];
+ ps->gpu.clip_readpixels[1] = (int)viewport[1];
+ ps->gpu.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect);
+ ps->gpu.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect);
- GPU_viewport(UNPACK4(ps->gl.clip_readpixels));
+ GPU_viewport(UNPACK4(ps->gpu.clip_readpixels));
/* It's possible we don't want to clear depth buffer,
* so existing elements are masked by current z-buffer. */
GPU_clear_depth(1.0f);
/* scratch buffer (read new values here) */
- ps->gl.rect_depth_test = depth_buf_malloc(rect_len);
- ps->gl.rect_depth = depth_buf_malloc(rect_len);
+ ps->gpu.rect_depth_test = depth_buf_malloc(rect_len);
+ ps->gpu.rect_depth = depth_buf_malloc(rect_len);
- /* set initial 'far' value */
+ /* Set initial 'far' value. */
for (uint i = 0; i < rect_len; i++) {
- ps->gl.rect_depth->buf[i] = DEPTH_MAX;
+ ps->gpu.rect_depth->buf[i] = DEPTH_MAX;
}
- ps->gl.is_init = false;
- ps->gl.prev_id = 0;
+ ps->gpu.is_init = false;
+ ps->gpu.prev_id = 0;
}
else {
- /* Using cache (ps->is_cached == true) */
- /* src.clip_rect -> dst.clip_rect */
+ /* Using cache `ps->is_cached == true`. */
+ /* `src.clip_rect` -> `dst.clip_rect`. */
rect_subregion_stride_calc(&ps->src.clip_rect, &ps->dst.clip_rect, &ps->cache.sub_rect);
- BLI_assert(ps->gl.rect_depth == NULL);
- BLI_assert(ps->gl.rect_depth_test == NULL);
+ BLI_assert(ps->gpu.rect_depth == NULL);
+ BLI_assert(ps->gpu.rect_depth_test == NULL);
}
if (mode == GPU_SELECT_PICK_ALL) {
@@ -379,7 +392,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c
ps->all.hits_len_alloc = ALLOC_DEPTHS;
}
else {
- /* Set to 0xff for SELECT_ID_NONE */
+ /* Set to 0xff for #SELECT_ID_NONE. */
ps->nearest.rect_id = MEM_mallocN(sizeof(uint) * ps->dst.rect_len, __func__);
memset(ps->nearest.rect_id, 0xff, sizeof(uint) * ps->dst.rect_len);
}
@@ -411,7 +424,7 @@ static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr)
}
}
else {
- /* same as above but different rect sizes */
+ /* Same as above but different rectangle sizes. */
const depth_t *curr = rect_curr->buf + ps->cache.sub_rect.start;
for (uint i = 0; i < ps->cache.sub_rect.span_len; i++) {
const depth_t *curr_end = curr + ps->cache.sub_rect.span;
@@ -424,7 +437,7 @@ static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr)
#undef EVAL_TEST
- /* ensure enough space */
+ /* Ensure enough space. */
if (UNLIKELY(ps->all.hits_len == ps->all.hits_len_alloc)) {
ps->all.hits_len_alloc += ALLOC_DEPTHS;
ps->all.hits = MEM_reallocN(ps->all.hits, ps->all.hits_len_alloc * sizeof(*ps->all.hits));
@@ -439,7 +452,7 @@ static void gpu_select_load_id_pass_nearest(const DepthBufCache *rect_prev,
{
GPUPickState *ps = &g_pick_state;
const uint id = rect_curr->id;
- /* keep track each pixels ID in 'nearest.rect_id' */
+ /* Keep track each pixels ID in `nearest.rect_id`. */
if (id != SELECT_ID_NONE) {
uint *id_ptr = ps->nearest.rect_id;
@@ -483,8 +496,8 @@ bool gpu_select_pick_load_id(uint id, bool end)
{
GPUPickState *ps = &g_pick_state;
- if (ps->gl.is_init) {
- if (id == ps->gl.prev_id && !end) {
+ if (ps->gpu.is_init) {
+ if (id == ps->gpu.prev_id && !end) {
/* No need to read if we are still drawing for the same id since
* all these depths will be merged / de-duplicated in the end. */
return true;
@@ -493,21 +506,21 @@ bool gpu_select_pick_load_id(uint id, bool end)
const uint rect_len = ps->src.rect_len;
GPUFrameBuffer *fb = GPU_framebuffer_active_get();
GPU_framebuffer_read_depth(
- fb, UNPACK4(ps->gl.clip_readpixels), GPU_DATA_UINT, ps->gl.rect_depth_test->buf);
+ fb, UNPACK4(ps->gpu.clip_readpixels), GPU_DATA_UINT, ps->gpu.rect_depth_test->buf);
/* Perform initial check since most cases the array remains unchanged. */
bool do_pass = false;
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
- if (depth_buf_rect_depth_any(ps->gl.rect_depth_test, rect_len)) {
- ps->gl.rect_depth_test->id = ps->gl.prev_id;
- gpu_select_load_id_pass_all(ps->gl.rect_depth_test);
+ if (depth_buf_rect_depth_any(ps->gpu.rect_depth_test, rect_len)) {
+ ps->gpu.rect_depth_test->id = ps->gpu.prev_id;
+ gpu_select_load_id_pass_all(ps->gpu.rect_depth_test);
do_pass = true;
}
}
else {
- if (depth_buf_rect_depth_any_filled(ps->gl.rect_depth, ps->gl.rect_depth_test, rect_len)) {
- ps->gl.rect_depth_test->id = ps->gl.prev_id;
- gpu_select_load_id_pass_nearest(ps->gl.rect_depth, ps->gl.rect_depth_test);
+ if (depth_buf_rect_depth_any_filled(ps->gpu.rect_depth, ps->gpu.rect_depth_test, rect_len)) {
+ ps->gpu.rect_depth_test->id = ps->gpu.prev_id;
+ gpu_select_load_id_pass_nearest(ps->gpu.rect_depth, ps->gpu.rect_depth_test);
do_pass = true;
}
}
@@ -515,11 +528,11 @@ bool gpu_select_pick_load_id(uint id, bool end)
if (do_pass) {
/* Store depth in cache */
if (ps->use_cache) {
- BLI_addtail(&ps->cache.bufs, ps->gl.rect_depth);
- ps->gl.rect_depth = depth_buf_malloc(ps->src.rect_len);
+ BLI_addtail(&ps->cache.bufs, ps->gpu.rect_depth);
+ ps->gpu.rect_depth = depth_buf_malloc(ps->src.rect_len);
}
- SWAP(DepthBufCache *, ps->gl.rect_depth, ps->gl.rect_depth_test);
+ SWAP(DepthBufCache *, ps->gpu.rect_depth, ps->gpu.rect_depth_test);
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
/* (fclem) This is to be on the safe side. I don't know if this is required. */
@@ -533,8 +546,8 @@ bool gpu_select_pick_load_id(uint id, bool end)
}
}
- ps->gl.is_init = true;
- ps->gl.prev_id = id;
+ ps->gpu.is_init = true;
+ ps->gpu.prev_id = id;
return true;
}
@@ -548,9 +561,9 @@ uint gpu_select_pick_end(void)
#endif
if (ps->is_cached == false) {
- if (ps->gl.is_init) {
+ if (ps->gpu.is_init) {
/* force finishing last pass */
- gpu_select_pick_load_id(ps->gl.prev_id, true);
+ gpu_select_pick_load_id(ps->gpu.prev_id, true);
}
GPU_write_mask(ps->write_mask);
GPU_depth_test(ps->depth_test);
@@ -559,39 +572,39 @@ uint gpu_select_pick_end(void)
GPU_debug_group_end();
- /* assign but never free directly since it may be in cache */
+ /* Assign but never free directly since it may be in cache. */
DepthBufCache *rect_depth_final;
/* Store depth in cache */
if (ps->use_cache && !ps->is_cached) {
- BLI_addtail(&ps->cache.bufs, ps->gl.rect_depth);
- ps->gl.rect_depth = NULL;
+ BLI_addtail(&ps->cache.bufs, ps->gpu.rect_depth);
+ ps->gpu.rect_depth = NULL;
rect_depth_final = ps->cache.bufs.last;
}
else if (ps->is_cached) {
rect_depth_final = ps->cache.bufs.last;
}
else {
- /* common case, no cache */
- rect_depth_final = ps->gl.rect_depth;
+ /* Common case, no cache. */
+ rect_depth_final = ps->gpu.rect_depth;
}
- uint maxhits = g_pick_state.bufsize;
+ uint maxhits = g_pick_state.buffer_len;
DepthID *depth_data;
uint depth_data_len = 0;
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
depth_data = ps->all.hits;
depth_data_len = ps->all.hits_len;
- /* move ownership */
+ /* Move ownership. */
ps->all.hits = NULL;
ps->all.hits_len = 0;
ps->all.hits_len_alloc = 0;
}
else {
- /* GPU_SELECT_PICK_NEAREST */
+ /* #GPU_SELECT_PICK_NEAREST */
- /* Over alloc (unlikely we have as many depths as pixels) */
+ /* Over allocate (unlikely we have as many depths as pixels). */
uint depth_data_len_first_pass = 0;
depth_data = MEM_mallocN(ps->dst.rect_len * sizeof(*depth_data), __func__);
@@ -624,7 +637,7 @@ uint gpu_select_pick_end(void)
}
}
else {
- /* same as above but different rect sizes */
+ /* Same as above but different rectangle sizes. */
uint i_src = ps->cache.sub_rect.start, i_dst = 0;
for (uint j = 0; j < ps->cache.sub_rect.span_len; j++) {
const uint i_src_end = i_src + ps->cache.sub_rect.span;
@@ -640,7 +653,7 @@ uint gpu_select_pick_end(void)
qsort(depth_data, depth_data_len_first_pass, sizeof(DepthID), depth_id_cmp);
- /* Sort by ID's then keep the best depth for each ID */
+ /* Sort by ID's then keep the best depth for each ID. */
depth_data_len = 0;
{
DepthID *depth_last = NULL;
@@ -657,25 +670,22 @@ uint gpu_select_pick_end(void)
}
/* Finally sort each unique (id, depth) pair by depth
- * so the final hit-list is sorted by depth (nearest first) */
+ * so the final hit-list is sorted by depth (nearest first). */
uint hits = 0;
if (depth_data_len > maxhits) {
hits = (uint)-1;
}
else {
- /* leave sorting up to the caller */
+ /* Leave sorting up to the caller. */
qsort(depth_data, depth_data_len, sizeof(DepthID), depth_cmp);
for (uint i = 0; i < depth_data_len; i++) {
#ifdef DEBUG_PRINT
printf(" hit: %u: depth %u\n", depth_data[i].id, depth_data[i].depth);
#endif
- /* first 3 are dummy values */
- g_pick_state.buffer[hits][0] = 1;
- g_pick_state.buffer[hits][1] = 0x0; /* depth_data[i].depth; */ /* unused */
- g_pick_state.buffer[hits][2] = 0x0; /* z-far is currently never used. */
- g_pick_state.buffer[hits][3] = depth_data[i].id;
+ g_pick_state.buffer[hits].depth = depth_data[i].depth;
+ g_pick_state.buffer[hits].id = depth_data[i].id;
hits++;
}
BLI_assert(hits < maxhits);
@@ -683,8 +693,8 @@ uint gpu_select_pick_end(void)
MEM_freeN(depth_data);
- MEM_SAFE_FREE(ps->gl.rect_depth);
- MEM_SAFE_FREE(ps->gl.rect_depth_test);
+ MEM_SAFE_FREE(ps->gpu.rect_depth);
+ MEM_SAFE_FREE(ps->gpu.rect_depth_test);
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
/* 'hits' already freed as 'depth_data' */
@@ -744,8 +754,8 @@ void gpu_select_pick_cache_load_id(void)
#endif
LISTBASE_FOREACH (DepthBufCache *, rect_depth, &ps->cache.bufs) {
if (rect_depth->next != NULL) {
- /* we know the buffers differ, but this sub-region may not.
- * double check before adding an id-pass */
+ /* We know the buffers differ, but this sub-region may not.
+ * Double check before adding an id-pass. */
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
if (depth_buf_subrect_depth_any(rect_depth->next, &ps->cache.sub_rect)) {
gpu_select_load_id_pass_all(rect_depth->next);
diff --git a/source/blender/gpu/intern/gpu_select_private.h b/source/blender/gpu/intern/gpu_select_private.h
index e5a84a037a6..0ec9f083c07 100644
--- a/source/blender/gpu/intern/gpu_select_private.h
+++ b/source/blender/gpu/intern/gpu_select_private.h
@@ -31,7 +31,10 @@ extern "C" {
/* gpu_select_pick */
-void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, char mode);
+void gpu_select_pick_begin(GPUSelectResult *buffer,
+ uint buffer_len,
+ const rcti *input,
+ eGPUSelectMode mode);
bool gpu_select_pick_load_id(uint id, bool end);
uint gpu_select_pick_end(void);
@@ -46,7 +49,7 @@ void gpu_select_pick_cache_load_id(void);
/* gpu_select_sample_query */
void gpu_select_query_begin(
- uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits);
+ GPUSelectResult *buffer, uint buffer_len, const rcti *input, eGPUSelectMode mode, int oldhits);
bool gpu_select_query_load_id(uint id);
uint gpu_select_query_end(void);
diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc
index a430d4a9d62..7559358aaca 100644
--- a/source/blender/gpu/intern/gpu_select_sample_query.cc
+++ b/source/blender/gpu/intern/gpu_select_sample_query.cc
@@ -48,22 +48,22 @@ using namespace blender;
using namespace blender::gpu;
struct GPUSelectQueryState {
- /* Tracks whether a query has been issued so that gpu_load_id can end the previous one. */
+ /** Tracks whether a query has been issued so that gpu_load_id can end the previous one. */
bool query_issued;
- /* GPU queries abstraction. Contains an array of queries. */
+ /** GPU queries abstraction. Contains an array of queries. */
QueryPool *queries;
- /* Array holding the id corresponding id to each query. */
+ /** Array holding the id corresponding id to each query. */
Vector<uint> *ids;
- /* Cache on initialization. */
- uint (*buffer)[4];
- /* Buffer size (stores number of integers, for actual size multiply by `sizeof(int)`). */
- uint bufsize;
- /* Mode of operation. */
- char mode;
+ /** Cache on initialization. */
+ GPUSelectResult *buffer;
+ /** The capacity of the `buffer` array. */
+ uint buffer_len;
+ /** Mode of operation. */
+ eGPUSelectMode mode;
uint index;
int oldhits;
- /* Previous state to restore after drawing. */
+ /** Previous state to restore after drawing. */
int viewport[4];
int scissor[4];
eGPUWriteMask write_mask;
@@ -72,14 +72,17 @@ struct GPUSelectQueryState {
static GPUSelectQueryState g_query_state = {false};
-void gpu_select_query_begin(
- uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits)
+void gpu_select_query_begin(GPUSelectResult *buffer,
+ uint buffer_len,
+ const rcti *input,
+ const eGPUSelectMode mode,
+ int oldhits)
{
GPU_debug_group_begin("Selection Queries");
g_query_state.query_issued = false;
- g_query_state.bufsize = bufsize;
g_query_state.buffer = buffer;
+ g_query_state.buffer_len = buffer_len;
g_query_state.mode = mode;
g_query_state.index = 0;
g_query_state.oldhits = oldhits;
@@ -111,7 +114,7 @@ void gpu_select_query_begin(
/* occlusion queries operates on fragments that pass tests and since we are interested on all
* objects in the view frustum independently of their order, we need to disable the depth test */
if (mode == GPU_SELECT_ALL) {
- /* glQueries on Windows+Intel drivers only works with depth testing turned on.
+ /* #glQueries on Windows+Intel drivers only works with depth testing turned on.
* See T62947 for details */
GPU_depth_test(GPU_DEPTH_ALWAYS);
GPU_depth_mask(true);
@@ -138,10 +141,11 @@ bool gpu_select_query_load_id(uint id)
g_query_state.query_issued = true;
if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS) {
- /* Second pass should never run if first pass fails, can read past 'bufsize' in this case. */
+ /* Second pass should never run if first pass fails,
+ * can read past `buffer_len` in this case. */
BLI_assert(g_query_state.oldhits != -1);
if (g_query_state.index < g_query_state.oldhits) {
- if (g_query_state.buffer[g_query_state.index][3] == id) {
+ if (g_query_state.buffer[g_query_state.index].id == id) {
g_query_state.index++;
return true;
}
@@ -154,7 +158,7 @@ bool gpu_select_query_load_id(uint id)
uint gpu_select_query_end()
{
uint hits = 0;
- const uint maxhits = g_query_state.bufsize;
+ const uint maxhits = g_query_state.buffer_len;
if (g_query_state.query_issued) {
g_query_state.queries->end_query();
@@ -168,10 +172,8 @@ uint gpu_select_query_end()
if (result[i] != 0) {
if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) {
if (hits < maxhits) {
- g_query_state.buffer[hits][0] = 1;
- g_query_state.buffer[hits][1] = 0xFFFF;
- g_query_state.buffer[hits][2] = 0xFFFF;
- g_query_state.buffer[hits][3] = ids[i];
+ g_query_state.buffer[hits].depth = 0xFFFF;
+ g_query_state.buffer[hits].id = ids[i];
hits++;
}
else {
@@ -183,9 +185,8 @@ uint gpu_select_query_end()
int j;
/* search in buffer and make selected object first */
for (j = 0; j < g_query_state.oldhits; j++) {
- if (g_query_state.buffer[j][3] == ids[i]) {
- g_query_state.buffer[j][1] = 0;
- g_query_state.buffer[j][2] = 0;
+ if (g_query_state.buffer[j].id == ids[i]) {
+ g_query_state.buffer[j].depth = 0;
}
}
break;
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index 3b41e804fd4..7db34917ad7 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -26,6 +26,7 @@
#include "BLI_string_utils.h"
#include "GPU_capabilities.h"
+#include "GPU_debug.h"
#include "GPU_matrix.h"
#include "GPU_platform.h"
@@ -249,6 +250,23 @@ GPUShader *GPU_shader_create_compute(const char *computecode,
shname);
}
+const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name)
+{
+ return gpu_shader_create_info_get(info_name);
+}
+
+GPUShader *GPU_shader_create_from_info_name(const char *info_name)
+{
+ using namespace blender::gpu::shader;
+ const GPUShaderCreateInfo *_info = gpu_shader_create_info_get(info_name);
+ const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(_info);
+ if (!info.do_static_compilation_) {
+ printf("Warning: Trying to compile \"%s\" which was not marked for static compilation.\n",
+ info.name_.c_str());
+ }
+ return GPU_shader_create_from_info(_info);
+}
+
GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
{
using namespace blender::gpu::shader;
@@ -256,6 +274,8 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
const_cast<ShaderCreateInfo &>(info).finalize();
+ GPU_debug_group_begin(GPU_DEBUG_SHADER_COMPILATION_GROUP);
+
/* At least a vertex shader and a fragment shader are required, or only a compute shader. */
if (info.compute_source_.is_empty()) {
if (info.vertex_source_.is_empty()) {
@@ -284,22 +304,23 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
std::string defines = shader->defines_declare(info);
std::string resources = shader->resources_declare(info);
- char *shader_shared_utils = nullptr;
defines += "#define USE_GPU_SHADER_CREATE_INFO\n";
- Vector<char *> typedefs;
- for (auto filename : info.typedef_sources_) {
- typedefs.append(gpu_shader_dependency_get_source(filename.c_str()));
+ Vector<const char *> typedefs;
+ if (!info.typedef_sources_.is_empty() || !info.typedef_source_generated.empty()) {
+ typedefs.append(gpu_shader_dependency_get_source("GPU_shader_shared_utils.h").c_str());
+ }
+ if (!info.typedef_source_generated.empty()) {
+ typedefs.append(info.typedef_source_generated.c_str());
}
- if (!typedefs.is_empty()) {
- shader_shared_utils = gpu_shader_dependency_get_source("gpu_shader_shared_utils.h");
+ for (auto filename : info.typedef_sources_) {
+ typedefs.append(gpu_shader_dependency_get_source(filename).c_str());
}
if (!info.vertex_source_.is_empty()) {
- uint32_t builtins = 0;
+ auto code = gpu_shader_dependency_get_resolved_source(info.vertex_source_);
std::string interface = shader->vertex_interface_declare(info);
- char *code = gpu_shader_dependency_get_resolved_source(info.vertex_source_.c_str(), &builtins);
Vector<const char *> sources;
standard_defines(sources);
@@ -308,26 +329,19 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
sources.append("#define USE_GEOMETRY_SHADER\n");
}
sources.append(defines.c_str());
- if (!typedefs.is_empty()) {
- sources.append(shader_shared_utils);
- }
- for (auto *types : typedefs) {
- sources.append(types);
- }
+ sources.extend(typedefs);
sources.append(resources.c_str());
sources.append(interface.c_str());
- sources.append(code);
+ sources.extend(code);
+ sources.extend(info.dependencies_generated);
+ sources.append(info.vertex_source_generated.c_str());
shader->vertex_shader_from_glsl(sources);
-
- free(code);
}
if (!info.fragment_source_.is_empty()) {
- uint32_t builtins = 0;
+ auto code = gpu_shader_dependency_get_resolved_source(info.fragment_source_);
std::string interface = shader->fragment_interface_declare(info);
- char *code = gpu_shader_dependency_get_resolved_source(info.fragment_source_.c_str(),
- &builtins);
Vector<const char *> sources;
standard_defines(sources);
@@ -336,84 +350,57 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
sources.append("#define USE_GEOMETRY_SHADER\n");
}
sources.append(defines.c_str());
- if (!typedefs.is_empty()) {
- sources.append(shader_shared_utils);
- }
- for (auto *types : typedefs) {
- sources.append(types);
- }
+ sources.extend(typedefs);
sources.append(resources.c_str());
sources.append(interface.c_str());
- sources.append(code);
+ sources.extend(code);
+ sources.extend(info.dependencies_generated);
+ sources.append(info.fragment_source_generated.c_str());
shader->fragment_shader_from_glsl(sources);
-
- free(code);
}
if (!info.geometry_source_.is_empty()) {
- uint32_t builtins = 0;
- std::string interface = shader->geometry_interface_declare(info);
+ auto code = gpu_shader_dependency_get_resolved_source(info.geometry_source_);
std::string layout = shader->geometry_layout_declare(info);
- char *code = gpu_shader_dependency_get_resolved_source(info.geometry_source_.c_str(),
- &builtins);
+ std::string interface = shader->geometry_interface_declare(info);
Vector<const char *> sources;
standard_defines(sources);
sources.append("#define GPU_GEOMETRY_SHADER\n");
sources.append(defines.c_str());
- if (!typedefs.is_empty()) {
- sources.append(shader_shared_utils);
- }
- for (auto *types : typedefs) {
- sources.append(types);
- }
+ sources.extend(typedefs);
sources.append(resources.c_str());
sources.append(layout.c_str());
sources.append(interface.c_str());
- sources.append(code);
+ sources.extend(code);
shader->geometry_shader_from_glsl(sources);
-
- free(code);
}
if (!info.compute_source_.is_empty()) {
- uint32_t builtins = 0;
- char *code = gpu_shader_dependency_get_resolved_source(info.compute_source_.c_str(),
- &builtins);
+ auto code = gpu_shader_dependency_get_resolved_source(info.compute_source_);
+ std::string layout = shader->compute_layout_declare(info);
Vector<const char *> sources;
standard_defines(sources);
sources.append("#define GPU_COMPUTE_SHADER\n");
sources.append(defines.c_str());
- if (!typedefs.is_empty()) {
- sources.append(shader_shared_utils);
- }
- for (auto *types : typedefs) {
- sources.append(types);
- }
+ sources.extend(typedefs);
sources.append(resources.c_str());
- sources.append(code);
+ sources.append(layout.c_str());
+ sources.extend(code);
shader->compute_shader_from_glsl(sources);
-
- free(code);
- }
-
- for (auto *types : typedefs) {
- free(types);
- }
-
- if (shader_shared_utils) {
- free(shader_shared_utils);
}
if (!shader->finalize(&info)) {
delete shader;
+ GPU_debug_group_end();
return nullptr;
}
+ GPU_debug_group_end();
return wrap(shader);
}
diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
index 40e54ab4394..716d6e33a81 100644
--- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
@@ -39,6 +39,8 @@
#include "DNA_userdef_types.h"
+#include "NOD_shader.h"
+
#include "DRW_engine.h"
#include "bmesh.h"
@@ -167,7 +169,7 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *UNUSED(mesh),
/** \name Stubs of BKE_material.h
* \{ */
-void BKE_material_defaults_free_gpu(void)
+void BKE_material_defaults_free_gpu()
{
/* This function is reachable via GPU_exit. */
}
diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c
index 13f1774df03..6b1163fdc78 100644
--- a/source/blender/gpu/intern/gpu_shader_builtin.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -41,9 +41,6 @@
#include "GPU_texture.h"
#include "GPU_uniform_buffer.h"
-/* TODO(jbakker): Need a better way to retrieve create_infos. */
-#include "gpu_shader_create_info_private.hh"
-
/* Adjust these constants as needed. */
#define MAX_DEFINE_LENGTH 256
#define MAX_EXT_DEFINE_LENGTH 512
@@ -192,8 +189,18 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.create_info = "gpu_shader_2D_flat_color"},
[GPU_SHADER_2D_SMOOTH_COLOR] = {.name = "GPU_SHADER_2D_SMOOTH_COLOR",
.create_info = "gpu_shader_2D_smooth_color"},
- [GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE] = {.name = "GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE",
- .create_info = "gpu_shader_2D_image_overlays_merge"},
+ [GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE] =
+ {
+ .name = "GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE",
+#ifdef __APPLE__
+ /* GPUShaderCreateInfo is disabled on MacOS due to mismatch with OCIO shader. See
+ * T95052 for more details. */
+ .vert = datatoc_gpu_shader_2D_image_vert_glsl,
+ .frag = datatoc_gpu_shader_image_overlays_merge_frag_glsl,
+#else
+ .create_info = "gpu_shader_2D_image_overlays_merge",
+#endif
+ },
[GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE] =
{.name = "GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE",
.create_info = "gpu_shader_2D_image_overlays_stereo_merge"},
@@ -356,7 +363,7 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader,
/* common case */
if (sh_cfg == GPU_SHADER_CFG_DEFAULT) {
if (stages->create_info != NULL) {
- *sh_p = GPU_shader_create_from_info(gpu_shader_create_info_get(stages->create_info));
+ *sh_p = GPU_shader_create_from_info_name(stages->create_info);
}
else {
*sh_p = GPU_shader_create_from_arrays_named(
@@ -382,8 +389,7 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader,
GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR));
/* In rare cases geometry shaders calculate clipping themselves. */
if (stages->clipped_create_info != NULL) {
- *sh_p = GPU_shader_create_from_info(
- gpu_shader_create_info_get(stages->clipped_create_info));
+ *sh_p = GPU_shader_create_from_info_name(stages->clipped_create_info);
}
else {
const char *world_clip_lib = datatoc_gpu_shader_cfg_world_clip_lib_glsl;
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc
index 439eb735c95..fe0304828c2 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.cc
+++ b/source/blender/gpu/intern/gpu_shader_create_info.cc
@@ -27,11 +27,15 @@
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
+#include "GPU_capabilities.h"
+#include "GPU_platform.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "gpu_shader_create_info.hh"
#include "gpu_shader_create_info_private.hh"
+#include "gpu_shader_dependency_private.h"
+#include "gpu_shader_private.hh"
#undef GPU_SHADER_INTERFACE_INFO
#undef GPU_SHADER_CREATE_INFO
@@ -51,6 +55,8 @@ void ShaderCreateInfo::finalize()
}
finalized_ = true;
+ Set<StringRefNull> deps_merged;
+
for (auto &info_name : additional_infos_) {
const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(
gpu_shader_create_info_get(info_name.c_str()));
@@ -70,34 +76,136 @@ void ShaderCreateInfo::finalize()
batch_resources_.extend(info.batch_resources_);
pass_resources_.extend(info.pass_resources_);
- typedef_sources_.extend(info.typedef_sources_);
+ typedef_sources_.extend_non_duplicates(info.typedef_sources_);
+
+ validate(info);
- if (info.local_group_size_[0] != 0) {
- BLI_assert(local_group_size_[0] == 0);
- for (int i = 0; i < 3; i++) {
- local_group_size_[i] = info.local_group_size_[i];
+ auto assert_no_overlap = [&](const bool test, const StringRefNull error) {
+ if (!test) {
+ std::cout << name_ << ": Validation failed while merging " << info.name_ << " : ";
+ std::cout << error << std::endl;
+ BLI_assert(0);
}
+ };
+
+ if (!deps_merged.add(info.name_)) {
+ assert_no_overlap(false, "additional info already merged via another info");
}
+
+ if (info.compute_layout_.local_size_x != -1) {
+ assert_no_overlap(compute_layout_.local_size_x == -1, "Compute layout already defined");
+ compute_layout_ = info.compute_layout_;
+ }
+
if (!info.vertex_source_.is_empty()) {
- BLI_assert(vertex_source_.is_empty());
+ assert_no_overlap(vertex_source_.is_empty(), "Vertex source already existing");
vertex_source_ = info.vertex_source_;
}
if (!info.geometry_source_.is_empty()) {
- BLI_assert(geometry_source_.is_empty());
+ assert_no_overlap(geometry_source_.is_empty(), "Geometry source already existing");
geometry_source_ = info.geometry_source_;
geometry_layout_ = info.geometry_layout_;
}
if (!info.fragment_source_.is_empty()) {
- BLI_assert(fragment_source_.is_empty());
+ assert_no_overlap(fragment_source_.is_empty(), "Fragment source already existing");
fragment_source_ = info.fragment_source_;
}
if (!info.compute_source_.is_empty()) {
- BLI_assert(compute_source_.is_empty());
+ assert_no_overlap(compute_source_.is_empty(), "Compute source already existing");
compute_source_ = info.compute_source_;
}
do_static_compilation_ = do_static_compilation_ || info.do_static_compilation_;
}
+
+ if (auto_resource_location_) {
+ int images = 0, samplers = 0, ubos = 0, ssbos = 0;
+
+ auto set_resource_slot = [&](Resource &res) {
+ switch (res.bind_type) {
+ case Resource::BindType::UNIFORM_BUFFER:
+ res.slot = ubos++;
+ break;
+ case Resource::BindType::STORAGE_BUFFER:
+ res.slot = ssbos++;
+ break;
+ case Resource::BindType::SAMPLER:
+ res.slot = samplers++;
+ break;
+ case Resource::BindType::IMAGE:
+ res.slot = images++;
+ break;
+ }
+ };
+
+ for (auto &res : batch_resources_) {
+ set_resource_slot(res);
+ }
+ for (auto &res : pass_resources_) {
+ set_resource_slot(res);
+ }
+ }
+}
+
+void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info)
+{
+ if (!auto_resource_location_) {
+ /* Check same bind-points usage in OGL. */
+ Set<int> images, samplers, ubos, ssbos;
+
+ auto register_resource = [&](const Resource &res) -> bool {
+ switch (res.bind_type) {
+ case Resource::BindType::UNIFORM_BUFFER:
+ return images.add(res.slot);
+ case Resource::BindType::STORAGE_BUFFER:
+ return samplers.add(res.slot);
+ case Resource::BindType::SAMPLER:
+ return ubos.add(res.slot);
+ case Resource::BindType::IMAGE:
+ return ssbos.add(res.slot);
+ default:
+ return false;
+ }
+ };
+
+ auto print_error_msg = [&](const Resource &res) {
+ std::cout << name_ << ": Validation failed : Overlapping ";
+
+ switch (res.bind_type) {
+ case Resource::BindType::UNIFORM_BUFFER:
+ std::cout << "Uniform Buffer " << res.uniformbuf.name;
+ break;
+ case Resource::BindType::STORAGE_BUFFER:
+ std::cout << "Storage Buffer " << res.storagebuf.name;
+ break;
+ case Resource::BindType::SAMPLER:
+ std::cout << "Sampler " << res.sampler.name;
+ break;
+ case Resource::BindType::IMAGE:
+ std::cout << "Image " << res.image.name;
+ break;
+ default:
+ std::cout << "Unknown Type";
+ break;
+ }
+ std::cout << " (" << res.slot << ") while merging " << other_info.name_ << std::endl;
+ };
+
+ for (auto &res : batch_resources_) {
+ if (register_resource(res) == false) {
+ print_error_msg(res);
+ }
+ }
+
+ for (auto &res : pass_resources_) {
+ if (register_resource(res) == false) {
+ print_error_msg(res);
+ }
+ }
+ }
+ {
+ /* TODO(@fclem): Push constant validation. */
+ }
}
} // namespace blender::gpu::shader
@@ -125,12 +233,28 @@ void gpu_shader_create_info_init()
#include "gpu_shader_create_info_list.hh"
/* Baked shader data appended to create infos. */
-/* TODO(jbakker): should call a function with a callback. so we could switch implementations. We
- * cannot compile bf_gpu twice.*/
+/* TODO(jbakker): should call a function with a callback. so we could switch implementations.
+ * We cannot compile bf_gpu twice. */
#ifdef GPU_RUNTIME
# include "gpu_shader_baked.hh"
#endif
+ /* WORKAROUND: Replace draw_mesh info with the legacy one for systems that have problems with UBO
+ * indexing. */
+ if (GPU_type_matches(GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_ANY, GPU_DRIVER_ANY) ||
+ GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY) || GPU_crappy_amd_driver()) {
+ draw_modelmat = draw_modelmat_legacy;
+ }
+
+ for (ShaderCreateInfo *info : g_create_infos->values()) {
+ if (info->do_static_compilation_) {
+ info->builtins_ |= gpu_shader_dependency_get_builtins(info->vertex_source_);
+ info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_);
+ info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_);
+ info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_);
+ }
+ }
+
/* TEST */
// gpu_shader_create_info_compile_all();
}
@@ -150,25 +274,82 @@ void gpu_shader_create_info_exit()
bool gpu_shader_create_info_compile_all()
{
+ using namespace blender::gpu;
+ int success = 0;
+ int total = 0;
for (ShaderCreateInfo *info : g_create_infos->values()) {
if (info->do_static_compilation_) {
- // printf("Compiling %s: ... \n", info->name_.c_str());
+ total++;
GPUShader *shader = GPU_shader_create_from_info(
reinterpret_cast<const GPUShaderCreateInfo *>(info));
if (shader == nullptr) {
printf("Compilation %s Failed\n", info->name_.c_str());
- return false;
+ }
+ else {
+ success++;
+
+#if 0 /* TODO(fclem): This is too verbose for now. Make it a cmake option. */
+ /* Test if any resource is optimized out and print a warning if that's the case. */
+ /* TODO(fclem): Limit this to OpenGL backend. */
+ const ShaderInterface *interface = unwrap(shader)->interface;
+
+ blender::Vector<ShaderCreateInfo::Resource> all_resources;
+ all_resources.extend(info->pass_resources_);
+ all_resources.extend(info->batch_resources_);
+
+ for (ShaderCreateInfo::Resource &res : all_resources) {
+ blender::StringRefNull name = "";
+ const ShaderInput *input = nullptr;
+
+ switch (res.bind_type) {
+ case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
+ input = interface->ubo_get(res.slot);
+ name = res.uniformbuf.name;
+ break;
+ case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
+ input = interface->ssbo_get(res.slot);
+ name = res.storagebuf.name;
+ break;
+ case ShaderCreateInfo::Resource::BindType::SAMPLER:
+ input = interface->texture_get(res.slot);
+ name = res.sampler.name;
+ break;
+ case ShaderCreateInfo::Resource::BindType::IMAGE:
+ input = interface->texture_get(res.slot);
+ name = res.image.name;
+ break;
+ }
+
+ if (input == nullptr) {
+ std::cout << "Error: " << info->name_;
+ std::cout << ": Resource « " << name << " » not found in the shader interface\n";
+ }
+ else if (input->location == -1) {
+ std::cout << "Warning: " << info->name_;
+ std::cout << ": Resource « " << name << " » is optimized out\n";
+ }
+ }
+#endif
}
GPU_shader_free(shader);
- // printf("Success\n");
}
}
- return true;
+ printf("===============================\n");
+ printf("Shader Test compilation result: \n");
+ printf("%d Total\n", total);
+ printf("%d Passed\n", success);
+ printf("%d Failed\n", total - success);
+ printf("===============================\n");
+ return success == total;
}
/* Runtime create infos are not registered in the dictionary and cannot be searched. */
const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name)
{
+ if (g_create_infos->contains(info_name) == false) {
+ printf("Error: Cannot find shader create info named \"%s\"\n", info_name);
+ return nullptr;
+ }
ShaderCreateInfo *info = g_create_infos->lookup(info_name);
return reinterpret_cast<const GPUShaderCreateInfo *>(info);
}
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh
index a1ebdb0ec51..dafa741977c 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.hh
+++ b/source/blender/gpu/intern/gpu_shader_create_info.hh
@@ -30,8 +30,11 @@
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
+#include "GPU_material.h"
#include "GPU_texture.h"
+#include <iostream>
+
namespace blender::gpu::shader {
#ifndef GPU_SHADER_CREATE_INFO
@@ -62,13 +65,74 @@ enum class Type {
BOOL,
};
+/* All of these functions is a bit out of place */
+static inline Type to_type(const eGPUType type)
+{
+ switch (type) {
+ case GPU_FLOAT:
+ return Type::FLOAT;
+ case GPU_VEC2:
+ return Type::VEC2;
+ case GPU_VEC3:
+ return Type::VEC3;
+ case GPU_VEC4:
+ return Type::VEC4;
+ case GPU_MAT3:
+ return Type::MAT3;
+ case GPU_MAT4:
+ return Type::MAT4;
+ default:
+ BLI_assert_msg(0, "Error: Cannot convert eGPUType to shader::Type.");
+ return Type::FLOAT;
+ }
+}
+
+static inline std::ostream &operator<<(std::ostream &stream, const Type type)
+{
+ switch (type) {
+ case Type::FLOAT:
+ return stream << "float";
+ case Type::VEC2:
+ return stream << "vec2";
+ case Type::VEC3:
+ return stream << "vec3";
+ case Type::VEC4:
+ return stream << "vec4";
+ case Type::MAT3:
+ return stream << "mat3";
+ case Type::MAT4:
+ return stream << "mat4";
+ default:
+ BLI_assert(0);
+ return stream;
+ }
+}
+
+static inline std::ostream &operator<<(std::ostream &stream, const eGPUType type)
+{
+ switch (type) {
+ case GPU_CLOSURE:
+ return stream << "Closure";
+ default:
+ return stream << to_type(type);
+ }
+}
+
enum class BuiltinBits {
- /** Allow getting barycentic coordinates inside the fragment shader. NOTE: emulated on OpenGL. */
+ NONE = 0,
+ /**
+ * Allow getting barycentric coordinates inside the fragment shader.
+ * \note Emulated on OpenGL.
+ */
BARYCENTRIC_COORD = (1 << 0),
FRAG_COORD = (1 << 2),
FRONT_FACING = (1 << 4),
GLOBAL_INVOCATION_ID = (1 << 5),
INSTANCE_ID = (1 << 6),
+ /**
+ * Allow setting the target layer when the output is a layered frame-buffer.
+ * \note Emulated through geometry shader on older hardware.
+ */
LAYER = (1 << 7),
LOCAL_INVOCATION_ID = (1 << 8),
LOCAL_INVOCATION_INDEX = (1 << 9),
@@ -122,10 +186,13 @@ enum class ImageType {
/* Storage qualifiers. */
enum class Qualifier {
- RESTRICT = (1 << 0),
- READ_ONLY = (1 << 1),
- WRITE_ONLY = (1 << 2),
- QUALIFIER_MAX = (WRITE_ONLY << 1) - 1,
+ /** Restrict flag is set by default. Unless specified otherwise. */
+ NO_RESTRICT = (1 << 0),
+ READ = (1 << 1),
+ WRITE = (1 << 2),
+ /** Shorthand version of combined flags. */
+ READ_WRITE = READ | WRITE,
+ QUALIFIER_MAX = (WRITE << 1) - 1,
};
ENUM_OPERATORS(Qualifier, Qualifier::QUALIFIER_MAX);
@@ -218,18 +285,45 @@ struct ShaderCreateInfo {
bool do_static_compilation_ = false;
/** If true, all additionally linked create info will be merged into this one. */
bool finalized_ = false;
+ /** If true, all resources will have an automatic location assigned. */
+ bool auto_resource_location_ = false;
/**
* Maximum length of all the resource names including each null terminator.
* Only for names used by gpu::ShaderInterface.
*/
size_t interface_names_size_ = 0;
- /** Only for compute shaders. */
- int local_group_size_[3] = {0, 0, 0};
+ /** Manually set builtins. */
+ BuiltinBits builtins_ = BuiltinBits::NONE;
+ /** Manually set generated code. */
+ std::string vertex_source_generated = "";
+ std::string fragment_source_generated = "";
+ std::string typedef_source_generated = "";
+ /** Manually set generated dependencies. */
+ Vector<const char *, 0> dependencies_generated;
+
+#define TEST_EQUAL(a, b, _member) \
+ if (!((a)._member == (b)._member)) { \
+ return false; \
+ }
+
+#define TEST_VECTOR_EQUAL(a, b, _vector) \
+ TEST_EQUAL(a, b, _vector.size()); \
+ for (auto i : _vector.index_range()) { \
+ TEST_EQUAL(a, b, _vector[i]); \
+ }
struct VertIn {
int index;
Type type;
StringRefNull name;
+
+ bool operator==(const VertIn &b)
+ {
+ TEST_EQUAL(*this, b, index);
+ TEST_EQUAL(*this, b, type);
+ TEST_EQUAL(*this, b, name);
+ return true;
+ }
};
Vector<VertIn> vertex_inputs_;
@@ -239,14 +333,47 @@ struct ShaderCreateInfo {
PrimitiveOut primitive_out;
/** Set to -1 by default to check if used. */
int max_vertices = -1;
+
+ bool operator==(const GeometryStageLayout &b)
+ {
+ TEST_EQUAL(*this, b, primitive_in);
+ TEST_EQUAL(*this, b, invocations);
+ TEST_EQUAL(*this, b, primitive_out);
+ TEST_EQUAL(*this, b, max_vertices);
+ return true;
+ }
};
GeometryStageLayout geometry_layout_;
+ struct ComputeStageLayout {
+ int local_size_x = -1;
+ int local_size_y = -1;
+ int local_size_z = -1;
+
+ bool operator==(const ComputeStageLayout &b)
+ {
+ TEST_EQUAL(*this, b, local_size_x);
+ TEST_EQUAL(*this, b, local_size_y);
+ TEST_EQUAL(*this, b, local_size_z);
+ return true;
+ }
+ };
+ ComputeStageLayout compute_layout_;
+
struct FragOut {
int index;
Type type;
DualBlend blend;
StringRefNull name;
+
+ bool operator==(const FragOut &b)
+ {
+ TEST_EQUAL(*this, b, index);
+ TEST_EQUAL(*this, b, type);
+ TEST_EQUAL(*this, b, blend);
+ TEST_EQUAL(*this, b, name);
+ return true;
+ }
};
Vector<FragOut> fragment_outputs_;
@@ -292,6 +419,35 @@ struct ShaderCreateInfo {
};
Resource(BindType type, int _slot) : bind_type(type), slot(_slot){};
+
+ bool operator==(const Resource &b)
+ {
+ TEST_EQUAL(*this, b, bind_type);
+ TEST_EQUAL(*this, b, slot);
+ switch (bind_type) {
+ case UNIFORM_BUFFER:
+ TEST_EQUAL(*this, b, uniformbuf.type_name);
+ TEST_EQUAL(*this, b, uniformbuf.name);
+ break;
+ case STORAGE_BUFFER:
+ TEST_EQUAL(*this, b, storagebuf.qualifiers);
+ TEST_EQUAL(*this, b, storagebuf.type_name);
+ TEST_EQUAL(*this, b, storagebuf.name);
+ break;
+ case SAMPLER:
+ TEST_EQUAL(*this, b, sampler.type);
+ TEST_EQUAL(*this, b, sampler.sampler);
+ TEST_EQUAL(*this, b, sampler.name);
+ break;
+ case IMAGE:
+ TEST_EQUAL(*this, b, image.format);
+ TEST_EQUAL(*this, b, image.type);
+ TEST_EQUAL(*this, b, image.qualifiers);
+ TEST_EQUAL(*this, b, image.name);
+ break;
+ }
+ return true;
+ }
};
/**
* Resources are grouped by frequency of change.
@@ -305,10 +461,17 @@ struct ShaderCreateInfo {
Vector<StageInterfaceInfo *> geometry_out_interfaces_;
struct PushConst {
- int index;
Type type;
StringRefNull name;
int array_size;
+
+ bool operator==(const PushConst &b)
+ {
+ TEST_EQUAL(*this, b, type);
+ TEST_EQUAL(*this, b, name);
+ TEST_EQUAL(*this, b, array_size);
+ return true;
+ }
};
Vector<PushConst> push_constants_;
@@ -366,7 +529,20 @@ struct ShaderCreateInfo {
return *(Self *)this;
}
- /* Only needed if geometry shader is enabled. */
+ Self &local_group_size(int local_size_x = -1, int local_size_y = -1, int local_size_z = -1)
+ {
+ compute_layout_.local_size_x = local_size_x;
+ compute_layout_.local_size_y = local_size_y;
+ compute_layout_.local_size_z = local_size_z;
+ return *(Self *)this;
+ }
+
+ /**
+ * Only needed if geometry shader is enabled.
+ * IMPORTANT: Input and output instance name will have respectively "_in" and "_out" suffix
+ * appended in the geometry shader IF AND ONLY IF the vertex_out interface instance name matches
+ * the geometry_out interface instance name.
+ */
Self &geometry_out(StageInterfaceInfo &interface)
{
geometry_out_interfaces_.append(&interface);
@@ -481,22 +657,14 @@ struct ShaderCreateInfo {
/** \name Push constants
*
* Data managed by GPUShader. Can be set through uniform functions. Must be less than 128bytes.
- * One slot represents 4bytes. Each element needs to have enough empty space left after it.
- * example:
- * [0] = PUSH_CONSTANT(MAT4, "ModelMatrix"),
- * ---- 16 slots occupied by ModelMatrix ----
- * [16] = PUSH_CONSTANT(VEC4, "color"),
- * ---- 4 slots occupied by color ----
- * [20] = PUSH_CONSTANT(BOOL, "srgbToggle"),
- * The maximum slot is 31.
* \{ */
- Self &push_constant(int slot, Type type, StringRefNull name, int array_size = 0)
+ Self &push_constant(Type type, StringRefNull name, int array_size = 0)
{
BLI_assert_msg(name.find("[") == -1,
"Array syntax is forbidden for push constants."
"Use the array_size parameter instead.");
- push_constants_.append({slot, type, name, array_size});
+ push_constants_.append({type, name, array_size});
interface_names_size_ += name.size() + 1;
return *(Self *)this;
}
@@ -504,20 +672,6 @@ struct ShaderCreateInfo {
/** \} */
/* -------------------------------------------------------------------- */
- /** \name Compute shaders Local Group Size
- * \{ */
-
- Self &local_group_size(int x, int y = 1, int z = 1)
- {
- local_group_size_[0] = x;
- local_group_size_[1] = y;
- local_group_size_[2] = z;
- return *(Self *)this;
- }
-
- /** \} */
-
- /* -------------------------------------------------------------------- */
/** \name Defines
* \{ */
@@ -539,6 +693,18 @@ struct ShaderCreateInfo {
return *(Self *)this;
}
+ Self &builtins(BuiltinBits builtin)
+ {
+ builtins_ |= builtin;
+ return *(Self *)this;
+ }
+
+ Self &auto_resource_location(bool value)
+ {
+ auto_resource_location_ = value;
+ return *(Self *)this;
+ }
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -551,7 +717,9 @@ struct ShaderCreateInfo {
StringRefNull info_name1 = "",
StringRefNull info_name2 = "",
StringRefNull info_name3 = "",
- StringRefNull info_name4 = "")
+ StringRefNull info_name4 = "",
+ StringRefNull info_name5 = "",
+ StringRefNull info_name6 = "")
{
additional_infos_.append(info_name0);
if (!info_name1.is_empty()) {
@@ -566,6 +734,12 @@ struct ShaderCreateInfo {
if (!info_name4.is_empty()) {
additional_infos_.append(info_name4);
}
+ if (!info_name5.is_empty()) {
+ additional_infos_.append(info_name5);
+ }
+ if (!info_name6.is_empty()) {
+ additional_infos_.append(info_name6);
+ }
return *(Self *)this;
}
@@ -597,7 +771,81 @@ struct ShaderCreateInfo {
/* WARNING: Recursive. */
void finalize();
+ /** Error detection that some backend compilers do not complain about. */
+ void validate(const ShaderCreateInfo &other_info);
+
/** \} */
+
+ /* -------------------------------------------------------------------- */
+ /** \name Operators.
+ *
+ * \{ */
+
+ /* Comparison operator for GPUPass cache. We only compare if it will create the same shader code.
+ * So we do not compare name and some other internal stuff. */
+ bool operator==(const ShaderCreateInfo &b)
+ {
+ TEST_EQUAL(*this, b, builtins_);
+ TEST_EQUAL(*this, b, vertex_source_generated);
+ TEST_EQUAL(*this, b, fragment_source_generated);
+ TEST_EQUAL(*this, b, typedef_source_generated);
+ TEST_VECTOR_EQUAL(*this, b, vertex_inputs_);
+ TEST_EQUAL(*this, b, geometry_layout_);
+ TEST_EQUAL(*this, b, compute_layout_);
+ TEST_VECTOR_EQUAL(*this, b, fragment_outputs_);
+ TEST_VECTOR_EQUAL(*this, b, pass_resources_);
+ TEST_VECTOR_EQUAL(*this, b, batch_resources_);
+ TEST_VECTOR_EQUAL(*this, b, vertex_out_interfaces_);
+ TEST_VECTOR_EQUAL(*this, b, geometry_out_interfaces_);
+ TEST_VECTOR_EQUAL(*this, b, push_constants_);
+ TEST_VECTOR_EQUAL(*this, b, typedef_sources_);
+ TEST_EQUAL(*this, b, vertex_source_);
+ TEST_EQUAL(*this, b, geometry_source_);
+ TEST_EQUAL(*this, b, fragment_source_);
+ TEST_EQUAL(*this, b, compute_source_);
+ TEST_VECTOR_EQUAL(*this, b, additional_infos_);
+ TEST_VECTOR_EQUAL(*this, b, defines_);
+ return true;
+ }
+
+ /** Debug print */
+ friend std::ostream &operator<<(std::ostream &stream, const ShaderCreateInfo &info)
+ {
+ /* TODO(@fclem): Complete print. */
+
+ auto print_resource = [&](const Resource &res) {
+ switch (res.bind_type) {
+ case Resource::BindType::UNIFORM_BUFFER:
+ stream << "UNIFORM_BUFFER(" << res.slot << ", " << res.uniformbuf.name << ")"
+ << std::endl;
+ break;
+ case Resource::BindType::STORAGE_BUFFER:
+ stream << "STORAGE_BUFFER(" << res.slot << ", " << res.storagebuf.name << ")"
+ << std::endl;
+ break;
+ case Resource::BindType::SAMPLER:
+ stream << "SAMPLER(" << res.slot << ", " << res.sampler.name << ")" << std::endl;
+ break;
+ case Resource::BindType::IMAGE:
+ stream << "IMAGE(" << res.slot << ", " << res.image.name << ")" << std::endl;
+ break;
+ }
+ };
+
+ /* TODO(@fclem): Order the resources. */
+ for (auto &res : info.batch_resources_) {
+ print_resource(res);
+ }
+ for (auto &res : info.pass_resources_) {
+ print_resource(res);
+ }
+ return stream;
+ }
+
+ /** \} */
+
+#undef TEST_EQUAL
+#undef TEST_VECTOR_EQUAL
};
} // namespace blender::gpu::shader
diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc
index 2d56e1cec05..06ad0817bfb 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency.cc
+++ b/source/blender/gpu/intern/gpu_shader_dependency.cc
@@ -34,7 +34,7 @@
#include "gpu_shader_dependency_private.h"
extern "C" {
-#define SHADER_SOURCE(datatoc, filename) extern char datatoc[];
+#define SHADER_SOURCE(datatoc, filename, filepath) extern char datatoc[];
#include "glsl_draw_source_list.h"
#include "glsl_gpu_source_list.h"
#undef SHADER_SOURCE
@@ -45,18 +45,20 @@ namespace blender::gpu {
using GPUSourceDictionnary = Map<StringRef, struct GPUSource *>;
struct GPUSource {
+ StringRefNull fullpath;
StringRefNull filename;
StringRefNull source;
Vector<GPUSource *> dependencies;
bool dependencies_init = false;
shader::BuiltinBits builtins = (shader::BuiltinBits)0;
+ std::string processed_source;
- GPUSource(const char *file, const char *datatoc) : filename(file), source(datatoc)
+ GPUSource(const char *path, const char *file, const char *datatoc)
+ : fullpath(path), filename(file), source(datatoc)
{
/* Scan for builtins. */
/* FIXME: This can trigger false positive caused by disabled #if blocks. */
/* TODO(fclem): Could be made faster by scanning once. */
- /* TODO(fclem): BARYCENTRIC_COORD. */
if (source.find("gl_FragCoord", 0)) {
builtins |= shader::BuiltinBits::FRAG_COORD;
}
@@ -69,9 +71,6 @@ struct GPUSource {
if (source.find("gl_InstanceID", 0)) {
builtins |= shader::BuiltinBits::INSTANCE_ID;
}
- if (source.find("gl_Layer", 0)) {
- builtins |= shader::BuiltinBits::LAYER;
- }
if (source.find("gl_LocalInvocationID", 0)) {
builtins |= shader::BuiltinBits::LOCAL_INVOCATION_ID;
}
@@ -99,27 +98,216 @@ struct GPUSource {
if (source.find("gl_WorkGroupSize", 0)) {
builtins |= shader::BuiltinBits::WORK_GROUP_SIZE;
}
+
+ /* TODO(fclem): We could do that at compile time. */
+ /* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */
+ if (filename.endswith(".h") || filename.endswith(".hh")) {
+ enum_preprocess();
+ }
};
- void init_dependencies(const GPUSourceDictionnary &dict)
+ bool is_in_comment(const StringRef &input, int64_t offset)
{
- if (dependencies_init) {
+ return (input.rfind("/*", offset) > input.rfind("*/", offset)) ||
+ (input.rfind("//", offset) > input.rfind("\n", offset));
+ }
+
+ template<bool check_whole_word = true, bool reversed = false, typename T>
+ int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0)
+ {
+ while (true) {
+ if constexpr (reversed) {
+ offset = input.rfind(keyword, offset);
+ }
+ else {
+ offset = input.find(keyword, offset);
+ }
+ if (offset > 0) {
+ if constexpr (check_whole_word) {
+ /* Fix false positive if something has "enum" as suffix. */
+ char previous_char = input[offset - 1];
+ if (!(ELEM(previous_char, '\n', '\t', ' ', ':'))) {
+ offset += (reversed) ? -1 : 1;
+ continue;
+ }
+ }
+ /* Fix case where the keyword is in a comment. */
+ if (is_in_comment(input, offset)) {
+ offset += (reversed) ? -1 : 1;
+ continue;
+ }
+ }
+ return offset;
+ }
+ }
+
+ void print_error(const StringRef &input, int64_t offset, const StringRef message)
+ {
+ std::cout << " error: " << message << "\n";
+ StringRef sub = input.substr(0, offset);
+ int64_t line_number = std::count(sub.begin(), sub.end(), '\n') + 1;
+ int64_t line_end = input.find("\n", offset);
+ int64_t line_start = input.rfind("\n", offset) + 1;
+ int64_t char_number = offset - line_start + 1;
+ char line_prefix[16] = "";
+ SNPRINTF(line_prefix, "%5ld | ", line_number);
+
+ /* TODO Use clog. */
+
+ std::cout << fullpath << ":" << line_number << ":" << char_number;
+
+ std::cout << " error: " << message << "\n";
+ std::cout << line_prefix << input.substr(line_start, line_end - line_start) << "\n";
+ std::cout << " | ";
+ for (int64_t i = 0; i < char_number - 1; i++) {
+ std::cout << " ";
+ }
+ std::cout << "^\n";
+ }
+
+ /**
+ * Transform C,C++ enum declaration into GLSL compatible defines and constants:
+ *
+ * \code{.cpp}
+ * enum eMyEnum : uint32_t {
+ * ENUM_1 = 0u,
+ * ENUM_2 = 1u,
+ * ENUM_3 = 2u,
+ * };
+ * \endcode
+ *
+ * or
+ *
+ * \code{.c}
+ * enum eMyEnum {
+ * ENUM_1 = 0u,
+ * ENUM_2 = 1u,
+ * ENUM_3 = 2u,
+ * };
+ * \endcode
+ *
+ * becomes
+ *
+ * \code{.glsl}
+ * #define eMyEnum uint
+ * const uint ENUM_1 = 0u, ENUM_2 = 1u, ENUM_3 = 2u;
+ * \endcode
+ *
+ * IMPORTANT: This has some requirements:
+ * - Enums needs to have underlying types specified to uint32_t to make them usable in UBO/SSBO.
+ * - All values needs to be specified using constant literals to avoid compiler differences.
+ * - All values needs to have the 'u' suffix to avoid GLSL compiler errors.
+ */
+ void enum_preprocess()
+ {
+ const StringRefNull input = source;
+ std::string output;
+ int64_t cursor = 0;
+ int64_t last_pos = 0;
+ const bool is_cpp = filename.endswith(".hh");
+
+#define find_keyword find_str<true, false>
+#define find_token find_str<false, false>
+#define rfind_token find_str<false, true>
+#define CHECK(test_value, str, ofs, msg) \
+ if ((test_value) == -1) { \
+ print_error(str, ofs, msg); \
+ cursor++; \
+ continue; \
+ }
+
+ while (true) {
+ cursor = find_keyword(input, "enum ", cursor);
+ if (cursor == -1) {
+ break;
+ }
+ /* Output anything between 2 enums blocks. */
+ output += input.substr(last_pos, cursor - last_pos);
+
+ /* Extract enum type name. */
+ int64_t name_start = input.find(" ", cursor);
+
+ int64_t values_start = find_token(input, '{', cursor);
+ CHECK(values_start, input, cursor, "Malformed enum class. Expected \'{\' after typename.");
+
+ StringRef enum_name = input.substr(name_start, values_start - name_start);
+ if (is_cpp) {
+ int64_t name_end = find_token(enum_name, ":");
+ CHECK(name_end, input, name_start, "Expected \':\' after C++ enum name.");
+
+ int64_t underlying_type = find_keyword(enum_name, "uint32_t", name_end);
+ CHECK(underlying_type, input, name_start, "C++ enums needs uint32_t underlying type.");
+
+ enum_name = input.substr(name_start, name_end);
+ }
+
+ output += "#define " + enum_name + " uint\n";
+
+ /* Extract enum values. */
+ int64_t values_end = find_token(input, '}', values_start);
+ CHECK(values_end, input, cursor, "Malformed enum class. Expected \'}\' after values.");
+
+ /* Skip opening brackets. */
+ values_start += 1;
+
+ StringRef enum_values = input.substr(values_start, values_end - values_start);
+
+ /* Really poor check. Could be done better. */
+ int64_t token = find_token(enum_values, '{');
+ int64_t not_found = (token == -1) ? 0 : -1;
+ CHECK(not_found, input, values_start + token, "Unexpected \'{\' token inside enum values.");
+
+ /* Do not capture the comma after the last value (if present). */
+ int64_t last_equal = rfind_token(enum_values, '=', values_end);
+ int64_t last_comma = rfind_token(enum_values, ',', values_end);
+ if (last_comma > last_equal) {
+ enum_values = input.substr(values_start, last_comma);
+ }
+
+ output += "const uint " + enum_values;
+
+ int64_t semicolon_found = (input[values_end + 1] == ';') ? 0 : -1;
+ CHECK(semicolon_found, input, values_end + 1, "Expected \';\' after enum type declaration.");
+
+ /* Skip the curly bracket but not the semicolon. */
+ cursor = last_pos = values_end + 1;
+ }
+ /* If nothing has been changed, do not allocate processed_source. */
+ if (last_pos == 0) {
return;
}
+
+#undef find_keyword
+#undef find_token
+#undef rfind_token
+
+ if (last_pos != 0) {
+ output += input.substr(last_pos);
+ }
+ processed_source = output;
+ source = processed_source.c_str();
+ };
+
+ /* Return 1 one error. */
+ int init_dependencies(const GPUSourceDictionnary &dict)
+ {
+ if (dependencies_init) {
+ return 0;
+ }
dependencies_init = true;
int64_t pos = 0;
while (true) {
pos = source.find("pragma BLENDER_REQUIRE(", pos);
if (pos == -1) {
- return;
+ return 0;
}
- int64_t start = source.find("(", pos) + 1;
- int64_t end = source.find(")", pos);
+ int64_t start = source.find('(', pos) + 1;
+ int64_t end = source.find(')', pos);
if (end == -1) {
/* TODO Use clog. */
std::cout << "Error: " << filename << " : Malformed BLENDER_REQUIRE: Missing \")\"."
<< std::endl;
- return;
+ return 1;
}
StringRef dependency_name = source.substr(start, end - start);
GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr);
@@ -127,10 +315,13 @@ struct GPUSource {
/* TODO Use clog. */
std::cout << "Error: " << filename << " : Dependency not found \"" << dependency_name
<< "\"." << std::endl;
- return;
+ return 1;
}
/* Recursive. */
- dependency_source->init_dependencies(dict);
+ int result = dependency_source->init_dependencies(dict);
+ if (result != 0) {
+ return 1;
+ }
for (auto *dep : dependency_source->dependencies) {
dependencies.append_non_duplicates(dep);
@@ -141,13 +332,21 @@ struct GPUSource {
}
/* Returns the final string with all includes done. */
- void build(std::string &str, shader::BuiltinBits &out_builtins)
+ void build(Vector<const char *> &result) const
{
for (auto *dep : dependencies) {
- out_builtins |= builtins;
- str += dep->source;
+ result.append(dep->source.c_str());
}
- str += source;
+ result.append(source.c_str());
+ }
+
+ shader::BuiltinBits builtins_get() const
+ {
+ shader::BuiltinBits out_builtins = shader::BuiltinBits::NONE;
+ for (auto *dep : dependencies) {
+ out_builtins |= dep->builtins;
+ }
+ return out_builtins;
}
};
@@ -161,15 +360,17 @@ void gpu_shader_dependency_init()
{
g_sources = new GPUSourceDictionnary();
-#define SHADER_SOURCE(datatoc, filename) \
- g_sources->add_new(filename, new GPUSource(filename, datatoc));
+#define SHADER_SOURCE(datatoc, filename, filepath) \
+ g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc));
#include "glsl_draw_source_list.h"
#include "glsl_gpu_source_list.h"
#undef SHADER_SOURCE
+ int errors = 0;
for (auto *value : g_sources->values()) {
- value->init_dependencies(*g_sources);
+ errors += value->init_dependencies(*g_sources);
}
+ BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting");
}
void gpu_shader_dependency_exit()
@@ -180,18 +381,47 @@ void gpu_shader_dependency_exit()
delete g_sources;
}
-char *gpu_shader_dependency_get_resolved_source(const char *shader_source_name, uint32_t *builtins)
+namespace blender::gpu::shader {
+
+BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull shader_source_name)
{
+ if (shader_source_name.is_empty()) {
+ return shader::BuiltinBits::NONE;
+ }
+ if (g_sources->contains(shader_source_name) == false) {
+ std::cout << "Error: Could not find \"" << shader_source_name
+ << "\" in the list of registered source.\n";
+ BLI_assert(0);
+ return shader::BuiltinBits::NONE;
+ }
GPUSource *source = g_sources->lookup(shader_source_name);
- std::string str;
- shader::BuiltinBits out_builtins;
- source->build(str, out_builtins);
- *builtins |= (uint32_t)out_builtins;
- return strdup(str.c_str());
+ return source->builtins_get();
}
-char *gpu_shader_dependency_get_source(const char *shader_source_name)
+Vector<const char *> gpu_shader_dependency_get_resolved_source(
+ const StringRefNull shader_source_name)
+{
+ Vector<const char *> result;
+ GPUSource *source = g_sources->lookup(shader_source_name);
+ source->build(result);
+ return result;
+}
+
+StringRefNull gpu_shader_dependency_get_source(const StringRefNull shader_source_name)
{
GPUSource *src = g_sources->lookup(shader_source_name);
- return strdup(src->source.c_str());
+ return src->source;
+}
+
+StringRefNull gpu_shader_dependency_get_filename_from_source_string(
+ const StringRefNull source_string)
+{
+ for (auto &source : g_sources->values()) {
+ if (source->source.c_str() == source_string.c_str()) {
+ return source->filename;
+ }
+ }
+ return "";
}
+
+} // namespace blender::gpu::shader
diff --git a/source/blender/gpu/intern/gpu_shader_dependency_private.h b/source/blender/gpu/intern/gpu_shader_dependency_private.h
index b129ca74a48..13261dd2021 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency_private.h
+++ b/source/blender/gpu/intern/gpu_shader_dependency_private.h
@@ -34,11 +34,32 @@ void gpu_shader_dependency_init(void);
void gpu_shader_dependency_exit(void);
-/* User must free the resulting string using free. */
-char *gpu_shader_dependency_get_resolved_source(const char *shader_source_name,
- uint32_t *builtins);
-char *gpu_shader_dependency_get_source(const char *shader_source_name);
-
#ifdef __cplusplus
}
#endif
+
+#ifdef __cplusplus
+
+# include "BLI_string_ref.hh"
+# include "BLI_vector.hh"
+
+# include "gpu_shader_create_info.hh"
+
+namespace blender::gpu::shader {
+
+BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull source_name);
+
+Vector<const char *> gpu_shader_dependency_get_resolved_source(const StringRefNull source_name);
+StringRefNull gpu_shader_dependency_get_source(const StringRefNull source_name);
+
+/**
+ * \brief Find the name of the file from which the given string was generated.
+ * \return Return filename or empty string.
+ * \note source_string needs to be identical to the one given by gpu_shader_dependency_get_source()
+ */
+StringRefNull gpu_shader_dependency_get_filename_from_source_string(
+ const StringRefNull source_string);
+
+} // namespace blender::gpu::shader
+
+#endif
diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh
index 735b8fea71d..514cfc01f09 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.hh
+++ b/source/blender/gpu/intern/gpu_shader_interface.hh
@@ -68,6 +68,7 @@ class ShaderInterface {
uint16_t enabled_ubo_mask_ = 0;
uint8_t enabled_ima_mask_ = 0;
uint64_t enabled_tex_mask_ = 0;
+ uint16_t enabled_ssbo_mask_ = 0;
/** Location of builtin uniforms. Fast access, no lookup needed. */
int32_t builtins_[GPU_NUM_UNIFORMS];
int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS];
@@ -107,6 +108,10 @@ class ShaderInterface {
{
return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name);
}
+ inline const ShaderInput *ssbo_get(const int binding) const
+ {
+ return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, binding);
+ }
inline const char *input_name_get(const ShaderInput *input) const
{
@@ -189,11 +194,11 @@ inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u)
case GPU_UNIFORM_COLOR:
return "color";
case GPU_UNIFORM_BASE_INSTANCE:
- return "baseInstance";
+ return "gpu_BaseInstance";
case GPU_UNIFORM_RESOURCE_CHUNK:
- return "resourceChunk";
+ return "drw_resourceChunk";
case GPU_UNIFORM_RESOURCE_ID:
- return "resourceId";
+ return "drw_ResourceID";
case GPU_UNIFORM_SRGB_TRANSFORM:
return "srgbTarget";
@@ -211,6 +216,13 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu
return "modelBlock";
case GPU_UNIFORM_BLOCK_INFO:
return "infoBlock";
+
+ case GPU_UNIFORM_BLOCK_DRW_VIEW:
+ return "drw_view";
+ case GPU_UNIFORM_BLOCK_DRW_MODEL:
+ return "drw_matrices";
+ case GPU_UNIFORM_BLOCK_DRW_INFOS:
+ return "drw_infos";
default:
return NULL;
}
diff --git a/source/blender/gpu/intern/gpu_shader_log.cc b/source/blender/gpu/intern/gpu_shader_log.cc
index 12459b4b721..c879e912c5d 100644
--- a/source/blender/gpu/intern/gpu_shader_log.cc
+++ b/source/blender/gpu/intern/gpu_shader_log.cc
@@ -26,7 +26,9 @@
#include "BLI_dynstr.h"
#include "BLI_string.h"
#include "BLI_string_utils.h"
+#include "BLI_vector.hh"
+#include "gpu_shader_dependency_private.h"
#include "gpu_shader_private.hh"
#include "GPU_platform.h"
@@ -41,6 +43,14 @@ namespace blender::gpu {
/** \name Debug functions
* \{ */
+/* Number of lines before and after the error line to print for compilation errors. */
+#define DEBUG_CONTEXT_LINES 0
+/**
+ * Print dependencies sources list before the shader report.
+ * Useful to debug include order or missing dependencies.
+ */
+#define DEBUG_DEPENDENCIES 0
+
void Shader::print_log(Span<const char *> sources,
char *log,
const char *stage,
@@ -61,6 +71,30 @@ void Shader::print_log(Span<const char *> sources,
BLI_dynstr_appendf(dynstr, "\n");
+#if DEBUG_DEPENDENCIES
+ BLI_dynstr_appendf(
+ dynstr, "%s%sIncluded files (in order):%s\n", info_col, line_prefix, reset_col);
+#endif
+
+ Vector<int64_t> sources_end_line;
+ for (StringRefNull src : sources) {
+ int64_t cursor = 0, line_count = 0;
+ while ((cursor = src.find('\n', cursor) + 1)) {
+ line_count++;
+ }
+ if (sources_end_line.is_empty() == false) {
+ line_count += sources_end_line.last();
+ }
+ sources_end_line.append(line_count);
+#if DEBUG_DEPENDENCIES
+ StringRefNull filename = shader::gpu_shader_dependency_get_filename_from_source_string(src);
+ if (!filename.is_empty()) {
+ BLI_dynstr_appendf(
+ dynstr, "%s%s %s%s\n", info_col, line_prefix, filename.c_str(), reset_col);
+ }
+#endif
+ }
+
char *log_line = log, *line_end;
LogCursor previous_location;
@@ -73,12 +107,32 @@ void Shader::print_log(Span<const char *> sources,
continue;
}
+ /* Silence not useful lines. */
+ StringRef logref = StringRefNull(log_line).substr(0, (size_t)line_end - (size_t)log_line);
+ if (logref.endswith(" shader failed to compile with the following errors:") ||
+ logref.endswith(" No code generated")) {
+ log_line += (size_t)line_end - (size_t)log_line;
+ continue;
+ }
+
GPULogItem log_item;
log_line = parser->parse_line(log_line, log_item);
+ /* Sanitize output. Really bad values can happen when the error line is buggy. */
+ if (log_item.cursor.source >= sources.size()) {
+ log_item.cursor.source = -1;
+ }
+ if (log_item.cursor.row >= sources_end_line.last()) {
+ log_item.cursor.source = -1;
+ log_item.cursor.row = -1;
+ }
+
if (log_item.cursor.row == -1) {
found_line_id = false;
}
+ else if (log_item.source_base_row && log_item.cursor.source > 0) {
+ log_item.cursor.row += sources_end_line[log_item.cursor.source - 1];
+ }
const char *src_line = sources_combined;
@@ -98,15 +152,14 @@ void Shader::print_log(Span<const char *> sources,
/* error_line is 1 based in this case. */
int src_line_index = 1;
while ((src_line_end = strchr(src_line, '\n'))) {
- if (src_line_index == log_item.cursor.row) {
+ if (src_line_index >= log_item.cursor.row) {
found_line_id = true;
break;
}
-/* TODO(fclem) Make this an option to display N lines before error. */
-#if 0 /* Uncomment to print shader file up to the error line to have more context. */
- BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
- BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
-#endif
+ if (src_line_index >= log_item.cursor.row - DEBUG_CONTEXT_LINES) {
+ BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
+ BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
+ }
/* Continue to next line. */
src_line = src_line_end + 1;
src_line_index++;
@@ -129,10 +182,53 @@ void Shader::print_log(Span<const char *> sources,
BLI_dynstr_appendf(dynstr, "^");
}
BLI_dynstr_appendf(dynstr, "\n");
+
+ /* Skip the error line. */
+ src_line = src_line_end + 1;
+ src_line_index++;
+ while ((src_line_end = strchr(src_line, '\n'))) {
+ if (src_line_index > log_item.cursor.row + DEBUG_CONTEXT_LINES) {
+ break;
+ }
+ BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
+ BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
+ /* Continue to next line. */
+ src_line = src_line_end + 1;
+ src_line_index++;
+ }
}
}
BLI_dynstr_appendf(dynstr, line_prefix);
+ /* Search the correct source index. */
+ int row_in_file = log_item.cursor.row;
+ int source_index = log_item.cursor.source;
+ if (source_index <= 0) {
+ for (auto i : sources_end_line.index_range()) {
+ if (log_item.cursor.row <= sources_end_line[i]) {
+ source_index = i;
+ break;
+ }
+ }
+ }
+ if (source_index > 0) {
+ row_in_file -= sources_end_line[source_index - 1];
+ }
+ /* Print the filename the error line is coming from. */
+ if (source_index > 0) {
+ StringRefNull filename = shader::gpu_shader_dependency_get_filename_from_source_string(
+ sources[source_index]);
+ if (!filename.is_empty()) {
+ BLI_dynstr_appendf(dynstr,
+ "%s%s:%d:%d: %s",
+ info_col,
+ filename.c_str(),
+ row_in_file,
+ log_item.cursor.column + 1,
+ reset_col);
+ }
+ }
+
if (log_item.severity == Severity::Error) {
BLI_dynstr_appendf(dynstr, "%s%s%s: ", err_col, "Error", info_col);
}
diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh
index 7837af0dcf2..93c33811ee0 100644
--- a/source/blender/gpu/intern/gpu_shader_private.hh
+++ b/source/blender/gpu/intern/gpu_shader_private.hh
@@ -77,6 +77,7 @@ class Shader {
virtual std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
virtual std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
virtual std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const = 0;
+ virtual std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const = 0;
/* DEPRECATED: Kept only because of BGL API. */
virtual int program_handle_get() const = 0;
@@ -119,6 +120,7 @@ struct LogCursor {
struct GPULogItem {
LogCursor cursor;
+ bool source_base_row = false;
Severity severity = Severity::Unknown;
};
diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
index 73b59b9f06f..3195e98da5e 100644
--- a/source/blender/gpu/intern/gpu_texture_private.hh
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -451,7 +451,6 @@ inline bool validate_data_format(eGPUTextureFormat tex_format, eGPUDataFormat da
}
}
-/* Definitely not complete, edit according to the gl specification. */
inline eGPUDataFormat to_data_format(eGPUTextureFormat tex_format)
{
switch (tex_format) {
@@ -462,16 +461,27 @@ inline eGPUDataFormat to_data_format(eGPUTextureFormat tex_format)
case GPU_DEPTH24_STENCIL8:
case GPU_DEPTH32F_STENCIL8:
return GPU_DATA_UINT_24_8;
- case GPU_R8UI:
case GPU_R16UI:
- case GPU_RG16UI:
case GPU_R32UI:
+ case GPU_RG16UI:
+ case GPU_RG32UI:
+ case GPU_RGBA16UI:
+ case GPU_RGBA32UI:
return GPU_DATA_UINT;
- case GPU_RG16I:
case GPU_R16I:
+ case GPU_R32I:
+ case GPU_R8I:
+ case GPU_RG16I:
+ case GPU_RG32I:
+ case GPU_RG8I:
+ case GPU_RGBA16I:
+ case GPU_RGBA32I:
+ case GPU_RGBA8I:
return GPU_DATA_INT;
case GPU_R8:
+ case GPU_R8UI:
case GPU_RG8:
+ case GPU_RG8UI:
case GPU_RGBA8:
case GPU_RGBA8UI:
case GPU_SRGB8_A8:
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index c604859fa94..7a20278e5aa 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -137,19 +137,24 @@ struct DRWData **GPU_viewport_data_get(GPUViewport *viewport)
static void gpu_viewport_textures_create(GPUViewport *viewport)
{
int *size = viewport->size;
+ float empty_pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f};
if (viewport->color_render_tx[0] == NULL) {
viewport->color_render_tx[0] = GPU_texture_create_2d(
"dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL);
+ GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel);
viewport->color_overlay_tx[0] = GPU_texture_create_2d(
"dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL);
+ GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel);
}
if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == NULL) {
viewport->color_render_tx[1] = GPU_texture_create_2d(
"dtxl_color_stereo", UNPACK2(size), 1, GPU_RGBA16F, NULL);
+ GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel);
viewport->color_overlay_tx[1] = GPU_texture_create_2d(
"dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, NULL);
+ GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel);
}
/* Can be shared with GPUOffscreen. */
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index 1a445ebd7eb..2ee7c0503f4 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -240,6 +240,7 @@ static void detect_workarounds()
GLContext::unused_fb_slot_workaround = true;
/* Turn off extensions. */
GCaps.shader_image_load_store_support = false;
+ GCaps.shader_storage_buffer_objects_support = false;
GLContext::base_instance_support = false;
GLContext::clear_texture_support = false;
GLContext::copy_image_support = false;
@@ -247,6 +248,8 @@ static void detect_workarounds()
GLContext::direct_state_access_support = false;
GLContext::fixed_restart_index_support = false;
GLContext::geometry_shader_invocations = false;
+ GLContext::layered_rendering_support = false;
+ GLContext::native_barycentric_support = false;
GLContext::multi_bind_support = false;
GLContext::multi_draw_indirect_support = false;
GLContext::shader_draw_parameters_support = false;
@@ -419,6 +422,12 @@ static void detect_workarounds()
strstr(renderer, "HD Graphics 4000")) {
GLContext::generate_mipmap_workaround = true;
}
+
+ /* Buggy interface query functions cause crashes when handling SSBOs (T93680) */
+ if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY) &&
+ (strstr(renderer, "HD Graphics 4400") || strstr(renderer, "HD Graphics 4600"))) {
+ GCaps.shader_storage_buffer_objects_support = false;
+ }
} // namespace blender::gpu
/** Internal capabilities. */
@@ -438,6 +447,8 @@ bool GLContext::direct_state_access_support = false;
bool GLContext::explicit_location_support = false;
bool GLContext::geometry_shader_invocations = false;
bool GLContext::fixed_restart_index_support = false;
+bool GLContext::layered_rendering_support = false;
+bool GLContext::native_barycentric_support = false;
bool GLContext::multi_bind_support = false;
bool GLContext::multi_draw_indirect_support = false;
bool GLContext::shader_draw_parameters_support = false;
@@ -498,6 +509,8 @@ void GLBackend::capabilities_init()
GLContext::explicit_location_support = GLEW_VERSION_4_3;
GLContext::geometry_shader_invocations = GLEW_ARB_gpu_shader5;
GLContext::fixed_restart_index_support = GLEW_ARB_ES3_compatibility;
+ GLContext::layered_rendering_support = GLEW_AMD_vertex_shader_layer;
+ GLContext::native_barycentric_support = GLEW_AMD_shader_explicit_vertex_parameter;
GLContext::multi_bind_support = GLEW_ARB_multi_bind;
GLContext::multi_draw_indirect_support = GLEW_ARB_multi_draw_indirect;
GLContext::shader_draw_parameters_support = GLEW_ARB_shader_draw_parameters;
diff --git a/source/blender/gpu/opengl/gl_compute.cc b/source/blender/gpu/opengl/gl_compute.cc
index fa8317dde4a..14164ee181b 100644
--- a/source/blender/gpu/opengl/gl_compute.cc
+++ b/source/blender/gpu/opengl/gl_compute.cc
@@ -28,8 +28,8 @@ namespace blender::gpu {
void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len)
{
+ GL_CHECK_RESOURCES("Compute");
glDispatchCompute(group_x_len, group_y_len, group_z_len);
- debug::check_gl_error("Dispatch Compute");
}
} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh
index dd22418972b..b7a74863ac4 100644
--- a/source/blender/gpu/opengl/gl_context.hh
+++ b/source/blender/gpu/opengl/gl_context.hh
@@ -72,6 +72,8 @@ class GLContext : public Context {
static bool explicit_location_support;
static bool geometry_shader_invocations;
static bool fixed_restart_index_support;
+ static bool layered_rendering_support;
+ static bool native_barycentric_support;
static bool multi_bind_support;
static bool multi_draw_indirect_support;
static bool shader_draw_parameters_support;
diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc
index 0a06d9cdb7c..95dea43636d 100644
--- a/source/blender/gpu/opengl/gl_debug.cc
+++ b/source/blender/gpu/opengl/gl_debug.cc
@@ -108,6 +108,11 @@ static void APIENTRY debug_callback(GLenum UNUSED(source),
GPU_debug_get_groups_names(sizeof(debug_groups), debug_groups);
CLG_Severity clog_severity;
+ if (GPU_debug_group_match(GPU_DEBUG_SHADER_COMPILATION_GROUP)) {
+ /** Do not duplicate shader compilation error/warnings. */
+ return;
+ }
+
switch (type) {
case GL_DEBUG_TYPE_ERROR:
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
@@ -191,6 +196,9 @@ void init_gl_callbacks()
void check_gl_error(const char *info)
{
+ if (!(G.debug & G_DEBUG_GPU)) {
+ return;
+ }
GLenum error = glGetError();
#define ERROR_CASE(err) \
@@ -339,7 +347,7 @@ void object_label(GLenum type, GLuint object, const char *name)
char label[64];
SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type));
/* Small convenience for caller. */
- if (ELEM(type, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_VERTEX_SHADER)) {
+ if (ELEM(type, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_VERTEX_SHADER, GL_COMPUTE_SHADER)) {
type = GL_SHADER;
}
if (ELEM(type, GL_UNIFORM_BUFFER)) {
diff --git a/source/blender/gpu/opengl/gl_debug_layer.cc b/source/blender/gpu/opengl/gl_debug_layer.cc
index e624cb9ee46..b82bf10ebe9 100644
--- a/source/blender/gpu/opengl/gl_debug_layer.cc
+++ b/source/blender/gpu/opengl/gl_debug_layer.cc
@@ -82,6 +82,8 @@ DEBUG_FUNC_DECLARE(PFNGLDRAWBUFFERSPROC, void, glDrawBuffers, GLsizei, n, const
DEBUG_FUNC_DECLARE(PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC, void, glDrawElementsInstancedBaseVertexBaseInstance, GLenum, mode, GLsizei, count, GLenum, type, const void *, indices, GLsizei, primcount, GLint, basevertex, GLuint, baseinstance);
DEBUG_FUNC_DECLARE(PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC, void, glDrawElementsInstancedBaseVertex, GLenum, mode, GLsizei, count, GLenum, type, const void *, indices, GLsizei, instancecount, GLint, basevertex);
DEBUG_FUNC_DECLARE(PFNGLENDQUERYPROC, void, glEndQuery, GLenum, target);
+DEBUG_FUNC_DECLARE(PFNGLDISPATCHCOMPUTEPROC, void, glDispatchCompute, GLuint, num_groups_x, GLuint, num_groups_y, GLuint, num_groups_z);
+DEBUG_FUNC_DECLARE(PFNGLDISPATCHCOMPUTEINDIRECTPROC, void, glDispatchComputeIndirect, GLintptr, indirect);
DEBUG_FUNC_DECLARE(PFNGLENDTRANSFORMFEEDBACKPROC, void, glEndTransformFeedback, void);
DEBUG_FUNC_DECLARE(PFNGLFRAMEBUFFERTEXTURE2DPROC, void, glFramebufferTexture2D, GLenum, target, GLenum, attachment, GLenum, textarget, GLuint, texture, GLint, level);
DEBUG_FUNC_DECLARE(PFNGLFRAMEBUFFERTEXTURELAYERPROC, void, glFramebufferTextureLayer, GLenum, target, GLenum, attachment, GLuint, texture, GLint, level, GLint, layer);
@@ -130,6 +132,8 @@ void init_debug_layer()
DEBUG_WRAP(glDeleteSamplers);
DEBUG_WRAP(glDeleteShader);
DEBUG_WRAP(glDeleteVertexArrays);
+ DEBUG_WRAP(glDispatchCompute);
+ DEBUG_WRAP(glDispatchComputeIndirect);
DEBUG_WRAP(glDrawArraysInstanced);
DEBUG_WRAP(glDrawArraysInstancedBaseInstance);
DEBUG_WRAP(glDrawBuffers);
diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc
index 13f03195437..106a12bfefd 100644
--- a/source/blender/gpu/opengl/gl_framebuffer.cc
+++ b/source/blender/gpu/opengl/gl_framebuffer.cc
@@ -429,8 +429,15 @@ void GLFrameBuffer::read(eGPUFrameBufferBits plane,
switch (plane) {
case GPU_DEPTH_BIT:
format = GL_DEPTH_COMPONENT;
+ BLI_assert_msg(
+ this->attachments_[GPU_FB_DEPTH_ATTACHMENT].tex != nullptr ||
+ this->attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != nullptr,
+ "GPUFramebuffer: Error: Trying to read depth without a depth buffer attached.");
break;
case GPU_COLOR_BIT:
+ BLI_assert_msg(
+ mode != GL_NONE,
+ "GPUFramebuffer: Error: Trying to read a color slot without valid attachment.");
format = channel_len_to_gl(channel_len);
/* TODO: needed for selection buffers to work properly, this should be handled better. */
if (format == GL_RED && type == GL_UNSIGNED_INT) {
diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh
index 9ebe549efe7..00a7676dd3f 100644
--- a/source/blender/gpu/opengl/gl_framebuffer.hh
+++ b/source/blender/gpu/opengl/gl_framebuffer.hh
@@ -141,6 +141,8 @@ static inline GLenum to_gl(const GPUAttachmentType type)
ATTACHMENT(COLOR_ATTACHMENT3);
ATTACHMENT(COLOR_ATTACHMENT4);
ATTACHMENT(COLOR_ATTACHMENT5);
+ ATTACHMENT(COLOR_ATTACHMENT6);
+ ATTACHMENT(COLOR_ATTACHMENT7);
default:
BLI_assert(0);
return GL_COLOR_ATTACHMENT0;
diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc
index cec85abae6f..9da35a2bc97 100644
--- a/source/blender/gpu/opengl/gl_shader.cc
+++ b/source/blender/gpu/opengl/gl_shader.cc
@@ -85,7 +85,7 @@ static const char *to_string(const Interpolation &interp)
case Interpolation::NO_PERSPECTIVE:
return "noperspective";
default:
- return "unkown";
+ return "unknown";
}
}
@@ -123,6 +123,74 @@ static const char *to_string(const Type &type)
case Type::BOOL:
return "bool";
default:
+ return "unknown";
+ }
+}
+
+static const char *to_string(const eGPUTextureFormat &type)
+{
+ switch (type) {
+ case GPU_RGBA8UI:
+ return "rgba8ui";
+ case GPU_RGBA8I:
+ return "rgba8i";
+ case GPU_RGBA8:
+ return "rgba8";
+ case GPU_RGBA32UI:
+ return "rgba32ui";
+ case GPU_RGBA32I:
+ return "rgba32i";
+ case GPU_RGBA32F:
+ return "rgba32f";
+ case GPU_RGBA16UI:
+ return "rgba16ui";
+ case GPU_RGBA16I:
+ return "rgba16i";
+ case GPU_RGBA16F:
+ return "rgba16f";
+ case GPU_RGBA16:
+ return "rgba16";
+ case GPU_RG8UI:
+ return "rg8ui";
+ case GPU_RG8I:
+ return "rg8i";
+ case GPU_RG8:
+ return "rg8";
+ case GPU_RG32UI:
+ return "rg32ui";
+ case GPU_RG32I:
+ return "rg32i";
+ case GPU_RG32F:
+ return "rg32f";
+ case GPU_RG16UI:
+ return "rg16ui";
+ case GPU_RG16I:
+ return "rg16i";
+ case GPU_RG16F:
+ return "rg16f";
+ case GPU_RG16:
+ return "rg16";
+ case GPU_R8UI:
+ return "r8ui";
+ case GPU_R8I:
+ return "r8i";
+ case GPU_R8:
+ return "r8";
+ case GPU_R32UI:
+ return "r32ui";
+ case GPU_R32I:
+ return "r32i";
+ case GPU_R32F:
+ return "r32f";
+ case GPU_R16UI:
+ return "r16ui";
+ case GPU_R16I:
+ return "r16i";
+ case GPU_R16F:
+ return "r16f";
+ case GPU_R16:
+ return "r16";
+ default:
return "unkown";
}
}
@@ -217,6 +285,8 @@ static void print_image_type(std::ostream &os,
case ImageType::UINT_2D_ARRAY:
case ImageType::SHADOW_2D:
case ImageType::SHADOW_2D_ARRAY:
+ case ImageType::DEPTH_2D:
+ case ImageType::DEPTH_2D_ARRAY:
os << "2D";
break;
case ImageType::FLOAT_3D:
@@ -232,6 +302,8 @@ static void print_image_type(std::ostream &os,
case ImageType::UINT_CUBE_ARRAY:
case ImageType::SHADOW_CUBE:
case ImageType::SHADOW_CUBE_ARRAY:
+ case ImageType::DEPTH_CUBE:
+ case ImageType::DEPTH_CUBE_ARRAY:
os << "Cube";
break;
default:
@@ -250,6 +322,8 @@ static void print_image_type(std::ostream &os,
case ImageType::UINT_CUBE_ARRAY:
case ImageType::SHADOW_2D_ARRAY:
case ImageType::SHADOW_CUBE_ARRAY:
+ case ImageType::DEPTH_2D_ARRAY:
+ case ImageType::DEPTH_CUBE_ARRAY:
os << "Array";
break;
default:
@@ -271,16 +345,16 @@ static void print_image_type(std::ostream &os,
static std::ostream &print_qualifier(std::ostream &os, const Qualifier &qualifiers)
{
- if ((qualifiers & Qualifier::RESTRICT) != Qualifier::RESTRICT) {
- os << "restrict";
+ if (bool(qualifiers & Qualifier::NO_RESTRICT) == false) {
+ os << "restrict ";
}
- if ((qualifiers & Qualifier::READ_ONLY) != Qualifier::READ_ONLY) {
- os << "readonly";
+ if (bool(qualifiers & Qualifier::READ) == false) {
+ os << "writeonly ";
}
- if ((qualifiers & Qualifier::WRITE_ONLY) != Qualifier::WRITE_ONLY) {
- os << "writeonly";
+ if (bool(qualifiers & Qualifier::WRITE) == false) {
+ os << "readonly ";
}
- return os << " ";
+ return os;
}
static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
@@ -288,7 +362,7 @@ static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &r
if (GLContext::explicit_location_support) {
os << "layout(binding = " << res.slot;
if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
- os << ", " << res.image.format;
+ os << ", " << to_string(res.image.format);
}
else if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
os << ", std140";
@@ -328,8 +402,8 @@ static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &r
array_offset = res.storagebuf.name.find_first_of("[");
name_no_array = (array_offset == -1) ? res.storagebuf.name :
StringRef(res.storagebuf.name.c_str(), array_offset);
- os << "buffer ";
print_qualifier(os, res.storagebuf.qualifiers);
+ os << "buffer ";
os << name_no_array << " { " << res.storagebuf.type_name << " _" << res.storagebuf.name
<< "; };\n";
break;
@@ -364,7 +438,7 @@ static void print_interface(std::ostream &os,
const StageInterfaceInfo &iface,
const StringRefNull &suffix = "")
{
- /* TODO(fclem) Move that to interface check. */
+ /* TODO(@fclem): Move that to interface check. */
// if (iface.instance_name.is_empty()) {
// BLI_assert_msg(0, "Interfaces require an instance name for geometry shader.");
// std::cout << iface.name << ": Interfaces require an instance name for geometry shader.\n";
@@ -403,18 +477,37 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const
}
ss << "\n/* Push Constants. */\n";
for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) {
- if (GLContext::explicit_location_support) {
- ss << "layout(location = " << uniform.index << ") ";
- }
ss << "uniform " << to_string(uniform.type) << " " << uniform.name;
if (uniform.array_size > 0) {
ss << "[" << uniform.array_size << "]";
}
ss << ";\n";
}
+#if 0 /* T95278: This is not be enough to prevent some compilers think it is recursive. */
for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) {
- ss << "#define " << uniform.name << " (" << uniform.name << ")\n";
+ /* T95278: Double macro to avoid some compilers think it is recursive. */
+ ss << "#define " << uniform.name << "_ " << uniform.name << "\n";
+ ss << "#define " << uniform.name << " (" << uniform.name << "_)\n";
}
+#endif
+ ss << "\n";
+ return ss.str();
+}
+
+static std::string main_function_wrapper(std::string &pre_main, std::string &post_main)
+{
+ std::stringstream ss;
+ /* Prototype for the original main. */
+ ss << "\n";
+ ss << "void main_function_();\n";
+ /* Wrapper to the main function in order to inject code processing on globals. */
+ ss << "void main() {\n";
+ ss << pre_main;
+ ss << " main_function_();\n";
+ ss << post_main;
+ ss << "}\n";
+ /* Rename the original main. */
+ ss << "#define main main_function_\n";
ss << "\n";
return ss.str();
}
@@ -422,10 +515,13 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const
std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) const
{
std::stringstream ss;
+ std::string post_main = "";
ss << "\n/* Inputs. */\n";
for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
- if (GLContext::explicit_location_support) {
+ if (GLContext::explicit_location_support &&
+ /* Fix issue with AMDGPU-PRO + workbench_prepass_mesh_vert.glsl being quantized. */
+ GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) == false) {
ss << "layout(location = " << attr.index << ") ";
}
ss << "in " << to_string(attr.type) << " " << attr.name << ";\n";
@@ -434,13 +530,35 @@ std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) con
for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) {
print_interface(ss, "out", *iface);
}
+ if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::LAYER)) {
+ ss << "out int gpu_Layer;\n";
+ }
+ if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
+ if (!GLContext::native_barycentric_support) {
+ /* Disabled or unsupported. */
+ }
+ else if (GLEW_AMD_shader_explicit_vertex_parameter) {
+ /* Need this for stable barycentric. */
+ ss << "flat out vec4 gpu_pos_flat;\n";
+ ss << "out vec4 gpu_pos;\n";
+
+ post_main += " gpu_pos = gpu_pos_flat = gl_Position;\n";
+ }
+ }
ss << "\n";
+
+ if (post_main.empty() == false) {
+ std::string pre_main = "";
+ ss << main_function_wrapper(pre_main, post_main);
+ }
return ss.str();
}
std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) const
{
std::stringstream ss;
+ std::string pre_main = "";
+
ss << "\n/* Interfaces. */\n";
const Vector<StageInterfaceInfo *> &in_interfaces = (info.geometry_source_.is_empty()) ?
info.vertex_out_interfaces_ :
@@ -448,6 +566,32 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
for (const StageInterfaceInfo *iface : in_interfaces) {
print_interface(ss, "in", *iface);
}
+ if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
+ if (!GLContext::native_barycentric_support) {
+ ss << "smooth in vec3 gpu_BaryCoord;\n";
+ ss << "noperspective in vec3 gpu_BaryCoordNoPersp;\n";
+ }
+ else if (GLEW_AMD_shader_explicit_vertex_parameter) {
+ /* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry
+ * shader workaround if this extension/feature is detected. */
+ ss << "\n/* Stable Barycentric Coordinates. */\n";
+ ss << "flat in vec4 gpu_pos_flat;\n";
+ ss << "__explicitInterpAMD in vec4 gpu_pos;\n";
+ /* Globals. */
+ ss << "vec3 gpu_BaryCoord;\n";
+ ss << "vec3 gpu_BaryCoordNoPersp;\n";
+ ss << "\n";
+ ss << "vec2 stable_bary_(vec2 in_bary) {\n";
+ ss << " vec3 bary = vec3(in_bary, 1.0 - in_bary.x - in_bary.y);\n";
+ ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { return bary.zxy; }\n";
+ ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { return bary.yzx; }\n";
+ ss << " return bary.xyz;\n";
+ ss << "}\n";
+
+ pre_main += " gpu_BaryCoord = stable_bary_(gl_BaryCoordSmoothAMD);\n";
+ pre_main += " gpu_BaryCoordNoPersp = stable_bary_(gl_BaryCoordNoPerspAMD);\n";
+ }
+ }
ss << "\n/* Outputs. */\n";
for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) {
ss << "layout(location = " << output.index;
@@ -465,6 +609,11 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
ss << "out " << to_string(output.type) << " " << output.name << ";\n";
}
ss << "\n";
+
+ if (pre_main.empty() == false) {
+ std::string post_main = "";
+ ss << main_function_wrapper(pre_main, post_main);
+ }
return ss.str();
}
@@ -492,21 +641,125 @@ std::string GLShader::geometry_layout_declare(const ShaderCreateInfo &info) cons
return ss.str();
}
+static StageInterfaceInfo *find_interface_by_name(const Vector<StageInterfaceInfo *> &ifaces,
+ const StringRefNull &name)
+{
+ for (auto *iface : ifaces) {
+ if (iface->instance_name == name) {
+ return iface;
+ }
+ }
+ return nullptr;
+}
+
std::string GLShader::geometry_interface_declare(const ShaderCreateInfo &info) const
{
std::stringstream ss;
+
ss << "\n/* Interfaces. */\n";
for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) {
- print_interface(ss, "in", *iface, "[]");
+ bool has_matching_output_iface = find_interface_by_name(info.geometry_out_interfaces_,
+ iface->instance_name) != nullptr;
+ const char *suffix = (has_matching_output_iface) ? "_in[]" : "[]";
+ print_interface(ss, "in", *iface, suffix);
}
ss << "\n";
for (const StageInterfaceInfo *iface : info.geometry_out_interfaces_) {
- print_interface(ss, "out", *iface);
+ bool has_matching_input_iface = find_interface_by_name(info.vertex_out_interfaces_,
+ iface->instance_name) != nullptr;
+ const char *suffix = (has_matching_input_iface) ? "_out" : "";
+ print_interface(ss, "out", *iface, suffix);
}
ss << "\n";
return ss.str();
}
+std::string GLShader::compute_layout_declare(const ShaderCreateInfo &info) const
+{
+ std::stringstream ss;
+ ss << "\n/* Compute Layout. */\n";
+ ss << "layout(local_size_x = " << info.compute_layout_.local_size_x;
+ if (info.compute_layout_.local_size_y != -1) {
+ ss << ", local_size_y = " << info.compute_layout_.local_size_y;
+ }
+ if (info.compute_layout_.local_size_z != -1) {
+ ss << ", local_size_y = " << info.compute_layout_.local_size_z;
+ }
+ ss << ") in;\n";
+ ss << "\n";
+ return ss.str();
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Passthrough geometry shader emulation
+ *
+ * \{ */
+
+std::string GLShader::workaround_geometry_shader_source_create(
+ const shader::ShaderCreateInfo &info)
+{
+ std::stringstream ss;
+
+ const bool do_layer_workaround = !GLContext::layered_rendering_support &&
+ bool(info.builtins_ & BuiltinBits::LAYER);
+ const bool do_barycentric_workaround = !GLContext::native_barycentric_support &&
+ bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD);
+
+ shader::ShaderCreateInfo info_modified = info;
+ info_modified.geometry_out_interfaces_ = info_modified.vertex_out_interfaces_;
+ /**
+ * NOTE(@fclem): Assuming we will render TRIANGLES. This will not work with other primitive
+ * types. In this case, it might not trigger an error on some implementations.
+ */
+ info_modified.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3);
+
+ ss << geometry_layout_declare(info_modified);
+ ss << geometry_interface_declare(info_modified);
+ if (do_layer_workaround) {
+ ss << "in int gpu_Layer[];\n";
+ }
+ if (do_barycentric_workaround) {
+ ss << "smooth out vec3 gpu_BaryCoord;\n";
+ ss << "noperspective out vec3 gpu_BaryCoordNoPersp;\n";
+ }
+ ss << "\n";
+
+ ss << "void main()\n";
+ ss << "{\n";
+ if (do_layer_workaround) {
+ ss << " gl_Layer = gpu_Layer[0];\n";
+ }
+ for (auto i : IndexRange(3)) {
+ for (auto iface : info_modified.vertex_out_interfaces_) {
+ for (auto &inout : iface->inouts) {
+ ss << " " << iface->instance_name << "_out." << inout.name;
+ ss << " = " << iface->instance_name << "_in[" << i << "]." << inout.name << ";\n";
+ }
+ }
+ if (do_barycentric_workaround) {
+ ss << " gpu_BaryCoordNoPersp = gpu_BaryCoord =";
+ ss << " vec3(" << int(i == 0) << ", " << int(i == 1) << ", " << int(i == 2) << ");\n";
+ }
+ ss << " gl_Position = gl_in[" << i << "].gl_Position;\n";
+ ss << " EmitVertex();\n";
+ }
+ ss << "}\n";
+ return ss.str();
+}
+
+bool GLShader::do_geometry_shader_injection(const shader::ShaderCreateInfo *info)
+{
+ BuiltinBits builtins = info->builtins_;
+ if (!GLContext::native_barycentric_support && bool(builtins & BuiltinBits::BARYCENTRIC_COORD)) {
+ return true;
+ }
+ if (!GLContext::layered_rendering_support && bool(builtins & BuiltinBits::LAYER)) {
+ return true;
+ }
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -516,7 +769,7 @@ std::string GLShader::geometry_interface_declare(const ShaderCreateInfo &info) c
static char *glsl_patch_default_get()
{
/** Used for shader patching. Init once. */
- static char patch[512] = "\0";
+ static char patch[1024] = "\0";
if (patch[0] != '\0') {
return patch;
}
@@ -543,6 +796,7 @@ static char *glsl_patch_default_get()
if (GLContext::shader_draw_parameters_support) {
STR_CONCAT(patch, slen, "#extension GL_ARB_shader_draw_parameters : enable\n");
STR_CONCAT(patch, slen, "#define GPU_ARB_shader_draw_parameters\n");
+ STR_CONCAT(patch, slen, "#define gpu_BaseInstance gl_BaseInstanceARB\n");
}
if (GLContext::geometry_shader_invocations) {
STR_CONCAT(patch, slen, "#extension GL_ARB_gpu_shader5 : enable\n");
@@ -552,6 +806,31 @@ static char *glsl_patch_default_get()
STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n");
STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n");
}
+ if (!GLEW_VERSION_4_2 && GLEW_ARB_conservative_depth) {
+ STR_CONCAT(patch, slen, "#extension GL_ARB_conservative_depth : enable\n");
+ }
+ if (GPU_shader_image_load_store_support()) {
+ STR_CONCAT(patch, slen, "#extension GL_ARB_shader_image_load_store: enable\n");
+ STR_CONCAT(patch, slen, "#extension GL_ARB_shading_language_420pack: enable\n");
+ }
+ if (GLContext::layered_rendering_support) {
+ STR_CONCAT(patch, slen, "#extension GL_AMD_vertex_shader_layer: enable\n");
+ STR_CONCAT(patch, slen, "#define gpu_Layer gl_Layer\n");
+ }
+ if (GLContext::native_barycentric_support) {
+ STR_CONCAT(patch, slen, "#extension GL_AMD_shader_explicit_vertex_parameter: enable\n");
+ }
+
+ /* Fallbacks. */
+ if (!GLContext::shader_draw_parameters_support) {
+ STR_CONCAT(patch, slen, "uniform int gpu_BaseInstance;\n");
+ }
+
+ /* Vulkan GLSL compat. */
+ STR_CONCAT(patch, slen, "#define gpu_InstanceIndex (gl_InstanceID + gpu_BaseInstance)\n");
+
+ /* Array compat. */
+ STR_CONCAT(patch, slen, "#define array(_type) _type[]\n");
/* Derivative sign can change depending on implementation. */
STR_CONCATF(patch, slen, "#define DFDX_SIGN %1.1f\n", GLContext::derivative_signs[0]);
@@ -660,6 +939,14 @@ bool GLShader::finalize(const shader::ShaderCreateInfo *info)
return false;
}
+ if (info && do_geometry_shader_injection(info)) {
+ std::string source = workaround_geometry_shader_source_create(*info);
+ Vector<const char *> sources;
+ sources.append("version");
+ sources.append(source.c_str());
+ geometry_shader_from_glsl(sources);
+ }
+
glLinkProgram(shader_program_);
GLint status;
diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh
index af92c77db54..cc1c93142f8 100644
--- a/source/blender/gpu/opengl/gl_shader.hh
+++ b/source/blender/gpu/opengl/gl_shader.hh
@@ -69,6 +69,7 @@ class GLShader : public Shader {
std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const override;
std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const override;
std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const override;
/** Should be called before linking. */
void transform_feedback_names_set(Span<const char *> name_list,
@@ -93,6 +94,14 @@ class GLShader : public Shader {
/** Create, compile and attach the shader stage to the shader program. */
GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources);
+ /**
+ * \brief features available on newer implementation such as native barycentric coordinates
+ * and layered rendering, necessitate a geometry shader to work on older hardware.
+ */
+ std::string workaround_geometry_shader_source_create(const shader::ShaderCreateInfo &info);
+
+ bool do_geometry_shader_injection(const shader::ShaderCreateInfo *info);
+
MEM_CXX_CLASS_ALLOC_FUNCS("GLShader");
};
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
index 2211c2fbb7c..0a31f8dee7f 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.cc
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -117,7 +117,28 @@ static inline int image_binding(int32_t program,
switch (type) {
case GL_IMAGE_1D:
case GL_IMAGE_2D:
- case GL_IMAGE_3D: {
+ case GL_IMAGE_3D:
+ case GL_IMAGE_CUBE:
+ case GL_IMAGE_BUFFER:
+ case GL_IMAGE_1D_ARRAY:
+ case GL_IMAGE_2D_ARRAY:
+ case GL_IMAGE_CUBE_MAP_ARRAY:
+ case GL_INT_IMAGE_1D:
+ case GL_INT_IMAGE_2D:
+ case GL_INT_IMAGE_3D:
+ case GL_INT_IMAGE_CUBE:
+ case GL_INT_IMAGE_BUFFER:
+ case GL_INT_IMAGE_1D_ARRAY:
+ case GL_INT_IMAGE_2D_ARRAY:
+ case GL_INT_IMAGE_CUBE_MAP_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_1D:
+ case GL_UNSIGNED_INT_IMAGE_2D:
+ case GL_UNSIGNED_INT_IMAGE_3D:
+ case GL_UNSIGNED_INT_IMAGE_CUBE:
+ case GL_UNSIGNED_INT_IMAGE_BUFFER:
+ case GL_UNSIGNED_INT_IMAGE_1D_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: {
/* For now just assign a consecutive index. In the future, we should set it in
* the shader using layout(binding = i) and query its value. */
int binding = *image_len;
@@ -298,6 +319,7 @@ GLShaderInterface::GLShaderInterface(GLuint program)
input->binding = input->location = binding;
name_buffer_offset += this->set_input_name(input, name, name_len);
+ enabled_ssbo_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
}
/* Builtin Uniforms */
@@ -355,15 +377,33 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
}
}
+ size_t workaround_names_size = 0;
+ Vector<StringRefNull> workaround_uniform_names;
+ auto check_enabled_uniform = [&](const char *uniform_name) {
+ if (glGetUniformLocation(program, uniform_name) != -1) {
+ workaround_uniform_names.append(uniform_name);
+ workaround_names_size += StringRefNull(uniform_name).size() + 1;
+ uniform_len_++;
+ }
+ };
+
+ if (!GLContext::shader_draw_parameters_support) {
+ check_enabled_uniform("gpu_BaseInstance");
+ }
+
BLI_assert_msg(ubo_len_ <= 16, "enabled_ubo_mask_ is uint16_t");
int input_tot_len = attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_;
inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__);
ShaderInput *input = inputs_;
- name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_, "name_buffer");
+ name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_ + workaround_names_size,
+ "name_buffer");
uint32_t name_buffer_offset = 0;
+ /* Necessary to make #glUniform works. TODO(fclem) Remove. */
+ glUseProgram(program);
+
/* Attributes */
for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
copy_input_name(input, attr.name, name_buffer_, name_buffer_offset);
@@ -382,7 +422,7 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
copy_input_name(input, res.uniformbuf.name, name_buffer_, name_buffer_offset);
if (true || !GLContext::explicit_location_support) {
- input->location = glGetUniformBlockIndex(program, res.uniformbuf.name.c_str());
+ input->location = glGetUniformBlockIndex(program, name_buffer_ + input->name_offset);
glUniformBlockBinding(program, input->location, res.slot);
}
input->binding = res.slot;
@@ -419,10 +459,15 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
}
for (const ShaderCreateInfo::PushConst &uni : info.push_constants_) {
copy_input_name(input, uni.name, name_buffer_, name_buffer_offset);
- /* Until we make use of explicit uniform location. */
- if (true || !GLContext::explicit_location_support) {
- input->location = glGetUniformLocation(program, uni.name.c_str());
- }
+ input->location = glGetUniformLocation(program, name_buffer_ + input->name_offset);
+ input->binding = -1;
+ input++;
+ }
+
+ /* Compatibility uniforms. */
+ for (auto &name : workaround_uniform_names) {
+ copy_input_name(input, name, name_buffer_, name_buffer_offset);
+ input->location = glGetUniformLocation(program, name_buffer_ + input->name_offset);
input->binding = -1;
input++;
}
@@ -432,7 +477,7 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset);
input->location = input->binding = res.slot;
- enabled_ubo_mask_ |= (1 << input->binding);
+ enabled_ssbo_mask_ |= (1 << input->binding);
input++;
}
}
@@ -474,7 +519,7 @@ GLShaderInterface::~GLShaderInterface()
void GLShaderInterface::ref_add(GLVaoCache *ref)
{
for (int i = 0; i < refs_.size(); i++) {
- if (refs_[i] == NULL) {
+ if (refs_[i] == nullptr) {
refs_[i] = ref;
return;
}
@@ -486,7 +531,7 @@ void GLShaderInterface::ref_remove(GLVaoCache *ref)
{
for (int i = 0; i < refs_.size(); i++) {
if (refs_[i] == ref) {
- refs_[i] = NULL;
+ refs_[i] = nullptr;
break; /* cannot have duplicates */
}
}
diff --git a/source/blender/gpu/opengl/gl_shader_log.cc b/source/blender/gpu/opengl/gl_shader_log.cc
index 174cc63ad81..ec13bc44136 100644
--- a/source/blender/gpu/opengl/gl_shader_log.cc
+++ b/source/blender/gpu/opengl/gl_shader_log.cc
@@ -60,6 +60,15 @@ char *GLLogParser::parse_line(char *log_line, GPULogItem &log_item)
log_item.cursor.row = log_item.cursor.column;
log_item.cursor.column = -1;
}
+ else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL) &&
+ /* WORKAROUND(@fclem): Both Mesa and AMDGPU-PRO are reported as official. */
+ StringRefNull(GPU_platform_version()).find(" Mesa ") == -1) {
+ /* source:row */
+ log_item.cursor.source = log_item.cursor.row;
+ log_item.cursor.row = log_item.cursor.column;
+ log_item.cursor.column = -1;
+ log_item.source_base_row = true;
+ }
else {
/* line:char */
}
diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh
index 83ff3ffc9e9..5985f1583f2 100644
--- a/source/blender/gpu/opengl/gl_state.hh
+++ b/source/blender/gpu/opengl/gl_state.hh
@@ -124,11 +124,20 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits)
if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS) {
barrier |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
}
+ if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) {
+ barrier |= GL_SHADER_STORAGE_BARRIER_BIT;
+ }
if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) {
barrier |= GL_TEXTURE_FETCH_BARRIER_BIT;
}
- if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) {
- barrier |= GL_SHADER_STORAGE_BARRIER_BIT;
+ if (barrier_bits & GPU_BARRIER_TEXTURE_UPDATE) {
+ barrier |= GL_TEXTURE_UPDATE_BARRIER_BIT;
+ }
+ if (barrier_bits & GPU_BARRIER_COMMAND) {
+ barrier |= GL_COMMAND_BARRIER_BIT;
+ }
+ if (barrier_bits & GPU_BARRIER_FRAMEBUFFER) {
+ barrier |= GL_FRAMEBUFFER_BARRIER_BIT;
}
if (barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY) {
barrier |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
index b83ea59a692..779bcc59487 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
@@ -4,57 +4,18 @@
#define MID_VERTEX 65
-#ifndef USE_GPU_SHADER_CREATE_INFO
-
-/* u is position along the curve, defining the tangent space.
- * v is "signed" distance (compressed to [0..1] range) from the pos in expand direction */
-in vec2 uv;
-in vec2 pos; /* verts position in the curve tangent space */
-in vec2 expand;
-
-# ifdef USE_INSTANCE
-/* Instance attrs. */
-in vec2 P0;
-in vec2 P1;
-in vec2 P2;
-in vec2 P3;
-in ivec4 colid_doarrow;
-in vec4 start_color;
-in vec4 end_color;
-in ivec2 domuted;
-in float dim_factor;
-in float thickness;
-in float dash_factor;
-in float dash_alpha;
-
-uniform vec4 colors[6];
-
-# else
-/* Single curve drawcall, use uniform. */
-uniform vec2 bezierPts[4];
-
-uniform vec4 colors[3];
-uniform bool doArrow;
-uniform bool doMuted;
-uniform float dim_factor;
-uniform float thickness;
-uniform float dash_factor;
-uniform float dash_alpha;
-
-# endif
-
-uniform float expandSize;
-uniform float arrowSize;
-uniform mat4 ModelViewProjectionMatrix;
-
-out float colorGradient;
-out vec4 finalColor;
-out float lineU;
-flat out float lineLength;
-flat out float dashFactor;
-flat out float dashAlpha;
-flat out int isMainLine;
-#endif
+/**
+ * `uv.x` is position along the curve, defining the tangent space.
+ * `uv.y` is "signed" distance (compressed to [0..1] range) from the pos in expand direction
+ * `pos` is the verts position in the curve tangent space
+ */
+
+void main(void)
+{
+ /* Define where along the noodle the gradient will starts and ends.
+ * Use 0.25 instead of 0.35-0.65, because of a visual shift issue. */
+ const float start_gradient_threshold = 0.25;
+ const float end_gradient_threshold = 0.55;
#ifdef USE_INSTANCE
# define colStart (colid_doarrow[0] < 3 ? start_color : node_link_data.colors[colid_doarrow[0]])
@@ -62,33 +23,23 @@ flat out int isMainLine;
# define colShadow node_link_data.colors[colid_doarrow[2]]
# define doArrow (colid_doarrow[3] != 0)
# define doMuted (domuted[0] != 0)
-
#else
-# define P0 node_link_data.bezierPts[0].xy
-# define P1 node_link_data.bezierPts[1].xy
-# define P2 node_link_data.bezierPts[2].xy
-# define P3 node_link_data.bezierPts[3].xy
-# define cols node_link_data.colors
-# define doArrow node_link_data.doArrow
-# define doMuted node_link_data.doMuted
-# define dim_factor node_link_data.dim_factor
-# define thickness node_link_data.thickness
-# define dash_factor node_link_data.dash_factor
-# define dash_alpha node_link_data.dash_alpha
-
-# define colShadow node_link_data.colors[0]
-# define colStart node_link_data.colors[1]
-# define colEnd node_link_data.colors[2]
-
+ vec2 P0 = node_link_data.bezierPts[0].xy;
+ vec2 P1 = node_link_data.bezierPts[1].xy;
+ vec2 P2 = node_link_data.bezierPts[2].xy;
+ vec2 P3 = node_link_data.bezierPts[3].xy;
+ bool doArrow = node_link_data.doArrow;
+ bool doMuted = node_link_data.doMuted;
+ float dim_factor = node_link_data.dim_factor;
+ float thickness = node_link_data.thickness;
+ float dash_factor = node_link_data.dash_factor;
+ float dash_alpha = node_link_data.dash_alpha;
+
+ vec4 colShadow = node_link_data.colors[0];
+ vec4 colStart = node_link_data.colors[1];
+ vec4 colEnd = node_link_data.colors[2];
#endif
-/* Define where along the noodle the gradient will starts and ends.
- * Use 0.25 instead of 0.35-0.65, because of a visual shift issue. */
-const float start_gradient_threshold = 0.25;
-const float end_gradient_threshold = 0.55;
-
-void main(void)
-{
/* Parameters for the dashed line. */
isMainLine = expand.y != 1.0 ? 0 : 1;
dashFactor = dash_factor;
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl
index 4d887a37807..1ec84598bf1 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl)
+
#ifndef USE_GPU_SHADER_CREATE_INFO
noperspective in vec4 finalColor;
out vec4 fragColor;
diff --git a/source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl
index de555cc5706..f374913a32c 100644
--- a/source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_3D_smooth_color_frag.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl)
+
#ifndef USE_GPU_SHADER_CREATE_INFO
in vec4 finalColor;
out vec4 fragColor;
diff --git a/source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl b/source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl
index 74341701fb0..7d69cba5017 100644
--- a/source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_colorspace_lib.glsl
@@ -6,13 +6,13 @@
uniform bool srgbTarget = false;
#endif
-vec4 blender_srgb_to_framebuffer_space(vec4 color)
+vec4 blender_srgb_to_framebuffer_space(vec4 col)
{
if (srgbTarget) {
- vec3 c = max(color.rgb, vec3(0.0));
+ vec3 c = max(col.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
- color.rgb = mix(c1, c2, step(vec3(0.04045), c));
+ col.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
- return color;
+ return col;
}
diff --git a/source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl
index 1675de3d567..28a716104f1 100644
--- a/source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_flat_color_frag.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl)
+
#ifndef USE_GPU_SHADER_CREATE_INFO
flat in vec4 finalColor;
out vec4 fragColor;
diff --git a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
index 1456bd0c732..c339d3cbabb 100644
--- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl)
+
#ifndef USE_GPU_SHADER_CREATE_INFO
flat in vec4 color_flat;
noperspective in vec2 texCoord_interp;
diff --git a/source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl
index b4a75cc489b..0510848e4d4 100644
--- a/source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_uniform_color_frag.glsl
@@ -1,3 +1,4 @@
+#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl)
#ifndef USE_GPU_SHADER_CREATE_INFO
uniform vec4 color;
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh
index bf746eae9b4..b5dce51fc1b 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_area_borders_info.hh
@@ -29,11 +29,11 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_area_borders)
.vertex_in(0, Type::VEC2, "pos")
.vertex_out(smooth_uv_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC4, "rect")
- .push_constant(20, Type::VEC4, "color")
- .push_constant(24, Type::FLOAT, "scale")
- .push_constant(25, Type::INT, "cornerLen")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "rect")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::FLOAT, "scale")
+ .push_constant(Type::INT, "cornerLen")
.vertex_source("gpu_shader_2D_area_borders_vert.glsl")
.fragment_source("gpu_shader_2D_area_borders_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh
index 48c261da8dd..b8dbca276ae 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_checker_info.hh
@@ -26,10 +26,10 @@
GPU_SHADER_CREATE_INFO(gpu_shader_2D_checker)
.vertex_in(0, Type::VEC2, "pos")
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC4, "color1")
- .push_constant(20, Type::VEC4, "color2")
- .push_constant(24, Type::INT, "size")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color1")
+ .push_constant(Type::VEC4, "color2")
+ .push_constant(Type::INT, "size")
.vertex_source("gpu_shader_2D_vert.glsl")
.fragment_source("gpu_shader_checker_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh
index 51ce7f503df..b52be8b328e 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_diag_stripes_info.hh
@@ -26,11 +26,11 @@
GPU_SHADER_CREATE_INFO(gpu_shader_2D_diag_stripes)
.vertex_in(0, Type::VEC2, "pos")
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC4, "color1")
- .push_constant(20, Type::VEC4, "color2")
- .push_constant(24, Type::INT, "size1")
- .push_constant(28, Type::INT, "size2")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color1")
+ .push_constant(Type::VEC4, "color2")
+ .push_constant(Type::INT, "size1")
+ .push_constant(Type::INT, "size2")
.vertex_source("gpu_shader_2D_vert.glsl")
.fragment_source("gpu_shader_diag_stripes_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh
index bbc5446f16f..699b0a456f9 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh
@@ -30,7 +30,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_flat_color)
.vertex_in(1, Type::VEC4, "color")
.vertex_out(flat_color_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_2D_flat_color_vert.glsl")
.fragment_source("gpu_shader_flat_color_frag.glsl")
.additional_info("gpu_srgb_to_framebuffer_space")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh
index a6cc9076d4a..8ac9f58d936 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh
@@ -25,6 +25,6 @@
GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_color)
.additional_info("gpu_shader_2D_image_common")
- .push_constant(16, Type::VEC4, "color")
+ .push_constant(Type::VEC4, "color")
.fragment_source("gpu_shader_image_color_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh
index e11d6746446..3e10c0e1651 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh
@@ -25,7 +25,7 @@
GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_desaturate_color)
.additional_info("gpu_shader_2D_image_common")
- .push_constant(16, Type::VEC4, "color")
- .push_constant(20, Type::FLOAT, "factor")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::FLOAT, "factor")
.fragment_source("gpu_shader_image_desaturate_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh
index 3d20b63c265..989e38527c0 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh
@@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_common)
.vertex_in(1, Type::VEC2, "texCoord")
.vertex_out(smooth_tex_coord_interp_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.sampler(0, ImageType::FLOAT_2D, "image")
.vertex_source("gpu_shader_2D_image_vert.glsl");
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh
index c2c0e9fec78..d1a2a8f6ee7 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh
@@ -29,9 +29,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_merge)
.vertex_in(1, Type::VEC2, "texCoord")
.vertex_out(smooth_tex_coord_interp_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::BOOL, "display_transform")
- .push_constant(17, Type::BOOL, "overlay")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::BOOL, "display_transform")
+ .push_constant(Type::BOOL, "overlay")
.sampler(0, ImageType::FLOAT_2D, "image_texture")
.sampler(1, ImageType::FLOAT_2D, "overlays_texture")
.vertex_source("gpu_shader_2D_image_vert.glsl")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh
index c1e6c3957d3..d099d95e4b6 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh
@@ -29,8 +29,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_stereo_merge)
.fragment_out(1, Type::VEC4, "overlayColor")
.sampler(0, ImageType::FLOAT_2D, "imageTexture")
.sampler(1, ImageType::FLOAT_2D, "overlayTexture")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::INT, "stereoDisplaySettings")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::INT, "stereoDisplaySettings")
.vertex_source("gpu_shader_2D_vert.glsl")
.fragment_source("gpu_shader_image_overlays_stereo_merge_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh
index b94556bbb25..9d5fb152561 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_rect_color_info.hh
@@ -25,14 +25,12 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_rect_color)
- .vertex_in(0, Type::VEC2, "pos")
- .vertex_in(1, Type::VEC2, "texCoord")
.vertex_out(smooth_tex_coord_interp_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC4, "color")
- .push_constant(20, Type::VEC4, "rect_icon")
- .push_constant(24, Type::VEC4, "rect_geom")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::VEC4, "rect_icon")
+ .push_constant(Type::VEC4, "rect_geom")
.sampler(0, ImageType::FLOAT_2D, "image")
.vertex_source("gpu_shader_2D_image_rect_vert.glsl")
.fragment_source("gpu_shader_image_color_frag.glsl")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh
index 3663de0a98f..93950b37509 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_shuffle_color_info.hh
@@ -25,7 +25,7 @@
GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_shuffle_color)
.additional_info("gpu_shader_2D_image_common")
- .push_constant(16, Type::VEC4, "color")
- .push_constant(20, Type::VEC4, "shuffle")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::VEC4, "shuffle")
.fragment_source("gpu_shader_image_shuffle_color_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh
index 371a35386a7..afac24e6241 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_line_dashed_uniform_color_info.hh
@@ -28,7 +28,7 @@
GPU_SHADER_CREATE_INFO(gpu_shader_2D_line_dashed_uniform_color)
.vertex_in(0, Type::VEC3, "pos")
.vertex_out(flat_color_iface)
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_2D_line_dashed_uniform_color_vert.glsl")
.fragment_source("gpu_shader_2D_line_dashed_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh
index b15d7ba3ada..15451899d5d 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh
@@ -39,7 +39,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_nodelink)
.vertex_out(nodelink_iface)
.fragment_out(0, Type::VEC4, "fragColor")
.uniform_buf(0, "NodeLinkData", "node_link_data", Frequency::PASS)
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_2D_nodelink_vert.glsl")
.fragment_source("gpu_shader_2D_nodelink_frag.glsl")
.typedef_source("GPU_shader_shared.h")
@@ -64,7 +64,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_nodelink_inst)
.vertex_out(nodelink_iface)
.fragment_out(0, Type::VEC4, "fragColor")
.uniform_buf(0, "NodeLinkInstanceData", "node_link_data", Frequency::PASS)
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_2D_nodelink_vert.glsl")
.fragment_source("gpu_shader_2D_nodelink_frag.glsl")
.typedef_source("GPU_shader_shared.h")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh
index d2753af8e9b..2a3d5698ba4 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh
@@ -28,9 +28,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_aa)
.vertex_in(0, Type::VEC2, "pos")
.vertex_out(smooth_radii_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC4, "color")
- .push_constant(20, Type::FLOAT, "size")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::FLOAT, "size")
.vertex_source("gpu_shader_2D_point_uniform_size_aa_vert.glsl")
.fragment_source("gpu_shader_point_uniform_color_aa_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh
index edc83534573..c7cc61e745b 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh
@@ -28,11 +28,11 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_outline_aa
.vertex_in(0, Type::VEC2, "pos")
.vertex_out(smooth_radii_outline_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(20, Type::VEC4, "color")
- .push_constant(24, Type::VEC4, "outlineColor")
- .push_constant(28, Type::FLOAT, "size")
- .push_constant(29, Type::FLOAT, "outlineWidth")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::VEC4, "outlineColor")
+ .push_constant(Type::FLOAT, "size")
+ .push_constant(Type::FLOAT, "outlineWidth")
.vertex_source("gpu_shader_2D_point_uniform_size_outline_aa_vert.glsl")
.fragment_source("gpu_shader_point_uniform_color_outline_aa_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh
index 4358e94f91f..38dddb4357e 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh
@@ -30,7 +30,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_varying_size_varying_color)
.vertex_in(2, Type::VEC4, "color")
.vertex_out(smooth_color_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_2D_point_varying_size_varying_color_vert.glsl")
.fragment_source("gpu_shader_point_varying_color_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh
index 0029e8d2044..128be12a7d9 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh
@@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_smooth_color)
.vertex_in(1, Type::VEC4, "color")
.vertex_out(smooth_color_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_2D_smooth_color_vert.glsl")
.fragment_source("gpu_shader_2D_smooth_color_frag.glsl")
.additional_info("gpu_srgb_to_framebuffer_space")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh
index 7e75b265711..3dab6d36753 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh
@@ -26,8 +26,8 @@
GPU_SHADER_CREATE_INFO(gpu_shader_2D_uniform_color)
.vertex_in(0, Type::VEC2, "pos")
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC4, "color")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color")
.vertex_source("gpu_shader_2D_vert.glsl")
.fragment_source("gpu_shader_uniform_color_frag.glsl")
.additional_info("gpu_srgb_to_framebuffer_space")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh
index 3e7e4aeecda..63a4e679215 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh
@@ -27,7 +27,7 @@
GPU_SHADER_CREATE_INFO(gpu_shader_3D_depth_only)
.vertex_in(0, Type::VEC3, "pos")
.vertex_out(flat_color_iface)
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_3D_vert.glsl")
.fragment_source("gpu_shader_depth_only_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh
index 70b2dac8266..4628f8bd6c4 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh
@@ -29,8 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_flat_color)
.vertex_in(1, Type::VEC4, "color")
.vertex_out(flat_color_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(1, Type::BOOL, "srgbTarget")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_3D_flat_color_vert.glsl")
.fragment_source("gpu_shader_flat_color_frag.glsl")
.additional_info("gpu_srgb_to_framebuffer_space")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
index b829975448c..d47df129501 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
@@ -29,8 +29,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_image_modulate_alpha)
.vertex_in(1, Type::VEC2, "texCoord")
.vertex_out(smooth_tex_coord_interp_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::FLOAT, "alpha")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::FLOAT, "alpha")
.sampler(0, ImageType::FLOAT_2D, "image", Frequency::PASS)
.vertex_source("gpu_shader_3D_image_vert.glsl")
.fragment_source("gpu_shader_image_modulate_alpha_frag.glsl")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh
index d43ea799420..62c05d4677c 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh
@@ -28,7 +28,7 @@
GPU_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color)
.vertex_in(0, Type::VEC3, "pos")
.vertex_out(flat_color_iface)
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_3D_line_dashed_uniform_color_vert.glsl")
.fragment_source("gpu_shader_2D_line_dashed_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh
index b62c8fe7518..d7b6806acc6 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh
@@ -28,8 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_fixed_size_varying_color)
.vertex_in(1, Type::VEC4, "color")
.vertex_out(smooth_color_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::FLOAT, "size")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_3D_point_fixed_size_varying_color_vert.glsl")
.fragment_source("gpu_shader_point_varying_color_frag.glsl")
.do_static_compilation(true);
@@ -40,7 +39,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_varying_size_varying_color)
.vertex_in(2, Type::FLOAT, "size")
.vertex_out(smooth_color_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_3D_point_varying_size_varying_color_vert.glsl")
.fragment_source("gpu_shader_point_varying_color_frag.glsl")
.do_static_compilation(true);
@@ -49,10 +48,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa)
.vertex_in(0, Type::VEC3, "pos")
.vertex_out(smooth_radii_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC4, "color")
- .push_constant(20, Type::FLOAT, "size")
- .push_constant(24, Type::FLOAT, "outlineWidth")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::FLOAT, "size")
.vertex_source("gpu_shader_3D_point_uniform_size_aa_vert.glsl")
.fragment_source("gpu_shader_point_uniform_color_aa_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh
index c99d9ed199d..10f6e9a5b83 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh
@@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color)
.vertex_in(1, Type::VEC4, "color")
.vertex_out(smooth_color_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.vertex_source("gpu_shader_3D_smooth_color_vert.glsl")
.fragment_source("gpu_shader_3D_smooth_color_frag.glsl")
.additional_info("gpu_srgb_to_framebuffer_space")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh
index 5b8517310ea..e96ce8842f1 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh
@@ -26,8 +26,8 @@
GPU_SHADER_CREATE_INFO(gpu_shader_3D_uniform_color)
.vertex_in(0, Type::VEC3, "pos")
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC4, "color")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color")
.vertex_source("gpu_shader_3D_vert.glsl")
.fragment_source("gpu_shader_uniform_color_frag.glsl")
.additional_info("gpu_srgb_to_framebuffer_space")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh
index c337c399f59..460ef3f5acf 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_gpencil_stroke_info.hh
@@ -41,8 +41,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke)
.uniform_buf(0, "GPencilStrokeData", "gpencil_stroke_data")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::MAT4, "ProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ProjectionMatrix")
.vertex_source("gpu_shader_gpencil_stroke_vert.glsl")
.geometry_source("gpu_shader_gpencil_stroke_geom.glsl")
.fragment_source("gpu_shader_gpencil_stroke_frag.glsl")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh
index 98a1fcf5b37..418ec0bef2d 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh
@@ -31,7 +31,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_instance_varying_color_varying_size)
.vertex_in(3, Type::FLOAT, "size")
.vertex_out(flat_color_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ViewProjectionMatrix")
.vertex_source("gpu_shader_instance_variying_size_variying_color_vert.glsl")
.fragment_source("gpu_shader_flat_color_frag.glsl")
.additional_info("gpu_srgb_to_framebuffer_space")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh
index f8cb94e52d0..bbd3b6a9f01 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_keyframe_shape_info.hh
@@ -38,9 +38,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_keyframe_shape)
.vertex_in(4, Type ::INT, "flags")
.vertex_out(keyframe_shape_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::VEC2, "ViewportSize")
- .push_constant(24, Type::FLOAT, "outline_scale")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC2, "ViewportSize")
+ .push_constant(Type::FLOAT, "outline_scale")
.vertex_source("gpu_shader_keyframe_shape_vert.glsl")
.fragment_source("gpu_shader_keyframe_shape_frag.glsl")
.do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh
index c3f86ed2b6f..8afa7d79f3f 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_simple_lighting_info.hh
@@ -32,8 +32,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_simple_lighting)
.vertex_out(smooth_normal_iface)
.fragment_out(0, Type::VEC4, "fragColor")
.uniform_buf(0, "SimpleLightingData", "simple_lighting_data", Frequency::PASS)
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(16, Type::MAT3, "NormalMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT3, "NormalMatrix")
.typedef_source("GPU_shader_shared.h")
.vertex_source("gpu_shader_3D_normal_vert.glsl")
.fragment_source("gpu_shader_simple_lighting_frag.glsl")
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_text_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_text_info.hh
index 2c17a494d76..9429db66f90 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_text_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_text_info.hh
@@ -37,7 +37,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_text)
.vertex_in(3, Type ::INT, "offset")
.vertex_out(text_iface)
.fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.sampler(0, ImageType::FLOAT_2D, "glyph", Frequency::PASS)
.vertex_source("gpu_shader_text_vert.glsl")
.fragment_source("gpu_shader_text_frag.glsl")
diff --git a/source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh b/source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh
index 3af49b56ab1..e9154bcaeda 100644
--- a/source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_srgb_to_framebuffer_space_info.hh
@@ -24,4 +24,5 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(gpu_srgb_to_framebuffer_space)
+ .push_constant(Type::BOOL, "srgbTarget")
.define("blender_srgb_to_framebuffer_space(a) a");
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
index 01a16e194ca..ab6024b073d 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
@@ -18,7 +18,7 @@ void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
result.radiance = out_Diffuse_0.radiance;
- /* TODO(fclem) Try to not use this. */
+ /* TODO(@fclem): Try to not use this. */
closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
index 6ffa6b59572..59f0377869b 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
@@ -1,15 +1,4 @@
-float wang_hash_noise(uint s)
-{
- s = (s ^ 61u) ^ (s >> 16u);
- s *= 9u;
- s = s ^ (s >> 4u);
- s *= 0x27d4eb2du;
- s = s ^ (s >> 15u);
-
- return fract(float(s) / 4294967296.0);
-}
-
void node_hair_info(float hair_length,
out float is_strand,
out float intercept,
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
index 86191451e5f..cb798047791 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
@@ -215,3 +215,14 @@ float integer_noise(int n)
nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
return 0.5 * (float(nn) / 1073741824.0);
}
+
+float wang_hash_noise(uint s)
+{
+ s = (s ^ 61u) ^ (s >> 16u);
+ s *= 9u;
+ s = s ^ (s >> 4u);
+ s *= 0x27d4eb2du;
+ s = s ^ (s >> 15u);
+
+ return fract(float(s) / 4294967296.0);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
index c6203bc36ab..2a98d9fadd0 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
@@ -109,6 +109,11 @@ void vector_normalize(vec3 normal, out vec3 outnormal)
outnormal = normalize(normal);
}
+void vector_copy(vec3 normal, out vec3 outnormal)
+{
+ outnormal = normal;
+}
+
/* Matirx Math */
mat3 euler_to_mat3(vec3 euler)
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl
new file mode 100644
index 00000000000..d717ac97b28
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl
@@ -0,0 +1,13 @@
+
+void node_point_info(out vec3 position, out float radius, out float random)
+{
+#ifdef POINTCLOUD_SHADER
+ position = pointPosition;
+ radius = pointRadius;
+ random = wang_hash_noise(uint(pointID));
+#else
+ position = vec3(0.0, 0.0, 0.0);
+ radius = 0.0;
+ random = 0.0;
+#endif
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
index bba84c2be52..c97fc090fe2 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
@@ -166,7 +166,7 @@ void node_bsdf_principled(vec4 base_color,
float btdf = (do_multiscatter != 0.0) ?
1.0 :
btdf_lut(NV, in_Refraction_3.roughness, in_Refraction_3.ior).x;
- /* TODO(fclem) This could be going to a transmission render pass instead. */
+ /* TODO(@fclem): This could be going to a transmission render pass instead. */
out_Refraction_3.radiance *= btdf;
out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance);
out_Refraction_3.radiance *= base_color.rgb;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
index 7cbc7218f5c..8a42a131f43 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
@@ -21,7 +21,7 @@ void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Cl
result.radiance = out_Refraction_0.radiance;
- /* TODO(fclem) Try to not use this. */
+ /* TODO(@fclem): Try to not use this. */
result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N,
viewCameraVec(viewPosition));
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
index d0c159cdf37..20b634aa801 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
@@ -22,7 +22,7 @@ void node_subsurface_scattering(vec4 color,
closure_load_sss_data(scale, out_Diffuse_0.radiance, color.rgb, int(sss_id), result);
- /* TODO(fclem) Try to not use this. */
+ /* TODO(@fclem): Try to not use this. */
closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
}
diff --git a/source/blender/ikplugin/BIK_api.h b/source/blender/ikplugin/BIK_api.h
index 674b384adf2..7ce0feb3989 100644
--- a/source/blender/ikplugin/BIK_api.h
+++ b/source/blender/ikplugin/BIK_api.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Benoit Bolsee
*/
/** \file
diff --git a/source/blender/ikplugin/intern/ikplugin_api.c b/source/blender/ikplugin/intern/ikplugin_api.c
index 233150a77aa..8bbab832b66 100644
--- a/source/blender/ikplugin/intern/ikplugin_api.c
+++ b/source/blender/ikplugin/intern/ikplugin_api.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Benoit Bolsee
*/
/** \file
diff --git a/source/blender/ikplugin/intern/ikplugin_api.h b/source/blender/ikplugin/intern/ikplugin_api.h
index f61ba7e3a63..9d54cbec80c 100644
--- a/source/blender/ikplugin/intern/ikplugin_api.h
+++ b/source/blender/ikplugin/intern/ikplugin_api.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Benoit Bolsee
*/
/** \file
diff --git a/source/blender/ikplugin/intern/iksolver_plugin.c b/source/blender/ikplugin/intern/iksolver_plugin.c
index 0ee8b3057d2..5daad507efe 100644
--- a/source/blender/ikplugin/intern/iksolver_plugin.c
+++ b/source/blender/ikplugin/intern/iksolver_plugin.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Benoit Bolsee
*/
/** \file
diff --git a/source/blender/ikplugin/intern/iksolver_plugin.h b/source/blender/ikplugin/intern/iksolver_plugin.h
index 28356b4fc9c..3cb6ac171d9 100644
--- a/source/blender/ikplugin/intern/iksolver_plugin.h
+++ b/source/blender/ikplugin/intern/iksolver_plugin.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Benoit Bolsee
*/
/** \file
diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp
index 4a4e22ed884..102ad46a5f2 100644
--- a/source/blender/ikplugin/intern/itasc_plugin.cpp
+++ b/source/blender/ikplugin/intern/itasc_plugin.cpp
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Benoit Bolsee
*/
/** \file
diff --git a/source/blender/ikplugin/intern/itasc_plugin.h b/source/blender/ikplugin/intern/itasc_plugin.h
index 89342295b35..cd4e210966e 100644
--- a/source/blender/ikplugin/intern/itasc_plugin.h
+++ b/source/blender/ikplugin/intern/itasc_plugin.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Original author: Benoit Bolsee
*/
/** \file
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 65d7631445d..a557d7dc6d1 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -378,8 +378,9 @@ struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim,
IMB_Timecode_Type tcs_in_use,
IMB_Proxy_Size proxy_sizes_in_use,
int quality,
- bool overwrite,
- struct GSet *file_list);
+ const bool overwrite,
+ struct GSet *file_list,
+ bool build_only_on_bad_performance);
/**
* Will rebuild all used indices and proxies at once.
@@ -431,6 +432,7 @@ bool IMB_anim_can_produce_frames(const struct anim *anim);
int ismovie(const char *filepath);
int IMB_anim_get_image_width(struct anim *anim);
int IMB_anim_get_image_height(struct anim *anim);
+bool IMB_get_gop_decode_time(struct anim *anim);
/**
*
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 6a05b681c88..38dbb9bfc47 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -504,11 +504,6 @@ static ImBuf *avi_fetchibuf(struct anim *anim, int position)
#ifdef WITH_FFMPEG
-BLI_INLINE bool need_aligned_ffmpeg_buffer(struct anim *anim)
-{
- return (anim->x & 31) != 0;
-}
-
static int startffmpeg(struct anim *anim)
{
int i, video_stream_index;
@@ -707,23 +702,20 @@ static int startffmpeg(struct anim *anim)
anim->pFrameComplete = false;
anim->pFrameDeinterlaced = av_frame_alloc();
anim->pFrameRGB = av_frame_alloc();
+ anim->pFrameRGB->format = AV_PIX_FMT_RGBA;
+ anim->pFrameRGB->width = anim->x;
+ anim->pFrameRGB->height = anim->y;
- if (need_aligned_ffmpeg_buffer(anim)) {
- anim->pFrameRGB->format = AV_PIX_FMT_RGBA;
- anim->pFrameRGB->width = anim->x;
- anim->pFrameRGB->height = anim->y;
-
- if (av_frame_get_buffer(anim->pFrameRGB, 32) < 0) {
- fprintf(stderr, "Could not allocate frame data.\n");
- avcodec_free_context(&anim->pCodecCtx);
- avformat_close_input(&anim->pFormatCtx);
- av_packet_free(&anim->cur_packet);
- av_frame_free(&anim->pFrameRGB);
- av_frame_free(&anim->pFrameDeinterlaced);
- av_frame_free(&anim->pFrame);
- anim->pCodecCtx = NULL;
- return -1;
- }
+ if (av_frame_get_buffer(anim->pFrameRGB, 0) < 0) {
+ fprintf(stderr, "Could not allocate frame data.\n");
+ avcodec_free_context(&anim->pCodecCtx);
+ avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
+ av_frame_free(&anim->pFrameRGB);
+ av_frame_free(&anim->pFrameDeinterlaced);
+ av_frame_free(&anim->pFrame);
+ anim->pCodecCtx = NULL;
+ return -1;
}
if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) {
@@ -851,92 +843,26 @@ static void ffmpeg_postprocess(struct anim *anim)
}
}
- if (!need_aligned_ffmpeg_buffer(anim)) {
- av_image_fill_arrays(anim->pFrameRGB->data,
- anim->pFrameRGB->linesize,
- (unsigned char *)ibuf->rect,
- AV_PIX_FMT_RGBA,
- anim->x,
- anim->y,
- 1);
- }
-
-# if defined(__x86_64__) || defined(_M_X64)
- /* Scale and flip image over Y axis in one go, using negative strides.
- * This doesn't work with ARM/PowerPC though and may be misusing the API.
- * Limit it x86_64 where it appears to work.
- * http://trac.ffmpeg.org/ticket/9060 */
- int *dstStride = anim->pFrameRGB->linesize;
- uint8_t **dst = anim->pFrameRGB->data;
- const int dstStride2[4] = {-dstStride[0], 0, 0, 0};
- uint8_t *dst2[4] = {dst[0] + (anim->y - 1) * dstStride[0], 0, 0, 0};
-
- sws_scale(anim->img_convert_ctx,
- (const uint8_t *const *)input->data,
- input->linesize,
- 0,
- anim->y,
- dst2,
- dstStride2);
-# else
- /* Scale with swscale. */
- int *dstStride = anim->pFrameRGB->linesize;
- uint8_t **dst = anim->pFrameRGB->data;
- const int dstStride2[4] = {dstStride[0], 0, 0, 0};
- uint8_t *dst2[4] = {dst[0], 0, 0, 0};
- int x, y, h, w;
- unsigned char *bottom;
- unsigned char *top;
-
sws_scale(anim->img_convert_ctx,
(const uint8_t *const *)input->data,
input->linesize,
0,
anim->y,
- dst2,
- dstStride2);
-
- /* Flip destination image buffer over Y axis. */
- bottom = (unsigned char *)dst[0];
- top = bottom + anim->x * (anim->y - 1) * 4;
-
- h = (anim->y + 1) / 2;
- w = anim->x;
-
- for (y = 0; y < h; y++) {
- unsigned char tmp[4];
- unsigned int *tmp_l = (unsigned int *)tmp;
-
- for (x = 0; x < w; x++) {
- tmp[0] = bottom[0];
- tmp[1] = bottom[1];
- tmp[2] = bottom[2];
- tmp[3] = bottom[3];
-
- bottom[0] = top[0];
- bottom[1] = top[1];
- bottom[2] = top[2];
- bottom[3] = top[3];
-
- *(unsigned int *)top = *tmp_l;
-
- bottom += 4;
- top += 4;
- }
- top -= 8 * w;
- }
-# endif
-
- if (need_aligned_ffmpeg_buffer(anim)) {
- uint8_t *buf_src = anim->pFrameRGB->data[0];
- uint8_t *buf_dst = (uint8_t *)ibuf->rect;
- for (int y = 0; y < anim->y; y++) {
- memcpy(buf_dst, buf_src, anim->x * 4);
- buf_dst += anim->x * 4;
- buf_src += anim->pFrameRGB->linesize[0];
- }
- }
-
+ anim->pFrameRGB->data,
+ anim->pFrameRGB->linesize);
+
+ /* Copy the valid bytes from the aligned buffer vertically flipped into ImBuf */
+ int aligned_stride = anim->pFrameRGB->linesize[0];
+ const uint8_t *const src[4] = {
+ anim->pFrameRGB->data[0] + (anim->y - 1) * aligned_stride, 0, 0, 0};
+ /* NOTE: Negative linesize is used to copy and flip image at once with function
+ * `av_image_copy_to_buffer`. This could cause issues in future and image may need to be flipped
+ * explicitly. */
+ const int src_linesize[4] = {-anim->pFrameRGB->linesize[0], 0, 0, 0};
+ int dst_size = av_image_get_buffer_size(
+ anim->pFrameRGB->format, anim->pFrameRGB->width, anim->pFrameRGB->height, 1);
+ av_image_copy_to_buffer(
+ (uint8_t *)ibuf->rect, dst_size, src, src_linesize, AV_PIX_FMT_RGBA, anim->x, anim->y, 1);
if (filter_y) {
IMB_filtery(ibuf);
}
@@ -1067,21 +993,23 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
return false;
}
-static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search)
+static double ffmpeg_steps_per_frame_get(struct anim *anim)
{
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- AVRational frame_rate = v_st->r_frame_rate;
AVRational time_base = v_st->time_base;
- double steps_per_frame = (double)(frame_rate.den * time_base.den) /
- (double)(frame_rate.num * time_base.num);
+ AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL);
+ return av_q2d(av_inv_q(av_mul_q(frame_rate, time_base)));
+ ;
+}
+
+static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search)
+{
/* Step back half a frame position to make sure that we get the requested
* frame and not the one after it. This is a workaround as ffmpeg will
* sometimes not seek to a frame after the requested pts even if
* AVSEEK_FLAG_BACKWARD is specified.
*/
- int64_t pts = pts_to_search - (steps_per_frame / 2);
-
- return pts;
+ return pts_to_search - (ffmpeg_steps_per_frame_get(anim) / 2);
}
/* This gives us an estimate of which pts our requested frame will have.
@@ -1100,13 +1028,8 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
else {
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
int64_t start_pts = v_st->start_time;
- AVRational frame_rate = v_st->r_frame_rate;
- AVRational time_base = v_st->time_base;
-
- double steps_per_frame = (double)(frame_rate.den * time_base.den) /
- (double)(frame_rate.num * time_base.num);
- pts_to_search = round(position * steps_per_frame);
+ pts_to_search = round(position * ffmpeg_steps_per_frame_get(anim));
if (start_pts != AV_NOPTS_VALUE) {
pts_to_search += start_pts;
@@ -1196,13 +1119,6 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim,
int64_t *requested_pts,
int64_t pts_to_search)
{
- AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- AVRational frame_rate = v_st->r_frame_rate;
- AVRational time_base = v_st->time_base;
-
- double steps_per_frame = (double)(frame_rate.den * time_base.den) /
- (double)(frame_rate.num * time_base.num);
-
int64_t current_pts = *requested_pts;
int64_t offset = 0;
@@ -1210,7 +1126,7 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim,
/* Step backward frame by frame until we find the key frame we are looking for. */
while (current_pts != 0) {
- current_pts = *requested_pts - (int64_t)round(offset * steps_per_frame);
+ current_pts = *requested_pts - (int64_t)round(offset * ffmpeg_steps_per_frame_get(anim));
current_pts = MAX2(current_pts, 0);
/* Seek to timestamp. */
@@ -1482,20 +1398,6 @@ static void free_anim_ffmpeg(struct anim *anim)
av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrame);
-
- if (!need_aligned_ffmpeg_buffer(anim)) {
- /* If there's no need for own aligned buffer it means that FFmpeg's
- * frame shares the same buffer as temporary ImBuf. In this case we
- * should not free the buffer when freeing the FFmpeg buffer.
- */
- av_image_fill_arrays(anim->pFrameRGB->data,
- anim->pFrameRGB->linesize,
- NULL,
- AV_PIX_FMT_RGBA,
- anim->x,
- anim->y,
- 1);
- }
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
diff --git a/source/blender/imbuf/intern/cineon/dpxlib.c b/source/blender/imbuf/intern/cineon/dpxlib.c
index 4580bfd2cbf..79374f87cc1 100644
--- a/source/blender/imbuf/intern/cineon/dpxlib.c
+++ b/source/blender/imbuf/intern/cineon/dpxlib.c
@@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Copyright 1999 - 2002 David Hodson <hodsond@acm.org>
+ * Copyright 1999-2002 David Hodson <hodsond@acm.org>
*/
/** \file
diff --git a/source/blender/imbuf/intern/cineon/dpxlib.h b/source/blender/imbuf/intern/cineon/dpxlib.h
index 1228ac4ee66..59fc6344737 100644
--- a/source/blender/imbuf/intern/cineon/dpxlib.h
+++ b/source/blender/imbuf/intern/cineon/dpxlib.h
@@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Copyright 1999 - 2002 David Hodson <hodsond@acm.org>
+ * Copyright 1999-2002 David Hodson <hodsond@acm.org>
*/
/** \file
diff --git a/source/blender/imbuf/intern/dds/FlipDXT.cpp b/source/blender/imbuf/intern/dds/FlipDXT.cpp
index 6686d56e9d1..a3cd8056290 100644
--- a/source/blender/imbuf/intern/dds/FlipDXT.cpp
+++ b/source/blender/imbuf/intern/dds/FlipDXT.cpp
@@ -31,8 +31,11 @@
* All rights reserved.
*/
-/* This file comes from the chromium project, adapted to Blender to add DDS
- * flipping to OpenGL convention for Blender */
+/** \file
+ * \ingroup imbdds
+ * This file comes from the chromium project, adapted to Blender to add DDS
+ * flipping to OpenGL convention for Blender.
+ */
#include "IMB_imbuf_types.h"
diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c
index ff86fbfdd38..37ed993f3f6 100644
--- a/source/blender/imbuf/intern/divers.c
+++ b/source/blender/imbuf/intern/divers.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * allocimbuf.c
*/
/** \file
@@ -104,7 +103,7 @@ MINLINE void float_to_byte_dither_v4(
bool IMB_alpha_affects_rgb(const ImBuf *ibuf)
{
- return (ibuf->flags & IB_alphamode_channel_packed) == 0;
+ return ibuf && (ibuf->flags & IB_alphamode_channel_packed) == 0;
}
void IMB_buffer_byte_from_float(uchar *rect_to,
diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c
index 324bc9806c1..c6f33664f8b 100644
--- a/source/blender/imbuf/intern/filter.c
+++ b/source/blender/imbuf/intern/filter.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * filter.c
*/
/** \file
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index 6cd87e29c9d..2aa61a3b5ee 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Peter Schlaile <peter [at] schlaile [dot] de> 2011
+ * Copyright 2011 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
@@ -37,6 +37,8 @@
# include "BLI_winstuff.h"
#endif
+#include "PIL_time.h"
+
#include "IMB_anim.h"
#include "IMB_indexer.h"
#include "imbuf.h"
@@ -170,7 +172,6 @@ struct anim_index *IMB_indexer_open(const char *name)
int i;
if (!fp) {
- fprintf(stderr, "Couldn't open indexer file: %s\n", name);
return NULL;
}
@@ -246,8 +247,10 @@ struct anim_index *IMB_indexer_open(const char *name)
uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index)
{
- if (frame_index < 0) {
- frame_index = 0;
+ /* This is hard coded, because our current timecode files return non zero seek position for index
+ * 0. Only when seeking to 0 it is guaranteed, that first packet will be read. */
+ if (frame_index <= 0) {
+ return 0;
}
if (frame_index >= idx->num_entries) {
frame_index = idx->num_entries - 1;
@@ -815,12 +818,16 @@ typedef struct FFmpegIndexBuilderContext {
double pts_time_base;
int frameno, frameno_gapless;
int start_pts_set;
+
+ bool build_only_on_bad_performance;
+ bool building_cancelled;
} FFmpegIndexBuilderContext;
static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
IMB_Timecode_Type tcs_in_use,
IMB_Proxy_Size proxy_sizes_in_use,
- int quality)
+ int quality,
+ bool build_only_on_bad_performance)
{
FFmpegIndexBuilderContext *context = MEM_callocN(sizeof(FFmpegIndexBuilderContext),
"FFmpeg index builder context");
@@ -832,6 +839,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
context->proxy_sizes_in_use = proxy_sizes_in_use;
context->num_proxy_sizes = IMB_PROXY_MAX_SLOT;
context->num_indexers = IMB_TC_MAX_SLOT;
+ context->build_only_on_bad_performance = build_only_on_bad_performance;
memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx));
memset(context->indexer, 0, sizeof(context->indexer));
@@ -937,15 +945,17 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int
{
int i;
+ const bool do_rollback = stop || context->building_cancelled;
+
for (i = 0; i < context->num_indexers; i++) {
if (context->tcs_in_use & tc_types[i]) {
- IMB_index_builder_finish(context->indexer[i], stop);
+ IMB_index_builder_finish(context->indexer[i], do_rollback);
}
}
for (i = 0; i < context->num_proxy_sizes; i++) {
if (context->proxy_sizes_in_use & proxy_sizes[i]) {
- free_proxy_output_ffmpeg(context->proxy_ctx[i], stop);
+ free_proxy_output_ffmpeg(context->proxy_ctx[i], do_rollback);
}
}
@@ -1096,6 +1106,111 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
return 1;
}
+/* Get number of frames, that can be decoded in specified time period. */
+static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *context,
+ const double time_period)
+{
+ AVFrame *in_frame = av_frame_alloc();
+ AVPacket *packet = av_packet_alloc();
+
+ const double start = PIL_check_seconds_timer();
+ int frames_decoded = 0;
+
+ while (av_read_frame(context->iFormatCtx, packet) >= 0) {
+ if (packet->stream_index != context->videoStream) {
+ continue;
+ }
+
+ int ret = avcodec_send_packet(context->iCodecCtx, packet);
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
+ }
+
+ if (ret < 0) {
+ fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret));
+ break;
+ }
+ frames_decoded++;
+ }
+
+ const double end = PIL_check_seconds_timer();
+
+ if (end > start + time_period) {
+ break;
+ }
+ }
+
+ avcodec_flush_buffers(context->iCodecCtx);
+ av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD);
+ return frames_decoded;
+}
+
+/* Read up to 10k movie packets and return max GOP size detected.
+ * Number of packets is arbitrary. It should be as large as possible, but processed within
+ * reasonable time period, so detected GOP size is as close to real as possible. */
+static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *context)
+{
+ AVPacket *packet = av_packet_alloc();
+
+ const int packets_max = 10000;
+ int packet_index = 0;
+ int max_gop = 0;
+ int cur_gop = 0;
+
+ while (av_read_frame(context->iFormatCtx, packet) >= 0) {
+ if (packet->stream_index != context->videoStream) {
+ continue;
+ }
+ packet_index++;
+ cur_gop++;
+
+ if (packet->flags & AV_PKT_FLAG_KEY) {
+ max_gop = max_ii(max_gop, cur_gop);
+ cur_gop = 0;
+ }
+
+ if (packet_index > packets_max) {
+ break;
+ }
+ }
+
+ av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD);
+ return max_gop;
+}
+
+/* Assess scrubbing performance of provided file. This function is not meant to be very exact.
+ * It compares number number of frames decoded in reasonable time with largest detected GOP size.
+ * Because seeking happens in single GOP, it means, that maximum seek time can be detected this
+ * way.
+ * Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or
+ * equal.
+ */
+static bool indexer_need_to_build_proxy(FFmpegIndexBuilderContext *context)
+{
+ if (!context->build_only_on_bad_performance) {
+ return true;
+ }
+
+ /* Make sure, that file is not cold read. */
+ indexer_performance_get_decode_rate(context, 0.1);
+ /* Get decode rate per 100ms. This is arbitrary, but seems to be good baseline cadence of
+ * seeking. */
+ const int decode_rate = indexer_performance_get_decode_rate(context, 0.1);
+ const int max_gop_size = indexer_performance_get_max_gop_size(context);
+
+ if (max_gop_size <= 10 || max_gop_size < decode_rate) {
+ printf("Skipping proxy building for %s: Decoding performance is already good.\n",
+ context->iFormatCtx->url);
+ context->building_cancelled = true;
+ return false;
+ }
+
+ return true;
+}
+
#endif
/* ----------------------------------------------------------------------
@@ -1275,7 +1390,8 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim,
IMB_Proxy_Size proxy_sizes_in_use,
int quality,
const bool overwrite,
- GSet *file_list)
+ GSet *file_list,
+ bool build_only_on_bad_performance)
{
IndexBuildContext *context = NULL;
IMB_Proxy_Size proxy_sizes_to_build = proxy_sizes_in_use;
@@ -1329,9 +1445,13 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim,
switch (anim->curtype) {
#ifdef WITH_FFMPEG
case ANIM_FFMPEG:
- context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality);
+ context = index_ffmpeg_create_context(
+ anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance);
break;
+#else
+ UNUSED_VARS(build_only_on_bad_performance);
#endif
+
#ifdef WITH_AVI
default:
context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality);
@@ -1359,7 +1479,9 @@ void IMB_anim_index_rebuild(struct IndexBuildContext *context,
switch (context->anim_type) {
#ifdef WITH_FFMPEG
case ANIM_FFMPEG:
- index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress);
+ if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) {
+ index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress);
+ }
break;
#endif
#ifdef WITH_AVI
diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c
index 925ef0a8502..616f4d1421c 100644
--- a/source/blender/imbuf/intern/radiance_hdr.c
+++ b/source/blender/imbuf/intern/radiance_hdr.c
@@ -12,21 +12,14 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright
- * All rights reserved.
*/
/** \file
* \ingroup imbuf
- */
-
-/* ----------------------------------------------------------------------
* Radiance High Dynamic Range image file IO
* For description and code for reading/writing of radiance hdr files
* by Greg Ward, refer to:
* http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html
- * ----------------------------------------------------------------------
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c
index c75bdfa375c..ef0d547055d 100644
--- a/source/blender/imbuf/intern/readimage.c
+++ b/source/blender/imbuf/intern/readimage.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * allocimbuf.c
*/
/** \file
diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c
index 1d81ee768e9..d368504dab0 100644
--- a/source/blender/imbuf/intern/rectop.c
+++ b/source/blender/imbuf/intern/rectop.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * allocimbuf.c
*/
/** \file
diff --git a/source/blender/imbuf/intern/rotate.c b/source/blender/imbuf/intern/rotate.c
index f02f3e37d6a..84161167ebc 100644
--- a/source/blender/imbuf/intern/rotate.c
+++ b/source/blender/imbuf/intern/rotate.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * rotate.c
*/
/** \file
diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c
index a18ba6748de..a745555eec7 100644
--- a/source/blender/imbuf/intern/scaling.c
+++ b/source/blender/imbuf/intern/scaling.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * allocimbuf.c
*/
/** \file
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index 18ed4710e78..10de80c1330 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * util.c
*/
/** \file
diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c
index cc6fef634d5..4775d5154a3 100644
--- a/source/blender/imbuf/intern/util_gpu.c
+++ b/source/blender/imbuf/intern/util_gpu.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * util.c
*/
/** \file
diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c
index f21d274f8fd..c2c87375363 100644
--- a/source/blender/imbuf/intern/writeimage.c
+++ b/source/blender/imbuf/intern/writeimage.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * writeimage.c
*/
/** \file
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index 9d61980c475..4234fb34015 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -769,6 +769,40 @@ static void read_vertex_creases(Mesh *mesh,
mesh->cd_flag |= ME_CDFLAG_VERT_CREASE;
}
+static void read_edge_creases(Mesh *mesh,
+ const Int32ArraySamplePtr &indices,
+ const FloatArraySamplePtr &sharpnesses)
+{
+ if (!(indices && sharpnesses)) {
+ return;
+ }
+
+ MEdge *edges = mesh->medge;
+ int totedge = mesh->totedge;
+
+ for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) {
+ int v1 = (*indices)[i];
+ int v2 = (*indices)[i + 1];
+
+ if (v2 < v1) {
+ /* It appears to be common to store edges with the smallest index first, in which case this
+ * prevents us from doing the second search below. */
+ std::swap(v1, v2);
+ }
+
+ MEdge *edge = find_edge(edges, totedge, v1, v2);
+ if (edge == nullptr) {
+ edge = find_edge(edges, totedge, v2, v1);
+ }
+
+ if (edge) {
+ edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]);
+ }
+ }
+
+ mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
+}
+
/* ************************************************************************** */
AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings)
@@ -841,36 +875,7 @@ void AbcSubDReader::readObjectData(Main *bmain,
return;
}
- /* Read egde creases. */
- Int32ArraySamplePtr indices = sample.getCreaseIndices();
- Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses();
-
- if (indices && sharpnesses) {
- MEdge *edges = mesh->medge;
- int totedge = mesh->totedge;
-
- for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) {
- int v1 = (*indices)[i];
- int v2 = (*indices)[i + 1];
-
- if (v2 < v1) {
- /* It appears to be common to store edges with the smallest index first, in which case this
- * prevents us from doing the second search below. */
- std::swap(v1, v2);
- }
-
- MEdge *edge = find_edge(edges, totedge, v1, v2);
- if (edge == nullptr) {
- edge = find_edge(edges, totedge, v2, v1);
- }
-
- if (edge) {
- edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]);
- }
- }
-
- mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
- }
+ read_edge_creases(mesh, sample.getCreaseIndices(), sample.getCreaseSharpnesses());
read_vertex_creases(mesh, sample.getCornerIndices(), sample.getCornerSharpnesses());
diff --git a/source/blender/io/collada/CMakeLists.txt b/source/blender/io/collada/CMakeLists.txt
index e1645083116..9ce3389257d 100644
--- a/source/blender/io/collada/CMakeLists.txt
+++ b/source/blender/io/collada/CMakeLists.txt
@@ -135,10 +135,6 @@ if(WITH_BUILDINFO)
add_definitions(-DWITH_BUILDINFO)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(CMAKE_COMPILER_IS_GNUCXX)
# COLLADAFWArray.h gives error with gcc 4.5
string(APPEND CMAKE_CXX_FLAGS " -fpermissive")
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index 8de8e7f0e2e..773518cb817 100644
--- a/source/blender/io/usd/CMakeLists.txt
+++ b/source/blender/io/usd/CMakeLists.txt
@@ -65,6 +65,7 @@ set(SRC
intern/usd_writer_camera.cc
intern/usd_writer_hair.cc
intern/usd_writer_light.cc
+ intern/usd_writer_material.cc
intern/usd_writer_mesh.cc
intern/usd_writer_metaball.cc
intern/usd_writer_transform.cc
@@ -90,6 +91,7 @@ set(SRC
intern/usd_writer_camera.h
intern/usd_writer_hair.h
intern/usd_writer_light.h
+ intern/usd_writer_material.h
intern/usd_writer_mesh.h
intern/usd_writer_metaball.h
intern/usd_writer_transform.h
diff --git a/source/blender/io/usd/intern/usd_reader_camera.cc b/source/blender/io/usd/intern/usd_reader_camera.cc
index 2732ed5770d..1d001e19140 100644
--- a/source/blender/io/usd/intern/usd_reader_camera.cc
+++ b/source/blender/io/usd/intern/usd_reader_camera.cc
@@ -72,7 +72,7 @@ void USDCameraReader::read_object_data(Main *bmain, const double motionSampleTim
cam_prim.GetHorizontalApertureAttr().Get(&horAp, motionSampleTime);
bcam->lens = val.Get<float>();
- /* TODO(makowalski) */
+ /* TODO(@makowalski): support sensor size. */
#if 0
bcam->sensor_x = 0.0f;
bcam->sensor_y = 0.0f;
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc
index 2b5326eb4c1..a358c563c88 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.cc
+++ b/source/blender/io/usd/intern/usd_writer_abstract.cc
@@ -18,11 +18,15 @@
*/
#include "usd_writer_abstract.h"
#include "usd_hierarchy_iterator.h"
+#include "usd_writer_material.h"
#include <pxr/base/tf/stringUtils.h>
+#include "BKE_customdata.h"
#include "BLI_assert.h"
+#include "DNA_mesh_types.h"
+
/* TfToken objects are not cheap to construct, so we do it once. */
namespace usdtokens {
/* Materials */
@@ -34,6 +38,19 @@ static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal);
static const pxr::TfToken surface("surface", pxr::TfToken::Immortal);
} // namespace usdtokens
+static std::string get_mesh_active_uvlayer_name(const Object *ob)
+{
+ if (!ob || ob->type != OB_MESH || !ob->data) {
+ return "";
+ }
+
+ const Mesh *me = static_cast<Mesh *>(ob->data);
+
+ const char *name = CustomData_get_active_layer_name(&me->ldata, CD_MLOOPUV);
+
+ return name ? name : "";
+}
+
namespace blender::io::usd {
USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_context)
@@ -78,7 +95,8 @@ const pxr::SdfPath &USDAbstractWriter::usd_path() const
return usd_export_context_.usd_path;
}
-pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material)
+pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(const HierarchyContext &context,
+ Material *material)
{
static pxr::SdfPath material_library_path("/_materials");
pxr::UsdStageRefPtr stage = usd_export_context_.stage;
@@ -92,17 +110,14 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material)
}
usd_material = pxr::UsdShadeMaterial::Define(stage, usd_path);
- /* Construct the shader. */
- pxr::SdfPath shader_path = usd_path.AppendChild(usdtokens::preview_shader);
- pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(stage, shader_path);
- shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface));
- shader.CreateInput(usdtokens::diffuse_color, pxr::SdfValueTypeNames->Color3f)
- .Set(pxr::GfVec3f(material->r, material->g, material->b));
- shader.CreateInput(usdtokens::roughness, pxr::SdfValueTypeNames->Float).Set(material->roughness);
- shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic);
-
- /* Connect the shader and the material together. */
- usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface);
+ if (material->use_nodes && this->usd_export_context_.export_params.generate_preview_surface) {
+ std::string active_uv = get_mesh_active_uvlayer_name(context.object);
+ create_usd_preview_surface_material(
+ this->usd_export_context_, material, usd_material, active_uv);
+ }
+ else {
+ create_usd_viewport_material(this->usd_export_context_, material, usd_material);
+ }
return usd_material;
}
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index dd81dd47c83..c67aa824263 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -69,7 +69,7 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
virtual void do_write(HierarchyContext &context) = 0;
pxr::UsdTimeCode get_export_time_code() const;
- pxr::UsdShadeMaterial ensure_usd_material(Material *material);
+ pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material);
void write_visibility(const HierarchyContext &context,
const pxr::UsdTimeCode timecode,
diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc
new file mode 100644
index 00000000000..5ce04339503
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_writer_material.cc
@@ -0,0 +1,767 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "usd_writer_material.h"
+
+#include "usd.h"
+#include "usd_exporter_context.h"
+
+#include "BKE_image.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+
+#include "BLI_fileops.h"
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "DNA_material_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+
+#include <pxr/base/tf/stringUtils.h>
+#include <pxr/pxr.h>
+#include <pxr/usd/usdGeom/scope.h>
+
+#include <iostream>
+
+/* TfToken objects are not cheap to construct, so we do it once. */
+namespace usdtokens {
+// Materials
+static const pxr::TfToken clearcoat("clearcoat", pxr::TfToken::Immortal);
+static const pxr::TfToken clearcoatRoughness("clearcoatRoughness", pxr::TfToken::Immortal);
+static const pxr::TfToken diffuse_color("diffuseColor", pxr::TfToken::Immortal);
+static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal);
+static const pxr::TfToken preview_shader("previewShader", pxr::TfToken::Immortal);
+static const pxr::TfToken preview_surface("UsdPreviewSurface", pxr::TfToken::Immortal);
+static const pxr::TfToken uv_texture("UsdUVTexture", pxr::TfToken::Immortal);
+static const pxr::TfToken primvar_float2("UsdPrimvarReader_float2", pxr::TfToken::Immortal);
+static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal);
+static const pxr::TfToken specular("specular", pxr::TfToken::Immortal);
+static const pxr::TfToken opacity("opacity", pxr::TfToken::Immortal);
+static const pxr::TfToken surface("surface", pxr::TfToken::Immortal);
+static const pxr::TfToken perspective("perspective", pxr::TfToken::Immortal);
+static const pxr::TfToken orthographic("orthographic", pxr::TfToken::Immortal);
+static const pxr::TfToken rgb("rgb", pxr::TfToken::Immortal);
+static const pxr::TfToken r("r", pxr::TfToken::Immortal);
+static const pxr::TfToken g("g", pxr::TfToken::Immortal);
+static const pxr::TfToken b("b", pxr::TfToken::Immortal);
+static const pxr::TfToken st("st", pxr::TfToken::Immortal);
+static const pxr::TfToken result("result", pxr::TfToken::Immortal);
+static const pxr::TfToken varname("varname", pxr::TfToken::Immortal);
+static const pxr::TfToken out("out", pxr::TfToken::Immortal);
+static const pxr::TfToken normal("normal", pxr::TfToken::Immortal);
+static const pxr::TfToken ior("ior", pxr::TfToken::Immortal);
+static const pxr::TfToken file("file", pxr::TfToken::Immortal);
+static const pxr::TfToken preview("preview", pxr::TfToken::Immortal);
+static const pxr::TfToken raw("raw", pxr::TfToken::Immortal);
+static const pxr::TfToken sRGB("sRGB", pxr::TfToken::Immortal);
+static const pxr::TfToken sourceColorSpace("sourceColorSpace", pxr::TfToken::Immortal);
+static const pxr::TfToken Shader("Shader", pxr::TfToken::Immortal);
+} // namespace usdtokens
+
+/* Cycles specific tokens. */
+namespace cyclestokens {
+static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal);
+} // namespace cyclestokens
+
+namespace blender::io::usd {
+
+/* Preview surface input specification. */
+struct InputSpec {
+ pxr::TfToken input_name;
+ pxr::SdfValueTypeName input_type;
+ pxr::TfToken source_name;
+ /* Whether a default value should be set
+ * if the node socket has not input. Usually
+ * false for the Normal input. */
+ bool set_default_value;
+};
+
+/* Map Blender socket names to USD Preview Surface InputSpec structs. */
+typedef std::map<std::string, InputSpec> InputSpecMap;
+
+/* Static function forward declarations. */
+static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &usd_export_context,
+ pxr::UsdShadeMaterial &material,
+ const char *name,
+ int type);
+static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &usd_export_context,
+ pxr::UsdShadeMaterial &material,
+ bNode *node);
+static void create_uvmap_shader(const USDExporterContext &usd_export_context,
+ bNode *tex_node,
+ pxr::UsdShadeMaterial &usd_material,
+ pxr::UsdShadeShader &usd_tex_shader,
+ const pxr::TfToken &default_uv);
+static void export_texture(bNode *node,
+ const pxr::UsdStageRefPtr stage,
+ const bool allow_overwrite = false);
+static bNode *find_bsdf_node(Material *material);
+static void get_absolute_path(Image *ima, char *r_path);
+static std::string get_tex_image_asset_path(bNode *node,
+ const pxr::UsdStageRefPtr stage,
+ const USDExportParams &export_params);
+static InputSpecMap &preview_surface_input_map();
+static bNode *traverse_channel(bNodeSocket *input, short target_type);
+
+template<typename T1, typename T2>
+void create_input(pxr::UsdShadeShader &shader, const InputSpec &spec, const void *value);
+
+void create_usd_preview_surface_material(const USDExporterContext &usd_export_context,
+ Material *material,
+ pxr::UsdShadeMaterial &usd_material,
+ const std::string &default_uv)
+{
+ if (!material) {
+ return;
+ }
+
+ /* Define a 'preview' scope beneath the material which will contain the preview shaders. */
+ pxr::UsdGeomScope::Define(usd_export_context.stage,
+ usd_material.GetPath().AppendChild(usdtokens::preview));
+
+ /* Default map when creating UV primvar reader shaders. */
+ pxr::TfToken default_uv_sampler = default_uv.empty() ? cyclestokens::UVMap :
+ pxr::TfToken(default_uv);
+
+ /* We only handle the first instance of either principled or
+ * diffuse bsdf nodes in the material's node tree, because
+ * USD Preview Surface has no concept of layering materials. */
+ bNode *node = find_bsdf_node(material);
+ if (!node) {
+ return;
+ }
+
+ pxr::UsdShadeShader preview_surface = create_usd_preview_shader(
+ usd_export_context, usd_material, node);
+
+ const InputSpecMap &input_map = preview_surface_input_map();
+
+ /* Set the preview surface inputs. */
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
+
+ /* Check if this socket is mapped to a USD preview shader input. */
+ const InputSpecMap::const_iterator it = input_map.find(sock->name);
+
+ if (it == input_map.end()) {
+ continue;
+ }
+
+ pxr::UsdShadeShader created_shader;
+
+ bNode *input_node = traverse_channel(sock, SH_NODE_TEX_IMAGE);
+
+ const InputSpec &input_spec = it->second;
+
+ if (input_node) {
+ /* Create connection. */
+ created_shader = create_usd_preview_shader(usd_export_context, usd_material, input_node);
+
+ preview_surface.CreateInput(input_spec.input_name, input_spec.input_type)
+ .ConnectToSource(created_shader, input_spec.source_name);
+ }
+ else if (input_spec.set_default_value) {
+ /* Set hardcoded value. */
+ switch (sock->type) {
+ case SOCK_FLOAT: {
+ create_input<bNodeSocketValueFloat, float>(
+ preview_surface, input_spec, sock->default_value);
+ } break;
+ case SOCK_VECTOR: {
+ create_input<bNodeSocketValueVector, pxr::GfVec3f>(
+ preview_surface, input_spec, sock->default_value);
+ } break;
+ case SOCK_RGBA: {
+ create_input<bNodeSocketValueRGBA, pxr::GfVec3f>(
+ preview_surface, input_spec, sock->default_value);
+ } break;
+ default:
+ break;
+ }
+ }
+
+ /* If any input texture node has been found, export the texture, if necessary,
+ * and look for a connected uv node. */
+ if (!(created_shader && input_node && input_node->type == SH_NODE_TEX_IMAGE)) {
+ continue;
+ }
+
+ if (usd_export_context.export_params.export_textures) {
+ export_texture(input_node,
+ usd_export_context.stage,
+ usd_export_context.export_params.overwrite_textures);
+ }
+
+ create_uvmap_shader(
+ usd_export_context, input_node, usd_material, created_shader, default_uv_sampler);
+ }
+}
+
+void create_usd_viewport_material(const USDExporterContext &usd_export_context,
+ Material *material,
+ pxr::UsdShadeMaterial &usd_material)
+{
+ /* Construct the shader. */
+ pxr::SdfPath shader_path = usd_material.GetPath().AppendChild(usdtokens::preview_shader);
+ pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(usd_export_context.stage, shader_path);
+
+ shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface));
+ shader.CreateInput(usdtokens::diffuse_color, pxr::SdfValueTypeNames->Color3f)
+ .Set(pxr::GfVec3f(material->r, material->g, material->b));
+ shader.CreateInput(usdtokens::roughness, pxr::SdfValueTypeNames->Float).Set(material->roughness);
+ shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic);
+
+ /* Connect the shader and the material together. */
+ usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface);
+}
+
+/* Return USD Preview Surface input map singleton. */
+static InputSpecMap &preview_surface_input_map()
+{
+ static InputSpecMap input_map = {
+ {"Base Color",
+ {usdtokens::diffuse_color, pxr::SdfValueTypeNames->Float3, usdtokens::rgb, true}},
+ {"Color", {usdtokens::diffuse_color, pxr::SdfValueTypeNames->Float3, usdtokens::rgb, true}},
+ {"Roughness", {usdtokens::roughness, pxr::SdfValueTypeNames->Float, usdtokens::r, true}},
+ {"Metallic", {usdtokens::metallic, pxr::SdfValueTypeNames->Float, usdtokens::r, true}},
+ {"Specular", {usdtokens::specular, pxr::SdfValueTypeNames->Float, usdtokens::r, true}},
+ {"Alpha", {usdtokens::opacity, pxr::SdfValueTypeNames->Float, usdtokens::r, true}},
+ {"IOR", {usdtokens::ior, pxr::SdfValueTypeNames->Float, usdtokens::r, true}},
+ /* Note that for the Normal input set_default_value is false. */
+ {"Normal", {usdtokens::normal, pxr::SdfValueTypeNames->Float3, usdtokens::rgb, false}},
+ {"Clearcoat", {usdtokens::clearcoat, pxr::SdfValueTypeNames->Float, usdtokens::r, true}},
+ {"Clearcoat Roughness",
+ {usdtokens::clearcoatRoughness, pxr::SdfValueTypeNames->Float, usdtokens::r, true}},
+ };
+
+ return input_map;
+}
+
+/* Create an input on the given shader with name and type
+ * provided by the InputSpec and assign the given value to the
+ * input. Parameters T1 and T2 indicate the Blender and USD
+ * value types, respectively. */
+template<typename T1, typename T2>
+void create_input(pxr::UsdShadeShader &shader, const InputSpec &spec, const void *value)
+{
+ const T1 *cast_value = static_cast<const T1 *>(value);
+ shader.CreateInput(spec.input_name, spec.input_type).Set(T2(cast_value->value));
+}
+
+/* Find the UVMAP node input to the given texture image node and convert it
+ * to a USD primvar reader shader. If no UVMAP node is found, create a primvar
+ * reader for the given default uv set. The primvar reader will be attached to
+ * the 'st' input of the given USD texture shader. */
+static void create_uvmap_shader(const USDExporterContext &usd_export_context,
+ bNode *tex_node,
+ pxr::UsdShadeMaterial &usd_material,
+ pxr::UsdShadeShader &usd_tex_shader,
+ const pxr::TfToken &default_uv)
+{
+ bool found_uv_node = false;
+
+ /* Find UV input to the texture node. */
+ LISTBASE_FOREACH (bNodeSocket *, tex_node_sock, &tex_node->inputs) {
+
+ if (!tex_node_sock->link || !STREQ(tex_node_sock->name, "Vector")) {
+ continue;
+ }
+
+ bNode *uv_node = traverse_channel(tex_node_sock, SH_NODE_UVMAP);
+ if (uv_node == nullptr) {
+ continue;
+ }
+
+ pxr::UsdShadeShader uv_shader = create_usd_preview_shader(
+ usd_export_context, usd_material, uv_node);
+
+ if (!uv_shader.GetPrim().IsValid()) {
+ continue;
+ }
+
+ found_uv_node = true;
+
+ if (NodeShaderUVMap *shader_uv_map = static_cast<NodeShaderUVMap *>(uv_node->storage)) {
+ /* We need to make valid here because actual uv primvar has been. */
+ std::string uv_set = pxr::TfMakeValidIdentifier(shader_uv_map->uv_map);
+
+ uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token)
+ .Set(pxr::TfToken(uv_set));
+ usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
+ .ConnectToSource(uv_shader, usdtokens::result);
+ }
+ else {
+ uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token).Set(default_uv);
+ usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
+ .ConnectToSource(uv_shader, usdtokens::result);
+ }
+ }
+
+ if (!found_uv_node) {
+ /* No UVMAP node was linked to the texture node. However, we generate
+ * a primvar reader node that specifies the UV set to sample, as some
+ * DCCs require this. */
+
+ pxr::UsdShadeShader uv_shader = create_usd_preview_shader(
+ usd_export_context, usd_material, "uvmap", SH_NODE_TEX_COORD);
+
+ if (uv_shader.GetPrim().IsValid()) {
+ uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token).Set(default_uv);
+ usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
+ .ConnectToSource(uv_shader, usdtokens::result);
+ }
+ }
+}
+
+/* Generate a file name for an in-memory image that doesn't have a
+ * filepath already defined. */
+static std::string get_in_memory_texture_filename(Image *ima)
+{
+ bool is_dirty = BKE_image_is_dirty(ima);
+ bool is_generated = ima->source == IMA_SRC_GENERATED;
+ bool is_packed = BKE_image_has_packedfile(ima);
+ if (!(is_generated || is_dirty || is_packed)) {
+ return "";
+ }
+
+ /* Determine the correct file extension from the image format. */
+ ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr);
+ if (!imbuf) {
+ return "";
+ }
+
+ ImageFormatData imageFormat;
+ BKE_imbuf_to_image_format(&imageFormat, imbuf);
+
+ char file_name[FILE_MAX];
+ /* Use the image name for the file name. */
+ strcpy(file_name, ima->id.name + 2);
+
+ BKE_image_path_ensure_ext_from_imformat(file_name, &imageFormat);
+
+ return file_name;
+}
+
+static void export_in_memory_texture(Image *ima,
+ const std::string &export_dir,
+ const bool allow_overwrite)
+{
+ char image_abs_path[FILE_MAX];
+
+ char file_name[FILE_MAX];
+ if (strlen(ima->filepath) > 0) {
+ get_absolute_path(ima, image_abs_path);
+ BLI_split_file_part(image_abs_path, file_name, FILE_MAX);
+ }
+ else {
+ /* Use the image name for the file name. */
+ strcpy(file_name, ima->id.name + 2);
+ }
+
+ ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr);
+ if (!imbuf) {
+ return;
+ }
+
+ ImageFormatData imageFormat;
+ BKE_imbuf_to_image_format(&imageFormat, imbuf);
+
+ /* This image in its current state only exists in Blender memory.
+ * So we have to export it. The export will keep the image state intact,
+ * so the exported file will not be associated with the image. */
+
+ BKE_image_path_ensure_ext_from_imformat(file_name, &imageFormat);
+
+ char export_path[FILE_MAX];
+ BLI_path_join(export_path, FILE_MAX, export_dir.c_str(), file_name, nullptr);
+
+ if (!allow_overwrite && BLI_exists(export_path)) {
+ return;
+ }
+
+ if ((BLI_path_cmp_normalized(export_path, image_abs_path) == 0) && BLI_exists(image_abs_path)) {
+ /* As a precaution, don't overwrite the original path. */
+ return;
+ }
+
+ std::cout << "Exporting in-memory texture to " << export_path << std::endl;
+
+ if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) {
+ WM_reportf(RPT_WARNING, "USD export: couldn't export in-memory texture to %s", export_path);
+ }
+}
+
+/* Get the absolute filepath of the given image. Assumes
+ * r_path result array is of length FILE_MAX. */
+static void get_absolute_path(Image *ima, char *r_path)
+{
+ /* Make absolute source path. */
+ BLI_strncpy(r_path, ima->filepath, FILE_MAX);
+ BLI_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
+ BLI_path_normalize(nullptr, r_path);
+}
+
+static pxr::TfToken get_node_tex_image_color_space(bNode *node)
+{
+ if (!node->id) {
+ return pxr::TfToken();
+ }
+
+ Image *ima = reinterpret_cast<Image *>(node->id);
+
+ if (strcmp(ima->colorspace_settings.name, "Raw") == 0) {
+ return usdtokens::raw;
+ }
+ if (strcmp(ima->colorspace_settings.name, "Non-Color") == 0) {
+ return usdtokens::raw;
+ }
+ if (strcmp(ima->colorspace_settings.name, "sRGB") == 0) {
+ return usdtokens::sRGB;
+ }
+
+ return pxr::TfToken();
+}
+
+/* Search the upstream nodes connected to the given socket and return the first occurrence
+ * of the node of the given type. Return null if no node of this type was found. */
+static bNode *traverse_channel(bNodeSocket *input, const short target_type)
+{
+ if (!input->link) {
+ return nullptr;
+ }
+
+ bNode *linked_node = input->link->fromnode;
+ if (linked_node->type == target_type) {
+ /* Return match. */
+ return linked_node;
+ }
+
+ /* Recursively traverse the linked node's sockets. */
+ LISTBASE_FOREACH (bNodeSocket *, sock, &linked_node->inputs) {
+ if (bNode *found_node = traverse_channel(sock, target_type)) {
+ return found_node;
+ }
+ }
+
+ return nullptr;
+}
+
+/* Returns the first occurrence of a principled BSDF or a diffuse BSDF node found in the given
+ * material's node tree. Returns null if no instance of either type was found.*/
+static bNode *find_bsdf_node(Material *material)
+{
+ LISTBASE_FOREACH (bNode *, node, &material->nodetree->nodes) {
+ if (node->type == SH_NODE_BSDF_PRINCIPLED || node->type == SH_NODE_BSDF_DIFFUSE) {
+ return node;
+ }
+ }
+
+ return nullptr;
+}
+
+/* Creates a USD Preview Surface shader based on the given cycles node name and type. */
+static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &usd_export_context,
+ pxr::UsdShadeMaterial &material,
+ const char *name,
+ const int type)
+{
+ pxr::SdfPath shader_path = material.GetPath()
+ .AppendChild(usdtokens::preview)
+ .AppendChild(pxr::TfToken(pxr::TfMakeValidIdentifier(name)));
+ pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(usd_export_context.stage, shader_path);
+
+ switch (type) {
+ case SH_NODE_TEX_IMAGE: {
+ shader.CreateIdAttr(pxr::VtValue(usdtokens::uv_texture));
+ break;
+ }
+ case SH_NODE_TEX_COORD:
+ case SH_NODE_UVMAP: {
+ shader.CreateIdAttr(pxr::VtValue(usdtokens::primvar_float2));
+ break;
+ }
+ case SH_NODE_BSDF_DIFFUSE:
+ case SH_NODE_BSDF_PRINCIPLED: {
+ shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface));
+ material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return shader;
+}
+
+/* Creates a USD Preview Surface shader based on the given cycles shading node. */
+static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &usd_export_context,
+ pxr::UsdShadeMaterial &material,
+ bNode *node)
+{
+ pxr::UsdShadeShader shader = create_usd_preview_shader(
+ usd_export_context, material, node->name, node->type);
+
+ if (node->type != SH_NODE_TEX_IMAGE) {
+ return shader;
+ }
+
+ /* For texture image nodes we set the image path and color space. */
+ std::string imagePath = get_tex_image_asset_path(
+ node, usd_export_context.stage, usd_export_context.export_params);
+ if (!imagePath.empty()) {
+ shader.CreateInput(usdtokens::file, pxr::SdfValueTypeNames->Asset)
+ .Set(pxr::SdfAssetPath(imagePath));
+ }
+
+ pxr::TfToken colorSpace = get_node_tex_image_color_space(node);
+ if (!colorSpace.IsEmpty()) {
+ shader.CreateInput(usdtokens::sourceColorSpace, pxr::SdfValueTypeNames->Token).Set(colorSpace);
+ }
+
+ return shader;
+}
+
+static std::string get_tex_image_asset_path(Image *ima)
+{
+ char filepath[FILE_MAX];
+ get_absolute_path(ima, filepath);
+
+ return std::string(filepath);
+}
+
+/* Gets an asset path for the given texture image node. The resulting path
+ * may be absolute, relative to the USD file, or in a 'textures' directory
+ * in the same directory as the USD file, depending on the export parameters.
+ * The filename is typically the image filepath but might also be automatically
+ * generated based on the image name for in-memory textures when exporting textures.
+ * This function may return an empty string if the image does not have a filepath
+ * assigned and no asset path could be determined. */
+static std::string get_tex_image_asset_path(bNode *node,
+ const pxr::UsdStageRefPtr stage,
+ const USDExportParams &export_params)
+{
+ Image *ima = reinterpret_cast<Image *>(node->id);
+ if (!ima) {
+ return "";
+ }
+
+ std::string path;
+
+ if (strlen(ima->filepath) > 0) {
+ /* Get absolute path. */
+ path = get_tex_image_asset_path(ima);
+ }
+ else if (export_params.export_textures) {
+ /* Image has no filepath, but since we are exporting textures,
+ * check if this is an in-memory texture for which we can
+ * generate a file name. */
+ path = get_in_memory_texture_filename(ima);
+ }
+
+ if (path.empty()) {
+ return path;
+ }
+
+ if (export_params.export_textures) {
+ /* The texture is exported to a 'textures' directory next to the
+ * USD root layer. */
+
+ char exp_path[FILE_MAX];
+ char file_path[FILE_MAX];
+ BLI_split_file_part(path.c_str(), file_path, FILE_MAX);
+
+ if (export_params.relative_texture_paths) {
+ BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path, nullptr);
+ }
+ else {
+ /* Create absolute path in the textures directory. */
+ pxr::SdfLayerHandle layer = stage->GetRootLayer();
+ std::string stage_path = layer->GetRealPath();
+ if (stage_path.empty()) {
+ return path;
+ }
+
+ char dir_path[FILE_MAX];
+ BLI_split_dir_part(stage_path.c_str(), dir_path, FILE_MAX);
+ BLI_path_join(exp_path, FILE_MAX, dir_path, "textures", file_path, nullptr);
+ }
+ return exp_path;
+ }
+
+ if (export_params.relative_texture_paths) {
+ /* Get the path relative to the USD. */
+ pxr::SdfLayerHandle layer = stage->GetRootLayer();
+ std::string stage_path = layer->GetRealPath();
+ if (stage_path.empty()) {
+ return path;
+ }
+
+ char rel_path[FILE_MAX];
+ strcpy(rel_path, path.c_str());
+
+ BLI_path_rel(rel_path, stage_path.c_str());
+
+ /* BLI_path_rel adds '//' as a prefix to the path, if
+ * generating the relative path was successful. */
+ if (rel_path[0] != '/' || rel_path[1] != '/') {
+ /* No relative path generated. */
+ return path;
+ }
+
+ return rel_path + 2;
+ }
+
+ return path;
+}
+
+/* If the given image is tiled, copy the image tiles to the given
+ * destination directory. */
+static void copy_tiled_textures(Image *ima,
+ const std::string &dest_dir,
+ const bool allow_overwrite)
+{
+ char src_path[FILE_MAX];
+ get_absolute_path(ima, src_path);
+
+ eUDIM_TILE_FORMAT tile_format;
+ char *udim_pattern = BKE_image_get_tile_strformat(src_path, &tile_format);
+
+ /* Only <UDIM> tile formats are supported by USD right now. */
+ if (tile_format != UDIM_TILE_FORMAT_UDIM) {
+ std::cout << "WARNING: unsupported tile format for `" << src_path << "`" << std::endl;
+ MEM_SAFE_FREE(udim_pattern);
+ return;
+ }
+
+ /* Copy all tiles. */
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ char src_tile_path[FILE_MAX];
+ BKE_image_set_filepath_from_tile_number(
+ src_tile_path, udim_pattern, tile_format, tile->tile_number);
+
+ char dest_filename[FILE_MAXFILE];
+ BLI_split_file_part(src_tile_path, dest_filename, sizeof(dest_filename));
+
+ char dest_tile_path[FILE_MAX];
+ BLI_path_join(dest_tile_path, FILE_MAX, dest_dir.c_str(), dest_filename, nullptr);
+
+ if (!allow_overwrite && BLI_exists(dest_tile_path)) {
+ continue;
+ }
+
+ if (BLI_path_cmp_normalized(src_tile_path, dest_tile_path) == 0) {
+ /* Source and destination paths are the same, don't copy. */
+ continue;
+ }
+
+ std::cout << "Copying texture tile from " << src_tile_path << " to " << dest_tile_path
+ << std::endl;
+
+ /* Copy the file. */
+ if (BLI_copy(src_tile_path, dest_tile_path) != 0) {
+ WM_reportf(RPT_WARNING,
+ "USD export: couldn't copy texture tile from %s to %s",
+ src_tile_path,
+ dest_tile_path);
+ }
+ }
+ MEM_SAFE_FREE(udim_pattern);
+}
+
+/* Copy the given image to the destination directory. */
+static void copy_single_file(Image *ima, const std::string &dest_dir, const bool allow_overwrite)
+{
+ char source_path[FILE_MAX];
+ get_absolute_path(ima, source_path);
+
+ char file_name[FILE_MAX];
+ BLI_split_file_part(source_path, file_name, FILE_MAX);
+
+ char dest_path[FILE_MAX];
+ BLI_path_join(dest_path, FILE_MAX, dest_dir.c_str(), file_name, nullptr);
+
+ if (!allow_overwrite && BLI_exists(dest_path)) {
+ return;
+ }
+
+ if (BLI_path_cmp_normalized(source_path, dest_path) == 0) {
+ /* Source and destination paths are the same, don't copy. */
+ return;
+ }
+
+ std::cout << "Copying texture from " << source_path << " to " << dest_path << std::endl;
+
+ /* Copy the file. */
+ if (BLI_copy(source_path, dest_path) != 0) {
+ WM_reportf(
+ RPT_WARNING, "USD export: couldn't copy texture from %s to %s", source_path, dest_path);
+ }
+}
+
+/* Export the given texture node's image to a 'textures' directory
+ * next to given stage's root layer USD.
+ * Based on ImagesExporter::export_UV_Image() */
+static void export_texture(bNode *node,
+ const pxr::UsdStageRefPtr stage,
+ const bool allow_overwrite)
+{
+ if (node->type != SH_NODE_TEX_IMAGE && node->type != SH_NODE_TEX_ENVIRONMENT) {
+ return;
+ }
+
+ Image *ima = reinterpret_cast<Image *>(node->id);
+ if (!ima) {
+ return;
+ }
+
+ pxr::SdfLayerHandle layer = stage->GetRootLayer();
+ std::string stage_path = layer->GetRealPath();
+ if (stage_path.empty()) {
+ return;
+ }
+
+ char usd_dir_path[FILE_MAX];
+ BLI_split_dir_part(stage_path.c_str(), usd_dir_path, FILE_MAX);
+
+ char tex_dir_path[FILE_MAX];
+ BLI_path_join(tex_dir_path, FILE_MAX, usd_dir_path, "textures", SEP_STR, nullptr);
+
+ BLI_dir_create_recursive(tex_dir_path);
+
+ const bool is_dirty = BKE_image_is_dirty(ima);
+ const bool is_generated = ima->source == IMA_SRC_GENERATED;
+ const bool is_packed = BKE_image_has_packedfile(ima);
+
+ std::string dest_dir(tex_dir_path);
+
+ if (is_generated || is_dirty || is_packed) {
+ export_in_memory_texture(ima, dest_dir, allow_overwrite);
+ }
+ else if (ima->source == IMA_SRC_TILED) {
+ copy_tiled_textures(ima, dest_dir, allow_overwrite);
+ }
+ else {
+ copy_single_file(ima, dest_dir, allow_overwrite);
+ }
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_writer_material.h b/source/blender/io/usd/intern/usd_writer_material.h
new file mode 100644
index 00000000000..435ac41c4bf
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_writer_material.h
@@ -0,0 +1,57 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#pragma once
+
+#include <pxr/pxr.h>
+#include <pxr/usd/usd/prim.h>
+#include <pxr/usd/usd/stage.h>
+#include <pxr/usd/usdShade/material.h>
+
+#include <string>
+
+struct bNode;
+struct bNodeTree;
+struct Material;
+struct USDExportParams;
+
+namespace blender::io::usd {
+
+struct USDExporterContext;
+
+/**
+ * Entry point to create an approximate USD Preview Surface network from a Cycles node graph.
+ * Due to the limited nodes in the USD Preview Surface specification, only the following nodes
+ * are supported:
+ * - UVMap
+ * - Texture Coordinate
+ * - Image Texture
+ * - Principled BSDF
+ * More may be added in the future.
+ *
+ * \param default_uv: used as the default UV set name sampled by the `primvar`
+ * reader shaders generated for image texture nodes that don't have an attached UVMap node.
+ */
+void create_usd_preview_surface_material(const USDExporterContext &usd_export_context,
+ Material *material,
+ pxr::UsdShadeMaterial &usd_material,
+ const std::string &default_uv = "");
+
+/* Entry point to create USD Shade Material network from Blender viewport display settings. */
+void create_usd_viewport_material(const USDExporterContext &usd_export_context,
+ Material *material,
+ pxr::UsdShadeMaterial &usd_material);
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc
index b061a2ff795..1d3a5f5280d 100644
--- a/source/blender/io/usd/intern/usd_writer_mesh.cc
+++ b/source/blender/io/usd/intern/usd_writer_mesh.cc
@@ -361,7 +361,7 @@ void USDGenericMeshWriter::assign_materials(const HierarchyContext &context,
continue;
}
- pxr::UsdShadeMaterial usd_material = ensure_usd_material(material);
+ pxr::UsdShadeMaterial usd_material = ensure_usd_material(context, material);
material_binding_api.Bind(usd_material);
/* USD seems to support neither per-material nor per-face-group double-sidedness, so we just
@@ -395,7 +395,7 @@ void USDGenericMeshWriter::assign_materials(const HierarchyContext &context,
continue;
}
- pxr::UsdShadeMaterial usd_material = ensure_usd_material(material);
+ pxr::UsdShadeMaterial usd_material = ensure_usd_material(context, material);
pxr::TfToken material_name = usd_material.GetPath().GetNameToken();
pxr::UsdGeomSubset usd_face_subset = material_binding_api.CreateMaterialBindSubset(
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index 853a29ef998..c9267d78572 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -43,6 +43,10 @@ struct USDExportParams {
bool visible_objects_only;
bool use_instancing;
enum eEvaluationMode evaluation_mode;
+ bool generate_preview_surface;
+ bool export_textures;
+ bool overwrite_textures;
+ bool relative_texture_paths;
};
struct USDImportParams {
diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt
index 296dd70b5a2..0b1be7946cf 100644
--- a/source/blender/io/wavefront_obj/CMakeLists.txt
+++ b/source/blender/io/wavefront_obj/CMakeLists.txt
@@ -56,6 +56,12 @@ set(LIB
bf_blenkernel
)
+if(WITH_TBB)
+ add_definitions(-DWITH_TBB)
+ list(APPEND INC_SYS ${TBB_INCLUDE_DIRS})
+ list(APPEND LIB ${TBB_LIBRARIES})
+endif()
+
blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_GTESTS)
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
index 8c845c34db2..87f87e37a7e 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
@@ -24,6 +24,7 @@
#include "BKE_blender_version.h"
#include "BLI_path_util.h"
+#include "BLI_task.hh"
#include "obj_export_mesh.hh"
#include "obj_export_mtl.hh"
@@ -52,68 +53,75 @@ const char *DEFORM_GROUP_DISABLED = "off";
* So an empty material name is written. */
const char *MATERIAL_GROUP_DISABLED = "";
-void OBJWriter::write_vert_uv_normal_indices(Span<int> vert_indices,
+void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> uv_indices,
Span<int> normal_indices) const
{
BLI_assert(vert_indices.size() == uv_indices.size() &&
vert_indices.size() == normal_indices.size());
- file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write<eOBJSyntaxElement::poly_element_begin>();
for (int j = 0; j < vert_indices.size(); j++) {
- file_handler_->write<eOBJSyntaxElement::vertex_uv_normal_indices>(
- vert_indices[j] + index_offsets_.vertex_offset + 1,
- uv_indices[j] + index_offsets_.uv_vertex_offset + 1,
- normal_indices[j] + index_offsets_.normal_offset + 1);
+ fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(
+ vert_indices[j] + offsets.vertex_offset + 1,
+ uv_indices[j] + offsets.uv_vertex_offset + 1,
+ normal_indices[j] + offsets.normal_offset + 1);
}
- file_handler_->write<eOBJSyntaxElement::poly_element_end>();
+ fh.write<eOBJSyntaxElement::poly_element_end>();
}
-void OBJWriter::write_vert_normal_indices(Span<int> vert_indices,
+void OBJWriter::write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> /*uv_indices*/,
Span<int> normal_indices) const
{
BLI_assert(vert_indices.size() == normal_indices.size());
- file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write<eOBJSyntaxElement::poly_element_begin>();
for (int j = 0; j < vert_indices.size(); j++) {
- file_handler_->write<eOBJSyntaxElement::vertex_normal_indices>(
- vert_indices[j] + index_offsets_.vertex_offset + 1,
- normal_indices[j] + index_offsets_.normal_offset + 1);
+ fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + offsets.vertex_offset + 1,
+ normal_indices[j] + offsets.normal_offset +
+ 1);
}
- file_handler_->write<eOBJSyntaxElement::poly_element_end>();
+ fh.write<eOBJSyntaxElement::poly_element_end>();
}
-void OBJWriter::write_vert_uv_indices(Span<int> vert_indices,
+void OBJWriter::write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> uv_indices,
Span<int> /*normal_indices*/) const
{
BLI_assert(vert_indices.size() == uv_indices.size());
- file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write<eOBJSyntaxElement::poly_element_begin>();
for (int j = 0; j < vert_indices.size(); j++) {
- file_handler_->write<eOBJSyntaxElement::vertex_uv_indices>(
- vert_indices[j] + index_offsets_.vertex_offset + 1,
- uv_indices[j] + index_offsets_.uv_vertex_offset + 1);
+ fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + offsets.vertex_offset + 1,
+ uv_indices[j] + offsets.uv_vertex_offset + 1);
}
- file_handler_->write<eOBJSyntaxElement::poly_element_end>();
+ fh.write<eOBJSyntaxElement::poly_element_end>();
}
-void OBJWriter::write_vert_indices(Span<int> vert_indices,
+void OBJWriter::write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> /*uv_indices*/,
Span<int> /*normal_indices*/) const
{
- file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write<eOBJSyntaxElement::poly_element_begin>();
for (const int vert_index : vert_indices) {
- file_handler_->write<eOBJSyntaxElement::vertex_indices>(vert_index +
- index_offsets_.vertex_offset + 1);
+ fh.write<eOBJSyntaxElement::vertex_indices>(vert_index + offsets.vertex_offset + 1);
}
- file_handler_->write<eOBJSyntaxElement::poly_element_end>();
+ fh.write<eOBJSyntaxElement::poly_element_end>();
}
void OBJWriter::write_header() const
{
using namespace std::string_literals;
- file_handler_->write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
- "\n");
- file_handler_->write<eOBJSyntaxElement::string>("# www.blender.org\n");
+ FormatHandler<eFileType::OBJ> fh;
+ fh.write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() + "\n");
+ fh.write<eOBJSyntaxElement::string>("# www.blender.org\n");
+ fh.write_to_file(outfile_);
}
void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
@@ -122,10 +130,13 @@ void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
char mtl_file_name[FILE_MAXFILE];
char mtl_dir_name[FILE_MAXDIR];
BLI_split_dirfile(mtl_filepath.data(), mtl_dir_name, mtl_file_name, FILE_MAXDIR, FILE_MAXFILE);
- file_handler_->write<eOBJSyntaxElement::mtllib>(mtl_file_name);
+ FormatHandler<eFileType::OBJ> fh;
+ fh.write<eOBJSyntaxElement::mtllib>(mtl_file_name);
+ fh.write_to_file(outfile_);
}
-void OBJWriter::write_object_group(const OBJMesh &obj_mesh_data) const
+void OBJWriter::write_object_group(FormatHandler<eFileType::OBJ> &fh,
+ const OBJMesh &obj_mesh_data) const
{
/* "o object_name" is not mandatory. A valid .OBJ file may contain neither
* "o name" nor "g group_name". */
@@ -138,125 +149,104 @@ void OBJWriter::write_object_group(const OBJMesh &obj_mesh_data) const
const char *object_material_name = obj_mesh_data.get_object_material_name(0);
if (export_params_.export_materials && export_params_.export_material_groups &&
object_material_name) {
- file_handler_->write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name +
- "_" + object_material_name);
- return;
+ fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name + "_" +
+ object_material_name);
+ }
+ else {
+ fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name);
}
- file_handler_->write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name);
}
-void OBJWriter::write_object_name(const OBJMesh &obj_mesh_data) const
+void OBJWriter::write_object_name(FormatHandler<eFileType::OBJ> &fh,
+ const OBJMesh &obj_mesh_data) const
{
const char *object_name = obj_mesh_data.get_object_name();
if (export_params_.export_object_groups) {
- write_object_group(obj_mesh_data);
+ write_object_group(fh, obj_mesh_data);
return;
}
- file_handler_->write<eOBJSyntaxElement::object_name>(object_name);
+ fh.write<eOBJSyntaxElement::object_name>(object_name);
}
-void OBJWriter::write_vertex_coords(const OBJMesh &obj_mesh_data) const
+/* Split up large meshes into multi-threaded jobs; each job processes
+ * this amount of items. */
+static const int chunk_size = 32768;
+static int calc_chunk_count(int count)
{
- const int tot_vertices = obj_mesh_data.tot_vertices();
- for (int i = 0; i < tot_vertices; i++) {
- float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
- file_handler_->write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]);
- }
+ return (count + chunk_size - 1) / chunk_size;
}
-void OBJWriter::write_uv_coords(OBJMesh &r_obj_mesh_data) const
+/* Write /tot_count/ items to OBJ file output. Each item is written
+ * by a /function/ that should be independent from other items.
+ * If the amount of items is large enough (> chunk_size), then writing
+ * will be done in parallel, into temporary FormatHandler buffers that
+ * will be written into the final /fh/ buffer at the end.
+ */
+template<typename Function>
+void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh,
+ int tot_count,
+ const Function &function)
{
- Vector<std::array<float, 2>> uv_coords;
- /* UV indices are calculated and stored in an OBJMesh member here. */
- r_obj_mesh_data.store_uv_coords_and_indices(uv_coords);
-
- for (const std::array<float, 2> &uv_vertex : uv_coords) {
- file_handler_->write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]);
+ if (tot_count <= 0) {
+ return;
}
-}
-
-void OBJWriter::write_poly_normals(OBJMesh &obj_mesh_data)
-{
- obj_mesh_data.ensure_mesh_normals();
- Vector<float3> normals;
- obj_mesh_data.store_normal_coords_and_indices(normals);
- for (const float3 &normal : normals) {
- file_handler_->write<eOBJSyntaxElement::normal>(normal[0], normal[1], normal[2]);
+ /* If we have just one chunk, process it directly into the output
+ * buffer - avoids all the job scheduling and temporary vector allocation
+ * overhead. */
+ const int chunk_count = calc_chunk_count(tot_count);
+ if (chunk_count == 1) {
+ for (int i = 0; i < tot_count; i++) {
+ function(fh, i);
+ }
+ return;
+ }
+ /* Give each chunk its own temporary output buffer, and process them in parallel. */
+ std::vector<FormatHandler<eFileType::OBJ>> buffers(chunk_count);
+ blender::threading::parallel_for(IndexRange(chunk_count), 1, [&](IndexRange range) {
+ for (const int r : range) {
+ int i_start = r * chunk_size;
+ int i_end = std::min(i_start + chunk_size, tot_count);
+ auto &buf = buffers[r];
+ for (int i = i_start; i < i_end; i++) {
+ function(buf, i);
+ }
+ }
+ });
+ /* Emit all temporary output buffers into the destination buffer. */
+ for (auto &buf : buffers) {
+ fh.append_from(buf);
}
}
-int OBJWriter::write_smooth_group(const OBJMesh &obj_mesh_data,
- const int poly_index,
- const int last_poly_smooth_group) const
+void OBJWriter::write_vertex_coords(FormatHandler<eFileType::OBJ> &fh,
+ const OBJMesh &obj_mesh_data) const
{
- int current_group = SMOOTH_GROUP_DISABLED;
- if (!export_params_.export_smooth_groups && obj_mesh_data.is_ith_poly_smooth(poly_index)) {
- /* Smooth group calculation is disabled, but polygon is smooth-shaded. */
- current_group = SMOOTH_GROUP_DEFAULT;
- }
- else if (obj_mesh_data.is_ith_poly_smooth(poly_index)) {
- /* Smooth group calc is enabled and polygon is smooth–shaded, so find the group. */
- current_group = obj_mesh_data.ith_smooth_group(poly_index);
- }
-
- if (current_group == last_poly_smooth_group) {
- /* Group has already been written, even if it is "s 0". */
- return current_group;
- }
- file_handler_->write<eOBJSyntaxElement::smooth_group>(current_group);
- return current_group;
+ const int tot_count = obj_mesh_data.tot_vertices();
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
+ buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]);
+ });
}
-int16_t OBJWriter::write_poly_material(const OBJMesh &obj_mesh_data,
- const int poly_index,
- const int16_t last_poly_mat_nr,
- std::function<const char *(int)> matname_fn) const
+void OBJWriter::write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &r_obj_mesh_data) const
{
- if (!export_params_.export_materials || obj_mesh_data.tot_materials() <= 0) {
- return last_poly_mat_nr;
- }
- const int16_t current_mat_nr = obj_mesh_data.ith_poly_matnr(poly_index);
- /* Whenever a polygon with a new material is encountered, write its material
- * and/or group, otherwise pass. */
- if (last_poly_mat_nr == current_mat_nr) {
- return current_mat_nr;
- }
- if (current_mat_nr == NOT_FOUND) {
- file_handler_->write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED);
- return current_mat_nr;
- }
- if (export_params_.export_object_groups) {
- write_object_group(obj_mesh_data);
- }
- const char *mat_name = matname_fn(current_mat_nr);
- if (!mat_name) {
- mat_name = MATERIAL_GROUP_DISABLED;
- }
- file_handler_->write<eOBJSyntaxElement::poly_usemtl>(mat_name);
-
- return current_mat_nr;
+ const Vector<float2> &uv_coords = r_obj_mesh_data.get_uv_coords();
+ const int tot_count = uv_coords.size();
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ const float2 &uv_vertex = uv_coords[i];
+ buf.write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]);
+ });
}
-int16_t OBJWriter::write_vertex_group(const OBJMesh &obj_mesh_data,
- const int poly_index,
- const int16_t last_poly_vertex_group) const
+void OBJWriter::write_poly_normals(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data)
{
- if (!export_params_.export_vertex_groups) {
- return last_poly_vertex_group;
- }
- const int16_t current_group = obj_mesh_data.get_poly_deform_group_index(poly_index);
-
- if (current_group == last_poly_vertex_group) {
- /* No vertex group found in this polygon, just like in the last iteration. */
- return current_group;
- }
- if (current_group == NOT_FOUND) {
- file_handler_->write<eOBJSyntaxElement::object_group>(DEFORM_GROUP_DISABLED);
- return current_group;
- }
- file_handler_->write<eOBJSyntaxElement::object_group>(
- obj_mesh_data.get_poly_deform_group_name(current_group));
- return current_group;
+ /* Poly normals should be calculated earlier via store_normal_coords_and_indices. */
+ const Vector<float3> &normal_coords = obj_mesh_data.get_normal_coords();
+ const int tot_count = normal_coords.size();
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ const float3 &normal = normal_coords[i];
+ buf.write<eOBJSyntaxElement::normal>(normal[0], normal[1], normal[2]);
+ });
}
OBJWriter::func_vert_uv_normal_indices OBJWriter::get_poly_element_writer(
@@ -278,32 +268,85 @@ OBJWriter::func_vert_uv_normal_indices OBJWriter::get_poly_element_writer(
return &OBJWriter::write_vert_indices;
}
-void OBJWriter::write_poly_elements(const OBJMesh &obj_mesh_data,
- std::function<const char *(int)> matname_fn)
+static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams &params, int poly_idx)
{
- int last_poly_smooth_group = NEGATIVE_INIT;
- int16_t last_poly_vertex_group = NEGATIVE_INIT;
- int16_t last_poly_mat_nr = NEGATIVE_INIT;
+ if (poly_idx < 0) {
+ return NEGATIVE_INIT;
+ }
+ int group = SMOOTH_GROUP_DISABLED;
+ if (mesh.is_ith_poly_smooth(poly_idx)) {
+ group = !params.export_smooth_groups ? SMOOTH_GROUP_DEFAULT : mesh.ith_smooth_group(poly_idx);
+ }
+ return group;
+}
+void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ const OBJMesh &obj_mesh_data,
+ std::function<const char *(int)> matname_fn)
+{
const func_vert_uv_normal_indices poly_element_writer = get_poly_element_writer(
obj_mesh_data.tot_uv_vertices());
const int tot_polygons = obj_mesh_data.tot_polygons();
- for (int i = 0; i < tot_polygons; i++) {
+ obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
Vector<int> poly_vertex_indices = obj_mesh_data.calc_poly_vertex_indices(i);
Span<int> poly_uv_indices = obj_mesh_data.calc_poly_uv_indices(i);
Vector<int> poly_normal_indices = obj_mesh_data.calc_poly_normal_indices(i);
- last_poly_smooth_group = write_smooth_group(obj_mesh_data, i, last_poly_smooth_group);
- last_poly_vertex_group = write_vertex_group(obj_mesh_data, i, last_poly_vertex_group);
- last_poly_mat_nr = write_poly_material(obj_mesh_data, i, last_poly_mat_nr, matname_fn);
- (this->*poly_element_writer)(poly_vertex_indices, poly_uv_indices, poly_normal_indices);
- }
+ /* Write smoothing group if different from previous. */
+ {
+ const int prev_group = get_smooth_group(obj_mesh_data, export_params_, i - 1);
+ const int group = get_smooth_group(obj_mesh_data, export_params_, i);
+ if (group != prev_group) {
+ buf.write<eOBJSyntaxElement::smooth_group>(group);
+ }
+ }
+
+ /* Write vertex group if different from previous. */
+ if (export_params_.export_vertex_groups) {
+ const int16_t prev_group = i == 0 ? NEGATIVE_INIT :
+ obj_mesh_data.get_poly_deform_group_index(i - 1);
+ const int16_t group = obj_mesh_data.get_poly_deform_group_index(i);
+ if (group != prev_group) {
+ buf.write<eOBJSyntaxElement::object_group>(
+ group == NOT_FOUND ? DEFORM_GROUP_DISABLED :
+ obj_mesh_data.get_poly_deform_group_name(group));
+ }
+ }
+
+ /* Write material name and material group if different from previous. */
+ if (export_params_.export_materials && obj_mesh_data.tot_materials() > 0) {
+ const int16_t prev_mat = i == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(i - 1);
+ const int16_t mat = obj_mesh_data.ith_poly_matnr(i);
+ if (mat != prev_mat) {
+ if (mat == NOT_FOUND) {
+ buf.write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED);
+ }
+ else {
+ if (export_params_.export_object_groups) {
+ write_object_group(buf, obj_mesh_data);
+ }
+ const char *mat_name = matname_fn(mat);
+ if (!mat_name) {
+ mat_name = MATERIAL_GROUP_DISABLED;
+ }
+ buf.write<eOBJSyntaxElement::poly_usemtl>(mat_name);
+ }
+ }
+ }
+
+ /* Write polygon elements. */
+ (this->*poly_element_writer)(
+ buf, offsets, poly_vertex_indices, poly_uv_indices, poly_normal_indices);
+ });
}
-void OBJWriter::write_edges_indices(const OBJMesh &obj_mesh_data) const
+void OBJWriter::write_edges_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ const OBJMesh &obj_mesh_data) const
{
- obj_mesh_data.ensure_mesh_edges();
+ /* Note: ensure_mesh_edges should be called before. */
const int tot_edges = obj_mesh_data.tot_edges();
for (int edge_index = 0; edge_index < tot_edges; edge_index++) {
const std::optional<std::array<int, 2>> vertex_indices =
@@ -311,13 +354,13 @@ void OBJWriter::write_edges_indices(const OBJMesh &obj_mesh_data) const
if (!vertex_indices) {
continue;
}
- file_handler_->write<eOBJSyntaxElement::edge>(
- (*vertex_indices)[0] + index_offsets_.vertex_offset + 1,
- (*vertex_indices)[1] + index_offsets_.vertex_offset + 1);
+ fh.write<eOBJSyntaxElement::edge>((*vertex_indices)[0] + offsets.vertex_offset + 1,
+ (*vertex_indices)[1] + offsets.vertex_offset + 1);
}
}
-void OBJWriter::write_nurbs_curve(const OBJCurve &obj_nurbs_data) const
+void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
+ const OBJCurve &obj_nurbs_data) const
{
const int total_splines = obj_nurbs_data.total_splines();
for (int spline_idx = 0; spline_idx < total_splines; spline_idx++) {
@@ -325,15 +368,15 @@ void OBJWriter::write_nurbs_curve(const OBJCurve &obj_nurbs_data) const
for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) {
const float3 vertex_coords = obj_nurbs_data.vertex_coordinates(
spline_idx, vertex_idx, export_params_.scaling_factor);
- file_handler_->write<eOBJSyntaxElement::vertex_coords>(
+ fh.write<eOBJSyntaxElement::vertex_coords>(
vertex_coords[0], vertex_coords[1], vertex_coords[2]);
}
const char *nurbs_name = obj_nurbs_data.get_curve_name();
const int nurbs_degree = obj_nurbs_data.get_nurbs_degree(spline_idx);
- file_handler_->write<eOBJSyntaxElement::object_group>(nurbs_name);
- file_handler_->write<eOBJSyntaxElement::cstype>();
- file_handler_->write<eOBJSyntaxElement::nurbs_degree>(nurbs_degree);
+ fh.write<eOBJSyntaxElement::object_group>(nurbs_name);
+ fh.write<eOBJSyntaxElement::cstype>();
+ fh.write<eOBJSyntaxElement::nurbs_degree>(nurbs_degree);
/**
* The numbers written here are indices into the vertex coordinates written
* earlier, relative to the line that is going to be written.
@@ -342,36 +385,42 @@ void OBJWriter::write_nurbs_curve(const OBJCurve &obj_nurbs_data) const
* 0.0 1.0 -1 -2 -3 -4 -1 -2 -3 for a cyclic curve with 4 vertices.
*/
const int total_control_points = obj_nurbs_data.total_spline_control_points(spline_idx);
- file_handler_->write<eOBJSyntaxElement::curve_element_begin>();
+ fh.write<eOBJSyntaxElement::curve_element_begin>();
for (int i = 0; i < total_control_points; i++) {
/* "+1" to keep indices one-based, even if they're negative: i.e., -1 refers to the
* last vertex coordinate, -2 second last. */
- file_handler_->write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1));
+ fh.write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1));
}
- file_handler_->write<eOBJSyntaxElement::curve_element_end>();
+ fh.write<eOBJSyntaxElement::curve_element_end>();
/**
* In `parm u 0 0.1 ..` line:, (total control points + 2) equidistant numbers in the
- * parameter range are inserted.
+ * parameter range are inserted. However for curves with endpoint flag,
+ * first degree+1 numbers are zeroes, and last degree+1 numbers are ones
*/
- file_handler_->write<eOBJSyntaxElement::nurbs_parameter_begin>();
+
+ const short flagsu = obj_nurbs_data.get_nurbs_flagu(spline_idx);
+ const bool cyclic = flagsu & CU_NURB_CYCLIC;
+ const bool endpoint = !cyclic && (flagsu & CU_NURB_ENDPOINT);
+ fh.write<eOBJSyntaxElement::nurbs_parameter_begin>();
for (int i = 1; i <= total_control_points + 2; i++) {
- file_handler_->write<eOBJSyntaxElement::nurbs_parameters>(1.0f * i /
- (total_control_points + 2 + 1));
+ float parm = 1.0f * i / (total_control_points + 2 + 1);
+ if (endpoint) {
+ if (i <= nurbs_degree) {
+ parm = 0;
+ }
+ else if (i > total_control_points + 2 - nurbs_degree) {
+ parm = 1;
+ }
+ }
+ fh.write<eOBJSyntaxElement::nurbs_parameters>(parm);
}
- file_handler_->write<eOBJSyntaxElement::nurbs_parameter_end>();
+ fh.write<eOBJSyntaxElement::nurbs_parameter_end>();
- file_handler_->write<eOBJSyntaxElement::nurbs_group_end>();
+ fh.write<eOBJSyntaxElement::nurbs_group_end>();
}
}
-void OBJWriter::update_index_offsets(const OBJMesh &obj_mesh_data)
-{
- index_offsets_.vertex_offset += obj_mesh_data.tot_vertices();
- index_offsets_.uv_vertex_offset += obj_mesh_data.tot_uv_vertices();
- index_offsets_.normal_offset += obj_mesh_data.tot_normal_indices();
-}
-
/* -------------------------------------------------------------------- */
/** \name .MTL writers.
* \{ */
@@ -394,18 +443,31 @@ MTLWriter::MTLWriter(const char *obj_filepath) noexcept(false)
if (!ok) {
throw std::system_error(ENAMETOOLONG, std::system_category(), "");
}
- file_handler_ = std::make_unique<FileHandler<eFileType::MTL>>(mtl_filepath_);
+ outfile_ = BLI_fopen(mtl_filepath_.c_str(), "wb");
+ if (!outfile_) {
+ throw std::system_error(errno, std::system_category(), "Cannot open file " + mtl_filepath_);
+ }
+}
+MTLWriter::~MTLWriter()
+{
+ if (outfile_) {
+ fmt_handler_.write_to_file(outfile_);
+ if (std::fclose(outfile_)) {
+ std::cerr << "Error: could not close the file '" << mtl_filepath_
+ << "' properly, it may be corrupted." << std::endl;
+ }
+ }
}
-void MTLWriter::write_header(const char *blen_filepath) const
+void MTLWriter::write_header(const char *blen_filepath)
{
using namespace std::string_literals;
const char *blen_basename = (blen_filepath && blen_filepath[0] != '\0') ?
BLI_path_basename(blen_filepath) :
"None";
- file_handler_->write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
- " MTL File: '" + blen_basename + "'\n");
- file_handler_->write<eMTLSyntaxElement::string>("# www.blender.org\n");
+ fmt_handler_.write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
+ " MTL File: '" + blen_basename + "'\n");
+ fmt_handler_.write<eMTLSyntaxElement::string>("# www.blender.org\n");
}
StringRefNull MTLWriter::mtl_file_path() const
@@ -415,18 +477,18 @@ StringRefNull MTLWriter::mtl_file_path() const
void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material)
{
- file_handler_->write<eMTLSyntaxElement::Ns>(mtl_material.Ns);
- file_handler_->write<eMTLSyntaxElement::Ka>(
+ fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl_material.Ns);
+ fmt_handler_.write<eMTLSyntaxElement::Ka>(
mtl_material.Ka.x, mtl_material.Ka.y, mtl_material.Ka.z);
- file_handler_->write<eMTLSyntaxElement::Kd>(
+ fmt_handler_.write<eMTLSyntaxElement::Kd>(
mtl_material.Kd.x, mtl_material.Kd.y, mtl_material.Kd.z);
- file_handler_->write<eMTLSyntaxElement::Ks>(
+ fmt_handler_.write<eMTLSyntaxElement::Ks>(
mtl_material.Ks.x, mtl_material.Ks.y, mtl_material.Ks.z);
- file_handler_->write<eMTLSyntaxElement::Ke>(
+ fmt_handler_.write<eMTLSyntaxElement::Ke>(
mtl_material.Ke.x, mtl_material.Ke.y, mtl_material.Ke.z);
- file_handler_->write<eMTLSyntaxElement::Ni>(mtl_material.Ni);
- file_handler_->write<eMTLSyntaxElement::d>(mtl_material.d);
- file_handler_->write<eMTLSyntaxElement::illum>(mtl_material.illum);
+ fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl_material.Ni);
+ fmt_handler_.write<eMTLSyntaxElement::d>(mtl_material.d);
+ fmt_handler_.write<eMTLSyntaxElement::illum>(mtl_material.illum);
}
void MTLWriter::write_texture_map(
@@ -449,8 +511,8 @@ void MTLWriter::write_texture_map(
#define SYNTAX_DISPATCH(eMTLSyntaxElement) \
if (texture_map.key == eMTLSyntaxElement) { \
- file_handler_->write<eMTLSyntaxElement>(translation + scale + map_bump_strength, \
- texture_map.value.image_path); \
+ fmt_handler_.write<eMTLSyntaxElement>(translation + scale + map_bump_strength, \
+ texture_map.value.image_path); \
return; \
}
@@ -474,8 +536,8 @@ void MTLWriter::write_materials()
mtlmaterials_.end(),
[](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; });
for (const MTLMaterial &mtlmat : mtlmaterials_) {
- file_handler_->write<eMTLSyntaxElement::string>("\n");
- file_handler_->write<eMTLSyntaxElement::newmtl>(mtlmat.name);
+ fmt_handler_.write<eMTLSyntaxElement::string>("\n");
+ fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name);
write_bsdf_properties(mtlmat);
for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map :
mtlmat.texture_maps.items()) {
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
index 1cad179a70c..c88955e5090 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
@@ -49,14 +49,29 @@ struct IndexOffsets {
class OBJWriter : NonMovable, NonCopyable {
private:
const OBJExportParams &export_params_;
- std::unique_ptr<FileHandler<eFileType::OBJ>> file_handler_ = nullptr;
- IndexOffsets index_offsets_{0, 0, 0};
+ std::string outfile_path_;
+ FILE *outfile_;
public:
OBJWriter(const char *filepath, const OBJExportParams &export_params) noexcept(false)
- : export_params_(export_params)
+ : export_params_(export_params), outfile_path_(filepath), outfile_(nullptr)
{
- file_handler_ = std::make_unique<FileHandler<eFileType::OBJ>>(filepath);
+ outfile_ = BLI_fopen(filepath, "wb");
+ if (!outfile_) {
+ throw std::system_error(errno, std::system_category(), "Cannot open file " + outfile_path_);
+ }
+ }
+ ~OBJWriter()
+ {
+ if (outfile_ && std::fclose(outfile_)) {
+ std::cerr << "Error: could not close the file '" << outfile_path_
+ << "' properly, it may be corrupted." << std::endl;
+ }
+ }
+
+ FILE *get_outfile() const
+ {
+ return outfile_;
}
void write_header() const;
@@ -64,11 +79,11 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write object's name or group.
*/
- void write_object_name(const OBJMesh &obj_mesh_data) const;
+ void write_object_name(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const;
/**
* Write an object's group with mesh and/or material name appended conditionally.
*/
- void write_object_group(const OBJMesh &obj_mesh_data) const;
+ void write_object_group(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const;
/**
* Write file name of Material Library in .OBJ file.
*/
@@ -76,38 +91,17 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write vertex coordinates for all vertices as "v x y z".
*/
- void write_vertex_coords(const OBJMesh &obj_mesh_data) const;
+ void write_vertex_coords(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const;
/**
* Write UV vertex coordinates for all vertices as `vt u v`.
* \note UV indices are stored here, but written with polygons later.
*/
- void write_uv_coords(OBJMesh &obj_mesh_data) const;
+ void write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data) const;
/**
* Write loop normals for smooth-shaded polygons, and polygon normals otherwise, as "vn x y z".
* \note Normal indices ares stored here, but written with polygons later.
*/
- void write_poly_normals(OBJMesh &obj_mesh_data);
- /**
- * Write smooth group if polygon at the given index is shaded smooth else "s 0"
- */
- int write_smooth_group(const OBJMesh &obj_mesh_data,
- int poly_index,
- int last_poly_smooth_group) const;
- /**
- * Write material name and material group of a polygon in the .OBJ file.
- * \return #mat_nr of the polygon at the given index.
- * \note It doesn't write to the material library.
- */
- int16_t write_poly_material(const OBJMesh &obj_mesh_data,
- int poly_index,
- int16_t last_poly_mat_nr,
- std::function<const char *(int)> matname_fn) const;
- /**
- * Write the name of the deform group of a polygon.
- */
- int16_t write_vertex_group(const OBJMesh &obj_mesh_data,
- int poly_index,
- int16_t last_poly_vertex_group) const;
+ void write_poly_normals(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data);
/**
* Write polygon elements with at least vertex indices, and conditionally with UV vertex
* indices and polygon normal indices. Also write groups: smooth, vertex, material.
@@ -115,25 +109,25 @@ class OBJWriter : NonMovable, NonCopyable {
* name used in the .obj file.
* \note UV indices were stored while writing UV vertices.
*/
- void write_poly_elements(const OBJMesh &obj_mesh_data,
+ void write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ const OBJMesh &obj_mesh_data,
std::function<const char *(int)> matname_fn);
/**
* Write loose edges of a mesh as "l v1 v2".
*/
- void write_edges_indices(const OBJMesh &obj_mesh_data) const;
+ void write_edges_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ const OBJMesh &obj_mesh_data) const;
/**
* Write a NURBS curve to the .OBJ file in parameter form.
*/
- void write_nurbs_curve(const OBJCurve &obj_nurbs_data) const;
-
- /**
- * When there are multiple objects in a frame, the indices of previous objects' coordinates or
- * normals add up.
- */
- void update_index_offsets(const OBJMesh &obj_mesh_data);
+ void write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh, const OBJCurve &obj_nurbs_data) const;
private:
- using func_vert_uv_normal_indices = void (OBJWriter::*)(Span<int> vert_indices,
+ using func_vert_uv_normal_indices = void (OBJWriter::*)(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> uv_indices,
Span<int> normal_indices) const;
/**
@@ -144,25 +138,33 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1/vt1/vn1 v2/vt2/vn2 ...".
*/
- void write_vert_uv_normal_indices(Span<int> vert_indices,
+ void write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> uv_indices,
Span<int> normal_indices) const;
/**
* Write one line of polygon indices as "f v1//vn1 v2//vn2 ...".
*/
- void write_vert_normal_indices(Span<int> vert_indices,
+ void write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> /*uv_indices*/,
Span<int> normal_indices) const;
/**
* Write one line of polygon indices as "f v1/vt1 v2/vt2 ...".
*/
- void write_vert_uv_indices(Span<int> vert_indices,
+ void write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> uv_indices,
Span<int> /*normal_indices*/) const;
/**
* Write one line of polygon indices as "f v1 v2 ...".
*/
- void write_vert_indices(Span<int> vert_indices,
+ void write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
+ const IndexOffsets &offsets,
+ Span<int> vert_indices,
Span<int> /*uv_indices*/,
Span<int> /*normal_indices*/) const;
};
@@ -172,7 +174,8 @@ class OBJWriter : NonMovable, NonCopyable {
*/
class MTLWriter : NonMovable, NonCopyable {
private:
- std::unique_ptr<FileHandler<eFileType::MTL>> file_handler_ = nullptr;
+ FormatHandler<eFileType::MTL> fmt_handler_;
+ FILE *outfile_;
std::string mtl_filepath_;
Vector<MTLMaterial> mtlmaterials_;
/* Map from a Material* to an index into mtlmaterials_. */
@@ -183,8 +186,9 @@ class MTLWriter : NonMovable, NonCopyable {
* Create the .MTL file.
*/
MTLWriter(const char *obj_filepath) noexcept(false);
+ ~MTLWriter();
- void write_header(const char *blen_filepath) const;
+ void write_header(const char *blen_filepath);
/**
* Write all of the material specifications to the MTL file.
* For consistency of output from run to run (useful for testing),
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
index a6f0174d68b..7d8427a8980 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
@@ -24,8 +24,10 @@
#include <string>
#include <system_error>
#include <type_traits>
+#include <vector>
#include "BLI_compiler_attrs.h"
+#include "BLI_fileops.h"
#include "BLI_string_ref.hh"
#include "BLI_utility_mixins.hh"
@@ -88,6 +90,7 @@ enum class eMTLSyntaxElement {
template<eFileType filetype> struct FileTypeTraits;
+/* Used to prevent mixing of say OBJ file format with MTL syntax elements. */
template<> struct FileTypeTraits<eFileType::OBJ> {
using SyntaxType = eOBJSyntaxElement;
};
@@ -96,15 +99,19 @@ template<> struct FileTypeTraits<eFileType::MTL> {
using SyntaxType = eMTLSyntaxElement;
};
-template<eFileType type> struct Formatting {
+struct FormattingSyntax {
+ /* Formatting syntax with the file format key like `newmtl %s\n`. */
const char *fmt = nullptr;
+ /* Number of arguments needed by the syntax. */
const int total_args = 0;
- /* Fail to compile by default. */
- const bool is_type_valid = false;
+ /* Whether types of the given arguments are accepted by the syntax above. Fail to compile by
+ * default.
+ */
+ const bool are_types_valid = false;
};
/**
- * Type dependent but always false. Use to add a conditional compile-time error.
+ * Type dependent but always false. Use to add a `constexpr` conditional compile-time error.
*/
template<typename T> struct always_false : std::false_type {
};
@@ -118,9 +125,16 @@ constexpr bool is_type_integral = (... && std::is_integral_v<std::decay_t<T>>);
template<typename... T>
constexpr bool is_type_string_related = (... && std::is_constructible_v<std::string, T>);
-template<eFileType filetype, typename... T>
-constexpr std::enable_if_t<filetype == eFileType::OBJ, Formatting<filetype>>
-syntax_elem_to_formatting(const eOBJSyntaxElement key)
+/* GCC (at least 9.3) while compiling the obj_exporter_tests.cc with optimizations on,
+ * results in "obj_export_io.hh:205:18: warning: ‘%s’ directive output truncated writing 34 bytes
+ * into a region of size 6" and similar warnings. Yes the output is truncated, and that is covered
+ * as an edge case by tests on purpose. */
+#if defined __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wformat-truncation"
+#endif
+template<typename... T>
+constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key)
{
switch (key) {
case eOBJSyntaxElement::vertex_coords: {
@@ -163,7 +177,7 @@ syntax_elem_to_formatting(const eOBJSyntaxElement key)
return {"curv 0.0 1.0", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::nurbs_parameter_begin: {
- return {"parm 0.0", 0, is_type_string_related<T...>};
+ return {"parm u 0.0", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::nurbs_parameters: {
return {" %f", 1, is_type_float<T...>};
@@ -201,9 +215,8 @@ syntax_elem_to_formatting(const eOBJSyntaxElement key)
}
}
-template<eFileType filetype, typename... T>
-constexpr std::enable_if_t<filetype == eFileType::MTL, Formatting<filetype>>
-syntax_elem_to_formatting(const eMTLSyntaxElement key)
+template<typename... T>
+constexpr FormattingSyntax syntax_elem_to_formatting(const eMTLSyntaxElement key)
{
switch (key) {
case eMTLSyntaxElement::newmtl: {
@@ -260,40 +273,72 @@ syntax_elem_to_formatting(const eMTLSyntaxElement key)
}
}
}
+#if defined __GNUC__
+# pragma GCC diagnostic pop
+#endif
-template<eFileType filetype> class FileHandler : NonCopyable, NonMovable {
+/**
+ * File format and syntax agnostic file buffer writer.
+ * All writes are done into an internal chunked memory buffer
+ * (list of default 64 kilobyte blocks).
+ * Call write_fo_file once in a while to write the memory buffer(s)
+ * into the given file.
+ */
+template<eFileType filetype,
+ size_t buffer_chunk_size = 64 * 1024,
+ size_t write_local_buffer_size = 1024>
+class FormatHandler : NonCopyable, NonMovable {
private:
- FILE *outfile_ = nullptr;
- std::string outfile_path_;
+ typedef std::vector<char> VectorChar;
+ std::vector<VectorChar> blocks_;
public:
- FileHandler(std::string outfile_path) noexcept(false) : outfile_path_(std::move(outfile_path))
+ /* Write contents to the buffer(s) into a file, and clear the buffers. */
+ void write_to_file(FILE *f)
{
- outfile_ = std::fopen(outfile_path_.c_str(), "w");
- if (!outfile_) {
- throw std::system_error(errno, std::system_category(), "Cannot open file");
- }
+ for (const auto &b : blocks_)
+ fwrite(b.data(), 1, b.size(), f);
+ blocks_.clear();
}
- ~FileHandler()
+ std::string get_as_string() const
{
- if (outfile_ && std::fclose(outfile_)) {
- std::cerr << "Error: could not close the file '" << outfile_path_
- << "' properly, it may be corrupted." << std::endl;
- }
+ std::string s;
+ for (const auto &b : blocks_)
+ s.append(b.data(), b.size());
+ return s;
+ }
+ size_t get_block_count() const
+ {
+ return blocks_.size();
}
+ void append_from(FormatHandler<filetype, buffer_chunk_size, write_local_buffer_size> &v)
+ {
+ blocks_.insert(blocks_.end(),
+ std::make_move_iterator(v.blocks_.begin()),
+ std::make_move_iterator(v.blocks_.end()));
+ v.blocks_.clear();
+ }
+
+ /**
+ * Example invocation: `writer->write<eMTLSyntaxElement::newmtl>("foo")`.
+ *
+ * \param key Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for
+ * `eFileType::MTL`.
+ */
template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T>
- constexpr void write(T &&...args) const
+ constexpr void write(T &&...args)
{
- constexpr Formatting<filetype> fmt_nargs_valid = syntax_elem_to_formatting<filetype, T...>(
- key);
- write__impl<fmt_nargs_valid.total_args>(fmt_nargs_valid.fmt, std::forward<T>(args)...);
- /* Types of all arguments and the number of arguments should match
- * what the formatting specifies. */
- return std::enable_if_t < fmt_nargs_valid.is_type_valid &&
- (sizeof...(T) == fmt_nargs_valid.total_args),
- void > ();
+ /* Get format syntax, number of arguments expected and whether types of given arguments are
+ * valid.
+ */
+ constexpr FormattingSyntax fmt_nargs_valid = syntax_elem_to_formatting<T...>(key);
+ BLI_STATIC_ASSERT(fmt_nargs_valid.are_types_valid &&
+ (sizeof...(T) == fmt_nargs_valid.total_args),
+ "Types of all arguments and the number of arguments should match what the "
+ "formatting specifies.");
+ write_impl(fmt_nargs_valid.fmt, std::forward<T>(args)...);
}
private:
@@ -301,11 +346,11 @@ template<eFileType filetype> class FileHandler : NonCopyable, NonMovable {
template<typename T> using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
/**
- * Make #std::string etc., usable for `fprintf` family.
+ * Make #std::string etc., usable for `fprintf` family. int float etc. are not affected.
* \return: `const char *` or the original argument if the argument is
* not related to #std::string.
*/
- template<typename T> constexpr auto string_to_primitive(T &&arg) const
+ template<typename T> constexpr auto convert_to_primitive(T &&arg) const
{
if constexpr (std::is_same_v<remove_cvref_t<T>, std::string> ||
std::is_same_v<remove_cvref_t<T>, blender::StringRefNull>) {
@@ -319,21 +364,53 @@ template<eFileType filetype> class FileHandler : NonCopyable, NonMovable {
return;
}
else {
+ /* For int, float etc. */
return std::forward<T>(arg);
}
}
- template<int total_args, typename... T>
- constexpr std::enable_if_t<(total_args != 0), void> write__impl(const char *fmt,
- T &&...args) const
+ /* Ensure the last block contains at least this amount of free space.
+ * If not, add a new block with max of block size & the amount of space needed. */
+ void ensure_space(size_t at_least)
{
- std::fprintf(outfile_, fmt, string_to_primitive(std::forward<T>(args))...);
+ if (blocks_.empty() || (blocks_.back().capacity() - blocks_.back().size() < at_least)) {
+ VectorChar &b = blocks_.emplace_back(VectorChar());
+ b.reserve(std::max(at_least, buffer_chunk_size));
+ }
}
- template<int total_args, typename... T>
- constexpr std::enable_if_t<(total_args == 0), void> write__impl(const char *fmt,
- T &&...args) const
+
+ template<typename... T> constexpr void write_impl(const char *fmt, T &&...args)
{
- std::fputs(fmt, outfile_);
+ if constexpr (sizeof...(T) == 0) {
+ /* No arguments: just emit the format string. */
+ size_t len = strlen(fmt);
+ ensure_space(len);
+ VectorChar &bb = blocks_.back();
+ bb.insert(bb.end(), fmt, fmt + len);
+ }
+ else {
+ /* Format into a local buffer. */
+ char buf[write_local_buffer_size];
+ int needed = std::snprintf(
+ buf, write_local_buffer_size, fmt, convert_to_primitive(std::forward<T>(args))...);
+ if (needed < 0)
+ throw std::system_error(
+ errno, std::system_category(), "Failed to format obj export string into a buffer");
+ ensure_space(needed + 1); /* Ensure space for zero terminator. */
+ VectorChar &bb = blocks_.back();
+ if (needed < write_local_buffer_size) {
+ /* String formatted successfully into the local buffer, copy it. */
+ bb.insert(bb.end(), buf, buf + needed);
+ }
+ else {
+ /* Would need more space than the local buffer: insert said space and format again into
+ * that. */
+ size_t bbEnd = bb.size();
+ bb.insert(bb.end(), needed, ' ');
+ std::snprintf(
+ bb.data() + bbEnd, needed + 1, fmt, convert_to_primitive(std::forward<T>(args))...);
+ }
+ }
}
};
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
index d6e1d8a7ea5..468631cdd82 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
@@ -17,6 +17,8 @@
/** \file
* \ingroup obj
*/
+/* Silence warnings from copying deprecated fields. Needed for an Object copy constructor use. */
+#define DNA_DEPRECATED_ALLOW
#include "BKE_customdata.h"
#include "BKE_deform.h"
@@ -42,20 +44,21 @@
namespace blender::io::obj {
OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Object *mesh_object)
{
- export_object_eval_ = DEG_get_evaluated_object(depsgraph, mesh_object);
- export_mesh_eval_ = BKE_object_get_evaluated_mesh(export_object_eval_);
+ /* We need to copy the object because it may be in temporary space. */
+ Object *obj_eval = DEG_get_evaluated_object(depsgraph, mesh_object);
+ export_object_eval_ = *obj_eval;
+ export_mesh_eval_ = BKE_object_get_evaluated_mesh(&export_object_eval_);
mesh_eval_needs_free_ = false;
if (!export_mesh_eval_) {
/* Curves and NURBS surfaces need a new mesh when they're
* exported in the form of vertices and edges.
*/
- export_mesh_eval_ = BKE_mesh_new_from_object(depsgraph, export_object_eval_, true, true);
+ export_mesh_eval_ = BKE_mesh_new_from_object(depsgraph, &export_object_eval_, true, true);
/* Since a new mesh been allocated, it needs to be freed in the destructor. */
mesh_eval_needs_free_ = true;
}
- if (export_params.export_triangulated_mesh &&
- ELEM(export_object_eval_->type, OB_MESH, OB_SURF)) {
+ if (export_params.export_triangulated_mesh && ELEM(export_object_eval_.type, OB_MESH, OB_SURF)) {
std::tie(export_mesh_eval_, mesh_eval_needs_free_) = triangulate_mesh_eval();
}
set_world_axes_transform(export_params.forward_axis, export_params.up_axis);
@@ -66,16 +69,28 @@ OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Obj
*/
OBJMesh::~OBJMesh()
{
- free_mesh_if_needed();
- if (poly_smooth_groups_) {
- MEM_freeN(poly_smooth_groups_);
- }
+ clear();
}
void OBJMesh::free_mesh_if_needed()
{
if (mesh_eval_needs_free_ && export_mesh_eval_) {
BKE_id_free(nullptr, export_mesh_eval_);
+ export_mesh_eval_ = nullptr;
+ mesh_eval_needs_free_ = false;
+ }
+}
+
+void OBJMesh::clear()
+{
+ free_mesh_if_needed();
+ uv_indices_.clear_and_make_inline();
+ uv_coords_.clear_and_make_inline();
+ loop_to_normal_index_.clear_and_make_inline();
+ normal_coords_.clear_and_make_inline();
+ if (poly_smooth_groups_) {
+ MEM_freeN(poly_smooth_groups_);
+ poly_smooth_groups_ = nullptr;
}
}
@@ -116,10 +131,10 @@ void OBJMesh::set_world_axes_transform(const eTransformAxisForward forward,
mat3_from_axis_conversion(OBJ_AXIS_Y_FORWARD, OBJ_AXIS_Z_UP, forward, up, axes_transform);
/* mat3_from_axis_conversion returns a transposed matrix! */
transpose_m3(axes_transform);
- mul_m4_m3m4(world_and_axes_transform_, axes_transform, export_object_eval_->obmat);
+ mul_m4_m3m4(world_and_axes_transform_, axes_transform, export_object_eval_.obmat);
/* mul_m4_m3m4 does not transform last row of obmat, i.e. location data. */
- mul_v3_m3v3(world_and_axes_transform_[3], axes_transform, export_object_eval_->obmat[3]);
- world_and_axes_transform_[3][3] = export_object_eval_->obmat[3][3];
+ mul_v3_m3v3(world_and_axes_transform_[3], axes_transform, export_object_eval_.obmat[3]);
+ world_and_axes_transform_[3][3] = export_object_eval_.obmat[3][3];
}
int OBJMesh::tot_vertices() const
@@ -185,8 +200,14 @@ void OBJMesh::calc_smooth_groups(const bool use_bitflags)
const Material *OBJMesh::get_object_material(const int16_t mat_nr) const
{
- /* "+ 1" as material getter needs one-based indices. */
- const Material *r_mat = BKE_object_material_get(export_object_eval_, mat_nr + 1);
+ /**
+ * The const_cast is safe here because BKE_object_material_get won't change the object
+ * but it is a big can of worms to fix the declaration of that function right now.
+ *
+ * The call uses "+ 1" as material getter needs one-based indices.
+ */
+ Object *obj = const_cast<Object *>(&export_object_eval_);
+ const Material *r_mat = BKE_object_material_get(obj, mat_nr + 1);
#ifdef DEBUG
if (!r_mat) {
std::cerr << "Material not found for mat_nr = " << mat_nr << std::endl;
@@ -209,7 +230,7 @@ int16_t OBJMesh::ith_poly_matnr(const int poly_index) const
const char *OBJMesh::get_object_name() const
{
- return export_object_eval_->id.name + 2;
+ return export_object_eval_.id.name + 2;
}
const char *OBJMesh::get_object_mesh_name() const
@@ -247,7 +268,7 @@ Vector<int> OBJMesh::calc_poly_vertex_indices(const int poly_index) const
return r_poly_vertex_indices;
}
-void OBJMesh::store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coords)
+void OBJMesh::store_uv_coords_and_indices()
{
const MPoly *mpoly = export_mesh_eval_->mpoly;
const MLoop *mloop = export_mesh_eval_->mloop;
@@ -267,7 +288,7 @@ void OBJMesh::store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coo
uv_indices_.resize(totpoly);
/* At least total vertices of a mesh will be present in its texture map. So
* reserve minimum space early. */
- r_uv_coords.reserve(totvert);
+ uv_coords_.reserve(totvert);
tot_uv_vertices_ = 0;
for (int vertex_index = 0; vertex_index < totvert; vertex_index++) {
@@ -279,11 +300,10 @@ void OBJMesh::store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coo
const int vertices_in_poly = mpoly[uv_vert->poly_index].totloop;
/* Store UV vertex coordinates. */
- r_uv_coords.resize(tot_uv_vertices_);
+ uv_coords_.resize(tot_uv_vertices_);
const int loopstart = mpoly[uv_vert->poly_index].loopstart;
Span<float> vert_uv_coords(mloopuv[loopstart + uv_vert->loop_of_poly_index].uv, 2);
- r_uv_coords[tot_uv_vertices_ - 1][0] = vert_uv_coords[0];
- r_uv_coords[tot_uv_vertices_ - 1][1] = vert_uv_coords[1];
+ uv_coords_[tot_uv_vertices_ - 1] = float2(vert_uv_coords[0], vert_uv_coords[1]);
/* Store UV vertex indices. */
uv_indices_[uv_vert->poly_index].resize(vertices_in_poly);
@@ -331,7 +351,7 @@ static float3 round_float3_to_n_digits(const float3 &v, int round_digits)
return ans;
}
-void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords)
+void OBJMesh::store_normal_coords_and_indices()
{
/* We'll round normal components to 4 digits.
* This will cover up some minor differences
@@ -341,7 +361,7 @@ void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords)
constexpr int round_digits = 4;
int cur_normal_index = 0;
Map<float3, int> normal_to_index;
- /* We don't know how many unique normals there will be, but this is a guess.*/
+ /* We don't know how many unique normals there will be, but this is a guess. */
normal_to_index.reserve(export_mesh_eval_->totpoly);
loop_to_normal_index_.resize(export_mesh_eval_->totloop);
loop_to_normal_index_.fill(-1);
@@ -349,7 +369,7 @@ void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords)
*lnors)[3] = (const float(*)[3])(CustomData_get_layer(&export_mesh_eval_->ldata, CD_NORMAL));
for (int poly_index = 0; poly_index < export_mesh_eval_->totpoly; ++poly_index) {
const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
- bool need_per_loop_normals = is_ith_poly_smooth(poly_index);
+ bool need_per_loop_normals = lnors != nullptr || (mpoly.flag & ME_SMOOTH);
if (need_per_loop_normals) {
for (int loop_of_poly = 0; loop_of_poly < mpoly.totloop; ++loop_of_poly) {
float3 loop_normal;
@@ -362,7 +382,7 @@ void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords)
if (loop_norm_index == -1) {
loop_norm_index = cur_normal_index++;
normal_to_index.add(rounded_loop_normal, loop_norm_index);
- r_normal_coords.append(rounded_loop_normal);
+ normal_coords_.append(rounded_loop_normal);
}
loop_to_normal_index_[loop_index] = loop_norm_index;
}
@@ -374,7 +394,7 @@ void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords)
if (poly_norm_index == -1) {
poly_norm_index = cur_normal_index++;
normal_to_index.add(rounded_poly_normal, poly_norm_index);
- r_normal_coords.append(rounded_poly_normal);
+ normal_coords_.append(rounded_poly_normal);
}
for (int i = 0; i < mpoly.totloop; ++i) {
int loop_index = mpoly.loopstart + i;
@@ -403,7 +423,7 @@ int16_t OBJMesh::get_poly_deform_group_index(const int poly_index) const
BLI_assert(poly_index < export_mesh_eval_->totpoly);
const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
- const Object *obj = export_object_eval_;
+ const Object *obj = &export_object_eval_;
const int tot_deform_groups = BKE_object_defgroup_count(obj);
/* Indices of the vector index into deform groups of an object; values are the]
* number of vertex members in one deform group. */
@@ -444,7 +464,7 @@ int16_t OBJMesh::get_poly_deform_group_index(const int poly_index) const
const char *OBJMesh::get_poly_deform_group_name(const int16_t def_group_index) const
{
const bDeformGroup &vertex_group = *(static_cast<bDeformGroup *>(
- BLI_findlink(BKE_object_defgroup_list(export_object_eval_), def_group_index)));
+ BLI_findlink(BKE_object_defgroup_list(&export_object_eval_), def_group_index)));
return vertex_group.name;
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
index 390d8034337..4cfbffdcebc 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
@@ -32,6 +32,7 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
#include "IO_wavefront_obj.h"
@@ -57,7 +58,11 @@ using unique_bmesh_ptr = std::unique_ptr<BMesh, CustomBMeshDeleter>;
class OBJMesh : NonCopyable {
private:
- Object *export_object_eval_;
+ /**
+ * We need to copy the entire Object structure here because the dependency graph iterator
+ * sometimes builds an Object in a temporary space that doesn't persist.
+ */
+ Object export_object_eval_;
Mesh *export_mesh_eval_;
/**
* For curves which are converted to mesh, and triangulated meshes, a new mesh is allocated.
@@ -77,15 +82,23 @@ class OBJMesh : NonCopyable {
* Per-polygon-per-vertex UV vertex indices.
*/
Vector<Vector<int>> uv_indices_;
+ /*
+ * UV vertices.
+ */
+ Vector<float2> uv_coords_;
/**
* Per-loop normal index.
*/
Vector<int> loop_to_normal_index_;
/*
+ * Normal coords.
+ */
+ Vector<float3> normal_coords_;
+ /*
* Total number of normal indices (maximum entry, plus 1, in
* the loop_to_norm_index_ vector).
*/
- int tot_normal_indices_ = NEGATIVE_INIT;
+ int tot_normal_indices_ = 0;
/**
* Total smooth groups in an object.
*/
@@ -103,6 +116,9 @@ class OBJMesh : NonCopyable {
OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Object *mesh_object);
~OBJMesh();
+ /* Clear various arrays to release potentially large memory allocations. */
+ void clear();
+
int tot_vertices() const;
int tot_polygons() const;
int tot_uv_vertices() const;
@@ -160,10 +176,14 @@ class OBJMesh : NonCopyable {
Vector<int> calc_poly_vertex_indices(int poly_index) const;
/**
* Calculate UV vertex coordinates of an Object.
- *
- * \note Also store the UV vertex indices in the member variable.
+ * Stores the coordinates and UV vertex indices in the member variables.
*/
- void store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coords);
+ void store_uv_coords_and_indices();
+ /* Get UV coordinates computed by store_uv_coords_and_indices. */
+ const Vector<float2> &get_uv_coords() const
+ {
+ return uv_coords_;
+ }
Span<int> calc_poly_uv_indices(int poly_index) const;
/**
* Calculate polygon normal of a polygon at given index.
@@ -172,10 +192,15 @@ class OBJMesh : NonCopyable {
*/
float3 calc_poly_normal(int poly_index) const;
/**
- * Find the unique normals of the mesh and return them in \a r_normal_coords.
- * Store the indices into that vector with for each loop in this #OBJMesh.
+ * Find the unique normals of the mesh and stores them in a member variable.
+ * Also stores the indices into that vector with for each loop.
*/
- void store_normal_coords_and_indices(Vector<float3> &r_normal_coords);
+ void store_normal_coords_and_indices();
+ /* Get normals calculate by store_normal_coords_and_indices. */
+ const Vector<float3> &get_normal_coords() const
+ {
+ return normal_coords_;
+ }
/**
* Calculate a polygon's polygon/loop normal indices.
* \param poly_index Index of the polygon to calculate indices for.
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
index 48136dad5f7..3637a3f3c5f 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
@@ -193,8 +193,8 @@ static void store_bsdf_properties(const nodes::NodeRef *bsdf_node,
copy_property_from_node(SOCK_FLOAT, bnode, "Roughness", {&roughness, 1});
}
/* Empirical approximation. Importer should use the inverse of this method. */
- float spec_exponent = (1.0f - roughness) * 30;
- spec_exponent *= spec_exponent;
+ float spec_exponent = (1.0f - roughness);
+ spec_exponent *= spec_exponent * 1000.0f;
float specular = material->spec;
if (bnode) {
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc
index ec690115115..8a6d3b4b93a 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc
@@ -102,4 +102,10 @@ int OBJCurve::get_nurbs_degree(const int spline_index) const
return nurb->orderu - 1;
}
+short OBJCurve::get_nurbs_flagu(const int spline_index) const
+{
+ const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index));
+ return nurb->flagu;
+}
+
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh
index 0c71c3cc09d..d831afec0a0 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh
@@ -61,6 +61,10 @@ class OBJCurve : NonCopyable {
* Get the degree of the NURBS spline at the given index.
*/
int get_nurbs_degree(int spline_index) const;
+ /**
+ * Get the U flags (CU_NURB_*) of the NURBS spline at the given index.
+ */
+ short get_nurbs_flagu(int spline_index) const;
private:
/**
diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
index 595e6aaf4f2..187f50277f1 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
@@ -25,6 +25,7 @@
#include "BKE_scene.h"
#include "BLI_path_util.h"
+#include "BLI_task.hh"
#include "BLI_vector.hh"
#include "DEG_depsgraph_query.h"
@@ -91,28 +92,29 @@ filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_par
{
Vector<std::unique_ptr<OBJMesh>> r_exportable_meshes;
Vector<std::unique_ptr<OBJCurve>> r_exportable_nurbs;
- const ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
- LISTBASE_FOREACH (const Base *, base, &view_layer->object_bases) {
- Object *object_in_layer = base->object;
- if (export_params.export_selected_objects && !(object_in_layer->base_flag & BASE_SELECTED)) {
+ const int deg_objects_visibility_flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
+ DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET |
+ DEG_ITER_OBJECT_FLAG_VISIBLE |
+ DEG_ITER_OBJECT_FLAG_DUPLI;
+ DEG_OBJECT_ITER_BEGIN (depsgraph, object, deg_objects_visibility_flags) {
+ if (export_params.export_selected_objects && !(object->base_flag & BASE_SELECTED)) {
continue;
}
- switch (object_in_layer->type) {
+ switch (object->type) {
case OB_SURF:
/* Export in mesh form: vertices and polygons. */
ATTR_FALLTHROUGH;
case OB_MESH:
- r_exportable_meshes.append(
- std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
+ r_exportable_meshes.append(std::make_unique<OBJMesh>(depsgraph, export_params, object));
break;
case OB_CURVE: {
- Curve *curve = static_cast<Curve *>(object_in_layer->data);
+ Curve *curve = static_cast<Curve *>(object->data);
Nurb *nurb{static_cast<Nurb *>(curve->nurb.first)};
if (!nurb) {
/* An empty curve. Not yet supported to export these as meshes. */
if (export_params.export_curves_as_nurbs) {
r_exportable_nurbs.append(
- std::make_unique<OBJCurve>(depsgraph, export_params, object_in_layer));
+ std::make_unique<OBJCurve>(depsgraph, export_params, object));
}
break;
}
@@ -121,18 +123,18 @@ filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_par
if (export_params.export_curves_as_nurbs) {
/* Export in parameter form: control points. */
r_exportable_nurbs.append(
- std::make_unique<OBJCurve>(depsgraph, export_params, object_in_layer));
+ std::make_unique<OBJCurve>(depsgraph, export_params, object));
}
else {
/* Export in mesh form: edges and vertices. */
r_exportable_meshes.append(
- std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
+ std::make_unique<OBJMesh>(depsgraph, export_params, object));
}
break;
case CU_BEZIER:
/* Always export in mesh form: edges and vertices. */
r_exportable_meshes.append(
- std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
+ std::make_unique<OBJMesh>(depsgraph, export_params, object));
break;
default:
/* Other curve types are not supported. */
@@ -145,6 +147,7 @@ filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_par
break;
}
}
+ DEG_OBJECT_ITER_END;
return {std::move(r_exportable_meshes), std::move(r_exportable_nurbs)};
}
@@ -153,44 +156,97 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me
MTLWriter *mtl_writer,
const OBJExportParams &export_params)
{
+ /* Parallelization is over meshes/objects, which means
+ * we have to have the output text buffer for each object,
+ * and write them all into the file at the end. */
+ size_t count = exportable_as_mesh.size();
+ std::vector<FormatHandler<eFileType::OBJ>> buffers(count);
+
+ /* Serial: gather material indices, ensure normals & edges. */
+ Vector<Vector<int>> mtlindices;
if (mtl_writer) {
obj_writer.write_mtllib_name(mtl_writer->mtl_file_path());
+ mtlindices.reserve(count);
}
-
- /* Smooth groups and UV vertex indices may make huge memory allocations, so they should be freed
- * right after they're written, instead of waiting for #blender::Vector to clean them up after
- * all the objects are exported. */
for (auto &obj_mesh : exportable_as_mesh) {
- obj_writer.write_object_name(*obj_mesh);
- obj_writer.write_vertex_coords(*obj_mesh);
- Vector<int> obj_mtlindices;
+ OBJMesh &obj = *obj_mesh;
+ if (mtl_writer) {
+ mtlindices.append(mtl_writer->add_materials(obj));
+ }
+ if (export_params.export_normals) {
+ obj.ensure_mesh_normals();
+ }
+ obj.ensure_mesh_edges();
+ }
- if (obj_mesh->tot_polygons() > 0) {
- if (export_params.export_smooth_groups) {
- obj_mesh->calc_smooth_groups(export_params.smooth_groups_bitflags);
- }
+ /* Parallel over meshes: store normal coords & indices, uv coords and indices. */
+ blender::threading::parallel_for(IndexRange(count), 1, [&](IndexRange range) {
+ for (const int i : range) {
+ OBJMesh &obj = *exportable_as_mesh[i];
if (export_params.export_normals) {
- obj_writer.write_poly_normals(*obj_mesh);
+ obj.store_normal_coords_and_indices();
}
if (export_params.export_uv) {
- obj_writer.write_uv_coords(*obj_mesh);
- }
- if (mtl_writer) {
- obj_mtlindices = mtl_writer->add_materials(*obj_mesh);
+ obj.store_uv_coords_and_indices();
}
- /* This function takes a 0-indexed slot index for the obj_mesh object and
- * returns the material name that we are using in the .obj file for it. */
- std::function<const char *(int)> matname_fn = [&](int s) -> const char * {
- if (!mtl_writer || s < 0 || s >= obj_mtlindices.size()) {
- return nullptr;
+ }
+ });
+
+ /* Serial: calculate index offsets; these are sequentially added
+ * over all meshes, and requite normal/uv indices to be calculated. */
+ Vector<IndexOffsets> index_offsets;
+ index_offsets.reserve(count);
+ IndexOffsets offsets{0, 0, 0};
+ for (auto &obj_mesh : exportable_as_mesh) {
+ OBJMesh &obj = *obj_mesh;
+ index_offsets.append(offsets);
+ offsets.vertex_offset += obj.tot_vertices();
+ offsets.uv_vertex_offset += obj.tot_uv_vertices();
+ offsets.normal_offset += obj.tot_normal_indices();
+ }
+
+ /* Parallel over meshes: main result writing. */
+ blender::threading::parallel_for(IndexRange(count), 1, [&](IndexRange range) {
+ for (const int i : range) {
+ OBJMesh &obj = *exportable_as_mesh[i];
+ auto &fh = buffers[i];
+
+ obj_writer.write_object_name(fh, obj);
+ obj_writer.write_vertex_coords(fh, obj);
+
+ if (obj.tot_polygons() > 0) {
+ if (export_params.export_smooth_groups) {
+ obj.calc_smooth_groups(export_params.smooth_groups_bitflags);
+ }
+ if (export_params.export_normals) {
+ obj_writer.write_poly_normals(fh, obj);
}
- return mtl_writer->mtlmaterial_name(obj_mtlindices[s]);
- };
- obj_writer.write_poly_elements(*obj_mesh, matname_fn);
+ if (export_params.export_uv) {
+ obj_writer.write_uv_coords(fh, obj);
+ }
+ /* This function takes a 0-indexed slot index for the obj_mesh object and
+ * returns the material name that we are using in the .obj file for it. */
+ const auto *obj_mtlindices = mtlindices.is_empty() ? nullptr : &mtlindices[i];
+ std::function<const char *(int)> matname_fn = [&](int s) -> const char * {
+ if (!obj_mtlindices || s < 0 || s >= obj_mtlindices->size()) {
+ return nullptr;
+ }
+ return mtl_writer->mtlmaterial_name((*obj_mtlindices)[s]);
+ };
+ obj_writer.write_poly_elements(fh, index_offsets[i], obj, matname_fn);
+ }
+ obj_writer.write_edges_indices(fh, index_offsets[i], obj);
+
+ /* Nothing will need this object's data after this point, release
+ * various arrays here. */
+ obj.clear();
}
- obj_writer.write_edges_indices(*obj_mesh);
+ });
- obj_writer.update_index_offsets(*obj_mesh);
+ /* Write all the object text buffers into the output file. */
+ FILE *f = obj_writer.get_outfile();
+ for (auto &b : buffers) {
+ b.write_to_file(f);
}
}
@@ -200,11 +256,13 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me
static void write_nurbs_curve_objects(const Vector<std::unique_ptr<OBJCurve>> &exportable_as_nurbs,
const OBJWriter &obj_writer)
{
+ FormatHandler<eFileType::OBJ> fh;
/* #OBJCurve doesn't have any dynamically allocated memory, so it's fine
* to wait for #blender::Vector to clean the objects up. */
for (const std::unique_ptr<OBJCurve> &obj_curve : exportable_as_nurbs) {
- obj_writer.write_nurbs_curve(*obj_curve);
+ obj_writer.write_nurbs_curve(fh, *obj_curve);
}
+ fh.write_to_file(obj_writer.get_outfile());
}
void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, const char *filepath)
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
index 92d478c20a1..58329f63650 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
@@ -1,10 +1,8 @@
/* Apache License, Version 2.0 */
-#include <fstream>
#include <gtest/gtest.h>
#include <ios>
#include <memory>
-#include <sstream>
#include <string>
#include <system_error>
@@ -60,7 +58,7 @@ TEST_F(obj_exporter_test, filter_objects_curves_as_mesh)
return;
}
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
- EXPECT_EQ(objmeshes.size(), 17);
+ EXPECT_EQ(objmeshes.size(), 19);
EXPECT_EQ(objcurves.size(), 0);
}
@@ -73,7 +71,7 @@ TEST_F(obj_exporter_test, filter_objects_curves_as_nurbs)
}
_export.params.export_curves_as_nurbs = true;
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
- EXPECT_EQ(objmeshes.size(), 16);
+ EXPECT_EQ(objmeshes.size(), 18);
EXPECT_EQ(objcurves.size(), 2);
}
@@ -185,15 +183,21 @@ static std::unique_ptr<OBJWriter> init_writer(const OBJExportParams &params,
}
}
-/* The following is relative to BKE_tempdir_base. */
-const char *const temp_file_path = "output.OBJ";
+/* The following is relative to BKE_tempdir_base.
+ * Use Latin Capital Letter A with Ogonek, Cyrillic Capital Letter Zhe
+ * at the end, to test I/O on non-English file names. */
+const char *const temp_file_path = "output\xc4\x84\xd0\x96.OBJ";
static std::string read_temp_file_in_string(const std::string &file_path)
{
- std::ifstream temp_stream(file_path);
- std::ostringstream input_ss;
- input_ss << temp_stream.rdbuf();
- return input_ss.str();
+ std::string res;
+ size_t buffer_len;
+ void *buffer = BLI_file_read_text_as_mem(file_path.c_str(), 0, &buffer_len);
+ if (buffer != nullptr) {
+ res.assign((const char *)buffer, buffer_len);
+ MEM_freeN(buffer);
+ }
+ return res;
}
TEST(obj_exporter_writer, header)
@@ -234,6 +238,38 @@ TEST(obj_exporter_writer, mtllib)
BLI_delete(out_file_path.c_str(), false, false);
}
+TEST(obj_exporter_writer, format_handler_buffer_chunking)
+{
+ /* Use a tiny buffer chunk size, so that the test below ends up creating several blocks. */
+ FormatHandler<eFileType::OBJ, 16, 8> h;
+ h.write<eOBJSyntaxElement::object_name>("abc");
+ h.write<eOBJSyntaxElement::object_name>("abcd");
+ h.write<eOBJSyntaxElement::object_name>("abcde");
+ h.write<eOBJSyntaxElement::object_name>("abcdef");
+ h.write<eOBJSyntaxElement::object_name>("012345678901234567890123456789abcd");
+ h.write<eOBJSyntaxElement::object_name>("123");
+ h.write<eOBJSyntaxElement::curve_element_begin>();
+ h.write<eOBJSyntaxElement::new_line>();
+ h.write<eOBJSyntaxElement::nurbs_parameter_begin>();
+ h.write<eOBJSyntaxElement::new_line>();
+
+ size_t got_blocks = h.get_block_count();
+ ASSERT_EQ(got_blocks, 7);
+
+ std::string got_string = h.get_as_string();
+ using namespace std::string_literals;
+ const char *expected = R"(o abc
+o abcd
+o abcde
+o abcdef
+o 012345678901234567890123456789abcd
+o 123
+curv 0.0 1.0
+parm u 0.0
+)";
+ ASSERT_EQ(got_string, expected);
+}
+
/* Return true if string #a and string #b are equal after their first newline. */
static bool strings_equal_after_first_lines(const std::string &a, const std::string &b)
{
@@ -309,8 +345,14 @@ class obj_exporter_regression_test : public obj_exporter_test {
std::string output_mtl_str = read_temp_file_in_string(out_mtl_file_path);
std::string golden_mtl_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_mtl;
std::string golden_mtl_str = read_temp_file_in_string(golden_mtl_file_path);
- ASSERT_TRUE(strings_equal_after_first_lines(output_mtl_str, golden_mtl_str));
- BLI_delete(out_mtl_file_path.c_str(), false, false);
+ are_equal = strings_equal_after_first_lines(output_mtl_str, golden_mtl_str);
+ if (save_failing_test_output && !are_equal) {
+ printf("failing test output in %s\n", out_mtl_file_path.c_str());
+ }
+ ASSERT_TRUE(are_equal);
+ if (!save_failing_test_output || are_equal) {
+ BLI_delete(out_mtl_file_path.c_str(), false, false);
+ }
}
}
};
@@ -374,6 +416,19 @@ TEST_F(obj_exporter_regression_test, nurbs_as_nurbs)
"io_tests/blend_geometry/nurbs.blend", "io_tests/obj/nurbs.obj", "", _export.params);
}
+TEST_F(obj_exporter_regression_test, nurbs_curves_as_nurbs)
+{
+ OBJExportParamsDefault _export;
+ _export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
+ _export.params.up_axis = OBJ_AXIS_Z_UP;
+ _export.params.export_materials = false;
+ _export.params.export_curves_as_nurbs = true;
+ compare_obj_export_to_golden("io_tests/blend_geometry/nurbs_curves.blend",
+ "io_tests/obj/nurbs_curves.obj",
+ "",
+ _export.params);
+}
+
TEST_F(obj_exporter_regression_test, nurbs_as_mesh)
{
OBJExportParamsDefault _export;
@@ -398,6 +453,18 @@ TEST_F(obj_exporter_regression_test, cube_all_data_triangulated)
_export.params);
}
+TEST_F(obj_exporter_regression_test, cube_normal_edit)
+{
+ OBJExportParamsDefault _export;
+ _export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
+ _export.params.up_axis = OBJ_AXIS_Z_UP;
+ _export.params.export_materials = false;
+ compare_obj_export_to_golden("io_tests/blend_geometry/cube_normal_edit.blend",
+ "io_tests/obj/cube_normal_edit.obj",
+ "",
+ _export.params);
+}
+
TEST_F(obj_exporter_regression_test, suzanne_all_data)
{
OBJExportParamsDefault _export;
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
index 4baf1df51f5..c101081ca54 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
@@ -138,7 +138,7 @@ const std::map<std::string, std::unique_ptr<NurbsObject>> all_nurbs_truth = []()
"NurbsCircle",
std::make_unique<NurbsObject>(
"NurbsCircle", coordinates_NurbsCircle, std::vector<int>{3}, std::vector<int>{11}));
- /* This is actually an Object containing a NurbsPath and a NurbsCurve spline. */
+ /* This is actually an Object containing a NurbsPath and a NurbsCurve spline. */
all_nurbs.emplace("NurbsPathCurve",
std::make_unique<NurbsObject>("NurbsPathCurve",
coordinates_NurbsPathCurve,
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 060b55ffe5c..cfee35e147a 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -310,6 +310,13 @@ typedef struct IDOverrideLibrary {
/** List of IDOverrideLibraryProperty structs. */
ListBase properties;
+ /** Override hierarchy root ID. Usually the actual root of the hierarchy, but not always
+ * in degenerated cases.
+ *
+ * All liboverrides of a same hierarchy (e.g. a character collection) share the same root.
+ */
+ struct ID *hierarchy_root;
+
/* Read/write data. */
/* Temp ID storing extra override data (used for differential operations only currently).
* Always NULL outside of read/write context. */
@@ -317,8 +324,6 @@ typedef struct IDOverrideLibrary {
IDOverrideLibraryRuntime *runtime;
- void *_pad_0;
-
unsigned int flag;
char _pad_1[4];
} IDOverrideLibrary;
@@ -534,12 +539,14 @@ typedef struct PreviewImage {
#define ID_IS_LINKED(_id) (((const ID *)(_id))->lib != NULL)
-/* Note that this is a fairly high-level check, should be used at user interaction level, not in
+/* Note that these are fairly high-level checks, should be used at user interaction level, not in
* BKE_library_override typically (especially due to the check on LIB_TAG_EXTERN). */
-#define ID_IS_OVERRIDABLE_LIBRARY(_id) \
- (ID_IS_LINKED(_id) && !ID_MISSING(_id) && (((const ID *)(_id))->tag & LIB_TAG_EXTERN) != 0 && \
+#define ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(_id) \
+ (ID_IS_LINKED(_id) && !ID_MISSING(_id) && \
(BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0 && \
!ELEM(GS(((ID *)(_id))->name), ID_SCE))
+#define ID_IS_OVERRIDABLE_LIBRARY(_id) \
+ (ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY((_id)) && (((const ID *)(_id))->tag & LIB_TAG_EXTERN) != 0)
/* NOTE: The three checks below do not take into account whether given ID is linked or not (when
* chaining overrides over several libraries). User must ensure the ID is not linked itself
@@ -798,7 +805,9 @@ typedef enum IDRecalcFlag {
* Use this tag with a scene ID which owns the sequences. */
ID_RECALC_SEQUENCER_STRIPS = (1 << 14),
- ID_RECALC_AUDIO_SEEK = (1 << 15),
+ /* Runs on frame-change (used for seeking audio too). */
+ ID_RECALC_FRAME_CHANGE = (1 << 15),
+
ID_RECALC_AUDIO_FPS = (1 << 16),
ID_RECALC_AUDIO_VOLUME = (1 << 17),
ID_RECALC_AUDIO_MUTE = (1 << 18),
@@ -886,7 +895,7 @@ typedef enum IDRecalcFlag {
#define FILTER_ID_CF (1ULL << 28)
#define FILTER_ID_WS (1ULL << 29)
#define FILTER_ID_LP (1ULL << 31)
-#define FILTER_ID_HA (1ULL << 32)
+#define FILTER_ID_CV (1ULL << 32)
#define FILTER_ID_PT (1ULL << 33)
#define FILTER_ID_VO (1ULL << 34)
#define FILTER_ID_SIM (1ULL << 35)
@@ -897,7 +906,7 @@ typedef enum IDRecalcFlag {
FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | \
FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | \
FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | FILTER_ID_WS | \
- FILTER_ID_LP | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM)
+ FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM)
/**
* This enum defines the index assigned to each type of IDs in the array returned by
@@ -980,7 +989,7 @@ enum {
INDEX_ID_ME,
INDEX_ID_CU,
INDEX_ID_MB,
- INDEX_ID_HA,
+ INDEX_ID_CV,
INDEX_ID_PT,
INDEX_ID_VO,
INDEX_ID_LT,
diff --git a/source/blender/makesdna/DNA_ID_enums.h b/source/blender/makesdna/DNA_ID_enums.h
index 45faf9e7f57..839c1e8933f 100644
--- a/source/blender/makesdna/DNA_ID_enums.h
+++ b/source/blender/makesdna/DNA_ID_enums.h
@@ -90,7 +90,7 @@ typedef enum ID_Type {
ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */
ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */
ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */
- ID_HA = MAKE_ID2('H', 'A'), /* Hair */
+ ID_CV = MAKE_ID2('C', 'V'), /* Curves */
ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */
ID_VO = MAKE_ID2('V', 'O'), /* Volume */
ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 82b20483902..1ca724b7108 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -478,9 +478,6 @@ typedef struct bPose {
short flag;
char _pad[2];
- /** Proxy layer: copy from armature, gets synced. */
- unsigned int proxy_layer;
- char _pad1[4];
/** Local action time of this pose. */
float ctime;
@@ -503,8 +500,6 @@ typedef struct bPose {
/** Settings for visualization of bone animation. */
bAnimVizSettings avs;
- /** Proxy active bone name, MAXBONENAME. */
- char proxy_act_bone[64];
} bPose;
/* Pose->flag */
diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h
index 566ffd19669..1005b5186aa 100644
--- a/source/blender/makesdna/DNA_armature_types.h
+++ b/source/blender/makesdna/DNA_armature_types.h
@@ -77,7 +77,7 @@ typedef struct Bone {
/** dist, weight: for non-deformgroup deforms. */
float dist, weight;
/**
- * The width for block bones.
+ * The width for block bones. The final X/Z bone widths are double these values.
*
* \note keep in this order for transform code which stores a pointer to `xwidth`,
* accessing length and `zwidth` as offsets.
diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h
index 72b2db89cef..2f41389f2c6 100644
--- a/source/blender/makesdna/DNA_brush_enums.h
+++ b/source/blender/makesdna/DNA_brush_enums.h
@@ -522,7 +522,7 @@ typedef enum eBrushUVSculptTool {
SCULPT_TOOL_SLIDE_RELAX, \
SCULPT_TOOL_MASK) == 0)
-/* ImagePaintSettings.tool */
+/** #ImagePaintSettings.tool */
typedef enum eBrushImagePaintTool {
PAINT_TOOL_DRAW = 0,
PAINT_TOOL_SOFTEN = 1,
@@ -532,6 +532,9 @@ typedef enum eBrushImagePaintTool {
PAINT_TOOL_MASK = 5,
} eBrushImagePaintTool;
+/* The enums here should be kept in sync with the weight paint tool.
+ * This is because #smooth_brush_toggle_on and #smooth_brush_toggle_off
+ * assumes that the blur brush has the same enum value. */
typedef enum eBrushVertexPaintTool {
VPAINT_TOOL_DRAW = 0,
VPAINT_TOOL_BLUR = 1,
@@ -539,6 +542,7 @@ typedef enum eBrushVertexPaintTool {
VPAINT_TOOL_SMEAR = 3,
} eBrushVertexPaintTool;
+/* See #eBrushVertexPaintTool when changing this definition. */
typedef enum eBrushWeightPaintTool {
WPAINT_TOOL_DRAW = 0,
WPAINT_TOOL_BLUR = 1,
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index d587bd8082b..b7ba05f5879 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * Constraint DNA data
*/
/** \file
* \ingroup DNA
+ * Constraint DNA data.
*/
#pragma once
@@ -714,8 +714,6 @@ typedef enum eBConstraint_Flags {
CONSTRAINT_SPACEONCE = (1 << 6),
/* influence ipo is on constraint itself, not in action channel */
CONSTRAINT_OWN_IPO = (1 << 7),
- /* indicates that constraint was added locally (i.e. didn't come from the proxy-lib) */
- CONSTRAINT_PROXY_LOCAL = (1 << 8),
/* indicates that constraint is temporarily disabled (only used in GE) */
CONSTRAINT_OFF = (1 << 9),
/* use bbone curve shape when calculating headtail values (also used by dependency graph!) */
diff --git a/source/blender/makesdna/DNA_hair_defaults.h b/source/blender/makesdna/DNA_curves_defaults.h
index 095e4fdf583..66c7a1bd71b 100644
--- a/source/blender/makesdna/DNA_hair_defaults.h
+++ b/source/blender/makesdna/DNA_curves_defaults.h
@@ -24,10 +24,10 @@
/* clang-format off */
/* -------------------------------------------------------------------- */
-/** \name Hair Struct
+/** \name Curves Struct
* \{ */
-#define _DNA_DEFAULT_Hair \
+#define _DNA_DEFAULT_Curves \
{ \
.flag = 0, \
}
diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h
new file mode 100644
index 00000000000..c7f31557e48
--- /dev/null
+++ b/source/blender/makesdna/DNA_curves_types.h
@@ -0,0 +1,108 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup DNA
+ */
+
+#pragma once
+
+#include "DNA_ID.h"
+#include "DNA_customdata_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A reusable data structure for geometry consisting of many curves. All control point data is
+ * stored contiguously for better efficiency. Data for each curve is stored as a slice of the
+ * main #point_data array.
+ *
+ * The data structure is meant to be embedded in other data-blocks to allow reusing
+ * curve-processing algorithms for multiple Blender data-block types.
+ */
+typedef struct CurvesGeometry {
+ /**
+ * A runtime pointer to the "position" attribute data.
+ * \note This data is owned by #point_data.
+ */
+ float (*position)[3];
+ /**
+ * A runtime pointer to the "radius" attribute data.
+ * \note This data is owned by #point_data.
+ */
+ float *radius;
+
+ /**
+ * The start index of each curve in the point data. The size of each curve can be calculated by
+ * subtracting the offset from the next offset. That is valid even for the last curve because
+ * this array is allocated with a length one larger than the number of splines.
+ *
+ * \note This is *not* stored in #CustomData because its size is one larger than #curve_data.
+ */
+ int *offsets;
+
+ /**
+ * All attributes stored on control points (#ATTR_DOMAIN_POINT).
+ */
+ CustomData point_data;
+
+ /**
+ * All attributes stored on curves (#ATTR_DOMAIN_CURVE).
+ */
+ CustomData curve_data;
+
+ /**
+ * The total number of control points in all curves.
+ */
+ int point_size;
+ /**
+ * The number of curves in the data-block.
+ */
+ int curve_size;
+} CurvesGeometry;
+
+typedef struct Curves {
+ ID id;
+ /* Animation data (must be immediately after id). */
+ struct AnimData *adt;
+
+ CurvesGeometry geometry;
+
+ int flag;
+ int attributes_active_index;
+
+ /* Materials. */
+ struct Material **mat;
+ short totcol;
+ short _pad2[3];
+
+ /* Draw Cache. */
+ void *batch_cache;
+} Curves;
+
+/* Curves.flag */
+enum {
+ HA_DS_EXPAND = (1 << 0),
+};
+
+/* Only one material supported currently. */
+#define CURVES_MATERIAL_NR 1
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index e1ec061f2f9..9c34d78c944 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -139,10 +139,12 @@ typedef enum CustomDataType {
CD_SHAPE_KEYINDEX = 27,
CD_SHAPEKEY = 28,
CD_BWEIGHT = 29,
- /* Usage of CD_CREASE depends on where on the Mesh the layer is added:
- * - for vertex creasing, this is persistent data accross all modes and is stored in the file,
- * - for egde creasing, it is runtime data which is only used in edit-mode before being copied to
- * MEdge when exiting edit-mode. */
+ /**
+ * Usage of #CD_CREASE depends on where on the Mesh the layer is added:
+ * - For vertex creasing, this is persistent data across all modes and is stored in the file.
+ * - For edge creasing, it is runtime data which is only used in edit-mode before being copied
+ * to #MEdge when exiting edit-mode.
+ */
CD_CREASE = 30,
CD_ORIGSPACE_MLOOP = 31,
CD_PREVIEW_MLOOPCOL = 32,
@@ -160,8 +162,8 @@ typedef enum CustomDataType {
/* CD_LOCATION = 43, */ /* UNUSED */
/* CD_RADIUS = 44, */ /* UNUSED */
- CD_HAIRCURVE = 45,
- CD_HAIRMAPPING = 46,
+ CD_PROP_INT8 = 45,
+ /* CD_HAIRMAPPING = 46, */ /* UNUSED, can be reused. */
CD_PROP_COLOR = 47,
CD_PROP_FLOAT3 = 48,
@@ -221,6 +223,7 @@ typedef enum CustomDataType {
#define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3)
#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
+#define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8)
#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH)
@@ -233,7 +236,8 @@ typedef enum CustomDataType {
/* All generic attributes. */
#define CD_MASK_PROP_ALL \
(CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | \
- CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL)
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL | \
+ CD_MASK_PROP_INT8)
typedef struct CustomData_MeshMasks {
uint64_t vmask;
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index 88eb164c2b4..c51a615bfb6 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -315,7 +315,8 @@
.opacity = 1.0f, \
.flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \
.crease_threshold = DEG2RAD(140.0f), \
- .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | LRT_USE_CREASE_ON_SHARP_EDGES, \
+ .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | \
+ LRT_USE_CREASE_ON_SHARP_EDGES | LRT_FILTER_FACE_MARK_KEEP_CONTOUR, \
.angle_splitting_threshold = DEG2RAD(60.0f), \
.chaining_image_threshold = 0.001f, \
.chain_smooth_tolerance = 0.2f,\
diff --git a/source/blender/makesdna/DNA_hair_types.h b/source/blender/makesdna/DNA_hair_types.h
deleted file mode 100644
index 2e819b32033..00000000000
--- a/source/blender/makesdna/DNA_hair_types.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup DNA
- */
-
-#pragma once
-
-#include "DNA_ID.h"
-#include "DNA_customdata_types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct HairCurve {
- /* Index of first point of hair curve. */
- int firstpoint;
- /* Number of points in hair curve, must be 2 or higher. */
- int numpoints;
-} HairCurve;
-
-/* Hair attachment to a mesh.
- * TODO: attach to tessellated triangles or polygons?
- * TODO: what type of interpolation to use for uv? */
-typedef struct HairMapping {
- float uv[2];
- int poly;
-} HairMapping;
-
-typedef struct Hair {
- ID id;
- struct AnimData *adt; /* animation data (must be immediately after id) */
-
- int flag;
- int _pad1[1];
-
- /* Geometry */
- float (*co)[3];
- float *radius;
- struct HairCurve *curves;
- struct HairMaping *mapping;
- int totpoint;
- int totcurve;
-
- /* Custom Data */
- struct CustomData pdata;
- struct CustomData cdata;
- int attributes_active_index;
- int _pad3;
-
- /* Material */
- struct Material **mat;
- short totcol;
- short _pad2[3];
-
- /* Draw Cache */
- void *batch_cache;
-} Hair;
-
-/* Hair.flag */
-enum {
- HA_DS_EXPAND = (1 << 0),
-};
-
-/* Only one material supported currently. */
-#define HAIR_MATERIAL_NR 1
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h
index 64c8fd3e3a9..7a789227128 100644
--- a/source/blender/makesdna/DNA_image_types.h
+++ b/source/blender/makesdna/DNA_image_types.h
@@ -142,10 +142,20 @@ typedef enum eImageTextureResolution {
IMA_TEXTURE_RESOLUTION_LEN
} eImageTextureResolution;
+/* Defined in BKE_image.h. */
+struct PartialUpdateRegister;
+struct PartialUpdateUser;
+
typedef struct Image_Runtime {
/* Mutex used to guarantee thread-safe access to the cached ImBuf of the corresponding image ID.
*/
void *cache_mutex;
+
+ /** \brief Register containing partial updates. */
+ struct PartialUpdateRegister *partial_update_register;
+ /** \brief Partial update user for GPUTextures stored inside the Image. */
+ struct PartialUpdateUser *partial_update_user;
+
} Image_Runtime;
typedef struct Image {
@@ -171,8 +181,6 @@ typedef struct Image {
int lastframe;
/* GPU texture flag. */
- /* Contains `ImagePartialRefresh`. */
- ListBase gpu_refresh_areas;
int gpuframenr;
short gpuflag;
short gpu_pass;
@@ -247,15 +255,13 @@ enum {
enum {
/** GPU texture needs to be refreshed. */
IMA_GPU_REFRESH = (1 << 0),
- /** GPU texture needs to be partially refreshed. */
- IMA_GPU_PARTIAL_REFRESH = (1 << 1),
/** All mipmap levels in OpenGL texture set? */
- IMA_GPU_MIPMAP_COMPLETE = (1 << 2),
+ IMA_GPU_MIPMAP_COMPLETE = (1 << 1),
/* Reuse the max resolution textures as they fit in the limited scale. */
- IMA_GPU_REUSE_MAX_RESOLUTION = (1 << 3),
+ IMA_GPU_REUSE_MAX_RESOLUTION = (1 << 2),
/* Has any limited scale textures been allocated.
* Adds additional checks to reuse max resolution images when they fit inside limited scale. */
- IMA_GPU_HAS_LIMITED_SCALE_TEXTURES = (1 << 4),
+ IMA_GPU_HAS_LIMITED_SCALE_TEXTURES = (1 << 3),
};
/* Image.source, where the image comes from */
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
index d4440592a00..3e77d566d27 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -50,7 +50,10 @@ typedef enum eLineartMainFlags {
LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15),
LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16),
LRT_USE_CUSTOM_CAMERA = (1 << 17),
+ LRT_FILTER_FACE_MARK_KEEP_CONTOUR = (1 << 18),
+ LRT_USE_BACK_FACE_CULLING = (1 << 19),
LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20),
+ LRT_CHAIN_PRESERVE_DETAILS = (1 << 22),
} eLineartMainFlags;
typedef enum eLineartEdgeFlag {
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index 22c523901c0..f2493bd5b85 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -48,7 +48,6 @@ typedef struct MVert {
/** #MVert.flag */
enum {
/* SELECT = (1 << 0), */
- ME_VERT_TMP_TAG = (1 << 2),
ME_HIDE = (1 << 4),
ME_VERT_FACEDOT = (1 << 5),
/* ME_VERT_MERGED = (1 << 6), */
@@ -266,6 +265,9 @@ typedef struct MStringProperty {
typedef struct MBoolProperty {
uint8_t b;
} MBoolProperty;
+typedef struct MInt8Property {
+ int8_t i;
+} MInt8Property;
/** \} */
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 1d0796bda8b..8e38d52a4d7 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -128,15 +128,11 @@ typedef struct ModifierData {
char *error;
- /** Pointer to a #ModifierData in the original domain. */
- struct ModifierData *orig_modifier_data;
-
/** Runtime field which contains unique identifier of the modifier. */
SessionUUID session_uuid;
/** Runtime field which contains runtime data which is specific to a modifier type. */
void *runtime;
- void *_pad1;
} ModifierData;
typedef enum {
@@ -1012,7 +1008,8 @@ typedef struct MeshDeformModifierData {
float *bindcos;
/* runtime */
- void (*bindfunc)(struct MeshDeformModifierData *mmd,
+ void (*bindfunc)(struct Object *object,
+ struct MeshDeformModifierData *mmd,
struct Mesh *cagemesh,
float *vertexcos,
int totvert,
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 114e350b582..fd77e8b9f1d 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -38,6 +38,7 @@ struct ID;
struct Image;
struct ListBase;
struct Material;
+struct PreviewImage;
struct Tex;
struct bGPdata;
struct bNodeInstanceHash;
@@ -46,7 +47,6 @@ struct bNodePreview;
struct bNodeTreeExec;
struct bNodeType;
struct uiBlock;
-struct PreviewImage;
#define NODE_MAXSTR 64
@@ -514,8 +514,10 @@ typedef struct bNodeTree {
/** Information about how inputs and outputs of the node group interact with fields. */
FieldInferencingInterfaceHandle *field_inferencing_interface;
- /** Set init on fileread. */
- int type, init;
+ int type;
+
+ char _pad1[4];
+
/**
* Sockets in groups have unique identifiers, adding new sockets always
* will increase this counter.
@@ -599,9 +601,6 @@ typedef struct bNodeTree {
#define NTREE_TEXTURE 2
#define NTREE_GEOMETRY 3
-/** #NodeTree.init, flag */
-#define NTREE_TYPE_INIT 1
-
/** #NodeTree.flag */
#define NTREE_DS_EXPAND (1 << 0) /* for animation editors */
#define NTREE_COM_OPENCL (1 << 1) /* use opencl */
@@ -1385,6 +1384,11 @@ typedef struct NodeGeometryPointTranslate {
uint8_t input_type;
} NodeGeometryPointTranslate;
+typedef struct NodeGeometryExtrudeMesh {
+ /* GeometryNodeExtrudeMeshMode */
+ uint8_t mode;
+} NodeGeometryExtrudeMesh;
+
typedef struct NodeGeometryObjectInfo {
/* GeometryNodeTransformSpace. */
uint8_t transform_space;
@@ -1503,6 +1507,11 @@ typedef struct NodeGeometryCurveSelectHandles {
uint8_t mode;
} NodeGeometryCurveSelectHandles;
+typedef struct NodeGeometryCurvePrimitiveArc {
+ /* GeometryNodeCurvePrimitiveArcMode. */
+ uint8_t mode;
+} NodeGeometryCurvePrimitiveArc;
+
typedef struct NodeGeometryCurvePrimitiveLine {
/* GeometryNodeCurvePrimitiveLineMode. */
uint8_t mode;
@@ -1606,7 +1615,8 @@ typedef struct NodeGeometryStringToCurves {
uint8_t align_x;
/* GeometryNodeStringToCurvesAlignYMode */
uint8_t align_y;
- char _pad[1];
+ /* GeometryNodeStringToCurvesPivotMode */
+ uint8_t pivot_mode;
} NodeGeometryStringToCurves;
typedef struct NodeGeometryDeleteGeometry {
@@ -1838,7 +1848,6 @@ enum {
/* math node clamp */
#define SHD_MATH_CLAMP 1
-/** Math node operations. */
typedef enum NodeMathOperation {
NODE_MATH_ADD = 0,
NODE_MATH_SUBTRACT = 1,
@@ -1882,7 +1891,6 @@ typedef enum NodeMathOperation {
NODE_MATH_SMOOTH_MAX = 39,
} NodeMathOperation;
-/** Vector Math node operations. */
typedef enum NodeVectorMathOperation {
NODE_VECTOR_MATH_ADD = 0,
NODE_VECTOR_MATH_SUBTRACT = 1,
@@ -1916,14 +1924,20 @@ typedef enum NodeVectorMathOperation {
NODE_VECTOR_MATH_MULTIPLY_ADD = 26,
} NodeVectorMathOperation;
-/** Boolean math node operations. */
-enum {
+typedef enum NodeBooleanMathOperation {
NODE_BOOLEAN_MATH_AND = 0,
NODE_BOOLEAN_MATH_OR = 1,
NODE_BOOLEAN_MATH_NOT = 2,
-};
-/** Float compare node operations. */
+ NODE_BOOLEAN_MATH_NAND = 3,
+ NODE_BOOLEAN_MATH_NOR = 4,
+ NODE_BOOLEAN_MATH_XNOR = 5,
+ NODE_BOOLEAN_MATH_XOR = 6,
+
+ NODE_BOOLEAN_MATH_IMPLY = 7,
+ NODE_BOOLEAN_MATH_NIMPLY = 8,
+} NodeBooleanMathOperation;
+
typedef enum NodeCompareMode {
NODE_COMPARE_MODE_ELEMENT = 0,
NODE_COMPARE_MODE_LENGTH = 1,
@@ -1941,10 +1955,8 @@ typedef enum NodeCompareOperation {
NODE_COMPARE_NOT_EQUAL = 5,
NODE_COMPARE_COLOR_BRIGHTER = 6,
NODE_COMPARE_COLOR_DARKER = 7,
-
} NodeCompareOperation;
-/** Float to Int node operations. */
typedef enum FloatToIntRoundingMode {
FN_NODE_FLOAT_TO_INT_ROUND = 0,
FN_NODE_FLOAT_TO_INT_FLOOR = 1,
@@ -2150,6 +2162,12 @@ typedef enum GeometryNodeDistributePointsOnFacesMode {
GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1,
} GeometryNodeDistributePointsOnFacesMode;
+typedef enum GeometryNodeExtrudeMeshMode {
+ GEO_NODE_EXTRUDE_MESH_VERTICES = 0,
+ GEO_NODE_EXTRUDE_MESH_EDGES = 1,
+ GEO_NODE_EXTRUDE_MESH_FACES = 2,
+} GeometryNodeExtrudeMeshMode;
+
typedef enum GeometryNodeRotatePointsType {
GEO_NODE_POINT_ROTATE_TYPE_EULER = 0,
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
@@ -2237,6 +2255,11 @@ typedef enum GeometryNodeMeshLineCountMode {
GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1,
} GeometryNodeMeshLineCountMode;
+typedef enum GeometryNodeCurvePrimitiveArcMode {
+ GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS = 0,
+ GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS = 1,
+} GeometryNodeCurvePrimitiveArcMode;
+
typedef enum GeometryNodeCurvePrimitiveLineMode {
GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS = 0,
GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION = 1
@@ -2321,6 +2344,16 @@ typedef enum GeometryNodeStringToCurvesAlignYMode {
GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM = 4,
} GeometryNodeStringToCurvesAlignYMode;
+typedef enum GeometryNodeStringToCurvesPivotMode {
+ GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_MIDPOINT = 0,
+ GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_LEFT = 1,
+ GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_CENTER = 2,
+ GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_RIGHT = 3,
+ GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT = 4,
+ GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_CENTER = 5,
+ GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_RIGHT = 6,
+} GeometryNodeStringToCurvesPivotMode;
+
typedef enum GeometryNodeDeleteGeometryMode {
GEO_NODE_DELETE_GEOMETRY_MODE_ALL = 0,
GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE = 1,
@@ -2331,6 +2364,11 @@ typedef enum GeometryNodeRealizeInstancesFlag {
GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR = (1 << 0),
} GeometryNodeRealizeInstancesFlag;
+typedef enum GeometryNodeScaleElementsMode {
+ GEO_NODE_SCALE_ELEMENTS_UNIFORM = 0,
+ GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS = 1,
+} GeometryNodeScaleElementsMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 5cce7ec5f67..ca8696d1326 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -181,6 +181,12 @@ typedef struct Object_Runtime {
*/
struct Mesh *mesh_deform_eval;
+ /* Evaluated mesh cage in edit mode. */
+ struct Mesh *editmesh_eval_cage;
+
+ /** Cached cage bounding box of `editmesh_eval_cage` for selection. */
+ struct BoundBox *editmesh_bb_cage;
+
/**
* Original grease pencil bGPdata pointer, before object->data was changed to point
* to gpd_eval.
@@ -210,6 +216,12 @@ typedef struct Object_Runtime {
unsigned short local_collections_bits;
short _pad2[3];
+
+ float (*crazyspace_deform_imats)[3][3];
+ float (*crazyspace_deform_cos)[3];
+ int crazyspace_num_verts;
+
+ int _pad3[3];
} Object_Runtime;
typedef struct ObjectLineArt {
@@ -251,9 +263,10 @@ typedef struct Object {
/** String describing subobject info, MAX_ID_NAME-2. */
char parsubstr[64];
struct Object *parent, *track;
- /* If `ob->proxy` (or proxy_group), this object is proxy for object `ob->proxy`. */
- /* proxy_from is set in target back to the proxy. */
- struct Object *proxy, *proxy_group, *proxy_from;
+ /* Proxy pointer are deprecated, only kept for conversion to liboverrides. */
+ struct Object *proxy DNA_DEPRECATED;
+ struct Object *proxy_group DNA_DEPRECATED;
+ struct Object *proxy_from DNA_DEPRECATED;
/** Old animation system, deprecated for 2.5. */
struct Ipo *ipo DNA_DEPRECATED;
/* struct Path *path; */
@@ -492,7 +505,7 @@ enum {
/** Grease Pencil object used in 3D view but not used for annotation in 2D. */
OB_GPENCIL = 26,
- OB_HAIR = 27,
+ OB_CURVES = 27,
OB_POINTCLOUD = 28,
@@ -507,7 +520,15 @@ enum {
(((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) >= OB_GPENCIL && (_type) <= OB_VOLUME))
/** Does the object have some render-able geometry (unlike empties, cameras, etc.). */
#define OB_TYPE_IS_GEOMETRY(_type) \
- (ELEM(_type, OB_MESH, OB_SURF, OB_FONT, OB_MBALL, OB_GPENCIL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME))
+ (ELEM(_type, \
+ OB_MESH, \
+ OB_SURF, \
+ OB_FONT, \
+ OB_MBALL, \
+ OB_GPENCIL, \
+ OB_CURVES, \
+ OB_POINTCLOUD, \
+ OB_VOLUME))
#define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL))
#define OB_TYPE_SUPPORT_EDITMODE(_type) \
(ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE))
@@ -529,7 +550,7 @@ enum {
ID_LT, \
ID_GD, \
ID_AR, \
- ID_HA, \
+ ID_CV, \
ID_PT, \
ID_VO))
@@ -544,7 +565,7 @@ enum {
case ID_LT: \
case ID_GD: \
case ID_AR: \
- case ID_HA: \
+ case ID_CV: \
case ID_PT: \
case ID_VO
diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h
index a50d0524998..71a5c0eb2e5 100644
--- a/source/blender/makesdna/DNA_outliner_types.h
+++ b/source/blender/makesdna/DNA_outliner_types.h
@@ -101,7 +101,7 @@ typedef enum eTreeStoreElemType {
TSE_DRIVER_BASE = 16, /* NO ID */
/* TSE_DRIVER = 17, */ /* UNUSED */
- TSE_PROXY = 18,
+ /* TSE_PROXY = 18, */ /* UNUSED */
TSE_R_LAYER_BASE = 19,
TSE_R_LAYER = 20,
/* TSE_R_PASS = 21, */ /* UNUSED */
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 5fe67a34dae..622175a8429 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -73,13 +73,14 @@ typedef struct StripCrop {
} StripCrop;
typedef struct StripTransform {
- int xofs;
- int yofs;
+ float xofs;
+ float yofs;
float scale_x;
float scale_y;
float rotation;
/** 0-1 range, use SEQ_image_transform_origin_offset_pixelspace_get to convert to pixel space. */
float origin[2];
+ int filter;
} StripTransform;
typedef struct StripColorBalance {
@@ -788,6 +789,12 @@ typedef enum SequenceColorTag {
SEQUENCE_COLOR_TOT,
} SequenceColorTag;
+/* Sequence->StripTransform->filter */
+enum {
+ SEQ_TRANSFORM_FILTER_NEAREST = 0,
+ SEQ_TRANSFORM_FILTER_BILINEAR = 1,
+};
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 45ac596695f..4e12f135242 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -65,7 +65,14 @@ struct wmTimer;
typedef struct SpaceProperties_Runtime SpaceProperties_Runtime;
/** Defined in `node_intern.hh`. */
+#ifdef __cplusplus
+namespace blender::ed::space_node {
+struct SpaceNode_Runtime;
+} // namespace blender::ed::space_node
+using SpaceNode_Runtime = blender::ed::space_node::SpaceNode_Runtime;
+#else
typedef struct SpaceNode_Runtime SpaceNode_Runtime;
+#endif
/** Defined in `file_intern.h`. */
typedef struct SpaceFile_Runtime SpaceFile_Runtime;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 15bb1ef920d..d1b015485c9 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -651,7 +651,6 @@ typedef struct UserDef_Experimental {
/* Debug options, always available. */
char use_undo_legacy;
char no_override_auto_resync;
- char no_proxy_to_override_conversion;
char use_cycles_debug;
char use_geometry_nodes_legacy;
char show_asset_debug_info;
@@ -659,14 +658,14 @@ typedef struct UserDef_Experimental {
char SANITIZE_AFTER_HERE;
/* The following options are automatically sanitized (set to 0)
* when the release cycle is not alpha. */
- char use_new_hair_type;
+ char use_new_curves_type;
char use_new_point_cloud_type;
char use_full_frame_compositor;
char use_sculpt_vertex_colors;
char use_sculpt_tools_tilt;
char use_extended_asset_browser;
char use_override_templates;
- char _pad[1];
+ char _pad[2];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
@@ -1253,7 +1252,7 @@ typedef enum eDupli_ID_Flags {
USER_DUP_PSYS = (1 << 11),
USER_DUP_LIGHTPROBE = (1 << 12),
USER_DUP_GPENCIL = (1 << 13),
- USER_DUP_HAIR = (1 << 14),
+ USER_DUP_CURVES = (1 << 14),
USER_DUP_POINTCLOUD = (1 << 15),
USER_DUP_VOLUME = (1 << 16),
USER_DUP_LATTICE = (1 << 17),
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index 2ec58181394..dafae6f2eb7 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -71,7 +71,7 @@ typedef struct RegionView3D {
float clip_local[6][4];
struct BoundBox *clipbb;
- /** Allocated backup of its self while in local-view. */
+ /** Allocated backup of itself while in local-view. */
struct RegionView3D *localvd;
struct RenderEngine *render_engine;
@@ -302,7 +302,7 @@ typedef struct View3D {
struct Object *camera, *ob_center;
rctf render_border;
- /** Allocated backup of its self while in local-view. */
+ /** Allocated backup of itself while in local-view. */
struct View3D *localvd;
/** Optional string for armature bone to define center, MAXBONENAME. */
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index a3c54e91780..af30fa5cc9e 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -142,7 +142,7 @@ set(SRC
../DNA_defaults.h
../DNA_fluid_defaults.h
../DNA_gpencil_modifier_defaults.h
- ../DNA_hair_defaults.h
+ ../DNA_curves_defaults.h
../DNA_image_defaults.h
../DNA_lattice_defaults.h
../DNA_light_defaults.h
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 5bc5de7a20b..93d8051d6c4 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -12,8 +12,6 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * DNA default value access.
*/
/** \file
@@ -94,9 +92,9 @@
#include "DNA_cloth_types.h"
#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
+#include "DNA_curves_types.h"
#include "DNA_fluid_types.h"
#include "DNA_gpencil_modifier_types.h"
-#include "DNA_hair_types.h"
#include "DNA_image_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
@@ -127,9 +125,9 @@
#include "DNA_camera_defaults.h"
#include "DNA_collection_defaults.h"
#include "DNA_curve_defaults.h"
+#include "DNA_curves_defaults.h"
#include "DNA_fluid_defaults.h"
#include "DNA_gpencil_modifier_defaults.h"
-#include "DNA_hair_defaults.h"
#include "DNA_image_defaults.h"
#include "DNA_lattice_defaults.h"
#include "DNA_light_defaults.h"
@@ -184,8 +182,8 @@ SDNA_DEFAULT_DECL_STRUCT(FluidEffectorSettings);
/* DNA_image_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(Image);
-/* DNA_hair_defaults.h */
-SDNA_DEFAULT_DECL_STRUCT(Hair);
+/* DNA_curves_defaults.h */
+SDNA_DEFAULT_DECL_STRUCT(Curves);
/* DNA_lattice_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(Lattice);
@@ -392,8 +390,8 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
/* DNA_image_defaults.h */
SDNA_DEFAULT_DECL(Image),
- /* DNA_hair_defaults.h */
- SDNA_DEFAULT_DECL(Hair),
+ /* DNA_curves_defaults.h */
+ SDNA_DEFAULT_DECL(Curves),
/* DNA_lattice_defaults.h */
SDNA_DEFAULT_DECL(Lattice),
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index 6322cb459dd..634aa91cf95 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -15,11 +15,11 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * DNA handling
*/
/** \file
* \ingroup DNA
+ * \brief DNA handling
*
* Lowest-level functions for decoding the parts of a saved .blend
* file, including interpretation of its SDNA block and conversion of
@@ -733,49 +733,73 @@ static void cast_primitive_type(const eSDNA_Type old_type,
const int curlen = DNA_elem_type_size(new_type);
double old_value_f = 0.0;
+ /* Intentionally overflow signed values into an unsigned type.
+ * Casting back to a signed value preserves the sign (when the new value is signed). */
uint64_t old_value_i = 0;
for (int a = 0; a < array_len; a++) {
switch (old_type) {
- case SDNA_TYPE_CHAR:
- old_value_i = *old_data;
- old_value_f = (double)old_value_i;
+ case SDNA_TYPE_CHAR: {
+ const char value = *old_data;
+ old_value_i = value;
+ old_value_f = (double)value;
break;
- case SDNA_TYPE_UCHAR:
- old_value_i = *((unsigned char *)old_data);
- old_value_f = (double)old_value_i;
+ }
+ case SDNA_TYPE_UCHAR: {
+ const uchar value = *((uchar *)old_data);
+ old_value_i = value;
+ old_value_f = (double)value;
break;
- case SDNA_TYPE_SHORT:
- old_value_i = *((short *)old_data);
- old_value_f = (double)old_value_i;
+ }
+ case SDNA_TYPE_SHORT: {
+ const short value = *((short *)old_data);
+ old_value_i = value;
+ old_value_f = (double)value;
break;
- case SDNA_TYPE_USHORT:
- old_value_i = *((unsigned short *)old_data);
- old_value_f = (double)old_value_i;
+ }
+ case SDNA_TYPE_USHORT: {
+ const ushort value = *((unsigned short *)old_data);
+ old_value_i = value;
+ old_value_f = (double)value;
break;
- case SDNA_TYPE_INT:
- old_value_i = *((int *)old_data);
- old_value_f = (double)old_value_i;
+ }
+ case SDNA_TYPE_INT: {
+ const int value = *((int *)old_data);
+ old_value_i = value;
+ old_value_f = (double)value;
break;
- case SDNA_TYPE_FLOAT:
- old_value_f = *((float *)old_data);
- old_value_i = (uint64_t)(int64_t)old_value_f;
+ }
+ case SDNA_TYPE_FLOAT: {
+ const float value = *((float *)old_data);
+ /* `int64_t` range stored in a `uint64_t`. */
+ old_value_i = (uint64_t)(int64_t)value;
+ old_value_f = value;
break;
- case SDNA_TYPE_DOUBLE:
- old_value_f = *((double *)old_data);
- old_value_i = (uint64_t)(int64_t)old_value_f;
+ }
+ case SDNA_TYPE_DOUBLE: {
+ const double value = *((double *)old_data);
+ /* `int64_t` range stored in a `uint64_t`. */
+ old_value_i = (uint64_t)(int64_t)value;
+ old_value_f = value;
break;
- case SDNA_TYPE_INT64:
- old_value_i = (uint64_t) * ((int64_t *)old_data);
- old_value_f = (double)old_value_i;
+ }
+ case SDNA_TYPE_INT64: {
+ const int64_t value = *((int64_t *)old_data);
+ old_value_i = (uint64_t)value;
+ old_value_f = (double)value;
break;
- case SDNA_TYPE_UINT64:
- old_value_i = *((uint64_t *)old_data);
- old_value_f = (double)old_value_i;
+ }
+ case SDNA_TYPE_UINT64: {
+ const uint64_t value = *((uint64_t *)old_data);
+ old_value_i = value;
+ old_value_f = (double)value;
break;
- case SDNA_TYPE_INT8:
- old_value_i = (uint64_t) * ((int8_t *)old_data);
- old_value_f = (double)old_value_i;
+ }
+ case SDNA_TYPE_INT8: {
+ const int8_t value = *((int8_t *)old_data);
+ old_value_i = (uint64_t)value;
+ old_value_f = (double)value;
+ }
}
switch (new_type) {
diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h
index cb8052856a7..b666a5dce56 100644
--- a/source/blender/makesdna/intern/dna_rename_defs.h
+++ b/source/blender/makesdna/intern/dna_rename_defs.h
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
- * DNA handling
*/
/** \file
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 114c0b40407..5156203b71f 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -134,7 +134,7 @@ static const char *includefiles[] = {
"DNA_lightprobe_types.h",
"DNA_curveprofile_types.h",
"DNA_xr_types.h",
- "DNA_hair_types.h",
+ "DNA_curves_types.h",
"DNA_pointcloud_types.h",
"DNA_volume_types.h",
"DNA_simulation_types.h",
@@ -1555,8 +1555,18 @@ int main(int argc, char **argv)
base_directory = BASE_HEADER;
}
+ /* NOTE: #init_structDNA() in dna_genfile.c expects `sdna->data` is 4-bytes aligned.
+ * `DNAstr[]` buffer written by `makesdna` is used for this data, so make `DNAstr` forcefully
+ * 4-bytes aligned. */
+#ifdef __GNUC__
+# define FORCE_ALIGN_4 " __attribute__((aligned(4))) "
+#else
+# define FORCE_ALIGN_4 " "
+#endif
fprintf(file_dna, "extern const unsigned char DNAstr[];\n");
- fprintf(file_dna, "const unsigned char DNAstr[] = {\n");
+ fprintf(file_dna, "const unsigned char" FORCE_ALIGN_4 "DNAstr[] = {\n");
+#undef FORCE_ALIGN_4
+
if (make_structDNA(base_directory, file_dna, file_dna_offsets, file_dna_verify)) {
/* error */
fclose(file_dna);
@@ -1626,6 +1636,7 @@ int main(int argc, char **argv)
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_curveprofile_types.h"
+#include "DNA_curves_types.h"
#include "DNA_customdata_types.h"
#include "DNA_dynamicpaint_types.h"
#include "DNA_effect_types.h"
@@ -1634,7 +1645,6 @@ int main(int argc, char **argv)
#include "DNA_freestyle_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
-#include "DNA_hair_types.h"
#include "DNA_image_types.h"
#include "DNA_ipo_types.h"
#include "DNA_key_types.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index a55afb23cbf..410e3a97f2a 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -104,6 +104,8 @@ extern StructRNA RNA_BuildGpencilModifier;
extern StructRNA RNA_BuildModifier;
extern StructRNA RNA_ByteColorAttribute;
extern StructRNA RNA_ByteColorAttributeValue;
+extern StructRNA RNA_ByteIntAttribute;
+extern StructRNA RNA_ByteIntAttributeValue;
extern StructRNA RNA_CacheAttributeMapping;
extern StructRNA RNA_CacheFile;
extern StructRNA RNA_CacheFileLayer;
@@ -144,6 +146,7 @@ extern StructRNA RNA_CompositorNodeCombHSVA;
extern StructRNA RNA_CompositorNodeCombRGBA;
extern StructRNA RNA_CompositorNodeCombYCCA;
extern StructRNA RNA_CompositorNodeCombYUVA;
+extern StructRNA RNA_CompositorNodeCombineXYZ;
extern StructRNA RNA_CompositorNodeComposite;
extern StructRNA RNA_CompositorNodeCornerPin;
extern StructRNA RNA_CompositorNodeCrop;
@@ -188,6 +191,7 @@ extern StructRNA RNA_CompositorNodeRLayers;
extern StructRNA RNA_CompositorNodeRotate;
extern StructRNA RNA_CompositorNodeScale;
extern StructRNA RNA_CompositorNodeSceneTime;
+extern StructRNA RNA_CompositorNodeSeparateXYZ;
extern StructRNA RNA_CompositorNodeSepHSVA;
extern StructRNA RNA_CompositorNodeSepRGBA;
extern StructRNA RNA_CompositorNodeSepYCCA;
@@ -305,7 +309,7 @@ extern StructRNA RNA_GizmoProperties;
extern StructRNA RNA_GlowSequence;
extern StructRNA RNA_GpencilModifier;
extern StructRNA RNA_GreasePencil;
-extern StructRNA RNA_Hair;
+extern StructRNA RNA_Curves;
extern StructRNA RNA_Header;
extern StructRNA RNA_Histogram;
extern StructRNA RNA_HookGpencilModifier;
@@ -1513,10 +1517,21 @@ void RNA_collection_clear(PointerRNA *ptr, const char *name);
/**
* Check if the #IDproperty exists, for operators.
+ *
+ * \param use_ghost: Internally an #IDProperty may exist,
+ * without the RNA considering it to be "set", see #IDP_FLAG_GHOST.
+ * This is used for operators, where executing an operator that has run previously
+ * will re-use the last value (unless #PROP_SKIP_SAVE property is set).
+ * In this case, the presence of the an existing value shouldn't prevent it being initialized
+ * from the context. Even though the this value will be returned if it's requested,
+ * it's not considered to be set (as it would if the menu item or key-map defined it's value).
+ * Set `use_ghost` to true for default behavior, otherwise false to check if there is a value
+ * exists internally and would be returned on request.
*/
bool RNA_property_is_set_ex(PointerRNA *ptr, PropertyRNA *prop, bool use_ghost);
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop);
void RNA_property_unset(PointerRNA *ptr, PropertyRNA *prop);
+/** See #RNA_property_is_set_ex documentation. */
bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool use_ghost);
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier);
bool RNA_property_is_idprop(const PropertyRNA *prop);
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index da07f1043a7..fcae1009c8b 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -35,7 +35,9 @@ struct bNodeType;
#define DEF_ENUM(id) extern const EnumPropertyItem id[];
#include "RNA_enum_items.h"
-extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free);
+extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id,
+ bool include_instances,
+ bool *r_free);
/**
* For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index 2d499dd113f..e37eb9f7188 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -240,7 +240,7 @@ typedef enum PropertyFlag {
PROP_ID_REFCOUNT = (1 << 6),
/**
- * Disallow assigning a variable to its self, eg an object tracking its self
+ * Disallow assigning a variable to itself, eg an object tracking itself
* only apply this to types that are derived from an ID ().
*/
PROP_ID_SELF_CHECK = (1 << 20),
@@ -322,7 +322,7 @@ typedef enum PropertyFlag {
* FREE FLAGS: 2, 3, 4, 5, 6, 7, 8, 9, 12 and above.
*/
typedef enum PropertyOverrideFlag {
- /** Means the property can be overridden by a local 'proxy' of some linked datablock. */
+ /** Means that the property can be overridden by a local override of some linked datablock. */
PROPOVERRIDE_OVERRIDABLE_LIBRARY = (1 << 0),
/**
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index f057d8e9d4c..91d7c5a1394 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -70,6 +70,7 @@ set(DEFSRC
rna_packedfile.c
rna_palette.c
rna_particle.c
+ rna_pointcloud.c
rna_pose.c
rna_render.c
rna_rigidbody.c
@@ -100,11 +101,9 @@ set(DEFSRC
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
- add_definitions(-DWITH_POINT_CLOUD)
- add_definitions(-DWITH_HAIR_NODES)
+ add_definitions(-DWITH_NEW_CURVES_TYPE)
list(APPEND DEFSRC
- rna_hair.c
- rna_pointcloud.c
+ rna_curves.c
rna_simulation.c
)
endif()
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index a6732ca1760..0fadbda5a18 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -4376,8 +4376,8 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint},
{"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve},
{"rna_gpencil.c", NULL, RNA_def_gpencil},
-#ifdef WITH_HAIR_NODES
- {"rna_hair.c", NULL, RNA_def_hair},
+#ifdef WITH_NEW_CURVES_TYPE
+ {"rna_curves.c", NULL, RNA_def_curves},
#endif
{"rna_image.c", "rna_image_api.c", RNA_def_image},
{"rna_key.c", NULL, RNA_def_key},
@@ -4401,9 +4401,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_packedfile.c", NULL, RNA_def_packedfile},
{"rna_palette.c", NULL, RNA_def_palette},
{"rna_particle.c", NULL, RNA_def_particle},
-#ifdef WITH_POINT_CLOUD
{"rna_pointcloud.c", NULL, RNA_def_pointcloud},
-#endif
{"rna_pose.c", "rna_pose_api.c", RNA_def_pose},
{"rna_curveprofile.c", NULL, RNA_def_profile},
{"rna_lightprobe.c", NULL, RNA_def_lightprobe},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index b92123f445b..e7f885b2160 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -76,7 +76,7 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
{ID_SPK, "SPEAKER", ICON_SPEAKER, "Speaker", ""},
{ID_TXT, "TEXT", ICON_TEXT, "Text", ""},
{ID_TE, "TEXTURE", ICON_TEXTURE_DATA, "Texture", ""},
- {ID_HA, "HAIR", ICON_HAIR_DATA, "Hair", ""},
+ {ID_CV, "CURVES", ICON_CURVES_DATA, "Hair Curves", ""},
{ID_PT, "POINTCLOUD", ICON_POINTCLOUD_DATA, "Point Cloud", ""},
{ID_VO, "VOLUME", ICON_VOLUME_DATA, "Volume", ""},
{ID_WM, "WINDOWMANAGER", ICON_WINDOW, "Window Manager", ""},
@@ -151,7 +151,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = {
ICON_OUTLINER_COLLECTION,
"Collections",
"Show Collection data-blocks"},
- {FILTER_ID_HA, "filter_hair", ICON_HAIR_DATA, "Hairs", "Show/hide Hair data-blocks"},
+ {FILTER_ID_CV, "filter_hair", ICON_CURVES_DATA, "Hairs", "Show/hide Hair data-blocks"},
{FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"},
{FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"},
{FILTER_ID_LP,
@@ -385,9 +385,9 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_FreestyleLineStyle) {
return ID_LS;
}
-# ifdef WITH_HAIR_NODES
- if (base_type == &RNA_Hair) {
- return ID_HA;
+# ifdef WITH_NEW_CURVES_TYPE
+ if (base_type == &RNA_Curves) {
+ return ID_CV;
}
# endif
if (base_type == &RNA_Lattice) {
@@ -423,11 +423,9 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_PaintCurve) {
return ID_PC;
}
-# ifdef WITH_POINT_CLOUD
if (base_type == &RNA_PointCloud) {
return ID_PT;
}
-# endif
if (base_type == &RNA_LightProbe) {
return ID_LP;
}
@@ -494,9 +492,9 @@ StructRNA *ID_code_to_RNA_type(short idcode)
return &RNA_GreasePencil;
case ID_GR:
return &RNA_Collection;
- case ID_HA:
-# ifdef WITH_HAIR_NODES
- return &RNA_Hair;
+ case ID_CV:
+# ifdef WITH_NEW_CURVES_TYPE
+ return &RNA_Curves;
# else
return &RNA_ID;
# endif
@@ -533,11 +531,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_PC:
return &RNA_PaintCurve;
case ID_PT:
-# ifdef WITH_POINT_CLOUD
return &RNA_PointCloud;
-# else
- return &RNA_ID;
-# endif
case ID_LP:
return &RNA_LightProbe;
case ID_SCE:
@@ -710,6 +704,7 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
}
WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
return local_id;
}
@@ -724,9 +719,11 @@ static ID *rna_ID_override_hierarchy_create(
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
ID *id_root_override = NULL;
- BKE_lib_override_library_create(bmain, scene, view_layer, id, id_reference, &id_root_override);
+ BKE_lib_override_library_create(
+ bmain, scene, view_layer, NULL, id, id_reference, &id_root_override);
WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
return id_root_override;
}
@@ -747,6 +744,8 @@ static void rna_ID_override_template_create(ID *id, ReportList *reports)
return;
}
BKE_lib_override_library_template_create(id);
+
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
}
static void rna_ID_override_library_operations_update(ID *id,
@@ -760,6 +759,8 @@ static void rna_ID_override_library_operations_update(ID *id,
}
BKE_lib_override_library_operations_create(bmain, id);
+
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
}
static void rna_ID_override_library_reset(ID *id,
@@ -779,6 +780,8 @@ static void rna_ID_override_library_reset(ID *id,
else {
BKE_lib_override_library_id_reset(bmain, id);
}
+
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
}
static void rna_ID_override_library_destroy(ID *id,
@@ -799,6 +802,8 @@ static void rna_ID_override_library_destroy(ID *id,
BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE);
BKE_id_delete(bmain, id);
}
+
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
}
static IDOverrideLibraryProperty *rna_ID_override_library_properties_add(
@@ -812,6 +817,7 @@ static IDOverrideLibraryProperty *rna_ID_override_library_properties_add(
BKE_report(reports, RPT_DEBUG, "No new override property created, property already exists");
}
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
return result;
}
@@ -825,6 +831,8 @@ static void rna_ID_override_library_properties_remove(IDOverrideLibrary *overrid
}
BKE_lib_override_library_property_delete(override_library, override_property);
+
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
}
static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_operations_add(
@@ -851,6 +859,8 @@ static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_oper
if (!created) {
BKE_report(reports, RPT_DEBUG, "No new override operation created, operation already exists");
}
+
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
return result;
}
@@ -865,6 +875,8 @@ static void rna_ID_override_library_property_operations_remove(
}
BKE_lib_override_library_property_operation_delete(override_property, override_operation);
+
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
}
static void rna_ID_update_tag(ID *id, Main *bmain, ReportList *reports, int flag)
@@ -937,9 +949,9 @@ static void rna_ID_user_remap(ID *id, Main *bmain, ID *new_id)
}
}
-static struct ID *rna_ID_make_local(struct ID *self, Main *bmain, bool clear_proxy)
+static struct ID *rna_ID_make_local(struct ID *self, Main *bmain, bool UNUSED(clear_proxy))
{
- BKE_lib_id_make_local(bmain, self, clear_proxy ? 0 : LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
+ BKE_lib_id_make_local(bmain, self, 0);
ID *ret_id = self->newid ? self->newid : self;
BKE_id_newptr_and_tag_clear(self);
@@ -1753,6 +1765,7 @@ static void rna_def_ID_override_library_property(BlenderRNA *brna)
"IDOverrideLibraryPropertyOperation",
"Operations",
"List of overriding operations for a property");
+ RNA_def_property_update(prop, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
rna_def_ID_override_library_property_operations(brna, prop);
rna_def_ID_override_library_property_operation(brna);
@@ -1805,8 +1818,16 @@ static void rna_def_ID_override_library(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna, "ID Library Override", "Struct gathering all data needed by overridden linked IDs");
- RNA_def_pointer(
+ prop = RNA_def_pointer(
srna, "reference", "ID", "Reference ID", "Linked ID used as reference by this override");
+ RNA_def_property_update(prop, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+
+ RNA_def_pointer(
+ srna,
+ "hierarchy_root",
+ "ID",
+ "Hierarchy Root ID",
+ "Library override ID used as root of the override hierarchy this ID is a member of");
prop = RNA_def_boolean(srna,
"is_in_hierarchy",
@@ -1814,6 +1835,7 @@ static void rna_def_ID_override_library(BlenderRNA *brna)
"Is In Hierarchy",
"Whether this library override is defined as part of a library "
"hierarchy, or as a single, isolated and autonomous override");
+ RNA_def_property_update(prop, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY);
prop = RNA_def_collection(srna,
@@ -1821,6 +1843,7 @@ static void rna_def_ID_override_library(BlenderRNA *brna)
"IDOverrideLibraryProperty",
"Properties",
"List of overridden properties");
+ RNA_def_property_update(prop, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
rna_def_ID_override_library_properties(brna, prop);
/* Update function. */
@@ -2073,13 +2096,7 @@ static void rna_def_ID(BlenderRNA *brna)
"Make this datablock local, return local one "
"(may be a copy of the original, in case it is also indirectly used)");
RNA_def_function_flag(func, FUNC_USE_MAIN);
- parm = RNA_def_boolean(
- func,
- "clear_proxy",
- true,
- "",
- "Whether to clear proxies (the default behavior, "
- "note that if object has to be duplicated to be made local, proxies are always cleared)");
+ parm = RNA_def_boolean(func, "clear_proxy", true, "", "Deprecated, has no effect");
parm = RNA_def_pointer(func, "id", "ID", "", "This ID, or the new ID if it was copied");
RNA_def_function_return(func, parm);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index b64fa58cf6b..63eb016b5de 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -3260,10 +3260,10 @@ void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *val
rna_idproperty_touch(idprop);
}
else if (sprop->set) {
- sprop->set(ptr, value); /* set function needs to clamp its self */
+ sprop->set(ptr, value); /* set function needs to clamp itself */
}
else if (sprop->set_ex) {
- sprop->set_ex(ptr, prop, value); /* set function needs to clamp its self */
+ sprop->set_ex(ptr, prop, value); /* set function needs to clamp itself */
}
else if (prop->flag & PROP_EDITABLE) {
IDProperty *group;
@@ -3292,11 +3292,11 @@ void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const cha
}
else if (sprop->set) {
/* XXX, should take length argument (currently not used). */
- sprop->set(ptr, value); /* set function needs to clamp its self */
+ sprop->set(ptr, value); /* set function needs to clamp itself */
}
else if (sprop->set_ex) {
/* XXX, should take length argument (currently not used). */
- sprop->set_ex(ptr, prop, value); /* set function needs to clamp its self */
+ sprop->set_ex(ptr, prop, value); /* set function needs to clamp itself */
}
else if (prop->flag & PROP_EDITABLE) {
IDProperty *group;
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index 6b134977c5a..f4236a860ab 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -625,11 +625,11 @@ static void rna_def_dopesheet(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_FILE, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
- prop = RNA_def_property(srna, "show_hairs", PROP_BOOLEAN, PROP_NONE);
+ prop = RNA_def_property(srna, "show_hair_curves", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag2", ADS_FILTER_NOHAIR);
RNA_def_property_ui_text(
prop, "Display Hair", "Include visualization of hair related animation data");
- RNA_def_property_ui_icon(prop, ICON_OUTLINER_OB_HAIR, 0);
+ RNA_def_property_ui_icon(prop, ICON_OUTLINER_OB_CURVES, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
prop = RNA_def_property(srna, "show_pointclouds", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index 28e50e80f32..b22d3654431 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -151,18 +151,9 @@ static void rna_Armature_edit_bone_remove(bArmature *arm,
RNA_POINTER_INVALIDATE(ebone_ptr);
}
-static void rna_Armature_update_layers(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+static void rna_Armature_update_layers(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
bArmature *arm = (bArmature *)ptr->owner_id;
- Object *ob;
-
- /* proxy lib exception, store it here so we can restore layers on file
- * load, since it would otherwise get lost due to being linked data */
- for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- if (ob->data == arm && ob->pose) {
- ob->pose->proxy_layer = arm->layer;
- }
- }
DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_GEOM | ND_DATA, arm);
diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c
index e79cbc838d4..e80c8559020 100644
--- a/source/blender/makesrna/intern/rna_asset.c
+++ b/source/blender/makesrna/intern/rna_asset.c
@@ -237,7 +237,7 @@ static void rna_AssetMetaData_catalog_id_set(PointerRNA *ptr, const char *value)
}
if (!BLI_uuid_parse_string(&new_uuid, value)) {
- // TODO(Sybren): raise ValueError exception once that's possible from an RNA setter.
+ /* TODO(@sybren): raise ValueError exception once that's possible from an RNA setter. */
printf("UUID %s not formatted correctly, ignoring new value\n", value);
return;
}
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index 78c15444308..dc0d00aaa77 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -26,8 +26,8 @@
#include "rna_internal.h"
+#include "DNA_curves_types.h"
#include "DNA_customdata_types.h"
-#include "DNA_hair_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
@@ -46,6 +46,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{CD_PROP_STRING, "STRING", 0, "String", "Text string"},
{CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"},
{CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
+ {CD_PROP_INT8, "INT8", 0, "8-Bit Integer", "Smaller integer with a range from -128 to 127"},
{0, NULL, 0, NULL, NULL},
};
@@ -59,6 +60,7 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
{CD_PROP_STRING, "STRING", 0, "String", "Text string"},
{CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"},
{CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
+ {CD_PROP_INT8, "INT8", 0, "8-Bit Integer", "Smaller integer with a range from -128 to 127"},
{0, NULL, 0, NULL, NULL},
};
@@ -133,6 +135,8 @@ static StructRNA *srna_by_custom_data_layer_type(const CustomDataType type)
return &RNA_BoolAttribute;
case CD_PROP_FLOAT2:
return &RNA_Float2Attribute;
+ case CD_PROP_INT8:
+ return &RNA_ByteIntAttribute;
default:
return NULL;
}
@@ -166,7 +170,9 @@ static int rna_Attribute_type_get(PointerRNA *ptr)
return layer->type;
}
-const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id, bool *r_free)
+const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id,
+ bool include_instances,
+ bool *r_free)
{
EnumPropertyItem *item = NULL;
const EnumPropertyItem *domain_item = NULL;
@@ -182,12 +188,15 @@ const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id, bool *r_free)
if (id_type == ID_PT && !ELEM(domain_item->value, ATTR_DOMAIN_POINT)) {
continue;
}
- if (id_type == ID_HA && !ELEM(domain_item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ if (id_type == ID_CV && !ELEM(domain_item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
continue;
}
if (id_type == ID_ME && ELEM(domain_item->value, ATTR_DOMAIN_CURVE)) {
continue;
}
+ if (!include_instances && domain_item->value == ATTR_DOMAIN_INSTANCE) {
+ continue;
+ }
if (domain_item->value == ATTR_DOMAIN_POINT && id_type == ID_ME) {
RNA_enum_item_add(&item, &totitem, &mesh_vertex_domain_item);
@@ -207,7 +216,7 @@ static const EnumPropertyItem *rna_Attribute_domain_itemf(bContext *UNUSED(C),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
- return rna_enum_attribute_domain_itemf(ptr->owner_id, r_free);
+ return rna_enum_attribute_domain_itemf(ptr->owner_id, true, r_free);
}
static int rna_Attribute_domain_get(PointerRNA *ptr)
@@ -248,6 +257,9 @@ static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRN
case CD_PROP_FLOAT2:
struct_size = sizeof(float[2]);
break;
+ case CD_PROP_INT8:
+ struct_size = sizeof(int8_t);
+ break;
default:
struct_size = 0;
length = 0;
@@ -289,6 +301,28 @@ static void rna_ByteColorAttributeValue_color_set(PointerRNA *ptr, const float *
linearrgb_to_srgb_uchar4(&mlcol->r, values);
}
+/* Int8 Attribute. */
+
+static int rna_ByteIntAttributeValue_get(PointerRNA *ptr)
+{
+ int8_t *value = (int8_t *)ptr->data;
+ return (int)(*value);
+}
+
+static void rna_ByteIntAttributeValue_set(PointerRNA *ptr, const int new_value)
+{
+ int8_t *value = (int8_t *)ptr->data;
+ if (new_value > INT8_MAX) {
+ *value = INT8_MAX;
+ }
+ else if (new_value < INT8_MIN) {
+ *value = INT8_MIN;
+ }
+ else {
+ *value = (int8_t)new_value;
+ }
+}
+
/* Attribute Group */
static PointerRNA rna_AttributeGroup_new(
@@ -643,6 +677,36 @@ static void rna_def_attribute_bool(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "b", 0x01);
}
+static void rna_def_attribute_int8(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ByteIntAttribute", "Attribute");
+ RNA_def_struct_sdna(srna, "CustomDataLayer");
+ RNA_def_struct_ui_text(srna, "8-bit Int Attribute", "8-bit int geometry attribute");
+
+ prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ByteIntAttributeValue");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Attribute_data_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_Attribute_data_length",
+ NULL,
+ NULL,
+ NULL);
+
+ srna = RNA_def_struct(brna, "ByteIntAttributeValue", NULL);
+ RNA_def_struct_sdna(srna, "MInt8Property");
+ RNA_def_struct_ui_text(
+ srna, "8-bit Integer Attribute Value", "8-bit value in geometry attribute");
+ prop = RNA_def_property(srna, "value", PROP_INT, PROP_NONE);
+ RNA_def_property_int_funcs(
+ prop, "rna_ByteIntAttributeValue_get", "rna_ByteIntAttributeValue_set", NULL);
+}
+
static void rna_def_attribute_float2(BlenderRNA *brna)
{
StructRNA *srna;
@@ -718,6 +782,7 @@ static void rna_def_attribute(BlenderRNA *brna)
rna_def_attribute_string(brna);
rna_def_attribute_bool(brna);
rna_def_attribute_float2(brna);
+ rna_def_attribute_int8(brna);
}
/* Mesh/PointCloud/Hair.attributes */
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 0c993660f39..e3a06c44eee 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -3543,14 +3543,6 @@ void RNA_def_constraint(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_ACTIVE);
RNA_def_property_ui_text(prop, "Active", "Constraint is the one being edited");
- prop = RNA_def_property(srna, "is_proxy_local", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_PROXY_LOCAL);
- RNA_def_property_ui_text(
- prop,
- "Proxy Local",
- "Constraint was added in this proxy instance (i.e. did not belong to source Armature)");
-
/* values */
prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "enforce");
diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c
new file mode 100644
index 00000000000..faa067000bb
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_curves.c
@@ -0,0 +1,296 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#include "DNA_curves_types.h"
+
+#include "BLI_math_base.h"
+#include "BLI_string.h"
+
+#ifdef RNA_RUNTIME
+
+# include "BLI_math_vector.h"
+
+# include "BKE_attribute.h"
+# include "BKE_curves.h"
+
+# include "DEG_depsgraph.h"
+
+# include "WM_api.h"
+# include "WM_types.h"
+
+static Curves *rna_curves(PointerRNA *ptr)
+{
+ return (Curves *)ptr->owner_id;
+}
+
+static int rna_Curves_curve_offset_data_length(PointerRNA *ptr)
+{
+ const Curves *curves = rna_curves(ptr);
+ return curves->geometry.curve_size + 1;
+}
+
+static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ const Curves *curves = rna_curves(ptr);
+ rna_iterator_array_begin(iter,
+ (void *)curves->geometry.offsets,
+ sizeof(int),
+ curves->geometry.curve_size + 1,
+ false,
+ NULL);
+}
+
+static int rna_CurvePoint_index_get(PointerRNA *ptr)
+{
+ const Curves *curves = rna_curves(ptr);
+ const float(*co)[3] = ptr->data;
+ return (int)(co - curves->geometry.position);
+}
+
+static void rna_CurvePoint_location_get(PointerRNA *ptr, float value[3])
+{
+ copy_v3_v3(value, (const float *)ptr->data);
+}
+
+static void rna_CurvePoint_location_set(PointerRNA *ptr, const float value[3])
+{
+ copy_v3_v3((float *)ptr->data, value);
+}
+
+static float rna_CurvePoint_radius_get(PointerRNA *ptr)
+{
+ const Curves *curves = rna_curves(ptr);
+ if (curves->geometry.radius == NULL) {
+ return 0.0f;
+ }
+ const float(*co)[3] = ptr->data;
+ return curves->geometry.radius[co - curves->geometry.position];
+}
+
+static void rna_CurvePoint_radius_set(PointerRNA *ptr, float value)
+{
+ const Curves *curves = rna_curves(ptr);
+ if (curves->geometry.radius == NULL) {
+ return;
+ }
+ const float(*co)[3] = ptr->data;
+ curves->geometry.radius[co - curves->geometry.position] = value;
+}
+
+static char *rna_CurvePoint_path(PointerRNA *ptr)
+{
+ return BLI_sprintfN("points[%d]", rna_CurvePoint_index_get(ptr));
+}
+
+static int rna_CurveSlice_index_get(PointerRNA *ptr)
+{
+ Curves *curves = rna_curves(ptr);
+ return (int)((int *)ptr->data - curves->geometry.offsets);
+}
+
+static char *rna_CurveSlice_path(PointerRNA *ptr)
+{
+ return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get(ptr));
+}
+
+static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Curves *curves = rna_curves(ptr);
+ const int *offset_ptr = (int *)ptr->data;
+ const int offset = *offset_ptr;
+ const int size = *(offset_ptr + 1) - offset;
+ float(*co)[3] = curves->geometry.position + *offset_ptr;
+ rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL);
+}
+
+static int rna_CurveSlice_first_point_index_get(PointerRNA *ptr)
+{
+ const int *offset_ptr = (int *)ptr->data;
+ return *offset_ptr;
+}
+
+static int rna_CurveSlice_points_length_get(PointerRNA *ptr)
+{
+ const int *offset_ptr = (int *)ptr->data;
+ const int offset = *offset_ptr;
+ return *(offset_ptr + 1) - offset;
+}
+
+static void rna_Curves_update_data(struct Main *UNUSED(bmain),
+ struct Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ ID *id = ptr->owner_id;
+
+ /* cheating way for importers to avoid slow updates */
+ if (id->us > 0) {
+ DEG_id_tag_update(id, 0);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, id);
+ }
+}
+
+#else
+
+static void rna_def_curves_point(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "CurvePoint", NULL);
+ RNA_def_struct_ui_text(srna, "Curve Point", "Curve curve control point");
+ RNA_def_struct_path_func(srna, "rna_CurvePoint_path");
+
+ prop = RNA_def_property(srna, "position", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_float_funcs(
+ prop, "rna_CurvePoint_location_get", "rna_CurvePoint_location_set", NULL);
+ RNA_def_property_ui_text(prop, "Position", "");
+ RNA_def_property_update(prop, 0, "rna_Curves_update_data");
+
+ prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_funcs(
+ prop, "rna_CurvePoint_radius_get", "rna_CurvePoint_radius_set", NULL);
+ RNA_def_property_ui_text(prop, "Radius", "");
+ RNA_def_property_update(prop, 0, "rna_Curves_update_data");
+
+ prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_CurvePoint_index_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Index", "Index of this points");
+}
+
+static void rna_def_curves_curve(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "CurveSlice", NULL);
+ RNA_def_struct_ui_text(srna, "Curve Slice", "A single curve from a curves data-block");
+ RNA_def_struct_path_func(srna, "rna_CurveSlice_path");
+
+ prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurvePoint");
+ RNA_def_property_ui_text(prop, "Points", "Control points of the curve");
+ RNA_def_property_collection_funcs(prop,
+ "rna_CurveSlice_points_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_CurveSlice_points_length_get",
+ NULL,
+ NULL,
+ NULL);
+
+ prop = RNA_def_property(srna, "first_point_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_CurveSlice_first_point_index_get", NULL, NULL);
+ RNA_def_property_ui_text(
+ prop, "First Point Index", "The index of this curve's first control point");
+
+ prop = RNA_def_property(srna, "points_length", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_CurveSlice_points_length_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Number of Points", "Number of control points in the curve");
+
+ prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_CurveSlice_index_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Index", "Index of this curve");
+}
+
+static void rna_def_curves(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "Curves", "ID");
+ RNA_def_struct_ui_text(srna, "Hair Curves", "Hair data-block for hair curves");
+ RNA_def_struct_ui_icon(srna, ICON_CURVES_DATA);
+
+ /* Point and Curve RNA API helpers. */
+
+ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", "geometry.curve_size");
+ RNA_def_property_struct_type(prop, "CurveSlice");
+ RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block");
+
+ /* TODO: better solution for (*co)[3] parsing issue. */
+
+ RNA_define_verify_sdna(0);
+ prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size");
+ RNA_def_property_struct_type(prop, "CurvePoint");
+ RNA_def_property_ui_text(prop, "Points", "Control points of all curves");
+ RNA_define_verify_sdna(1);
+
+ /* Direct access to built-in attributes. */
+
+ RNA_define_verify_sdna(0);
+ prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size");
+ RNA_def_property_struct_type(prop, "FloatVectorAttributeValue");
+ RNA_def_property_update(prop, 0, "rna_Curves_update_data");
+ RNA_define_verify_sdna(1);
+
+ prop = RNA_def_property(srna, "curve_offset_data", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", NULL);
+ RNA_def_property_struct_type(prop, "IntAttributeValue");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Curves_curve_offset_data_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_Curves_curve_offset_data_length",
+ NULL,
+ NULL,
+ NULL);
+ RNA_def_property_update(prop, 0, "rna_Curves_update_data");
+
+ /* materials */
+ prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_ui_text(prop, "Materials", "");
+ RNA_def_property_srna(prop, "IDMaterials"); /* see rna_ID.c */
+ RNA_def_property_collection_funcs(
+ prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int");
+
+ /* attributes */
+ rna_def_attributes_common(srna);
+
+ /* common */
+ rna_def_animdata_common(srna);
+}
+
+void RNA_def_curves(BlenderRNA *brna)
+{
+ rna_def_curves_point(brna);
+ rna_def_curves_curve(brna);
+ rna_def_curves(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 3f380cd1830..7ef2f757cd8 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -2228,7 +2228,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_solo_mode", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_SOLO_MODE);
RNA_def_property_ui_text(
- prop, "Solo Mode", "In Paint mode display only layers with keyframe in current frame");
+ prop, "Solo Mode", "In Draw Mode only display layers with keyframe in current frame");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
/* Layer is used as Ruler. */
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 086da4c31ba..e4e594cd8cf 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -3211,6 +3211,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop, "Boundaries", "Filter feature lines based on face mark boundaries");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_face_mark_keep_contour", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(
+ prop, NULL, "calculation_flags", LRT_FILTER_FACE_MARK_KEEP_CONTOUR);
+ RNA_def_property_ui_text(prop, "Keep Contour", "Preserve contour lines while filtering");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "chaining_image_threshold", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_ui_text(
prop,
@@ -3231,6 +3237,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop, "Use Geometry Space", "Use geometry distance for chaining instead of image space");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_detail_preserve", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_CHAIN_PRESERVE_DETAILS);
+ RNA_def_property_ui_text(
+ prop, "Preserve Details", "Keep the zig-zag \"noise\" in initial chaining");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "use_overlap_edge_type_support", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_ALLOW_OVERLAP_EDGE_TYPES);
RNA_def_property_ui_text(prop,
@@ -3444,6 +3456,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop,
"Image Boundary Trimming",
"Trim all edges right at the boundary of image(including overscan region)");
+
+ prop = RNA_def_property(srna, "use_back_face_culling", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_BACK_FACE_CULLING);
+ RNA_def_property_ui_text(
+ prop,
+ "Back Face Culling",
+ "Remove all back faces to speed up calculation, this will create edges in "
+ "different occlusion levels than when disabled");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_invert_collection", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_hair.c b/source/blender/makesrna/intern/rna_hair.c
deleted file mode 100644
index 4ca66c6b583..00000000000
--- a/source/blender/makesrna/intern/rna_hair.c
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup RNA
- */
-
-#include <stdlib.h>
-
-#include "RNA_define.h"
-#include "RNA_enum_types.h"
-
-#include "rna_internal.h"
-
-#include "DNA_hair_types.h"
-
-#include "BLI_math_base.h"
-#include "BLI_string.h"
-
-#ifdef RNA_RUNTIME
-
-# include "BLI_math_vector.h"
-
-# include "BKE_attribute.h"
-# include "BKE_hair.h"
-
-# include "DEG_depsgraph.h"
-
-# include "WM_api.h"
-# include "WM_types.h"
-
-static Hair *rna_hair(PointerRNA *ptr)
-{
- return (Hair *)ptr->owner_id;
-}
-
-static int rna_HairPoint_index_get(PointerRNA *ptr)
-{
- const Hair *hair = rna_hair(ptr);
- const float(*co)[3] = ptr->data;
- return (int)(co - hair->co);
-}
-
-static void rna_HairPoint_location_get(PointerRNA *ptr, float value[3])
-{
- copy_v3_v3(value, (const float *)ptr->data);
-}
-
-static void rna_HairPoint_location_set(PointerRNA *ptr, const float value[3])
-{
- copy_v3_v3((float *)ptr->data, value);
-}
-
-static float rna_HairPoint_radius_get(PointerRNA *ptr)
-{
- const Hair *hair = rna_hair(ptr);
- if (hair->radius == NULL) {
- return 0.0f;
- }
- const float(*co)[3] = ptr->data;
- return hair->radius[co - hair->co];
-}
-
-static void rna_HairPoint_radius_set(PointerRNA *ptr, float value)
-{
- const Hair *hair = rna_hair(ptr);
- if (hair->radius == NULL) {
- return;
- }
- const float(*co)[3] = ptr->data;
- hair->radius[co - hair->co] = value;
-}
-
-static char *rna_HairPoint_path(PointerRNA *ptr)
-{
- return BLI_sprintfN("points[%d]", rna_HairPoint_index_get(ptr));
-}
-
-static int rna_HairCurve_index_get(PointerRNA *ptr)
-{
- Hair *hair = rna_hair(ptr);
- return (int)((HairCurve *)ptr->data - hair->curves);
-}
-
-static char *rna_HairCurve_path(PointerRNA *ptr)
-{
- return BLI_sprintfN("curves[%d]", rna_HairCurve_index_get(ptr));
-}
-
-static void rna_HairCurve_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
-{
- Hair *hair = rna_hair(ptr);
- HairCurve *curve = ptr->data;
- float(*co)[3] = hair->co + curve->firstpoint;
- rna_iterator_array_begin(iter, co, sizeof(float[3]), curve->numpoints, 0, NULL);
-}
-
-static int rna_HairCurve_points_length(PointerRNA *ptr)
-{
- HairCurve *curve = ptr->data;
- return curve->numpoints;
-}
-
-static void rna_Hair_update_data(struct Main *UNUSED(bmain),
- struct Scene *UNUSED(scene),
- PointerRNA *ptr)
-{
- ID *id = ptr->owner_id;
-
- /* cheating way for importers to avoid slow updates */
- if (id->us > 0) {
- DEG_id_tag_update(id, 0);
- WM_main_add_notifier(NC_GEOM | ND_DATA, id);
- }
-}
-
-#else
-
-static void rna_def_hair_point(BlenderRNA *brna)
-{
- StructRNA *srna;
- PropertyRNA *prop;
-
- srna = RNA_def_struct(brna, "HairPoint", NULL);
- RNA_def_struct_ui_text(srna, "Hair Point", "Hair curve control point");
- RNA_def_struct_path_func(srna, "rna_HairPoint_path");
-
- prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION);
- RNA_def_property_array(prop, 3);
- RNA_def_property_float_funcs(
- prop, "rna_HairPoint_location_get", "rna_HairPoint_location_set", NULL);
- RNA_def_property_ui_text(prop, "Location", "");
- RNA_def_property_update(prop, 0, "rna_Hair_update_data");
-
- prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_DISTANCE);
- RNA_def_property_float_funcs(prop, "rna_HairPoint_radius_get", "rna_HairPoint_radius_set", NULL);
- RNA_def_property_ui_text(prop, "Radius", "");
- RNA_def_property_update(prop, 0, "rna_Hair_update_data");
-
- prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- RNA_def_property_int_funcs(prop, "rna_HairPoint_index_get", NULL, NULL);
- RNA_def_property_ui_text(prop, "Index", "Index of this points");
-}
-
-static void rna_def_hair_curve(BlenderRNA *brna)
-{
- StructRNA *srna;
- PropertyRNA *prop;
-
- srna = RNA_def_struct(brna, "HairCurve", NULL);
- RNA_def_struct_ui_text(srna, "Hair Curve", "Hair curve");
- RNA_def_struct_path_func(srna, "rna_HairCurve_path");
-
- prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_struct_type(prop, "HairPoint");
- RNA_def_property_ui_text(prop, "Points", "Control points of the curve");
- RNA_def_property_collection_funcs(prop,
- "rna_HairCurve_points_begin",
- "rna_iterator_array_next",
- "rna_iterator_array_end",
- "rna_iterator_array_get",
- "rna_HairCurve_points_length",
- NULL,
- NULL,
- NULL);
-
- /* TODO: naming consistency, editable? */
- prop = RNA_def_property(srna, "first_point_index", PROP_INT, PROP_UNSIGNED);
- RNA_def_property_int_sdna(prop, NULL, "firstpoint");
- RNA_def_property_ui_text(prop, "First Point Index", "Index of the first loop of this polygon");
-
- prop = RNA_def_property(srna, "num_points", PROP_INT, PROP_UNSIGNED);
- RNA_def_property_int_sdna(prop, NULL, "numpoints");
- RNA_def_property_ui_text(prop, "Number of Points", "Number of loops used by this polygon");
-
- prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- RNA_def_property_int_funcs(prop, "rna_HairCurve_index_get", NULL, NULL);
- RNA_def_property_ui_text(prop, "Index", "Index of this curve");
-}
-
-static void rna_def_hair(BlenderRNA *brna)
-{
- StructRNA *srna;
- PropertyRNA *prop;
-
- srna = RNA_def_struct(brna, "Hair", "ID");
- RNA_def_struct_ui_text(srna, "Hair", "Hair data-block for hair curves");
- RNA_def_struct_ui_icon(srna, ICON_HAIR_DATA);
-
- /* geometry */
- prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "curves", "totcurve");
- RNA_def_property_struct_type(prop, "HairCurve");
- RNA_def_property_ui_text(prop, "Curves", "All hair curves");
-
- /* TODO: better solution for (*co)[3] parsing issue. */
- RNA_define_verify_sdna(0);
- prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "co", "totpoint");
- RNA_def_property_struct_type(prop, "HairPoint");
- RNA_def_property_ui_text(prop, "Points", "Control points of all hair curves");
- RNA_define_verify_sdna(1);
-
- /* materials */
- prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
- RNA_def_property_struct_type(prop, "Material");
- RNA_def_property_ui_text(prop, "Materials", "");
- RNA_def_property_srna(prop, "IDMaterials"); /* see rna_ID.c */
- RNA_def_property_collection_funcs(
- prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int");
-
- /* attributes */
- rna_def_attributes_common(srna);
-
- /* common */
- rna_def_animdata_common(srna);
-}
-
-void RNA_def_hair(BlenderRNA *brna)
-{
- rna_def_hair_point(brna);
- rna_def_hair_curve(brna);
- rna_def_hair(brna);
-}
-
-#endif
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index 0d86572357f..af13baad5a2 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -1107,7 +1107,7 @@ static void rna_def_image(BlenderRNA *brna)
prop, "Duration", "Duration (in frames) of the image (1 when not a video/sequence)");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- /* NOTE about pixels/channels/is_float:
+ /* NOTE: About pixels/channels/is_float:
* These properties describe how the image is stored internally (inside of ImBuf),
* not how it was saved to disk or how it'll be saved on disk.
*/
diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c
index 7d2697c8770..1c04805be8b 100644
--- a/source/blender/makesrna/intern/rna_image_api.c
+++ b/source/blender/makesrna/intern/rna_image_api.c
@@ -243,7 +243,7 @@ static int rna_Image_gl_touch(
BKE_image_tag_time(image);
- if (image->gputexture[TEXTARGET_2D][0] == NULL) {
+ if (image->gputexture[TEXTARGET_2D][0][IMA_TEXTURE_RESOLUTION_FULL] == NULL) {
error = rna_Image_gl_load(image, reports, frame, layer_index, pass_index);
}
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 95ad184c6b9..407f474ddab 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -170,7 +170,7 @@ void RNA_def_fcurve(struct BlenderRNA *brna);
void RNA_def_gpencil(struct BlenderRNA *brna);
void RNA_def_greasepencil_modifier(struct BlenderRNA *brna);
void RNA_def_shader_fx(struct BlenderRNA *brna);
-void RNA_def_hair(struct BlenderRNA *brna);
+void RNA_def_curves(struct BlenderRNA *brna);
void RNA_def_image(struct BlenderRNA *brna);
void RNA_def_key(struct BlenderRNA *brna);
void RNA_def_light(struct BlenderRNA *brna);
@@ -369,6 +369,14 @@ void rna_ViewLayer_active_aov_index_range(
PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax);
int rna_ViewLayer_active_aov_index_get(PointerRNA *ptr);
void rna_ViewLayer_active_aov_index_set(PointerRNA *ptr, int value);
+/**
+ * Set `r_rna_path` with the base view-layer path.
+ * `rna_path_buffer_size` should be at least `sizeof(ViewLayer.name) * 3`.
+ * \return actual length of the generated RNA path.
+ */
+size_t rna_ViewLayer_path_buffer_get(struct ViewLayer *view_layer,
+ char *r_rna_path,
+ const size_t rna_path_buffer_size);
/* named internal so as not to conflict with obj.update() rna func */
void rna_Object_internal_update_data(struct Main *bmain,
@@ -488,12 +496,10 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop);
-#ifdef WITH_HAIR_NODES
-void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop);
+#ifdef WITH_NEW_CURVES_TYPE
+void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop);
#endif
-#ifdef WITH_POINT_CLOUD
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
-#endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
#ifdef WITH_SIMULATION_DATABLOCK
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop);
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index ab4cbc429ce..278d611cc41 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -53,6 +53,8 @@
# include "BKE_node.h"
# include "BKE_scene.h"
+# include "NOD_composite.h"
+
# include "BLI_listbase.h"
# include "DEG_depsgraph_build.h"
@@ -110,13 +112,24 @@ static void rna_LayerObjects_active_object_set(PointerRNA *ptr,
}
}
+size_t rna_ViewLayer_path_buffer_get(ViewLayer *view_layer,
+ char *r_rna_path,
+ const size_t rna_path_buffer_size)
+{
+ char name_esc[sizeof(view_layer->name) * 2];
+ BLI_str_escape(name_esc, view_layer->name, sizeof(name_esc));
+
+ return BLI_snprintf_rlen(r_rna_path, rna_path_buffer_size, "view_layers[\"%s\"]", name_esc);
+}
+
static char *rna_ViewLayer_path(PointerRNA *ptr)
{
- ViewLayer *srl = (ViewLayer *)ptr->data;
- char name_esc[sizeof(srl->name) * 2];
+ ViewLayer *view_layer = (ViewLayer *)ptr->data;
+ char rna_path[sizeof(view_layer->name) * 3];
+
+ rna_ViewLayer_path_buffer_get(view_layer, rna_path, sizeof(rna_path));
- BLI_str_escape(name_esc, srl->name, sizeof(name_esc));
- return BLI_sprintfN("view_layers[\"%s\"]", name_esc);
+ return BLI_strdup(rna_path);
}
static IDProperty **rna_ViewLayer_idprops(PointerRNA *ptr)
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index a162aa26b78..230c04bfd9c 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -110,8 +110,8 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections)
RNA_MAIN_LISTBASE_FUNCS_DEF(curves)
RNA_MAIN_LISTBASE_FUNCS_DEF(fonts)
RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils)
-# ifdef WITH_HAIR_NODES
-RNA_MAIN_LISTBASE_FUNCS_DEF(hairs)
+# ifdef WITH_NEW_CURVES_TYPE
+RNA_MAIN_LISTBASE_FUNCS_DEF(hair_curves)
# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(images)
RNA_MAIN_LISTBASE_FUNCS_DEF(lattices)
@@ -129,9 +129,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(objects)
RNA_MAIN_LISTBASE_FUNCS_DEF(paintcurves)
RNA_MAIN_LISTBASE_FUNCS_DEF(palettes)
RNA_MAIN_LISTBASE_FUNCS_DEF(particles)
-# ifdef WITH_POINT_CLOUD
RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds)
-# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(scenes)
RNA_MAIN_LISTBASE_FUNCS_DEF(screens)
RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys)
@@ -391,17 +389,24 @@ void RNA_def_main(BlenderRNA *brna)
"Light Probes",
"Light Probe data-blocks",
RNA_def_main_lightprobes},
-# ifdef WITH_HAIR_NODES
- {"hairs", "Hair", "rna_Main_hairs_begin", "Hairs", "Hair data-blocks", RNA_def_main_hairs},
+# ifdef WITH_NEW_CURVES_TYPE
+ /**
+ * \note The name `hair_curves` is chosen to be different than `curves`,
+ * but they are generic curve data-blocks, not just for hair.
+ */
+ {"hair_curves",
+ "Curves",
+ "rna_Main_hair_curves_begin",
+ "Hair Curves",
+ "Hair curve data-blocks",
+ RNA_def_main_hair_curves},
# endif
-# ifdef WITH_POINT_CLOUD
{"pointclouds",
"PointCloud",
"rna_Main_pointclouds_begin",
"Point Clouds",
"Point cloud data-blocks",
RNA_def_main_pointclouds},
-# endif
{"volumes",
"Volume",
"rna_Main_volumes_begin",
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 0276c8a3f8a..f8d2d6524c2 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -46,9 +46,9 @@
# include "BKE_camera.h"
# include "BKE_collection.h"
# include "BKE_curve.h"
+# include "BKE_curves.h"
# include "BKE_displist.h"
# include "BKE_gpencil.h"
-# include "BKE_hair.h"
# include "BKE_icons.h"
# include "BKE_idtype.h"
# include "BKE_image.h"
@@ -86,8 +86,8 @@
# include "DNA_camera_types.h"
# include "DNA_collection_types.h"
# include "DNA_curve_types.h"
+# include "DNA_curves_types.h"
# include "DNA_gpencil_types.h"
-# include "DNA_hair_types.h"
# include "DNA_lattice_types.h"
# include "DNA_light_types.h"
# include "DNA_lightprobe_types.h"
@@ -763,22 +763,21 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name)
return gpd;
}
-# ifdef WITH_HAIR_NODES
-static Hair *rna_Main_hairs_new(Main *bmain, const char *name)
+# ifdef WITH_NEW_CURVES_TYPE
+static Curves *rna_Main_hair_curves_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
rna_idname_validate(name, safe_name);
- Hair *hair = BKE_hair_add(bmain, safe_name);
- id_us_min(&hair->id);
+ Curves *curves = BKE_curves_add(bmain, safe_name);
+ id_us_min(&curves->id);
WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
- return hair;
+ return curves;
}
# endif
-# ifdef WITH_POINT_CLOUD
static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -791,7 +790,6 @@ static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
return pointcloud;
}
-# endif
static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
{
@@ -863,12 +861,10 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF)
RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC)
RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS)
RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP)
-# ifdef WITH_HAIR_NODES
-RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA)
+# ifdef WITH_NEW_CURVES_TYPE
+RNA_MAIN_ID_TAG_FUNCS_DEF(hair_curves, hair_curves, ID_CV)
# endif
-# ifdef WITH_POINT_CLOUD
RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT)
-# endif
RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO)
# ifdef WITH_SIMULATION_DATABLOCK
RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM)
@@ -2273,53 +2269,52 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
-# ifdef WITH_HAIR_NODES
-void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop)
+# ifdef WITH_NEW_CURVES_TYPE
+void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
FunctionRNA *func;
PropertyRNA *parm;
- RNA_def_property_srna(cprop, "BlendDataHairs");
- srna = RNA_def_struct(brna, "BlendDataHairs", NULL);
+ RNA_def_property_srna(cprop, "BlendDataHairCurves");
+ srna = RNA_def_struct(brna, "BlendDataHairCurves", NULL);
RNA_def_struct_sdna(srna, "Main");
- RNA_def_struct_ui_text(srna, "Main Hairs", "Collection of hairs");
+ RNA_def_struct_ui_text(srna, "Main Hair Curves", "Collection of hair curves");
- func = RNA_def_function(srna, "new", "rna_Main_hairs_new");
+ func = RNA_def_function(srna, "new", "rna_Main_hair_curves_new");
RNA_def_function_ui_description(func, "Add a new hair to the main database");
- parm = RNA_def_string(func, "name", "Hair", 0, "", "New name for the data-block");
+ parm = RNA_def_string(func, "name", "Curves", 0, "", "New name for the data-block");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return type */
- parm = RNA_def_pointer(func, "hair", "Hair", "", "New hair data-block");
+ parm = RNA_def_pointer(func, "curves", "Curves", "", "New curves data-block");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a hair from the current blendfile");
- parm = RNA_def_pointer(func, "hair", "Hair", "", "Hair to remove");
+ RNA_def_function_ui_description(func, "Remove a curves data-block from the current blendfile");
+ parm = RNA_def_pointer(func, "curves", "Curves", "", "Curves data-block to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
RNA_def_boolean(func,
"do_unlink",
true,
"",
- "Unlink all usages of this hair before deleting it "
- "(WARNING: will also delete objects instancing that hair data)");
+ "Unlink all usages of this curves before deleting it "
+ "(WARNING: will also delete objects instancing that curves data)");
RNA_def_boolean(func,
"do_id_user",
true,
"",
- "Decrement user counter of all datablocks used by this hair data");
+ "Decrement user counter of all datablocks used by this curves data");
RNA_def_boolean(
- func, "do_ui_user", true, "", "Make sure interface does not reference this hair data");
+ func, "do_ui_user", true, "", "Make sure interface does not reference this curves data");
- func = RNA_def_function(srna, "tag", "rna_Main_hairs_tag");
+ func = RNA_def_function(srna, "tag", "rna_Main_hair_curves_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
# endif
-# ifdef WITH_POINT_CLOUD
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -2366,7 +2361,6 @@ void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
-# endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop)
{
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 22a75c0d992..6dea0dbbf7d 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -718,7 +718,7 @@ void RNA_def_material(BlenderRNA *brna)
{MA_FLAT, "FLAT", ICON_MATPLANE, "Flat", "Flat XY plane"},
{MA_SPHERE, "SPHERE", ICON_MATSPHERE, "Sphere", "Sphere"},
{MA_CUBE, "CUBE", ICON_MATCUBE, "Cube", "Cube"},
- {MA_HAIR, "HAIR", ICON_HAIR, "Hair", "Hair strands"},
+ {MA_HAIR, "HAIR", ICON_CURVES, "Hair", "Hair strands"},
{MA_SHADERBALL, "SHADERBALL", ICON_MATSHADERBALL, "Shader Ball", "Shader ball"},
{MA_CLOTH, "CLOTH", ICON_MATCLOTH, "Cloth", "Cloth"},
{MA_FLUID, "FLUID", ICON_MATFLUID, "Fluid", "Fluid"},
@@ -791,8 +791,8 @@ void RNA_def_material(BlenderRNA *brna)
RNA_def_property_boolean_negative_sdna(prop, NULL, "blend_flag", MA_BL_HIDE_BACKFACE);
RNA_def_property_ui_text(prop,
"Show Backface",
- "Limit transparency to a single layer "
- "(avoids transparency sorting problems)");
+ "Render multiple transparent layers "
+ "(may introduce transparency sorting problems)");
RNA_def_property_update(prop, 0, "rna_Material_draw_update");
prop = RNA_def_property(srna, "use_backface_culling", PROP_BOOLEAN, PROP_NONE);
@@ -819,7 +819,7 @@ void RNA_def_material(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Refraction Depth",
"Approximate the thickness of the object to compute two refraction "
- "event (0 is disabled)");
+ "events (0 is disabled)");
RNA_def_property_update(prop, 0, "rna_Material_draw_update");
/* For Preview Render */
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 552cd836d76..55b70fd1b41 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -3120,6 +3120,7 @@ static void rna_def_mesh(BlenderRNA *brna)
prop = RNA_def_property(srna, "vertex_normals", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "MeshNormalValue");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop,
"Vertex Normals",
"The normal direction of each vertex, defined as the average of the "
@@ -3136,6 +3137,7 @@ static void rna_def_mesh(BlenderRNA *brna)
prop = RNA_def_property(srna, "polygon_normals", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "MeshNormalValue");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop,
"Polygon Normals",
"The normal direction of each polygon, defined by the winding order "
@@ -3157,7 +3159,7 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Loop Triangles", "Tessellation of mesh polygons into triangles");
rna_def_mesh_looptris(brna, prop);
- /* TODO: should this be allowed to be its self? */
+ /* TODO: should this be allowed to be itself? */
prop = RNA_def_property(srna, "texture_mesh", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "texcomesh");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
@@ -3246,6 +3248,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshVertColorLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "All vertex colors");
rna_def_vert_colors(brna, prop);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 775c7a68f65..bd74f86c79a 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -300,9 +300,33 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = {
};
const EnumPropertyItem rna_enum_node_boolean_math_items[] = {
- {NODE_BOOLEAN_MATH_AND, "AND", 0, "And", "Outputs true only when both inputs are true"},
- {NODE_BOOLEAN_MATH_OR, "OR", 0, "Or", "Outputs or when at least one of the inputs is true"},
- {NODE_BOOLEAN_MATH_NOT, "NOT", 0, "Not", "Outputs the opposite of the input"},
+ {NODE_BOOLEAN_MATH_AND, "AND", 0, "And", "True when both inputs are true"},
+ {NODE_BOOLEAN_MATH_OR, "OR", 0, "Or", "True when at least one input is true"},
+ {NODE_BOOLEAN_MATH_NOT, "NOT", 0, "Not", "Opposite of the input"},
+ {0, "", ICON_NONE, NULL, NULL},
+ {NODE_BOOLEAN_MATH_NAND, "NAND", 0, "Not And", "True when at least one input is false"},
+ {NODE_BOOLEAN_MATH_NOR, "NOR", 0, "Nor", "True when both inputs are false"},
+ {NODE_BOOLEAN_MATH_XNOR,
+ "XNOR",
+ 0,
+ "Equal",
+ "True when both inputs are equal (exclusive nor)"},
+ {NODE_BOOLEAN_MATH_XOR,
+ "XOR",
+ 0,
+ "Not Equal",
+ "True when both inputs are different (exclusive or)"},
+ {0, "", ICON_NONE, NULL, NULL},
+ {NODE_BOOLEAN_MATH_IMPLY,
+ "IMPLY",
+ 0,
+ "Imply",
+ "True unless the first input is true and the second is false"},
+ {NODE_BOOLEAN_MATH_NIMPLY,
+ "NIMPLY",
+ 0,
+ "Subtract",
+ "True when the first input is true and the second is false (not imply)"},
{0, NULL, 0, NULL, NULL},
};
@@ -441,7 +465,8 @@ static const EnumPropertyItem rna_enum_node_tex_dimensions_items[] = {
const EnumPropertyItem rna_enum_node_filter_items[] = {
{0, "SOFTEN", 0, "Soften", ""},
- {1, "SHARPEN", 0, "Sharpen", ""},
+ {1, "SHARPEN", 0, "Box Sharpen", "An aggressive sharpening filter"},
+ {7, "SHARPEN_DIAMOND", 0, "Diamond Sharpen", "A moderate sharpening filter"},
{2, "LAPLACE", 0, "Laplace", ""},
{3, "SOBEL", 0, "Sobel", ""},
{4, "PREWITT", 0, "Prewitt", ""},
@@ -631,6 +656,7 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_bo
# include "NOD_geometry.h"
# include "NOD_shader.h"
# include "NOD_socket.h"
+# include "NOD_texture.h"
# include "RE_engine.h"
# include "RE_pipeline.h"
@@ -9894,6 +9920,27 @@ static void def_geo_point_distribute(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_extrude_mesh(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem mode_items[] = {
+ {GEO_NODE_EXTRUDE_MESH_VERTICES, "VERTICES", 0, "Vertices", ""},
+ {GEO_NODE_EXTRUDE_MESH_EDGES, "EDGES", 0, "Edges", ""},
+ {GEO_NODE_EXTRUDE_MESH_FACES, "FACES", 0, "Faces", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryExtrudeMesh", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "mode");
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_EXTRUDE_MESH_FACES);
+ RNA_def_property_ui_text(prop, "Mode", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_distribute_points_on_faces(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10139,6 +10186,33 @@ static void def_geo_curve_primitive_circle(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_curve_primitive_arc(StructRNA *srna)
+{
+ static const EnumPropertyItem mode_items[] = {
+
+ {GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS,
+ "POINTS",
+ ICON_NONE,
+ "Points",
+ "Define arc by 3 points on circle. Arc is calculated between start and end points"},
+ {GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS,
+ "RADIUS",
+ ICON_NONE,
+ "Radius",
+ "Define radius with a float"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurvePrimitiveArc", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Method used to determine radius and placement");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_curve_primitive_line(StructRNA *srna)
{
static const EnumPropertyItem mode_items[] = {
@@ -10451,7 +10525,7 @@ static void def_geo_object_info(StructRNA *srna)
RNA_def_property_enum_items(prop, rna_node_geometry_object_info_transform_space_items);
RNA_def_property_ui_text(
prop, "Transform Space", "The transformation of the vector and geometry outputs");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations");
}
static void def_geo_legacy_points_to_volume(StructRNA *srna)
@@ -10535,7 +10609,7 @@ static void def_geo_collection_info(StructRNA *srna)
prop = RNA_def_property(srna, "transform_space", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_collection_info_transform_space_items);
RNA_def_property_ui_text(prop, "Transform Space", "The transformation of the geometry output");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations");
}
static void def_geo_legacy_attribute_proximity(StructRNA *srna)
@@ -11305,6 +11379,33 @@ static void def_geo_string_to_curves(StructRNA *srna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_pivot_mode[] = {
+ {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_MIDPOINT, "MIDPOINT", 0, "Midpoint", "Midpoint"},
+ {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_LEFT, "TOP_LEFT", 0, "Top Left", "Top Left"},
+ {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_CENTER,
+ "TOP_CENTER",
+ 0,
+ "Top Center",
+ "Top Center"},
+ {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_RIGHT, "TOP_RIGHT", 0, "Top Right", "Top Right"},
+ {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT,
+ "BOTTOM_LEFT",
+ 0,
+ "Bottom Left",
+ "Bottom Left"},
+ {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_CENTER,
+ "BOTTOM_CENTER",
+ 0,
+ "Bottom Center",
+ "Bottom Center"},
+ {GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_RIGHT,
+ "BOTTOM_RIGHT",
+ 0,
+ "Bottom Right",
+ "Bottom Right"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
PropertyRNA *prop;
prop = RNA_def_property(srna, "font", PROP_POINTER, PROP_NONE);
@@ -11337,6 +11438,13 @@ static void def_geo_string_to_curves(StructRNA *srna)
RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE);
RNA_def_property_ui_text(prop, "Align Y", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "pivot_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "pivot_mode");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_pivot_mode);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT);
+ RNA_def_property_ui_text(prop, "Pivot Point", "Pivot point position relative to character");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_separate_geometry(StructRNA *srna)
@@ -11395,6 +11503,53 @@ static void def_geo_field_at_index(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
}
+static void def_geo_scale_elements(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem domain_items[] = {
+ {ATTR_DOMAIN_FACE,
+ "FACE",
+ ICON_NONE,
+ "Face",
+ "Scale individual faces or neighboring face islands"},
+ {ATTR_DOMAIN_EDGE,
+ "EDGE",
+ ICON_NONE,
+ "Edge",
+ "Scale individual edges or neighboring edge islands"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem scale_mode_items[] = {
+ {GEO_NODE_SCALE_ELEMENTS_UNIFORM,
+ "UNIFORM",
+ ICON_NONE,
+ "Uniform",
+ "Scale elements by the same factor in every direction"},
+ {GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS,
+ "SINGLE_AXIS",
+ ICON_NONE,
+ "Single Axis",
+ "Scale elements in a single direction"},
+ {0, NULL, 0, NULL, NULL},
+
+ };
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, domain_items);
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_FACE);
+ RNA_def_property_ui_text(prop, "Domain", "Element type to transform");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+
+ prop = RNA_def_property(srna, "scale_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom2");
+ RNA_def_property_enum_items(prop, scale_mode_items);
+ RNA_def_property_ui_text(prop, "Scale Mode", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 0cb132786cd..be37e574c9c 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -271,7 +271,7 @@ const EnumPropertyItem rna_enum_object_type_items[] = {
OBTYPE_CU_SURF,
{OB_MBALL, "META", ICON_OUTLINER_OB_META, "Metaball", ""},
OBTYPE_CU_FONT,
- {OB_HAIR, "HAIR", ICON_OUTLINER_OB_HAIR, "Hair", ""},
+ {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Hair Curves", ""},
{OB_POINTCLOUD, "POINTCLOUD", ICON_OUTLINER_OB_POINTCLOUD, "Point Cloud", ""},
{OB_VOLUME, "VOLUME", ICON_OUTLINER_OB_VOLUME, "Volume", ""},
{OB_GPENCIL, "GPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil", ""},
@@ -613,18 +613,14 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr)
return &RNA_LightProbe;
case OB_GPENCIL:
return &RNA_GreasePencil;
- case OB_HAIR:
-# ifdef WITH_HAIR_NODES
- return &RNA_Hair;
+ case OB_CURVES:
+# ifdef WITH_NEW_CURVES_TYPE
+ return &RNA_Curves;
# else
return &RNA_ID;
# endif
case OB_POINTCLOUD:
-# ifdef WITH_POINT_CLOUD
return &RNA_PointCloud;
-# else
- return &RNA_ID;
-# endif
case OB_VOLUME:
return &RNA_Volume;
default:
@@ -3154,19 +3150,6 @@ static void rna_def_object(BlenderRNA *brna)
"Align to Vertex Normal is enabled)");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update");
- /* proxy */
- prop = RNA_def_property(srna, "proxy", PROP_POINTER, PROP_NONE);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
- RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_ui_text(prop, "Proxy", "Library object this proxy object controls");
-
- prop = RNA_def_property(srna, "proxy_collection", PROP_POINTER, PROP_NONE);
- RNA_def_property_pointer_sdna(prop, NULL, "proxy_group");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
- RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_ui_text(
- prop, "Proxy Collection", "Library collection duplicator object this proxy object controls");
-
/* materials */
prop = RNA_def_property(srna, "material_slots", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "MaterialSlot");
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index ec20fa54a44..dbf9b757728 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -68,6 +68,7 @@ static const EnumPropertyItem space_items[] = {
# include "BKE_bvhutils.h"
# include "BKE_constraint.h"
# include "BKE_context.h"
+# include "BKE_crazyspace.h"
# include "BKE_customdata.h"
# include "BKE_global.h"
# include "BKE_layer.h"
@@ -379,6 +380,39 @@ static void rna_Object_camera_fit_coords(
depsgraph, (const float(*)[3])cos, num_cos / 3, ob, co_ret, scale_ret);
}
+static void rna_Object_crazyspace_eval(Object *object,
+ ReportList *reports,
+ Depsgraph *depsgraph,
+ Scene *scene)
+{
+ BKE_crazyspace_api_eval(depsgraph, scene, object, reports);
+}
+
+static void rna_Object_crazyspace_displacement_to_deformed(Object *object,
+ ReportList *reports,
+ const int vertex_index,
+ float displacement[3],
+ float r_displacement_deformed[3])
+{
+ BKE_crazyspace_api_displacement_to_deformed(
+ object, reports, vertex_index, displacement, r_displacement_deformed);
+}
+
+static void rna_Object_crazyspace_displacement_to_original(Object *object,
+ ReportList *reports,
+ const int vertex_index,
+ float displacement_deformed[3],
+ float r_displacement[3])
+{
+ BKE_crazyspace_api_displacement_to_original(
+ object, reports, vertex_index, displacement_deformed, r_displacement);
+}
+
+static void rna_Object_crazyspace_eval_clear(Object *object)
+{
+ BKE_crazyspace_api_eval_clear(object);
+}
+
/* copied from Mesh_getFromObject and adapted to RNA interface */
static Mesh *rna_Object_to_mesh(Object *object,
ReportList *reports,
@@ -978,6 +1012,52 @@ void RNA_api_object(StructRNA *srna)
parm, "", "The ortho scale to aim to be able to see all given points (if relevant)");
RNA_def_parameter_flags(parm, 0, PARM_OUTPUT);
+ /* Crazy-space access. */
+
+ func = RNA_def_function(srna, "crazyspace_eval", "rna_Object_crazyspace_eval");
+ RNA_def_function_ui_description(
+ func,
+ "Compute orientation mapping between vertices of an original object and object with shape "
+ "keys and deforming modifiers applied."
+ "The evaluation is to be freed with the crazyspace_eval_free function");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(
+ func, "depsgraph", "Depsgraph", "Dependency Graph", "Evaluated dependency graph");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "scene", "Scene", "Scene", "Scene of the object");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+
+ func = RNA_def_function(srna,
+ "crazyspace_displacement_to_deformed",
+ "rna_Object_crazyspace_displacement_to_deformed");
+ RNA_def_function_ui_description(
+ func, "Convert displacement vector from non-deformed object space to deformed object space");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_property(func, "vertex_index", PROP_INT, PROP_NONE);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_property(func, "displacement", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_array(parm, 3);
+ parm = RNA_def_property(func, "displacement_deformed", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_array(parm, 3);
+ RNA_def_function_output(func, parm);
+
+ func = RNA_def_function(srna,
+ "crazyspace_displacement_to_original",
+ "rna_Object_crazyspace_displacement_to_original");
+ RNA_def_function_ui_description(
+ func, "Convert displacement vector from deformed object space to non-deformed object space");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_property(func, "vertex_index", PROP_INT, PROP_NONE);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_property(func, "displacement", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_array(parm, 3);
+ parm = RNA_def_property(func, "displacement_original", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_array(parm, 3);
+ RNA_def_function_output(func, parm);
+
+ RNA_def_function(srna, "crazyspace_eval_clear", "rna_Object_crazyspace_eval_clear");
+ RNA_def_function_ui_description(func, "Free evaluated state of crazyspace");
+
/* mesh */
func = RNA_def_function(srna, "to_mesh", "rna_Object_to_mesh");
RNA_def_function_ui_description(
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index fbc7625d815..df6c12ffb27 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -12,8 +12,8 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * Adaptive time step
- * Copyright 2011 AutoCRC
+ *
+ * Copyright 2011 AutoCRC (adaptive time step)
*/
/** \file
@@ -249,7 +249,8 @@ static void rna_ParticleHairKey_location_object_get(PointerRNA *ptr, float *valu
*
* Such trickery is needed to allow modification of hair keys in the original object using
* evaluated particle and object to access proper hair matrix. */
-static int hair_key_index_get(/*const*/ HairKey *hair_key,
+static int hair_key_index_get(const Object *object,
+ /*const*/ HairKey *hair_key,
/*const*/ ParticleSystemModifierData *modifier,
/*const*/ ParticleData *particle)
{
@@ -261,7 +262,7 @@ static int hair_key_index_get(/*const*/ HairKey *hair_key,
const int particle_index = particle - particle_system->particles;
const ParticleSystemModifierData *original_modifier = (ParticleSystemModifierData *)
- BKE_modifier_get_original(&modifier->modifier);
+ BKE_modifier_get_original(object, &modifier->modifier);
const ParticleSystem *original_particle_system = original_modifier->psys;
const ParticleData *original_particle = &original_particle_system->particles[particle_index];
@@ -288,7 +289,7 @@ static void hair_key_location_object_set(HairKey *hair_key,
NULL;
if (hair_mesh != NULL) {
- const int hair_key_index = hair_key_index_get(hair_key, modifier, particle);
+ const int hair_key_index = hair_key_index_get(object, hair_key, modifier, particle);
if (hair_key_index == -1) {
return;
}
@@ -368,7 +369,7 @@ static void rna_ParticleHairKey_co_object_set(ID *id,
/* Mark particle system as edited, so then particle_system_update() does not reset the hair
* keys from path. This behavior is similar to how particle edit mode sets flags. */
ParticleSystemModifierData *orig_modifier = (ParticleSystemModifierData *)
- modifier->modifier.orig_modifier_data;
+ BKE_modifier_get_original(object, &modifier->modifier);
orig_modifier->psys->flag |= PSYS_EDITED;
hair_key_location_object_set(hair_key, object, modifier, particle, co);
diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c
index 76bfea00a79..7714e4d1e59 100644
--- a/source/blender/makesrna/intern/rna_pose.c
+++ b/source/blender/makesrna/intern/rna_pose.c
@@ -140,7 +140,7 @@ static char *rna_PoseBone_path(PointerRNA *ptr)
static bool rna_bone_group_poll(Object *ob, ReportList *reports)
{
- if ((ob->proxy != NULL) || (ob->proxy_group != NULL) || ID_IS_OVERRIDE_LIBRARY(ob)) {
+ if (ID_IS_OVERRIDE_LIBRARY(ob)) {
BKE_report(reports, RPT_ERROR, "Cannot edit bone groups for proxies or library overrides");
return false;
}
@@ -717,7 +717,7 @@ static int rna_PoseChannel_proxy_editable(PointerRNA *ptr, const char **r_info)
bArmature *arm = ob->data;
bPoseChannel *pchan = (bPoseChannel *)ptr->data;
- if (ob->proxy && pchan->bone && (pchan->bone->layer & arm->layer_protected)) {
+ if (false && pchan->bone && (pchan->bone->layer & arm->layer_protected)) {
*r_info = "Can't edit property of a proxy on a protected layer";
return 0;
}
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index 3e150d03bf0..85955c5e9f7 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -2984,14 +2984,14 @@ static void rna_def_function(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"No Self",
- "Function does not pass its self as an argument (becomes a static method in python)");
+ "Function does not pass itself as an argument (becomes a static method in python)");
prop = RNA_def_property(srna, "use_self_type", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Function_use_self_type_get", NULL);
RNA_def_property_ui_text(prop,
"Use Self Type",
- "Function passes its self type as an argument (becomes a class method "
+ "Function passes itself type as an argument (becomes a class method "
"in python if use_self is false)");
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 5bfbd42866b..201ea5469cc 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -655,6 +655,8 @@ const EnumPropertyItem rna_enum_transform_orientation_items[] = {
# include "BKE_screen.h"
# include "BKE_unit.h"
+# include "NOD_composite.h"
+
# include "ED_image.h"
# include "ED_info.h"
# include "ED_keyframing.h"
@@ -1026,7 +1028,7 @@ static void rna_Scene_frame_update(Main *UNUSED(bmain),
PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_main_add_notifier(NC_SCENE | ND_FRAME, scene);
}
@@ -1767,6 +1769,20 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr)
rna_Scene_glsl_update(bmain, activescene, ptr);
}
+static char *rna_ViewLayerEEVEE_path(PointerRNA *ptr)
+{
+ ViewLayerEEVEE *view_layer_eevee = (ViewLayerEEVEE *)ptr->data;
+ ViewLayer *view_layer = (ViewLayer *)((uint8_t *)view_layer_eevee - offsetof(ViewLayer, eevee));
+ char rna_path[sizeof(view_layer->name) * 3];
+
+ const size_t view_layer_path_len = rna_ViewLayer_path_buffer_get(
+ view_layer, rna_path, sizeof(rna_path));
+
+ BLI_strncpy(rna_path + view_layer_path_len, ".eevee", sizeof(rna_path) - view_layer_path_len);
+
+ return BLI_strdup(rna_path);
+}
+
static char *rna_SceneRenderView_path(PointerRNA *ptr)
{
SceneRenderView *srv = (SceneRenderView *)ptr->data;
@@ -4017,6 +4033,7 @@ static void rna_def_view_layer_eevee(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "ViewLayerEEVEE", NULL);
+ RNA_def_struct_path_func(srna, "rna_ViewLayerEEVEE_path");
RNA_def_struct_ui_text(srna, "Eevee Settings", "View layer settings for Eevee");
prop = RNA_def_property(srna, "use_pass_volume_direct", PROP_BOOLEAN, PROP_NONE);
@@ -7911,8 +7928,10 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "clip");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "MovieClip");
- RNA_def_property_ui_text(
- prop, "Active Movie Clip", "Active movie clip used for constraints and viewport drawing");
+ RNA_def_property_ui_text(prop,
+ "Active Movie Clip",
+ "Active Movie Clip that can be used by motion tracking constraints "
+ "or as a camera's background image");
RNA_def_property_update(prop, NC_SCENE | ND_DRAW_RENDER_VIEWPORT, NULL);
/* color management */
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 6c3e3ab3058..15b3a2fe998 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -1456,6 +1456,12 @@ static void rna_def_strip_crop(BlenderRNA *brna)
RNA_def_struct_path_func(srna, "rna_SequenceCrop_path");
}
+static const EnumPropertyItem transform_filter_items[] = {
+ {SEQ_TRANSFORM_FILTER_NEAREST, "NEAREST", 0, "Nearest", ""},
+ {SEQ_TRANSFORM_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
static void rna_def_strip_transform(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1479,16 +1485,16 @@ static void rna_def_strip_transform(BlenderRNA *brna)
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
- prop = RNA_def_property(srna, "offset_x", PROP_INT, PROP_PIXEL);
- RNA_def_property_int_sdna(prop, NULL, "xofs");
+ prop = RNA_def_property(srna, "offset_x", PROP_FLOAT, PROP_PIXEL);
+ RNA_def_property_float_sdna(prop, NULL, "xofs");
RNA_def_property_ui_text(prop, "Translate X", "Move along X axis");
- RNA_def_property_ui_range(prop, INT_MIN, INT_MAX, 1, 6);
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 100, 3);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
- prop = RNA_def_property(srna, "offset_y", PROP_INT, PROP_PIXEL);
- RNA_def_property_int_sdna(prop, NULL, "yofs");
+ prop = RNA_def_property(srna, "offset_y", PROP_FLOAT, PROP_PIXEL);
+ RNA_def_property_float_sdna(prop, NULL, "yofs");
RNA_def_property_ui_text(prop, "Translate Y", "Move along Y axis");
- RNA_def_property_ui_range(prop, INT_MIN, INT_MAX, 1, 6);
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 100, 3);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE);
@@ -1502,6 +1508,13 @@ static void rna_def_strip_transform(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 1, 1, 3);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
+ prop = RNA_def_property(srna, "filter", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "filter");
+ RNA_def_property_enum_items(prop, transform_filter_items);
+ RNA_def_property_enum_default(prop, SEQ_TRANSFORM_FILTER_BILINEAR);
+ RNA_def_property_ui_text(prop, "Filter", "Type of filter to use for image transformation");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
+
RNA_def_struct_path_func(srna, "rna_SequenceTransform_path");
}
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index 7989c316c4c..e2e2bc2d5e8 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -328,8 +328,7 @@ static Sequence *rna_Sequences_new_movie(ID *id,
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.fit_method = fit_method;
load_data.allow_invalid_file = true;
- double start_offset = -1;
- Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &start_offset);
+ Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -378,7 +377,7 @@ static Sequence *rna_Sequences_new_sound(ID *id,
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.allow_invalid_file = true;
- Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data, 0.0f);
+ Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data);
if (seq == NULL) {
BKE_report(reports, RPT_ERROR, "Sequences.new_sound: unable to open sound file");
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 05c1a645823..07521d39256 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -1861,6 +1861,9 @@ static void rna_SpaceTextEditor_text_set(PointerRNA *ptr,
SpaceText *st = (SpaceText *)(ptr->data);
st->text = value.data;
+ if (st->text != NULL) {
+ id_us_ensure_real((ID *)st->text);
+ }
ScrArea *area = rna_area_from_space(ptr);
if (area) {
@@ -2337,7 +2340,8 @@ static void seq_build_proxy(bContext *C, PointerRNA *ptr)
seq->strip->proxy->build_size_flags |= SEQ_rendersize_to_proxysize(sseq->render_size);
/* Build proxy. */
- SEQ_proxy_rebuild_context(pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue);
+ SEQ_proxy_rebuild_context(
+ pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue, true);
}
BLI_gset_free(file_list, MEM_freeN);
@@ -3273,7 +3277,7 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[
ICON_OUTLINER_COLLECTION,
"Objects & Collections",
"Show objects and collections"},
- {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_HA |
+ {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_CV |
FILTER_ID_PT | FILTER_ID_VO,
"category_geometry",
ICON_NODETREE,
@@ -4304,7 +4308,7 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
prop = RNA_def_property(srna, "bone_wire_alpha", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "overlay.bone_wire_alpha");
RNA_def_property_ui_text(
- prop, "Bone Wireframe Opacity", "Maximim opacity of bones in wireframe display mode");
+ prop, "Bone Wireframe Opacity", "Maximum opacity of bones in wireframe display mode");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 2);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -5000,7 +5004,7 @@ static void rna_def_space_view3d(BlenderRNA *brna)
{"Surface", (1 << OB_SURF), {"show_object_viewport_surf", "show_object_select_surf"}},
{"Meta", (1 << OB_MBALL), {"show_object_viewport_meta", "show_object_select_meta"}},
{"Font", (1 << OB_FONT), {"show_object_viewport_font", "show_object_select_font"}},
- {"Hair", (1 << OB_HAIR), {"show_object_viewport_hair", "show_object_select_hair"}},
+ {"Hair", (1 << OB_CURVES), {"show_object_viewport_hair", "show_object_select_hair"}},
{"Point Cloud",
(1 << OB_POINTCLOUD),
{"show_object_viewport_pointcloud", "show_object_select_pointcloud"}},
diff --git a/source/blender/makesrna/intern/rna_texture_api.c b/source/blender/makesrna/intern/rna_texture_api.c
index 0920fe6679a..2a4cdaebcee 100644
--- a/source/blender/makesrna/intern/rna_texture_api.c
+++ b/source/blender/makesrna/intern/rna_texture_api.c
@@ -32,6 +32,7 @@
# include "BKE_context.h"
# include "BKE_global.h"
+# include "BLI_math.h"
# include "DNA_scene_types.h"
# include "IMB_imbuf.h"
# include "IMB_imbuf_types.h"
@@ -40,14 +41,12 @@
static void texture_evaluate(struct Tex *tex, float value[3], float r_color[4])
{
- TexResult texres = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL};
+ TexResult texres = {0.0f};
/* TODO(sergey): always use color management now. */
multitex_ext(tex, value, NULL, NULL, 1, &texres, 0, NULL, true, false);
- r_color[0] = texres.tr;
- r_color[1] = texres.tg;
- r_color[2] = texres.tb;
+ copy_v3_v3(r_color, texres.trgba);
r_color[3] = texres.tin;
}
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 4379b4ebe1d..53af3f5bed5 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -5293,7 +5293,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
prop, "Duplicate GPencil", "Causes grease pencil data to be duplicated with the object");
prop = RNA_def_property(srna, "use_duplicate_hair", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_HAIR);
+ RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_CURVES);
RNA_def_property_ui_text(
prop, "Duplicate Hair", "Causes hair data to be duplicated with the object");
@@ -6386,13 +6386,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"Enable library overrides automatic resync detection and process on file load. Disable when "
"dealing with older .blend files that need manual Resync (Enforce) handling");
- prop = RNA_def_property(srna, "proxy_to_override_auto_conversion", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_negative_sdna(prop, NULL, "no_proxy_to_override_conversion", 1);
- RNA_def_property_ui_text(
- prop,
- "Proxy to Override Auto Conversion",
- "Enable automatic conversion of proxies to library overrides on file load");
-
prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1);
RNA_def_property_ui_text(
@@ -6406,9 +6399,9 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"reduces execution time and memory usage)");
RNA_def_property_update(prop, 0, "rna_userdef_update");
- prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1);
- RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui");
+ prop = RNA_def_property(srna, "use_new_curves_type", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_new_curves_type", 1);
+ RNA_def_property_ui_text(prop, "New Curves Type", "Enable the new curves data type in the UI");
prop = RNA_def_property(srna, "use_cycles_debug", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_cycles_debug", 1);
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 8c128292fd7..178a50c4d29 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -965,7 +965,7 @@ static void rna_wmKeyMapItem_keymodifier_set(PointerRNA *ptr, int value)
if (value == EVT_ESCKEY) {
/* pass */
}
- else if (value >= EVT_AKEY) {
+ else if (ISKEYBOARD(value) && !ISKEYMODIFIER(value)) {
kmi->keymodifier = value;
}
else {
@@ -2175,7 +2175,8 @@ static void rna_def_event(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Mouse Previous Y Position", "The window relative vertical location of the mouse");
- prop = RNA_def_property(srna, "pressure", PROP_FLOAT, PROP_NONE);
+ prop = RNA_def_property(srna, "pressure", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_float_funcs(prop, "rna_Event_pressure_get", NULL, NULL);
RNA_def_property_ui_text(
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 88ac06b25eb..55c4b3e9705 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -183,10 +183,6 @@ if(WITH_BULLET)
add_definitions(-DWITH_BULLET)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
# To disable adaptive subdivision test in subsurf UI without cycles
if(WITH_CYCLES)
add_definitions(-DWITH_CYCLES)
@@ -236,8 +232,7 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
- add_definitions(-DWITH_POINT_CLOUD)
- add_definitions(-DWITH_HAIR_NODES)
+ add_definitions(-DWITH_NEW_CURVES_TYPE)
endif()
# So we can have special tricks in modifier system.
diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c
index 828b8b79664..a86a667974e 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -296,7 +296,6 @@ ModifierTypeInfo modifierType_Armature = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ deformMatricesEM,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index 56db68b163c..c2b1478c1b2 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -1037,7 +1037,6 @@ ModifierTypeInfo modifierType_Array = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index add95a0d248..984e85f58ef 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -446,7 +446,6 @@ ModifierTypeInfo modifierType_Bevel = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc
index bb05ae3e1b3..7b084d608fb 100644
--- a/source/blender/modifiers/intern/MOD_boolean.cc
+++ b/source/blender/modifiers/intern/MOD_boolean.cc
@@ -641,7 +641,6 @@ ModifierTypeInfo modifierType_Boolean = {
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 86f0df1418b..867c1e9b5bb 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -345,7 +345,6 @@ ModifierTypeInfo modifierType_Build = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index 715bc26e5d3..defc7df31dc 100644
--- a/source/blender/modifiers/intern/MOD_cast.c
+++ b/source/blender/modifiers/intern/MOD_cast.c
@@ -589,7 +589,6 @@ ModifierTypeInfo modifierType_Cast = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index 8aff29dc17d..4b8928009fe 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -308,7 +308,6 @@ ModifierTypeInfo modifierType_Cloth = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index 02e1f61b824..658a569627b 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -317,7 +317,6 @@ ModifierTypeInfo modifierType_Collision = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index 9e8f5bee396..d75c2a13587 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -588,7 +588,7 @@ static void correctivesmooth_modifier_do(ModifierData *md,
CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
const bool force_delta_cache_update =
- /* XXX, take care! if mesh data its self changes we need to forcefully recalculate deltas */
+ /* XXX, take care! if mesh data itself changes we need to forcefully recalculate deltas */
!cache_settings_equal(csmd) ||
((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO) &&
(((ID *)ob->data)->recalc & ID_RECALC_ALL));
@@ -610,7 +610,7 @@ static void correctivesmooth_modifier_do(ModifierData *md,
BLI_assert(csmd->bind_coords != NULL);
/* Copy bound data to the original modifier. */
CorrectiveSmoothModifierData *csmd_orig = (CorrectiveSmoothModifierData *)
- BKE_modifier_get_original(&csmd->modifier);
+ BKE_modifier_get_original(ob, &csmd->modifier);
csmd_orig->bind_coords = MEM_dupallocN(csmd->bind_coords);
csmd_orig->bind_coords_num = csmd->bind_coords_num;
}
@@ -850,7 +850,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index 20dbb299767..b01b70000b8 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -234,7 +234,6 @@ ModifierTypeInfo modifierType_Curve = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index 34bb93cbbbc..a289b9c918a 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -484,7 +484,6 @@ ModifierTypeInfo modifierType_DataTransfer = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 975f80a04f8..ad5391d2b6c 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -298,7 +298,6 @@ ModifierTypeInfo modifierType_Decimate = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index 010292d2ebb..94cdcad3b6a 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -262,9 +262,9 @@ static void displaceModifier_do_task(void *__restrict userdata,
}
break;
case MOD_DISP_DIR_RGB_XYZ:
- local_vec[0] = texres.tr - dmd->midlevel;
- local_vec[1] = texres.tg - dmd->midlevel;
- local_vec[2] = texres.tb - dmd->midlevel;
+ local_vec[0] = texres.trgba[0] - dmd->midlevel;
+ local_vec[1] = texres.trgba[1] - dmd->midlevel;
+ local_vec[2] = texres.trgba[2] - dmd->midlevel;
if (use_global_direction) {
mul_transposed_mat3_m4_v3(data->local_mat, local_vec);
}
@@ -508,7 +508,6 @@ ModifierTypeInfo modifierType_Displace = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index a696ce216c7..676433b14b1 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_DynamicPaint = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c
index 1039bcb2b3b..55707435e52 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -185,7 +185,6 @@ ModifierTypeInfo modifierType_EdgeSplit = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index 68d6b4a3626..c788633f978 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -1255,7 +1255,6 @@ ModifierTypeInfo modifierType_Explode = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index a21eb603300..748633b78b3 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -273,7 +273,6 @@ ModifierTypeInfo modifierType_Fluid = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index 18ce37c5d85..b4c081906ba 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -572,7 +572,6 @@ ModifierTypeInfo modifierType_Hook = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index 6efeec1970f..090973f12c0 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -887,7 +887,6 @@ ModifierTypeInfo modifierType_LaplacianDeform = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index a36a8c386b4..8cc34d43d82 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -633,7 +633,6 @@ ModifierTypeInfo modifierType_LaplacianSmooth = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ init_data,
diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c
index 29d1ecf6050..709b617d3ec 100644
--- a/source/blender/modifiers/intern/MOD_lattice.c
+++ b/source/blender/modifiers/intern/MOD_lattice.c
@@ -191,7 +191,6 @@ ModifierTypeInfo modifierType_Lattice = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 0de8b26a1b7..097f7241205 100644
--- a/source/blender/modifiers/intern/MOD_mask.cc
+++ b/source/blender/modifiers/intern/MOD_mask.cc
@@ -837,7 +837,6 @@ ModifierTypeInfo modifierType_Mask = {
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
index 910b52dea67..a8e1c91dd20 100644
--- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
+++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
@@ -308,7 +308,6 @@ ModifierTypeInfo modifierType_MeshToVolume = {
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
- /* modifyHair */ nullptr,
/* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index 74f9887a973..48ff4f7d6af 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -446,7 +446,6 @@ ModifierTypeInfo modifierType_MeshCache = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index cb043643dd9..5c6bcb4ba24 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -390,7 +390,7 @@ static void meshdeformModifier_do(ModifierData *md,
}
if (!recursive_bind_sentinel) {
recursive_bind_sentinel = 1;
- mmd->bindfunc(mmd, cagemesh, (float *)vertexCos, numVerts, cagemat);
+ mmd->bindfunc(ob, mmd, cagemesh, (float *)vertexCos, numVerts, cagemat);
recursive_bind_sentinel = 0;
}
@@ -642,7 +642,6 @@ ModifierTypeInfo modifierType_MeshDeform = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc
index 6b8b83e842c..7d07e854ae7 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc
@@ -508,6 +508,10 @@ static void render_procedural_panel_draw(const bContext *C, Panel *panel)
return;
}
+ if (RNA_pointer_is_null(&fileptr)) {
+ return;
+ }
+
uiLayoutSetPropSep(layout, true);
uiTemplateCacheFileProcedural(layout, C, &fileptr);
}
@@ -568,7 +572,6 @@ ModifierTypeInfo modifierType_MeshSequenceCache = {
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ nullptr,
/* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c
index bbac6589577..721906a6a01 100644
--- a/source/blender/modifiers/intern/MOD_mirror.c
+++ b/source/blender/modifiers/intern/MOD_mirror.c
@@ -120,9 +120,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
result = mirrorModifier__doMirror(mmd, ctx->object, mesh);
- if (result != mesh) {
- BKE_mesh_normals_tag_dirty(result);
- }
return result;
}
@@ -241,7 +238,6 @@ ModifierTypeInfo modifierType_Mirror = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index c3b34f3cd23..205839774d8 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -518,7 +518,6 @@ ModifierTypeInfo modifierType_Multires = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 49528845197..f16fc8ff764 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -109,6 +109,8 @@ using blender::float3;
using blender::FunctionRef;
using blender::IndexRange;
using blender::Map;
+using blender::MultiValueMap;
+using blender::MutableSpan;
using blender::Set;
using blender::Span;
using blender::StringRef;
@@ -119,7 +121,9 @@ using blender::fn::Field;
using blender::fn::GField;
using blender::fn::GMutablePointer;
using blender::fn::GPointer;
+using blender::fn::GVArray;
using blender::fn::ValueOrField;
+using blender::fn::ValueOrFieldCPPType;
using blender::nodes::FieldInferencingInterface;
using blender::nodes::GeoNodeExecParams;
using blender::nodes::InputSocketFieldType;
@@ -137,56 +141,85 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(nmd, DNA_struct_default_get(NodesModifierData), modifier);
}
-static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids)
+static void add_used_ids_from_sockets(const ListBase &sockets, Set<ID *> &ids)
{
- LISTBASE_FOREACH (const bNodeSocket *, socket, sockets) {
- if (socket->type == SOCK_OBJECT) {
- Object *object = ((bNodeSocketValueObject *)socket->default_value)->value;
- if (object != nullptr) {
- ids.add(&object->id);
+ LISTBASE_FOREACH (const bNodeSocket *, socket, &sockets) {
+ switch (socket->type) {
+ case SOCK_OBJECT: {
+ if (Object *object = ((bNodeSocketValueObject *)socket->default_value)->value) {
+ ids.add(&object->id);
+ }
+ break;
}
- }
- else if (socket->type == SOCK_COLLECTION) {
- Collection *collection = ((bNodeSocketValueCollection *)socket->default_value)->value;
- if (collection != nullptr) {
- ids.add(&collection->id);
+ case SOCK_COLLECTION: {
+ if (Collection *collection =
+ ((bNodeSocketValueCollection *)socket->default_value)->value) {
+ ids.add(&collection->id);
+ }
+ break;
}
- }
- else if (socket->type == SOCK_MATERIAL) {
- Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value;
- if (material != nullptr) {
- ids.add(&material->id);
+ case SOCK_MATERIAL: {
+ if (Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value) {
+ ids.add(&material->id);
+ }
+ break;
}
- }
- else if (socket->type == SOCK_TEXTURE) {
- Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value;
- if (texture != nullptr) {
- ids.add(&texture->id);
+ case SOCK_TEXTURE: {
+ if (Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value) {
+ ids.add(&texture->id);
+ }
+ break;
}
- }
- else if (socket->type == SOCK_IMAGE) {
- Image *image = ((bNodeSocketValueImage *)socket->default_value)->value;
- if (image != nullptr) {
- ids.add(&image->id);
+ case SOCK_IMAGE: {
+ if (Image *image = ((bNodeSocketValueImage *)socket->default_value)->value) {
+ ids.add(&image->id);
+ }
+ break;
}
}
}
}
-static void find_used_ids_from_nodes(const bNodeTree &tree, Set<ID *> &ids)
+/**
+ * \note We can only check properties here that cause the dependency graph to update relations when
+ * they are changed, otherwise there may be a missing relation after editing. So this could check
+ * more properties like whether the node is muted, but we would have to accept the cost of updating
+ * relations when those properties are changed.
+ */
+static bool node_needs_own_transform_relation(const bNode &node)
+{
+ if (node.type == GEO_NODE_COLLECTION_INFO) {
+ const NodeGeometryCollectionInfo &storage = *static_cast<const NodeGeometryCollectionInfo *>(
+ node.storage);
+ return storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE;
+ }
+
+ if (node.type == GEO_NODE_OBJECT_INFO) {
+ const NodeGeometryObjectInfo &storage = *static_cast<const NodeGeometryObjectInfo *>(
+ node.storage);
+ return storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE;
+ }
+
+ return false;
+}
+
+static void process_nodes_for_depsgraph(const bNodeTree &tree,
+ Set<ID *> &ids,
+ bool &needs_own_transform_relation)
{
Set<const bNodeTree *> handled_groups;
LISTBASE_FOREACH (const bNode *, node, &tree.nodes) {
- addIdsUsedBySocket(&node->inputs, ids);
- addIdsUsedBySocket(&node->outputs, ids);
+ add_used_ids_from_sockets(node->inputs, ids);
+ add_used_ids_from_sockets(node->outputs, ids);
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
const bNodeTree *group = (bNodeTree *)node->id;
if (group != nullptr && handled_groups.add(group)) {
- find_used_ids_from_nodes(*group, ids);
+ process_nodes_for_depsgraph(*group, ids, needs_own_transform_relation);
}
}
+ needs_own_transform_relation |= node_needs_own_transform_relation(*node);
}
}
@@ -236,37 +269,43 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
- DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier");
- if (nmd->node_group != nullptr) {
- DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Nodes Modifier");
-
- Set<ID *> used_ids;
- find_used_ids_from_settings(nmd->settings, used_ids);
- find_used_ids_from_nodes(*nmd->node_group, used_ids);
- for (ID *id : used_ids) {
- switch ((ID_Type)GS(id->name)) {
- case ID_OB: {
- Object *object = reinterpret_cast<Object *>(id);
- add_object_relation(ctx, *object);
- break;
- }
- case ID_GR: {
- Collection *collection = reinterpret_cast<Collection *>(id);
- add_collection_relation(ctx, *collection);
- break;
- }
- case ID_IM:
- case ID_TE: {
- DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier");
- }
- default: {
- /* Purposefully don't add relations for materials. While there are material sockets,
- * the pointers are only passed around as handles rather than dereferenced. */
- break;
- }
+ if (nmd->node_group == nullptr) {
+ return;
+ }
+
+ DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Nodes Modifier");
+
+ bool needs_own_transform_relation = false;
+ Set<ID *> used_ids;
+ find_used_ids_from_settings(nmd->settings, used_ids);
+ process_nodes_for_depsgraph(*nmd->node_group, used_ids, needs_own_transform_relation);
+ for (ID *id : used_ids) {
+ switch ((ID_Type)GS(id->name)) {
+ case ID_OB: {
+ Object *object = reinterpret_cast<Object *>(id);
+ add_object_relation(ctx, *object);
+ break;
+ }
+ case ID_GR: {
+ Collection *collection = reinterpret_cast<Collection *>(id);
+ add_collection_relation(ctx, *collection);
+ break;
+ }
+ case ID_IM:
+ case ID_TE: {
+ DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier");
+ }
+ default: {
+ /* Purposefully don't add relations for materials. While there are material sockets,
+ * the pointers are only passed around as handles rather than dereferenced. */
+ break;
}
}
}
+
+ if (needs_own_transform_relation) {
+ DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier");
+ }
}
static bool check_tree_for_time_node(const bNodeTree &tree,
@@ -892,80 +931,150 @@ static void clear_runtime_data(NodesModifierData *nmd)
}
}
-static void store_field_on_geometry_component(GeometryComponent &component,
- const StringRef attribute_name,
- AttributeDomain domain,
- const GField &field)
+struct OutputAttributeInfo {
+ GField field;
+ StringRefNull name;
+};
+
+struct OutputAttributeToStore {
+ GeometryComponentType component_type;
+ AttributeDomain domain;
+ StringRefNull name;
+ GMutableSpan data;
+};
+
+/**
+ * The output attributes are organized based on their domain, because attributes on the same domain
+ * can be evaluated together.
+ */
+static MultiValueMap<AttributeDomain, OutputAttributeInfo> find_output_attributes_to_store(
+ const NodesModifierData &nmd, const NodeRef &output_node, Span<GMutablePointer> output_values)
{
- /* If the attribute name corresponds to a built-in attribute, use the domain of the built-in
- * attribute instead. */
- if (component.attribute_is_builtin(attribute_name)) {
- component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
- std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(attribute_name);
- if (meta_data.has_value()) {
- domain = meta_data->domain;
+ MultiValueMap<AttributeDomain, OutputAttributeInfo> outputs_by_domain;
+ for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) {
+ if (!socket_type_has_attribute_toggle(*socket->bsocket())) {
+ continue;
}
- else {
- return;
+
+ const std::string prop_name = socket->identifier() + attribute_name_suffix;
+ const IDProperty *prop = IDP_GetPropertyFromGroup(nmd.settings.properties, prop_name.c_str());
+ if (prop == nullptr) {
+ continue;
}
+ const StringRefNull attribute_name = IDP_String(prop);
+ if (attribute_name.is_empty()) {
+ continue;
+ }
+
+ const int index = socket->index();
+ const GPointer value = output_values[index];
+ const ValueOrFieldCPPType *cpp_type = dynamic_cast<const ValueOrFieldCPPType *>(value.type());
+ BLI_assert(cpp_type != nullptr);
+ const GField field = cpp_type->as_field(value.get());
+
+ const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink(
+ &nmd.node_group->outputs, socket->index());
+ const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain;
+ OutputAttributeInfo output_info;
+ output_info.field = std::move(field);
+ output_info.name = attribute_name;
+ outputs_by_domain.add(domain, std::move(output_info));
}
- const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(field.cpp_type());
- OutputAttribute attribute = component.attribute_try_get_for_output_only(
- attribute_name, domain, data_type);
- if (attribute) {
- /* In the future we could also evaluate all output fields at once. */
- const int domain_size = component.attribute_domain_size(domain);
- blender::bke::GeometryComponentFieldContext field_context{component, domain};
- blender::fn::FieldEvaluator field_evaluator{field_context, domain_size};
- field_evaluator.add_with_destination(field, attribute.varray());
- field_evaluator.evaluate();
- attribute.save();
- }
+ return outputs_by_domain;
}
-static void store_output_value_in_geometry(GeometrySet &geometry_set,
- NodesModifierData *nmd,
- const InputSocketRef &socket,
- const GPointer value)
+/**
+ * The computed values are stored in newly allocated arrays. They still have to be moved to the
+ * actual geometry.
+ */
+static Vector<OutputAttributeToStore> compute_attributes_to_store(
+ const GeometrySet &geometry,
+ const MultiValueMap<AttributeDomain, OutputAttributeInfo> &outputs_by_domain)
{
- if (!socket_type_has_attribute_toggle(*socket.bsocket())) {
- return;
- }
- const std::string prop_name = socket.identifier() + attribute_name_suffix;
- const IDProperty *prop = IDP_GetPropertyFromGroup(nmd->settings.properties, prop_name.c_str());
- if (prop == nullptr) {
- return;
- }
- const StringRefNull attribute_name = IDP_String(prop);
- if (attribute_name.is_empty()) {
- return;
+ Vector<OutputAttributeToStore> attributes_to_store;
+ for (const GeometryComponentType component_type : {GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES}) {
+ if (!geometry.has(component_type)) {
+ continue;
+ }
+ const GeometryComponent &component = *geometry.get_component_for_read(component_type);
+ for (const auto item : outputs_by_domain.items()) {
+ const AttributeDomain domain = item.key;
+ const Span<OutputAttributeInfo> outputs_info = item.value;
+ if (!component.attribute_domain_supported(domain)) {
+ continue;
+ }
+ const int domain_size = component.attribute_domain_size(domain);
+ blender::bke::GeometryComponentFieldContext field_context{component, domain};
+ blender::fn::FieldEvaluator field_evaluator{field_context, domain_size};
+ for (const OutputAttributeInfo &output_info : outputs_info) {
+ const CPPType &type = output_info.field.cpp_type();
+ OutputAttributeToStore store{
+ component_type,
+ domain,
+ output_info.name,
+ GMutableSpan{
+ type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}};
+ field_evaluator.add_with_destination(output_info.field, store.data);
+ attributes_to_store.append(store);
+ }
+ field_evaluator.evaluate();
+ }
}
- const blender::fn::ValueOrFieldCPPType *cpp_type =
- dynamic_cast<const blender::fn::ValueOrFieldCPPType *>(value.type());
- BLI_assert(cpp_type != nullptr);
+ return attributes_to_store;
+}
- const GField field = cpp_type->as_field(value.get());
- const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&nmd->node_group->outputs,
- socket.index());
- const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain;
- if (geometry_set.has_mesh()) {
- MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
- store_field_on_geometry_component(component, attribute_name, domain, field);
- }
- if (geometry_set.has_pointcloud()) {
- PointCloudComponent &component = geometry_set.get_component_for_write<PointCloudComponent>();
- store_field_on_geometry_component(component, attribute_name, domain, field);
- }
- if (geometry_set.has_curve()) {
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
- store_field_on_geometry_component(component, attribute_name, domain, field);
- }
- if (geometry_set.has_instances()) {
- InstancesComponent &component = geometry_set.get_component_for_write<InstancesComponent>();
- store_field_on_geometry_component(component, attribute_name, domain, field);
+static void store_computed_output_attributes(
+ GeometrySet &geometry, const Span<OutputAttributeToStore> attributes_to_store)
+{
+ for (const OutputAttributeToStore &store : attributes_to_store) {
+ GeometryComponent &component = geometry.get_component_for_write(store.component_type);
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(store.data.type());
+ const std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(
+ store.name);
+ if (meta_data.has_value() && meta_data->domain == store.domain &&
+ meta_data->data_type == data_type) {
+ /* Copy the data into an existing attribute. */
+ blender::bke::WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(
+ store.name);
+ if (write_attribute) {
+ write_attribute.varray.set_all(store.data.data());
+ if (write_attribute.tag_modified_fn) {
+ write_attribute.tag_modified_fn();
+ }
+ }
+ store.data.type().destruct_n(store.data.data(), store.data.size());
+ MEM_freeN(store.data.data());
+ }
+ else {
+ /* Replace the existing attribute with the new data. */
+ if (meta_data.has_value()) {
+ component.attribute_try_delete(store.name);
+ }
+ component.attribute_try_create(store.name,
+ store.domain,
+ blender::bke::cpp_type_to_custom_data_type(store.data.type()),
+ AttributeInitMove(store.data.data()));
+ }
}
}
+static void store_output_attributes(GeometrySet &geometry,
+ const NodesModifierData &nmd,
+ const NodeRef &output_node,
+ Span<GMutablePointer> output_values)
+{
+ /* All new attribute values have to be computed before the geometry is actually changed. This is
+ * necessary because some fields might depend on attributes that are overwritten. */
+ MultiValueMap<AttributeDomain, OutputAttributeInfo> outputs_by_domain =
+ find_output_attributes_to_store(nmd, output_node, output_values);
+ Vector<OutputAttributeToStore> attributes_to_store = compute_attributes_to_store(
+ geometry, outputs_by_domain);
+ store_computed_output_attributes(geometry, attributes_to_store);
+}
+
/**
* Evaluate a node group to compute the output geometry.
*/
@@ -1040,19 +1149,20 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr;
blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params);
- GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out<GeometrySet>();
+ GeometrySet output_geometry_set = std::move(*eval_params.r_output_values[0].get<GeometrySet>());
if (geo_logger.has_value()) {
geo_logger->log_output_geometry(output_geometry_set);
- NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(&nmd->modifier);
+ NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(ctx->object,
+ &nmd->modifier);
clear_runtime_data(nmd_orig);
nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger);
}
- for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) {
- GMutablePointer socket_value = eval_params.r_output_values[socket->index()];
- store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value);
- socket_value.destruct();
+ store_output_attributes(output_geometry_set, *nmd, output_node, eval_params.r_output_values);
+
+ for (GMutablePointer value : eval_params.r_output_values) {
+ value.destruct();
}
return output_geometry_set;
@@ -1616,7 +1726,6 @@ ModifierTypeInfo modifierType_Nodes = {
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ nullptr,
/* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index 21ad8dd5bbc..f59af9074ce 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -381,6 +381,11 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value)
new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side));
return true;
}
+ if (bnode.type == GEO_NODE_EXTRUDE_MESH) {
+ new (r_value)
+ ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>()));
+ return true;
+ }
new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position"));
return true;
}
@@ -1342,7 +1347,7 @@ class GeometryNodesEvaluator {
}
input_state.usage = ValueUsage::Unused;
- /* If the input is unused, it's value can be destructed now. */
+ /* If the input is unused, its value can be destructed now. */
this->destruct_input_value_if_exists(locked_node, socket);
if (input_state.was_ready_for_execution) {
diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c
index a01f63be791..cce3434f6e3 100644
--- a/source/blender/modifiers/intern/MOD_none.c
+++ b/source/blender/modifiers/intern/MOD_none.c
@@ -58,7 +58,6 @@ ModifierTypeInfo modifierType_None = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index 61099fedf46..8b58b575d24 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -336,8 +336,6 @@ static void normalEditModifier_do_radial(NormalEditModifierData *enmd,
if (do_polynors_fix &&
polygons_check_flip(
mloop, nos, &mesh->ldata, mpoly, BKE_mesh_poly_normals_for_write(mesh), num_polys)) {
- /* XXX TODO: is this still needed? */
- // mesh->dirty |= DM_DIRTY_TESS_CDLAYERS;
/* We need to recompute vertex normals! */
BKE_mesh_normals_tag_dirty(mesh);
}
@@ -788,7 +786,6 @@ ModifierTypeInfo modifierType_NormalEdit = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index d821caf25a7..0c00f807df3 100644
--- a/source/blender/modifiers/intern/MOD_ocean.c
+++ b/source/blender/modifiers/intern/MOD_ocean.c
@@ -741,7 +741,6 @@ ModifierTypeInfo modifierType_Ocean = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index 4fffa7c93f3..67a492d4154 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -677,7 +677,6 @@ ModifierTypeInfo modifierType_ParticleInstance = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index 2a4cc1c2747..6fb60fffcf4 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -334,7 +334,6 @@ ModifierTypeInfo modifierType_ParticleSystem = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index 937a73fddd9..67f83b17f3f 100644
--- a/source/blender/modifiers/intern/MOD_remesh.c
+++ b/source/blender/modifiers/intern/MOD_remesh.c
@@ -299,7 +299,6 @@ ModifierTypeInfo modifierType_Remesh = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index f5db3bced7a..33c62197dbd 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -27,6 +27,7 @@
#include "BLI_utildefines.h"
#include "BLI_alloca.h"
+#include "BLI_bitmap.h"
#include "BLI_math.h"
#include "BLT_translation.h"
@@ -134,6 +135,8 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result,
const float axis_offset[3],
const float merge_threshold)
{
+ BLI_bitmap *vert_tag = BLI_BITMAP_NEW(totvert, __func__);
+
const float merge_threshold_sq = square_f(merge_threshold);
const bool use_offset = axis_offset != NULL;
uint tot_doubles = 0;
@@ -150,13 +153,10 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result,
}
const float dist_sq = len_squared_v3v3(axis_co, mvert_new[i].co);
if (dist_sq <= merge_threshold_sq) {
- mvert_new[i].flag |= ME_VERT_TMP_TAG;
+ BLI_BITMAP_ENABLE(vert_tag, i);
tot_doubles += 1;
copy_v3_v3(mvert_new[i].co, axis_co);
}
- else {
- mvert_new[i].flag &= ~ME_VERT_TMP_TAG & 0xFF;
- }
}
if (tot_doubles != 0) {
@@ -166,7 +166,7 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result,
uint tot_doubles_left = tot_doubles;
for (uint i = 0; i < totvert; i += 1) {
- if (mvert_new[i].flag & ME_VERT_TMP_TAG) {
+ if (BLI_BITMAP_TEST(vert_tag, i)) {
int *doubles_map = &full_doubles_map[totvert + i];
for (uint step = 1; step < step_tot; step += 1) {
*doubles_map = (int)i;
@@ -184,6 +184,9 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result,
MESH_MERGE_VERTS_DUMP_IF_MAPPED);
MEM_freeN(full_doubles_map);
}
+
+ MEM_freeN(vert_tag);
+
return result;
}
@@ -439,6 +442,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
mv_new = mvert_new;
mv_orig = mvert_orig;
+ BLI_bitmap *vert_tag = BLI_BITMAP_NEW(totvert, __func__);
+
/* Copy the first set of edges */
med_orig = medge_orig;
med_new = medge_new;
@@ -447,10 +452,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
med_new->v2 = med_orig->v2;
med_new->crease = med_orig->crease;
med_new->flag = med_orig->flag & ~ME_LOOSEEDGE;
- /* Tag mvert as not loose.
- * NOTE: ME_VERT_TMP_TAG is given to be cleared by BKE_mesh_new_nomain_from_template. */
- mvert_new[med_orig->v1].flag |= ME_VERT_TMP_TAG;
- mvert_new[med_orig->v2].flag |= ME_VERT_TMP_TAG;
+
+ /* Tag mvert as not loose. */
+ BLI_BITMAP_ENABLE(vert_tag, med_orig->v1);
+ BLI_BITMAP_ENABLE(vert_tag, med_orig->v1);
}
/* build polygon -> edge map */
@@ -910,7 +915,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
med_new->v1 = varray_stride + j;
med_new->v2 = med_new->v1 - totvert;
med_new->flag = ME_EDGEDRAW | ME_EDGERENDER;
- if ((mv_new_base->flag & ME_VERT_TMP_TAG) == 0) {
+ if (!BLI_BITMAP_TEST(vert_tag, j)) {
med_new->flag |= ME_LOOSEEDGE;
}
med_new++;
@@ -931,7 +936,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
med_new->v1 = i;
med_new->v2 = varray_stride + i;
med_new->flag = ME_EDGEDRAW | ME_EDGERENDER;
- if ((mvert_new[i].flag & ME_VERT_TMP_TAG) == 0) {
+ if (!BLI_BITMAP_TEST(vert_tag, i)) {
med_new->flag |= ME_LOOSEEDGE;
}
med_new++;
@@ -1119,6 +1124,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
#endif
+ MEM_freeN(vert_tag);
+
if (edge_poly_map) {
MEM_freeN(edge_poly_map);
}
@@ -1257,7 +1264,6 @@ ModifierTypeInfo modifierType_Screw = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c
index b517bc102f8..8c5299a965d 100644
--- a/source/blender/modifiers/intern/MOD_shapekey.c
+++ b/source/blender/modifiers/intern/MOD_shapekey.c
@@ -139,7 +139,6 @@ ModifierTypeInfo modifierType_ShapeKey = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ deformMatricesEM,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index a12724ec23c..4d10df91331 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -291,7 +291,6 @@ ModifierTypeInfo modifierType_Shrinkwrap = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index 39ebc415021..25c3acb0c4f 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -596,7 +596,6 @@ ModifierTypeInfo modifierType_SimpleDeform = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 07ce819e91c..d1cb120132d 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -2101,7 +2101,6 @@ ModifierTypeInfo modifierType_Skin = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index 97027e2ecff..3228a72d958 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -284,7 +284,6 @@ ModifierTypeInfo modifierType_Smooth = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index 46e960e10d4..f0178a817dc 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -120,7 +120,6 @@ ModifierTypeInfo modifierType_Softbody = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 736dd08a713..09933fef7ff 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -274,7 +274,6 @@ ModifierTypeInfo modifierType_Solidify = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index b77f6b7e3e2..65ad298cff1 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -245,9 +245,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
if ((ctx->flag & MOD_APPLY_TO_BASE_MESH) == 0) {
Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
const bool is_render_mode = (ctx->flag & MOD_APPLY_RENDER) != 0;
- /* Same check as in `DRW_mesh_batch_cache_create_requested` to keep both code coherent. */
- const bool is_editmode = (mesh->edit_mesh != NULL) &&
- (mesh->edit_mesh->mesh_eval_final != NULL);
+ /* Same check as in `DRW_mesh_batch_cache_create_requested` to keep both code coherent. The
+ * difference is that here we do not check for the final edit mesh pointer as it is not yet
+ * assigned at this stage of modifier stack evaluation. */
+ const bool is_editmode = (mesh->edit_mesh != NULL);
const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode);
if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex(scene, ctx->object, smd, required_mode, false)) {
subdiv_cache_cpu_evaluation_settings(ctx, mesh, smd);
@@ -499,7 +500,6 @@ ModifierTypeInfo modifierType_Subsurf = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index c8be2bd2829..a54af766b2f 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -245,7 +245,6 @@ ModifierTypeInfo modifierType_Surface = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index ec6de8f8387..70a05002a4f 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -1454,7 +1454,7 @@ static void surfacedeformModifier_do(ModifierData *md,
BKE_modifier_set_error(ob, md, "Attempt to bind from inactive dependency graph");
return;
}
- ModifierData *md_orig = BKE_modifier_get_original(md);
+ ModifierData *md_orig = BKE_modifier_get_original(ob, md);
freeData(md_orig);
}
return;
@@ -1478,7 +1478,7 @@ static void surfacedeformModifier_do(ModifierData *md,
}
SurfaceDeformModifierData *smd_orig = (SurfaceDeformModifierData *)BKE_modifier_get_original(
- md);
+ ob, md);
float tmp_mat[4][4];
invert_m4_m4(tmp_mat, ob->obmat);
@@ -1715,7 +1715,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index 52d5f3e97ef..d4fcebb3216 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -48,17 +48,11 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
-Mesh *triangulate_mesh(Mesh *mesh,
- const int quad_method,
- const int ngon_method,
- const int min_vertices,
- const int flag);
-
-Mesh *triangulate_mesh(Mesh *mesh,
- const int quad_method,
- const int ngon_method,
- const int min_vertices,
- const int flag)
+static Mesh *triangulate_mesh(Mesh *mesh,
+ const int quad_method,
+ const int ngon_method,
+ const int min_vertices,
+ const int flag)
{
Mesh *result;
BMesh *bm;
@@ -176,7 +170,6 @@ ModifierTypeInfo modifierType_Triangulate = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c
index 238952fde00..5721834c032 100644
--- a/source/blender/modifiers/intern/MOD_uvproject.c
+++ b/source/blender/modifiers/intern/MOD_uvproject.c
@@ -383,7 +383,6 @@ ModifierTypeInfo modifierType_UVProject = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index 3f161d339c2..552cf1d5d3b 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -233,9 +233,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
settings.use_threading = (numPolys > 1000);
BLI_task_parallel_range(0, numPolys, &data, uv_warp_compute, &settings);
- /* XXX TODO: is this still needed? */
- // me_eval->dirty |= DM_DIRTY_TESS_CDLAYERS;
-
mesh->runtime.is_original = false;
return mesh;
@@ -340,7 +337,6 @@ ModifierTypeInfo modifierType_UVWarp = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc
index a1ca29f454c..2b9ee97ea09 100644
--- a/source/blender/modifiers/intern/MOD_volume_displace.cc
+++ b/source/blender/modifiers/intern/MOD_volume_displace.cc
@@ -183,7 +183,7 @@ template<typename GridType> struct DisplaceOp {
TexResult texture_result = {0};
BKE_texture_get_value(
nullptr, this->texture, const_cast<float *>(pos.asV()), &texture_result, false);
- return {texture_result.tr, texture_result.tg, texture_result.tb};
+ return {texture_result.trgba[0], texture_result.trgba[1], texture_result.trgba[2]};
}
};
@@ -339,7 +339,6 @@ ModifierTypeInfo modifierType_VolumeDisplace = {
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
- /* modifyHair */ nullptr,
/* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
index 72358844838..9557abf0a8a 100644
--- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
+++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
@@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_VolumeToMesh = {
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 25e33b22bde..777ae4d9b10 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -539,7 +539,6 @@ ModifierTypeInfo modifierType_Warp = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index b7ab5dac388..5a56ec1d9e5 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -494,7 +494,6 @@ ModifierTypeInfo modifierType_Wave = {
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index bfe389eb080..21e6ed46908 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -757,7 +757,6 @@ ModifierTypeInfo modifierType_WeightedNormal = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c
index cd9e5162527..eedaea9d403 100644
--- a/source/blender/modifiers/intern/MOD_weightvg_util.c
+++ b/source/blender/modifiers/intern/MOD_weightvg_util.c
@@ -186,28 +186,32 @@ void weightvg_do_mask(const ModifierEvalContext *ctx,
org_w[i] = (new_w[i] * texres.tin * fact) + (org_w[i] * (1.0f - (texres.tin * fact)));
break;
case MOD_WVG_MASK_TEX_USE_RED:
- org_w[i] = (new_w[i] * texres.tr * fact) + (org_w[i] * (1.0f - (texres.tr * fact)));
+ org_w[i] = (new_w[i] * texres.trgba[0] * fact) +
+ (org_w[i] * (1.0f - (texres.trgba[0] * fact)));
break;
case MOD_WVG_MASK_TEX_USE_GREEN:
- org_w[i] = (new_w[i] * texres.tg * fact) + (org_w[i] * (1.0f - (texres.tg * fact)));
+ org_w[i] = (new_w[i] * texres.trgba[1] * fact) +
+ (org_w[i] * (1.0f - (texres.trgba[1] * fact)));
break;
case MOD_WVG_MASK_TEX_USE_BLUE:
- org_w[i] = (new_w[i] * texres.tb * fact) + (org_w[i] * (1.0f - (texres.tb * fact)));
+ org_w[i] = (new_w[i] * texres.trgba[2] * fact) +
+ (org_w[i] * (1.0f - (texres.trgba[2] * fact)));
break;
case MOD_WVG_MASK_TEX_USE_HUE:
- rgb_to_hsv_v(&texres.tr, hsv);
+ rgb_to_hsv_v(texres.trgba, hsv);
org_w[i] = (new_w[i] * hsv[0] * fact) + (org_w[i] * (1.0f - (hsv[0] * fact)));
break;
case MOD_WVG_MASK_TEX_USE_SAT:
- rgb_to_hsv_v(&texres.tr, hsv);
+ rgb_to_hsv_v(texres.trgba, hsv);
org_w[i] = (new_w[i] * hsv[1] * fact) + (org_w[i] * (1.0f - (hsv[1] * fact)));
break;
case MOD_WVG_MASK_TEX_USE_VAL:
- rgb_to_hsv_v(&texres.tr, hsv);
+ rgb_to_hsv_v(texres.trgba, hsv);
org_w[i] = (new_w[i] * hsv[2] * fact) + (org_w[i] * (1.0f - (hsv[2] * fact)));
break;
case MOD_WVG_MASK_TEX_USE_ALPHA:
- org_w[i] = (new_w[i] * texres.ta * fact) + (org_w[i] * (1.0f - (texres.ta * fact)));
+ org_w[i] = (new_w[i] * texres.trgba[3] * fact) +
+ (org_w[i] * (1.0f - (texres.trgba[3] * fact)));
break;
default:
org_w[i] = (new_w[i] * texres.tin * fact) + (org_w[i] * (1.0f - (texres.tin * fact)));
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index a9d01c64ff1..6e4f8be8c50 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -428,7 +428,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index b369b82ebb7..1b2608afa12 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -514,7 +514,6 @@ ModifierTypeInfo modifierType_WeightVGMix = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index 7ee19e1c537..27242b32432 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -767,7 +767,6 @@ ModifierTypeInfo modifierType_WeightVGProximity = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weld.cc b/source/blender/modifiers/intern/MOD_weld.cc
index 3a06c6a649b..e9d98429ff2 100644
--- a/source/blender/modifiers/intern/MOD_weld.cc
+++ b/source/blender/modifiers/intern/MOD_weld.cc
@@ -27,21 +27,12 @@
* - Review weight and vertex color interpolation.;
*/
-//#define USE_WELD_DEBUG
-//#define USE_WELD_NORMALS
-//#define USE_BVHTREEKDOP
-
-#include <algorithm>
-
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_array.hh"
-#include "BLI_bitmap.h"
#include "BLI_index_range.hh"
-#include "BLI_kdtree.h"
-#include "BLI_math.h"
#include "BLI_span.hh"
#include "BLI_vector.hh"
@@ -51,7 +42,6 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
-#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#ifdef USE_BVHTREEKDOP
@@ -60,7 +50,6 @@
#include "BKE_context.h"
#include "BKE_deform.h"
-#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
@@ -74,1810 +63,94 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
+#include "GEO_mesh_merge_by_distance.hh"
+
using blender::Array;
-using blender::IndexRange;
-using blender::MutableSpan;
+using blender::IndexMask;
using blender::Span;
using blender::Vector;
-/* Indicates when the element was not computed. */
-#define OUT_OF_CONTEXT (int)(-1)
-/* Indicates if the edge or face will be collapsed. */
-#define ELEM_COLLAPSED (int)(-2)
-/* indicates whether an edge or vertex in groups_map will be merged. */
-#define ELEM_MERGED (int)(-2)
-
-/* Used to indicate a range in an array specifying a group. */
-struct WeldGroup {
- int len;
- int ofs;
-};
-
-/* Edge groups that will be merged. Final vertices are also indicated. */
-struct WeldGroupEdge {
- struct WeldGroup group;
- int v1;
- int v2;
-};
-
-struct WeldVert {
- /* Indexes relative to the original Mesh. */
- int vert_dest;
- int vert_orig;
-};
-
-struct WeldEdge {
- union {
- int flag;
- struct {
- /* Indexes relative to the original Mesh. */
- int edge_dest;
- int edge_orig;
- int vert_a;
- int vert_b;
- };
- };
-};
-
-struct WeldLoop {
- union {
- int flag;
- struct {
- /* Indexes relative to the original Mesh. */
- int vert;
- int edge;
- int loop_orig;
- int loop_skip_to;
- };
- };
-};
-
-struct WeldPoly {
- union {
- int flag;
- struct {
- /* Indexes relative to the original Mesh. */
- int poly_dst;
- int poly_orig;
- int loop_start;
- int loop_end;
- /* Final Polygon Size. */
- int len;
- /* Group of loops that will be affected. */
- struct WeldGroup loops;
- };
- };
-};
-
-struct WeldMesh {
- /* Group of vertices to be merged. */
- Array<WeldGroup> vert_groups;
- Array<int> vert_groups_buffer;
-
- /* Group of edges to be merged. */
- Array<WeldGroupEdge> edge_groups;
- Array<int> edge_groups_buffer;
- /* From the original index of the vertex, this indicates which group it is or is going to be
- * merged. */
- Array<int> edge_groups_map;
-
- /* References all polygons and loops that will be affected. */
- Vector<WeldLoop> wloop;
- Vector<WeldPoly> wpoly;
- WeldPoly *wpoly_new;
- int wloop_len;
- int wpoly_len;
- int wpoly_new_len;
-
- /* From the actual index of the element in the mesh, it indicates what is the index of the Weld
- * element above. */
- Array<int> loop_map;
- Array<int> poly_map;
-
- int vert_kill_len;
- int edge_kill_len;
- int loop_kill_len;
- int poly_kill_len; /* Including the new polygons. */
-
- /* Size of the affected polygon with more sides. */
- int max_poly_len;
-};
-
-struct WeldLoopOfPolyIter {
- int loop_start;
- int loop_end;
- Span<WeldLoop> wloop;
- Span<MLoop> mloop;
- Span<int> loop_map;
- /* Weld group. */
- int *group;
-
- int l_curr;
- int l_next;
-
- /* Return */
- int group_len;
- int v;
- int e;
- char type;
-};
-
-/* -------------------------------------------------------------------- */
-/** \name Debug Utils
- * \{ */
-
-#ifdef USE_WELD_DEBUG
-static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter,
- const WeldPoly &wp,
- Span<WeldLoop> wloop,
- Span<MLoop> mloop,
- Span<int> loop_map,
- int *group_buffer);
-
-static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter);
-
-static void weld_assert_edge_kill_len(Span<WeldEdge> wedge, const int supposed_kill_len)
+static Span<MDeformVert> get_vertex_group(const Mesh &mesh, const int defgrp_index)
{
- int kills = 0;
- const WeldEdge *we = &wedge[0];
- for (int i = wedge.size(); i--; we++) {
- int edge_dest = we->edge_dest;
- /* Magically includes collapsed edges. */
- if (edge_dest != OUT_OF_CONTEXT) {
- kills++;
- }
+ if (defgrp_index == -1) {
+ return {};
}
- BLI_assert(kills == supposed_kill_len);
-}
-
-static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly,
- Span<WeldPoly> wpoly_new,
- Span<WeldLoop> wloop,
- Span<MLoop> mloop,
- Span<int> loop_map,
- Span<int> poly_map,
- Span<MPoly> mpoly,
- const int supposed_poly_kill_len,
- const int supposed_loop_kill_len)
-{
- int poly_kills = 0;
- int loop_kills = mloop.size();
- const MPoly *mp = &mpoly[0];
- for (int i = 0; i < mpoly.size(); i++, mp++) {
- int poly_ctx = poly_map[i];
- if (poly_ctx != OUT_OF_CONTEXT) {
- const WeldPoly *wp = &wpoly[poly_ctx];
- WeldLoopOfPolyIter iter;
- if (!weld_iter_loop_of_poly_begin(&iter, *wp, wloop, mloop, loop_map, nullptr)) {
- poly_kills++;
- continue;
- }
- else {
- if (wp->poly_dst != OUT_OF_CONTEXT) {
- poly_kills++;
- continue;
- }
- int remain = wp->len;
- int l = wp->loop_start;
- while (remain) {
- int l_next = l + 1;
- int loop_ctx = loop_map[l];
- if (loop_ctx != OUT_OF_CONTEXT) {
- const WeldLoop *wl = &wloop[loop_ctx];
- if (wl->loop_skip_to != OUT_OF_CONTEXT) {
- l_next = wl->loop_skip_to;
- }
- if (wl->flag != ELEM_COLLAPSED) {
- loop_kills--;
- remain--;
- }
- }
- else {
- loop_kills--;
- remain--;
- }
- l = l_next;
- }
- }
- }
- else {
- loop_kills -= mp->totloop;
- }
+ const MDeformVert *vertex_group = static_cast<const MDeformVert *>(
+ CustomData_get_layer(&mesh.vdata, CD_MDEFORMVERT));
+ if (!vertex_group) {
+ return {};
}
-
- const WeldPoly *wp = wpoly_new.data();
- for (int i = wpoly_new.size(); i--; wp++) {
- if (wp->poly_dst != OUT_OF_CONTEXT) {
- poly_kills++;
- continue;
- }
- int remain = wp->len;
- int l = wp->loop_start;
- while (remain) {
- int l_next = l + 1;
- int loop_ctx = loop_map[l];
- if (loop_ctx != OUT_OF_CONTEXT) {
- const WeldLoop *wl = &wloop[loop_ctx];
- if (wl->loop_skip_to != OUT_OF_CONTEXT) {
- l_next = wl->loop_skip_to;
- }
- if (wl->flag != ELEM_COLLAPSED) {
- loop_kills--;
- remain--;
- }
- }
- else {
- loop_kills--;
- remain--;
- }
- l = l_next;
- }
- }
-
- BLI_assert(poly_kills == supposed_poly_kill_len);
- BLI_assert(loop_kills == supposed_loop_kill_len);
+ return {vertex_group, mesh.totvert};
}
-static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp,
- Span<WeldLoop> wloop,
- Span<MLoop> mloop,
- Span<int> loop_map)
+static Vector<int64_t> selected_indices_from_vertex_group(Span<MDeformVert> vertex_group,
+ const int index,
+ const bool invert)
{
- const int len = wp.len;
- Array<int, 64> verts(len);
- WeldLoopOfPolyIter iter;
- if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) {
- return;
- }
- else {
- int i = 0;
- while (weld_iter_loop_of_poly_next(&iter)) {
- verts[i++] = iter.v;
- }
- }
- for (int i = 0; i < len; i++) {
- int va = verts[i];
- for (int j = i + 1; j < len; j++) {
- int vb = verts[j];
- BLI_assert(va != vb);
+ Vector<int64_t> selected_indices;
+ for (const int i : vertex_group.index_range()) {
+ const bool found = BKE_defvert_find_weight(&vertex_group[i], index) > 0.0f;
+ if (found != invert) {
+ selected_indices.append(i);
}
}
+ return selected_indices;
}
-static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop)
+static Array<bool> selection_array_from_vertex_group(Span<MDeformVert> vertex_group,
+ const int index,
+ const bool invert)
{
- if (wp->flag == ELEM_COLLAPSED) {
- return;
+ Array<bool> selection(vertex_group.size());
+ for (const int i : vertex_group.index_range()) {
+ const bool found = BKE_defvert_find_weight(&vertex_group[i], index) > 0.0f;
+ selection[i] = (found != invert);
}
-
- int len = wp->len;
- const WeldLoop *wl = &wloop[wp->loops.ofs];
- BLI_assert(wp->loop_start <= wl->loop_orig);
-
- int end_wloop = wp->loops.ofs + wp->loops.len;
- const WeldLoop *wl_end = &wloop[end_wloop - 1];
-
- int min_len = 0;
- for (; wl <= wl_end; wl++) {
- BLI_assert(wl->loop_skip_to == OUT_OF_CONTEXT); /* Not for this case. */
- if (wl->flag != ELEM_COLLAPSED) {
- min_len++;
- }
- }
- BLI_assert(len >= min_len);
-
- int max_len = wp->loop_end - wp->loop_start + 1;
- BLI_assert(len <= max_len);
+ return selection;
}
-#endif /* USE_WELD_DEBUG */
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Weld Vert API
- * \{ */
-
-static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map,
- const int vert_kill_len)
+static std::optional<Mesh *> calculate_weld(const Mesh &mesh, const WeldModifierData &wmd)
{
- Vector<WeldVert> wvert;
- wvert.reserve(std::min<int>(2 * vert_kill_len, vert_dest_map.size()));
+ const int defgrp_index = BKE_id_defgroup_name_index(&mesh.id, wmd.defgrp_name);
+ Span<MDeformVert> vertex_group = get_vertex_group(mesh, defgrp_index);
+ const bool invert = (wmd.flag & MOD_WELD_INVERT_VGROUP) != 0;
- for (const int i : vert_dest_map.index_range()) {
- if (vert_dest_map[i] != OUT_OF_CONTEXT) {
- wvert.append({vert_dest_map[i], i});
+ if (wmd.mode == MOD_WELD_MODE_ALL) {
+ if (!vertex_group.is_empty()) {
+ Vector<int64_t> selected_indices = selected_indices_from_vertex_group(
+ vertex_group, defgrp_index, invert);
+ return blender::geometry::mesh_merge_by_distance_all(
+ mesh, IndexMask(selected_indices), wmd.merge_dist);
}
+ return blender::geometry::mesh_merge_by_distance_all(
+ mesh, IndexMask(mesh.totvert), wmd.merge_dist);
}
- return wvert;
-}
-
-static void weld_vert_groups_setup(Span<WeldVert> wvert,
- Span<int> vert_dest_map,
- MutableSpan<int> r_vert_groups_map,
- Array<int> &r_vert_groups_buffer,
- Array<WeldGroup> &r_vert_groups)
-{
- /* Get weld vert groups. */
-
- int wgroups_len = 0;
- for (const int i : vert_dest_map.index_range()) {
- const int vert_dest = vert_dest_map[i];
- if (vert_dest != OUT_OF_CONTEXT) {
- if (vert_dest != i) {
- r_vert_groups_map[i] = ELEM_MERGED;
- }
- else {
- r_vert_groups_map[i] = wgroups_len;
- wgroups_len++;
- }
- }
- else {
- r_vert_groups_map[i] = OUT_OF_CONTEXT;
+ if (wmd.mode == MOD_WELD_MODE_CONNECTED) {
+ const bool only_loose_edges = (wmd.flag & MOD_WELD_LOOSE_EDGES) != 0;
+ if (!vertex_group.is_empty()) {
+ Array<bool> selection = selection_array_from_vertex_group(
+ vertex_group, defgrp_index, invert);
+ return blender::geometry::mesh_merge_by_distance_connected(
+ mesh, selection, wmd.merge_dist, only_loose_edges);
}
+ Array<bool> selection(mesh.totvert, true);
+ return blender::geometry::mesh_merge_by_distance_connected(
+ mesh, selection, wmd.merge_dist, only_loose_edges);
}
- r_vert_groups.reinitialize(wgroups_len);
- r_vert_groups.fill({0, 0});
- MutableSpan<WeldGroup> wgroups = r_vert_groups;
-
- for (const WeldVert &wv : wvert) {
- int group_index = r_vert_groups_map[wv.vert_dest];
- wgroups[group_index].len++;
- }
-
- int ofs = 0;
- for (WeldGroup &wg : wgroups) {
- wg.ofs = ofs;
- ofs += wg.len;
- }
-
- BLI_assert(ofs == wvert.size());
-
- r_vert_groups_buffer.reinitialize(ofs);
- for (const WeldVert &wv : wvert) {
- int group_index = r_vert_groups_map[wv.vert_dest];
- r_vert_groups_buffer[wgroups[group_index].ofs++] = wv.vert_orig;
- }
-
- for (WeldGroup &wg : wgroups) {
- wg.ofs -= wg.len;
- }
+ BLI_assert_unreachable();
+ return nullptr;
}
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Weld Edge API
- * \{ */
-
-static void weld_edge_ctx_setup(MutableSpan<WeldGroup> r_vlinks,
- MutableSpan<int> r_edge_dest_map,
- MutableSpan<WeldEdge> r_wedge,
- int *r_edge_kiil_len)
+static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh)
{
- /* Setup Edge Overlap. */
- int edge_kill_len = 0;
-
- MutableSpan<WeldGroup> v_links = r_vlinks;
-
- for (WeldEdge &we : r_wedge) {
- int dst_vert_a = we.vert_a;
- int dst_vert_b = we.vert_b;
-
- if (dst_vert_a == dst_vert_b) {
- BLI_assert(we.edge_dest == OUT_OF_CONTEXT);
- r_edge_dest_map[we.edge_orig] = ELEM_COLLAPSED;
- we.flag = ELEM_COLLAPSED;
- edge_kill_len++;
- continue;
- }
-
- v_links[dst_vert_a].len++;
- v_links[dst_vert_b].len++;
- }
-
- int link_len = 0;
- for (WeldGroup &vl : r_vlinks) {
- vl.ofs = link_len;
- link_len += vl.len;
- }
-
- if (link_len > 0) {
- Array<int> link_edge_buffer(link_len);
-
- for (const int i : r_wedge.index_range()) {
- const WeldEdge &we = r_wedge[i];
- if (we.flag == ELEM_COLLAPSED) {
- continue;
- }
-
- int dst_vert_a = we.vert_a;
- int dst_vert_b = we.vert_b;
+ const WeldModifierData &wmd = reinterpret_cast<WeldModifierData &>(*md);
- link_edge_buffer[v_links[dst_vert_a].ofs++] = i;
- link_edge_buffer[v_links[dst_vert_b].ofs++] = i;
- }
-
- for (WeldGroup &vl : r_vlinks) {
- /* Fix offset */
- vl.ofs -= vl.len;
- }
-
- for (const int i : r_wedge.index_range()) {
- const WeldEdge &we = r_wedge[i];
- if (we.edge_dest != OUT_OF_CONTEXT) {
- /* No need to retest edges.
- * (Already includes collapsed edges). */
- continue;
- }
-
- int dst_vert_a = we.vert_a;
- int dst_vert_b = we.vert_b;
-
- struct WeldGroup *link_a = &v_links[dst_vert_a];
- struct WeldGroup *link_b = &v_links[dst_vert_b];
-
- int edges_len_a = link_a->len;
- int edges_len_b = link_b->len;
-
- if (edges_len_a <= 1 || edges_len_b <= 1) {
- continue;
- }
-
- int *edges_ctx_a = &link_edge_buffer[link_a->ofs];
- int *edges_ctx_b = &link_edge_buffer[link_b->ofs];
- int edge_orig = we.edge_orig;
-
- for (; edges_len_a--; edges_ctx_a++) {
- int e_ctx_a = *edges_ctx_a;
- if (e_ctx_a == i) {
- continue;
- }
- while (edges_len_b && *edges_ctx_b < e_ctx_a) {
- edges_ctx_b++;
- edges_len_b--;
- }
- if (edges_len_b == 0) {
- break;
- }
- int e_ctx_b = *edges_ctx_b;
- if (e_ctx_a == e_ctx_b) {
- WeldEdge *we_b = &r_wedge[e_ctx_b];
- BLI_assert(ELEM(we_b->vert_a, dst_vert_a, dst_vert_b));
- BLI_assert(ELEM(we_b->vert_b, dst_vert_a, dst_vert_b));
- BLI_assert(we_b->edge_dest == OUT_OF_CONTEXT);
- BLI_assert(we_b->edge_orig != edge_orig);
- r_edge_dest_map[we_b->edge_orig] = edge_orig;
- we_b->edge_dest = edge_orig;
- edge_kill_len++;
- }
- }
- }
-
-#ifdef USE_WELD_DEBUG
- weld_assert_edge_kill_len(r_wedge, edge_kill_len);
-#endif
- }
-
- *r_edge_kiil_len = edge_kill_len;
-}
-
-static Vector<WeldEdge> weld_edge_ctx_alloc(Span<MEdge> medge,
- Span<int> vert_dest_map,
- MutableSpan<int> r_edge_dest_map,
- MutableSpan<int> r_edge_ctx_map)
-{
- /* Edge Context. */
- int wedge_len = 0;
-
- Vector<WeldEdge> wedge;
- wedge.reserve(medge.size());
-
- for (const int i : medge.index_range()) {
- int v1 = medge[i].v1;
- int v2 = medge[i].v2;
- int v_dest_1 = vert_dest_map[v1];
- int v_dest_2 = vert_dest_map[v2];
- if ((v_dest_1 != OUT_OF_CONTEXT) || (v_dest_2 != OUT_OF_CONTEXT)) {
- WeldEdge we{};
- we.vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1;
- we.vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2;
- we.edge_dest = OUT_OF_CONTEXT;
- we.edge_orig = i;
- wedge.append(we);
- r_edge_dest_map[i] = i;
- r_edge_ctx_map[i] = wedge_len++;
- }
- else {
- r_edge_dest_map[i] = OUT_OF_CONTEXT;
- r_edge_ctx_map[i] = OUT_OF_CONTEXT;
- }
- }
-
- return wedge;
-}
-
-static void weld_edge_groups_setup(const int medge_len,
- const int edge_kill_len,
- MutableSpan<WeldEdge> wedge,
- Span<int> wedge_map,
- MutableSpan<int> r_edge_groups_map,
- Array<int> &r_edge_groups_buffer,
- Array<WeldGroupEdge> &r_edge_groups)
-{
- /* Get weld edge groups. */
-
- struct WeldGroupEdge *wegrp_iter;
-
- int wgroups_len = wedge.size() - edge_kill_len;
- r_edge_groups.reinitialize(wgroups_len);
- r_edge_groups.fill({{0}});
- MutableSpan<WeldGroupEdge> wegroups = r_edge_groups;
- wegrp_iter = &r_edge_groups[0];
-
- wgroups_len = 0;
- for (const int i : IndexRange(medge_len)) {
- int edge_ctx = wedge_map[i];
- if (edge_ctx != OUT_OF_CONTEXT) {
- WeldEdge *we = &wedge[edge_ctx];
- int edge_dest = we->edge_dest;
- if (edge_dest != OUT_OF_CONTEXT) {
- BLI_assert(edge_dest != we->edge_orig);
- r_edge_groups_map[i] = ELEM_MERGED;
- }
- else {
- we->edge_dest = we->edge_orig;
- wegrp_iter->v1 = we->vert_a;
- wegrp_iter->v2 = we->vert_b;
- r_edge_groups_map[i] = wgroups_len;
- wgroups_len++;
- wegrp_iter++;
- }
- }
- else {
- r_edge_groups_map[i] = OUT_OF_CONTEXT;
- }
- }
-
- BLI_assert(wgroups_len == wedge.size() - edge_kill_len);
-
- for (const WeldEdge &we : wedge) {
- if (we.flag == ELEM_COLLAPSED) {
- continue;
- }
- int group_index = r_edge_groups_map[we.edge_dest];
- wegroups[group_index].group.len++;
- }
-
- int ofs = 0;
- for (WeldGroupEdge &wegrp : wegroups) {
- wegrp.group.ofs = ofs;
- ofs += wegrp.group.len;
- }
-
- r_edge_groups_buffer.reinitialize(ofs);
- for (const WeldEdge &we : wedge) {
- if (we.flag == ELEM_COLLAPSED) {
- continue;
- }
- int group_index = r_edge_groups_map[we.edge_dest];
- r_edge_groups_buffer[wegroups[group_index].group.ofs++] = we.edge_orig;
- }
-
- for (WeldGroupEdge &wegrp : wegroups) {
- wegrp.group.ofs -= wegrp.group.len;
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Weld Poly and Loop API
- * \{ */
-
-static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter,
- const WeldPoly &wp,
- Span<WeldLoop> wloop,
- Span<MLoop> mloop,
- Span<int> loop_map,
- int *group_buffer)
-{
- if (wp.flag == ELEM_COLLAPSED) {
- return false;
- }
-
- iter->loop_start = wp.loop_start;
- iter->loop_end = wp.loop_end;
- iter->wloop = wloop;
- iter->mloop = mloop;
- iter->loop_map = loop_map;
- iter->group = group_buffer;
-
- int group_len = 0;
- if (group_buffer) {
- /* First loop group needs more attention. */
- int loop_start, loop_end, l;
- loop_start = iter->loop_start;
- loop_end = l = iter->loop_end;
- while (l >= loop_start) {
- const int loop_ctx = loop_map[l];
- if (loop_ctx != OUT_OF_CONTEXT) {
- const WeldLoop *wl = &wloop[loop_ctx];
- if (wl->flag == ELEM_COLLAPSED) {
- l--;
- continue;
- }
- }
- break;
- }
- if (l != loop_end) {
- group_len = loop_end - l;
- int i = 0;
- while (l < loop_end) {
- iter->group[i++] = ++l;
- }
- }
- }
- iter->group_len = group_len;
-
- iter->l_next = iter->loop_start;
-#ifdef USE_WELD_DEBUG
- iter->v = OUT_OF_CONTEXT;
-#endif
- return true;
-}
-
-static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter)
-{
- int loop_end = iter->loop_end;
- Span<WeldLoop> wloop = iter->wloop;
- Span<int> loop_map = iter->loop_map;
- int l = iter->l_curr = iter->l_next;
- if (l == iter->loop_start) {
- /* `grupo_len` is already calculated in the first loop */
- }
- else {
- iter->group_len = 0;
- }
- while (l <= loop_end) {
- int l_next = l + 1;
- const int loop_ctx = loop_map[l];
- if (loop_ctx != OUT_OF_CONTEXT) {
- const WeldLoop *wl = &wloop[loop_ctx];
- if (wl->loop_skip_to != OUT_OF_CONTEXT) {
- l_next = wl->loop_skip_to;
- }
- if (wl->flag == ELEM_COLLAPSED) {
- if (iter->group) {
- iter->group[iter->group_len++] = l;
- }
- l = l_next;
- continue;
- }
-#ifdef USE_WELD_DEBUG
- BLI_assert(iter->v != wl->vert);
-#endif
- iter->v = wl->vert;
- iter->e = wl->edge;
- iter->type = 1;
- }
- else {
- const MLoop *ml = &iter->mloop[l];
-#ifdef USE_WELD_DEBUG
- BLI_assert((uint)iter->v != ml->v);
-#endif
- iter->v = ml->v;
- iter->e = ml->e;
- iter->type = 0;
- }
- if (iter->group) {
- iter->group[iter->group_len++] = l;
- }
- iter->l_next = l_next;
- return true;
- }
-
- return false;
-}
-
-static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly,
- Span<MLoop> mloop,
- Span<int> vert_dest_map,
- Span<int> edge_dest_map,
- WeldMesh *r_weld_mesh)
-{
- /* Loop/Poly Context. */
- Array<int> loop_map(mloop.size());
- Array<int> poly_map(mpoly.size());
- int wloop_len = 0;
- int wpoly_len = 0;
- int max_ctx_poly_len = 4;
-
- Vector<WeldLoop> wloop;
- wloop.reserve(mloop.size());
-
- Vector<WeldPoly> wpoly;
- wpoly.reserve(mpoly.size());
-
- int maybe_new_poly = 0;
-
- for (const int i : mpoly.index_range()) {
- const MPoly &mp = mpoly[i];
- const int loopstart = mp.loopstart;
- const int totloop = mp.totloop;
-
- int vert_ctx_len = 0;
-
- int prev_wloop_len = wloop_len;
- for (const int i_loop : mloop.index_range().slice(loopstart, totloop)) {
- int v = mloop[i_loop].v;
- int e = mloop[i_loop].e;
- int v_dest = vert_dest_map[v];
- int e_dest = edge_dest_map[e];
- bool is_vert_ctx = v_dest != OUT_OF_CONTEXT;
- bool is_edge_ctx = e_dest != OUT_OF_CONTEXT;
- if (is_vert_ctx) {
- vert_ctx_len++;
- }
- if (is_vert_ctx || is_edge_ctx) {
- WeldLoop wl{};
- wl.vert = is_vert_ctx ? v_dest : v;
- wl.edge = is_edge_ctx ? e_dest : e;
- wl.loop_orig = i_loop;
- wl.loop_skip_to = OUT_OF_CONTEXT;
- wloop.append(wl);
-
- loop_map[i_loop] = wloop_len++;
- }
- else {
- loop_map[i_loop] = OUT_OF_CONTEXT;
- }
- }
- if (wloop_len != prev_wloop_len) {
- int loops_len = wloop_len - prev_wloop_len;
- WeldPoly wp{};
- wp.poly_dst = OUT_OF_CONTEXT;
- wp.poly_orig = i;
- wp.loops.len = loops_len;
- wp.loops.ofs = prev_wloop_len;
- wp.loop_start = loopstart;
- wp.loop_end = loopstart + totloop - 1;
- wp.len = totloop;
- wpoly.append(wp);
-
- poly_map[i] = wpoly_len++;
- if (totloop > 5 && vert_ctx_len > 1) {
- int max_new = (totloop / 3) - 1;
- vert_ctx_len /= 2;
- maybe_new_poly += MIN2(max_new, vert_ctx_len);
- CLAMP_MIN(max_ctx_poly_len, totloop);
- }
- }
- else {
- poly_map[i] = OUT_OF_CONTEXT;
- }
- }
-
- if (mpoly.size() < (wpoly_len + maybe_new_poly)) {
- wpoly.resize(wpoly_len + maybe_new_poly);
- }
-
- WeldPoly *poly_new = wpoly.data() + wpoly_len;
-
- r_weld_mesh->wloop = std::move(wloop);
- r_weld_mesh->wpoly = std::move(wpoly);
- r_weld_mesh->wpoly_new = poly_new;
- r_weld_mesh->wloop_len = wloop_len;
- r_weld_mesh->wpoly_len = wpoly_len;
- r_weld_mesh->wpoly_new_len = 0;
- r_weld_mesh->loop_map = std::move(loop_map);
- r_weld_mesh->poly_map = std::move(poly_map);
- r_weld_mesh->max_poly_len = max_ctx_poly_len;
-}
-
-static void weld_poly_split_recursive(Span<int> vert_dest_map,
-#ifdef USE_WELD_DEBUG
- const Span<MLoop> mloop,
-#endif
- int ctx_verts_len,
- WeldPoly *r_wp,
- WeldMesh *r_weld_mesh,
- int *r_poly_kill,
- int *r_loop_kill)
-{
- int poly_len = r_wp->len;
- if (poly_len > 3 && ctx_verts_len > 1) {
- const int ctx_loops_len = r_wp->loops.len;
- const int ctx_loops_ofs = r_wp->loops.ofs;
- MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
- WeldPoly *wpoly_new = r_weld_mesh->wpoly_new;
-
- int loop_kill = 0;
-
- WeldLoop *poly_loops = &wloop[ctx_loops_ofs];
- WeldLoop *wla = &poly_loops[0];
- WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1];
- while (wla_prev->flag == ELEM_COLLAPSED) {
- wla_prev--;
- }
- const int la_len = ctx_loops_len - 1;
- for (int la = 0; la < la_len; la++, wla++) {
- wa_continue:
- if (wla->flag == ELEM_COLLAPSED) {
- continue;
- }
- int vert_a = wla->vert;
- /* Only test vertices that will be merged. */
- if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) {
- int lb = la + 1;
- WeldLoop *wlb = wla + 1;
- WeldLoop *wlb_prev = wla;
- int killed_ab = 0;
- ctx_verts_len = 1;
- for (; lb < ctx_loops_len; lb++, wlb++) {
- BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT);
- if (wlb->flag == ELEM_COLLAPSED) {
- killed_ab++;
- continue;
- }
- int vert_b = wlb->vert;
- if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) {
- ctx_verts_len++;
- }
- if (vert_a == vert_b) {
- const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab;
- const int dist_b = poly_len - dist_a;
-
- BLI_assert(dist_a != 0 && dist_b != 0);
- if (dist_a == 1 || dist_b == 1) {
- BLI_assert(dist_a != dist_b);
- BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED));
- }
- else {
- WeldLoop *wl_tmp = nullptr;
- if (dist_a == 2) {
- wl_tmp = wlb_prev;
- BLI_assert(wla->flag != ELEM_COLLAPSED);
- BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
- wla->flag = ELEM_COLLAPSED;
- wl_tmp->flag = ELEM_COLLAPSED;
- loop_kill += 2;
- poly_len -= 2;
- }
- if (dist_b == 2) {
- if (wl_tmp != nullptr) {
- r_wp->flag = ELEM_COLLAPSED;
- *r_poly_kill += 1;
- }
- else {
- wl_tmp = wla_prev;
- BLI_assert(wlb->flag != ELEM_COLLAPSED);
- BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
- wlb->flag = ELEM_COLLAPSED;
- wl_tmp->flag = ELEM_COLLAPSED;
- }
- loop_kill += 2;
- poly_len -= 2;
- }
- if (wl_tmp == nullptr) {
- const int new_loops_len = lb - la;
- const int new_loops_ofs = ctx_loops_ofs + la;
-
- WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++];
- new_wp->poly_dst = OUT_OF_CONTEXT;
- new_wp->poly_orig = r_wp->poly_orig;
- new_wp->loops.len = new_loops_len;
- new_wp->loops.ofs = new_loops_ofs;
- new_wp->loop_start = wla->loop_orig;
- new_wp->loop_end = wlb_prev->loop_orig;
- new_wp->len = dist_a;
- weld_poly_split_recursive(vert_dest_map,
-#ifdef USE_WELD_DEBUG
- mloop,
-#endif
- ctx_verts_len,
- new_wp,
- r_weld_mesh,
- r_poly_kill,
- r_loop_kill);
- BLI_assert(dist_b == poly_len - dist_a);
- poly_len = dist_b;
- if (wla_prev->loop_orig > wla->loop_orig) {
- /* New start. */
- r_wp->loop_start = wlb->loop_orig;
- }
- else {
- /* The `loop_start` doesn't change but some loops must be skipped. */
- wla_prev->loop_skip_to = wlb->loop_orig;
- }
- wla = wlb;
- la = lb;
- goto wa_continue;
- }
- break;
- }
- }
- if (wlb->flag != ELEM_COLLAPSED) {
- wlb_prev = wlb;
- }
- }
- }
- if (wla->flag != ELEM_COLLAPSED) {
- wla_prev = wla;
- }
- }
- r_wp->len = poly_len;
- *r_loop_kill += loop_kill;
-
-#ifdef USE_WELD_DEBUG
- weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map);
-#endif
- }
-}
-
-static void weld_poly_loop_ctx_setup(Span<MLoop> mloop,
-#ifdef USE_WELD_DEBUG
- Span<MPoly> mpoly,
-#endif
- const int mvert_len,
- Span<int> vert_dest_map,
- const int remain_edge_ctx_len,
- MutableSpan<WeldGroup> r_vlinks,
- WeldMesh *r_weld_mesh)
-{
- int poly_kill_len, loop_kill_len, wpoly_len, wpoly_new_len;
-
- WeldPoly *wpoly_new;
- WeldLoop *wl;
-
- MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly;
- MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
- wpoly_new = r_weld_mesh->wpoly_new;
- wpoly_len = r_weld_mesh->wpoly_len;
- wpoly_new_len = 0;
- poly_kill_len = 0;
- loop_kill_len = 0;
-
- Span<int> loop_map = r_weld_mesh->loop_map;
-
- if (remain_edge_ctx_len) {
-
- /* Setup Poly/Loop. Note that `wpoly_len` may be different than `wpoly.size()` here. */
- for (const int i : IndexRange(wpoly_len)) {
- WeldPoly &wp = wpoly[i];
- const int ctx_loops_len = wp.loops.len;
- const int ctx_loops_ofs = wp.loops.ofs;
-
- int poly_len = wp.len;
- int ctx_verts_len = 0;
- wl = &wloop[ctx_loops_ofs];
- for (int l = ctx_loops_len; l--; wl++) {
- const int edge_dest = wl->edge;
- if (edge_dest == ELEM_COLLAPSED) {
- wl->flag = ELEM_COLLAPSED;
- if (poly_len == 3) {
- wp.flag = ELEM_COLLAPSED;
- poly_kill_len++;
- loop_kill_len += 3;
- poly_len = 0;
- break;
- }
- loop_kill_len++;
- poly_len--;
- }
- else {
- const int vert_dst = wl->vert;
- if (vert_dest_map[vert_dst] != OUT_OF_CONTEXT) {
- ctx_verts_len++;
- }
- }
- }
-
- if (poly_len) {
- wp.len = poly_len;
-#ifdef USE_WELD_DEBUG
- weld_assert_poly_len(wp, wloop);
-#endif
-
- weld_poly_split_recursive(vert_dest_map,
-#ifdef USE_WELD_DEBUG
- mloop,
-#endif
- ctx_verts_len,
- &wp,
- r_weld_mesh,
- &poly_kill_len,
- &loop_kill_len);
-
- wpoly_new_len = r_weld_mesh->wpoly_new_len;
- }
- }
-
-#ifdef USE_WELD_DEBUG
- weld_assert_poly_and_loop_kill_len(wpoly,
- {wpoly_new, wpoly_new_len},
- wloop,
- mloop,
- loop_map,
- r_weld_mesh->poly_map,
- mpoly,
- poly_kill_len,
- loop_kill_len);
-#endif
-
- /* Setup Polygon Overlap. */
-
- int wpoly_and_new_len = wpoly_len + wpoly_new_len;
-
- r_vlinks.fill({0, 0});
- MutableSpan<WeldGroup> v_links = r_vlinks;
-
- for (const int i : IndexRange(wpoly_and_new_len)) {
- const WeldPoly &wp = wpoly[i];
- WeldLoopOfPolyIter iter;
- if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) {
- while (weld_iter_loop_of_poly_next(&iter)) {
- v_links[iter.v].len++;
- }
- }
- }
-
- int link_len = 0;
- for (const int i : IndexRange(mvert_len)) {
- v_links[i].ofs = link_len;
- link_len += v_links[i].len;
- }
-
- if (link_len) {
- Array<int> link_poly_buffer(link_len);
-
- for (const int i : IndexRange(wpoly_and_new_len)) {
- const WeldPoly &wp = wpoly[i];
- WeldLoopOfPolyIter iter;
- if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) {
- while (weld_iter_loop_of_poly_next(&iter)) {
- link_poly_buffer[v_links[iter.v].ofs++] = i;
- }
- }
- }
-
- for (WeldGroup &vl : r_vlinks) {
- /* Fix offset */
- vl.ofs -= vl.len;
- }
-
- int polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b;
- polys_len_b = p_ctx_b = 0; /* silence warnings */
-
- for (const int i : IndexRange(wpoly_and_new_len)) {
- const WeldPoly &wp = wpoly[i];
- if (wp.poly_dst != OUT_OF_CONTEXT) {
- /* No need to retest poly.
- * (Already includes collapsed polygons). */
- continue;
- }
-
- WeldLoopOfPolyIter iter;
- weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr);
- weld_iter_loop_of_poly_next(&iter);
- struct WeldGroup *link_a = &v_links[iter.v];
- polys_len_a = link_a->len;
- if (polys_len_a == 1) {
- BLI_assert(link_poly_buffer[link_a->ofs] == i);
- continue;
- }
- int wp_len = wp.len;
- polys_ctx_a = &link_poly_buffer[link_a->ofs];
- for (; polys_len_a--; polys_ctx_a++) {
- p_ctx_a = *polys_ctx_a;
- if (p_ctx_a == i) {
- continue;
- }
-
- WeldPoly *wp_tmp = &wpoly[p_ctx_a];
- if (wp_tmp->len != wp_len) {
- continue;
- }
-
- WeldLoopOfPolyIter iter_b = iter;
- while (weld_iter_loop_of_poly_next(&iter_b)) {
- struct WeldGroup *link_b = &v_links[iter_b.v];
- polys_len_b = link_b->len;
- if (polys_len_b == 1) {
- BLI_assert(link_poly_buffer[link_b->ofs] == i);
- polys_len_b = 0;
- break;
- }
-
- polys_ctx_b = &link_poly_buffer[link_b->ofs];
- for (; polys_len_b; polys_len_b--, polys_ctx_b++) {
- p_ctx_b = *polys_ctx_b;
- if (p_ctx_b < p_ctx_a) {
- continue;
- }
- if (p_ctx_b >= p_ctx_a) {
- if (p_ctx_b > p_ctx_a) {
- polys_len_b = 0;
- }
- break;
- }
- }
- if (polys_len_b == 0) {
- break;
- }
- }
- if (polys_len_b == 0) {
- continue;
- }
- BLI_assert(p_ctx_a > i);
- BLI_assert(p_ctx_a == p_ctx_b);
- BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT);
- BLI_assert(wp_tmp != &wp);
- wp_tmp->poly_dst = wp.poly_orig;
- loop_kill_len += wp_tmp->len;
- poly_kill_len++;
- }
- }
- }
- }
- else {
- poly_kill_len = r_weld_mesh->wpoly_len;
- loop_kill_len = r_weld_mesh->wloop_len;
-
- for (WeldPoly &wp : wpoly) {
- wp.flag = ELEM_COLLAPSED;
- }
- }
-
-#ifdef USE_WELD_DEBUG
- weld_assert_poly_and_loop_kill_len(wpoly,
- {wpoly_new, wpoly_new_len},
- wloop,
- mloop,
- loop_map,
- r_weld_mesh->poly_map,
- mpoly,
- poly_kill_len,
- loop_kill_len);
-#endif
-
- r_weld_mesh->wpoly_new = wpoly_new;
- r_weld_mesh->poly_kill_len = poly_kill_len;
- r_weld_mesh->loop_kill_len = loop_kill_len;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Weld Mesh API
- * \{ */
-
-static void weld_mesh_context_create(const Mesh *mesh,
- MutableSpan<int> vert_dest_map,
- const int vert_kill_len,
- WeldMesh *r_weld_mesh)
-{
- Span<MEdge> medge{mesh->medge, mesh->totedge};
- Span<MPoly> mpoly{mesh->mpoly, mesh->totpoly};
- Span<MLoop> mloop{mesh->mloop, mesh->totloop};
- const int mvert_len = mesh->totvert;
-
- Vector<WeldVert> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map, vert_kill_len);
- r_weld_mesh->vert_kill_len = vert_kill_len;
-
- Array<int> edge_dest_map(medge.size());
- Array<int> edge_ctx_map(medge.size());
- Vector<WeldEdge> wedge = weld_edge_ctx_alloc(medge, vert_dest_map, edge_dest_map, edge_ctx_map);
-
- Array<WeldGroup> v_links(mvert_len, {0, 0});
- weld_edge_ctx_setup(v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len);
-
- weld_poly_loop_ctx_alloc(mpoly, mloop, vert_dest_map, edge_dest_map, r_weld_mesh);
-
- weld_poly_loop_ctx_setup(mloop,
-#ifdef USE_WELD_DEBUG
- mpoly,
-
-#endif
- mvert_len,
- vert_dest_map,
- wedge.size() - r_weld_mesh->edge_kill_len,
- v_links,
- r_weld_mesh);
-
- weld_vert_groups_setup(wvert,
- vert_dest_map,
- vert_dest_map,
- r_weld_mesh->vert_groups_buffer,
- r_weld_mesh->vert_groups);
-
- weld_edge_groups_setup(medge.size(),
- r_weld_mesh->edge_kill_len,
- wedge,
- edge_ctx_map,
- edge_dest_map,
- r_weld_mesh->edge_groups_buffer,
- r_weld_mesh->edge_groups);
-
- r_weld_mesh->edge_groups_map = std::move(edge_dest_map);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Weld CustomData
- * \{ */
-
-static void customdata_weld(
- const CustomData *source, CustomData *dest, const int *src_indices, int count, int dest_index)
-{
- if (count == 1) {
- CustomData_copy_data(source, dest, src_indices[0], dest_index, 1);
- return;
- }
-
- CustomData_interp(source, dest, (const int *)src_indices, nullptr, nullptr, count, dest_index);
-
- int src_i, dest_i;
- int j;
-
- float co[3] = {0.0f, 0.0f, 0.0f};
-#ifdef USE_WELD_NORMALS
- float no[3] = {0.0f, 0.0f, 0.0f};
-#endif
- int crease = 0;
- int bweight = 0;
- short flag = 0;
-
- /* interpolates a layer at a time */
- dest_i = 0;
- for (src_i = 0; src_i < source->totlayer; src_i++) {
- const int type = source->layers[src_i].type;
-
- /* find the first dest layer with type >= the source type
- * (this should work because layers are ordered by type)
- */
- while (dest_i < dest->totlayer && dest->layers[dest_i].type < type) {
- dest_i++;
- }
-
- /* if there are no more dest layers, we're done */
- if (dest_i == dest->totlayer) {
- break;
- }
-
- /* if we found a matching layer, add the data */
- if (dest->layers[dest_i].type == type) {
- void *src_data = source->layers[src_i].data;
-
- if (type == CD_MVERT) {
- for (j = 0; j < count; j++) {
- MVert *mv_src = &((MVert *)src_data)[src_indices[j]];
- add_v3_v3(co, mv_src->co);
-#ifdef USE_WELD_NORMALS
- short *mv_src_no = mv_src->no;
- no[0] += mv_src_no[0];
- no[1] += mv_src_no[1];
- no[2] += mv_src_no[2];
-#endif
- bweight += mv_src->bweight;
- flag |= mv_src->flag;
- }
- }
- else if (type == CD_MEDGE) {
- for (j = 0; j < count; j++) {
- MEdge *me_src = &((MEdge *)src_data)[src_indices[j]];
- crease += me_src->crease;
- bweight += me_src->bweight;
- flag |= me_src->flag;
- }
- }
- else if (CustomData_layer_has_interp(dest, dest_i)) {
- /* Already calculated.
- * TODO: Optimize by exposing `typeInfo->interp`. */
- }
- else if (CustomData_layer_has_math(dest, dest_i)) {
- const int size = CustomData_sizeof(type);
- void *dst_data = dest->layers[dest_i].data;
- void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size);
- for (j = 0; j < count; j++) {
- CustomData_data_add(
- type, v_dst, POINTER_OFFSET(src_data, (size_t)src_indices[j] * size));
- }
- }
- else {
- CustomData_copy_layer_type_data(source, dest, type, src_indices[0], dest_index, 1);
- }
-
- /* if there are multiple source & dest layers of the same type,
- * we don't want to copy all source layers to the same dest, so
- * increment dest_i
- */
- dest_i++;
- }
- }
-
- float fac = 1.0f / count;
-
- for (dest_i = 0; dest_i < dest->totlayer; dest_i++) {
- CustomDataLayer *layer_dst = &dest->layers[dest_i];
- const int type = layer_dst->type;
- if (type == CD_MVERT) {
- MVert *mv = &((MVert *)layer_dst->data)[dest_index];
- mul_v3_fl(co, fac);
- bweight *= fac;
- CLAMP_MAX(bweight, 255);
-
- copy_v3_v3(mv->co, co);
-#ifdef USE_WELD_NORMALS
- mul_v3_fl(no, fac);
- short *mv_no = mv->no;
- mv_no[0] = (short)no[0];
- mv_no[1] = (short)no[1];
- mv_no[2] = (short)no[2];
-#endif
-
- mv->flag = (char)flag;
- mv->bweight = (char)bweight;
- }
- else if (type == CD_MEDGE) {
- MEdge *me = &((MEdge *)layer_dst->data)[dest_index];
- crease *= fac;
- bweight *= fac;
- CLAMP_MAX(crease, 255);
- CLAMP_MAX(bweight, 255);
-
- me->crease = (char)crease;
- me->bweight = (char)bweight;
- me->flag = flag;
- }
- else if (CustomData_layer_has_interp(dest, dest_i)) {
- /* Already calculated. */
- }
- else if (CustomData_layer_has_math(dest, dest_i)) {
- const int size = CustomData_sizeof(type);
- void *dst_data = layer_dst->data;
- void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size);
- CustomData_data_multiply(type, v_dst, fac);
- }
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Weld Modifier Main
- * \{ */
-
-#ifdef USE_BVHTREEKDOP
-struct WeldOverlapData {
- const MVert *mvert;
- float merge_dist_sq;
-};
-static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
-{
- if (index_a < index_b) {
- struct WeldOverlapData *data = userdata;
- const MVert *mvert = data->mvert;
- const float dist_sq = len_squared_v3v3(mvert[index_a].co, mvert[index_b].co);
- BLI_assert(dist_sq <= ((data->merge_dist_sq + FLT_EPSILON) * 3));
- return dist_sq <= data->merge_dist_sq;
- }
- return false;
-}
-#endif
-
-/** Use for #MOD_WELD_MODE_CONNECTED calculation. */
-struct WeldVertexCluster {
- float co[3];
- int merged_verts;
-};
-
-static Mesh *weldModifier_doWeld(WeldModifierData *wmd,
- const ModifierEvalContext *UNUSED(ctx),
- Mesh *mesh)
-{
- BLI_bitmap *v_mask = nullptr;
- int v_mask_act = 0;
-
- Span<MVert> mvert{mesh->mvert, mesh->totvert};
- Span<MEdge> medge{mesh->medge, mesh->totedge};
- Span<MPoly> mpoly{mesh->mpoly, mesh->totpoly};
- Span<MLoop> mloop{mesh->mloop, mesh->totloop};
- const int totvert = mesh->totvert;
- const int totedge = mesh->totedge;
- const int totloop = mesh->totloop;
- const int totpoly = mesh->totpoly;
-
- /* Vertex Group. */
- const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name);
- if (defgrp_index != -1) {
- MDeformVert *dvert;
- dvert = static_cast<MDeformVert *>(CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT));
- if (dvert) {
- const bool invert_vgroup = (wmd->flag & MOD_WELD_INVERT_VGROUP) != 0;
- v_mask = BLI_BITMAP_NEW(totvert, __func__);
- for (const int i : IndexRange(totvert)) {
- const bool found = BKE_defvert_find_weight(&dvert[i], defgrp_index) > 0.0f;
- if (found != invert_vgroup) {
- BLI_BITMAP_ENABLE(v_mask, i);
- v_mask_act++;
- }
- }
- }
- }
-
- /* From the original index of the vertex.
- * This indicates which vert it is or is going to be merged. */
- Array<int> vert_dest_map(totvert);
- int vert_kill_len = 0;
- if (wmd->mode == MOD_WELD_MODE_ALL)
-#ifdef USE_BVHTREEKDOP
- {
- /* Get overlap map. */
- struct BVHTreeFromMesh treedata;
- BVHTree *bvhtree = bvhtree_from_mesh_verts_ex(&treedata,
- mvert.data(),
- totvert,
- false,
- v_mask,
- v_mask_act,
- wmd->merge_dist / 2,
- 2,
- 6,
- 0,
- nullptr,
- nullptr);
-
- if (bvhtree) {
- struct WeldOverlapData data;
- data.mvert = mvert;
- data.merge_dist_sq = square_f(wmd->merge_dist);
-
- uint overlap_len;
- BVHTreeOverlap *overlap = BLI_bvhtree_overlap_ex(bvhtree,
- bvhtree,
- &overlap_len,
- bvhtree_weld_overlap_cb,
- &data,
- 1,
- BVH_OVERLAP_RETURN_PAIRS);
-
- free_bvhtree_from_mesh(&treedata);
- if (overlap) {
- range_vn_i(vert_dest_map.data(), totvert, 0);
-
- const BVHTreeOverlap *overlap_iter = &overlap[0];
- for (int i = 0; i < overlap_len; i++, overlap_iter++) {
- int indexA = overlap_iter->indexA;
- int indexB = overlap_iter->indexB;
-
- BLI_assert(indexA < indexB);
-
- int va_dst = vert_dest_map[indexA];
- while (va_dst != vert_dest_map[va_dst]) {
- va_dst = vert_dest_map[va_dst];
- }
- int vb_dst = vert_dest_map[indexB];
- while (vb_dst != vert_dest_map[vb_dst]) {
- vb_dst = vert_dest_map[vb_dst];
- }
- if (va_dst == vb_dst) {
- continue;
- }
- if (va_dst > vb_dst) {
- SWAP(int, va_dst, vb_dst);
- }
- vert_kill_len++;
- vert_dest_map[vb_dst] = va_dst;
- }
-
- /* Fix #r_vert_dest_map for next step. */
- for (int i = 0; i < totvert; i++) {
- if (i == vert_dest_map[i]) {
- vert_dest_map[i] = OUT_OF_CONTEXT;
- }
- else {
- int v = i;
- while (v != vert_dest_map[v] && vert_dest_map[v] != OUT_OF_CONTEXT) {
- v = vert_dest_map[v];
- }
- vert_dest_map[v] = v;
- vert_dest_map[i] = v;
- }
- }
-
- MEM_freeN(overlap);
- }
- }
- }
-#else
- {
- KDTree_3d *tree = BLI_kdtree_3d_new(v_mask ? v_mask_act : totvert);
- for (const int i : IndexRange(totvert)) {
- if (!v_mask || BLI_BITMAP_TEST(v_mask, i)) {
- BLI_kdtree_3d_insert(tree, i, mvert[i].co);
- }
- vert_dest_map[i] = OUT_OF_CONTEXT;
- }
-
- BLI_kdtree_3d_balance(tree);
- vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast(
- tree, wmd->merge_dist, false, vert_dest_map.data());
- BLI_kdtree_3d_free(tree);
- }
-#endif
- else {
- BLI_assert(wmd->mode == MOD_WELD_MODE_CONNECTED);
-
- Array<WeldVertexCluster> vert_clusters(totvert);
-
- for (const int i : mvert.index_range()) {
- WeldVertexCluster &vc = vert_clusters[i];
- copy_v3_v3(vc.co, mvert[i].co);
- vc.merged_verts = 0;
- }
- const float merge_dist_sq = square_f(wmd->merge_dist);
-
- range_vn_i(vert_dest_map.data(), totvert, 0);
-
- /* Collapse Edges that are shorter than the threshold. */
- for (const int i : medge.index_range()) {
- int v1 = medge[i].v1;
- int v2 = medge[i].v2;
-
- if (wmd->flag & MOD_WELD_LOOSE_EDGES && (medge[i].flag & ME_LOOSEEDGE) == 0) {
- continue;
- }
- while (v1 != vert_dest_map[v1]) {
- v1 = vert_dest_map[v1];
- }
- while (v2 != vert_dest_map[v2]) {
- v2 = vert_dest_map[v2];
- }
- if (v1 == v2) {
- continue;
- }
- if (v_mask && (!BLI_BITMAP_TEST(v_mask, v1) || !BLI_BITMAP_TEST(v_mask, v2))) {
- continue;
- }
- if (v1 > v2) {
- SWAP(int, v1, v2);
- }
- WeldVertexCluster *v1_cluster = &vert_clusters[v1];
- WeldVertexCluster *v2_cluster = &vert_clusters[v2];
-
- float edgedir[3];
- sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co);
- const float dist_sq = len_squared_v3(edgedir);
- if (dist_sq <= merge_dist_sq) {
- float influence = (v2_cluster->merged_verts + 1) /
- (float)(v1_cluster->merged_verts + v2_cluster->merged_verts + 2);
- madd_v3_v3fl(v1_cluster->co, edgedir, influence);
-
- v1_cluster->merged_verts += v2_cluster->merged_verts + 1;
- vert_dest_map[v2] = v1;
- vert_kill_len++;
- }
- }
-
- for (const int i : IndexRange(totvert)) {
- if (i == vert_dest_map[i]) {
- vert_dest_map[i] = OUT_OF_CONTEXT;
- }
- else {
- int v = i;
- while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) {
- v = vert_dest_map[v];
- }
- vert_dest_map[v] = v;
- vert_dest_map[i] = v;
- }
- }
- }
-
- if (v_mask) {
- MEM_freeN(v_mask);
- }
-
- if (vert_kill_len == 0) {
+ std::optional<Mesh *> result = calculate_weld(*mesh, wmd);
+ if (!result) {
return mesh;
}
-
- WeldMesh weld_mesh;
- weld_mesh_context_create(mesh, vert_dest_map, vert_kill_len, &weld_mesh);
-
- const int result_nverts = totvert - weld_mesh.vert_kill_len;
- const int result_nedges = totedge - weld_mesh.edge_kill_len;
- const int result_nloops = totloop - weld_mesh.loop_kill_len;
- const int result_npolys = totpoly - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len;
-
- Mesh *result = BKE_mesh_new_nomain_from_template(
- mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys);
-
- /* Vertices. */
-
- /* Be careful when editing this array, to avoid new allocations it uses the same buffer as
- * #vert_dest_map. This map will be used to adjust the edges, polys and loops. */
- MutableSpan<int> vert_final = vert_dest_map;
-
- int dest_index = 0;
- for (int i = 0; i < totvert; i++) {
- int source_index = i;
- int count = 0;
- while (i < totvert && vert_dest_map[i] == OUT_OF_CONTEXT) {
- vert_final[i] = dest_index + count;
- count++;
- i++;
- }
- if (count) {
- CustomData_copy_data(&mesh->vdata, &result->vdata, source_index, dest_index, count);
- dest_index += count;
- }
- if (i == totvert) {
- break;
- }
- if (vert_dest_map[i] != ELEM_MERGED) {
- struct WeldGroup *wgroup = &weld_mesh.vert_groups[vert_dest_map[i]];
- customdata_weld(&mesh->vdata,
- &result->vdata,
- &weld_mesh.vert_groups_buffer[wgroup->ofs],
- wgroup->len,
- dest_index);
- vert_final[i] = dest_index;
- dest_index++;
- }
- }
-
- BLI_assert(dest_index == result_nverts);
-
- /* Edges. */
-
- /* Be careful when editing this array, to avoid new allocations it uses the same buffer as
- * #edge_groups_map. This map will be used to adjust the polys and loops. */
- MutableSpan<int> edge_final = weld_mesh.edge_groups_map;
-
- dest_index = 0;
- for (int i = 0; i < totedge; i++) {
- int source_index = i;
- int count = 0;
- while (i < totedge && weld_mesh.edge_groups_map[i] == OUT_OF_CONTEXT) {
- edge_final[i] = dest_index + count;
- count++;
- i++;
- }
- if (count) {
- CustomData_copy_data(&mesh->edata, &result->edata, source_index, dest_index, count);
- MEdge *me = &result->medge[dest_index];
- dest_index += count;
- for (; count--; me++) {
- me->v1 = vert_final[me->v1];
- me->v2 = vert_final[me->v2];
- }
- }
- if (i == totedge) {
- break;
- }
- if (weld_mesh.edge_groups_map[i] != ELEM_MERGED) {
- struct WeldGroupEdge *wegrp = &weld_mesh.edge_groups[weld_mesh.edge_groups_map[i]];
- customdata_weld(&mesh->edata,
- &result->edata,
- &weld_mesh.edge_groups_buffer[wegrp->group.ofs],
- wegrp->group.len,
- dest_index);
- MEdge *me = &result->medge[dest_index];
- me->v1 = vert_final[wegrp->v1];
- me->v2 = vert_final[wegrp->v2];
- me->flag |= ME_LOOSEEDGE;
-
- edge_final[i] = dest_index;
- dest_index++;
- }
- }
-
- BLI_assert(dest_index == result_nedges);
-
- /* Polys/Loops. */
-
- MPoly *r_mp = &result->mpoly[0];
- MLoop *r_ml = &result->mloop[0];
- int r_i = 0;
- int loop_cur = 0;
- Array<int, 64> group_buffer(weld_mesh.max_poly_len);
- for (const int i : mpoly.index_range()) {
- const MPoly &mp = mpoly[i];
- int loop_start = loop_cur;
- int poly_ctx = weld_mesh.poly_map[i];
- if (poly_ctx == OUT_OF_CONTEXT) {
- int mp_loop_len = mp.totloop;
- CustomData_copy_data(&mesh->ldata, &result->ldata, mp.loopstart, loop_cur, mp_loop_len);
- loop_cur += mp_loop_len;
- for (; mp_loop_len--; r_ml++) {
- r_ml->v = vert_final[r_ml->v];
- r_ml->e = edge_final[r_ml->e];
- }
- }
- else {
- const WeldPoly &wp = weld_mesh.wpoly[poly_ctx];
- WeldLoopOfPolyIter iter;
- if (!weld_iter_loop_of_poly_begin(
- &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) {
- continue;
- }
-
- if (wp.poly_dst != OUT_OF_CONTEXT) {
- continue;
- }
- while (weld_iter_loop_of_poly_next(&iter)) {
- customdata_weld(
- &mesh->ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur);
- int v = vert_final[iter.v];
- int e = edge_final[iter.e];
- r_ml->v = v;
- r_ml->e = e;
- r_ml++;
- loop_cur++;
- if (iter.type) {
- result->medge[e].flag &= ~ME_LOOSEEDGE;
- }
- BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0);
- }
- }
-
- CustomData_copy_data(&mesh->pdata, &result->pdata, i, r_i, 1);
- r_mp->loopstart = loop_start;
- r_mp->totloop = loop_cur - loop_start;
- r_mp++;
- r_i++;
- }
-
- for (const int i : IndexRange(weld_mesh.wpoly_new_len)) {
- const WeldPoly &wp = weld_mesh.wpoly_new[i];
- int loop_start = loop_cur;
- WeldLoopOfPolyIter iter;
- if (!weld_iter_loop_of_poly_begin(
- &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) {
- continue;
- }
-
- if (wp.poly_dst != OUT_OF_CONTEXT) {
- continue;
- }
- while (weld_iter_loop_of_poly_next(&iter)) {
- customdata_weld(&mesh->ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur);
- int v = vert_final[iter.v];
- int e = edge_final[iter.e];
- r_ml->v = v;
- r_ml->e = e;
- r_ml++;
- loop_cur++;
- if (iter.type) {
- result->medge[e].flag &= ~ME_LOOSEEDGE;
- }
- BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0);
- }
-
- r_mp->loopstart = loop_start;
- r_mp->totloop = loop_cur - loop_start;
- r_mp++;
- r_i++;
- }
-
- BLI_assert((int)r_i == result_npolys);
- BLI_assert(loop_cur == result_nloops);
-
- /* We could only update the normals of the elements in context, but the next modifier can make it
- * dirty anyway which would make the work useless. */
- BKE_mesh_normals_tag_dirty(result);
-
- return result;
-}
-
-static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
-{
- WeldModifierData *wmd = (WeldModifierData *)md;
- return weldModifier_doWeld(wmd, ctx, mesh);
+ return *result;
}
static void initData(ModifierData *md)
@@ -1945,7 +218,6 @@ ModifierTypeInfo modifierType_Weld = {
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c
index 706960182cf..72bc4336eff 100644
--- a/source/blender/modifiers/intern/MOD_wireframe.c
+++ b/source/blender/modifiers/intern/MOD_wireframe.c
@@ -194,7 +194,6 @@ ModifierTypeInfo modifierType_Wireframe = {
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
/* initData */ initData,
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 402a71af27f..43fb90a52cd 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -133,10 +133,6 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index c80d0bcdd1e..71acc738472 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -140,6 +140,8 @@ void register_node_type_cmp_pixelate(void);
void register_node_type_cmp_trackpos(void);
void register_node_type_cmp_planetrackdeform(void);
void register_node_type_cmp_cornerpin(void);
+void register_node_type_cmp_separate_xyz(void);
+void register_node_type_cmp_combine_xyz(void);
void node_cmp_rlayers_outputs(struct bNodeTree *ntree, struct bNode *node);
void node_cmp_rlayers_register_pass(struct bNodeTree *ntree,
@@ -152,6 +154,75 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index);
void register_node_type_cmp_custom_group(bNodeType *ntype);
+void ntreeCompositExecTree(struct Scene *scene,
+ struct bNodeTree *ntree,
+ struct RenderData *rd,
+ int rendering,
+ int do_previews,
+ const struct ColorManagedViewSettings *view_settings,
+ const struct ColorManagedDisplaySettings *display_settings,
+ const char *view_name);
+
+/**
+ * Called from render pipeline, to tag render input and output.
+ * need to do all scenes, to prevent errors when you re-render 1 scene.
+ */
+void ntreeCompositTagRender(struct Scene *scene);
+
+/**
+ * Update the outputs of the render layer nodes.
+ * Since the outputs depend on the render engine, this part is a bit complex:
+ * - #ntreeCompositUpdateRLayers is called and loops over all render layer nodes.
+ * - Each render layer node calls the update function of the
+ * render engine that's used for its scene.
+ * - The render engine calls RE_engine_register_pass for each pass.
+ * - #RE_engine_register_pass calls #node_cmp_rlayers_register_pass.
+ */
+void ntreeCompositUpdateRLayers(struct bNodeTree *ntree);
+
+void ntreeCompositClearTags(struct bNodeTree *ntree);
+
+struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree,
+ struct bNode *node,
+ const char *name,
+ struct ImageFormatData *im_format);
+
+int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node);
+void ntreeCompositOutputFileSetPath(struct bNode *node,
+ struct bNodeSocket *sock,
+ const char *name);
+void ntreeCompositOutputFileSetLayer(struct bNode *node,
+ struct bNodeSocket *sock,
+ const char *name);
+/* needed in do_versions */
+void ntreeCompositOutputFileUniquePath(struct ListBase *list,
+ struct bNodeSocket *sock,
+ const char defname[],
+ char delim);
+void ntreeCompositOutputFileUniqueLayer(struct ListBase *list,
+ struct bNodeSocket *sock,
+ const char defname[],
+ char delim);
+
+void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node);
+void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node);
+
+void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node);
+void ntreeCompositCryptomatteSyncFromRemove(bNode *node);
+bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node);
+int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node);
+void ntreeCompositCryptomatteLayerPrefix(const Scene *scene,
+ const bNode *node,
+ char *r_prefix,
+ size_t prefix_len);
+
+/**
+ * Update the runtime layer names with the crypto-matte layer names of the references render layer
+ * or image.
+ */
+void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node);
+struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 3b9dd5a1f7c..609d92c09df 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -77,6 +77,7 @@ void register_node_type_geo_curve_fill(void);
void register_node_type_geo_curve_fillet(void);
void register_node_type_geo_curve_handle_type_selection(void);
void register_node_type_geo_curve_length(void);
+void register_node_type_geo_curve_primitive_arc(void);
void register_node_type_geo_curve_primitive_bezier_segment(void);
void register_node_type_geo_curve_primitive_circle(void);
void register_node_type_geo_curve_primitive_line(void);
@@ -98,7 +99,9 @@ void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_dual_mesh(void);
void register_node_type_geo_edge_split(void);
+void register_node_type_geo_extrude_mesh(void);
void register_node_type_geo_field_at_index(void);
+void register_node_type_geo_flip_faces(void);
void register_node_type_geo_geometry_to_instance(void);
void register_node_type_geo_image_texture(void);
void register_node_type_geo_input_curve_handles(void);
@@ -129,6 +132,7 @@ void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
void register_node_type_geo_material_replace(void);
void register_node_type_geo_material_selection(void);
+void register_node_type_geo_merge_by_distance(void);
void register_node_type_geo_mesh_primitive_circle(void);
void register_node_type_geo_mesh_primitive_cone(void);
void register_node_type_geo_mesh_primitive_cube(void);
@@ -154,6 +158,7 @@ void register_node_type_geo_raycast(void);
void register_node_type_geo_realize_instances(void);
void register_node_type_geo_rotate_instances(void);
void register_node_type_geo_sample_texture(void);
+void register_node_type_geo_scale_elements(void);
void register_node_type_geo_scale_instances(void);
void register_node_type_geo_select_by_handle_type(void);
void register_node_type_geo_separate_components(void);
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 76c174201e8..57740ce3ad9 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -85,6 +85,7 @@ void register_node_type_sh_layer_weight(void);
void register_node_type_sh_tex_coord(void);
void register_node_type_sh_particle_info(void);
void register_node_type_sh_hair_info(void);
+void register_node_type_sh_point_info(void);
void register_node_type_sh_volume_info(void);
void register_node_type_sh_script(void);
void register_node_type_sh_normal_map(void);
@@ -143,6 +144,27 @@ void register_node_type_sh_tex_white_noise(void);
void register_node_type_sh_custom_group(bNodeType *ntype);
+struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree);
+void ntreeShaderEndExecTree(struct bNodeTreeExec *exec);
+
+/**
+ * Find an output node of the shader tree.
+ *
+ * \note it will only return output which is NOT in the group, which isn't how
+ * render engines works but it's how the GPU shader compilation works. This we
+ * can change in the future and make it a generic function, but for now it stays
+ * private here.
+ */
+struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target);
+
+/**
+ * This one needs to work on a local tree.
+ */
+void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
+ struct GPUMaterial *mat,
+ bool *has_surface_output,
+ bool *has_volume_output);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 7434e0071ee..8fd71a978e3 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -96,6 +96,7 @@ DefNode(ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIG
DefNode(ShaderNode, SH_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "" )
DefNode(ShaderNode, SH_NODE_PARTICLE_INFO, 0, "PARTICLE_INFO", ParticleInfo, "Particle Info", "" )
DefNode(ShaderNode, SH_NODE_HAIR_INFO, 0, "HAIR_INFO", HairInfo, "Hair Info", "" )
+DefNode(ShaderNode, SH_NODE_POINT_INFO, 0, "POINT_INFO", PointInfo, "Point Info", "" )
DefNode(ShaderNode, SH_NODE_VOLUME_INFO, 0, "VOLUME_INFO", VolumeInfo, "Volume Info", "" )
DefNode(ShaderNode, SH_NODE_WIREFRAME, def_sh_tex_wireframe, "WIREFRAME", Wireframe, "Wireframe", "" )
DefNode(ShaderNode, SH_NODE_WAVELENGTH, 0, "WAVELENGTH", Wavelength, "Wavelength", "" )
@@ -229,6 +230,8 @@ DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIAL
DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" )
DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space","" )
DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" )
+DefNode(CompositorNode, CMP_NODE_COMBINE_XYZ, 0, "COMBINE_XYZ", CombineXYZ, "Combine XYZ", "" )
+DefNode(CompositorNode, CMP_NODE_SEPARATE_XYZ, 0, "SEPARATE_XYZ", SeparateXYZ, "Separate XYZ", "" )
DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )
@@ -335,6 +338,7 @@ DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Conve
DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "")
DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "")
DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_ARC, def_geo_curve_primitive_arc, "CURVE_PRIMITIVE_ARC", CurveArc, "Arc", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "")
@@ -351,9 +355,11 @@ DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE
DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "")
DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "")
+DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "")
DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "")
DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
+DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "")
DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "")
DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "")
@@ -383,6 +389,7 @@ DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", In
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "")
+DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, 0, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "")
DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
@@ -405,6 +412,7 @@ DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE
DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
+DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "")
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "")
diff --git a/source/blender/nodes/NOD_texture.h b/source/blender/nodes/NOD_texture.h
index af59fefd925..c08bc814915 100644
--- a/source/blender/nodes/NOD_texture.h
+++ b/source/blender/nodes/NOD_texture.h
@@ -74,6 +74,22 @@ void register_node_type_tex_proc_noise(void);
void register_node_type_tex_proc_stucci(void);
void register_node_type_tex_proc_distnoise(void);
+void ntreeTexCheckCyclics(struct bNodeTree *ntree);
+struct bNodeTreeExec *ntreeTexBeginExecTree(struct bNodeTree *ntree);
+void ntreeTexEndExecTree(struct bNodeTreeExec *exec);
+int ntreeTexExecTree(struct bNodeTree *ntree,
+ struct TexResult *target,
+ const float co[3],
+ float dxt[3],
+ float dyt[3],
+ int osatex,
+ short thread,
+ const struct Tex *tex,
+ short which_output,
+ int cfra,
+ int preview,
+ struct MTex *mtex);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt
index aceca617fb4..8581ab7f82f 100644
--- a/source/blender/nodes/composite/CMakeLists.txt
+++ b/source/blender/nodes/composite/CMakeLists.txt
@@ -54,9 +54,9 @@ set(SRC
nodes/node_composite_color_spill.cc
nodes/node_composite_colorbalance.cc
nodes/node_composite_colorcorrection.cc
- nodes/node_composite_convert_color_space.cc
nodes/node_composite_common.cc
nodes/node_composite_composite.cc
+ nodes/node_composite_convert_color_space.cc
nodes/node_composite_cornerpin.cc
nodes/node_composite_crop.cc
nodes/node_composite_cryptomatte.cc
@@ -105,10 +105,12 @@ set(SRC
nodes/node_composite_rgb.cc
nodes/node_composite_rotate.cc
nodes/node_composite_scale.cc
+ nodes/node_composite_scene_time.cc
nodes/node_composite_sepcomb_hsva.cc
nodes/node_composite_sepcomb_rgba.cc
nodes/node_composite_sepcomb_ycca.cc
nodes/node_composite_sepcomb_yuva.cc
+ nodes/node_composite_sepcomb_xyz.cc
nodes/node_composite_setalpha.cc
nodes/node_composite_split_viewer.cc
nodes/node_composite_stabilize2d.cc
@@ -125,7 +127,6 @@ set(SRC
nodes/node_composite_vec_blur.cc
nodes/node_composite_viewer.cc
nodes/node_composite_zcombine.cc
- nodes/node_composite_scene_time.cc
node_composite_tree.cc
node_composite_util.cc
@@ -133,10 +134,6 @@ set(SRC
node_composite_util.hh
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
index fcc04a85b38..d889130c2fa 100644
--- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2017 Blender Foundation.
* All rights reserved.
- *
- * The Original Code is: all of this file.
*/
/** \file
diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc
index dd0a6db74c1..96dbf0def4a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- * Juho Vepsäläinen
*/
/** \file
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
index 282328b5e10..b7ab28f3465 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- * Juho Vepsäläinen
*/
/** \file
diff --git a/source/blender/nodes/composite/nodes/node_composite_common.cc b/source/blender/nodes/composite/nodes/node_composite_common.cc
index d5f7279398e..a3fa940460b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_common.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_common.cc
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- * Juho Vepsäläinen
*/
/** \file
diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.cc b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
index d407bcbde63..8b9ccfe47e0 100644
--- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
@@ -15,8 +15,6 @@
*
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
- *
- * The Original Code is: all of this file.
*/
/** \file
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
new file mode 100644
index 00000000000..5dfcf5dca1e
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
@@ -0,0 +1,71 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup cmpnodes
+ */
+
+#include "node_composite_util.hh"
+
+/* **************** SEPARATE XYZ ******************** */
+namespace blender::nodes {
+
+static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Float>("X");
+ b.add_output<decl::Float>("Y");
+ b.add_output<decl::Float>("Z");
+}
+
+} // namespace blender::nodes
+
+void register_node_type_cmp_separate_xyz()
+{
+ static bNodeType ntype;
+
+ cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_XYZ, "Separate XYZ", NODE_CLASS_CONVERTER);
+ ntype.declare = blender::nodes::cmp_node_separate_xyz_declare;
+
+ nodeRegisterType(&ntype);
+}
+
+/* **************** COMBINE XYZ ******************** */
+
+namespace blender::nodes {
+
+static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("X").min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>("Y").min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>("Z").min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Vector>("Vector");
+}
+
+} // namespace blender::nodes
+
+void register_node_type_cmp_combine_xyz()
+{
+ static bNodeType ntype;
+
+ cmp_node_type_base(&ntype, CMP_NODE_COMBINE_XYZ, "Combine XYZ", NODE_CLASS_CONVERTER);
+ ntype.declare = blender::nodes::cmp_node_combine_xyz_declare;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.cc b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
index 678d7fe1a9b..606eaa8ed99 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switchview.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- * Dalai Felinto
*/
/** \file
diff --git a/source/blender/nodes/function/CMakeLists.txt b/source/blender/nodes/function/CMakeLists.txt
index 0c3c6a34995..6418c525776 100644
--- a/source/blender/nodes/function/CMakeLists.txt
+++ b/source/blender/nodes/function/CMakeLists.txt
@@ -63,10 +63,6 @@ set(LIB
bf_functions
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_nodes_function "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_UNITY_BUILD)
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index cd05f07d392..9425c4ff328 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -22,6 +22,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "NOD_socket_search_link.hh"
+
#include "node_function_util.hh"
namespace blender::nodes::node_fn_boolean_math_cc {
@@ -43,8 +45,7 @@ static void node_boolean_math_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sockB = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
- nodeSetSocketAvailability(
- ntree, sockB, ELEM(node->custom1, NODE_BOOLEAN_MATH_AND, NODE_BOOLEAN_MATH_OR));
+ nodeSetSocketAvailability(ntree, sockB, !ELEM(node->custom1, NODE_BOOLEAN_MATH_NOT));
}
static void node_boolean_math_label(const bNodeTree *UNUSED(ntree),
@@ -60,6 +61,27 @@ static void node_boolean_math_label(const bNodeTree *UNUSED(ntree),
BLI_strncpy(label, IFACE_(name), maxlen);
}
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ if (!params.node_tree().typeinfo->validate_link(
+ static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_BOOLEAN)) {
+ return;
+ }
+
+ for (const EnumPropertyItem *item = rna_enum_node_boolean_math_items;
+ item->identifier != nullptr;
+ item++) {
+ if (item->name != nullptr && item->identifier[0] != '\0') {
+ NodeBooleanMathOperation operation = static_cast<NodeBooleanMathOperation>(item->value);
+ params.add_item(IFACE_(item->name), [operation](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("FunctionNodeBooleanMath");
+ node.custom1 = operation;
+ params.update_and_connect_available_socket(node, "Boolean");
+ });
+ }
+ }
+}
+
static const fn::MultiFunction *get_multi_function(bNode &bnode)
{
static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{"And",
@@ -67,6 +89,18 @@ static const fn::MultiFunction *get_multi_function(bNode &bnode)
static fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{"Or",
[](bool a, bool b) { return a || b; }};
static fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }};
+ static fn::CustomMF_SI_SI_SO<bool, bool, bool> nand_fn{"Not And",
+ [](bool a, bool b) { return !(a && b); }};
+ static fn::CustomMF_SI_SI_SO<bool, bool, bool> nor_fn{"Nor",
+ [](bool a, bool b) { return !(a || b); }};
+ static fn::CustomMF_SI_SI_SO<bool, bool, bool> xnor_fn{"Equal",
+ [](bool a, bool b) { return a == b; }};
+ static fn::CustomMF_SI_SI_SO<bool, bool, bool> xor_fn{"Not Equal",
+ [](bool a, bool b) { return a != b; }};
+ static fn::CustomMF_SI_SI_SO<bool, bool, bool> imply_fn{"Imply",
+ [](bool a, bool b) { return !a || b; }};
+ static fn::CustomMF_SI_SI_SO<bool, bool, bool> nimply_fn{"Subtract",
+ [](bool a, bool b) { return a && !b; }};
switch (bnode.custom1) {
case NODE_BOOLEAN_MATH_AND:
@@ -75,6 +109,18 @@ static const fn::MultiFunction *get_multi_function(bNode &bnode)
return &or_fn;
case NODE_BOOLEAN_MATH_NOT:
return &not_fn;
+ case NODE_BOOLEAN_MATH_NAND:
+ return &nand_fn;
+ case NODE_BOOLEAN_MATH_NOR:
+ return &nor_fn;
+ case NODE_BOOLEAN_MATH_XNOR:
+ return &xnor_fn;
+ case NODE_BOOLEAN_MATH_XOR:
+ return &xor_fn;
+ case NODE_BOOLEAN_MATH_IMPLY:
+ return &imply_fn;
+ case NODE_BOOLEAN_MATH_NIMPLY:
+ return &nimply_fn;
}
BLI_assert_unreachable();
@@ -101,5 +147,6 @@ void register_node_type_fn_boolean_math()
node_type_update(&ntype, file_ns::node_boolean_math_update);
ntype.build_multi_function = file_ns::fn_node_boolean_math_build_multi_function;
ntype.draw_buttons = file_ns::fn_node_boolean_math_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index b9e762a341a..2a8faf65a6d 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -95,6 +95,7 @@ set(SRC
nodes/node_geo_curve_fillet.cc
nodes/node_geo_curve_handle_type_selection.cc
nodes/node_geo_curve_length.cc
+ nodes/node_geo_curve_primitive_arc.cc
nodes/node_geo_curve_primitive_bezier_segment.cc
nodes/node_geo_curve_primitive_circle.cc
nodes/node_geo_curve_primitive_line.cc
@@ -116,7 +117,9 @@ set(SRC
nodes/node_geo_distribute_points_on_faces.cc
nodes/node_geo_dual_mesh.cc
nodes/node_geo_edge_split.cc
+ nodes/node_geo_extrude_mesh.cc
nodes/node_geo_field_at_index.cc
+ nodes/node_geo_flip_faces.cc
nodes/node_geo_geometry_to_instance.cc
nodes/node_geo_image_texture.cc
nodes/node_geo_input_curve_handles.cc
@@ -147,6 +150,7 @@ set(SRC
nodes/node_geo_join_geometry.cc
nodes/node_geo_material_replace.cc
nodes/node_geo_material_selection.cc
+ nodes/node_geo_merge_by_distance.cc
nodes/node_geo_mesh_primitive_circle.cc
nodes/node_geo_mesh_primitive_cone.cc
nodes/node_geo_mesh_primitive_cube.cc
@@ -165,6 +169,7 @@ set(SRC
nodes/node_geo_raycast.cc
nodes/node_geo_realize_instances.cc
nodes/node_geo_rotate_instances.cc
+ nodes/node_geo_scale_elements.cc
nodes/node_geo_scale_instances.cc
nodes/node_geo_separate_components.cc
nodes/node_geo_separate_geometry.cc
@@ -234,10 +239,6 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
index ae034d152be..a71431bdf8e 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
@@ -93,7 +93,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
/* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
const float3 remapped_position = position * 2.0f - float3(1.0f);
BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
- colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
+ copy_v4_v4(colors[i], texture_result.trgba);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index 9001cb2d1f2..840dfd2fbd3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -48,8 +48,8 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
}
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index 68b609f8045..81421609dfd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -44,7 +44,9 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PropertySubType::PROP_DISTANCE)
.default_value(0.25f)
.supports_field();
- b.add_input<decl::Bool>(N_("Limit Radius"));
+ b.add_input<decl::Bool>(N_("Limit Radius"))
+ .description(
+ N_("Limit the maximum value of the radius in order to avoid overlapping fillets"));
b.add_output<decl::Geometry>(N_("Curve"));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc
new file mode 100644
index 00000000000..3f6298168a2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc
@@ -0,0 +1,391 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+#include "BLI_math_base_safe.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "node_geometry_util.hh"
+#include <numeric>
+
+namespace blender::nodes::node_geo_curve_primitive_arc_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveArc)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(16)
+ .min(2)
+ .max(256)
+ .subtype(PROP_UNSIGNED)
+ .description(N_("The number of points on the arc"));
+ b.add_input<decl::Vector>(N_("Start"))
+ .default_value({-1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the first control point"));
+ b.add_input<decl::Vector>(N_("Middle"))
+ .default_value({0.0f, 2.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the middle control point"));
+ b.add_input<decl::Vector>(N_("End"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the last control point"));
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Distance of the points from the origin"));
+ b.add_input<decl::Float>(N_("Start Angle"))
+ .default_value(0.0f)
+ .subtype(PROP_ANGLE)
+ .description(N_("Starting angle of the arc"));
+ b.add_input<decl::Float>(N_("Sweep Angle"))
+ .default_value(1.75f * M_PI)
+ .min(-2 * M_PI)
+ .max(2 * M_PI)
+ .subtype(PROP_ANGLE)
+ .description(N_("Length of the arc"));
+ b.add_input<decl::Float>(N_("Offset Angle"))
+ .default_value(0.0f)
+ .subtype(PROP_ANGLE)
+ .description(N_("Offset angle of the arc"));
+ b.add_input<decl::Bool>(N_("Connect Center"))
+ .default_value(false)
+ .description(N_("Connect the arc at the center"));
+ b.add_input<decl::Bool>(N_("Invert Arc"))
+ .default_value(false)
+ .description(N_("Invert and draw opposite arc"));
+
+ b.add_output<decl::Geometry>(N_("Curve"));
+ b.add_output<decl::Vector>(N_("Center"))
+ .description(N_("The center of the circle described by the three points"))
+ .make_available(
+ [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; });
+ b.add_output<decl::Vector>(N_("Normal"))
+ .description(N_("The normal direction of the plane described by the three points, pointing "
+ "towards the positive Z axis"))
+ .make_available(
+ [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; });
+ b.add_output<decl::Float>(N_("Radius"))
+ .description(N_("The radius of the circle described by the three points"))
+ .make_available(
+ [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; });
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurvePrimitiveArc *data = MEM_cnew<NodeGeometryCurvePrimitiveArc>(__func__);
+
+ data->mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryCurvePrimitiveArc &storage = node_storage(*node);
+ const GeometryNodeCurvePrimitiveArcMode mode = (GeometryNodeCurvePrimitiveArcMode)storage.mode;
+
+ bNodeSocket *start_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *middle_socket = start_socket->next;
+ bNodeSocket *end_socket = middle_socket->next;
+
+ bNodeSocket *radius_socket = end_socket->next;
+ bNodeSocket *start_angle_socket = radius_socket->next;
+ bNodeSocket *sweep_angle_socket = start_angle_socket->next;
+
+ bNodeSocket *offset_angle_socket = sweep_angle_socket->next;
+
+ bNodeSocket *center_out_socket = ((bNodeSocket *)node->outputs.first)->next;
+ bNodeSocket *normal_out_socket = center_out_socket->next;
+ bNodeSocket *radius_out_socket = normal_out_socket->next;
+
+ const bool radius_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS);
+ const bool points_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS);
+
+ nodeSetSocketAvailability(ntree, start_socket, points_mode);
+ nodeSetSocketAvailability(ntree, middle_socket, points_mode);
+ nodeSetSocketAvailability(ntree, end_socket, points_mode);
+
+ nodeSetSocketAvailability(ntree, radius_socket, radius_mode);
+ nodeSetSocketAvailability(ntree, start_angle_socket, radius_mode);
+ nodeSetSocketAvailability(ntree, sweep_angle_socket, radius_mode);
+
+ nodeSetSocketAvailability(ntree, offset_angle_socket, points_mode);
+
+ nodeSetSocketAvailability(ntree, center_out_socket, points_mode);
+ nodeSetSocketAvailability(ntree, normal_out_socket, points_mode);
+ nodeSetSocketAvailability(ntree, radius_out_socket, points_mode);
+}
+
+static float3 rotate_vector_around_axis(const float3 vector, const float3 axis, const float angle)
+{
+ float3 result = vector;
+ float mat[3][3];
+ axis_angle_to_mat3(mat, axis, angle);
+ mul_m3_v3(mat, result);
+ return result;
+}
+
+static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3)
+{
+ const float3 a = math::normalize(p2 - p1);
+ const float3 b = math::normalize(p3 - p1);
+ return (ELEM(a, b, b * -1.0f));
+}
+
+static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolution,
+ const float3 a,
+ const float3 b,
+ const float3 c,
+ float angle_offset,
+ const bool connect_center,
+ const bool invert_arc,
+ float3 &r_center,
+ float3 &r_normal,
+ float &r_radius)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+
+ if (connect_center) {
+ spline->resize(resolution + 1);
+ }
+ else {
+ spline->resize(resolution);
+ }
+
+ const int stepcount = resolution - 1;
+ const int centerpoint = resolution;
+ MutableSpan<float3> positions = spline->positions();
+ spline->radii().fill(1.0f);
+ spline->tilts().fill(0.0f);
+
+ const bool is_colinear = colinear_f3_f3_f3(a, b, c);
+
+ float3 center;
+ float3 normal;
+ float radius;
+ const float3 mid_ac = math::midpoint(a, c);
+ normal_tri_v3(normal, a, c, b);
+
+ if (is_colinear || a == c || a == b || b == c || resolution == 2) {
+ /* If colinear, generate a point line between points. */
+ float3 p1, p2;
+
+ /* Find the two points that are furthest away from each other. */
+ const float ab = math::distance_squared(a, b);
+ const float ac = math::distance_squared(a, c);
+ const float bc = math::distance_squared(b, c);
+ if (ab > ac && ab > bc) {
+ p1 = a;
+ p2 = b;
+ }
+ else if (bc > ab && bc > ac) {
+ p1 = b;
+ p2 = c;
+ }
+ else {
+ p1 = a;
+ p2 = c;
+ }
+
+ const float step = 1.0f / stepcount;
+ for (const int i : IndexRange(resolution)) {
+ const float factor = step * i;
+ positions[i] = math::interpolate(p1, p2, factor);
+ }
+ center = mid_ac;
+ radius = 0.0f;
+ }
+ else {
+ /* Midpoints of `A->B` and `B->C`. */
+ const float3 mid_ab = math::midpoint(a, b);
+ const float3 mid_bc = math::midpoint(c, b);
+
+ /* Normalized vectors of `A->B` and `B->C`. */
+ const float3 nba = math::normalize(b - a);
+ const float3 ncb = math::normalize(c - b);
+
+ /* Normal of plane of main 2 segments A->B and `B->C`. */
+ const float3 nabc = math::normalize(math::cross(nba, ncb));
+
+ /* Determine center point from the intersection of 3 planes. */
+ float plane_1[4], plane_2[4], plane_3[4];
+ plane_from_point_normal_v3(plane_1, mid_ab, nabc);
+ plane_from_point_normal_v3(plane_2, mid_ab, nba);
+ plane_from_point_normal_v3(plane_3, mid_bc, ncb);
+
+ /* If the 3 planes do not intersect at one point, just return empty geometry. */
+ if (!isect_plane_plane_plane_v3(plane_1, plane_2, plane_3, center)) {
+ r_center = mid_ac;
+ r_normal = normal;
+ r_radius = 0.0f;
+ return nullptr;
+ }
+
+ /* Radial vectors. */
+ const float3 rad_a = math::normalize(a - center);
+ const float3 rad_b = math::normalize(b - center);
+ const float3 rad_c = math::normalize(c - center);
+
+ /* Calculate angles. */
+ radius = math::distance(center, b);
+ float angle_ab = angle_signed_on_axis_v3v3_v3(rad_a, rad_b, normal) + 2.0f * M_PI;
+ float angle_ac = angle_signed_on_axis_v3v3_v3(rad_a, rad_c, normal) + 2.0f * M_PI;
+ float angle = (angle_ac > angle_ab) ? angle_ac : angle_ab;
+ angle -= 2.0f * M_PI;
+ if (invert_arc) {
+ angle = -(2.0f * M_PI - angle);
+ }
+
+ /* Create arc. */
+ const float step = angle / stepcount;
+ for (const int i : IndexRange(resolution)) {
+ const float factor = step * i + angle_offset;
+ float3 out = rotate_vector_around_axis(rad_a, -normal, factor);
+ positions[i] = out * radius + center;
+ }
+ }
+
+ if (connect_center) {
+ spline->set_cyclic(true);
+ positions[centerpoint] = center;
+ }
+
+ /* Ensure normal is relative to Z-up. */
+ if (math::dot(float3(0, 0, 1), normal) < 0) {
+ normal = -normal;
+ }
+
+ curve->add_spline(std::move(spline));
+ curve->attributes.reallocate(curve->splines().size());
+ r_center = center;
+ r_radius = radius;
+ r_normal = normal;
+ return curve;
+}
+
+static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolution,
+ const float radius,
+ const float start_angle,
+ const float sweep_angle,
+ const bool connect_center,
+ const bool invert_arc)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+
+ if (connect_center) {
+ spline->resize(resolution + 1);
+ }
+ else {
+ spline->resize(resolution);
+ }
+
+ const int stepcount = resolution - 1;
+ const int centerpoint = resolution;
+ MutableSpan<float3> positions = spline->positions();
+ spline->radii().fill(1.0f);
+ spline->tilts().fill(0.0f);
+
+ const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle;
+
+ const float theta_step = sweep / float(stepcount);
+ for (const int i : IndexRange(resolution)) {
+ const float theta = theta_step * i + start_angle;
+ const float x = radius * cos(theta);
+ const float y = radius * sin(theta);
+ positions[i] = float3(x, y, 0.0f);
+ }
+
+ if (connect_center) {
+ spline->set_cyclic(true);
+ positions[centerpoint] = float3(0.0f, 0.0f, 0.0f);
+ }
+
+ curve->add_spline(std::move(spline));
+ curve->attributes.reallocate(curve->splines().size());
+ return curve;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurvePrimitiveArc &storage = node_storage(params.node());
+
+ const GeometryNodeCurvePrimitiveArcMode mode = (GeometryNodeCurvePrimitiveArcMode)storage.mode;
+
+ switch (mode) {
+ case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS: {
+ std::unique_ptr<CurveEval> curve;
+ float3 r_center, r_normal;
+ float r_radius;
+ curve = create_arc_curve_from_points(std::max(params.extract_input<int>("Resolution"), 2),
+ params.extract_input<float3>("Start"),
+ params.extract_input<float3>("Middle"),
+ params.extract_input<float3>("End"),
+ params.extract_input<float>("Offset Angle"),
+ params.extract_input<bool>("Connect Center"),
+ params.extract_input<bool>("Invert Arc"),
+ r_center,
+ r_normal,
+ r_radius);
+ params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ params.set_output("Center", r_center);
+ params.set_output("Normal", r_normal);
+ params.set_output("Radius", r_radius);
+ break;
+ }
+ case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS: {
+ std::unique_ptr<CurveEval> curve;
+ curve = create_arc_curve_from_radius(std::max(params.extract_input<int>("Resolution"), 2),
+ params.extract_input<float>("Radius"),
+ params.extract_input<float>("Start Angle"),
+ params.extract_input<float>("Sweep Angle"),
+ params.extract_input<bool>("Connect Center"),
+ params.extract_input<bool>("Invert Arc"));
+
+ params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ break;
+ }
+ }
+}
+
+} // namespace blender::nodes::node_geo_curve_primitive_arc_cc
+
+void register_node_type_geo_curve_primitive_arc()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_arc_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_ARC, "Arc", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_storage(&ntype,
+ "NodeGeometryCurvePrimitiveArc",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
index ae282017e0c..22549e59ad2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -30,7 +30,13 @@ namespace blender::nodes::node_geo_curve_subdivide_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
- b.add_input<decl::Int>(N_("Cuts")).default_value(1).min(0).max(1000).supports_field();
+ b.add_input<decl::Int>(N_("Cuts"))
+ .default_value(1)
+ .min(0)
+ .max(1000)
+ .supports_field()
+ .description(
+ N_("The number of control points to create on the segment following each point"));
b.add_output<decl::Geometry>(N_("Curve"));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
index 19efd4b7508..c0c1244f031 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -282,18 +282,20 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines,
}
if (!data.tangents.is_empty()) {
- spline.sample_with_index_factors<float3>(
- spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size));
- for (float3 &tangent : data.tangents) {
- tangent = math::normalize(tangent);
+ Span<float3> src_tangents = spline.evaluated_tangents();
+ MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, size);
+ spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents);
+ for (float3 &vector : sampled_tangents) {
+ vector = math::normalize(vector);
}
}
if (!data.normals.is_empty()) {
- spline.sample_with_index_factors<float3>(
- spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size));
- for (float3 &normals : data.normals) {
- normals = math::normalize(normals);
+ Span<float3> src_normals = spline.evaluated_normals();
+ MutableSpan<float3> sampled_normals = data.normals.slice(offset, size);
+ spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals);
+ for (float3 &vector : sampled_normals) {
+ vector = math::normalize(vector);
}
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
index a257af4391c..d17657bfa3a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -44,7 +44,7 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Density Max")).default_value(10.0f).min(0.0f);
- b.add_input<decl::Float>(N_("Density")).default_value(10.0f).supports_field();
+ b.add_input<decl::Float>(N_("Density")).default_value(10.0f).min(0.0f).supports_field();
b.add_input<decl::Float>(N_("Density Factor"))
.default_value(1.0f)
.min(0.0f)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
new file mode 100644
index 00000000000..1d1c5bd2285
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
@@ -0,0 +1,1365 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_disjoint_set.hh"
+#include "BLI_task.hh"
+#include "BLI_vector_set.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_extrude_mesh_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryExtrudeMesh)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Vector>(N_("Offset")).subtype(PROP_TRANSLATION).implicit_field().hide_value();
+ b.add_input<decl::Float>(N_("Offset Scale")).default_value(1.0f).min(0.0f).supports_field();
+ b.add_input<decl::Bool>(N_("Individual")).default_value(true);
+ b.add_output<decl::Geometry>("Mesh");
+ b.add_output<decl::Bool>(N_("Top")).field_source();
+ b.add_output<decl::Bool>(N_("Side")).field_source();
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryExtrudeMesh *data = MEM_cnew<NodeGeometryExtrudeMesh>(__func__);
+ data->mode = GEO_NODE_EXTRUDE_MESH_FACES;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryExtrudeMesh &storage = node_storage(*node);
+ const GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode);
+
+ bNodeSocket *individual_socket = (bNodeSocket *)node->inputs.last;
+
+ nodeSetSocketAvailability(ntree, individual_socket, mode == GEO_NODE_EXTRUDE_MESH_FACES);
+}
+
+struct AttributeOutputs {
+ StrongAnonymousAttributeID top_id;
+ StrongAnonymousAttributeID side_id;
+};
+
+static void save_selection_as_attribute(MeshComponent &component,
+ const AnonymousAttributeID *id,
+ const AttributeDomain domain,
+ const IndexMask selection)
+{
+ BLI_assert(!component.attribute_exists(id));
+
+ OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>(
+ id, domain);
+ /* Rely on the new attribute being zeroed by default. */
+ BLI_assert(!attribute.as_span().as_span().contains(true));
+
+ if (selection.is_range()) {
+ attribute.as_span().slice(selection.as_range()).fill(true);
+ }
+ else {
+ attribute.as_span().fill_indices(selection, true);
+ }
+
+ attribute.save();
+}
+
+static MutableSpan<MVert> mesh_verts(Mesh &mesh)
+{
+ return {mesh.mvert, mesh.totvert};
+}
+static MutableSpan<MEdge> mesh_edges(Mesh &mesh)
+{
+ return {mesh.medge, mesh.totedge};
+}
+static Span<MPoly> mesh_polys(const Mesh &mesh)
+{
+ return {mesh.mpoly, mesh.totpoly};
+}
+static MutableSpan<MPoly> mesh_polys(Mesh &mesh)
+{
+ return {mesh.mpoly, mesh.totpoly};
+}
+static Span<MLoop> mesh_loops(const Mesh &mesh)
+{
+ return {mesh.mloop, mesh.totloop};
+}
+static MutableSpan<MLoop> mesh_loops(Mesh &mesh)
+{
+ return {mesh.mloop, mesh.totloop};
+}
+
+/**
+ * \note: Some areas in this file rely on the new sections of attributes from #CustomData_realloc
+ * to be zeroed.
+ */
+static void expand_mesh(Mesh &mesh,
+ const int vert_expand,
+ const int edge_expand,
+ const int poly_expand,
+ const int loop_expand)
+{
+ if (vert_expand != 0) {
+ CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert);
+ mesh.totvert += vert_expand;
+ CustomData_realloc(&mesh.vdata, mesh.totvert);
+ }
+ else {
+ /* Even when the number of vertices is not changed, the mesh can still be deformed. */
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert);
+ }
+ if (edge_expand != 0) {
+ CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge);
+ mesh.totedge += edge_expand;
+ CustomData_realloc(&mesh.edata, mesh.totedge);
+ }
+ if (poly_expand != 0) {
+ CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly);
+ mesh.totpoly += poly_expand;
+ CustomData_realloc(&mesh.pdata, mesh.totpoly);
+ }
+ if (loop_expand != 0) {
+ CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop);
+ mesh.totloop += loop_expand;
+ CustomData_realloc(&mesh.ldata, mesh.totloop);
+ }
+ BKE_mesh_update_customdata_pointers(&mesh, false);
+}
+
+static MEdge new_edge(const int v1, const int v2)
+{
+ MEdge edge;
+ edge.v1 = v1;
+ edge.v2 = v2;
+ edge.flag = (ME_EDGEDRAW | ME_EDGERENDER);
+ return edge;
+}
+
+static MEdge new_loose_edge(const int v1, const int v2)
+{
+ MEdge edge;
+ edge.v1 = v1;
+ edge.v2 = v2;
+ edge.flag = ME_LOOSEEDGE;
+ return edge;
+}
+
+static MPoly new_poly(const int loopstart, const int totloop)
+{
+ MPoly poly;
+ poly.loopstart = loopstart;
+ poly.totloop = totloop;
+ poly.flag = 0;
+ return poly;
+}
+
+template<typename T> void copy_with_indices(MutableSpan<T> dst, Span<T> src, Span<int> indices)
+{
+ BLI_assert(dst.size() == indices.size());
+ for (const int i : dst.index_range()) {
+ dst[i] = src[indices[i]];
+ }
+}
+
+template<typename T> void copy_with_mask(MutableSpan<T> dst, Span<T> src, IndexMask mask)
+{
+ BLI_assert(dst.size() == mask.size());
+ threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) {
+ for (const int i : range) {
+ dst[i] = src[mask[i]];
+ }
+ });
+}
+
+/**
+ * \param get_mix_indices_fn: Returns a Span of indices of the source points to mix for every
+ * result point.
+ */
+template<typename T, typename GetMixIndicesFn>
+void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn)
+{
+ threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) {
+ attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)};
+ for (const int i_dst : IndexRange(range.size())) {
+ for (const int i_src : get_mix_indices_fn(range[i_dst])) {
+ mixer.mix_in(i_dst, src[i_src]);
+ }
+ }
+ mixer.finalize();
+ });
+}
+
+static Array<Vector<int>> create_vert_to_edge_map(const int vert_size,
+ Span<MEdge> edges,
+ const int vert_offset = 0)
+{
+ Array<Vector<int>> vert_to_edge_map(vert_size);
+ for (const int i : edges.index_range()) {
+ vert_to_edge_map[edges[i].v1 - vert_offset].append(i);
+ vert_to_edge_map[edges[i].v2 - vert_offset].append(i);
+ }
+ return vert_to_edge_map;
+}
+
+static void extrude_mesh_vertices(MeshComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &offset_field,
+ const AttributeOutputs &attribute_outputs)
+{
+ Mesh &mesh = *component.get_for_write();
+ const int orig_vert_size = mesh.totvert;
+ const int orig_edge_size = mesh.totedge;
+
+ GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, mesh.totvert};
+ evaluator.add(offset_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> offsets = evaluator.get_evaluated<float3>(0);
+
+ /* This allows parallelizing attribute mixing for new edges. */
+ Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh_edges(mesh));
+
+ expand_mesh(mesh, selection.size(), selection.size(), 0, 0);
+
+ const IndexRange new_vert_range{orig_vert_size, selection.size()};
+ const IndexRange new_edge_range{orig_edge_size, selection.size()};
+
+ MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
+ MutableSpan<MEdge> new_edges = mesh_edges(mesh).slice(new_edge_range);
+
+ for (const int i_selection : selection.index_range()) {
+ new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]);
+ }
+
+ component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) {
+ return true;
+ }
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ id, meta_data.domain, meta_data.data_type);
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> data = attribute.as_span().typed<T>();
+ switch (attribute.domain()) {
+ case ATTR_DOMAIN_POINT: {
+ /* New vertices copy the attribute values from their source vertex. */
+ copy_with_mask(data.slice(new_vert_range), data.as_span(), selection);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* New edge values are mixed from of all the edges connected to the source vertex. */
+ copy_with_mixing(data.slice(new_edge_range), data.as_span(), [&](const int i) {
+ return vert_to_edge_map[selection[i]].as_span();
+ });
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ });
+
+ attribute.save();
+ return true;
+ });
+
+ devirtualize_varray(offsets, [&](const auto offsets) {
+ threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ const float3 offset = offsets[selection[i]];
+ add_v3_v3(new_verts[i].co, offset);
+ }
+ });
+ });
+
+ if (attribute_outputs.top_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range);
+ }
+ if (attribute_outputs.side_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range);
+ }
+
+ BKE_mesh_runtime_clear_cache(&mesh);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh)
+{
+ Span<MPoly> polys = mesh_polys(mesh);
+ Span<MLoop> loops = mesh_loops(mesh);
+ Array<Vector<int, 2>> polys_of_edge(mesh.totedge);
+
+ for (const int i_poly : polys.index_range()) {
+ const MPoly &poly = polys[i_poly];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ polys_of_edge[loop.e].append(i_poly);
+ }
+ }
+
+ return polys_of_edge;
+}
+
+static void fill_quad_consistent_direction(Span<MLoop> other_poly_loops,
+ MutableSpan<MLoop> new_loops,
+ const int vert_connected_to_poly_1,
+ const int vert_connected_to_poly_2,
+ const int vert_across_from_poly_1,
+ const int vert_across_from_poly_2,
+ const int edge_connected_to_poly,
+ const int connecting_edge_1,
+ const int edge_across_from_poly,
+ const int connecting_edge_2)
+{
+ /* Find the loop on the polygon connected to the new quad that uses the duplicate edge. */
+ bool start_with_connecting_edge = true;
+ for (const MLoop &loop : other_poly_loops) {
+ if (loop.e == edge_connected_to_poly) {
+ start_with_connecting_edge = loop.v == vert_connected_to_poly_1;
+ break;
+ }
+ }
+ if (start_with_connecting_edge) {
+ new_loops[0].v = vert_connected_to_poly_1;
+ new_loops[0].e = connecting_edge_1;
+ new_loops[1].v = vert_across_from_poly_1;
+ new_loops[1].e = edge_across_from_poly;
+ new_loops[2].v = vert_across_from_poly_2;
+ new_loops[2].e = connecting_edge_2;
+ new_loops[3].v = vert_connected_to_poly_2;
+ new_loops[3].e = edge_connected_to_poly;
+ }
+ else {
+ new_loops[0].v = vert_connected_to_poly_1;
+ new_loops[0].e = edge_connected_to_poly;
+ new_loops[1].v = vert_connected_to_poly_2;
+ new_loops[1].e = connecting_edge_2;
+ new_loops[2].v = vert_across_from_poly_2;
+ new_loops[2].e = edge_across_from_poly;
+ new_loops[3].v = vert_across_from_poly_1;
+ new_loops[3].e = connecting_edge_1;
+ }
+}
+
+template<typename T>
+static VectorSet<int> vert_indices_from_edges(const Mesh &mesh, const Span<T> edge_indices)
+{
+ static_assert(is_same_any_v<T, int, int64_t>);
+
+ VectorSet<int> vert_indices;
+ vert_indices.reserve(edge_indices.size());
+ for (const T i_edge : edge_indices) {
+ const MEdge &edge = mesh.medge[i_edge];
+ vert_indices.add(edge.v1);
+ vert_indices.add(edge.v2);
+ }
+ return vert_indices;
+}
+
+static void extrude_mesh_edges(MeshComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &offset_field,
+ const AttributeOutputs &attribute_outputs)
+{
+ Mesh &mesh = *component.get_for_write();
+ const int orig_vert_size = mesh.totvert;
+ Span<MEdge> orig_edges = mesh_edges(mesh);
+ Span<MPoly> orig_polys = mesh_polys(mesh);
+ const int orig_loop_size = mesh.totloop;
+
+ GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
+ edge_evaluator.set_selection(selection_field);
+ edge_evaluator.add(offset_field);
+ edge_evaluator.evaluate();
+ const IndexMask edge_selection = edge_evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> &edge_offsets = edge_evaluator.get_evaluated<float3>(0);
+ if (edge_selection.is_empty()) {
+ return;
+ }
+
+ const Array<Vector<int, 2>> edge_to_poly_map = mesh_calculate_polys_of_edge(mesh);
+
+ /* Find the offsets on the vertex domain for translation. This must be done before the mesh's
+ * custom data layers are reallocated, in case the virtual array references on of them. */
+ Array<float3> vert_offsets;
+ if (!edge_offsets.is_single()) {
+ vert_offsets.reinitialize(orig_vert_size);
+ attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
+ for (const int i_edge : edge_selection) {
+ const MEdge &edge = orig_edges[i_edge];
+ const float3 offset = edge_offsets[i_edge];
+ mixer.mix_in(edge.v1, offset);
+ mixer.mix_in(edge.v2, offset);
+ }
+ mixer.finalize();
+ }
+
+ const VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, edge_selection.indices());
+
+ const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()};
+ /* The extruded edges connect the original and duplicate edges. */
+ const IndexRange connect_edge_range{orig_edges.size(), new_vert_range.size()};
+ /* The duplicate edges are extruded copies of the selected edges. */
+ const IndexRange duplicate_edge_range = connect_edge_range.after(edge_selection.size());
+ /* There is a new polygon for every selected edge. */
+ const IndexRange new_poly_range{orig_polys.size(), edge_selection.size()};
+ /* Every new polygon is a quad with four corners. */
+ const IndexRange new_loop_range{orig_loop_size, new_poly_range.size() * 4};
+
+ expand_mesh(mesh,
+ new_vert_range.size(),
+ connect_edge_range.size() + duplicate_edge_range.size(),
+ new_poly_range.size(),
+ new_loop_range.size());
+
+ MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
+ MutableSpan<MEdge> connect_edges = mesh_edges(mesh).slice(connect_edge_range);
+ MutableSpan<MEdge> duplicate_edges = mesh_edges(mesh).slice(duplicate_edge_range);
+ MutableSpan<MPoly> polys = mesh_polys(mesh);
+ MutableSpan<MPoly> new_polys = polys.slice(new_poly_range);
+ MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> new_loops = loops.slice(new_loop_range);
+
+ for (const int i : connect_edges.index_range()) {
+ connect_edges[i] = new_edge(new_vert_indices[i], new_vert_range[i]);
+ }
+
+ for (const int i : duplicate_edges.index_range()) {
+ const MEdge &orig_edge = mesh.medge[edge_selection[i]];
+ const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
+ const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
+ duplicate_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
+ }
+
+ for (const int i : new_polys.index_range()) {
+ new_polys[i] = new_poly(new_loop_range[i * 4], 4);
+ }
+
+ for (const int i : edge_selection.index_range()) {
+ const int orig_edge_index = edge_selection[i];
+
+ const MEdge &duplicate_edge = duplicate_edges[i];
+ const int new_vert_1 = duplicate_edge.v1;
+ const int new_vert_2 = duplicate_edge.v2;
+ const int extrude_index_1 = new_vert_1 - orig_vert_size;
+ const int extrude_index_2 = new_vert_2 - orig_vert_size;
+
+ Span<int> connected_polys = edge_to_poly_map[orig_edge_index];
+
+ /* When there was a single polygon connected to the new polygon, we can use the old one to keep
+ * the face direction consistent. When there is more than one connected edge, the new face
+ * direction is totally arbitrary and the only goal for the behavior is to be deterministic. */
+ Span<MLoop> connected_poly_loops = {};
+ if (connected_polys.size() == 1) {
+ const MPoly &connected_poly = polys[connected_polys.first()];
+ connected_poly_loops = loops.slice(connected_poly.loopstart, connected_poly.totloop);
+ }
+ fill_quad_consistent_direction(connected_poly_loops,
+ new_loops.slice(4 * i, 4),
+ new_vert_indices[extrude_index_1],
+ new_vert_indices[extrude_index_2],
+ new_vert_1,
+ new_vert_2,
+ orig_edge_index,
+ connect_edge_range[extrude_index_1],
+ duplicate_edge_range[i],
+ connect_edge_range[extrude_index_2]);
+ }
+
+ /* Create a map of indices in the extruded vertices array to all of the indices of edges
+ * in the duplicate edges array that connect to that vertex. This can be used to simplify the
+ * mixing of attribute data for the connecting edges. */
+ const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
+ new_vert_range.size(), duplicate_edges, orig_vert_size);
+
+ component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ id, meta_data.domain, meta_data.data_type);
+ if (!attribute) {
+ return true; /* Impossible to write the "normal" attribute. */
+ }
+
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> data = attribute.as_span().typed<T>();
+ switch (attribute.domain()) {
+ case ATTR_DOMAIN_POINT: {
+ /* New vertices copy the attribute values from their source vertex. */
+ copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Edges parallel to original edges copy the edge attributes from the original edges. */
+ MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
+ copy_with_mask(duplicate_data, data.as_span(), edge_selection);
+
+ /* Edges connected to original vertices mix values of selected connected edges. */
+ MutableSpan<T> connect_data = data.slice(connect_edge_range);
+ copy_with_mixing(connect_data, duplicate_data.as_span(), [&](const int i_new_vert) {
+ return new_vert_to_duplicate_edge_map[i_new_vert].as_span();
+ });
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ /* Attribute values for new faces are a mix of the values of faces connected to the its
+ * original edge. */
+ copy_with_mixing(data.slice(new_poly_range), data.as_span(), [&](const int i) {
+ return edge_to_poly_map[edge_selection[i]].as_span();
+ });
+
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ /* New corners get the average value of all adjacent corners on original faces connected
+ * to the original edge of their face. */
+ MutableSpan<T> new_data = data.slice(new_loop_range);
+ threading::parallel_for(edge_selection.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_edge_selection : range) {
+ const int orig_edge_index = edge_selection[i_edge_selection];
+
+ Span<int> connected_polys = edge_to_poly_map[orig_edge_index];
+ if (connected_polys.is_empty()) {
+ /* If there are no connected polygons, there is no corner data to
+ * interpolate. */
+ new_data.slice(4 * i_edge_selection, 4).fill(T());
+ continue;
+ }
+
+ /* Both corners on each vertical edge of the side polygon get the same value,
+ * so there are only two unique values to mix. */
+ Array<T> side_poly_corner_data(2);
+ attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data};
+
+ const MEdge &duplicate_edge = duplicate_edges[i_edge_selection];
+ const int new_vert_1 = duplicate_edge.v1;
+ const int new_vert_2 = duplicate_edge.v2;
+ const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
+ const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
+
+ /* Average the corner data from the corners that share a vertex from the
+ * polygons that share an edge with the extruded edge. */
+ for (const int i_connected_poly : connected_polys.index_range()) {
+ const MPoly &connected_poly = polys[connected_polys[i_connected_poly]];
+ for (const int i_loop :
+ IndexRange(connected_poly.loopstart, connected_poly.totloop)) {
+ const MLoop &loop = loops[i_loop];
+ if (loop.v == orig_vert_1) {
+ mixer.mix_in(0, data[i_loop]);
+ }
+ if (loop.v == orig_vert_2) {
+ mixer.mix_in(1, data[i_loop]);
+ }
+ }
+ }
+
+ mixer.finalize();
+
+ /* Instead of replicating the order in #fill_quad_consistent_direction here, it's
+ * simpler (though probably slower) to just match the corner data based on the vertex
+ * indices. */
+ for (const int i : IndexRange(4 * i_edge_selection, 4)) {
+ if (ELEM(new_loops[i].v, new_vert_1, orig_vert_1)) {
+ new_data[i] = side_poly_corner_data.first();
+ }
+ else if (ELEM(new_loops[i].v, new_vert_2, orig_vert_2)) {
+ new_data[i] = side_poly_corner_data.last();
+ }
+ }
+ }
+ });
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ });
+
+ attribute.save();
+ return true;
+ });
+
+ if (edge_offsets.is_single()) {
+ const float3 offset = edge_offsets.get_internal_single();
+ threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ add_v3_v3(new_verts[i].co, offset);
+ }
+ });
+ }
+ else {
+ threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ add_v3_v3(new_verts[i].co, vert_offsets[new_vert_indices[i]]);
+ }
+ });
+ }
+
+ if (attribute_outputs.top_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range);
+ }
+ if (attribute_outputs.side_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range);
+ }
+
+ BKE_mesh_runtime_clear_cache(&mesh);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+/**
+ * Edges connected to one selected face are on the boundary of a region and will be duplicated into
+ * a "side face". Edges inside a region will be duplicated to leave any original faces unchanged.
+ */
+static void extrude_mesh_face_regions(MeshComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &offset_field,
+ const AttributeOutputs &attribute_outputs)
+{
+ Mesh &mesh = *component.get_for_write();
+ const int orig_vert_size = mesh.totvert;
+ Span<MEdge> orig_edges = mesh_edges(mesh);
+ Span<MPoly> orig_polys = mesh_polys(mesh);
+ Span<MLoop> orig_loops = mesh_loops(mesh);
+
+ GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
+ poly_evaluator.set_selection(selection_field);
+ poly_evaluator.add(offset_field);
+ poly_evaluator.evaluate();
+ const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> &poly_offsets = poly_evaluator.get_evaluated<float3>(0);
+ if (poly_selection.is_empty()) {
+ return;
+ }
+
+ Array<bool> poly_selection_array(orig_polys.size(), false);
+ for (const int i_poly : poly_selection) {
+ poly_selection_array[i_poly] = true;
+ }
+
+ /* Mix the offsets from the face domain to the vertex domain. Evaluate on the face domain above
+ * in order to be consistent with the selection, and to use the face normals rather than vertex
+ * normals as an offset, for example. */
+ Array<float3> vert_offsets;
+ if (!poly_offsets.is_single()) {
+ vert_offsets.reinitialize(orig_vert_size);
+ attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
+ for (const int i_poly : poly_selection) {
+ const MPoly &poly = orig_polys[i_poly];
+ const float3 offset = poly_offsets[i_poly];
+ for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) {
+ mixer.mix_in(loop.v, offset);
+ }
+ }
+ mixer.finalize();
+ }
+
+ /* All of the faces (selected and deselected) connected to each edge. */
+ const Array<Vector<int, 2>> edge_to_poly_map = mesh_calculate_polys_of_edge(mesh);
+
+ /* All vertices that are connected to the selected polygons.
+ * Start the size at one vert per poly to reduce unnecessary reallocation. */
+ VectorSet<int> all_selected_verts;
+ all_selected_verts.reserve(orig_polys.size());
+ for (const int i_poly : poly_selection) {
+ const MPoly &poly = orig_polys[i_poly];
+ for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) {
+ all_selected_verts.add(loop.v);
+ }
+ }
+
+ /* Edges inside of an extruded region that are also attached to deselected edges. They must be
+ * duplicated in order to leave the old edge attached to the unchanged deselected faces. */
+ VectorSet<int> new_inner_edge_indices;
+ /* Edges inside of an extruded region. Their vertices should be translated
+ * with the offset, but the edges themselves should not be duplicated. */
+ Vector<int> inner_edge_indices;
+ /* The extruded face corresponding to each boundary edge (and each boundary face). */
+ Vector<int> edge_extruded_face_indices;
+ /* Edges on the outside of selected regions, either because there are no
+ * other connected faces, or because all of the other faces aren't selected. */
+ VectorSet<int> boundary_edge_indices;
+ for (const int i_edge : orig_edges.index_range()) {
+ Span<int> polys = edge_to_poly_map[i_edge];
+
+ int i_selected_poly = -1;
+ int deselected_poly_count = 0;
+ int selected_poly_count = 0;
+ for (const int i_other_poly : polys) {
+ if (poly_selection_array[i_other_poly]) {
+ selected_poly_count++;
+ i_selected_poly = i_other_poly;
+ }
+ else {
+ deselected_poly_count++;
+ }
+ }
+
+ if (selected_poly_count == 1) {
+ /* If there is only one selected polygon connected to the edge,
+ * the edge should be extruded to form a "side face". */
+ boundary_edge_indices.add_new(i_edge);
+ edge_extruded_face_indices.append(i_selected_poly);
+ }
+ else if (selected_poly_count > 1) {
+ /* The edge is inside an extruded region of faces. */
+ if (deselected_poly_count > 0) {
+ /* Add edges that are also connected to deselected edges to a separate list. */
+ new_inner_edge_indices.add_new(i_edge);
+ }
+ else {
+ /* Otherwise, just keep track of edges inside the region so that
+ * we can reattach them to duplicated vertices if necessary. */
+ inner_edge_indices.append(i_edge);
+ }
+ }
+ }
+
+ VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, boundary_edge_indices.as_span());
+ /* Before adding the rest of the new vertices from the new inner edges, store the number
+ * of new vertices from the boundary edges, since this is the number of connecting edges. */
+ const int extruded_vert_size = new_vert_indices.size();
+
+ /* The vertices attached to duplicate inner edges also have to be duplicated. */
+ for (const int i_edge : new_inner_edge_indices) {
+ const MEdge &edge = mesh.medge[i_edge];
+ new_vert_indices.add(edge.v1);
+ new_vert_indices.add(edge.v2);
+ }
+
+ /* New vertices forming the duplicated boundary edges and the ends of the new inner edges. */
+ const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()};
+ /* One edge connects each selected vertex to a new vertex on the extruded polygons. */
+ const IndexRange connect_edge_range{orig_edges.size(), extruded_vert_size};
+ /* Each selected edge is duplicated to form a single edge on the extrusion. */
+ const IndexRange boundary_edge_range = connect_edge_range.after(boundary_edge_indices.size());
+ /* Duplicated edges inside regions that were connected to deselected faces. */
+ const IndexRange new_inner_edge_range = boundary_edge_range.after(new_inner_edge_indices.size());
+ /* Each edge selected for extrusion is extruded into a single face. */
+ const IndexRange side_poly_range{orig_polys.size(), boundary_edge_indices.size()};
+ /* The loops that form the new side faces. */
+ const IndexRange side_loop_range{orig_loops.size(), side_poly_range.size() * 4};
+
+ expand_mesh(mesh,
+ new_vert_range.size(),
+ connect_edge_range.size() + boundary_edge_range.size() + new_inner_edge_range.size(),
+ side_poly_range.size(),
+ side_loop_range.size());
+
+ MutableSpan<MEdge> edges = mesh_edges(mesh);
+ MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
+ MutableSpan<MEdge> boundary_edges = edges.slice(boundary_edge_range);
+ MutableSpan<MEdge> new_inner_edges = edges.slice(new_inner_edge_range);
+ MutableSpan<MPoly> polys = mesh_polys(mesh);
+ MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
+ MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> new_loops = loops.slice(side_loop_range);
+
+ /* Initialize the edges that form the sides of the extrusion. */
+ for (const int i : connect_edges.index_range()) {
+ connect_edges[i] = new_edge(new_vert_indices[i], new_vert_range[i]);
+ }
+
+ /* Initialize the edges that form the top of the extrusion. */
+ for (const int i : boundary_edges.index_range()) {
+ const MEdge &orig_edge = edges[boundary_edge_indices[i]];
+ const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
+ const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
+ boundary_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
+ }
+
+ /* Initialize the new edges inside of extrude regions. */
+ for (const int i : new_inner_edge_indices.index_range()) {
+ const MEdge &orig_edge = edges[new_inner_edge_indices[i]];
+ const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
+ const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
+ new_inner_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
+ }
+
+ /* Initialize the new side polygons. */
+ for (const int i : new_polys.index_range()) {
+ new_polys[i] = new_poly(side_loop_range[i * 4], 4);
+ }
+
+ /* Connect original edges inside face regions to any new vertices, if necessary. */
+ for (const int i : inner_edge_indices) {
+ MEdge &edge = edges[i];
+ const int i_new_vert_1 = new_vert_indices.index_of_try(edge.v1);
+ const int i_new_vert_2 = new_vert_indices.index_of_try(edge.v2);
+ if (i_new_vert_1 != -1) {
+ edge.v1 = new_vert_range[i_new_vert_1];
+ }
+ if (i_new_vert_2 != -1) {
+ edge.v2 = new_vert_range[i_new_vert_2];
+ }
+ }
+
+ /* Connect the selected faces to the extruded or duplicated edges and the new vertices. */
+ for (const int i_poly : poly_selection) {
+ const MPoly &poly = polys[i_poly];
+ for (MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ const int i_new_vert = new_vert_indices.index_of_try(loop.v);
+ if (i_new_vert != -1) {
+ loop.v = new_vert_range[i_new_vert];
+ }
+ const int i_boundary_edge = boundary_edge_indices.index_of_try(loop.e);
+ if (i_boundary_edge != -1) {
+ loop.e = boundary_edge_range[i_boundary_edge];
+ /* Skip the next check, an edge cannot be both a boundary edge and an inner edge. */
+ continue;
+ }
+ const int i_new_inner_edge = new_inner_edge_indices.index_of_try(loop.e);
+ if (i_new_inner_edge != -1) {
+ loop.e = new_inner_edge_range[i_new_inner_edge];
+ }
+ }
+ }
+
+ /* Create the faces on the sides of extruded regions. */
+ for (const int i : boundary_edge_indices.index_range()) {
+ const MEdge &boundary_edge = boundary_edges[i];
+ const int new_vert_1 = boundary_edge.v1;
+ const int new_vert_2 = boundary_edge.v2;
+ const int extrude_index_1 = new_vert_1 - orig_vert_size;
+ const int extrude_index_2 = new_vert_2 - orig_vert_size;
+
+ const MPoly &extrude_poly = polys[edge_extruded_face_indices[i]];
+
+ fill_quad_consistent_direction(loops.slice(extrude_poly.loopstart, extrude_poly.totloop),
+ new_loops.slice(4 * i, 4),
+ new_vert_1,
+ new_vert_2,
+ new_vert_indices[extrude_index_1],
+ new_vert_indices[extrude_index_2],
+ boundary_edge_range[i],
+ connect_edge_range[extrude_index_1],
+ boundary_edge_indices[i],
+ connect_edge_range[extrude_index_2]);
+ }
+
+ /* Create a map of indices in the extruded vertices array to all of the indices of edges
+ * in the duplicate edges array that connect to that vertex. This can be used to simplify the
+ * mixing of attribute data for the connecting edges. */
+ const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
+ new_vert_range.size(), boundary_edges, orig_vert_size);
+
+ component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ id, meta_data.domain, meta_data.data_type);
+ if (!attribute) {
+ return true; /* Impossible to write the "normal" attribute. */
+ }
+
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> data = attribute.as_span().typed<T>();
+ switch (attribute.domain()) {
+ case ATTR_DOMAIN_POINT: {
+ /* New vertices copy the attributes from their original vertices. */
+ copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Edges parallel to original edges copy the edge attributes from the original edges. */
+ MutableSpan<T> boundary_data = data.slice(boundary_edge_range);
+ copy_with_indices(boundary_data, data.as_span(), boundary_edge_indices);
+
+ /* Edges inside of face regions also just duplicate their source data. */
+ MutableSpan<T> new_inner_data = data.slice(new_inner_edge_range);
+ copy_with_indices(new_inner_data, data.as_span(), new_inner_edge_indices);
+
+ /* Edges connected to original vertices mix values of selected connected edges. */
+ MutableSpan<T> connect_data = data.slice(connect_edge_range);
+ copy_with_mixing(connect_data, boundary_data.as_span(), [&](const int i) {
+ return new_vert_to_duplicate_edge_map[i].as_span();
+ });
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ /* New faces on the side of extrusions get the values from the corresponding selected
+ * face. */
+ copy_with_indices(
+ data.slice(side_poly_range), data.as_span(), edge_extruded_face_indices);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ /* New corners get the values from the corresponding corner on the extruded face. */
+ MutableSpan<T> new_data = data.slice(side_loop_range);
+ threading::parallel_for(
+ boundary_edge_indices.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_boundary_edge : range) {
+ const MPoly &poly = polys[edge_extruded_face_indices[i_boundary_edge]];
+
+ const MEdge &boundary_edge = boundary_edges[i_boundary_edge];
+ const int new_vert_1 = boundary_edge.v1;
+ const int new_vert_2 = boundary_edge.v2;
+ const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
+ const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
+
+ /* Retrieve the data for the first two sides of the quad from the extruded
+ * polygon, which we generally expect to have just a small amount of sides. This
+ * loop could be eliminated by adding a cache of connected loops (which would
+ * also simplify some of the other code to find the correct loops on the extruded
+ * face). */
+ T data_1;
+ T data_2;
+ for (const int i_loop : IndexRange(poly.loopstart, poly.totloop)) {
+ if (loops[i_loop].v == new_vert_1) {
+ data_1 = data[i_loop];
+ }
+ if (loops[i_loop].v == new_vert_2) {
+ data_2 = data[i_loop];
+ }
+ }
+
+ /* Instead of replicating the order in #fill_quad_consistent_direction here, it's
+ * simpler (though probably slower) to just match the corner data based on the
+ * vertex indices. */
+ for (const int i : IndexRange(4 * i_boundary_edge, 4)) {
+ if (ELEM(new_loops[i].v, new_vert_1, orig_vert_1)) {
+ new_data[i] = data_1;
+ }
+ else if (ELEM(new_loops[i].v, new_vert_2, orig_vert_2)) {
+ new_data[i] = data_2;
+ }
+ }
+ }
+ });
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ });
+
+ attribute.save();
+ return true;
+ });
+
+ /* Translate vertices based on the offset. If the vertex is used by a selected edge, it will
+ * have been duplicated and only the new vertex should use the offset. Otherwise the vertex might
+ * still need an offset, but it was reused on the inside of a region of extruded faces. */
+ if (poly_offsets.is_single()) {
+ const float3 offset = poly_offsets.get_internal_single();
+ threading::parallel_for(
+ IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) {
+ for (const int i_orig : all_selected_verts.as_span().slice(range)) {
+ const int i_new = new_vert_indices.index_of_try(i_orig);
+ MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
+ add_v3_v3(vert.co, offset);
+ }
+ });
+ }
+ else {
+ threading::parallel_for(
+ IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) {
+ for (const int i_orig : all_selected_verts.as_span().slice(range)) {
+ const int i_new = new_vert_indices.index_of_try(i_orig);
+ const float3 offset = vert_offsets[i_orig];
+ MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
+ add_v3_v3(vert.co, offset);
+ }
+ });
+ }
+
+ if (attribute_outputs.top_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ }
+ if (attribute_outputs.side_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
+ }
+
+ BKE_mesh_runtime_clear_cache(&mesh);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+/* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */
+static IndexRange selected_corner_range(Span<int> offsets, const int index)
+{
+ const int offset = offsets[index];
+ const int next_offset = offsets[index + 1];
+ return IndexRange(offset, next_offset - offset);
+}
+
+static void extrude_individual_mesh_faces(MeshComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &offset_field,
+ const AttributeOutputs &attribute_outputs)
+{
+ Mesh &mesh = *component.get_for_write();
+ const int orig_vert_size = mesh.totvert;
+ const int orig_edge_size = mesh.totedge;
+ Span<MPoly> orig_polys = mesh_polys(mesh);
+ Span<MLoop> orig_loops = mesh_loops(mesh);
+
+ /* Use a mesh for the result of the evaluation because the mesh is reallocated before
+ * the vertices are moved, and the evaluated result might reference an attribute. */
+ Array<float3> poly_offset(orig_polys.size());
+ GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
+ poly_evaluator.set_selection(selection_field);
+ poly_evaluator.add_with_destination(offset_field, poly_offset.as_mutable_span());
+ poly_evaluator.evaluate();
+ const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask();
+
+ /* Build an array of offsets into the new data for each polygon. This is used to facilitate
+ * parallelism later on by avoiding the need to keep track of an offset when iterating through
+ * all polygons. */
+ int extrude_corner_size = 0;
+ Array<int> index_offsets(poly_selection.size() + 1);
+ for (const int i_selection : poly_selection.index_range()) {
+ const MPoly &poly = orig_polys[poly_selection[i_selection]];
+ index_offsets[i_selection] = extrude_corner_size;
+ extrude_corner_size += poly.totloop;
+ }
+ index_offsets.last() = extrude_corner_size;
+
+ const IndexRange new_vert_range{orig_vert_size, extrude_corner_size};
+ /* One edge connects each selected vertex to a new vertex on the extruded polygons. */
+ const IndexRange connect_edge_range{orig_edge_size, extrude_corner_size};
+ /* Each selected edge is duplicated to form a single edge on the extrusion. */
+ const IndexRange duplicate_edge_range = connect_edge_range.after(extrude_corner_size);
+ /* Each edge selected for extrusion is extruded into a single face. */
+ const IndexRange side_poly_range{orig_polys.size(), duplicate_edge_range.size()};
+ const IndexRange side_loop_range{orig_loops.size(), side_poly_range.size() * 4};
+
+ expand_mesh(mesh,
+ new_vert_range.size(),
+ connect_edge_range.size() + duplicate_edge_range.size(),
+ side_poly_range.size(),
+ side_loop_range.size());
+
+ MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
+ MutableSpan<MEdge> edges{mesh.medge, mesh.totedge};
+ MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
+ MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range);
+ MutableSpan<MPoly> polys{mesh.mpoly, mesh.totpoly};
+ MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
+ MutableSpan<MLoop> loops{mesh.mloop, mesh.totloop};
+
+ /* For every selected polygon, build the faces that form the sides of the extrusion. Filling some
+ * of this data like the new edges or polygons could be easily split into separate loops, which
+ * may or may not be faster, and would involve more duplication. */
+ threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
+
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ for (const int i : IndexRange(poly.totloop)) {
+ const int i_next = (i == poly.totloop - 1) ? 0 : i + 1;
+ const MLoop &orig_loop = poly_loops[i];
+ const MLoop &orig_loop_next = poly_loops[i_next];
+
+ const int i_extrude = poly_corner_range[i];
+ const int i_extrude_next = poly_corner_range[i_next];
+
+ const int i_duplicate_edge = duplicate_edge_range[i_extrude];
+ const int new_vert = new_vert_range[i_extrude];
+ const int new_vert_next = new_vert_range[i_extrude_next];
+
+ const int orig_edge = orig_loop.e;
+
+ const int orig_vert = orig_loop.v;
+ const int orig_vert_next = orig_loop_next.v;
+
+ duplicate_edges[i_extrude] = new_edge(new_vert, new_vert_next);
+
+ new_polys[i_extrude] = new_poly(side_loop_range[i_extrude * 4], 4);
+
+ MutableSpan<MLoop> side_loops = loops.slice(side_loop_range[i_extrude * 4], 4);
+ side_loops[0].v = new_vert_next;
+ side_loops[0].e = i_duplicate_edge;
+ side_loops[1].v = new_vert;
+ side_loops[1].e = connect_edge_range[i_extrude];
+ side_loops[2].v = orig_vert;
+ side_loops[2].e = orig_edge;
+ side_loops[3].v = orig_vert_next;
+ side_loops[3].e = connect_edge_range[i_extrude_next];
+
+ connect_edges[i_extrude] = new_edge(orig_vert, new_vert);
+ }
+ }
+ });
+
+ component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ id, meta_data.domain, meta_data.data_type);
+ if (!attribute) {
+ return true; /* Impossible to write the "normal" attribute. */
+ }
+
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> data = attribute.as_span().typed<T>();
+ switch (attribute.domain()) {
+ case ATTR_DOMAIN_POINT: {
+ /* New vertices copy the attributes from their original vertices. */
+ MutableSpan<T> new_data = data.slice(new_vert_range);
+
+ threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ const int corner_offset = index_offsets[i_selection];
+ for (const int i : poly_loops.index_range()) {
+ const int orig_index = poly_loops[i].v;
+ new_data[corner_offset + i] = data[orig_index];
+ }
+ }
+ });
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
+ MutableSpan<T> connect_data = data.slice(connect_edge_range);
+
+ threading::parallel_for(poly_selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets,
+ i_selection);
+
+ /* The data for the duplicate edge is simply a copy of the original edge's data. */
+ for (const int i : poly_loops.index_range()) {
+ const int orig_index = poly_loops[i].e;
+ duplicate_data[poly_corner_range[i]] = data[orig_index];
+ }
+
+ /* For the extruded edges, mix the data from the two neighboring original edges of
+ * the extruded polygon. */
+ for (const int i : poly_loops.index_range()) {
+ const int i_loop_prev = (i == 0) ? poly.totloop - 1 : i - 1;
+ const int orig_index = poly_loops[i].e;
+ const int orig_index_prev = poly_loops[i_loop_prev].e;
+ if constexpr (std::is_same_v<T, bool>) {
+ /* Propagate selections with "or" instead of "at least half". */
+ connect_data[poly_corner_range[i]] = data[orig_index] || data[orig_index_prev];
+ }
+ else {
+ connect_data[poly_corner_range[i]] = attribute_math::mix2(
+ 0.5f, data[orig_index], data[orig_index_prev]);
+ }
+ }
+ }
+ });
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ /* Each side face gets the values from the corresponding new face. */
+ MutableSpan<T> new_data = data.slice(side_poly_range);
+ threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const int poly_index = poly_selection[i_selection];
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets,
+ i_selection);
+ new_data.slice(poly_corner_range).fill(data[poly_index]);
+ }
+ });
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ /* Each corner on a side face gets its value from the matching corner on an extruded
+ * face. */
+ MutableSpan<T> new_data = data.slice(side_loop_range);
+ threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ Span<T> poly_loop_data = data.slice(poly.loopstart, poly.totloop);
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets,
+ i_selection);
+
+ for (const int i : IndexRange(poly.totloop)) {
+ const int i_next = (i == poly.totloop - 1) ? 0 : i + 1;
+ const int i_extrude = poly_corner_range[i];
+
+ MutableSpan<T> side_loop_data = new_data.slice(i_extrude * 4, 4);
+
+ /* The two corners on each side of the side polygon get the data from the matching
+ * corners of the extruded polygon. This order depends on the loop filling the loop
+ * indices. */
+ side_loop_data[0] = poly_loop_data[i_next];
+ side_loop_data[1] = poly_loop_data[i];
+ side_loop_data[2] = poly_loop_data[i];
+ side_loop_data[3] = poly_loop_data[i_next];
+ }
+ }
+ });
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ });
+
+ attribute.save();
+ return true;
+ });
+
+ /* Offset the new vertices. */
+ threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
+ for (MVert &vert : new_verts.slice(poly_corner_range)) {
+ add_v3_v3(vert.co, poly_offset[poly_selection[i_selection]]);
+ }
+ }
+ });
+
+ /* Finally update each extruded polygon's loops to point to the new edges and vertices.
+ * This must be done last, because they were used to find original indices for attribute
+ * interpolation before. Alternatively an original index array could be built for each domain. */
+ threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
+
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ MutableSpan<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ for (const int i : IndexRange(poly.totloop)) {
+ MLoop &loop = poly_loops[i];
+ loop.v = new_vert_range[poly_corner_range[i]];
+ loop.e = duplicate_edge_range[poly_corner_range[i]];
+ }
+ }
+ });
+
+ if (attribute_outputs.top_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ }
+ if (attribute_outputs.side_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
+ }
+
+ BKE_mesh_runtime_clear_cache(&mesh);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ Field<float3> offset_field = params.extract_input<Field<float3>>("Offset");
+ Field<float> scale_field = params.extract_input<Field<float>>("Offset Scale");
+ const NodeGeometryExtrudeMesh &storage = node_storage(params.node());
+ GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode);
+
+ /* Create a combined field from the offset and the scale so the field evaluator
+ * can take care of the multiplication and to simplify each extrude function. */
+ static fn::CustomMF_SI_SI_SO<float3, float, float3> multiply_fn{
+ "Scale", [](const float3 &offset, const float scale) { return offset * scale; }};
+ std::shared_ptr<FieldOperation> multiply_op = std::make_shared<FieldOperation>(
+ FieldOperation(multiply_fn, {std::move(offset_field), std::move(scale_field)}));
+ const Field<float3> final_offset{std::move(multiply_op)};
+
+ AttributeOutputs attribute_outputs;
+ if (params.output_is_required("Top")) {
+ attribute_outputs.top_id = StrongAnonymousAttributeID("Top");
+ }
+ if (params.output_is_required("Side")) {
+ attribute_outputs.side_id = StrongAnonymousAttributeID("Side");
+ }
+
+ const bool extrude_individual = mode == GEO_NODE_EXTRUDE_MESH_FACES &&
+ params.extract_input<bool>("Individual");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_mesh()) {
+ MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
+ switch (mode) {
+ case GEO_NODE_EXTRUDE_MESH_VERTICES:
+ extrude_mesh_vertices(component, selection, final_offset, attribute_outputs);
+ break;
+ case GEO_NODE_EXTRUDE_MESH_EDGES:
+ extrude_mesh_edges(component, selection, final_offset, attribute_outputs);
+ break;
+ case GEO_NODE_EXTRUDE_MESH_FACES: {
+ if (extrude_individual) {
+ extrude_individual_mesh_faces(component, selection, final_offset, attribute_outputs);
+ }
+ else {
+ extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs);
+ }
+ break;
+ }
+ }
+
+ BLI_assert(BKE_mesh_is_valid(component.get_for_write()));
+ }
+ });
+
+ params.set_output("Mesh", std::move(geometry_set));
+ if (attribute_outputs.top_id) {
+ params.set_output("Top",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_outputs.top_id), params.attribute_producer_name()));
+ }
+ if (attribute_outputs.side_id) {
+ params.set_output("Side",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_outputs.side_id), params.attribute_producer_name()));
+ }
+}
+
+} // namespace blender::nodes::node_geo_extrude_mesh_cc
+
+void register_node_type_geo_extrude_mesh()
+{
+ namespace file_ns = blender::nodes::node_geo_extrude_mesh_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_EXTRUDE_MESH, "Extrude Mesh", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_storage(
+ &ntype, "NodeGeometryExtrudeMesh", node_free_standard_storage, node_copy_standard_storage);
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
new file mode 100644
index 00000000000..41970d75dfe
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
@@ -0,0 +1,114 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_flip_faces_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Mesh"));
+}
+
+static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ if (domain_size == 0) {
+ return;
+ }
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+
+ Mesh *mesh = component.get_for_write();
+
+ mesh->mloop = (MLoop *)CustomData_duplicate_referenced_layer(
+ &mesh->ldata, CD_MLOOP, mesh->totloop);
+ Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+
+ for (const int i : selection.index_range()) {
+ const MPoly &poly = polys[selection[i]];
+ int start = poly.loopstart;
+ for (const int j : IndexRange(poly.totloop / 2)) {
+ const int index1 = start + j + 1;
+ const int index2 = start + poly.totloop - j - 1;
+ std::swap(loops[index1].v, loops[index2].v);
+ std::swap(loops[index1 - 1].e, loops[index2].e);
+ }
+ }
+
+ component.attribute_foreach(
+ [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ if (meta_data.domain == ATTR_DOMAIN_CORNER) {
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type, nullptr);
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> dst_span = attribute.as_span<T>();
+ for (const int j : selection.index_range()) {
+ const MPoly &poly = polys[selection[j]];
+ dst_span.slice(poly.loopstart + 1, poly.totloop - 1).reverse();
+ }
+ });
+ attribute.save();
+ }
+ return true;
+ });
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_mesh()) {
+ return;
+ }
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_flip_faces(mesh_component, selection_field);
+ });
+
+ params.set_output("Mesh", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_flip_faces_cc
+
+void register_node_type_geo_flip_faces()
+{
+ namespace file_ns = blender::nodes::node_geo_flip_faces_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_FLIP_FACES, "Flip Faces", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
index c3c26736e88..b1144b58c37 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
@@ -14,20 +14,100 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_spline.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_input_curve_handles_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
+ b.add_input<decl::Bool>(N_("Relative"))
+ .default_value(false)
+ .supports_field()
+ .description(N_("Output the handle positions relative to the corresponding control point "
+ "instead of in the local space of the geometry"));
b.add_output<decl::Vector>(N_("Left")).field_source();
b.add_output<decl::Vector>(N_("Right")).field_source();
}
+class HandlePositionFieldInput final : public GeometryFieldInput {
+ Field<bool> relative_;
+ bool left_;
+
+ public:
+ HandlePositionFieldInput(Field<bool> relative, bool left)
+ : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left)
+ {
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_CURVE) {
+ return {};
+ }
+
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator(field_context, &mask);
+ evaluator.add(relative_);
+ evaluator.evaluate();
+ const VArray<bool> &relative = evaluator.get_evaluated<bool>(0);
+
+ VArray<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ StringRef side = left_ ? "handle_left" : "handle_right";
+ VArray<float3> handles = component.attribute_get_for_read<float3>(
+ side, ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ if (relative.is_single()) {
+ if (relative.get_internal_single()) {
+ Array<float3> output(positions.size());
+ for (const int i : positions.index_range()) {
+ output[i] = handles[i] - positions[i];
+ }
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
+ }
+ return component.attribute_try_adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain);
+ }
+
+ Array<float3> output(positions.size());
+ for (const int i : positions.index_range()) {
+ if (relative[i]) {
+ output[i] = handles[i] - positions[i];
+ }
+ else {
+ output[i] = handles[i];
+ }
+ }
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(relative_, left_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const HandlePositionFieldInput *other_handle =
+ dynamic_cast<const HandlePositionFieldInput *>(&other)) {
+ return relative_ == other_handle->relative_ && left_ == other_handle->left_;
+ }
+ return false;
+ }
+};
+
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float3> left_field = AttributeFieldInput::Create<float3>("handle_left");
- Field<float3> right_field = AttributeFieldInput::Create<float3>("handle_right");
+ Field<bool> relative = params.extract_input<Field<bool>>("Relative");
+ Field<float3> left_field{std::make_shared<HandlePositionFieldInput>(relative, true)};
+ Field<float3> right_field{std::make_shared<HandlePositionFieldInput>(relative, false)};
+
params.set_output("Left", std::move(left_field));
params.set_output("Right", std::move(right_field));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc
index 3fe0588a46d..afe7546f7e5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc
@@ -20,7 +20,9 @@ namespace blender::nodes::node_geo_input_id_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>(N_("ID")).field_source();
+ b.add_output<decl::Int>(N_("ID")).field_source().description(
+ N_("The values from the \"id\" attribute on points, or the index if that attribute does not "
+ "exist"));
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
index ddeb3ded511..43c867e0977 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
@@ -27,7 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Int>(N_("Face Count"))
.field_source()
- .description(N_("Number of faces that contain the edge"));
+ .description(N_("The number of faces that use each edge as one of their sides"));
}
class EdgeNeighborCountFieldInput final : public GeometryFieldInput {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
index 629279a44e9..f1777c9ebf5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
@@ -27,10 +27,13 @@ namespace blender::nodes::node_geo_input_mesh_island_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>(N_("Index"))
+ b.add_output<decl::Int>(N_("Island Index"))
.field_source()
- .description(N_("Island indices are based on the order of the lowest-numbered vertex "
- "contained in each island"));
+ .description(N_("The index of the each vertex's island. Indices are based on the "
+ "lowest vertex index contained in each island"));
+ b.add_output<decl::Int>(N_("Island Count"))
+ .field_source()
+ .description(N_("The total number of mesh islands"));
}
class IslandFieldInput final : public GeometryFieldInput {
@@ -81,10 +84,63 @@ class IslandFieldInput final : public GeometryFieldInput {
}
};
+class IslandCountFieldInput final : public GeometryFieldInput {
+ public:
+ IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ return {};
+ }
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ DisjointSet islands(mesh->totvert);
+ for (const int i : IndexRange(mesh->totedge)) {
+ islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ }
+
+ Set<int> island_list;
+ for (const int i_vert : IndexRange(mesh->totvert)) {
+ const int64_t root = islands.find_root(i_vert);
+ island_list.add(root);
+ }
+
+ return VArray<int>::ForSingle(island_list.size(),
+ mesh_component.attribute_domain_size(domain));
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random hash. */
+ return 45634572457;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const IslandCountFieldInput *>(&other) != nullptr;
+ }
+};
+
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<int> island_field{std::make_shared<IslandFieldInput>()};
- params.set_output("Index", std::move(island_field));
+ if (params.output_is_required("Island Index")) {
+ Field<int> field{std::make_shared<IslandFieldInput>()};
+ params.set_output("Island Index", std::move(field));
+ }
+ if (params.output_is_required("Island Count")) {
+ Field<int> field{std::make_shared<IslandCountFieldInput>()};
+ params.set_output("Island Count", std::move(field));
+ }
}
} // namespace blender::nodes::node_geo_input_mesh_island_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
index 7d79164634d..c2da065cbfc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
@@ -27,7 +27,8 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Int>(N_("Vertex Count"))
.field_source()
- .description(N_("Vertex count and edge count are equal"));
+ .description(N_("The number of vertices connected to this vertex with an edge, "
+ "equal to the number of connected edges"));
b.add_output<decl::Int>(N_("Face Count"))
.field_source()
.description(N_("Number of faces that contain the vertex"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
index 71256a7f781..06f24113308 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -36,7 +36,8 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("Geometry that is instanced on the points"));
b.add_input<decl::Bool>(N_("Pick Instance"))
.supports_field()
- .description(N_("Place different instances on different points"));
+ .description(N_("Choose instances from the \"Instance\" input at each point instead of "
+ "instancing the entire geometry"));
b.add_input<decl::Int>(N_("Instance Index"))
.implicit_field()
.description(N_(
diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
new file mode 100644
index 00000000000..89227c773cc
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
@@ -0,0 +1,110 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "GEO_mesh_merge_by_distance.hh"
+#include "GEO_point_merge_by_distance.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_merge_by_distance_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"))
+ .supported_type({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_MESH});
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Distance")).default_value(0.001f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points,
+ const float merge_distance,
+ const Field<bool> &selection_field)
+{
+ const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
+ GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, src_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+ if (selection.is_empty()) {
+ return nullptr;
+ }
+
+ return geometry::point_merge_by_distance(src_points, merge_distance, selection);
+}
+
+static std::optional<Mesh *> mesh_merge_by_distance(const MeshComponent &mesh_component,
+ const float merge_distance,
+ const Field<bool> &selection_field)
+{
+ const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, src_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+ if (selection.is_empty()) {
+ return std::nullopt;
+ }
+
+ const Mesh &mesh = *mesh_component.get_for_read();
+ return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ const float merge_distance = params.extract_input<float>("Distance");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_pointcloud()) {
+ PointCloud *result = pointcloud_merge_by_distance(
+ *geometry_set.get_component_for_read<PointCloudComponent>(), merge_distance, selection);
+ if (result) {
+ geometry_set.replace_pointcloud(result);
+ }
+ }
+ if (geometry_set.has_mesh()) {
+ std::optional<Mesh *> result = mesh_merge_by_distance(
+ *geometry_set.get_component_for_read<MeshComponent>(), merge_distance, selection);
+ if (result) {
+ geometry_set.replace_mesh(*result);
+ }
+ }
+ });
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_merge_by_distance_cc
+
+void register_node_type_geo_merge_by_distance()
+{
+ namespace file_ns = blender::nodes::node_geo_merge_by_distance_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MERGE_BY_DISTANCE, "Merge by Distance", NODE_CLASS_GEOMETRY);
+
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
new file mode 100644
index 00000000000..aaa2c156442
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
@@ -0,0 +1,485 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_disjoint_set.hh"
+#include "BLI_task.hh"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_scale_elements_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Scale"), "Scale").default_value(1.0f).min(0.0f).supports_field();
+ b.add_input<decl::Vector>(N_("Center"))
+ .subtype(PROP_TRANSLATION)
+ .implicit_field()
+ .description(N_("Origin of the scaling for each element. If multiple elements are "
+ "connected, their center is averaged"));
+ b.add_input<decl::Vector>(N_("Axis"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .supports_field()
+ .description(N_("Direction in which to scale the element"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
+};
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "scale_mode", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = ATTR_DOMAIN_FACE;
+ node->custom2 = GEO_NODE_SCALE_ELEMENTS_UNIFORM;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ bNodeSocket *geometry_socket = static_cast<bNodeSocket *>(node->inputs.first);
+ bNodeSocket *selection_socket = geometry_socket->next;
+ bNodeSocket *scale_float_socket = selection_socket->next;
+ bNodeSocket *center_socket = scale_float_socket->next;
+ bNodeSocket *axis_socket = center_socket->next;
+
+ const GeometryNodeScaleElementsMode mode = static_cast<GeometryNodeScaleElementsMode>(
+ node->custom2);
+ const bool use_single_axis = mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS;
+
+ nodeSetSocketAvailability(ntree, axis_socket, use_single_axis);
+}
+
+struct UniformScaleFields {
+ Field<bool> selection;
+ Field<float> scale;
+ Field<float3> center;
+};
+
+struct UniformScaleParams {
+ IndexMask selection;
+ VArray<float> scales;
+ VArray<float3> centers;
+};
+
+struct AxisScaleFields {
+ Field<bool> selection;
+ Field<float> scale;
+ Field<float3> center;
+ Field<float3> axis;
+};
+
+struct AxisScaleParams {
+ IndexMask selection;
+ VArray<float> scales;
+ VArray<float3> centers;
+ VArray<float3> axis_vectors;
+};
+
+/**
+ * When multiple elements share the same vertices, they are scaled together.
+ */
+struct ElementIsland {
+ /* Either face or edge indices. */
+ Vector<int> element_indices;
+};
+
+static float3 transform_with_uniform_scale(const float3 &position,
+ const float3 &center,
+ const float scale)
+{
+ const float3 diff = position - center;
+ const float3 scaled_diff = scale * diff;
+ const float3 new_position = center + scaled_diff;
+ return new_position;
+}
+
+static float4x4 create_single_axis_transform(const float3 &center,
+ const float3 &axis,
+ const float scale)
+{
+ /* Scale along x axis. The other axis need to be orthogonal, but their specific value does not
+ * matter. */
+ const float3 x_axis = math::normalize(axis);
+ float3 y_axis = math::cross(x_axis, float3(0.0f, 0.0f, 1.0f));
+ if (math::is_zero(y_axis)) {
+ y_axis = math::cross(x_axis, float3(0.0f, 1.0f, 0.0f));
+ }
+ y_axis = math::normalize(y_axis);
+ const float3 z_axis = math::cross(x_axis, y_axis);
+
+ float4x4 transform = float4x4::identity();
+
+ /* Move scaling center to the origin. */
+ sub_v3_v3(transform.values[3], center);
+
+ /* `base_change` and `base_change_inv` are used to rotate space so that scaling along the
+ * provided axis is the same as scaling along the x axis. */
+ float4x4 base_change = float4x4::identity();
+ copy_v3_v3(base_change.values[0], x_axis);
+ copy_v3_v3(base_change.values[1], y_axis);
+ copy_v3_v3(base_change.values[2], z_axis);
+
+ /* Can invert by transposing, because the matrix is orthonormal. */
+ float4x4 base_change_inv = base_change.transposed();
+
+ float4x4 scale_transform = float4x4::identity();
+ scale_transform.values[0][0] = scale;
+
+ transform = base_change * scale_transform * base_change_inv * transform;
+
+ /* Move scaling center back to where it was. */
+ add_v3_v3(transform.values[3], center);
+
+ return transform;
+}
+
+using GetVertexIndicesFn =
+ FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>;
+
+static void scale_vertex_islands_uniformly(Mesh &mesh,
+ const Span<ElementIsland> islands,
+ const UniformScaleParams &params,
+ const GetVertexIndicesFn get_vertex_indices)
+{
+ threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
+ for (const int island_index : range) {
+ const ElementIsland &island = islands[island_index];
+
+ float scale = 0.0f;
+ float3 center = {0.0f, 0.0f, 0.0f};
+
+ VectorSet<int> vertex_indices;
+ for (const int poly_index : island.element_indices) {
+ get_vertex_indices(mesh, poly_index, vertex_indices);
+ center += params.centers[poly_index];
+ scale += params.scales[poly_index];
+ }
+
+ /* Divide by number of elements to get the average. */
+ const float f = 1.0f / island.element_indices.size();
+ scale *= f;
+ center *= f;
+
+ for (const int vert_index : vertex_indices) {
+ MVert &vert = mesh.mvert[vert_index];
+ const float3 old_position = vert.co;
+ const float3 new_position = transform_with_uniform_scale(old_position, center, scale);
+ copy_v3_v3(vert.co, new_position);
+ }
+ }
+ });
+
+ /* Positions have changed, so the normals will have to be recomputed. */
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static void scale_vertex_islands_on_axis(Mesh &mesh,
+ const Span<ElementIsland> islands,
+ const AxisScaleParams &params,
+ const GetVertexIndicesFn get_vertex_indices)
+{
+ threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
+ for (const int island_index : range) {
+ const ElementIsland &island = islands[island_index];
+
+ float scale = 0.0f;
+ float3 center = {0.0f, 0.0f, 0.0f};
+ float3 axis = {0.0f, 0.0f, 0.0f};
+
+ VectorSet<int> vertex_indices;
+ for (const int poly_index : island.element_indices) {
+ get_vertex_indices(mesh, poly_index, vertex_indices);
+ center += params.centers[poly_index];
+ scale += params.scales[poly_index];
+ axis += params.axis_vectors[poly_index];
+ }
+
+ /* Divide by number of elements to get the average. */
+ const float f = 1.0f / island.element_indices.size();
+ scale *= f;
+ center *= f;
+ axis *= f;
+
+ if (math::is_zero(axis)) {
+ axis = float3(1.0f, 0.0f, 0.0f);
+ }
+
+ const float4x4 transform = create_single_axis_transform(center, axis, scale);
+ for (const int vert_index : vertex_indices) {
+ MVert &vert = mesh.mvert[vert_index];
+ const float3 old_position = vert.co;
+ const float3 new_position = transform * old_position;
+ copy_v3_v3(vert.co, new_position);
+ }
+ }
+ });
+
+ /* Positions have changed, so the normals will have to be recomputed. */
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection)
+{
+ /* Use the disjoint set data structure to determine which vertices have to be scaled together. */
+ DisjointSet disjoint_set(mesh.totvert);
+ for (const int poly_index : face_selection) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ for (const int loop_index : IndexRange(poly.totloop - 1)) {
+ const int v1 = poly_loops[loop_index].v;
+ const int v2 = poly_loops[loop_index + 1].v;
+ disjoint_set.join(v1, v2);
+ }
+ disjoint_set.join(poly_loops.first().v, poly_loops.last().v);
+ }
+
+ VectorSet<int> island_ids;
+ Vector<ElementIsland> islands;
+ /* There are at most as many islands as there are selected faces. */
+ islands.reserve(face_selection.size());
+
+ /* Gather all of the face indices in each island into separate vectors. */
+ for (const int poly_index : face_selection) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const int island_id = disjoint_set.find_root(poly_loops[0].v);
+ const int island_index = island_ids.index_of_or_add(island_id);
+ if (island_index == islands.size()) {
+ islands.append_as();
+ }
+ ElementIsland &island = islands[island_index];
+ island.element_indices.append(poly_index);
+ }
+
+ return islands;
+}
+
+static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices)
+{
+ const MPoly &poly = mesh.mpoly[face_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ for (const MLoop &loop : poly_loops) {
+ r_vertex_indices.add(loop.v);
+ }
+}
+
+static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator,
+ const AxisScaleFields &fields)
+{
+ AxisScaleParams out;
+ evaluator.set_selection(fields.selection);
+ evaluator.add(fields.scale, &out.scales);
+ evaluator.add(fields.center, &out.centers);
+ evaluator.add(fields.axis, &out.axis_vectors);
+ evaluator.evaluate();
+ out.selection = evaluator.get_evaluated_selection_as_mask();
+ return out;
+}
+
+static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{field_context, mesh.totpoly};
+ AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
+ scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices);
+}
+
+static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator,
+ const UniformScaleFields &fields)
+{
+ UniformScaleParams out;
+ evaluator.set_selection(fields.selection);
+ evaluator.add(fields.scale, &out.scales);
+ evaluator.add(fields.center, &out.centers);
+ evaluator.evaluate();
+ out.selection = evaluator.get_evaluated_selection_as_mask();
+ return out;
+}
+
+static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{field_context, mesh.totpoly};
+ UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
+ scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices);
+}
+
+static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection)
+{
+ /* Use the disjoint set data structure to determine which vertices have to be scaled together. */
+ DisjointSet disjoint_set(mesh.totvert);
+ for (const int edge_index : edge_selection) {
+ const MEdge &edge = mesh.medge[edge_index];
+ disjoint_set.join(edge.v1, edge.v2);
+ }
+
+ VectorSet<int> island_ids;
+ Vector<ElementIsland> islands;
+ /* There are at most as many islands as there are selected edges. */
+ islands.reserve(edge_selection.size());
+
+ /* Gather all of the edge indices in each island into separate vectors. */
+ for (const int edge_index : edge_selection) {
+ const MEdge &edge = mesh.medge[edge_index];
+ const int island_id = disjoint_set.find_root(edge.v1);
+ const int island_index = island_ids.index_of_or_add(island_id);
+ if (island_index == islands.size()) {
+ islands.append_as();
+ }
+ ElementIsland &island = islands[island_index];
+ island.element_indices.append(edge_index);
+ }
+
+ return islands;
+}
+
+static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices)
+{
+ const MEdge &edge = mesh.medge[edge_index];
+ r_vertex_indices.add(edge.v1);
+ r_vertex_indices.add(edge.v2);
+}
+
+static void scale_edges_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator evaluator{field_context, mesh.totedge};
+ UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
+ scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices);
+}
+
+static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator evaluator{field_context, mesh.totedge};
+ AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
+ scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const bNode &node = params.node();
+ const AttributeDomain domain = static_cast<AttributeDomain>(node.custom1);
+ const GeometryNodeScaleElementsMode scale_mode = static_cast<GeometryNodeScaleElementsMode>(
+ node.custom2);
+
+ GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
+
+ Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
+ Field<float> scale_field = params.get_input<Field<float>>("Scale");
+ Field<float3> center_field = params.get_input<Field<float3>>("Center");
+ Field<float3> axis_field;
+ if (scale_mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS) {
+ axis_field = params.get_input<Field<float3>>("Axis");
+ }
+
+ geometry.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (!geometry.has_mesh()) {
+ return;
+ }
+ MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
+ switch (domain) {
+ case ATTR_DOMAIN_FACE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_faces_uniformly(mesh_component, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_faces_on_axis(mesh_component,
+ {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_edges_uniformly(mesh_component, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_edges_on_axis(mesh_component,
+ {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ });
+
+ params.set_output("Geometry", std::move(geometry));
+}
+
+} // namespace blender::nodes::node_geo_scale_elements_cc
+
+void register_node_type_geo_scale_elements()
+{
+ namespace file_ns = blender::nodes::node_geo_scale_elements_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SCALE_ELEMENTS, "Scale Elements", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.initfunc = file_ns::node_init;
+ ntype.updatefunc = file_ns::node_update;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
index 10c0d61ccb6..c4bd6c6157f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -65,6 +65,8 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::String>(N_("Remainder")).make_available([](bNode &node) {
node_storage(node).overflow = GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE;
});
+ b.add_output<decl::Int>(N_("Line")).field_source();
+ b.add_output<decl::Vector>(N_("Pivot Point")).field_source();
}
static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
@@ -84,6 +86,7 @@ static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
uiItemR(layout, ptr, "overflow", 0, "", ICON_NONE);
uiItemR(layout, ptr, "align_x", 0, "", ICON_NONE);
uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "pivot_mode", 0, IFACE_("Pivot Point"), ICON_NONE);
}
static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -93,6 +96,7 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW;
data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT;
data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE;
+ data->pivot_mode = GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT;
node->storage = data;
node->id = (ID *)BKE_vfont_builtin_get();
}
@@ -115,10 +119,51 @@ static void node_update(bNodeTree *ntree, bNode *node)
N_("Text Box Width"));
}
+static float3 get_pivot_point(GeoNodeExecParams &params, CurveEval &curve)
+{
+ const NodeGeometryStringToCurves &storage = node_storage(params.node());
+ const GeometryNodeStringToCurvesPivotMode pivot_mode = (GeometryNodeStringToCurvesPivotMode)
+ storage.pivot_mode;
+
+ float3 min(FLT_MAX), max(FLT_MIN);
+
+ /* Check if curve is empty. */
+ if (!curve.bounds_min_max(min, max, false)) {
+ return {0.0f, 0.0f, 0.0f};
+ }
+
+ switch (pivot_mode) {
+ case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_MIDPOINT:
+ return (min + max) / 2;
+ case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT:
+ return float3(min.x, min.y, 0.0f);
+ case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_CENTER:
+ return float3((min.x + max.x) / 2, min.y, 0.0f);
+ case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_RIGHT:
+ return float3(max.x, min.y, 0.0f);
+ case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_LEFT:
+ return float3(min.x, max.y, 0.0f);
+ case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_CENTER:
+ return float3((min.x + max.x) / 2, max.y, 0.0f);
+ case GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_TOP_RIGHT:
+ return float3(max.x, max.y, 0.0f);
+ }
+ return {0.0f, 0.0f, 0.0f};
+}
+
struct TextLayout {
/* Position of each character. */
Vector<float2> positions;
+ /* Line number of each character. */
+ Array<int> line_numbers;
+
+ /* Map of Pivot point for each character code. */
+ Map<int, float3> pivot_points;
+
+ /* UTF-32 Character codes. */
+ Vector<char32_t> char_codes;
+
/* The text that fit into the text box, with newline character sequences replaced. */
std::string text;
@@ -212,6 +257,20 @@ static TextLayout get_text_layout(GeoNodeExecParams &params)
}
}
+ if (params.output_is_required("Line")) {
+ layout.line_numbers.reinitialize(layout.positions.size());
+ for (const int i : layout.positions.index_range()) {
+ CharTrans &ct = chartransdata[i];
+ layout.line_numbers[i] = ct.linenr;
+ }
+ }
+
+ /* Convert UTF-8 encoded string to UTF-32. */
+ len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
+ layout.char_codes.resize(len_chars + 1);
+ BLI_str_utf8_as_utf32(layout.char_codes.data(), layout.text.c_str(), layout.char_codes.size());
+ layout.char_codes.remove_last();
+
MEM_SAFE_FREE(chartransdata);
MEM_SAFE_FREE(cu.str);
MEM_SAFE_FREE(cu.strinfo);
@@ -222,15 +281,15 @@ static TextLayout get_text_layout(GeoNodeExecParams &params)
/* Returns a mapping of UTF-32 character code to instance handle. */
static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
- const float fontsize,
- const Span<char32_t> charcodes,
+ TextLayout &layout,
InstancesComponent &instance_component)
{
VFont *vfont = (VFont *)params.node().id;
Map<int, int> handles;
+ bool pivot_required = params.output_is_required("Pivot Point");
- for (int i : charcodes.index_range()) {
- if (handles.contains(charcodes[i])) {
+ for (int i : layout.char_codes.index_range()) {
+ if (handles.contains(layout.char_codes[i])) {
continue;
}
Curve cu = {{nullptr}};
@@ -240,36 +299,75 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
CharInfo charinfo = {0};
charinfo.mat_nr = 1;
- BKE_vfont_build_char(&cu, &cu.nurb, charcodes[i], &charinfo, 0, 0, 0, i, 1);
+ BKE_vfont_build_char(&cu, &cu.nurb, layout.char_codes[i], &charinfo, 0, 0, 0, i, 1);
std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(cu);
BKE_nurbList_free(&cu.nurb);
+
float4x4 size_matrix = float4x4::identity();
- size_matrix.apply_scale(fontsize);
+ size_matrix.apply_scale(layout.final_font_size);
curve_eval->transform(size_matrix);
+ if (pivot_required) {
+ float3 pivot_point = get_pivot_point(params, *curve_eval);
+ layout.pivot_points.add_new(layout.char_codes[i], pivot_point);
+ }
+
GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release());
- handles.add_new(charcodes[i], instance_component.add_reference(std::move(geometry_set_curve)));
+ handles.add_new(layout.char_codes[i],
+ instance_component.add_reference(std::move(geometry_set_curve)));
}
return handles;
}
static void add_instances_from_handles(InstancesComponent &instances,
const Map<int, int> &char_handles,
- const Span<char32_t> charcodes,
- const Span<float2> positions)
+ const TextLayout &layout)
{
- instances.resize(positions.size());
+ instances.resize(layout.positions.size());
MutableSpan<int> handles = instances.instance_reference_handles();
MutableSpan<float4x4> transforms = instances.instance_transforms();
- threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(layout.positions.size()), 256, [&](IndexRange range) {
for (const int i : range) {
- handles[i] = char_handles.lookup(charcodes[i]);
- transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0});
+ handles[i] = char_handles.lookup(layout.char_codes[i]);
+ transforms[i] = float4x4::from_location({layout.positions[i].x, layout.positions[i].y, 0});
}
});
}
+static void create_attributes(GeoNodeExecParams &params,
+ const TextLayout &layout,
+ InstancesComponent &instances)
+{
+ if (params.output_is_required("Line")) {
+ StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line");
+ OutputAttribute_Typed<int> line_attribute = instances.attribute_try_get_for_output_only<int>(
+ line_id.get(), ATTR_DOMAIN_INSTANCE);
+ MutableSpan<int> lines = line_attribute.as_span();
+ lines.copy_from(layout.line_numbers);
+ line_attribute.save();
+ params.set_output("Line",
+ AnonymousAttributeFieldInput::Create<int>(std::move(line_id),
+ params.attribute_producer_name()));
+ }
+
+ if (params.output_is_required("Pivot Point")) {
+ StrongAnonymousAttributeID pivot_id = StrongAnonymousAttributeID("Pivot");
+ OutputAttribute_Typed<float3> pivot_attribute =
+ instances.attribute_try_get_for_output_only<float3>(pivot_id.get(), ATTR_DOMAIN_INSTANCE);
+ MutableSpan<float3> pivots = pivot_attribute.as_span();
+
+ for (const int i : layout.char_codes.index_range()) {
+ pivots[i] = layout.pivot_points.lookup(layout.char_codes[i]);
+ }
+
+ pivot_attribute.save();
+ params.set_output("Pivot Point",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(pivot_id), params.attribute_producer_name()));
+ }
+}
+
static void node_geo_exec(GeoNodeExecParams params)
{
TextLayout layout = get_text_layout(params);
@@ -282,22 +380,16 @@ static void node_geo_exec(GeoNodeExecParams params)
if (layout.positions.size() == 0) {
params.set_output("Curve Instances", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
- /* Convert UTF-8 encoded string to UTF-32. */
- size_t len_bytes;
- size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
- Array<char32_t> char_codes_with_null(len_chars + 1);
- BLI_str_utf8_as_utf32(char_codes_with_null.data(), layout.text.c_str(), len_chars + 1);
- const Span<char32_t> char_codes = char_codes_with_null.as_span().drop_back(1);
-
/* Create and add instances. */
GeometrySet geometry_set_out;
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
- Map<int, int> char_handles = create_curve_instances(
- params, layout.final_font_size, char_codes, instances);
- add_instances_from_handles(instances, char_handles, char_codes, layout.positions);
+ Map<int, int> char_handles = create_curve_instances(params, layout, instances);
+ add_instances_from_handles(instances, char_handles, layout);
+ create_attributes(params, layout, instances);
params.set_output("Curve Instances", std::move(geometry_set_out));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
index 5a8d9ab470d..e6b14bc69e5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
@@ -386,11 +386,11 @@ static bool component_is_available(const GeometrySet &geometry,
/**
* \note Multi-threading for this function is provided by the field evaluator. Since the #call
- * function could be called many times, calculate the data from the target geometry once and store
+ * function could be called many times, calculate the data from the source geometry once and store
* it for later.
*/
class NearestInterpolatedTransferFunction : public fn::MultiFunction {
- GeometrySet target_;
+ GeometrySet source_;
GField src_field_;
/**
@@ -403,18 +403,18 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
fn::MFSignature signature_;
- std::optional<GeometryComponentFieldContext> target_context_;
- std::unique_ptr<FieldEvaluator> target_evaluator_;
- const GVArray *target_data_;
+ std::optional<GeometryComponentFieldContext> source_context_;
+ std::unique_ptr<FieldEvaluator> source_evaluator_;
+ const GVArray *source_data_;
public:
NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field)
- : target_(std::move(geometry)), src_field_(std::move(src_field))
+ : source_(std::move(geometry)), src_field_(std::move(src_field))
{
- target_.ensure_owns_direct_data();
+ source_.ensure_owns_direct_data();
signature_ = this->create_signature();
this->set_signature(&signature_);
- this->evaluate_target_field();
+ this->evaluate_source_field();
}
fn::MFSignature create_signature()
@@ -430,7 +430,7 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position");
GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute");
- const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
+ const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>();
BLI_assert(mesh_component.has_mesh());
const Mesh &mesh = *mesh_component.get_for_read();
BLI_assert(mesh.totpoly > 0);
@@ -441,29 +441,29 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions);
MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices);
- interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, dst);
+ interp.sample_data(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst);
}
private:
- void evaluate_target_field()
+ void evaluate_source_field()
{
- const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
- target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
+ const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>();
+ source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
const int domain_size = mesh_component.attribute_domain_size(domain_);
- target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size);
- target_evaluator_->add(src_field_);
- target_evaluator_->evaluate();
- target_data_ = &target_evaluator_->get_evaluated(0);
+ source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size);
+ source_evaluator_->add(src_field_);
+ source_evaluator_->evaluate();
+ source_data_ = &source_evaluator_->get_evaluated(0);
}
};
/**
* \note Multi-threading for this function is provided by the field evaluator. Since the #call
- * function could be called many times, calculate the data from the target geometry once and store
+ * function could be called many times, calculate the data from the source geometry once and store
* it for later.
*/
class NearestTransferFunction : public fn::MultiFunction {
- GeometrySet target_;
+ GeometrySet source_;
GField src_field_;
AttributeDomain domain_;
@@ -472,7 +472,7 @@ class NearestTransferFunction : public fn::MultiFunction {
bool use_mesh_;
bool use_points_;
- /* Store data from the target as a virtual array, since we may only access a few indices. */
+ /* Store data from the source as a virtual array, since we may only access a few indices. */
std::optional<GeometryComponentFieldContext> mesh_context_;
std::unique_ptr<FieldEvaluator> mesh_evaluator_;
const GVArray *mesh_data_;
@@ -483,16 +483,16 @@ class NearestTransferFunction : public fn::MultiFunction {
public:
NearestTransferFunction(GeometrySet geometry, GField src_field, AttributeDomain domain)
- : target_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain)
+ : source_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain)
{
- target_.ensure_owns_direct_data();
+ source_.ensure_owns_direct_data();
signature_ = this->create_signature();
this->set_signature(&signature_);
- this->use_mesh_ = component_is_available(target_, GEO_COMPONENT_TYPE_MESH, domain_);
- this->use_points_ = component_is_available(target_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_);
+ this->use_mesh_ = component_is_available(source_, GEO_COMPONENT_TYPE_MESH, domain_);
+ this->use_points_ = component_is_available(source_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_);
- this->evaluate_target_field();
+ this->evaluate_source_field();
}
fn::MFSignature create_signature()
@@ -513,8 +513,8 @@ class NearestTransferFunction : public fn::MultiFunction {
return;
}
- const Mesh *mesh = use_mesh_ ? target_.get_mesh_for_read() : nullptr;
- const PointCloud *pointcloud = use_points_ ? target_.get_pointcloud_for_read() : nullptr;
+ const Mesh *mesh = use_mesh_ ? source_.get_mesh_for_read() : nullptr;
+ const PointCloud *pointcloud = use_points_ ? source_.get_pointcloud_for_read() : nullptr;
const int tot_samples = mask.min_array_size();
@@ -590,10 +590,10 @@ class NearestTransferFunction : public fn::MultiFunction {
}
private:
- void evaluate_target_field()
+ void evaluate_source_field()
{
if (use_mesh_) {
- const MeshComponent &mesh = *target_.get_component_for_read<MeshComponent>();
+ const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>();
const int domain_size = mesh.attribute_domain_size(domain_);
mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_));
mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size);
@@ -603,7 +603,7 @@ class NearestTransferFunction : public fn::MultiFunction {
}
if (use_points_) {
- const PointCloudComponent &points = *target_.get_component_for_read<PointCloudComponent>();
+ const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>();
const int domain_size = points.attribute_domain_size(domain_);
point_context_.emplace(GeometryComponentFieldContext(points, domain_));
point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_size);
@@ -614,7 +614,7 @@ class NearestTransferFunction : public fn::MultiFunction {
}
};
-static const GeometryComponent *find_target_component(const GeometrySet &geometry,
+static const GeometryComponent *find_source_component(const GeometrySet &geometry,
const AttributeDomain domain)
{
/* Choose the other component based on a consistent order, rather than some more complicated
@@ -634,7 +634,7 @@ static const GeometryComponent *find_target_component(const GeometrySet &geometr
/**
* The index-based transfer theoretically does not need realized data when there is only one
- * instance geometry set in the target. A future optimization could be removing that limitation
+ * instance geometry set in the source. A future optimization could be removing that limitation
* internally.
*/
class IndexTransferFunction : public fn::MultiFunction {
@@ -670,7 +670,7 @@ class IndexTransferFunction : public fn::MultiFunction {
void evaluate_field()
{
- const GeometryComponent *component = find_target_component(src_geometry_, domain_);
+ const GeometryComponent *component = find_source_component(src_geometry_, domain_);
if (component == nullptr) {
return;
}
@@ -772,7 +772,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (mesh == nullptr) {
if (!geometry.is_empty()) {
params.error_message_add(NodeWarningType::Error,
- TIP_("The target geometry must contain a mesh"));
+ TIP_("The source geometry must contain a mesh"));
}
return return_default();
}
@@ -780,7 +780,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Don't add a warning for empty meshes. */
if (mesh->totvert != 0) {
params.error_message_add(NodeWarningType::Error,
- TIP_("The target mesh must have faces"));
+ TIP_("The source mesh must have faces"));
}
return return_default();
}
@@ -794,7 +794,7 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
params.error_message_add(NodeWarningType::Error,
- TIP_("The target geometry must contain a mesh or a point cloud"));
+ TIP_("The source geometry must contain a mesh or a point cloud"));
return return_default();
}
auto fn = std::make_unique<NearestTransferFunction>(
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 998e4d58bf3..ab1afeb39f4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -14,24 +14,25 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "DNA_mesh_types.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
-extern "C" {
-Mesh *triangulate_mesh(Mesh *mesh,
- const int quad_method,
- const int ngon_method,
- const int min_vertices,
- const int flag);
-}
-
namespace blender::nodes::node_geo_triangulate_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
b.add_input<decl::Int>(N_("Minimum Vertices")).default_value(4).min(4).max(10000);
b.add_output<decl::Geometry>(N_("Mesh"));
}
@@ -48,9 +49,35 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node)
node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY;
}
+static Mesh *triangulate_mesh_selection(const Mesh &mesh,
+ const int quad_method,
+ const int ngon_method,
+ const IndexMask selection,
+ const int min_vertices)
+{
+ CustomData_MeshMasks cd_mask_extra = {
+ CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, 0, CD_MASK_ORIGINDEX};
+ BMeshCreateParams create_params{0};
+ BMeshFromMeshParams from_mesh_params{true, 1, 1, 1, cd_mask_extra};
+ BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params);
+
+ /* Tag faces to be triangulated from the selection mask. */
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+ for (int i_face : selection) {
+ BM_elem_flag_set(BM_face_at_index(bm, i_face), BM_ELEM_TAG, true);
+ }
+
+ BM_mesh_triangulate(bm, quad_method, ngon_method, min_vertices, true, nullptr, nullptr, nullptr);
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, &mesh);
+ BM_mesh_free(bm);
+ BKE_mesh_normals_tag_dirty(result);
+ return result;
+}
+
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4);
GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>(
@@ -59,12 +86,22 @@ static void node_geo_exec(GeoNodeExecParams params)
params.node().custom2);
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- /* #triangulate_mesh might modify the input mesh currently. */
- Mesh *mesh_in = geometry_set.get_mesh_for_write();
- if (mesh_in != nullptr) {
- Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
- geometry_set.replace_mesh(mesh_out);
+ if (!geometry_set.has_mesh()) {
+ return;
}
+ GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>();
+ const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
+
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{context, domain_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+
+ Mesh *mesh_out = triangulate_mesh_selection(
+ mesh_in, quad_method, ngon_method, selection, min_vertices);
+ geometry_set.replace_mesh(mesh_out);
});
params.set_output("Mesh", std::move(geometry_set));
diff --git a/source/blender/nodes/intern/node_exec.cc b/source/blender/nodes/intern/node_exec.cc
index 4ff662036c3..788d938ca6f 100644
--- a/source/blender/nodes/intern/node_exec.cc
+++ b/source/blender/nodes/intern/node_exec.cc
@@ -35,7 +35,7 @@
#include "node_exec.h"
#include "node_util.h"
-int node_exec_socket_use_stack(bNodeSocket *sock)
+static int node_exec_socket_use_stack(bNodeSocket *sock)
{
/* NOTE: INT supported as FLOAT. Only for EEVEE. */
return ELEM(sock->type, SOCK_INT, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER);
@@ -276,60 +276,3 @@ void ntree_exec_end(bNodeTreeExec *exec)
MEM_freeN(exec);
}
-
-/**** Material/Texture trees ****/
-
-bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
-{
- ListBase *lb = &exec->threadstack[thread];
- bNodeThreadStack *nts;
-
- for (nts = (bNodeThreadStack *)lb->first; nts; nts = nts->next) {
- if (!nts->used) {
- nts->used = true;
- break;
- }
- }
-
- if (!nts) {
- nts = MEM_cnew<bNodeThreadStack>("bNodeThreadStack");
- nts->stack = (bNodeStack *)MEM_dupallocN(exec->stack);
- nts->used = true;
- BLI_addtail(lb, nts);
- }
-
- return nts;
-}
-
-void ntreeReleaseThreadStack(bNodeThreadStack *nts)
-{
- nts->used = false;
-}
-
-bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *callerdata, int thread)
-{
- bNodeStack *nsin[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */
- bNodeStack *nsout[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */
- bNodeExec *nodeexec;
- bNode *node;
- int n;
-
- /* nodes are presorted, so exec is in order of list */
-
- for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; n++, nodeexec++) {
- node = nodeexec->node;
- if (node->need_exec) {
- node_get_stack(node, nts->stack, nsin, nsout);
- /* Handle muted nodes...
- * If the mute func is not set, assume the node should never be muted,
- * and hence execute it!
- */
- if (node->typeinfo->exec_fn && !(node->flag & NODE_MUTED)) {
- node->typeinfo->exec_fn(callerdata, thread, node, &nodeexec->data, nsin, nsout);
- }
- }
- }
-
- /* signal to that all went OK, for render */
- return true;
-}
diff --git a/source/blender/nodes/intern/node_exec.h b/source/blender/nodes/intern/node_exec.h
index b2e1c6564b6..115389afd67 100644
--- a/source/blender/nodes/intern/node_exec.h
+++ b/source/blender/nodes/intern/node_exec.h
@@ -71,9 +71,6 @@ typedef struct bNodeThreadStack {
bool used;
} bNodeThreadStack;
-/** Supported socket types in old nodes. */
-int node_exec_socket_use_stack(struct bNodeSocket *sock);
-
/** For a given socket, find the actual stack entry. */
struct bNodeStack *node_get_socket_stack(struct bNodeStack *stack, struct bNodeSocket *sock);
void node_get_stack(struct bNode *node,
@@ -86,23 +83,6 @@ struct bNodeTreeExec *ntree_exec_begin(struct bNodeExecContext *context,
bNodeInstanceKey parent_key);
void ntree_exec_end(struct bNodeTreeExec *exec);
-struct bNodeThreadStack *ntreeGetThreadStack(struct bNodeTreeExec *exec, int thread);
-void ntreeReleaseThreadStack(struct bNodeThreadStack *nts);
-bool ntreeExecThreadNodes(struct bNodeTreeExec *exec,
- struct bNodeThreadStack *nts,
- void *callerdata,
- int thread);
-
-struct bNodeTreeExec *ntreeShaderBeginExecTree_internal(struct bNodeExecContext *context,
- struct bNodeTree *ntree,
- bNodeInstanceKey parent_key);
-void ntreeShaderEndExecTree_internal(struct bNodeTreeExec *exec);
-
-struct bNodeTreeExec *ntreeTexBeginExecTree_internal(struct bNodeExecContext *context,
- struct bNodeTree *ntree,
- bNodeInstanceKey parent_key);
-void ntreeTexEndExecTree_internal(struct bNodeTreeExec *exec);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc
index 4fef5b96e9f..1e6b77a9620 100644
--- a/source/blender/nodes/intern/node_socket_declarations.cc
+++ b/source/blender/nodes/intern/node_socket_declarations.cc
@@ -509,7 +509,8 @@ bool Shader::can_connect(const bNodeSocket &socket) const
}
/* Basic types can convert to shaders, but not the other way around. */
if (in_out_ == SOCK_IN) {
- return ELEM(socket.type, SOCK_VECTOR, SOCK_RGBA, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN);
+ return ELEM(
+ socket.type, SOCK_VECTOR, SOCK_RGBA, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_SHADER);
}
return socket.type == SOCK_SHADER;
}
diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt
index 4cf18429e0d..598b0c81b51 100644
--- a/source/blender/nodes/shader/CMakeLists.txt
+++ b/source/blender/nodes/shader/CMakeLists.txt
@@ -93,6 +93,7 @@ set(SRC
nodes/node_shader_output_material.cc
nodes/node_shader_output_world.cc
nodes/node_shader_particle_info.cc
+ nodes/node_shader_point_info.cc
nodes/node_shader_rgb.cc
nodes/node_shader_rgb_to_bw.cc
nodes/node_shader_script.cc
@@ -157,10 +158,6 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
diff --git a/source/blender/nodes/shader/node_shader_util.hh b/source/blender/nodes/shader/node_shader_util.hh
index 5a5b4f613f3..31229c8693b 100644
--- a/source/blender/nodes/shader/node_shader_util.hh
+++ b/source/blender/nodes/shader/node_shader_util.hh
@@ -101,6 +101,11 @@ void node_shader_gpu_tex_mapping(struct GPUMaterial *mat,
struct GPUNodeStack *in,
struct GPUNodeStack *out);
+struct bNodeTreeExec *ntreeShaderBeginExecTree_internal(struct bNodeExecContext *context,
+ struct bNodeTree *ntree,
+ bNodeInstanceKey parent_key);
+void ntreeShaderEndExecTree_internal(struct bNodeTreeExec *exec);
+
void ntreeExecGPUNodes(struct bNodeTreeExec *exec,
struct GPUMaterial *mat,
struct bNode *output_node);
diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.cc b/source/blender/nodes/shader/nodes/node_shader_bump.cc
index 252abd02ad7..690eacf30ff 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bump.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bump.cc
@@ -60,6 +60,17 @@ static int gpu_shader_bump(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
+ /* If there is no Height input, the node becomes a no-op. */
+ if (!in[2].link) {
+ if (!in[5].link) {
+ return GPU_link(mat, "world_normals_get", &out[0].link);
+ }
+ else {
+ /* Actually running the bump code would normalize, but Cycles handles it as total no-op. */
+ return GPU_link(mat, "vector_copy", in[5].link, &out[0].link);
+ }
+ }
+
if (!in[5].link) {
GPU_link(mat, "world_normals_get", &in[5].link);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_common.cc b/source/blender/nodes/shader/nodes/node_shader_common.cc
index 3f0fff34533..f7a17f0722c 100644
--- a/source/blender/nodes/shader/nodes/node_shader_common.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_common.cc
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- * Juho Vepsäläinen
*/
/** \file
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index bce59a60033..6570ffcac83 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -49,6 +49,7 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
CurveMapping *cumap = (CurveMapping *)node->storage;
+ BKE_curvemapping_init(cumap);
BKE_curvemapping_table_RGBA(cumap, &array, &size);
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
@@ -344,6 +345,7 @@ static int gpu_shader_curve_float(GPUMaterial *mat,
CurveMapping *cumap = (CurveMapping *)node->storage;
+ BKE_curvemapping_init(cumap);
BKE_curvemapping_table_F(cumap, &array, &size);
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
index e22a825c066..12c29c40b1d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
@@ -28,7 +28,6 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Length"));
b.add_output<decl::Float>(N_("Thickness"));
b.add_output<decl::Vector>(N_("Tangent Normal"));
- // b.add_output<decl::Float>(N_("Fade"));
b.add_output<decl::Float>(N_("Random"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index bc7ca661a77..254ab9f866d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -66,6 +66,16 @@ static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), Po
}
}
+static int node_shader_map_range_ui_class(const bNode *node)
+{
+ const NodeMapRange &storage = node_storage(*node);
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ if (data_type == CD_PROP_FLOAT3) {
+ return NODE_CLASS_OP_VECTOR;
+ }
+ return NODE_CLASS_CONVERTER;
+}
+
static void node_shader_update_map_range(bNodeTree *ntree, bNode *node)
{
const NodeMapRange &storage = node_storage(*node);
@@ -665,6 +675,7 @@ void register_node_type_sh_map_range()
sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::sh_node_map_range_declare;
ntype.draw_buttons = file_ns::node_shader_buts_map_range;
+ ntype.ui_class = file_ns::node_shader_map_range_ui_class;
node_type_init(&ntype, file_ns::node_shader_init_map_range);
node_type_storage(
&ntype, "NodeMapRange", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/shader/nodes/node_shader_point_info.cc b/source/blender/nodes/shader/nodes/node_shader_point_info.cc
new file mode 100644
index 00000000000..adc58ca065a
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_point_info.cc
@@ -0,0 +1,54 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "node_shader_util.hh"
+
+namespace blender::nodes::node_shader_point_info_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Vector>(N_("Position"));
+ b.add_output<decl::Float>(N_("Radius"));
+ b.add_output<decl::Float>(N_("Random"));
+}
+
+static int node_shader_gpu_point_info(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ return GPU_stack_link(mat, node, "node_point_info", in, out);
+}
+
+} // namespace blender::nodes::node_shader_point_info_cc
+
+/* node type definition */
+void register_node_type_sh_point_info()
+{
+ namespace file_ns = blender::nodes::node_shader_point_info_cc;
+
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_POINT_INFO, "Point Info", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ node_type_gpu(&ntype, file_ns::node_shader_gpu_point_info);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt
index 053b17e4e57..e595343b7d2 100644
--- a/source/blender/nodes/texture/CMakeLists.txt
+++ b/source/blender/nodes/texture/CMakeLists.txt
@@ -82,8 +82,5 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
blender_add_lib(bf_nodes_texture "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 3d914d486c3..82ed43be779 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -170,6 +170,63 @@ void register_node_tree_type_tex(void)
ntreeTypeAdd(tt);
}
+/**** Material/Texture trees ****/
+
+bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
+{
+ ListBase *lb = &exec->threadstack[thread];
+ bNodeThreadStack *nts;
+
+ for (nts = (bNodeThreadStack *)lb->first; nts; nts = nts->next) {
+ if (!nts->used) {
+ nts->used = true;
+ break;
+ }
+ }
+
+ if (!nts) {
+ nts = MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack");
+ nts->stack = (bNodeStack *)MEM_dupallocN(exec->stack);
+ nts->used = true;
+ BLI_addtail(lb, nts);
+ }
+
+ return nts;
+}
+
+void ntreeReleaseThreadStack(bNodeThreadStack *nts)
+{
+ nts->used = false;
+}
+
+bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *callerdata, int thread)
+{
+ bNodeStack *nsin[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */
+ bNodeStack *nsout[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */
+ bNodeExec *nodeexec;
+ bNode *node;
+ int n;
+
+ /* nodes are presorted, so exec is in order of list */
+
+ for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; n++, nodeexec++) {
+ node = nodeexec->node;
+ if (node->need_exec) {
+ node_get_stack(node, nts->stack, nsin, nsout);
+ /* Handle muted nodes...
+ * If the mute func is not set, assume the node should never be muted,
+ * and hence execute it!
+ */
+ if (node->typeinfo->exec_fn && !(node->flag & NODE_MUTED)) {
+ node->typeinfo->exec_fn(callerdata, thread, node, &nodeexec->data, nsin, nsout);
+ }
+ }
+ }
+
+ /* signal to that all went OK, for render */
+ return true;
+}
+
bNodeTreeExec *ntreeTexBeginExecTree_internal(bNodeExecContext *context,
bNodeTree *ntree,
bNodeInstanceKey parent_key)
diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h
index d53000058a3..7c8e39925bc 100644
--- a/source/blender/nodes/texture/node_texture_util.h
+++ b/source/blender/nodes/texture/node_texture_util.h
@@ -123,6 +123,18 @@ void tex_output(bNode *node,
void params_from_cdata(TexParams *out, TexCallData *in);
+struct bNodeThreadStack *ntreeGetThreadStack(struct bNodeTreeExec *exec, int thread);
+void ntreeReleaseThreadStack(struct bNodeThreadStack *nts);
+bool ntreeExecThreadNodes(struct bNodeTreeExec *exec,
+ struct bNodeThreadStack *nts,
+ void *callerdata,
+ int thread);
+
+struct bNodeTreeExec *ntreeTexBeginExecTree_internal(struct bNodeExecContext *context,
+ struct bNodeTree *ntree,
+ bNodeInstanceKey parent_key);
+void ntreeTexEndExecTree_internal(struct bNodeTreeExec *exec);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/nodes/texture/nodes/node_texture_common.c b/source/blender/nodes/texture/nodes/node_texture_common.c
index d68cfe78b44..9bd16a318c9 100644
--- a/source/blender/nodes/texture/nodes/node_texture_common.c
+++ b/source/blender/nodes/texture/nodes/node_texture_common.c
@@ -15,7 +15,6 @@
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
- * Juho Vepsäläinen
*/
/** \file
diff --git a/source/blender/nodes/texture/nodes/node_texture_output.c b/source/blender/nodes/texture/nodes/node_texture_output.c
index 4911ab7ba9e..59c2d2766d0 100644
--- a/source/blender/nodes/texture/nodes/node_texture_output.c
+++ b/source/blender/nodes/texture/nodes/node_texture_output.c
@@ -49,10 +49,10 @@ static void exec(void *data,
params_from_cdata(&params, cdata);
if (in[1] && in[1]->hasinput && !in[0]->hasinput) {
- tex_input_rgba(&target->tr, in[1], &params, cdata->thread);
+ tex_input_rgba(target->trgba, in[1], &params, cdata->thread);
}
else {
- tex_input_rgba(&target->tr, in[0], &params, cdata->thread);
+ tex_input_rgba(target->trgba, in[0], &params, cdata->thread);
}
}
else {
@@ -61,9 +61,9 @@ static void exec(void *data,
TexParams params;
params_from_cdata(&params, cdata);
- tex_input_rgba(&target->tr, in[0], &params, cdata->thread);
+ tex_input_rgba(target->trgba, in[0], &params, cdata->thread);
- target->tin = (target->tr + target->tg + target->tb) / 3.0f;
+ target->tin = (target->trgba[0] + target->trgba[1] + target->trgba[2]) / 3.0f;
target->talpha = true;
if (target->nor) {
diff --git a/source/blender/nodes/texture/nodes/node_texture_proc.c b/source/blender/nodes/texture/nodes/node_texture_proc.c
index 8c294b5954d..38e4f0268e8 100644
--- a/source/blender/nodes/texture/nodes/node_texture_proc.c
+++ b/source/blender/nodes/texture/nodes/node_texture_proc.c
@@ -72,7 +72,7 @@ static void do_proc(float *result,
}
if (textype & TEX_RGB) {
- copy_v4_v4(result, &texres.tr);
+ copy_v4_v4(result, texres.trgba);
}
else {
copy_v4_v4(result, col1);
diff --git a/source/blender/nodes/texture/nodes/node_texture_texture.c b/source/blender/nodes/texture/nodes/node_texture_texture.c
index 083ae67ccb6..f8f8f25cb5a 100644
--- a/source/blender/nodes/texture/nodes/node_texture_texture.c
+++ b/source/blender/nodes/texture/nodes/node_texture_texture.c
@@ -71,7 +71,7 @@ static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor
textype = multitex_nodes(nodetex, co, dxt, dyt, p->osatex, &texres, thread, 0, p->mtex, NULL);
if (textype & TEX_RGB) {
- copy_v4_v4(out, &texres.tr);
+ copy_v4_v4(out, texres.trgba);
}
else {
copy_v4_v4(out, col1);
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index faa668df775..cbbf2a3cb43 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -12,8 +12,6 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Chris Keith, Chris Want, Ken Hughes, Campbell Barton
*/
/** \file
diff --git a/source/blender/python/intern/bpy_interface_run.c b/source/blender/python/intern/bpy_interface_run.c
index bc21c91074f..79744090cc3 100644
--- a/source/blender/python/intern/bpy_interface_run.c
+++ b/source/blender/python/intern/bpy_interface_run.c
@@ -180,7 +180,7 @@ static bool python_script_exec(
if (!py_result) {
if (text) {
if (do_jump) {
- /* ensure text is valid before use, the script may have freed its self */
+ /* ensure text is valid before use, the script may have freed itself */
Main *bmain_new = CTX_data_main(C);
if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) {
python_script_error_jump_text(text);
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index 2e684faf61b..11e3ad0bcf4 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -94,7 +94,7 @@ static int foreach_libblock_id_user_map_callback(LibraryIDLinkCallbackData *cb_d
}
if (cb_flag & IDWALK_CB_LOOPBACK) {
- /* We skip loop-back pointers like Object.proxy_from or Key.from here,
+ /* We skip loop-back pointers like Key.from here,
* since it's some internal pointer which is not relevant info for py/API level. */
return IDWALK_RET_NOP;
}
diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c
index d3ec54fc12d..d24e2a77a75 100644
--- a/source/blender/python/intern/bpy_rna_operator.c
+++ b/source/blender/python/intern/bpy_rna_operator.c
@@ -103,7 +103,7 @@ PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc,
static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args)
{
- const ssize_t args_len = PyTuple_GET_SIZE(args);
+ const Py_ssize_t args_len = PyTuple_GET_SIZE(args);
if (args_len == 0) {
PyErr_SetString(PyExc_ValueError,
"poll_message_set(message, ...): requires a message argument");
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index d47b59d0c76..b8244efbee2 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -1236,7 +1236,7 @@ static PyObject *M_Geometry_tessellate_polygon(PyObject *UNUSED(self), PyObject
polyLine = PySequence_GetItem(polyLineSeq, i);
if (!PySequence_Check(polyLine)) {
BKE_displist_free(&dispbase);
- Py_XDECREF(polyLine); /* may be null so use Py_XDECREF*/
+ Py_XDECREF(polyLine); /* May be null so use #Py_XDECREF. */
PyErr_SetString(PyExc_TypeError,
"One or more of the polylines is not a sequence of mathutils.Vector's");
return NULL;
diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index a7c1b12982c..a49fc21d7ff 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -95,9 +95,5 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib_nolist(bf_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/render/RE_bake.h b/source/blender/render/RE_bake.h
index d61c0a8bf90..d3025bf4198 100644
--- a/source/blender/render/RE_bake.h
+++ b/source/blender/render/RE_bake.h
@@ -25,9 +25,9 @@
struct Depsgraph;
struct ImBuf;
+struct MLoopUV;
struct Mesh;
struct Render;
-struct MLoopUV;
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h
index 072fc7363b2..c1392b24023 100644
--- a/source/blender/render/RE_pipeline.h
+++ b/source/blender/render/RE_pipeline.h
@@ -310,7 +310,7 @@ void RE_SetOverrideCamera(struct Render *re, struct Object *cam_ob);
*
* \note call this after #RE_InitState().
*/
-void RE_SetCamera(struct Render *re, struct Object *cam_ob);
+void RE_SetCamera(struct Render *re, const struct Object *cam_ob);
/**
* Get current view and window transform.
@@ -454,12 +454,14 @@ struct RenderPass *RE_pass_find_by_type(volatile struct RenderLayer *rl,
#define RE_BAKE_DISPLACEMENT 1
#define RE_BAKE_AO 2
-void RE_GetCameraWindow(struct Render *re, struct Object *camera, float mat[4][4]);
+void RE_GetCameraWindow(struct Render *re, const struct Object *camera, float mat[4][4]);
/**
* Must be called after #RE_GetCameraWindow(), does not change `re->winmat`.
*/
-void RE_GetCameraWindowWithOverscan(struct Render *re, float overscan, float r_winmat[4][4]);
-void RE_GetCameraModelMatrix(struct Render *re, struct Object *camera, float r_modelmat[4][4]);
+void RE_GetCameraWindowWithOverscan(const struct Render *re, float overscan, float r_winmat[4][4]);
+void RE_GetCameraModelMatrix(const struct Render *re,
+ const struct Object *camera,
+ float r_modelmat[4][4]);
struct Scene *RE_GetScene(struct Render *re);
void RE_SetScene(struct Render *re, struct Scene *sce);
diff --git a/source/blender/render/RE_texture.h b/source/blender/render/RE_texture.h
index d71c793f300..4d58d866e9f 100644
--- a/source/blender/render/RE_texture.h
+++ b/source/blender/render/RE_texture.h
@@ -99,10 +99,11 @@ void RE_point_density_fix_linking(void);
/**
* Texture evaluation result.
- * \note `tr tg tb ta` have to remain in this order for array access.
*/
typedef struct TexResult {
- float tin, tr, tg, tb, ta;
+ float tin;
+ float trgba[4];
+ /* Is actually a boolean: When true -> use alpha, false -> set alpha to 1.0. */
int talpha;
float *nor;
} TexResult;
diff --git a/source/blender/render/RE_texture_margin.h b/source/blender/render/RE_texture_margin.h
index 3fb151bbedd..8d1a6c45809 100644
--- a/source/blender/render/RE_texture_margin.h
+++ b/source/blender/render/RE_texture_margin.h
@@ -26,11 +26,11 @@
extern "C" {
#endif
-struct Mesh;
+struct DerivedMesh;
struct IMBuf;
-struct MLoopUV;
struct ImBuf;
-struct DerivedMesh;
+struct MLoopUV;
+struct Mesh;
/**
* Generate a margin around the textures uv islands by copying pixels from the adjacent polygon.
diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c
index 93d2f721cc5..883e026472b 100644
--- a/source/blender/render/intern/bake.c
+++ b/source/blender/render/intern/bake.c
@@ -512,9 +512,9 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval
triangles[i].mverts[0] = &mvert[me->mloop[lt->tri[0]].v];
triangles[i].mverts[1] = &mvert[me->mloop[lt->tri[1]].v];
triangles[i].mverts[2] = &mvert[me->mloop[lt->tri[2]].v];
- triangles[i].vert_normals[0] = &vert_normals[me->mloop[lt->tri[0]].v][0];
- triangles[i].vert_normals[1] = &vert_normals[me->mloop[lt->tri[1]].v][1];
- triangles[i].vert_normals[2] = &vert_normals[me->mloop[lt->tri[2]].v][2];
+ triangles[i].vert_normals[0] = vert_normals[me->mloop[lt->tri[0]].v];
+ triangles[i].vert_normals[1] = vert_normals[me->mloop[lt->tri[1]].v];
+ triangles[i].vert_normals[2] = vert_normals[me->mloop[lt->tri[2]].v];
triangles[i].is_smooth = (mp->flag & ME_SMOOTH) != 0;
if (tangent) {
diff --git a/source/blender/render/intern/initrender.c b/source/blender/render/intern/initrender.c
index f696b81cffb..3bdf60c13a2 100644
--- a/source/blender/render/intern/initrender.c
+++ b/source/blender/render/intern/initrender.c
@@ -174,7 +174,7 @@ void RE_SetOverrideCamera(Render *re, Object *cam_ob)
re->camera_override = cam_ob;
}
-void RE_SetCamera(Render *re, Object *cam_ob)
+void RE_SetCamera(Render *re, const Object *cam_ob)
{
CameraParams params;
@@ -194,13 +194,13 @@ void RE_SetCamera(Render *re, Object *cam_ob)
re->viewplane = params.viewplane;
}
-void RE_GetCameraWindow(struct Render *re, struct Object *camera, float r_winmat[4][4])
+void RE_GetCameraWindow(struct Render *re, const struct Object *camera, float r_winmat[4][4])
{
RE_SetCamera(re, camera);
copy_m4_m4(r_winmat, re->winmat);
}
-void RE_GetCameraWindowWithOverscan(struct Render *re, float overscan, float r_winmat[4][4])
+void RE_GetCameraWindowWithOverscan(const struct Render *re, float overscan, float r_winmat[4][4])
{
CameraParams params;
params.is_ortho = re->winmat[3][3] != 0.0f;
@@ -218,7 +218,7 @@ void RE_GetCameraWindowWithOverscan(struct Render *re, float overscan, float r_w
copy_m4_m4(r_winmat, params.winmat);
}
-void RE_GetCameraModelMatrix(Render *re, struct Object *camera, float r_modelmat[4][4])
+void RE_GetCameraModelMatrix(const Render *re, const struct Object *camera, float r_modelmat[4][4])
{
BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_modelmat);
}
diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c
index 5cf328a3a73..9468e4c5b0f 100644
--- a/source/blender/render/intern/multires_bake.c
+++ b/source/blender/render/intern/multires_bake.c
@@ -33,11 +33,14 @@
#include "BLI_math.h"
#include "BLI_threads.h"
+#include "BKE_DerivedMesh.h"
#include "BKE_ccg.h"
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_lib_id.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_tangent.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_subsurf.h"
@@ -488,9 +491,35 @@ static void do_multires_bake(MultiresBakeRender *bkr,
void *bake_data = NULL;
+ Mesh *temp_mesh = BKE_mesh_new_nomain(
+ dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm));
+ memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert));
+ memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge));
+ memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly));
+ memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop));
+ const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh);
+
if (require_tangent) {
if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) {
- DM_calc_loop_tangents(dm, true, NULL, 0);
+ BKE_mesh_calc_loop_tangent_ex(
+ dm->getVertArray(dm),
+ dm->getPolyArray(dm),
+ dm->getNumPolys(dm),
+ dm->getLoopArray(dm),
+ dm->getLoopTriArray(dm),
+ dm->getNumLoopTri(dm),
+ &dm->loopData,
+ true,
+ NULL,
+ 0,
+ vert_normals,
+ (const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL),
+ (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
+ (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */
+ /* result */
+ &dm->loopData,
+ dm->getNumLoops(dm),
+ &dm->tangent_mask);
}
pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT);
@@ -524,6 +553,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
handle->data.mpoly = mpoly;
handle->data.mvert = mvert;
+ handle->data.vert_normals = vert_normals;
handle->data.mloopuv = mloopuv;
handle->data.mlooptri = mlooptri;
handle->data.mloop = mloop;
@@ -575,6 +605,8 @@ static void do_multires_bake(MultiresBakeRender *bkr,
MEM_freeN(handles);
+ BKE_id_free(NULL, temp_mesh);
+
BKE_image_release_ibuf(ima, ibuf, NULL);
}
}
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c
index 721abe45932..739202564af 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.c
@@ -73,6 +73,8 @@
#include "BKE_sound.h"
#include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */
+#include "NOD_composite.h"
+
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_debug.h"
diff --git a/source/blender/render/intern/texture_common.h b/source/blender/render/intern/texture_common.h
index 5fc3af6153f..43487439228 100644
--- a/source/blender/render/intern/texture_common.h
+++ b/source/blender/render/intern/texture_common.h
@@ -40,34 +40,38 @@ extern "C" {
((void)0)
#define BRICONTRGB \
- texres->tr = tex->rfac * ((texres->tr - 0.5f) * tex->contrast + tex->bright - 0.5f); \
- texres->tg = tex->gfac * ((texres->tg - 0.5f) * tex->contrast + tex->bright - 0.5f); \
- texres->tb = tex->bfac * ((texres->tb - 0.5f) * tex->contrast + tex->bright - 0.5f); \
+ texres->trgba[0] = tex->rfac * \
+ ((texres->trgba[0] - 0.5f) * tex->contrast + tex->bright - 0.5f); \
+ texres->trgba[1] = tex->gfac * \
+ ((texres->trgba[1] - 0.5f) * tex->contrast + tex->bright - 0.5f); \
+ texres->trgba[2] = tex->bfac * \
+ ((texres->trgba[2] - 0.5f) * tex->contrast + tex->bright - 0.5f); \
if (!(tex->flag & TEX_NO_CLAMP)) { \
- if (texres->tr < 0.0f) { \
- texres->tr = 0.0f; \
+ if (texres->trgba[0] < 0.0f) { \
+ texres->trgba[0] = 0.0f; \
} \
- if (texres->tg < 0.0f) { \
- texres->tg = 0.0f; \
+ if (texres->trgba[1] < 0.0f) { \
+ texres->trgba[1] = 0.0f; \
} \
- if (texres->tb < 0.0f) { \
- texres->tb = 0.0f; \
+ if (texres->trgba[2] < 0.0f) { \
+ texres->trgba[2] = 0.0f; \
} \
} \
if (tex->saturation != 1.0f) { \
float _hsv[3]; \
- rgb_to_hsv(texres->tr, texres->tg, texres->tb, _hsv, _hsv + 1, _hsv + 2); \
+ rgb_to_hsv(texres->trgba[0], texres->trgba[1], texres->trgba[2], _hsv, _hsv + 1, _hsv + 2); \
_hsv[1] *= tex->saturation; \
- hsv_to_rgb(_hsv[0], _hsv[1], _hsv[2], &texres->tr, &texres->tg, &texres->tb); \
+ hsv_to_rgb( \
+ _hsv[0], _hsv[1], _hsv[2], &texres->trgba[0], &texres->trgba[1], &texres->trgba[2]); \
if ((tex->saturation > 1.0f) && !(tex->flag & TEX_NO_CLAMP)) { \
- if (texres->tr < 0.0f) { \
- texres->tr = 0.0f; \
+ if (texres->trgba[0] < 0.0f) { \
+ texres->trgba[0] = 0.0f; \
} \
- if (texres->tg < 0.0f) { \
- texres->tg = 0.0f; \
+ if (texres->trgba[1] < 0.0f) { \
+ texres->trgba[1] = 0.0f; \
} \
- if (texres->tb < 0.0f) { \
- texres->tb = 0.0f; \
+ if (texres->trgba[2] < 0.0f) { \
+ texres->trgba[2] = 0.0f; \
} \
} \
} \
diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c
index 6ded799b773..ad355d0cb25 100644
--- a/source/blender/render/intern/texture_image.c
+++ b/source/blender/render/intern/texture_image.c
@@ -108,7 +108,7 @@ int imagewrap(Tex *tex,
int x, y, retval;
int xi, yi; /* original values */
- texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f;
+ texres->tin = texres->trgba[3] = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = 0.0f;
/* we need to set retval OK, otherwise texture code generates normals itself... */
retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB;
@@ -269,7 +269,7 @@ int imagewrap(Tex *tex,
(tex->extend == TEX_EXTEND));
}
else { /* no filtering */
- ibuf_get_color(&texres->tr, ibuf, x, y);
+ ibuf_get_color(texres->trgba, ibuf, x, y);
}
if (texres->nor) {
@@ -281,13 +281,13 @@ int imagewrap(Tex *tex,
* the normal used in the renderer points inward. It is generated
* this way in calc_vertexnormals(). Should this ever change
* this negate must be removed. */
- texres->nor[0] = -2.0f * (texres->tr - 0.5f);
- texres->nor[1] = 2.0f * (texres->tg - 0.5f);
- texres->nor[2] = 2.0f * (texres->tb - 0.5f);
+ texres->nor[0] = -2.0f * (texres->trgba[0] - 0.5f);
+ texres->nor[1] = 2.0f * (texres->trgba[1] - 0.5f);
+ texres->nor[2] = 2.0f * (texres->trgba[2] - 0.5f);
}
else {
/* bump: take three samples */
- val1 = texres->tr + texres->tg + texres->tb;
+ val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2];
if (x < ibuf->x - 1) {
float col[4];
@@ -314,26 +314,26 @@ int imagewrap(Tex *tex,
}
if (texres->talpha) {
- texres->tin = texres->ta;
+ texres->tin = texres->trgba[3];
}
else if (tex->imaflag & TEX_CALCALPHA) {
- texres->ta = texres->tin = max_fff(texres->tr, texres->tg, texres->tb);
+ texres->trgba[3] = texres->tin = max_fff(texres->trgba[0], texres->trgba[1], texres->trgba[2]);
}
else {
- texres->ta = texres->tin = 1.0;
+ texres->trgba[3] = texres->tin = 1.0;
}
if (tex->flag & TEX_NEGALPHA) {
- texres->ta = 1.0f - texres->ta;
+ texres->trgba[3] = 1.0f - texres->trgba[3];
}
/* de-premul, this is being pre-multiplied in shade_input_do_shade()
* do not de-premul for generated alpha, it is already in straight */
- if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) {
- fx = 1.0f / texres->ta;
- texres->tr *= fx;
- texres->tg *= fx;
- texres->tb *= fx;
+ if (texres->trgba[3] != 1.0f && texres->trgba[3] > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) {
+ fx = 1.0f / texres->trgba[3];
+ texres->trgba[0] *= fx;
+ texres->trgba[1] *= fx;
+ texres->trgba[2] *= fx;
}
if (ima) {
@@ -546,10 +546,10 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres)
}
if (starty == endy && startx == endx) {
- ibuf_get_color(&texres->tr, ibuf, startx, starty);
+ ibuf_get_color(texres->trgba, ibuf, startx, starty);
}
else {
- div = texres->tr = texres->tg = texres->tb = texres->ta = 0.0;
+ div = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = texres->trgba[3] = 0.0;
for (y = starty; y <= endy; y++) {
muly = 1.0;
@@ -570,11 +570,7 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres)
mulx = muly;
ibuf_get_color(col, ibuf, startx, y);
-
- texres->ta += mulx * col[3];
- texres->tr += mulx * col[0];
- texres->tg += mulx * col[1];
- texres->tb += mulx * col[2];
+ madd_v4_v4fl(texres->trgba, col, mulx);
div += mulx;
}
else {
@@ -588,19 +584,14 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres)
}
ibuf_get_color(col, ibuf, x, y);
-
+ /* TODO(jbakker): No need to do manual optimization. Branching is slower than multiplying
+ * with 1. */
if (mulx == 1.0f) {
- texres->ta += col[3];
- texres->tr += col[0];
- texres->tg += col[1];
- texres->tb += col[2];
+ add_v4_v4(texres->trgba, col);
div += 1.0f;
}
else {
- texres->ta += mulx * col[3];
- texres->tr += mulx * col[0];
- texres->tg += mulx * col[1];
- texres->tb += mulx * col[2];
+ madd_v4_v4fl(texres->trgba, col, mulx);
div += mulx;
}
}
@@ -609,13 +600,10 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres)
if (div != 0.0f) {
div = 1.0f / div;
- texres->tb *= div;
- texres->tg *= div;
- texres->tr *= div;
- texres->ta *= div;
+ mul_v4_fl(texres->trgba, div);
}
else {
- texres->tr = texres->tg = texres->tb = texres->ta = 0.0f;
+ zero_v4(texres->trgba);
}
}
}
@@ -663,7 +651,7 @@ static void boxsample(ImBuf *ibuf,
alphaclip = clipx_rctf(rf, 0.0, (float)(ibuf->x));
if (alphaclip <= 0.0f) {
- texres->tr = texres->tb = texres->tg = texres->ta = 0.0;
+ texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0;
return;
}
}
@@ -679,33 +667,33 @@ static void boxsample(ImBuf *ibuf,
alphaclip *= clipy_rctf(rf, 0.0, (float)(ibuf->y));
if (alphaclip <= 0.0f) {
- texres->tr = texres->tb = texres->tg = texres->ta = 0.0;
+ texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0;
return;
}
}
if (count > 1) {
- tot = texres->tr = texres->tb = texres->tg = texres->ta = 0.0;
+ tot = texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0;
while (count--) {
boxsampleclip(ibuf, rf, &texr);
opp = square_rctf(rf);
tot += opp;
- texres->tr += opp * texr.tr;
- texres->tg += opp * texr.tg;
- texres->tb += opp * texr.tb;
+ texres->trgba[0] += opp * texr.trgba[0];
+ texres->trgba[1] += opp * texr.trgba[1];
+ texres->trgba[2] += opp * texr.trgba[2];
if (texres->talpha) {
- texres->ta += opp * texr.ta;
+ texres->trgba[3] += opp * texr.trgba[3];
}
rf++;
}
if (tot != 0.0f) {
- texres->tr /= tot;
- texres->tg /= tot;
- texres->tb /= tot;
+ texres->trgba[0] /= tot;
+ texres->trgba[1] /= tot;
+ texres->trgba[2] /= tot;
if (texres->talpha) {
- texres->ta /= tot;
+ texres->trgba[3] /= tot;
}
}
}
@@ -714,15 +702,15 @@ static void boxsample(ImBuf *ibuf,
}
if (texres->talpha == 0) {
- texres->ta = 1.0;
+ texres->trgba[3] = 1.0;
}
if (alphaclip != 1.0f) {
/* premul it all */
- texres->tr *= alphaclip;
- texres->tg *= alphaclip;
- texres->tb *= alphaclip;
- texres->ta *= alphaclip;
+ texres->trgba[0] *= alphaclip;
+ texres->trgba[1] *= alphaclip;
+ texres->trgba[2] *= alphaclip;
+ texres->trgba[3] *= alphaclip;
}
}
@@ -850,7 +838,7 @@ static void area_sample(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata
ysam = CLAMPIS(ysam, minsam, ibuf->y * 2);
xsd = 1.0f / xsam;
ysd = 1.0f / ysam;
- texr->tr = texr->tg = texr->tb = texr->ta = 0.0f;
+ texr->trgba[0] = texr->trgba[1] = texr->trgba[2] = texr->trgba[3] = 0.0f;
for (ys = 0; ys < ysam; ys++) {
for (xs = 0; xs < xsam; xs++) {
const float su = (xs + ((ys & 1) + 0.5f) * 0.5f) * xsd - 0.5f;
@@ -861,18 +849,18 @@ static void area_sample(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata
tc, ibuf, pu * ibuf->x, pv * ibuf->y, AFD->intpol, AFD->extflag);
clip |= out;
cw += out ? 0.0f : 1.0f;
- texr->tr += tc[0];
- texr->tg += tc[1];
- texr->tb += tc[2];
- texr->ta += texr->talpha ? tc[3] : 0.0f;
+ texr->trgba[0] += tc[0];
+ texr->trgba[1] += tc[1];
+ texr->trgba[2] += tc[2];
+ texr->trgba[3] += texr->talpha ? tc[3] : 0.0f;
}
}
xsd *= ysd;
- texr->tr *= xsd;
- texr->tg *= xsd;
- texr->tb *= xsd;
- /* clipping can be ignored if alpha used, texr->ta already includes filtered edge */
- texr->ta = texr->talpha ? texr->ta * xsd : (clip ? cw * xsd : 1.0f);
+ texr->trgba[0] *= xsd;
+ texr->trgba[1] *= xsd;
+ texr->trgba[2] *= xsd;
+ /* clipping can be ignored if alpha used, texr->trgba[3] already includes filtered edge */
+ texr->trgba[3] = texr->talpha ? texr->trgba[3] * xsd : (clip ? cw * xsd : 1.0f);
}
typedef struct ReadEWAData {
@@ -901,7 +889,7 @@ static void ewa_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata_t
AFD->dyt,
ewa_read_pixel_cb,
&data,
- &texr->tr);
+ texr->trgba);
}
static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata_t *AFD)
@@ -919,7 +907,7 @@ static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata
/* have to use same scaling for du/dv here as for Ux/Vx/Uy/Vy (*after* D calc.) */
du *= AFD->dusc;
dv *= AFD->dvsc;
- d = texr->tr = texr->tb = texr->tg = texr->ta = 0.0f;
+ d = texr->trgba[0] = texr->trgba[2] = texr->trgba[1] = texr->trgba[3] = 0.0f;
for (n = -maxn; n <= maxn; n += 2) {
float tc[4];
const float hn = n * 0.5f;
@@ -934,19 +922,20 @@ static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata
tc, ibuf, ibuf->x * u, ibuf->y * v, AFD->intpol, AFD->extflag);
/* TXF alpha: `clip |= out;`
* TXF alpha: `cw += out ? 0.0f : wt;` */
- texr->tr += tc[0] * wt;
- texr->tg += tc[1] * wt;
- texr->tb += tc[2] * wt;
- texr->ta += texr->talpha ? tc[3] * wt : 0.0f;
+ texr->trgba[0] += tc[0] * wt;
+ texr->trgba[1] += tc[1] * wt;
+ texr->trgba[2] += tc[2] * wt;
+ texr->trgba[3] += texr->talpha ? tc[3] * wt : 0.0f;
d += wt;
}
d = 1.0f / d;
- texr->tr *= d;
- texr->tg *= d;
- texr->tb *= d;
- /* Clipping can be ignored if alpha used, `texr->ta` already includes filtered edge */
- texr->ta = texr->talpha ? texr->ta * d : 1.0f; /* TXF alpha: `(clip ? cw*d : 1.0f);` */
+ texr->trgba[0] *= d;
+ texr->trgba[1] *= d;
+ texr->trgba[2] *= d;
+ /* Clipping can be ignored if alpha used, `texr->trgba[3]` already includes filtered edge */
+ texr->trgba[3] = texr->talpha ? texr->trgba[3] * d :
+ 1.0f; /* TXF alpha: `(clip ? cw*d : 1.0f);` */
}
#undef EWA_MAXIDX
@@ -971,10 +960,10 @@ static void alpha_clip_aniso(
if (alphaclip != 1.0f) {
/* premul it all */
- texres->tr *= alphaclip;
- texres->tg *= alphaclip;
- texres->tb *= alphaclip;
- texres->ta *= alphaclip;
+ texres->trgba[0] *= alphaclip;
+ texres->trgba[1] *= alphaclip;
+ texres->trgba[2] *= alphaclip;
+ texres->trgba[3] *= alphaclip;
}
}
}
@@ -1033,7 +1022,7 @@ static int imagewraposa_aniso(Tex *tex,
filterfunc = area_sample;
}
- texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f;
+ texres->tin = texres->trgba[3] = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = 0.0f;
/* we need to set retval OK, otherwise texture code generates normals itself... */
retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB;
@@ -1332,27 +1321,27 @@ static int imagewraposa_aniso(Tex *tex,
if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) {
/* color & normal */
filterfunc(texres, curibuf, fx, fy, &AFD);
- val1 = texres->tr + texres->tg + texres->tb;
+ val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2];
filterfunc(&texr, curibuf, fx + dxt[0], fy + dxt[1], &AFD);
- val2 = texr.tr + texr.tg + texr.tb;
+ val2 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2];
filterfunc(&texr, curibuf, fx + dyt[0], fy + dyt[1], &AFD);
- val3 = texr.tr + texr.tg + texr.tb;
+ val3 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2];
/* don't switch x or y! */
texres->nor[0] = val1 - val2;
texres->nor[1] = val1 - val3;
if (previbuf != curibuf) { /* interpolate */
filterfunc(&texr, previbuf, fx, fy, &AFD);
/* rgb */
- texres->tr += levf * (texr.tr - texres->tr);
- texres->tg += levf * (texr.tg - texres->tg);
- texres->tb += levf * (texr.tb - texres->tb);
- texres->ta += levf * (texr.ta - texres->ta);
+ texres->trgba[0] += levf * (texr.trgba[0] - texres->trgba[0]);
+ texres->trgba[1] += levf * (texr.trgba[1] - texres->trgba[1]);
+ texres->trgba[2] += levf * (texr.trgba[2] - texres->trgba[2]);
+ texres->trgba[3] += levf * (texr.trgba[3] - texres->trgba[3]);
/* normal */
- val1 += levf * ((texr.tr + texr.tg + texr.tb) - val1);
+ val1 += levf * ((texr.trgba[0] + texr.trgba[1] + texr.trgba[2]) - val1);
filterfunc(&texr, previbuf, fx + dxt[0], fy + dxt[1], &AFD);
- val2 += levf * ((texr.tr + texr.tg + texr.tb) - val2);
+ val2 += levf * ((texr.trgba[0] + texr.trgba[1] + texr.trgba[2]) - val2);
filterfunc(&texr, previbuf, fx + dyt[0], fy + dyt[1], &AFD);
- val3 += levf * ((texr.tr + texr.tg + texr.tb) - val3);
+ val3 += levf * ((texr.trgba[0] + texr.trgba[1] + texr.trgba[2]) - val3);
texres->nor[0] = val1 - val2; /* vals have been interpolated above! */
texres->nor[1] = val1 - val3;
}
@@ -1361,10 +1350,10 @@ static int imagewraposa_aniso(Tex *tex,
filterfunc(texres, curibuf, fx, fy, &AFD);
if (previbuf != curibuf) { /* interpolate */
filterfunc(&texr, previbuf, fx, fy, &AFD);
- texres->tr += levf * (texr.tr - texres->tr);
- texres->tg += levf * (texr.tg - texres->tg);
- texres->tb += levf * (texr.tb - texres->tb);
- texres->ta += levf * (texr.ta - texres->ta);
+ texres->trgba[0] += levf * (texr.trgba[0] - texres->trgba[0]);
+ texres->trgba[1] += levf * (texr.trgba[1] - texres->trgba[1]);
+ texres->trgba[2] += levf * (texr.trgba[2] - texres->trgba[2]);
+ texres->trgba[3] += levf * (texr.trgba[3] - texres->trgba[3]);
}
if (tex->texfilter != TXF_EWA) {
@@ -1402,11 +1391,11 @@ static int imagewraposa_aniso(Tex *tex,
if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) {
/* color & normal */
filterfunc(texres, ibuf, fx, fy, &AFD);
- val1 = texres->tr + texres->tg + texres->tb;
+ val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2];
filterfunc(&texr, ibuf, fx + dxt[0], fy + dxt[1], &AFD);
- val2 = texr.tr + texr.tg + texr.tb;
+ val2 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2];
filterfunc(&texr, ibuf, fx + dyt[0], fy + dyt[1], &AFD);
- val3 = texr.tr + texr.tg + texr.tb;
+ val3 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2];
/* don't switch x or y! */
texres->nor[0] = val1 - val2;
texres->nor[1] = val1 - val3;
@@ -1420,13 +1409,14 @@ static int imagewraposa_aniso(Tex *tex,
}
if (tex->imaflag & TEX_CALCALPHA) {
- texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb);
+ texres->trgba[3] = texres->tin = texres->trgba[3] *
+ max_fff(texres->trgba[0], texres->trgba[1], texres->trgba[2]);
}
else {
- texres->tin = texres->ta;
+ texres->tin = texres->trgba[3];
}
if (tex->flag & TEX_NEGALPHA) {
- texres->ta = 1.0f - texres->ta;
+ texres->trgba[3] = 1.0f - texres->trgba[3];
}
if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { /* normal from color */
@@ -1436,9 +1426,9 @@ static int imagewraposa_aniso(Tex *tex,
* the normal used in the renderer points inward. It is generated
* this way in calc_vertexnormals(). Should this ever change
* this negate must be removed. */
- texres->nor[0] = -2.0f * (texres->tr - 0.5f);
- texres->nor[1] = 2.0f * (texres->tg - 0.5f);
- texres->nor[2] = 2.0f * (texres->tb - 0.5f);
+ texres->nor[0] = -2.0f * (texres->trgba[0] - 0.5f);
+ texres->nor[1] = 2.0f * (texres->trgba[1] - 0.5f);
+ texres->nor[2] = 2.0f * (texres->trgba[2] - 0.5f);
}
/* de-premul, this is being pre-multiplied in shade_input_do_shade()
@@ -1449,11 +1439,11 @@ static int imagewraposa_aniso(Tex *tex,
/* brecht: tried to fix this, see "TXF alpha" comments */
/* do not de-premul for generated alpha, it is already in straight */
- if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) {
- fx = 1.0f / texres->ta;
- texres->tr *= fx;
- texres->tg *= fx;
- texres->tb *= fx;
+ if (texres->trgba[3] != 1.0f && texres->trgba[3] > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) {
+ fx = 1.0f / texres->trgba[3];
+ texres->trgba[0] *= fx;
+ texres->trgba[1] *= fx;
+ texres->trgba[2] *= fx;
}
if (ima) {
@@ -1490,7 +1480,7 @@ int imagewraposa(Tex *tex,
return imagewraposa_aniso(tex, ima, ibuf, texvec, dxt, dyt, texres, pool, skip_load_image);
}
- texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f;
+ texres->tin = texres->trgba[3] = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = 0.0f;
/* we need to set retval OK, otherwise texture code generates normals itself... */
retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB;
@@ -1795,7 +1785,7 @@ int imagewraposa(Tex *tex,
boxsample(
curibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend);
- val1 = texres->tr + texres->tg + texres->tb;
+ val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2];
boxsample(curibuf,
fx - minx + dxt[0],
fy - miny + dxt[1],
@@ -1804,7 +1794,7 @@ int imagewraposa(Tex *tex,
&texr,
imaprepeat,
imapextend);
- val2 = texr.tr + texr.tg + texr.tb;
+ val2 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2];
boxsample(curibuf,
fx - minx + dyt[0],
fy - miny + dyt[1],
@@ -1813,7 +1803,7 @@ int imagewraposa(Tex *tex,
&texr,
imaprepeat,
imapextend);
- val3 = texr.tr + texr.tg + texr.tb;
+ val3 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2];
/* don't switch x or y! */
texres->nor[0] = (val1 - val2);
@@ -1827,20 +1817,20 @@ int imagewraposa(Tex *tex,
/* calc rgb */
dx = 2.0f * (pixsize - maxd) / pixsize;
if (dx >= 1.0f) {
- texres->ta = texr.ta;
- texres->tb = texr.tb;
- texres->tg = texr.tg;
- texres->tr = texr.tr;
+ texres->trgba[3] = texr.trgba[3];
+ texres->trgba[2] = texr.trgba[2];
+ texres->trgba[1] = texr.trgba[1];
+ texres->trgba[0] = texr.trgba[0];
}
else {
dy = 1.0f - dx;
- texres->tb = dy * texres->tb + dx * texr.tb;
- texres->tg = dy * texres->tg + dx * texr.tg;
- texres->tr = dy * texres->tr + dx * texr.tr;
- texres->ta = dy * texres->ta + dx * texr.ta;
+ texres->trgba[2] = dy * texres->trgba[2] + dx * texr.trgba[2];
+ texres->trgba[1] = dy * texres->trgba[1] + dx * texr.trgba[1];
+ texres->trgba[0] = dy * texres->trgba[0] + dx * texr.trgba[0];
+ texres->trgba[3] = dy * texres->trgba[3] + dx * texr.trgba[3];
}
- val1 = dy * val1 + dx * (texr.tr + texr.tg + texr.tb);
+ val1 = dy * val1 + dx * (texr.trgba[0] + texr.trgba[1] + texr.trgba[2]);
boxsample(previbuf,
fx - minx + dxt[0],
fy - miny + dxt[1],
@@ -1849,7 +1839,7 @@ int imagewraposa(Tex *tex,
&texr,
imaprepeat,
imapextend);
- val2 = dy * val2 + dx * (texr.tr + texr.tg + texr.tb);
+ val2 = dy * val2 + dx * (texr.trgba[0] + texr.trgba[1] + texr.trgba[2]);
boxsample(previbuf,
fx - minx + dyt[0],
fy - miny + dyt[1],
@@ -1858,17 +1848,17 @@ int imagewraposa(Tex *tex,
&texr,
imaprepeat,
imapextend);
- val3 = dy * val3 + dx * (texr.tr + texr.tg + texr.tb);
+ val3 = dy * val3 + dx * (texr.trgba[0] + texr.trgba[1] + texr.trgba[2]);
texres->nor[0] = (val1 - val2); /* vals have been interpolated above! */
texres->nor[1] = (val1 - val3);
if (dx < 1.0f) {
dy = 1.0f - dx;
- texres->tb = dy * texres->tb + dx * texr.tb;
- texres->tg = dy * texres->tg + dx * texr.tg;
- texres->tr = dy * texres->tr + dx * texr.tr;
- texres->ta = dy * texres->ta + dx * texr.ta;
+ texres->trgba[2] = dy * texres->trgba[2] + dx * texr.trgba[2];
+ texres->trgba[1] = dy * texres->trgba[1] + dx * texr.trgba[1];
+ texres->trgba[0] = dy * texres->trgba[0] + dx * texr.trgba[0];
+ texres->trgba[3] = dy * texres->trgba[3] + dx * texr.trgba[3];
}
}
texres->nor[0] *= bumpscale;
@@ -1888,17 +1878,17 @@ int imagewraposa(Tex *tex,
fx = 2.0f * (pixsize - maxd) / pixsize;
if (fx >= 1.0f) {
- texres->ta = texr.ta;
- texres->tb = texr.tb;
- texres->tg = texr.tg;
- texres->tr = texr.tr;
+ texres->trgba[3] = texr.trgba[3];
+ texres->trgba[2] = texr.trgba[2];
+ texres->trgba[1] = texr.trgba[1];
+ texres->trgba[0] = texr.trgba[0];
}
else {
fy = 1.0f - fx;
- texres->tb = fy * texres->tb + fx * texr.tb;
- texres->tg = fy * texres->tg + fx * texr.tg;
- texres->tr = fy * texres->tr + fx * texr.tr;
- texres->ta = fy * texres->ta + fx * texr.ta;
+ texres->trgba[2] = fy * texres->trgba[2] + fx * texr.trgba[2];
+ texres->trgba[1] = fy * texres->trgba[1] + fx * texr.trgba[1];
+ texres->trgba[0] = fy * texres->trgba[0] + fx * texr.trgba[0];
+ texres->trgba[3] = fy * texres->trgba[3] + fx * texr.trgba[3];
}
}
}
@@ -1917,7 +1907,7 @@ int imagewraposa(Tex *tex,
if (texres->nor && (tex->imaflag & TEX_NORMALMAP) == 0) {
boxsample(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend);
- val1 = texres->tr + texres->tg + texres->tb;
+ val1 = texres->trgba[0] + texres->trgba[1] + texres->trgba[2];
boxsample(ibuf,
fx - minx + dxt[0],
fy - miny + dxt[1],
@@ -1926,7 +1916,7 @@ int imagewraposa(Tex *tex,
&texr,
imaprepeat,
imapextend);
- val2 = texr.tr + texr.tg + texr.tb;
+ val2 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2];
boxsample(ibuf,
fx - minx + dyt[0],
fy - miny + dyt[1],
@@ -1935,7 +1925,7 @@ int imagewraposa(Tex *tex,
&texr,
imaprepeat,
imapextend);
- val3 = texr.tr + texr.tg + texr.tb;
+ val3 = texr.trgba[0] + texr.trgba[1] + texr.trgba[2];
/* don't switch x or y! */
texres->nor[0] = (val1 - val2);
@@ -1947,14 +1937,15 @@ int imagewraposa(Tex *tex,
}
if (tex->imaflag & TEX_CALCALPHA) {
- texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb);
+ texres->trgba[3] = texres->tin = texres->trgba[3] *
+ max_fff(texres->trgba[0], texres->trgba[1], texres->trgba[2]);
}
else {
- texres->tin = texres->ta;
+ texres->tin = texres->trgba[3];
}
if (tex->flag & TEX_NEGALPHA) {
- texres->ta = 1.0f - texres->ta;
+ texres->trgba[3] = 1.0f - texres->trgba[3];
}
if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) {
@@ -1963,15 +1954,15 @@ int imagewraposa(Tex *tex,
* It needs to be done because in Blender the normal used in the renderer points inward.
* It is generated this way in #calc_vertexnormals().
* Should this ever change this negate must be removed. */
- texres->nor[0] = -2.0f * (texres->tr - 0.5f);
- texres->nor[1] = 2.0f * (texres->tg - 0.5f);
- texres->nor[2] = 2.0f * (texres->tb - 0.5f);
+ texres->nor[0] = -2.0f * (texres->trgba[0] - 0.5f);
+ texres->nor[1] = 2.0f * (texres->trgba[1] - 0.5f);
+ texres->nor[2] = 2.0f * (texres->trgba[2] - 0.5f);
}
/* de-premul, this is being pre-multiplied in shade_input_do_shade() */
/* do not de-premul for generated alpha, it is already in straight */
- if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) {
- mul_v3_fl(&texres->tr, 1.0f / texres->ta);
+ if (texres->trgba[3] != 1.0f && texres->trgba[3] > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) {
+ mul_v3_fl(texres->trgba, 1.0f / texres->trgba[3]);
}
if (ima) {
@@ -1996,7 +1987,7 @@ void image_sample(
texres.talpha = true; /* boxsample expects to be initialized */
boxsample(ibuf, fx, fy, fx + dx, fy + dy, &texres, 0, 1);
- copy_v4_v4(result, &texres.tr);
+ copy_v4_v4(result, texres.trgba);
ima->flag |= IMA_USED_FOR_RENDER;
@@ -2020,5 +2011,5 @@ void ibuf_sample(ImBuf *ibuf, float fx, float fy, float dx, float dy, float resu
ewa_eval(&texres, ibuf, fx, fy, &AFD);
- copy_v4_v4(result, &texres.tr);
+ copy_v4_v4(result, texres.trgba);
}
diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc
index 9ce5a0f9f01..7aff01242e3 100644
--- a/source/blender/render/intern/texture_margin.cc
+++ b/source/blender/render/intern/texture_margin.cc
@@ -43,20 +43,22 @@
#include "RE_texture_margin.h"
#include <algorithm>
-#include <math.h>
+#include <cmath>
#include <valarray>
namespace blender::render::texturemargin {
-/* The map class contains both a pixel map which maps out polygon indices for all UV-polygons and
+/**
+ * The map class contains both a pixel map which maps out polygon indices for all UV-polygons and
* adjacency tables.
*/
class TextureMarginMap {
- static const int directions[4][2];
+ static const int directions[8][2];
+ static const int distances[8];
- /* Maps UV-edges to their corresponding UV-edge. */
+ /** Maps UV-edges to their corresponding UV-edge. */
Vector<int> loop_adjacency_map_;
- /* Maps UV-edges to their corresponding polygon. */
+ /** Maps UV-edges to their corresponding polygon. */
Vector<int> loop_to_poly_map_;
int w_, h_;
@@ -121,18 +123,17 @@ class TextureMarginMap {
void rasterize_tri(float *v1, float *v2, float *v3, uint32_t value, char *mask)
{
/* NOTE: This is not thread safe, because the value to be written by the rasterizer is
- * a class member. If this is ever made multi-threaded each thread needs to get it's own. */
+ * a class member. If this is ever made multi-threaded each thread needs to get its own. */
value_to_store_ = value;
mask_ = mask;
zspan_scanconvert(
&zspan_, this, &(v1[0]), &(v2[0]), &(v3[0]), TextureMarginMap::zscan_store_pixel);
}
- static void zscan_store_pixel(void *map, int x, int y, float, float)
+ static void zscan_store_pixel(
+ void *map, int x, int y, [[maybe_unused]] float u, [[maybe_unused]] float v)
{
- /* NOTE: Not thread safe, see comment above.
- *
- */
+ /* NOTE: Not thread safe, see comment above. */
TextureMarginMap *m = static_cast<TextureMarginMap *>(map);
m->set_pixel(x, y, m->value_to_store_);
if (m->mask_) {
@@ -141,17 +142,18 @@ class TextureMarginMap {
}
/* The map contains 2 kinds of pixels: DijkstraPixels and polygon indices. The top bit determines
- * what kind it is. With the top bit set, it is a 'dijkstra' pixel. The bottom 3 bits encode the
- * direction of the shortest path and the remaining 28 bits are used to store the distance. If
+ * what kind it is. With the top bit set, it is a 'dijkstra' pixel. The bottom 4 bits encode the
+ * direction of the shortest path and the remaining 27 bits are used to store the distance. If
* the top bit is not set, the rest of the bits is used to store the polygon index.
*/
-#define PackDijkstraPixel(dist, dir) (0x80000000 + ((dist) << 3) + (dir))
-#define DijkstraPixelGetDistance(dp) (((dp) ^ 0x80000000) >> 3)
-#define DijkstraPixelGetDirection(dp) ((dp)&0x7)
+#define PackDijkstraPixel(dist, dir) (0x80000000 + ((dist) << 4) + (dir))
+#define DijkstraPixelGetDistance(dp) (((dp) ^ 0x80000000) >> 4)
+#define DijkstraPixelGetDirection(dp) ((dp)&0xF)
#define IsDijkstraPixel(dp) ((dp)&0x80000000)
#define DijkstraPixelIsUnset(dp) ((dp) == 0xFFFFFFFF)
- /* Use dijkstra's algorithm to 'grow' a border around the polygons marked in the map.
+ /**
+ * Use dijkstra's algorithm to 'grow' a border around the polygons marked in the map.
* For each pixel mark which direction is the shortest way to a polygon.
*/
void grow_dijkstra(int margin)
@@ -172,13 +174,13 @@ class TextureMarginMap {
for (int y = 0; y < h_; y++) {
for (int x = 0; x < w_; x++) {
if (DijkstraPixelIsUnset(get_pixel(x, y))) {
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 8; i++) {
int xx = x - directions[i][0];
int yy = y - directions[i][1];
if (xx >= 0 && xx < w_ && yy >= 0 && yy < w_ && !IsDijkstraPixel(get_pixel(xx, yy))) {
- set_pixel(x, y, PackDijkstraPixel(1, i));
- active_pixels.append(DijkstraActivePixel(1, x, y));
+ set_pixel(x, y, PackDijkstraPixel(distances[i], i));
+ active_pixels.append(DijkstraActivePixel(distances[i], x, y));
break;
}
}
@@ -186,8 +188,10 @@ class TextureMarginMap {
}
}
- // std::make_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
- // Not strictly needed because at this point it already is a heap.
+ /* Not strictly needed because at this point it already is a heap. */
+#if 0
+ std::make_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
+#endif
while (active_pixels.size()) {
std::pop_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
@@ -195,17 +199,16 @@ class TextureMarginMap {
int dist = p.distance;
- dist++;
- if (dist < margin) {
- for (int i = 0; i < 4; i++) {
+ if (dist < 2 * (margin + 1)) {
+ for (int i = 0; i < 8; i++) {
int x = p.x + directions[i][0];
int y = p.y + directions[i][1];
if (x >= 0 && x < w_ && y >= 0 && y < h_) {
uint32_t dp = get_pixel(x, y);
- if (IsDijkstraPixel(dp) && (DijkstraPixelGetDistance(dp) > dist)) {
- BLI_assert(abs((int)DijkstraPixelGetDirection(dp) - (int)i) != 2);
- set_pixel(x, y, PackDijkstraPixel(dist, i));
- active_pixels.append(DijkstraActivePixel(dist, x, y));
+ if (IsDijkstraPixel(dp) && (DijkstraPixelGetDistance(dp) > dist + distances[i])) {
+ BLI_assert(DijkstraPixelGetDirection(dp) != i);
+ set_pixel(x, y, PackDijkstraPixel(dist + distances[i], i));
+ active_pixels.append(DijkstraActivePixel(dist + distances[i], x, y));
std::push_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
}
}
@@ -214,7 +217,8 @@ class TextureMarginMap {
}
}
- /* Walk over the map and for margin pixels follow the direction stored in the bottom 3
+ /**
+ * Walk over the map and for margin pixels follow the direction stored in the bottom 3
* bits back to the polygon.
* Then look up the pixel from the next polygon.
*/
@@ -235,7 +239,7 @@ class TextureMarginMap {
xx -= directions[direction][0];
yy -= directions[direction][1];
dp = get_pixel(xx, yy);
- dist--;
+ dist -= distances[direction];
BLI_assert(!dist || (dist == DijkstraPixelGetDistance(dp)));
direction = DijkstraPixelGetDirection(dp);
}
@@ -248,7 +252,7 @@ class TextureMarginMap {
int other_poly;
bool found_pixel_in_polygon = false;
- if (lookup_pixel(x, y, poly, &destX, &destY, &other_poly)) {
+ if (lookup_pixel_polygon_neighbourhood(x, y, &poly, &destX, &destY, &other_poly)) {
for (int i = 0; i < maxPolygonSteps; i++) {
/* Force to pixel grid. */
@@ -260,8 +264,12 @@ class TextureMarginMap {
break;
}
+ float dist_to_edge;
/* Look up again, but starting from the polygon we were expected to land in. */
- lookup_pixel(nx, ny, other_poly, &destX, &destY, &other_poly);
+ if (!lookup_pixel(nx, ny, other_poly, &destX, &destY, &other_poly, &dist_to_edge)) {
+ found_pixel_in_polygon = false;
+ break;
+ }
}
if (found_pixel_in_polygon) {
@@ -319,12 +327,67 @@ class TextureMarginMap {
}
}
- /* Find which edge of the src_poly is closest to x,y. Look up it's adjacent UV-edge and polygon.
+ /**
+ * Call lookup_pixel for the start_poly. If that fails, try the adjacent polygons as well.
+ * Because the Dijkstra is not very exact in determining which polygon is the closest, the
+ * polygon we need can be the one next to the one the Dijkstra map provides. To prevent missing
+ * pixels also check the neighboring polygons.
+ */
+ bool lookup_pixel_polygon_neighbourhood(
+ float x, float y, uint32_t *r_start_poly, float *r_destx, float *r_desty, int *r_other_poly)
+ {
+ float found_dist;
+ if (lookup_pixel(x, y, *r_start_poly, r_destx, r_desty, r_other_poly, &found_dist)) {
+ return true;
+ }
+
+ int loopstart = mpoly_[*r_start_poly].loopstart;
+ int totloop = mpoly_[*r_start_poly].totloop;
+
+ float destx, desty;
+ int foundpoly;
+
+ float mindist = -1.f;
+
+ /* Loop over all adjacent polygons and determine which edge is closest.
+ * This could be optimized by only inspecting neighbors which are on the edge of an island.
+ * But it seems fast enough for now and that would add a lot of complexity. */
+ for (int i = 0; i < totloop; i++) {
+ int otherloop = loop_adjacency_map_[i + loopstart];
+
+ if (otherloop < 0) {
+ continue;
+ }
+
+ uint32_t poly = loop_to_poly_map_[otherloop];
+
+ if (lookup_pixel(x, y, poly, &destx, &desty, &foundpoly, &found_dist)) {
+ if (mindist < 0.f || found_dist < mindist) {
+ mindist = found_dist;
+ *r_other_poly = foundpoly;
+ *r_destx = destx;
+ *r_desty = desty;
+ *r_start_poly = poly;
+ }
+ }
+ }
+
+ return mindist >= 0.f;
+ }
+
+ /**
+ * Find which edge of the src_poly is closest to x,y. Look up its adjacent UV-edge and polygon.
* Then return the location of the equivalent pixel in the other polygon.
* Returns true if a new pixel location was found, false if it wasn't, which can happen if the
- * margin pixel is on a corner, or the UV-edge doesn't have an adjacent polygon. */
- bool lookup_pixel(
- float x, float y, int src_poly, float *r_destx, float *r_desty, int *r_other_poly)
+ * margin pixel is on a corner, or the UV-edge doesn't have an adjacent polygon.
+ */
+ bool lookup_pixel(float x,
+ float y,
+ int src_poly,
+ float *r_destx,
+ float *r_desty,
+ int *r_other_poly,
+ float *r_dist_to_edge)
{
float2 point(x, y);
@@ -384,6 +447,8 @@ class TextureMarginMap {
return false;
}
+ *r_dist_to_edge = found_dist;
+
/* Get the 'other' edge. I.E. the UV edge from the neighbor polygon. */
int other_edge = loop_adjacency_map_[found_edge];
@@ -405,7 +470,7 @@ class TextureMarginMap {
float2 other_edgepoint1 = uv_to_xy(mloopuv_[other_edge]);
float2 other_edgepoint2 = uv_to_xy(mloopuv_[other_edge2]);
- /* Calculate the vector from the order edges last point to it's first point. */
+ /* Calculate the vector from the order edges last point to its first point. */
float2 other_ab = other_edgepoint1 - other_edgepoint2;
float2 other_reflect_point = other_edgepoint2 + (found_t * other_ab);
float2 perpendicular_other_ab;
@@ -424,7 +489,9 @@ class TextureMarginMap {
}
}; // class TextureMarginMap
-const int TextureMarginMap::directions[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
+const int TextureMarginMap::directions[8][2] = {
+ {-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}};
+const int TextureMarginMap::distances[8] = {2, 3, 2, 3, 2, 3, 2, 3};
static void generate_margin(ImBuf *ibuf,
char *mask,
@@ -441,17 +508,17 @@ static void generate_margin(ImBuf *ibuf,
int tottri;
MLoopTri const *looptri;
- MLoopTri *looptri_mem = NULL;
+ MLoopTri *looptri_mem = nullptr;
if (me) {
- BLI_assert(dm == NULL);
+ BLI_assert(dm == nullptr);
totpoly = me->totpoly;
totloop = me->totloop;
totedge = me->totedge;
mpoly = me->mpoly;
mloop = me->mloop;
- if ((uv_layer == NULL) || (uv_layer[0] == '\0')) {
+ if ((uv_layer == nullptr) || (uv_layer[0] == '\0')) {
mloopuv = static_cast<MLoopUV const *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV));
}
else {
@@ -467,9 +534,8 @@ static void generate_margin(ImBuf *ibuf,
looptri = looptri_mem;
}
else {
- BLI_assert(dm != NULL);
- BLI_assert(me == NULL);
- BLI_assert(mloopuv == NULL);
+ BLI_assert(dm != nullptr);
+ BLI_assert(me == nullptr);
totpoly = dm->getNumPolys(dm);
totedge = dm->getNumEdges(dm);
totloop = dm->getNumLoops(dm);
@@ -509,8 +575,10 @@ static void generate_margin(ImBuf *ibuf,
vec[a][1] = uv[1] * (float)ibuf->y - (0.5f + 0.002f);
}
- BLI_assert(lt->poly < 0x80000000); // NOTE: we need the top bit for the dijkstra distance map
- map.rasterize_tri(vec[0], vec[1], vec[2], lt->poly, draw_new_mask ? mask : NULL);
+ /* NOTE: we need the top bit for the dijkstra distance map. */
+ BLI_assert(lt->poly < 0x80000000);
+
+ map.rasterize_tri(vec[0], vec[1], vec[2], lt->poly, draw_new_mask ? mask : nullptr);
}
char *tmpmask = (char *)MEM_dupallocN(mask);
@@ -543,7 +611,7 @@ static void generate_margin(ImBuf *ibuf,
void RE_generate_texturemargin_adjacentfaces(
ImBuf *ibuf, char *mask, const int margin, const Mesh *me, char const *uv_layer)
{
- blender::render::texturemargin::generate_margin(ibuf, mask, margin, me, NULL, uv_layer);
+ blender::render::texturemargin::generate_margin(ibuf, mask, margin, me, nullptr, uv_layer);
}
void RE_generate_texturemargin_adjacentfaces_dm(ImBuf *ibuf,
@@ -551,5 +619,5 @@ void RE_generate_texturemargin_adjacentfaces_dm(ImBuf *ibuf,
const int margin,
DerivedMesh *dm)
{
- blender::render::texturemargin::generate_margin(ibuf, mask, margin, NULL, dm, NULL);
+ blender::render::texturemargin::generate_margin(ibuf, mask, margin, nullptr, dm, nullptr);
}
diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c
index 683260f86cb..9abaeb3739f 100644
--- a/source/blender/render/intern/texture_pointdensity.c
+++ b/source/blender/render/intern/texture_pointdensity.c
@@ -687,7 +687,7 @@ static int pointdensity(PointDensity *pd,
static void pointdensity_color(
PointDensity *pd, TexResult *texres, float age, const float vec[3], const float col[3])
{
- texres->tr = texres->tg = texres->tb = texres->ta = 1.0f;
+ copy_v4_fl(texres->trgba, 1.0f);
if (pd->source == TEX_PD_PSYS) {
float rgba[4];
@@ -697,9 +697,9 @@ static void pointdensity_color(
if (pd->coba) {
if (BKE_colorband_evaluate(pd->coba, age, rgba)) {
texres->talpha = true;
- copy_v3_v3(&texres->tr, rgba);
+ copy_v3_v3(texres->trgba, rgba);
texres->tin *= rgba[3];
- texres->ta = texres->tin;
+ texres->trgba[3] = texres->tin;
}
}
break;
@@ -709,17 +709,17 @@ static void pointdensity_color(
if (pd->coba) {
if (BKE_colorband_evaluate(pd->coba, speed, rgba)) {
texres->talpha = true;
- copy_v3_v3(&texres->tr, rgba);
+ copy_v3_v3(texres->trgba, rgba);
texres->tin *= rgba[3];
- texres->ta = texres->tin;
+ texres->trgba[3] = texres->tin;
}
}
break;
}
case TEX_PD_COLOR_PARTVEL:
texres->talpha = true;
- mul_v3_v3fl(&texres->tr, vec, pd->speed_scale);
- texres->ta = texres->tin;
+ mul_v3_v3fl(texres->trgba, vec, pd->speed_scale);
+ texres->trgba[3] = texres->tin;
break;
case TEX_PD_COLOR_CONSTANT:
default:
@@ -732,24 +732,24 @@ static void pointdensity_color(
switch (pd->ob_color_source) {
case TEX_PD_COLOR_VERTCOL:
texres->talpha = true;
- copy_v3_v3(&texres->tr, col);
- texres->ta = texres->tin;
+ copy_v3_v3(texres->trgba, col);
+ texres->trgba[3] = texres->tin;
break;
case TEX_PD_COLOR_VERTWEIGHT:
texres->talpha = true;
if (pd->coba && BKE_colorband_evaluate(pd->coba, col[0], rgba)) {
- copy_v3_v3(&texres->tr, rgba);
+ copy_v3_v3(texres->trgba, rgba);
texres->tin *= rgba[3];
}
else {
- copy_v3_v3(&texres->tr, col);
+ copy_v3_v3(texres->trgba, col);
}
- texres->ta = texres->tin;
+ texres->trgba[3] = texres->tin;
break;
case TEX_PD_COLOR_VERTNOR:
texres->talpha = true;
- copy_v3_v3(&texres->tr, col);
- texres->ta = texres->tin;
+ copy_v3_v3(texres->trgba, col);
+ texres->trgba[3] = texres->tin;
break;
case TEX_PD_COLOR_CONSTANT:
default:
@@ -915,7 +915,7 @@ static void point_density_sample_func(void *__restrict data_v,
pointdensity(pd, texvec, &texres, vec, &age, col);
pointdensity_color(pd, &texres, age, vec, col);
- copy_v3_v3(&values[index * 4 + 0], &texres.tr);
+ copy_v3_v3(&values[index * 4 + 0], texres.trgba);
values[index * 4 + 3] = texres.tin;
}
}
diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c
index f563cb9f84a..ee45810f445 100644
--- a/source/blender/render/intern/texture_procedural.c
+++ b/source/blender/render/intern/texture_procedural.c
@@ -43,15 +43,15 @@
#include "IMB_colormanagement.h"
#include "IMB_imbuf_types.h"
-#include "BKE_image.h"
-#include "BKE_node.h"
-
#include "BKE_colorband.h"
+#include "BKE_image.h"
#include "BKE_material.h"
+#include "BKE_node.h"
#include "BKE_scene.h"
-
#include "BKE_texture.h"
+#include "NOD_texture.h"
+
#include "MEM_guardedalloc.h"
#include "render_types.h"
@@ -212,23 +212,23 @@ static int clouds(const Tex *tex, const float texvec[3], TexResult *texres)
if (tex->stype == TEX_COLOR) {
/* in this case, int. value should really be computed from color,
* and bumpnormal from that, would be too slow, looks ok as is */
- texres->tr = texres->tin;
- texres->tg = BLI_noise_generic_turbulence(tex->noisesize,
- texvec[1],
- texvec[0],
- texvec[2],
- tex->noisedepth,
- (tex->noisetype != TEX_NOISESOFT),
- tex->noisebasis);
- texres->tb = BLI_noise_generic_turbulence(tex->noisesize,
- texvec[1],
- texvec[2],
- texvec[0],
- tex->noisedepth,
- (tex->noisetype != TEX_NOISESOFT),
- tex->noisebasis);
+ texres->trgba[0] = texres->tin;
+ texres->trgba[1] = BLI_noise_generic_turbulence(tex->noisesize,
+ texvec[1],
+ texvec[0],
+ texvec[2],
+ tex->noisedepth,
+ (tex->noisetype != TEX_NOISESOFT),
+ tex->noisebasis);
+ texres->trgba[2] = BLI_noise_generic_turbulence(tex->noisesize,
+ texvec[1],
+ texvec[2],
+ texvec[0],
+ tex->noisedepth,
+ (tex->noisetype != TEX_NOISESOFT),
+ tex->noisebasis);
BRICONTRGB;
- texres->ta = 1.0;
+ texres->trgba[3] = 1.0;
return (rv | TEX_RGB);
}
@@ -453,14 +453,14 @@ static int magic(const Tex *tex, const float texvec[3], TexResult *texres)
y /= turb;
z /= turb;
}
- texres->tr = 0.5f - x;
- texres->tg = 0.5f - y;
- texres->tb = 0.5f - z;
+ texres->trgba[0] = 0.5f - x;
+ texres->trgba[1] = 0.5f - y;
+ texres->trgba[2] = 0.5f - z;
- texres->tin = (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb);
+ texres->tin = (1.0f / 3.0f) * (texres->trgba[0] + texres->trgba[1] + texres->trgba[2]);
BRICONTRGB;
- texres->ta = 1.0f;
+ texres->trgba[3] = 1.0f;
return TEX_RGB;
}
@@ -767,21 +767,21 @@ static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres)
if (tex->vn_coltype) {
float ca[3]; /* cell color */
BLI_noise_cell_v3(pa[0], pa[1], pa[2], ca);
- texres->tr = aw1 * ca[0];
- texres->tg = aw1 * ca[1];
- texres->tb = aw1 * ca[2];
+ texres->trgba[0] = aw1 * ca[0];
+ texres->trgba[1] = aw1 * ca[1];
+ texres->trgba[2] = aw1 * ca[2];
BLI_noise_cell_v3(pa[3], pa[4], pa[5], ca);
- texres->tr += aw2 * ca[0];
- texres->tg += aw2 * ca[1];
- texres->tb += aw2 * ca[2];
+ texres->trgba[0] += aw2 * ca[0];
+ texres->trgba[1] += aw2 * ca[1];
+ texres->trgba[2] += aw2 * ca[2];
BLI_noise_cell_v3(pa[6], pa[7], pa[8], ca);
- texres->tr += aw3 * ca[0];
- texres->tg += aw3 * ca[1];
- texres->tb += aw3 * ca[2];
+ texres->trgba[0] += aw3 * ca[0];
+ texres->trgba[1] += aw3 * ca[1];
+ texres->trgba[2] += aw3 * ca[2];
BLI_noise_cell_v3(pa[9], pa[10], pa[11], ca);
- texres->tr += aw4 * ca[0];
- texres->tg += aw4 * ca[1];
- texres->tb += aw4 * ca[2];
+ texres->trgba[0] += aw4 * ca[0];
+ texres->trgba[1] += aw4 * ca[1];
+ texres->trgba[2] += aw4 * ca[2];
if (tex->vn_coltype >= 2) {
float t1 = (da[1] - da[0]) * 10;
if (t1 > 1) {
@@ -793,14 +793,14 @@ static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres)
else {
t1 *= sc;
}
- texres->tr *= t1;
- texres->tg *= t1;
- texres->tb *= t1;
+ texres->trgba[0] *= t1;
+ texres->trgba[1] *= t1;
+ texres->trgba[2] *= t1;
}
else {
- texres->tr *= sc;
- texres->tg *= sc;
- texres->tb *= sc;
+ texres->trgba[0] *= sc;
+ texres->trgba[1] *= sc;
+ texres->trgba[2] *= sc;
}
}
@@ -821,7 +821,7 @@ static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres)
if (tex->vn_coltype) {
BRICONTRGB;
- texres->ta = 1.0;
+ texres->trgba[3] = 1.0;
return (rv | TEX_RGB);
}
@@ -1270,10 +1270,7 @@ static int multitex(Tex *tex,
float col[4];
if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) {
texres->talpha = true;
- texres->tr = col[0];
- texres->tg = col[1];
- texres->tb = col[2];
- texres->ta = col[3];
+ copy_v4_v4(texres->trgba, col);
retval |= TEX_RGB;
}
}
@@ -1330,7 +1327,7 @@ static int multitex_nodes_intern(Tex *tex,
/* don't linearize float buffers, assumed to be linear */
if (ibuf != NULL && ibuf->rect_float == NULL && (rgbnor & TEX_RGB) && scene_color_manage) {
- IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace);
+ IMB_colormanagement_colorspace_to_scene_linear_v3(texres->trgba, ibuf->rect_colorspace);
}
BKE_image_pool_release_ibuf(tex->ima, ibuf, pool);
@@ -1375,7 +1372,7 @@ static int multitex_nodes_intern(Tex *tex,
/* don't linearize float buffers, assumed to be linear */
if (ibuf != NULL && ibuf->rect_float == NULL && (rgbnor & TEX_RGB) && scene_color_manage) {
- IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace);
+ IMB_colormanagement_colorspace_to_scene_linear_v3(texres->trgba, ibuf->rect_colorspace);
}
BKE_image_pool_release_ibuf(tex->ima, ibuf, pool);
@@ -1768,19 +1765,14 @@ bool RE_texture_evaluate(const MTex *mtex,
true);
if (rgb) {
- texr.tin = IMB_colormanagement_get_luminance(&texr.tr);
+ texr.tin = IMB_colormanagement_get_luminance(texr.trgba);
}
else {
- texr.tr = mtex->r;
- texr.tg = mtex->g;
- texr.tb = mtex->b;
+ copy_v3_fl3(texr.trgba, mtex->r, mtex->g, mtex->b);
}
*r_intensity = texr.tin;
- r_rgba[0] = texr.tr;
- r_rgba[1] = texr.tg;
- r_rgba[2] = texr.tb;
- r_rgba[3] = texr.ta;
+ copy_v4_v4(r_rgba, texr.trgba);
return (rgb != 0);
}
diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h
index 85f44ab914f..23ad845fda1 100644
--- a/source/blender/sequencer/SEQ_add.h
+++ b/source/blender/sequencer/SEQ_add.h
@@ -64,7 +64,8 @@ typedef struct SeqLoadData {
bool use_multiview;
char views_format;
struct Stereo3dFormat *stereo3d_format;
- bool allow_invalid_file; /* Used by RNA API to create placeholder strips. */
+ bool allow_invalid_file; /* Used by RNA API to create placeholder strips. */
+ double r_video_stream_start; /* For AV synchronization. Set by `SEQ_add_movie_strip`. */
} SeqLoadData;
/**
@@ -108,8 +109,7 @@ struct Sequence *SEQ_add_image_strip(struct Main *bmain,
struct Sequence *SEQ_add_sound_strip(struct Main *bmain,
struct Scene *scene,
struct ListBase *seqbase,
- struct SeqLoadData *load_data,
- double audio_offset);
+ struct SeqLoadData *load_data);
/**
* Add meta strip.
*
@@ -133,8 +133,7 @@ struct Sequence *SEQ_add_meta_strip(struct Scene *scene,
struct Sequence *SEQ_add_movie_strip(struct Main *bmain,
struct Scene *scene,
struct ListBase *seqbase,
- struct SeqLoadData *load_data,
- double *r_start_offset);
+ struct SeqLoadData *load_data);
/**
* Add scene strip.
*
diff --git a/source/blender/sequencer/SEQ_proxy.h b/source/blender/sequencer/SEQ_proxy.h
index 7bfe932ff1c..164b279245c 100644
--- a/source/blender/sequencer/SEQ_proxy.h
+++ b/source/blender/sequencer/SEQ_proxy.h
@@ -42,7 +42,8 @@ bool SEQ_proxy_rebuild_context(struct Main *bmain,
struct Scene *scene,
struct Sequence *seq,
struct GSet *file_list,
- struct ListBase *queue);
+ struct ListBase *queue,
+ bool build_only_on_bad_performance);
void SEQ_proxy_rebuild(struct SeqIndexBuildContext *context,
short *stop,
short *do_update,
diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h
index a209857d1f9..515c47f53fd 100644
--- a/source/blender/sequencer/SEQ_utils.h
+++ b/source/blender/sequencer/SEQ_utils.h
@@ -32,9 +32,9 @@ extern "C" {
struct ListBase;
struct Mask;
struct Scene;
+struct SeqRenderData;
struct Sequence;
struct StripElem;
-struct SeqRenderData;
/**
* Sort strips in provided seqbase. Effect strips are trailing the list and they are sorted by
diff --git a/source/blender/sequencer/intern/clipboard.c b/source/blender/sequencer/intern/clipboard.c
index 886ee89595b..82599a5fb69 100644
--- a/source/blender/sequencer/intern/clipboard.c
+++ b/source/blender/sequencer/intern/clipboard.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index a35e83a8632..909954b99f1 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c
index 6cd53f08b3a..42a5f5df787 100644
--- a/source/blender/sequencer/intern/iterator.c
+++ b/source/blender/sequencer/intern/iterator.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/multiview.c b/source/blender/sequencer/intern/multiview.c
index 68d2a33fd5c..2658846209f 100644
--- a/source/blender/sequencer/intern/multiview.c
+++ b/source/blender/sequencer/intern/multiview.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c
index 2cb2ba13fb5..906474f301d 100644
--- a/source/blender/sequencer/intern/proxy.c
+++ b/source/blender/sequencer/intern/proxy.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
@@ -415,7 +413,8 @@ bool SEQ_proxy_rebuild_context(Main *bmain,
Scene *scene,
Sequence *seq,
struct GSet *file_list,
- ListBase *queue)
+ ListBase *queue,
+ bool build_only_on_bad_performance)
{
SeqIndexBuildContext *context;
Sequence *nseq;
@@ -476,7 +475,8 @@ bool SEQ_proxy_rebuild_context(Main *bmain,
context->size_flags,
context->quality,
context->overwrite,
- file_list);
+ file_list,
+ build_only_on_bad_performance);
}
if (!context->index_context) {
MEM_freeN(context);
@@ -601,10 +601,7 @@ void SEQ_proxy_set(struct Sequence *seq, bool value)
if (value) {
seq->flag |= SEQ_USE_PROXY;
if (seq->strip->proxy == NULL) {
- seq->strip->proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy");
- seq->strip->proxy->quality = 50;
- seq->strip->proxy->build_tc_flags = SEQ_PROXY_TC_ALL;
- seq->strip->proxy->build_size_flags = SEQ_PROXY_IMAGE_SIZE_25;
+ seq->strip->proxy = seq_strip_proxy_alloc();
}
}
else {
diff --git a/source/blender/sequencer/intern/proxy_job.c b/source/blender/sequencer/intern/proxy_job.c
index afdac04d998..8989d7959c9 100644
--- a/source/blender/sequencer/intern/proxy_job.c
+++ b/source/blender/sequencer/intern/proxy_job.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c
index 482425e70d3..08c81096b14 100644
--- a/source/blender/sequencer/intern/render.c
+++ b/source/blender/sequencer/intern/render.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
@@ -524,8 +522,15 @@ static void sequencer_preprocess_transform_crop(
const float crop_scale_factor = do_scale_to_render_size ? preview_scale_factor : 1.0f;
sequencer_image_crop_init(seq, in, crop_scale_factor, &source_crop);
- const eIMBInterpolationFilterMode filter = context->for_render ? IMB_FILTER_BILINEAR :
- IMB_FILTER_NEAREST;
+ eIMBInterpolationFilterMode filter;
+ const StripTransform *transform = seq->strip->transform;
+ if (transform->filter == SEQ_TRANSFORM_FILTER_NEAREST) {
+ filter = IMB_FILTER_NEAREST;
+ }
+ else {
+ filter = IMB_FILTER_BILINEAR;
+ }
+
IMB_transform(in, out, IMB_TRANSFORM_MODE_CROP_SRC, filter, transform_matrix, &source_crop);
if (!seq_image_transform_transparency_gained(context, seq)) {
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index f0a45355143..387b65648a4 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
@@ -66,6 +64,15 @@
/** \name Allocate / Free Functions
* \{ */
+StripProxy *seq_strip_proxy_alloc(void)
+{
+ StripProxy *strip_proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy");
+ strip_proxy->quality = 50;
+ strip_proxy->build_tc_flags = SEQ_PROXY_TC_ALL;
+ strip_proxy->tc = SEQ_PROXY_TC_RECORD_RUN;
+ return strip_proxy;
+}
+
static Strip *seq_strip_alloc(int type)
{
Strip *strip = MEM_callocN(sizeof(Strip), "strip");
@@ -76,6 +83,7 @@ static Strip *seq_strip_alloc(int type)
strip->transform->scale_y = 1;
strip->transform->origin[0] = 0.5f;
strip->transform->origin[1] = 0.5f;
+ strip->transform->filter = SEQ_TRANSFORM_FILTER_BILINEAR;
strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop");
}
diff --git a/source/blender/sequencer/intern/sequencer.h b/source/blender/sequencer/intern/sequencer.h
index 7d7ecbc8178..2a82f966f02 100644
--- a/source/blender/sequencer/intern/sequencer.h
+++ b/source/blender/sequencer/intern/sequencer.h
@@ -29,12 +29,13 @@ extern "C" {
struct Scene;
struct Sequence;
-
+struct StripProxy;
/**
* Cache must be freed before calling this function
* since it leaves the seqbase in an invalid state.
*/
void seq_free_sequence_recurse(struct Scene *scene, struct Sequence *seq, bool do_id_user);
+struct StripProxy *seq_strip_proxy_alloc(void);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c
index 86a37aca4a9..141b07cebbe 100644
--- a/source/blender/sequencer/intern/sound.c
+++ b/source/blender/sequencer/intern/sound.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
@@ -31,6 +29,7 @@
#include "DNA_sound_types.h"
#include "BLI_listbase.h"
+#include "BLI_utildefines.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -56,11 +55,15 @@ static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene,
}
}
else if (seq->type == SEQ_TYPE_SOUND_RAM && seq->sound) {
- const float length = BKE_sound_get_length(bmain, seq->sound);
+ SoundInfo info;
+ if (!BKE_sound_info_get(bmain, seq->sound, &info)) {
+ continue;
+ }
+
int old = seq->len;
float fac;
- seq->len = (int)ceil((double)length * FPS);
+ seq->len = MAX2(1, round((info.length - seq->sound->offset_time) * FPS));
fac = (float)seq->len / (float)old;
old = seq->startofs;
seq->startofs *= fac;
diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c
index f342765eec9..eb34d287dcc 100644
--- a/source/blender/sequencer/intern/strip_add.c
+++ b/source/blender/sequencer/intern/strip_add.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
@@ -287,14 +285,24 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
}
#ifdef WITH_AUDASPACE
-Sequence *SEQ_add_sound_strip(Main *bmain,
- Scene *scene,
- ListBase *seqbase,
- SeqLoadData *load_data,
- const double audio_offset)
+
+static void seq_add_sound_av_sync(Main *bmain, Scene *scene, Sequence *seq, SeqLoadData *load_data)
+{
+ SoundStreamInfo sound_stream;
+ if (!BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_stream)) {
+ return;
+ }
+
+ const double av_stream_offset = sound_stream.start - load_data->r_video_stream_start;
+ const int frame_offset = av_stream_offset * FPS;
+ /* Set sub-frame offset. */
+ seq->sound->offset_time = ((double)frame_offset / FPS) - av_stream_offset;
+ SEQ_transform_translate_sequence(scene, seq, frame_offset);
+}
+
+Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data)
{
bSound *sound = BKE_sound_new_file(bmain, load_data->path); /* Handles relative paths. */
- sound->offset_time = audio_offset;
SoundInfo info;
bool sound_loaded = BKE_sound_info_get(bmain, sound, &info);
@@ -337,6 +345,8 @@ Sequence *SEQ_add_sound_strip(Main *bmain,
}
}
+ seq_add_sound_av_sync(bmain, scene, seq, load_data);
+
/* Set Last active directory. */
BLI_strncpy(scene->ed->act_sounddir, strip->dir, FILE_MAXDIR);
seq_add_set_name(scene, seq, load_data);
@@ -349,8 +359,7 @@ Sequence *SEQ_add_sound_strip(Main *bmain,
Sequence *SEQ_add_sound_strip(Main *UNUSED(bmain),
Scene *UNUSED(scene),
ListBase *UNUSED(seqbase),
- SeqLoadData *UNUSED(load_data),
- const double UNUSED(audio_offset))
+ SeqLoadData *UNUSED(load_data))
{
return NULL;
}
@@ -373,8 +382,7 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_
return seqm;
}
-Sequence *SEQ_add_movie_strip(
- Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data, double *r_start_offset)
+Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data)
{
char path[sizeof(load_data->path)];
BLI_strncpy(path, load_data->path, sizeof(path));
@@ -420,8 +428,8 @@ Sequence *SEQ_add_movie_strip(
return NULL;
}
- int video_frame_offset = 0;
float video_fps = 0.0f;
+ load_data->r_video_stream_start = 0.0;
if (anim_arr[0] != NULL) {
short fps_denom;
@@ -437,23 +445,11 @@ Sequence *SEQ_add_movie_strip(
scene->r.frs_sec_base = fps_num;
}
- double video_start_offset = IMD_anim_get_offset(anim_arr[0]);
- int minimum_frame_offset;
-
- if (*r_start_offset >= 0) {
- minimum_frame_offset = MIN2(video_start_offset, *r_start_offset) * FPS;
- }
- else {
- minimum_frame_offset = video_start_offset * FPS;
- }
-
- video_frame_offset = video_start_offset * FPS - minimum_frame_offset;
-
- *r_start_offset = video_start_offset;
+ load_data->r_video_stream_start = IMD_anim_get_offset(anim_arr[0]);
}
Sequence *seq = SEQ_sequence_alloc(
- seqbase, load_data->start_frame + video_frame_offset, load_data->channel, SEQ_TYPE_MOVIE);
+ seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIE);
/* Multiview settings. */
if (load_data->use_multiview) {
diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c
index 0479d3012fa..0d6a94a806d 100644
--- a/source/blender/sequencer/intern/strip_edit.c
+++ b/source/blender/sequencer/intern/strip_edit.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c
index 7e7fc9e6bf7..a716bd6fed8 100644
--- a/source/blender/sequencer/intern/strip_relations.c
+++ b/source/blender/sequencer/intern/strip_relations.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/strip_select.c b/source/blender/sequencer/intern/strip_select.c
index 8927e092864..c73e7d3a420 100644
--- a/source/blender/sequencer/intern/strip_select.c
+++ b/source/blender/sequencer/intern/strip_select.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index 31ee20cb6ca..2d36ab0654b 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index 432fc1c166f..f4f492708eb 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c
index 140aa2d67c5..fa34f0d6c79 100644
--- a/source/blender/sequencer/intern/utils.c
+++ b/source/blender/sequencer/intern/utils.c
@@ -13,11 +13,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * - Blender Foundation, 2003-2009
- * - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved.
+ * 2003-2009 Blender Foundation.
+ * 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*/
/** \file
diff --git a/source/blender/sequencer/intern/utils.h b/source/blender/sequencer/intern/utils.h
index 512647ed2e2..b5aa66804c6 100644
--- a/source/blender/sequencer/intern/utils.h
+++ b/source/blender/sequencer/intern/utils.h
@@ -27,8 +27,8 @@
extern "C" {
#endif
-struct Scene;
struct ListBase;
+struct Scene;
bool sequencer_seq_generates_image(struct Sequence *seq);
void seq_open_anim_file(struct Scene *scene, struct Sequence *seq, bool openfile);
diff --git a/source/blender/shader_fx/CMakeLists.txt b/source/blender/shader_fx/CMakeLists.txt
index 0167591c3ba..ae78d9964e5 100644
--- a/source/blender/shader_fx/CMakeLists.txt
+++ b/source/blender/shader_fx/CMakeLists.txt
@@ -63,9 +63,5 @@ set(SRC
set(LIB
)
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
blender_add_lib(bf_shader_fx "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 03b2fb49085..37b233d85d2 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -135,10 +135,6 @@ if(WITH_CYCLES)
add_definitions(-DWITH_CYCLES)
endif()
-if(WITH_INTERNATIONAL)
- add_definitions(-DWITH_INTERNATIONAL)
-endif()
-
if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 2e305c0bf3c..ff3e1b7474c 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -47,6 +47,7 @@ struct GHashIterator;
struct GPUViewport;
struct ID;
struct IDProperty;
+struct IDRemapper;
struct ImBuf;
struct ImageFormatData;
struct Main;
@@ -471,7 +472,7 @@ void WM_main_add_notifier(unsigned int type, void *reference);
* Clear notifiers by reference, Used so listeners don't act on freed data.
*/
void WM_main_remove_notifier_reference(const void *reference);
-void WM_main_remap_editor_id_reference(struct ID *old_id, struct ID *new_id);
+void WM_main_remap_editor_id_reference(const struct IDRemapper *mappings);
/* reports */
/**
@@ -636,12 +637,12 @@ bool WM_operator_poll_context(struct bContext *C, struct wmOperatorType *ot, sho
*
* \param store: Store settings for re-use.
*
- * \warning do not use this within an operator to call its self! T29537.
+ * \warning do not use this within an operator to call itself! T29537.
*/
int WM_operator_call_ex(struct bContext *C, struct wmOperator *op, bool store);
int WM_operator_call(struct bContext *C, struct wmOperator *op);
/**
- * This is intended to be used when an invoke operator wants to call exec on its self
+ * This is intended to be used when an invoke operator wants to call exec on itself
* and is basically like running op->type->exec() directly, no poll checks no freeing,
* since we assume whoever called invoke will take care of that
*/
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index d1f790ce3e2..929b39715b9 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -1048,6 +1048,10 @@ typedef struct wmDragActiveDropState {
* it as needed. */
struct ARegion *region_from;
+ /** If `active_dropbox` is set, additional context provided by the active (i.e. hovered) button.
+ * Activated before context sensitive operations (polling, drawing, dropping). */
+ struct bContextStore *ui_context;
+
/** Text to show when a dropbox poll succeeds (so the dropbox itself is available) but the
* operator poll fails. Typically the message the operator set with
* CTX_wm_operator_poll_msg_set(). */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index 6f10e4f3f0d..2a93e279576 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -706,7 +706,7 @@ wmKeyMap *WM_gizmogroup_setup_keymap_generic_maybe_drag(const wmGizmoGroupType *
/**
* Variation of #WM_gizmogroup_keymap_common but with keymap items for selection
*
- * TODO(campbell): move to Python.
+ * TODO(@campbellbarton): move to Python.
*
* \param name: Typically #wmGizmoGroupType.name
* \param params: Typically #wmGizmoGroupType.gzmap_params
@@ -719,7 +719,7 @@ static wmKeyMap *WM_gizmogroup_keymap_template_select_ex(
wmKeyMap *km = WM_keymap_ensure(kc, name, params->spaceid, params->regionid);
const bool do_init = BLI_listbase_is_empty(&km->items);
- /* FIXME(campbell) */
+ /* FIXME(@campbellbarton): Currently hard coded. */
#if 0
const int select_mouse = (U.flag & USER_LMOUSESELECT) ? LEFTMOUSE : RIGHTMOUSE;
const int select_tweak = (U.flag & USER_LMOUSESELECT) ? EVT_TWEAK_L : EVT_TWEAK_R;
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index d3e682f1490..83e6e347ce7 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -38,9 +38,11 @@
#include "ED_select_utils.h"
#include "ED_view3d.h"
+#include "GPU_framebuffer.h"
#include "GPU_matrix.h"
#include "GPU_select.h"
#include "GPU_state.h"
+#include "GPU_viewport.h"
#include "MEM_guardedalloc.h"
@@ -505,8 +507,7 @@ void WM_gizmomap_draw(wmGizmoMap *gzmap,
static void gizmo_draw_select_3d_loop(const bContext *C,
wmGizmo **visible_gizmos,
- const int visible_gizmos_len,
- bool *r_use_select_bias)
+ const int visible_gizmos_len)
{
/* TODO(campbell): this depends on depth buffer being written to,
@@ -542,10 +543,6 @@ static void gizmo_draw_select_3d_loop(const bContext *C,
is_depth_skip_prev = is_depth_skip;
}
- if (gz->select_bias != 0.0) {
- *r_use_select_bias = true;
- }
-
/* pass the selection id shifted by 8 bits. Last 8 bits are used for selected gizmo part id */
gz->type->draw_select(C, gz, select_id << 8);
@@ -563,7 +560,10 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
const int visible_gizmos_len,
const bContext *C,
const int co[2],
- const int hotspot)
+ const int hotspot,
+ const bool use_depth_test,
+ const bool has_3d_select_bias,
+ int *r_hits)
{
const wmWindowManager *wm = CTX_wm_manager(C);
ScrArea *area = CTX_wm_area(C);
@@ -572,35 +572,81 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
rcti rect;
/* Almost certainly overkill, but allow for many custom gizmos. */
- uint buffer[MAXPICKBUF];
+ GPUSelectResult buffer[MAXPICKELEMS];
short hits;
BLI_rcti_init_pt_radius(&rect, co, hotspot);
- ED_view3d_draw_setup_view(
- wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect);
+ /* The selection mode is assigned for the following reasons:
+ *
+ * - #GPU_SELECT_ALL: Use it to check if there is anything at the cursor location
+ * (only ever runs once).
+ * - #GPU_SELECT_PICK_NEAREST: Use if there are more than 1 item at the cursor location,
+ * pick the nearest one.
+ * - #GPU_SELECT_PICK_ALL: Use for the same purpose as #GPU_SELECT_PICK_NEAREST
+ * when the selection depths need to re-ordered based on a bias.
+ * */
+ const eGPUSelectMode gpu_select_mode =
+ (use_depth_test ? (has_3d_select_bias ?
+ /* Using select bias means the depths need to be
+ * re-calculated based on the bias to pick the best. */
+ GPU_SELECT_PICK_ALL :
+ /* No bias, just pick the closest. */
+ GPU_SELECT_PICK_NEAREST) :
+ /* Fast-path (occlusion queries). */
+ GPU_SELECT_ALL);
+
+ /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is
+ * performed before the viewport is fully initialized (region->draw_buffer = NULL).
+ * When this is the case we should not use depth testing. */
+ GPUViewport *gpu_viewport = WM_draw_region_get_viewport(region);
+ if (use_depth_test && gpu_viewport == NULL) {
+ return -1;
+ }
- bool use_select_bias = false;
+ if (GPU_select_is_cached()) {
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0);
+ GPU_select_cache_load_id();
+ hits = GPU_select_end();
+ }
+ else {
+ /* TODO: waiting for the GPU in the middle of the event loop for every
+ * mouse move is bad for performance, we need to find a solution to not
+ * use the GPU or draw something once. (see T61474) */
+
+ ED_view3d_draw_setup_view(
+ wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect);
+
+ /* There is no need to bind to the depth buffer outside this function
+ * because all future passes the will use the cached depths. */
+ GPUFrameBuffer *depth_read_fb = NULL;
+ if (use_depth_test) {
+ GPUTexture *depth_tx = GPU_viewport_depth_texture(gpu_viewport);
+ GPU_framebuffer_ensure_config(&depth_read_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(depth_tx),
+ GPU_ATTACHMENT_NONE,
+ });
+ GPU_framebuffer_bind(depth_read_fb);
+ }
- /* TODO: waiting for the GPU in the middle of the event loop for every
- * mouse move is bad for performance, we need to find a solution to not
- * use the GPU or draw something once. (see T61474) */
- GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
- /* do the drawing */
- gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias);
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0);
+ gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len);
+ hits = GPU_select_end();
- hits = GPU_select_end();
+ if (use_depth_test) {
+ GPU_framebuffer_restore();
+ GPU_framebuffer_free(depth_read_fb);
+ }
- if (hits > 0) {
- GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
- gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias);
- GPU_select_end();
+ ED_view3d_draw_setup_view(
+ wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL);
}
- ED_view3d_draw_setup_view(
- wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL);
+ /* When selection bias is needed, this function will run again with `use_depth_test` enabled. */
+ int hit_found = -1;
- if (use_select_bias && (hits > 1)) {
+ if (has_3d_select_bias && use_depth_test && (hits > 1)) {
float co_direction[3];
float co_screen[3] = {co[0], co[1], 0.0f};
ED_view3d_win_to_vector(region, (float[2]){UNPACK2(co)}, co_direction);
@@ -611,15 +657,14 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin);
- uint *buf_iter = buffer;
- int hit_found = -1;
+ GPUSelectResult *buf_iter = buffer;
float dot_best = FLT_MAX;
- for (int i = 0; i < hits; i++, buf_iter += 4) {
- BLI_assert(buf_iter[3] != -1);
- wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8];
+ for (int i = 0; i < hits; i++, buf_iter++) {
+ BLI_assert(buf_iter->id != -1);
+ wmGizmo *gz = visible_gizmos[buf_iter->id >> 8];
float co_3d[3];
- co_screen[2] = int_as_float(buf_iter[1]);
+ co_screen[2] = int_as_float(buf_iter->depth);
GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d);
float select_bias = gz->select_bias;
if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) {
@@ -629,14 +674,19 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
const float dot_test = dot_v3v3(co_3d, co_direction) - select_bias;
if (dot_best > dot_test) {
dot_best = dot_test;
- hit_found = buf_iter[3];
+ hit_found = buf_iter->id;
}
}
- return hit_found;
+ }
+ else {
+ const GPUSelectResult *hit_near = GPU_select_buffer_near(buffer, hits);
+ if (hit_near) {
+ hit_found = hit_near->id;
+ }
}
- const uint *hit_near = GPU_select_buffer_near(buffer, hits);
- return hit_near ? hit_near[3] : -1;
+ *r_hits = hits;
+ return hit_found;
}
/**
@@ -659,6 +709,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C,
/* Search for 3D gizmo's that use the 2D callback for checking intersections. */
bool has_3d = false;
+ bool has_3d_select_bias = false;
{
for (int select_id = 0; select_id < visible_gizmos_len; select_id++) {
wmGizmo *gz = visible_gizmos[select_id];
@@ -674,6 +725,9 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C,
}
else if (gz->type->draw_select != NULL) {
has_3d = true;
+ if (gz->select_bias != 0.0f) {
+ has_3d_select_bias = true;
+ }
}
}
}
@@ -681,17 +735,78 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C,
/* Search for 3D intersections if they're before 2D that have been found (if any).
* This way we always use the first hit. */
if (has_3d) {
+
+ /* NOTE(@campbellbarton): The selection logic here uses a fast-path that exits early
+ * where possible. This is important as this runs on cursor-motion in the 3D view-port.
+ *
+ * - First, don't use the depth buffer at all, use occlusion queries to detect any gizmos.
+ * If there are no gizmos or only one - early exit, otherwise.
+ *
+ * - Bind the depth buffer and and use selection picking logic.
+ * This is much slower than occlusion queries (since it's reading depths while drawing).
+ * When there is a single gizmo under the cursor (quite common), early exit, otherwise.
+ *
+ * - Perform another pass at a reduced size (see: `hotspot_radii`),
+ * since the result depths are cached this pass is practically free.
+ *
+ * Other notes:
+ *
+ * - If any of these passes fail, use the nearest result from the previous pass.
+ *
+ * - Drawing is only ever done twice.
+ */
+
+ /* Order largest to smallest so the first pass can be used as cache for
+ * later passes (when `use_depth_test == true`). */
const int hotspot_radii[] = {
- 3 * U.pixelsize,
- /* This runs on mouse move, careful doing too many tests! */
10 * U.pixelsize,
+ /* This runs on mouse move, careful doing too many tests! */
+ 3 * U.pixelsize,
};
+
+ /* Narrowing may assign zero to `hit`, allow falling back to the previous test. */
+ int hit_prev = -1;
+
+ bool use_depth_test = false;
+ bool use_depth_cache = false;
+
for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) {
- hit = gizmo_find_intersected_3d_intern(
- visible_gizmos, visible_gizmos_len_trim, C, co, hotspot_radii[i]);
- if (hit != -1) {
+
+ if (use_depth_test && (use_depth_cache == false)) {
+ GPU_select_cache_begin();
+ use_depth_cache = true;
+ }
+
+ int hit_count;
+ hit = gizmo_find_intersected_3d_intern(visible_gizmos,
+ visible_gizmos_len_trim,
+ C,
+ co,
+ hotspot_radii[i],
+ use_depth_test,
+ has_3d_select_bias,
+ &hit_count);
+ /* Only continue searching when there are multiple options to narrow down. */
+ if (hit_count < 2) {
break;
}
+
+ /* Fast path for simple case, one item or nothing. */
+ if (use_depth_test == false) {
+ /* Restart, using depth buffer (slower). */
+ use_depth_test = true;
+ i = -1;
+ }
+ hit_prev = hit;
+ }
+ /* Narrowing the search area may yield no hits,
+ * in this case fall back to the previous search. */
+ if (hit == -1) {
+ hit = hit_prev;
+ }
+
+ if (use_depth_cache) {
+ GPU_select_cache_end();
}
if (hit != -1) {
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 96cb66b44ea..3bc77db45ca 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -226,6 +226,30 @@ void wm_drags_exit(wmWindowManager *wm, wmWindow *win)
}
}
+static bContextStore *wm_drop_ui_context_create(const bContext *C)
+{
+ uiBut *active_but = UI_region_active_but_get(CTX_wm_region(C));
+ if (!active_but) {
+ return NULL;
+ }
+
+ bContextStore *but_context = UI_but_context_get(active_but);
+ if (!but_context) {
+ return NULL;
+ }
+
+ return CTX_store_copy(but_context);
+}
+
+static void wm_drop_ui_context_free(bContextStore **context_store)
+{
+ if (!*context_store) {
+ return;
+ }
+ CTX_store_free(*context_store);
+ *context_store = NULL;
+}
+
void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy)
{
drag->imb = imb;
@@ -259,6 +283,7 @@ void WM_drag_free(wmDrag *drag)
if (drag->flags & WM_DRAG_FREE_DATA) {
WM_drag_data_free(drag->type, drag->poin);
}
+ wm_drop_ui_context_free(&drag->drop_state.ui_context);
if (drag->drop_state.free_disabled_info) {
MEM_SAFE_FREE(drag->drop_state.disabled_info);
}
@@ -317,6 +342,10 @@ static wmDropBox *dropbox_active(bContext *C,
}
const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop);
+ if (drag->drop_state.ui_context) {
+ CTX_store_set(C, drag->drop_state.ui_context);
+ }
+
if (WM_operator_poll_context(C, drop->ot, opcontext)) {
return drop;
}
@@ -367,6 +396,10 @@ static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *even
return;
}
+ /* Update UI context, before polling so polls can query this context. */
+ wm_drop_ui_context_free(&drag->drop_state.ui_context);
+ drag->drop_state.ui_context = wm_drop_ui_context_create(C);
+
wmDropBox *drop_prev = drag->drop_state.active_dropbox;
wmDropBox *drop = wm_dropbox_active(C, drag, event);
if (drop != drop_prev) {
@@ -381,11 +414,20 @@ static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *even
drag->drop_state.area_from = drop ? CTX_wm_area(C) : NULL;
drag->drop_state.region_from = drop ? CTX_wm_region(C) : NULL;
}
+
+ if (!drag->drop_state.active_dropbox) {
+ wm_drop_ui_context_free(&drag->drop_state.ui_context);
+ }
}
void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop)
{
const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop);
+
+ if (drag->drop_state.ui_context) {
+ CTX_store_set(C, drag->drop_state.ui_context);
+ }
+
/* Optionally copy drag information to operator properties. Don't call it if the
* operator fails anyway, it might do more than just set properties (e.g.
* typically import an asset). */
@@ -396,6 +438,11 @@ void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop)
wm_drags_exit(CTX_wm_manager(C), CTX_wm_window(C));
}
+void wm_drop_end(bContext *C, wmDrag *UNUSED(drag), wmDropBox *UNUSED(drop))
+{
+ CTX_store_set(C, NULL);
+}
+
void wm_drags_check_ops(bContext *C, const wmEvent *event)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -897,6 +944,7 @@ void wm_drags_draw(bContext *C, wmWindow *win)
if (drag->drop_state.active_dropbox) {
CTX_wm_area_set(C, drag->drop_state.area_from);
CTX_wm_region_set(C, drag->drop_state.region_from);
+ CTX_store_set(C, drag->drop_state.ui_context);
/* Drawing should be allowed to assume the context from handling and polling (that's why we
* restore it above). */
@@ -915,4 +963,5 @@ void wm_drags_draw(bContext *C, wmWindow *win)
GPU_blend(GPU_BLEND_NONE);
CTX_wm_area_set(C, NULL);
CTX_wm_region_set(C, NULL);
+ CTX_store_set(C, NULL);
}
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 28d8413fe0b..5210b315af6 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -51,6 +51,7 @@
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
+#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -312,28 +313,39 @@ void WM_main_remove_notifier_reference(const void *reference)
}
}
-void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
+static void wm_main_remap_assetlist(ID *old_id, ID *new_id, void *UNUSED(user_data))
+{
+ ED_assetlist_storage_id_remap(old_id, new_id);
+}
+
+static void wm_main_remap_msgbus_notify(ID *old_id, ID *new_id, void *user_data)
+{
+ struct wmMsgBus *mbus = user_data;
+ if (new_id != NULL) {
+ WM_msg_id_update(mbus, old_id, new_id);
+ }
+ else {
+ WM_msg_id_remove(mbus, old_id);
+ }
+}
+
+void WM_main_remap_editor_id_reference(const struct IDRemapper *mappings)
{
Main *bmain = G_MAIN;
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
- ED_spacedata_id_remap(area, sl, old_id, new_id);
+ ED_spacedata_id_remap(area, sl, mappings);
}
}
}
- ED_assetlist_storage_id_remap(old_id, new_id);
+
+ BKE_id_remapper_iter(mappings, wm_main_remap_assetlist, NULL);
wmWindowManager *wm = bmain->wm.first;
if (wm && wm->message_bus) {
- struct wmMsgBus *mbus = wm->message_bus;
- if (new_id != NULL) {
- WM_msg_id_update(mbus, old_id, new_id);
- }
- else {
- WM_msg_id_remove(mbus, old_id);
- }
+ BKE_id_remapper_iter(mappings, wm_main_remap_msgbus_notify, wm->message_bus);
}
}
@@ -633,6 +645,20 @@ static int wm_event_always_pass(const wmEvent *event)
return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
}
+/**
+ * Debug only sanity check for the return value of event handlers. Checks that "always pass" events
+ * don't cause non-passing handler return values, and thus actually pass.
+ *
+ * Can't be executed if the handler just loaded a file (typically identified by `CTX_wm_window(C)`
+ * returning `NULL`), because the event will have been freed then.
+ */
+BLI_INLINE void wm_event_handler_return_value_check(const wmEvent *event, const int action)
+{
+ BLI_assert_msg(!wm_event_always_pass(event) || (action != WM_HANDLER_BREAK),
+ "Return value for events that should always pass should never be BREAK.");
+ UNUSED_VARS_NDEBUG(event, action);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1055,7 +1081,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
}
}
- /* XXX(mont29) Disabled the repeat check to address part 2 of T31840.
+ /* XXX(@mont29): Disabled the repeat check to address part 2 of T31840.
* Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason
* why this was needed, but worth to note it in case something turns bad. */
if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */) {
@@ -2953,9 +2979,9 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
wmWindowManager *wm = CTX_wm_manager(C);
int action = WM_HANDLER_CONTINUE;
- int always_pass;
if (handlers == NULL) {
+ wm_event_handler_return_value_check(event, action);
return action;
}
@@ -2976,7 +3002,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
}
else if (handler_base->poll == NULL || handler_base->poll(CTX_wm_region(C), event)) {
/* In advance to avoid access to freed event on window close. */
- always_pass = wm_event_always_pass(event);
+ const int always_pass = wm_event_always_pass(event);
/* Modal+blocking handler_base. */
if (handler_base->flag & WM_HANDLER_BLOCKING) {
@@ -3050,6 +3076,8 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
event->customdata = NULL;
event->custom = 0;
+ wm_drop_end(C, drag, drop);
+
/* XXX fileread case. */
if (CTX_wm_window(C) == NULL) {
return action;
@@ -3118,6 +3146,10 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
wm_cursor_arrow_move(CTX_wm_window(C), event);
}
+ /* Do some extra sanity checking before returning the action. */
+ if (CTX_wm_window(C) != NULL) {
+ wm_event_handler_return_value_check(event, action);
+ }
return action;
}
@@ -3245,6 +3277,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
}
}
+ wm_event_handler_return_value_check(event, action);
return action;
}
@@ -3267,14 +3300,6 @@ static bool wm_event_inside_rect(const wmEvent *event, const rcti *rect)
return false;
}
-static bool wm_event_inside_region(const wmEvent *event, const ARegion *region)
-{
- if (wm_event_always_pass(event)) {
- return true;
- }
- return ED_region_contains_xy(region, event->xy);
-}
-
static ScrArea *area_event_inside(bContext *C, const int xy[2])
{
wmWindow *win = CTX_wm_window(C);
@@ -3488,6 +3513,55 @@ static void wm_event_handle_xrevent(bContext *C,
}
#endif /* WITH_XR_OPENXR */
+static int wm_event_do_region_handlers(bContext *C, wmEvent *event, ARegion *region)
+{
+ CTX_wm_region_set(C, region);
+
+ /* Call even on non mouse events, since the */
+ wm_region_mouse_co(C, event);
+
+ const wmWindowManager *wm = CTX_wm_manager(C);
+ if (!BLI_listbase_is_empty(&wm->drags)) {
+ /* Does polls for drop regions and checks #uiButs. */
+ /* Need to be here to make sure region context is true. */
+ if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) {
+ wm_drags_check_ops(C, event);
+ }
+ }
+
+ return wm_handlers_do(C, event, &region->handlers);
+}
+
+/**
+ * Send event to region handlers in \a area.
+ *
+ * Two cases:
+ * 1) Always pass events (#wm_event_always_pass()) are sent to all regions.
+ * 2) Event is passed to the region visually under the cursor (#ED_area_find_region_xy_visual()).
+ */
+static int wm_event_do_handlers_area_regions(bContext *C, wmEvent *event, ScrArea *area)
+{
+ /* Case 1. */
+ if (wm_event_always_pass(event)) {
+ int action = WM_HANDLER_CONTINUE;
+
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ action |= wm_event_do_region_handlers(C, event, region);
+ }
+
+ wm_event_handler_return_value_check(event, action);
+ return action;
+ }
+
+ /* Case 2. */
+ ARegion *region_hovered = ED_area_find_region_xy_visual(area, RGN_TYPE_ANY, event->xy);
+ if (!region_hovered) {
+ return WM_HANDLER_CONTINUE;
+ }
+
+ return wm_event_do_region_handlers(C, event, region_hovered);
+}
+
void wm_event_do_handlers(bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -3517,7 +3591,7 @@ void wm_event_do_handlers(bContext *C)
if (scene_eval != NULL) {
const int is_playing_sound = BKE_sound_scene_playing(scene_eval);
- if (scene_eval->id.recalc & ID_RECALC_AUDIO_SEEK) {
+ if (scene_eval->id.recalc & ID_RECALC_FRAME_CHANGE) {
/* Ignore seek here, the audio will be updated to the scene frame after jump during next
* dependency graph update. */
}
@@ -3670,36 +3744,12 @@ void wm_event_do_handlers(bContext *C)
if (wm_event_inside_rect(event, &area->totrct)) {
CTX_wm_area_set(C, area);
- if ((action & WM_HANDLER_BREAK) == 0) {
- LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (wm_event_inside_region(event, region)) {
-
- CTX_wm_region_set(C, region);
+ action |= wm_event_do_handlers_area_regions(C, event, area);
- /* Call even on non mouse events, since the */
- wm_region_mouse_co(C, event);
-
- if (!BLI_listbase_is_empty(&wm->drags)) {
- /* Does polls for drop regions and checks #uiButs. */
- /* Need to be here to make sure region context is true. */
- if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) {
- wm_drags_check_ops(C, event);
- }
- }
-
- action |= wm_handlers_do(C, event, &region->handlers);
-
- /* Fileread case (python), T29489. */
- if (CTX_wm_window(C) == NULL) {
- wm_event_free_and_remove_from_queue_if_valid(event);
- return;
- }
-
- if (action & WM_HANDLER_BREAK) {
- break;
- }
- }
- }
+ /* Fileread case (python), T29489. */
+ if (CTX_wm_window(C) == NULL) {
+ wm_event_free_and_remove_from_queue_if_valid(event);
+ return;
}
CTX_wm_region_set(C, NULL);
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 1478712c3cd..9b9aa37a251 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -874,18 +874,16 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports)
duration_lib_override_recursive_resync_seconds);
}
- if (bf_reports->count.linked_proxies != 0 ||
- bf_reports->count.proxies_to_lib_overrides_success != 0 ||
+ if (bf_reports->count.proxies_to_lib_overrides_success != 0 ||
bf_reports->count.proxies_to_lib_overrides_failures != 0) {
- BKE_reportf(bf_reports->reports,
- RPT_WARNING,
- "Proxies are deprecated (%d proxies were automatically converted to library "
- "overrides, %d proxies could not be converted and %d linked proxies were kept "
- "untouched). If you need to keep proxies for the time being, please disable the "
- "`Proxy to Override Auto Conversion` in Experimental user preferences",
- bf_reports->count.proxies_to_lib_overrides_success,
- bf_reports->count.proxies_to_lib_overrides_failures,
- bf_reports->count.linked_proxies);
+ BKE_reportf(
+ bf_reports->reports,
+ RPT_WARNING,
+ "Proxies have been removed from Blender (%d proxies were automatically converted "
+ "to library overrides, %d proxies could not be converted and were cleared). "
+ "Please also consider re-saving any library .blend file with the newest Blender version.",
+ bf_reports->count.proxies_to_lib_overrides_success,
+ bf_reports->count.proxies_to_lib_overrides_failures);
}
if (bf_reports->count.sequence_strips_skipped != 0) {
@@ -2706,7 +2704,7 @@ static char *wm_open_mainfile_description(struct bContext *UNUSED(C),
BLI_stat_t stats;
if (BLI_stat(path, &stats) == -1) {
- return BLI_sprintfN("%s\n\n%s", path, N_("File Not Found"));
+ return BLI_sprintfN("%s\n\n%s", path, TIP_("File Not Found"));
}
/* Date. */
@@ -2716,7 +2714,7 @@ static char *wm_open_mainfile_description(struct bContext *UNUSED(C),
BLI_filelist_entry_datetime_to_string(
NULL, (int64_t)stats.st_mtime, false, time_st, date_st, &is_today, &is_yesterday);
if (is_today || is_yesterday) {
- BLI_strncpy(date_st, is_today ? N_("Today") : N_("Yesterday"), sizeof(date_st));
+ BLI_strncpy(date_st, is_today ? TIP_("Today") : TIP_("Yesterday"), sizeof(date_st));
}
/* Size. */
@@ -2724,7 +2722,7 @@ static char *wm_open_mainfile_description(struct bContext *UNUSED(C),
BLI_filelist_entry_size_to_string(NULL, (uint64_t)stats.st_size, false, size_str);
return BLI_sprintfN(
- "%s\n\n%s: %s %s\n%s: %s", path, N_("Modified"), date_st, time_st, N_("Size"), size_str);
+ "%s\n\n%s: %s %s\n%s: %s", path, TIP_("Modified"), date_st, time_st, TIP_("Size"), size_str);
}
/* currently fits in a pointer */
@@ -3171,8 +3169,8 @@ static char *wm_save_as_mainfile_get_description(bContext *UNUSED(C),
PointerRNA *ptr)
{
if (RNA_boolean_get(ptr, "copy")) {
- return BLI_strdup(
- "Save the current file in the desired location but do not make the saved file active");
+ return BLI_strdup(TIP_(
+ "Save the current file in the desired location but do not make the saved file active"));
}
return NULL;
}
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 957ec7d800d..cff4287a2d0 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -252,7 +252,7 @@ void WM_init(bContext *C, int argc, const char **argv)
BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove);
BKE_region_callback_refresh_tag_gizmomap_set(WM_gizmomap_tag_refresh);
BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference);
- BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap);
+ BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap_single);
DEG_editors_set_update_cb(ED_render_id_flush_update, ED_render_scene_update);
ED_spacetypes_init();
@@ -356,10 +356,10 @@ void WM_init(bContext *C, int argc, const char **argv)
if (!G.background) {
if (wm_start_with_console) {
- GHOST_toggleConsole(1);
+ setConsoleWindowState(GHOST_kConsoleWindowStateShow);
}
else {
- GHOST_toggleConsole(3);
+ setConsoleWindowState(GHOST_kConsoleWindowStateHideForNonConsoleLaunch);
}
}
@@ -594,9 +594,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
DRW_opengl_context_destroy();
}
-#ifdef WITH_INTERNATIONAL
BLT_lang_free();
-#endif
ANIM_keyingset_infos_exit();
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 6c9b0af5186..2c82d8f5b6b 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -2046,7 +2046,7 @@ static void WM_OT_quit_blender(wmOperatorType *ot)
static int wm_console_toggle_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
{
- GHOST_toggleConsole(2);
+ setConsoleWindowState(GHOST_kConsoleWindowStateToggle);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index a1854a8ed86..1d9535e5102 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -14,8 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2007 Blender Foundation but based
- * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV
- * All rights reserved.
+ * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV. All rights reserved.
*/
/** \file
@@ -1483,7 +1482,11 @@ static bool wm_window_timer(const bContext *C)
wt->delta = time - wt->ltime;
wt->duration += wt->delta;
wt->ltime = time;
- wt->ntime = wt->stime + wt->timestep * ceil(wt->duration / wt->timestep);
+
+ wt->ntime = wt->stime;
+ if (wt->timestep != 0.0f) {
+ wt->ntime += wt->timestep * ceil(wt->duration / wt->timestep);
+ }
if (wt->event_type == TIMERJOBS) {
wm_jobs_timer(wm, wt);
@@ -1596,6 +1599,7 @@ void WM_event_timer_sleep(wmWindowManager *wm,
wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
{
wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
+ BLI_assert(timestep >= 0.0f);
wt->event_type = event_type;
wt->ltime = PIL_check_seconds_timer();
@@ -1615,6 +1619,7 @@ wmTimer *WM_event_add_timer_notifier(wmWindowManager *wm,
double timestep)
{
wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
+ BLI_assert(timestep >= 0.0f);
wt->event_type = TIMERNOTIFIER;
wt->ltime = PIL_check_seconds_timer();
diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index 925be8ab183..252ff228bc1 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -199,6 +199,7 @@ void wm_dropbox_free(void);
*/
void wm_drags_exit(wmWindowManager *wm, wmWindow *win);
void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop);
+void wm_drop_end(bContext *C, wmDrag *drag, wmDropBox *drop);
/**
* Called in inner handler loop, region context.
*/
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index 4ed1b7b9d0d..6fcaad92661 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -29,7 +29,7 @@
extern "C" {
#endif
-/* customdata type */
+/** #wmEvent.customdata type */
enum {
EVT_DATA_TIMER = 2,
EVT_DATA_DRAGDROP = 3,
@@ -37,7 +37,11 @@ enum {
EVT_DATA_XR = 5,
};
-/* tablet active, matches GHOST_TTabletMode */
+/**
+ * #wmTabletData.active tablet active, matches #GHOST_TTabletMode.
+ *
+ * Typically access via `event->tablet.active`.
+ */
enum {
EVT_TABLET_NONE = 0,
EVT_TABLET_STYLUS = 1,
@@ -56,7 +60,7 @@ enum {
/* ********** Start of Input devices. ********** */
- /* MOUSE : 0x000x, 0x001x */
+ /* MOUSE: 0x000x, 0x001x */
LEFTMOUSE = 0x0001,
MIDDLEMOUSE = 0x0002,
RIGHTMOUSE = 0x0003,
@@ -67,7 +71,7 @@ enum {
/* More mouse buttons - can't use 9 and 10 here (wheel) */
BUTTON6MOUSE = 0x0012,
BUTTON7MOUSE = 0x0013,
- /* Extra trackpad gestures */
+ /* Extra track-pad gestures. */
MOUSEPAN = 0x000e,
MOUSEZOOM = 0x000f,
MOUSEROTATE = 0x0010,
@@ -95,10 +99,14 @@ enum {
TABLET_STYLUS = 0x001a,
TABLET_ERASER = 0x001b,
- /* *** Start of keyboard codes. *** */
+/* *** Start of keyboard codes. *** */
+
+/* Minimum keyboard value (inclusive). */
+#define _EVT_KEYBOARD_MIN 0x0020
/* Standard keyboard.
- * From 0x0020 to 0x00ff, and 0x012c to 0x0143 for function keys! */
+ * - 0x0020 to 0x00ff [#_EVT_KEYBOARD_MIN to #_EVT_KEYBOARD_MAX] inclusive - for keys.
+ * - 0x012c to 0x0143 [#EVT_F1KEY to #EVT_F24KEY] inclusive - for function keys. */
EVT_ZEROKEY = 0x0030, /* '0' (48). */
EVT_ONEKEY = 0x0031, /* '1' (49). */
@@ -210,6 +218,12 @@ enum {
EVT_LEFTBRACKETKEY = 0x00eb, /* 235 */
EVT_RIGHTBRACKETKEY = 0x00ec, /* 236 */
+/* Maximum keyboard value (inclusive). */
+#define _EVT_KEYBOARD_MAX 0x00ff /* 255 */
+
+ /* WARNING: 0x010x are used for internal events
+ * (but are still stored in the key-map). */
+
EVT_F1KEY = 0x012c, /* 300 */
EVT_F2KEY = 0x012d, /* 301 */
EVT_F3KEY = 0x012e, /* 302 */
@@ -237,60 +251,65 @@ enum {
/* *** End of keyboard codes. *** */
- /* NDOF (from SpaceNavigator & friends)
- * These should be kept in sync with GHOST_NDOFManager.h
+ /* NDOF (from "Space Navigator" & friends)
+ * These must be kept in sync with `GHOST_NDOFManager.h`.
* Ordering matters, exact values do not. */
+
NDOF_MOTION = 0x0190, /* 400 */
+
+#define _NDOF_MIN NDOF_MOTION
+
/* used internally, never sent */
NDOF_BUTTON_NONE = NDOF_MOTION,
/* these two are available from any 3Dconnexion device */
- NDOF_BUTTON_MENU,
- NDOF_BUTTON_FIT,
+
+ NDOF_BUTTON_MENU = 0x0191, /* 401 */
+ NDOF_BUTTON_FIT = 0x0192, /* 402 */
/* standard views */
- NDOF_BUTTON_TOP,
- NDOF_BUTTON_BOTTOM,
- NDOF_BUTTON_LEFT,
- NDOF_BUTTON_RIGHT,
- NDOF_BUTTON_FRONT,
- NDOF_BUTTON_BACK,
+ NDOF_BUTTON_TOP = 0x0193, /* 403 */
+ NDOF_BUTTON_BOTTOM = 0x0194, /* 404 */
+ NDOF_BUTTON_LEFT = 0x0195, /* 405 */
+ NDOF_BUTTON_RIGHT = 0x0196, /* 406 */
+ NDOF_BUTTON_FRONT = 0x0197, /* 407 */
+ NDOF_BUTTON_BACK = 0x0198, /* 408 */
/* more views */
- NDOF_BUTTON_ISO1,
- NDOF_BUTTON_ISO2,
+ NDOF_BUTTON_ISO1 = 0x0199, /* 409 */
+ NDOF_BUTTON_ISO2 = 0x019a, /* 410 */
/* 90 degree rotations */
- NDOF_BUTTON_ROLL_CW,
- NDOF_BUTTON_ROLL_CCW,
- NDOF_BUTTON_SPIN_CW,
- NDOF_BUTTON_SPIN_CCW,
- NDOF_BUTTON_TILT_CW,
- NDOF_BUTTON_TILT_CCW,
+ NDOF_BUTTON_ROLL_CW = 0x019b, /* 411 */
+ NDOF_BUTTON_ROLL_CCW = 0x019c, /* 412 */
+ NDOF_BUTTON_SPIN_CW = 0x019d, /* 413 */
+ NDOF_BUTTON_SPIN_CCW = 0x019e, /* 414 */
+ NDOF_BUTTON_TILT_CW = 0x019f, /* 415 */
+ NDOF_BUTTON_TILT_CCW = 0x01a0, /* 416 */
/* device control */
- NDOF_BUTTON_ROTATE,
- NDOF_BUTTON_PANZOOM,
- NDOF_BUTTON_DOMINANT,
- NDOF_BUTTON_PLUS,
- NDOF_BUTTON_MINUS,
+ NDOF_BUTTON_ROTATE = 0x01a1, /* 417 */
+ NDOF_BUTTON_PANZOOM = 0x01a2, /* 418 */
+ NDOF_BUTTON_DOMINANT = 0x01a3, /* 419 */
+ NDOF_BUTTON_PLUS = 0x01a4, /* 420 */
+ NDOF_BUTTON_MINUS = 0x01a5, /* 421 */
/* keyboard emulation */
- NDOF_BUTTON_ESC,
- NDOF_BUTTON_ALT,
- NDOF_BUTTON_SHIFT,
- NDOF_BUTTON_CTRL,
+ NDOF_BUTTON_ESC = 0x01a6, /* 422 */
+ NDOF_BUTTON_ALT = 0x01a7, /* 423 */
+ NDOF_BUTTON_SHIFT = 0x01a8, /* 424 */
+ NDOF_BUTTON_CTRL = 0x01a9, /* 425 */
/* general-purpose buttons */
- NDOF_BUTTON_1,
- NDOF_BUTTON_2,
- NDOF_BUTTON_3,
- NDOF_BUTTON_4,
- NDOF_BUTTON_5,
- NDOF_BUTTON_6,
- NDOF_BUTTON_7,
- NDOF_BUTTON_8,
- NDOF_BUTTON_9,
- NDOF_BUTTON_10,
+ NDOF_BUTTON_1 = 0x01aa, /* 426 */
+ NDOF_BUTTON_2 = 0x01ab, /* 427 */
+ NDOF_BUTTON_3 = 0x01ac, /* 428 */
+ NDOF_BUTTON_4 = 0x01ad, /* 429 */
+ NDOF_BUTTON_5 = 0x01ae, /* 430 */
+ NDOF_BUTTON_6 = 0x01af, /* 431 */
+ NDOF_BUTTON_7 = 0x01b0, /* 432 */
+ NDOF_BUTTON_8 = 0x01b1, /* 433 */
+ NDOF_BUTTON_9 = 0x01b2, /* 434 */
+ NDOF_BUTTON_10 = 0x01b3, /* 435 */
/* more general-purpose buttons */
- NDOF_BUTTON_A,
- NDOF_BUTTON_B,
- NDOF_BUTTON_C,
- /* the end */
- NDOF_LAST,
+ NDOF_BUTTON_A = 0x01b4, /* 436 */
+ NDOF_BUTTON_B = 0x01b5, /* 437 */
+ NDOF_BUTTON_C = 0x01b6, /* 438 */
+
+#define _NDOF_MAX NDOF_BUTTON_C
/* ********** End of Input devices. ********** */
@@ -318,13 +337,13 @@ enum {
EVT_ACTIONZONE_REGION = 0x5001, /* 20481 */
EVT_ACTIONZONE_FULLSCREEN = 0x5011, /* 20497 */
- /* NOTE: these values are saved in keymap files, do not change them but just add new ones */
+ /* NOTE: these values are saved in key-map files, do not change them but just add new ones. */
/* Tweak events:
* Sent as additional event with the mouse coordinates
* from where the initial click was placed. */
- /* tweak events for L M R mousebuttons */
+ /* Tweak events for L M R mouse-buttons. */
EVT_TWEAK_L = 0x5002, /* 20482 */
EVT_TWEAK_M = 0x5003, /* 20483 */
EVT_TWEAK_R = 0x5004, /* 20484 */
@@ -348,9 +367,11 @@ enum {
/* ********** End of Blender internal events. ********** */
};
-/* *********** wmEvent.type helpers. ********** */
+/* -------------------------------------------------------------------- */
+/** \name #wmEvent.type Helpers
+ * \{ */
-/* test whether the event is timer event */
+/** Test whether the event is timer event. */
#define ISTIMER(event_type) ((event_type) >= TIMER && (event_type) <= TIMERF)
/* for event checks */
@@ -359,22 +380,24 @@ enum {
// #define ISTEXTINPUT(event_type) ((event_type) >= ' ' && (event_type) <= 255)
/* NOTE: an alternative could be to check `event->utf8_buf`. */
-/* test whether the event is a key on the keyboard */
+/** Test whether the event is a key on the keyboard (including modifier keys). */
#define ISKEYBOARD(event_type) \
- (((event_type) >= 0x0020 && (event_type) <= 0x00ff) || \
- ((event_type) >= 0x012c && (event_type) <= 0x0143))
+ (((event_type) >= _EVT_KEYBOARD_MIN && (event_type) <= _EVT_KEYBOARD_MAX) || \
+ ((event_type) >= EVT_F1KEY && (event_type) <= EVT_F24KEY))
-/* test whether the event is a modifier key */
+/** Test whether the event is a modifier key. */
#define ISKEYMODIFIER(event_type) \
(((event_type) >= EVT_LEFTCTRLKEY && (event_type) <= EVT_LEFTSHIFTKEY) || \
(event_type) == EVT_OSKEY)
-/* test whether the event is a mouse button */
+/** Test whether the event is a mouse button. */
#define ISMOUSE(event_type) \
(((event_type) >= LEFTMOUSE && (event_type) <= BUTTON7MOUSE) || (event_type) == MOUSESMARTZOOM)
-
+/** Test whether the event is a mouse wheel. */
#define ISMOUSE_WHEEL(event_type) ((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE)
+/** Test whether the event is a mouse (track-pad) gesture. */
#define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSEROTATE)
+/** Test whether the event is a mouse button (excluding mouse-wheel). */
#define ISMOUSE_BUTTON(event_type) \
(ELEM(event_type, \
LEFTMOUSE, \
@@ -385,16 +408,16 @@ enum {
BUTTON6MOUSE, \
BUTTON7MOUSE))
-/* test whether the event is tweak event */
+/** Test whether the event is tweak event. */
#define ISTWEAK(event_type) ((event_type) >= EVT_TWEAK_L && (event_type) <= EVT_TWEAK_R)
-/* test whether the event is a NDOF event */
-#define ISNDOF(event_type) ((event_type) >= NDOF_MOTION && (event_type) < NDOF_LAST)
+/** Test whether the event is a NDOF event. */
+#define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX)
#define IS_EVENT_ACTIONZONE(event_type) \
ELEM(event_type, EVT_ACTIONZONE_AREA, EVT_ACTIONZONE_REGION, EVT_ACTIONZONE_FULLSCREEN)
-/* test whether event type is acceptable as hotkey, excluding modifiers */
+/** Test whether event type is acceptable as hotkey (excluding modifiers). */
#define ISHOTKEY(event_type) \
((ISKEYBOARD(event_type) || ISMOUSE(event_type) || ISNDOF(event_type)) && \
(ISKEYMODIFIER(event_type) == false))
@@ -409,23 +432,23 @@ enum {
#define IS_EVENT_MOD(...) VA_NARGS_CALL_OVERLOAD(_VA_IS_EVENT_MOD, __VA_ARGS__)
enum eEventType_Mask {
- /* ISKEYMODIFIER */
+ /** #ISKEYMODIFIER */
EVT_TYPE_MASK_KEYBOARD_MODIFIER = (1 << 0),
- /* ISKEYBOARD */
+ /** #ISKEYBOARD */
EVT_TYPE_MASK_KEYBOARD = (1 << 1),
- /* ISMOUSE_WHEEL */
+ /** #ISMOUSE_WHEEL */
EVT_TYPE_MASK_MOUSE_WHEEL = (1 << 2),
- /* ISMOUSE_BUTTON */
+ /** #ISMOUSE_BUTTON */
EVT_TYPE_MASK_MOUSE_GESTURE = (1 << 3),
- /* ISMOUSE_GESTURE */
+ /** #ISMOUSE_GESTURE */
EVT_TYPE_MASK_MOUSE_BUTTON = (1 << 4),
- /* ISMOUSE */
+ /** #ISMOUSE */
EVT_TYPE_MASK_MOUSE = (1 << 5),
- /* ISNDOF */
+ /** #ISNDOF */
EVT_TYPE_MASK_NDOF = (1 << 6),
- /* ISTWEAK */
+ /** #ISTWEAK */
EVT_TYPE_MASK_TWEAK = (1 << 7),
- /* IS_EVENT_ACTIONZONE */
+ /** #IS_EVENT_ACTIONZONE */
EVT_TYPE_MASK_ACTIONZONE = (1 << 8),
};
#define EVT_TYPE_MASK_ALL \
@@ -438,7 +461,11 @@ enum eEventType_Mask {
bool WM_event_type_mask_test(int event_type, enum eEventType_Mask mask);
-/* ********** wmEvent.val ********** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #wmEvent.val Values
+ * \{ */
/* Gestures */
/* NOTE: these values are saved in keymap files, do not change them but just add new ones */
@@ -505,6 +532,8 @@ enum {
GESTURE_MODAL_FLIP = 14,
};
+/** \} */
+
#ifdef __cplusplus
}
#endif
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index 0609b8fd792..4a399ca9823 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -685,7 +685,11 @@ if(UNIX AND NOT APPLE)
PATTERN "*.pyo" EXCLUDE # * any cache *
)
# On some platforms requests does have extra dependencies.
- set(_requests_deps "certifi" "chardet" "idna" "urllib3")
+ #
+ # Either 'chardet' or 'charset_normalizer" is used, depending on the
+ # version of Python. The code below silently skips the one that's not
+ # available, so we can just list both here.
+ set(_requests_deps "certifi" "chardet" "charset_normalizer" "idna" "urllib3")
foreach(_requests_dep ${_requests_deps})
if(EXISTS ${PYTHON_REQUESTS_PATH}/${_requests_dep})
install(
@@ -1078,6 +1082,13 @@ elseif(APPLE)
Blender.app/Contents/
)
+ if(WITH_BLENDER_THUMBNAILER)
+ install(
+ TARGETS blender-thumbnailer
+ DESTINATION Blender.app/Contents/MacOS/
+ )
+ endif()
+
if(WITH_OPENMP AND OPENMP_CUSTOM)
install(
FILES "${OpenMP_LIBRARY}"
@@ -1100,9 +1111,9 @@ elseif(APPLE)
${TARGETDIR_VER}/python/lib
)
- install(DIRECTORY ${LIBDIR}/python/bin
- DESTINATION ${TARGETDIR_VER}/python
- USE_SOURCE_PERMISSIONS
+ install(
+ PROGRAMS ${PYTHON_EXECUTABLE}
+ DESTINATION ${TARGETDIR_VER}/python/bin
)
# Needed for distutils/pip
diff --git a/source/creator/blender_launcher_win32.c b/source/creator/blender_launcher_win32.c
index 86b0f4f3b97..f19438ad907 100644
--- a/source/creator/blender_launcher_win32.c
+++ b/source/creator/blender_launcher_win32.c
@@ -79,7 +79,26 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
BOOL success = CreateProcess(
path, buffer, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &procInfo);
+ DWORD returnValue = success ? 0 : -1;
+
if (success) {
+ /* If blender-launcher is called with background command line flag,
+ * wait for the blender process to exit and return its return value. */
+ BOOL background = FALSE;
+ int argc = 0;
+ LPWSTR *argv = CommandLineToArgvW(pCmdLine, &argc);
+ for (int i = 0; i < argc; i++) {
+ if ((wcscmp(argv[i], L"-b") == 0) || (wcscmp(argv[i], L"--background") == 0)) {
+ background = TRUE;
+ break;
+ }
+ }
+
+ if (background) {
+ WaitForSingleObject(procInfo.hProcess, INFINITE);
+ GetExitCodeProcess(procInfo.hProcess, &returnValue);
+ }
+
/* Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer
* needed - MSDN. Closing the handles will NOT terminate the thread/process that we just
* started. */
@@ -88,5 +107,5 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
}
free(buffer);
- return success ? 0 : -1;
+ return returnValue;
}
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index d3cec093980..11bea595690 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -970,9 +970,6 @@ static const char arg_handle_debug_mode_generic_set_doc_xr_time[] =
static const char arg_handle_debug_mode_generic_set_doc_jobs[] =
"\n\t"
"Enable time profiling for background jobs.";
-static const char arg_handle_debug_mode_generic_set_doc_gpu[] =
- "\n\t"
- "Enable GPU debug context and information for OpenGL 4.3+.";
static const char arg_handle_debug_mode_generic_set_doc_depsgraph[] =
"\n\t"
"Enable all debug messages from dependency graph.";
@@ -1097,6 +1094,20 @@ static int arg_handle_debug_value_set(int argc, const char **argv, void *UNUSED(
return 0;
}
+static const char arg_handle_debug_gpu_set_doc[] =
+ "\n"
+ "\tEnable GPU debug context and information for OpenGL 4.3+.";
+static int arg_handle_debug_gpu_set(int UNUSED(argc),
+ const char **UNUSED(argv),
+ void *UNUSED(data))
+{
+ /* Also enable logging because that how gl errors are reported. */
+ const char *gpu_filter = "gpu.*";
+ CLG_type_filter_include(gpu_filter, strlen(gpu_filter));
+ G.debug |= G_DEBUG_GPU;
+ return 0;
+}
+
static const char arg_handle_debug_fpe_set_doc[] =
"\n\t"
"Enable floating-point exceptions.";
@@ -2155,8 +2166,8 @@ void main_args_setup(bContext *C, bArgs *ba)
"--debug-jobs",
CB_EX(arg_handle_debug_mode_generic_set, jobs),
(void *)G_DEBUG_JOBS);
- BLI_args_add(
- ba, NULL, "--debug-gpu", CB_EX(arg_handle_debug_mode_generic_set, gpu), (void *)G_DEBUG_GPU);
+ BLI_args_add(ba, NULL, "--debug-gpu", CB(arg_handle_debug_gpu_set), NULL);
+
BLI_args_add(ba,
NULL,
"--debug-depsgraph",
diff --git a/source/tools b/source/tools
-Subproject 26bc78162ec89f21453ce3ded7b999bc6649f32
+Subproject 515e67c1932bc06f24cb50b621265c2a6e8a25a
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index f3136d6c649..04fdb380da2 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -293,6 +293,13 @@ add_blender_test(
--testdir "${TEST_SRC_DIR}/animation"
)
+add_blender_test(
+ bl_rigging_symmetrize
+ --python ${CMAKE_CURRENT_LIST_DIR}/bl_rigging_symmetrize.py
+ --
+ --testdir "${TEST_SRC_DIR}/animation"
+)
+
# ------------------------------------------------------------------------------
# IO TESTS
@@ -324,7 +331,10 @@ add_blender_test(
)
endif()
+if(FALSE)
# OBJ Export tests
+# Disabled because new C++ Obj exporter has C++ tests.
+
add_blender_test(
export_obj_cube
${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend
@@ -345,8 +355,6 @@ add_blender_test(
--md5=a733ae4fa4a591ea9b0912da3af042de --md5_method=FILE
)
-# disabled until updated & working
-if(FALSE)
add_blender_test(
export_obj_all_objects
${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend
@@ -692,7 +700,7 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS)
foreach(render_test ${_cycles_render_tests})
# Enable just one simple test for Metal until more tests are passing.
- if ((NOT (_cycles_device MATCHES "METAL")) OR (render_test MATCHES "camera"))
+ if((NOT (_cycles_device MATCHES "METAL")) OR (render_test MATCHES "camera"))
add_python_test(
cycles_${render_test}_${_cycles_device_lower}
${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py
@@ -772,6 +780,7 @@ set(geo_node_tests
geometry
mesh_primitives
mesh
+ mesh/extrude
points
utilities
vector
diff --git a/tests/python/bl_keymap_validate.py b/tests/python/bl_keymap_validate.py
index ebc35c1178c..705a026e441 100644
--- a/tests/python/bl_keymap_validate.py
+++ b/tests/python/bl_keymap_validate.py
@@ -162,7 +162,7 @@ def keymap_data_clean(keyconfig_data: typing.List, *, relaxed: bool) -> None:
item_prop.pop("properties")
# Needed so: `{"properties": ()}` matches `None` as there is no meaningful difference.
- # Wruting `None` makes the most sense when explicitly written, however generated properties
+ # Writing `None` makes the most sense when explicitly written, however generated properties
# might be empty and it's not worth adding checks in the generation logic to use `None`
# just to satisfy this check.
if not item_prop:
diff --git a/tests/python/bl_rigging_symmetrize.py b/tests/python/bl_rigging_symmetrize.py
new file mode 100644
index 00000000000..b47ace7f3f1
--- /dev/null
+++ b/tests/python/bl_rigging_symmetrize.py
@@ -0,0 +1,244 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+"""
+blender -b -noaudio --factory-startup --python tests/python/bl_rigging_symmetrize.py -- --testdir /path/to/lib/tests/animation
+"""
+
+import pathlib
+import sys
+import unittest
+
+import bpy
+
+
+def check_loc_rot_scale(self, bone, exp_bone):
+ # Check if posistions are the same
+ self.assertEqualVector(
+ bone.head, exp_bone.head, "Head position", bone.name)
+ self.assertEqualVector(
+ bone.tail, exp_bone.tail, "Tail position", bone.name)
+
+ # Scale
+ self.assertEqualVector(
+ bone.scale, exp_bone.scale, "Scale", bone.name)
+
+ # Rotation
+ rot_mode = exp_bone.rotation_mode
+ self.assertEqual(bone.rotation_mode, rot_mode, "Rotations mode does not match on bone %s" % (bone.name))
+
+ if rot_mode == 'QUATERNION':
+ self.assertEqualVector(
+ bone.rotation_quaternion, exp_bone.rotation_quaternion, "Quaternion rotation", bone.name)
+ elif rot_mode == 'AXIS_ANGLE':
+ self.assertEqualVector(
+ bone.axis_angle, exp_bone.axis_angle, "Axis Angle rotation", bone.name)
+ else:
+ # Euler rotation
+ self.assertEqualVector(
+ bone.rotation_euler, exp_bone.rotation_euler, "Euler rotation", bone.name)
+
+
+def check_parent(self, bone, exp_bone):
+ self.assertEqual(type(bone.parent), type(exp_bone.parent),
+ "Missmatching types in pose.bones[%s].parent" % (bone.name))
+ self.assertTrue(bone.parent is None or bone.parent.name == exp_bone.parent.name,
+ "Bone parent does not match on bone %s" % (bone.name))
+
+
+def check_bendy_bones(self, bone, exp_bone):
+ bone_variables = bone.bl_rna.properties.keys()
+
+ bendy_bone_variables = [
+ var for var in bone_variables if var.startswith("bbone_")]
+
+ for var in bendy_bone_variables:
+ value = getattr(bone, var)
+ exp_value = getattr(exp_bone, var)
+
+ self.assertEqual(type(value), type(exp_value),
+ "Missmatching types in pose.bones[%s].%s" % (bone.name, var))
+
+ if isinstance(value, str):
+ self.assertEqual(value, exp_value,
+ "Missmatching value in pose.bones[%s].%s" % (bone.name, var))
+ elif hasattr(value, "name"):
+ self.assertEqual(value.name, exp_value.name,
+ "Missmatching value in pose.bones[%s].%s" % (bone.name, var))
+ else:
+ self.assertAlmostEqual(value, exp_value,
+ "Missmatching value in pose.bones[%s].%s" % (bone.name, var))
+
+
+def check_ik(self, bone, exp_bone):
+ bone_variables = bone.bl_rna.properties.keys()
+ prefixes = ("ik_", "lock_ik", "use_ik")
+ ik_bone_variables = (
+ var for var in bone_variables
+ if var.startswith(prefixes)
+ )
+
+ for var in ik_bone_variables:
+ value = getattr(bone, var)
+ exp_value = getattr(exp_bone, var)
+ self.assertAlmostEqual(value, exp_value,
+ "Missmatching value in pose.bones[%s].%s" % (bone.name, var))
+
+
+def check_constraints(self, input_arm, expected_arm, bone, exp_bone):
+ const_len = len(bone.constraints)
+ expo_const_len = len(exp_bone.constraints)
+
+ self.assertEqual(const_len, expo_const_len,
+ "Constraints missmatch on bone %s" % (bone.name))
+
+ for exp_constraint in exp_bone.constraints:
+ const_name = exp_constraint.name
+ # Make sure that the constraint exists
+ self.assertTrue(const_name in bone.constraints,
+ "Bone %s is expected to contain constraint %s, but it does not." % (
+ bone.name, const_name))
+ constraint = bone.constraints[const_name]
+ const_variables = constraint.bl_rna.properties.keys()
+
+ for var in const_variables:
+
+ if var == "is_override_data":
+ # This variable is not used for local (non linked) data.
+ # For local object it is not initialized, so don't check this value.
+ continue
+
+ value = getattr(constraint, var)
+ exp_value = getattr(exp_constraint, var)
+
+ self.assertEqual(type(value), type(exp_value),
+ "Missmatching constraint value types in pose.bones[%s].constraints[%s].%s" % (
+ bone.name, const_name, var))
+
+ if isinstance(value, str):
+ self.assertEqual(value, exp_value,
+ "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % (
+ bone.name, const_name, var))
+ elif hasattr(value, "name"):
+ # Some constraints targets the armature itself, so the armature name should missmatch.
+ if value.name == input_arm.name and exp_value.name == expected_arm.name:
+ continue
+
+ self.assertEqual(value.name, exp_value.name,
+ "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % (
+ bone.name, const_name, var))
+
+ elif isinstance(value, bool):
+ self.assertEqual(value, exp_value,
+ "Missmatching constraint boolean in pose.bones[%s].constraints[%s].%s" % (
+ bone.name, const_name, var))
+ else:
+ self.assertAlmostEqual(value, exp_value,
+ "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % (
+ bone.name, const_name, var))
+
+
+class AbstractAnimationTest:
+ @classmethod
+ def setUpClass(cls):
+ cls.testdir = args.testdir
+
+ def setUp(self):
+ self.assertTrue(self.testdir.exists(),
+ 'Test dir %s should exist' % self.testdir)
+
+
+class ArmatureSymmetrizeTest(AbstractAnimationTest, unittest.TestCase):
+ def test_symmetrize_operator(self):
+ """Test that the symmetrize operator is working correctly."""
+ bpy.ops.wm.open_mainfile(filepath=str(
+ self.testdir / "symm_test.blend"))
+
+ # T81541 (D9214)
+ arm = bpy.data.objects['transform_const_rig']
+ expected_arm = bpy.data.objects['expected_transform_const_rig']
+ self.assertEqualSymmetrize(arm, expected_arm)
+
+ # T66751 (D6009)
+ arm = bpy.data.objects['dragon_rig']
+ expected_arm = bpy.data.objects['expected_dragon_rig']
+ self.assertEqualSymmetrize(arm, expected_arm)
+
+ def assertEqualSymmetrize(self, input_arm, expected_arm):
+
+ # Symmetrize our input armature
+ bpy.context.view_layer.objects.active = input_arm
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.armature.select_all(action='SELECT')
+ bpy.ops.armature.symmetrize()
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # Make sure that the bone count is the same
+ bone_len = len(input_arm.pose.bones)
+ expected_bone_len = len(expected_arm.pose.bones)
+ self.assertEqual(bone_len, expected_bone_len,
+ "Expected bone count to match")
+
+ for exp_bone in expected_arm.pose.bones:
+ bone_name = exp_bone.name
+ # Make sure that the bone exists
+ self.assertTrue(bone_name in input_arm.pose.bones,
+ "Armature is expected to contain bone %s, but it does not." % (bone_name))
+ bone = input_arm.pose.bones[bone_name]
+
+ # Loc Rot Scale
+ check_loc_rot_scale(self, bone, exp_bone)
+
+ # Parent settings
+ check_parent(self, bone, exp_bone)
+
+ # Bendy Bones
+ check_bendy_bones(self, bone, exp_bone)
+
+ # IK
+ check_ik(self, bone, exp_bone)
+
+ # Constraints
+ check_constraints(self, input_arm, expected_arm, bone, exp_bone)
+
+ def assertEqualVector(self, vec1, vec2, check_str, bone_name) -> None:
+ for idx, value in enumerate(vec1):
+ self.assertAlmostEqual(
+ value, vec2[idx], 3, "%s does not match with expected value on bone %s" % (check_str, bone_name))
+
+
+def main():
+ global args
+ import argparse
+
+ if '--' in sys.argv:
+ argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
+ else:
+ argv = sys.argv
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--testdir', required=True, type=pathlib.Path)
+ args, remaining = parser.parse_known_args(argv)
+
+ unittest.main(argv=remaining)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/python/collada/animation/test_animation_simple.py b/tests/python/collada/animation/test_animation_simple.py
index 6b18313c3cd..95c20cbcf31 100644
--- a/tests/python/collada/animation/test_animation_simple.py
+++ b/tests/python/collada/animation/test_animation_simple.py
@@ -16,10 +16,12 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
-#
-# Call as follows:
-# python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir PATH_TO_SVN/lib/tests/collada/mesh
-#
+
+"""
+Call as follows:
+python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir PATH_TO_SVN/lib/tests/collada/mesh
+"""
+
import sys
import bpy
import argparse
diff --git a/tests/python/collada/mesh/test_mesh_simple.py b/tests/python/collada/mesh/test_mesh_simple.py
index 5899d986a14..e48ddcd4477 100644
--- a/tests/python/collada/mesh/test_mesh_simple.py
+++ b/tests/python/collada/mesh/test_mesh_simple.py
@@ -16,10 +16,12 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
-#
-# Call as follows:
-# python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir PATH_TO_SVN/lib/tests/collada/mesh
-#
+
+"""
+Call as follows:
+python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir PATH_TO_SVN/lib/tests/collada/mesh
+"""
+
import sys
import bpy
import argparse
diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py
index 98b2cd4a200..b0938c06ecf 100644
--- a/tests/python/cycles_render_tests.py
+++ b/tests/python/cycles_render_tests.py
@@ -11,6 +11,15 @@ from pathlib import Path
# List of .blend files that are known to be failing and are not ready to be
# tested, or that only make sense on some devices. Accepts regular expressions.
+BLACKLIST_ALL = [
+ # Blacklisted due overlapping object differences between platforms.
+ "hair_geom_reflection.blend",
+ "hair_geom_transmission.blend",
+ "hair_instancer_uv.blend",
+ "principled_hair_directcoloring.blend",
+ "visibility_particles.blend",
+]
+
BLACKLIST_OSL = [
# OSL only supported on CPU.
'.*_osl.blend',
@@ -36,8 +45,12 @@ BLACKLIST_GPU = [
'hair_instancer_uv.blend',
'hair_length_info.blend',
'hair_particle_random.blend',
+ "hair_transmission.blend",
'principled_hair_.*.blend',
'transparent_shadow_hair.*.blend',
+ # Inconsistent handling of overlapping objects.
+ "T41143.blend",
+ "visibility_particles.blend",
]
@@ -96,7 +109,7 @@ def main():
output_dir = args.outdir[0]
device = args.device[0]
- blacklist = []
+ blacklist = BLACKLIST_ALL
if device != 'CPU':
blacklist += BLACKLIST_GPU
if device != 'CPU' or 'OSL' in args.blacklist:
diff --git a/tests/python/modules/global_report.py b/tests/python/modules/global_report.py
index 636ec61d471..89715e526fa 100755
--- a/tests/python/modules/global_report.py
+++ b/tests/python/modules/global_report.py
@@ -1,5 +1,5 @@
# Apache License, Version 2.0
-#
+
# Generate a HTML page that links to all test reports.
import glob
diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py
index 90f16dc80fb..d7b198b3203 100755
--- a/tests/python/modules/render_report.py
+++ b/tests/python/modules/render_report.py
@@ -1,7 +1,9 @@
# Apache License, Version 2.0
-#
-# Compare renders or screenshots against reference versions and generate
-# a HTML report showing the differences, for regression testing.
+
+"""
+Compare renders or screenshots against reference versions and generate
+a HTML report showing the differences, for regression testing.
+"""
import glob
import os