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:
authorStefan Werner <stefan.werner@tangent-animation.com>2021-08-02 10:28:54 +0300
committerStefan Werner <stefan.werner@tangent-animation.com>2021-08-02 10:28:54 +0300
commit34e8d79c3edbc58fd242cec0c1f2bed4e43855af (patch)
tree36c70e63515af2bd8ea840102493028faec37971
parent465fb31ed275618ec71e4925ab94bd4a9b077a12 (diff)
parent48722e8971133dbe14ecc6825a2451637df77eab (diff)
Merge branch 'master' into cycles_texture_cache
-rw-r--r--CMakeLists.txt37
-rw-r--r--build_files/build_environment/CMakeLists.txt6
-rw-r--r--build_files/build_environment/cmake/download.cmake1
-rw-r--r--build_files/build_environment/cmake/ffmpeg.cmake1
-rw-r--r--build_files/build_environment/cmake/flex.cmake28
-rw-r--r--build_files/build_environment/cmake/harvest.cmake1
-rw-r--r--build_files/build_environment/cmake/ispc.cmake9
-rw-r--r--build_files/build_environment/cmake/llvm.cmake6
-rw-r--r--build_files/build_environment/cmake/openimagedenoise.cmake1
-rw-r--r--build_files/build_environment/cmake/openmp.cmake3
-rw-r--r--build_files/build_environment/cmake/osl.cmake23
-rw-r--r--build_files/build_environment/cmake/versions.cmake83
-rwxr-xr-xbuild_files/build_environment/install_deps.sh56
-rw-r--r--build_files/build_environment/patches/oidn.diff10
-rw-r--r--build_files/build_environment/patches/openimageio.diff21
-rw-r--r--build_files/build_environment/patches/openmp.diff23
-rw-r--r--build_files/build_environment/patches/osl.diff76
-rwxr-xr-xbuild_files/cmake/cmake_netbeans_project.py2
-rw-r--r--build_files/cmake/config/blender_developer.cmake1
-rw-r--r--build_files/cmake/platform/platform_apple.cmake8
-rw-r--r--build_files/cmake/platform/platform_unix.cmake2
-rw-r--r--build_files/config/README.md9
-rw-r--r--build_files/config/pipeline_config.json2
-rw-r--r--build_files/config/pipeline_config.yaml70
-rwxr-xr-xdoc/manpage/blender.1.py2
-rw-r--r--doc/python_api/sphinx_doc_gen.py2
-rw-r--r--extern/glog/README.blender1
-rw-r--r--extern/glog/src/raw_logging.cc2
-rw-r--r--extern/glog/src/utilities.cc6
-rw-r--r--extern/rangetree/intern/generic_alloc_impl.h2
-rw-r--r--intern/atomic/intern/atomic_ops_unix.h6
-rw-r--r--intern/cycles/CMakeLists.txt6
-rw-r--r--intern/cycles/blender/addon/engine.py22
-rw-r--r--intern/cycles/blender/addon/properties.py25
-rw-r--r--intern/cycles/blender/addon/ui.py37
-rw-r--r--intern/cycles/blender/blender_mesh.cpp40
-rw-r--r--intern/cycles/blender/blender_python.cpp8
-rw-r--r--intern/cycles/blender/blender_sync.cpp24
-rw-r--r--intern/cycles/device/cuda/device_cuda_impl.cpp12
-rw-r--r--intern/cycles/device/device_cpu.cpp2
-rw-r--r--intern/cycles/device/device_optix.cpp12
-rw-r--r--intern/cycles/device/opencl/device_opencl_impl.cpp4
-rw-r--r--intern/cycles/kernel/CMakeLists.txt18
-rw-r--r--intern/cycles/kernel/bvh/bvh_traversal.h8
-rw-r--r--intern/cycles/kernel/bvh/bvh_types.h27
-rw-r--r--intern/cycles/kernel/closure/bssrdf.h6
-rw-r--r--intern/cycles/kernel/filter/filter_defines.h17
-rw-r--r--intern/cycles/kernel/kernel_accumulate.h13
-rw-r--r--intern/cycles/kernel/kernel_passes.h29
-rw-r--r--intern/cycles/kernel/kernel_path.h9
-rw-r--r--intern/cycles/kernel/kernel_types.h40
-rw-r--r--intern/cycles/kernel/shaders/node_voronoi_texture.osl10
-rw-r--r--intern/cycles/kernel/svm/svm_voronoi.h9
-rw-r--r--intern/cycles/render/attribute.cpp3
-rw-r--r--intern/cycles/render/buffers.cpp9
-rw-r--r--intern/cycles/render/colorspace.cpp2
-rw-r--r--intern/cycles/render/film.cpp29
-rw-r--r--intern/cycles/render/graph.h2
-rw-r--r--intern/cycles/render/osl.cpp2
-rw-r--r--intern/cycles/render/scene.h2
-rw-r--r--intern/cycles/util/util_avxb.h2
-rw-r--r--intern/cycles/util/util_avxi.h2
-rw-r--r--intern/cycles/util/util_logging.cpp6
-rw-r--r--intern/cycles/util/util_logging.h32
-rw-r--r--intern/cycles/util/util_math_fast.h10
-rw-r--r--intern/cycles/util/util_sseb.h2
-rw-r--r--intern/cycles/util/util_ssei.h2
-rw-r--r--intern/ghost/GHOST_C-api.h6
-rw-r--r--intern/ghost/GHOST_IContext.h4
-rw-r--r--intern/ghost/GHOST_ISystem.h4
-rw-r--r--intern/ghost/GHOST_IWindow.h2
-rw-r--r--intern/ghost/GHOST_Types.h10
-rw-r--r--intern/ghost/intern/GHOST_ContextCGL.mm17
-rw-r--r--intern/ghost/intern/GHOST_ContextEGL.cpp24
-rw-r--r--intern/ghost/intern/GHOST_ContextGLX.cpp4
-rw-r--r--intern/ghost/intern/GHOST_ContextWGL.cpp20
-rw-r--r--intern/ghost/intern/GHOST_DisplayManager.cpp10
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.cpp6
-rw-r--r--intern/ghost/intern/GHOST_EventDragnDrop.h2
-rw-r--r--intern/ghost/intern/GHOST_EventManager.cpp8
-rw-r--r--intern/ghost/intern/GHOST_ISystem.cpp2
-rw-r--r--intern/ghost/intern/GHOST_ImeWin32.cpp59
-rw-r--r--intern/ghost/intern/GHOST_ImeWin32.h18
-rw-r--r--intern/ghost/intern/GHOST_NDOFManager.cpp126
-rw-r--r--intern/ghost/intern/GHOST_NDOFManager.h95
-rw-r--r--intern/ghost/intern/GHOST_Rect.cpp12
-rw-r--r--intern/ghost/intern/GHOST_System.cpp16
-rw-r--r--intern/ghost/intern/GHOST_System.h4
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.h4
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm4
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.cpp31
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp27
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h12
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp30
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.h4
-rw-r--r--intern/ghost/intern/GHOST_TimerManager.cpp10
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.h12
-rw-r--r--intern/ghost/intern/GHOST_WindowManager.cpp2
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp6
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.h2
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h2
-rw-r--r--intern/ghost/intern/GHOST_XrSession.cpp49
-rw-r--r--intern/ghost/intern/GHOST_XrSession.h1
-rw-r--r--intern/guardedalloc/intern/mallocn.c2
-rw-r--r--intern/libmv/CMakeLists.txt64
-rw-r--r--intern/memutil/MEM_Allocator.h4
-rw-r--r--intern/memutil/MEM_CacheLimiter.h6
-rw-r--r--intern/numaapi/source/build_config.h13
-rw-r--r--intern/opensubdiv/CMakeLists.txt2
-rw-r--r--intern/openvdb/intern/openvdb_level_set.cc19
-rw-r--r--intern/openvdb/intern/openvdb_level_set.h6
-rw-r--r--intern/openvdb/openvdb_capi.cc12
-rw-r--r--intern/openvdb/openvdb_capi.h22
-rw-r--r--intern/quadriflow/quadriflow_capi.cpp10
-rw-r--r--intern/quadriflow/quadriflow_capi.hpp4
-rw-r--r--intern/rigidbody/RBI_api.h2
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/userdef/userdef_default.c2
-rw-r--r--release/datafiles/userdef/userdef_default_theme.c2
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/modules/bl_app_template_utils.py8
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils_cli.py2
-rw-r--r--release/scripts/modules/bpy_extras/asset_utils.py11
-rw-r--r--release/scripts/modules/nodeitems_utils.py2
-rw-r--r--release/scripts/modules/rna_manual_reference.py45
-rw-r--r--release/scripts/presets/keyconfig/Blender.py6
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py44
-rw-r--r--release/scripts/startup/bl_operators/assets.py82
-rw-r--r--release/scripts/startup/bl_operators/geometry_nodes.py5
-rw-r--r--release/scripts/startup/bl_operators/rigidbody.py21
-rw-r--r--release/scripts/startup/bl_operators/sequencer.py13
-rw-r--r--release/scripts/startup/bl_ui/__init__.py24
-rw-r--r--release/scripts/startup/bl_ui/properties_collection.py11
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py16
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_fluid.py4
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py27
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py21
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py32
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py5
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py2
-rw-r--r--release/scripts/startup/nodeitems_builtins.py2
-rw-r--r--release/scripts/templates_py/custom_nodes.py2
-rw-r--r--source/blender/blenfont/BLF_api.h2
-rw-r--r--source/blender/blenfont/intern/blf.c11
-rw-r--r--source/blender/blenfont/intern/blf_font.c10
-rw-r--r--source/blender/blenfont/intern/blf_internal.h2
-rw-r--r--source/blender/blenkernel/BKE_DerivedMesh.h2
-rw-r--r--source/blender/blenkernel/BKE_action.hh35
-rw-r--r--source/blender/blenkernel/BKE_animsys.h6
-rw-r--r--source/blender/blenkernel/BKE_appdir.h2
-rw-r--r--source/blender/blenkernel/BKE_armature.h17
-rw-r--r--source/blender/blenkernel/BKE_armature.hh48
-rw-r--r--source/blender/blenkernel/BKE_asset.h3
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h6
-rw-r--r--source/blender/blenkernel/BKE_cloth.h28
-rw-r--r--source/blender/blenkernel/BKE_context.h6
-rw-r--r--source/blender/blenkernel/BKE_curve.h1
-rw-r--r--source/blender/blenkernel/BKE_customdata.h2
-rw-r--r--source/blender/blenkernel/BKE_deform.h2
-rw-r--r--source/blender/blenkernel/BKE_editmesh.h2
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh4
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h10
-rw-r--r--source/blender/blenkernel/BKE_layer.h3
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h2
-rw-r--r--source/blender/blenkernel/BKE_main.h4
-rw-r--r--source/blender/blenkernel/BKE_main_idmap.h5
-rw-r--r--source/blender/blenkernel/BKE_mesh.h31
-rw-r--r--source/blender/blenkernel/BKE_mesh_iterators.h1
-rw-r--r--source/blender/blenkernel/BKE_mesh_mapping.h5
-rw-r--r--source/blender/blenkernel/BKE_mesh_remesh_voxel.h38
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.hh2
-rw-r--r--source/blender/blenkernel/BKE_mesh_types.h1
-rw-r--r--source/blender/blenkernel/BKE_node.h7
-rw-r--r--source/blender/blenkernel/BKE_object.h4
-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_spline.hh4
-rw-r--r--source/blender/blenkernel/CMakeLists.txt8
-rw-r--r--source/blender/blenkernel/intern/action_bones.cc48
-rw-r--r--source/blender/blenkernel/intern/action_mirror.c19
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c134
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c8
-rw-r--r--source/blender/blenkernel/intern/armature_pose.cc115
-rw-r--r--source/blender/blenkernel/intern/armature_selection.cc78
-rw-r--r--source/blender/blenkernel/intern/armature_test.cc81
-rw-r--r--source/blender/blenkernel/intern/asset.cc5
-rw-r--r--source/blender/blenkernel/intern/attribute.c74
-rw-r--r--source/blender/blenkernel/intern/blendfile.c3
-rw-r--r--source/blender/blenkernel/intern/boids.c79
-rw-r--r--source/blender/blenkernel/intern/cloth.c9
-rw-r--r--source/blender/blenkernel/intern/collection.c140
-rw-r--r--source/blender/blenkernel/intern/context.c30
-rw-r--r--source/blender/blenkernel/intern/curve.c22
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc16
-rw-r--r--source/blender/blenkernel/intern/displist.cc5
-rw-r--r--source/blender/blenkernel/intern/editmesh.c2
-rw-r--r--source/blender/blenkernel/intern/effect.c2
-rw-r--r--source/blender/blenkernel/intern/fcurve.c2
-rw-r--r--source/blender/blenkernel/intern/fluid.c3
-rw-r--r--source/blender/blenkernel/intern/fmodifier.c2
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc10
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc11
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc5
-rw-r--r--source/blender/blenkernel/intern/gpencil.c2
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc (renamed from source/blender/blenkernel/intern/gpencil_geom.c)495
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c4
-rw-r--r--source/blender/blenkernel/intern/icons.cc2
-rw-r--r--source/blender/blenkernel/intern/image_save.c4
-rw-r--r--source/blender/blenkernel/intern/layer.c483
-rw-r--r--source/blender/blenkernel/intern/lib_override.c22
-rw-r--r--source/blender/blenkernel/intern/main.c5
-rw-r--r--source/blender/blenkernel/intern/main_idmap.c91
-rw-r--r--source/blender/blenkernel/intern/mask_rasterize.c2
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.c2
-rw-r--r--source/blender/blenkernel/intern/mesh.c12
-rw-r--r--source/blender/blenkernel/intern/mesh_boolean_convert.cc24
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c124
-rw-r--r--source/blender/blenkernel/intern/mesh_iterators.c9
-rw-r--r--source/blender/blenkernel/intern/mesh_mapping.c4
-rw-r--r--source/blender/blenkernel/intern/mesh_normals.cc30
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.cc (renamed from source/blender/blenkernel/intern/mesh_remesh_voxel.c)441
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.cc23
-rw-r--r--source/blender/blenkernel/intern/node.cc45
-rw-r--r--source/blender/blenkernel/intern/object.c2
-rw-r--r--source/blender/blenkernel/intern/object_update.c21
-rw-r--r--source/blender/blenkernel/intern/paint.c2
-rw-r--r--source/blender/blenkernel/intern/particle.c4
-rw-r--r--source/blender/blenkernel/intern/pbvh_bmesh.c10
-rw-r--r--source/blender/blenkernel/intern/scene.c7
-rw-r--r--source/blender/blenkernel/intern/screen.c12
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc2
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c2
-rw-r--r--source/blender/blenkernel/intern/unit.c2
-rw-r--r--source/blender/blenkernel/intern/workspace.c10
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c7
-rw-r--r--source/blender/blenlib/BLI_alloca.h4
-rw-r--r--source/blender/blenlib/BLI_array.h2
-rw-r--r--source/blender/blenlib/BLI_color.hh4
-rw-r--r--source/blender/blenlib/BLI_compiler_typecheck.h2
-rw-r--r--source/blender/blenlib/BLI_delaunay_2d.h8
-rw-r--r--source/blender/blenlib/BLI_dlrbTree.h15
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh13
-rw-r--r--source/blender/blenlib/BLI_ghash.h6
-rw-r--r--source/blender/blenlib/BLI_inplace_priority_queue.hh4
-rw-r--r--source/blender/blenlib/BLI_linear_allocator.hh6
-rw-r--r--source/blender/blenlib/BLI_link_utils.h2
-rw-r--r--source/blender/blenlib/BLI_linklist_stack.h2
-rw-r--r--source/blender/blenlib/BLI_map.hh24
-rw-r--r--source/blender/blenlib/BLI_map_slots.hh4
-rw-r--r--source/blender/blenlib/BLI_math.h40
-rw-r--r--source/blender/blenlib/BLI_resource_scope.hh2
-rw-r--r--source/blender/blenlib/BLI_stack.hh2
-rw-r--r--source/blender/blenlib/BLI_string_ref.hh42
-rw-r--r--source/blender/blenlib/BLI_vector.hh4
-rw-r--r--source/blender/blenlib/intern/BLI_dynstr.c2
-rw-r--r--source/blender/blenlib/intern/BLI_ghash.c2
-rw-r--r--source/blender/blenlib/intern/BLI_ghash_utils.c4
-rw-r--r--source/blender/blenlib/intern/BLI_kdopbvh.c4
-rw-r--r--source/blender/blenlib/intern/BLI_memarena.c3
-rw-r--r--source/blender/blenlib/intern/BLI_memiter.c2
-rw-r--r--source/blender/blenlib/intern/BLI_mempool.c4
-rw-r--r--source/blender/blenlib/intern/BLI_mmap.c6
-rw-r--r--source/blender/blenlib/intern/DLRB_tree.c10
-rw-r--r--source/blender/blenlib/intern/array_store.c10
-rw-r--r--source/blender/blenlib/intern/convexhull_2d.c2
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.cc370
-rw-r--r--source/blender/blenlib/intern/fileops.c8
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c12
-rw-r--r--source/blender/blenlib/intern/math_color.c10
-rw-r--r--source/blender/blenlib/intern/math_color_inline.c22
-rw-r--r--source/blender/blenlib/intern/math_geom.c10
-rw-r--r--source/blender/blenlib/intern/math_geom_inline.c8
-rw-r--r--source/blender/blenlib/intern/math_interp.c2
-rw-r--r--source/blender/blenlib/intern/math_matrix.c6
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c2
-rw-r--r--source/blender/blenlib/intern/memory_utils.c2
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc6
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc69
-rw-r--r--source/blender/blenlib/intern/noise.c37
-rw-r--r--source/blender/blenlib/intern/polyfill_2d.c6
-rw-r--r--source/blender/blenlib/intern/smallhash.c6
-rw-r--r--source/blender/blenlib/intern/string.c4
-rw-r--r--source/blender/blenlib/intern/string_utils.c6
-rw-r--r--source/blender/blenlib/intern/system.c2
-rw-r--r--source/blender/blenlib/intern/task_iterator.c48
-rw-r--r--source/blender/blenlib/intern/task_pool.cc2
-rw-r--r--source/blender/blenlib/intern/timecode.c8
-rw-r--r--source/blender/blenlib/intern/winstuff.c2
-rw-r--r--source/blender/blenlib/tests/BLI_delaunay_2d_test.cc181
-rw-r--r--source/blender/blenlib/tests/BLI_mesh_intersect_test.cc10
-rw-r--r--source/blender/blenlib/tests/BLI_string_ref_test.cc38
-rw-r--r--source/blender/blenloader/intern/readfile.c59
-rw-r--r--source/blender/blenloader/intern/versioning_250.c46
-rw-r--r--source/blender/blenloader/intern/versioning_260.c2
-rw-r--r--source/blender/blenloader/intern/versioning_270.c2
-rw-r--r--source/blender/blenloader/intern/versioning_280.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c172
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c8
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c9
-rw-r--r--source/blender/blenloader/intern/writefile.c2
-rw-r--r--source/blender/bmesh/bmesh.h22
-rw-r--r--source/blender/bmesh/bmesh_class.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_edgeloop.c3
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_normals.c1244
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c4
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_query.c6
-rw-r--r--source/blender/bmesh/intern/bmesh_query.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_structure.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers_impl.c2
-rw-r--r--source/blender/bmesh/operators/bmo_connect_concave.c8
-rw-r--r--source/blender/bmesh/operators/bmo_edgenet.c2
-rw-r--r--source/blender/bmesh/operators/bmo_fill_grid.c16
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide_edgering.c8
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c7
-rw-r--r--source/blender/bmesh/tools/bmesh_region_match.c2
-rw-r--r--source/blender/compositor/CMakeLists.txt19
-rw-r--r--source/blender/compositor/COM_defines.h24
-rw-r--r--source/blender/compositor/intern/COM_BufferArea.h215
-rw-r--r--source/blender/compositor/intern/COM_BufferRange.h171
-rw-r--r--source/blender/compositor/intern/COM_BuffersIterator.h195
-rw-r--r--source/blender/compositor/intern/COM_ConstantFolder.cc9
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc2
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc46
-rw-r--r--source/blender/compositor/intern/COM_Debug.h17
-rw-r--r--source/blender/compositor/intern/COM_Enums.cc19
-rw-r--r--source/blender/compositor/intern/COM_Enums.h9
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.cc6
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.h27
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc18
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc14
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h52
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.cc5
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h12
-rw-r--r--source/blender/compositor/nodes/COM_IDMaskNode.cc26
-rw-r--r--source/blender/compositor/nodes/COM_RenderLayersNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.cc2
-rw-r--r--source/blender/compositor/operations/COM_BoxMaskOperation.cc58
-rw-r--r--source/blender/compositor/operations/COM_BoxMaskOperation.h16
-rw-r--r--source/blender/compositor/operations/COM_BrightnessOperation.cc45
-rw-r--r--source/blender/compositor/operations/COM_BrightnessOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_CalculateMeanOperation.cc90
-rw-r--r--source/blender/compositor/operations/COM_CalculateMeanOperation.h31
-rw-r--r--source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc47
-rw-r--r--source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc1086
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.cc73
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.h16
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc43
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.h120
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.cc555
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.h89
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.cc24
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc179
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.h68
-rw-r--r--source/blender/compositor/operations/COM_SunBeamsOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.cc73
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.cc45
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.h14
-rw-r--r--source/blender/compositor/tests/COM_BufferArea_test.cc141
-rw-r--r--source/blender/compositor/tests/COM_BufferRange_test.cc98
-rw-r--r--source/blender/compositor/tests/COM_BuffersIterator_test.cc299
-rw-r--r--source/blender/datatoc/datatoc_icon.c5
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc102
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc68
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.cc4
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc9
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc23
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.cc10
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h10
-rw-r--r--source/blender/draw/CMakeLists.txt4
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c6
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c8
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c10
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c7
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c8
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl2
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.c7
-rw-r--r--source/blender/draw/engines/overlay/overlay_outline.c4
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c2
-rw-r--r--source/blender/draw/intern/DRW_render.h7
-rw-r--r--source/blender/draw/intern/draw_cache.c4
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h13
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc40
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_render_data.c248
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.cc2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c18
-rw-r--r--source/blender/draw/intern/draw_cache_impl_volume.c2
-rw-r--r--source/blender/draw/intern/draw_cache_inline.h6
-rw-r--r--source/blender/draw/intern/draw_instance_data.c2
-rw-r--r--source/blender/draw/intern/draw_manager.c4
-rw-r--r--source/blender/draw/intern/draw_manager.h4
-rw-r--r--source/blender/draw/intern/draw_manager_data.c12
-rw-r--r--source/blender/draw/intern/draw_manager_text.c2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh.c (renamed from source/blender/draw/intern/draw_cache_extract_mesh_extractors.c)3
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh.h (renamed from source/blender/draw/intern/draw_cache_extract_mesh_private.h)20
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc6
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc113
-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_edit_data.cc3
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc3
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc3
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc2
-rw-r--r--source/blender/editors/animation/CMakeLists.txt1
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c2
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c5
-rw-r--r--source/blender/editors/animation/anim_draw.c1
-rw-r--r--source/blender/editors/animation/anim_markers.c5
-rw-r--r--source/blender/editors/animation/anim_motion_paths.c2
-rw-r--r--source/blender/editors/animation/keyframes_draw.c758
-rw-r--r--source/blender/editors/animation/keyframes_keylist.c793
-rw-r--r--source/blender/editors/armature/CMakeLists.txt3
-rw-r--r--source/blender/editors/armature/armature_edit.c5
-rw-r--r--source/blender/editors/armature/armature_intern.h4
-rw-r--r--source/blender/editors/armature/armature_ops.c2
-rw-r--r--source/blender/editors/armature/armature_utils.c2
-rw-r--r--source/blender/editors/armature/pose_backup.cc143
-rw-r--r--source/blender/editors/armature/pose_lib.c2
-rw-r--r--source/blender/editors/armature/pose_lib_2.c638
-rw-r--r--source/blender/editors/armature/pose_slide.c534
-rw-r--r--source/blender/editors/asset/CMakeLists.txt37
-rw-r--r--source/blender/editors/asset/ED_asset_filter.h35
-rw-r--r--source/blender/editors/asset/ED_asset_handle.h45
-rw-r--r--source/blender/editors/asset/ED_asset_library.h35
-rw-r--r--source/blender/editors/asset/ED_asset_list.h53
-rw-r--r--source/blender/editors/asset/ED_asset_list.hh38
-rw-r--r--source/blender/editors/asset/ED_asset_mark_clear.h37
-rw-r--r--source/blender/editors/asset/ED_asset_temp_id_consumer.h49
-rw-r--r--source/blender/editors/asset/intern/asset_filter.cc64
-rw-r--r--source/blender/editors/asset/intern/asset_handle.cc75
-rw-r--r--source/blender/editors/asset/intern/asset_library_reference.cc50
-rw-r--r--source/blender/editors/asset/intern/asset_library_reference.hh46
-rw-r--r--source/blender/editors/asset/intern/asset_library_reference_enum.cc156
-rw-r--r--source/blender/editors/asset/intern/asset_list.cc586
-rw-r--r--source/blender/editors/asset/intern/asset_mark_clear.cc (renamed from source/blender/editors/asset/asset_edit.cc)20
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc (renamed from source/blender/editors/asset/asset_ops.cc)55
-rw-r--r--source/blender/editors/asset/intern/asset_temp_id_consumer.cc116
-rw-r--r--source/blender/editors/geometry/geometry_attributes.c17
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c6
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c2
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_armature.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c11
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c7
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c2
-rw-r--r--source/blender/editors/include/BIF_glutil.h12
-rw-r--r--source/blender/editors/include/ED_anim_api.h4
-rw-r--r--source/blender/editors/include/ED_armature.h16
-rw-r--r--source/blender/editors/include/ED_asset.h21
-rw-r--r--source/blender/editors/include/ED_fileselect.h1
-rw-r--r--source/blender/editors/include/ED_keyframes_draw.h149
-rw-r--r--source/blender/editors/include/ED_keyframes_keylist.h192
-rw-r--r--source/blender/editors/include/ED_node.h2
-rw-r--r--source/blender/editors/include/ED_particle.h2
-rw-r--r--source/blender/editors/include/ED_render.h5
-rw-r--r--source/blender/editors/include/ED_screen.h2
-rw-r--r--source/blender/editors/include/ED_spreadsheet.h10
-rw-r--r--source/blender/editors/include/ED_util.h19
-rw-r--r--source/blender/editors/include/UI_interface.h67
-rw-r--r--source/blender/editors/include/UI_interface_icons.h5
-rw-r--r--source/blender/editors/include/UI_view2d.h10
-rw-r--r--source/blender/editors/interface/CMakeLists.txt2
-rw-r--r--source/blender/editors/interface/interface.c60
-rw-r--r--source/blender/editors/interface/interface_context_menu.c18
-rw-r--r--source/blender/editors/interface/interface_draw.c32
-rw-r--r--source/blender/editors/interface/interface_handlers.c279
-rw-r--r--source/blender/editors/interface/interface_icons.c20
-rw-r--r--source/blender/editors/interface/interface_intern.h59
-rw-r--r--source/blender/editors/interface/interface_layout.c13
-rw-r--r--source/blender/editors/interface/interface_ops.c94
-rw-r--r--source/blender/editors/interface/interface_panel.c2
-rw-r--r--source/blender/editors/interface/interface_query.c113
-rw-r--r--source/blender/editors/interface/interface_region_popover.c2
-rw-r--r--source/blender/editors/interface/interface_region_search.c16
-rw-r--r--source/blender/editors/interface/interface_template_asset_view.cc277
-rw-r--r--source/blender/editors/interface/interface_template_list.cc1314
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c4
-rw-r--r--source/blender/editors/interface/interface_templates.c881
-rw-r--r--source/blender/editors/interface/interface_utils.c96
-rw-r--r--source/blender/editors/interface/interface_widgets.c79
-rw-r--r--source/blender/editors/interface/resources.c2
-rw-r--r--source/blender/editors/interface/view2d.c74
-rw-r--r--source/blender/editors/interface/view2d_draw.c62
-rw-r--r--source/blender/editors/io/io_gpencil.h9
-rw-r--r--source/blender/editors/mask/mask_add.c6
-rw-r--r--source/blender/editors/mesh/editmesh_loopcut.c3
-rw-r--r--source/blender/editors/mesh/mesh_data.c65
-rw-r--r--source/blender/editors/mesh/mesh_ops.c8
-rw-r--r--source/blender/editors/object/CMakeLists.txt2
-rw-r--r--source/blender/editors/object/object_bake_api.c1
-rw-r--r--source/blender/editors/object/object_data_transfer.c87
-rw-r--r--source/blender/editors/object/object_facemap_ops.c17
-rw-r--r--source/blender/editors/object/object_intern.h2
-rw-r--r--source/blender/editors/object/object_modifier.c4
-rw-r--r--source/blender/editors/object/object_remesh.cc (renamed from source/blender/editors/object/object_remesh.c)122
-rw-r--r--source/blender/editors/object/object_vgroup.c6
-rw-r--r--source/blender/editors/physics/particle_edit.c4
-rw-r--r--source/blender/editors/physics/particle_object.c5
-rw-r--r--source/blender/editors/render/render_opengl.c2
-rw-r--r--source/blender/editors/render/render_preview.c160
-rw-r--r--source/blender/editors/render/render_shading.c9
-rw-r--r--source/blender/editors/render/render_update.c2
-rw-r--r--source/blender/editors/screen/area.c8
-rw-r--r--source/blender/editors/screen/screen_context.c22
-rw-r--r--source/blender/editors/screen/screen_ops.c29
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c9
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c2
-rw-r--r--source/blender/editors/space_action/action_select.c2
-rw-r--r--source/blender/editors/space_action/space_action.c3
-rw-r--r--source/blender/editors/space_api/spacetypes.c2
-rw-r--r--source/blender/editors/space_buttons/buttons_intern.h2
-rw-r--r--source/blender/editors/space_clip/space_clip.c2
-rw-r--r--source/blender/editors/space_file/file_draw.c8
-rw-r--r--source/blender/editors/space_file/file_intern.h1
-rw-r--r--source/blender/editors/space_file/file_ops.c143
-rw-r--r--source/blender/editors/space_file/filelist.c70
-rw-r--r--source/blender/editors/space_file/filelist.h10
-rw-r--r--source/blender/editors/space_file/filesel.c19
-rw-r--r--source/blender/editors/space_file/space_file.c20
-rw-r--r--source/blender/editors/space_graph/graph_draw.c7
-rw-r--r--source/blender/editors/space_graph/graph_edit.c10
-rw-r--r--source/blender/editors/space_graph/graph_select.c2
-rw-r--r--source/blender/editors/space_graph/graph_view.c2
-rw-r--r--source/blender/editors/space_image/image_buttons.c18
-rw-r--r--source/blender/editors/space_image/image_draw.c475
-rw-r--r--source/blender/editors/space_image/image_intern.h2
-rw-r--r--source/blender/editors/space_image/image_ops.c2
-rw-r--r--source/blender/editors/space_nla/nla_draw.c1
-rw-r--r--source/blender/editors/space_nla/space_nla.c2
-rw-r--r--source/blender/editors/space_node/drawnode.cc34
-rw-r--r--source/blender/editors/space_node/node_add.cc2
-rw-r--r--source/blender/editors/space_node/node_draw.cc2
-rw-r--r--source/blender/editors/space_node/node_edit.cc44
-rw-r--r--source/blender/editors/space_node/node_intern.h2
-rw-r--r--source/blender/editors/space_node/node_relationships.cc70
-rw-r--r--source/blender/editors/space_node/node_select.cc2
-rw-r--r--source/blender/editors/space_node/node_templates.cc5
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c7
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c11
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c15
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c52
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c40
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c19
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.hh4
-rw-r--r--source/blender/editors/space_text/text_format_lua.c6
-rw-r--r--source/blender/editors/space_text/text_format_osl.c10
-rw-r--r--source/blender/editors/space_text/text_format_pov.c10
-rw-r--r--source/blender/editors/space_text/text_format_pov_ini.c4
-rw-r--r--source/blender/editors/space_text/text_format_py.c8
-rw-r--r--source/blender/editors/space_userpref/userpref_ops.c57
-rw-r--r--source/blender/editors/space_view3d/view3d_camera_control.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_iterators.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c4
-rw-r--r--source/blender/editors/transform/transform.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c28
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c8
-rw-r--r--source/blender/editors/transform/transform_generics.c4
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c7
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c3
-rw-r--r--source/blender/editors/transform/transform_orientations.c4
-rw-r--r--source/blender/editors/transform/transform_snap_object.c122
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c82
-rw-r--r--source/blender/editors/undo/ed_undo.c5
-rw-r--r--source/blender/editors/util/ed_draw.c473
-rw-r--r--source/blender/editors/util/ed_util.c3
-rw-r--r--source/blender/editors/util/ed_util_ops.cc3
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c14
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c4
-rw-r--r--source/blender/freestyle/intern/application/Controller.cpp2
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp2
-rw-r--r--source/blender/freestyle/intern/geometry/FastGrid.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/Interface0D.h4
-rw-r--r--source/blender/freestyle/intern/view_map/Silhouette.h6
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMap.h12
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp2
-rw-r--r--source/blender/freestyle/intern/winged_edge/Curvature.cpp22
-rw-r--r--source/blender/functions/FN_cpp_type.hh2
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c74
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h13
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c64
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h4
-rw-r--r--source/blender/gpu/GPU_vertex_format.h2
-rw-r--r--source/blender/gpu/intern/gpu_batch.cc6
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c2
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc10
-rw-r--r--source/blender/gpu/intern/gpu_material.c4
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c2
-rw-r--r--source/blender/gpu/intern/gpu_select_sample_query.cc2
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh10
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc2
-rw-r--r--source/blender/gpu/opengl/gl_context.cc2
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc2
-rw-r--r--source/blender/gpu/opengl/gl_texture.hh8
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.cc2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl10
-rw-r--r--source/blender/imbuf/IMB_imbuf.h2
-rw-r--r--source/blender/imbuf/intern/iris.c2
-rw-r--r--source/blender/imbuf/intern/oiio/openimageio_api.cpp2
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp2
-rw-r--r--source/blender/imbuf/intern/png.c5
-rw-r--r--source/blender/imbuf/intern/scaling.c496
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc2
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp2
-rw-r--r--source/blender/io/collada/BCAnimationSampler.cpp10
-rw-r--r--source/blender/io/collada/ControllerExporter.cpp2
-rw-r--r--source/blender/io/collada/DocumentExporter.cpp2
-rw-r--r--source/blender/io/collada/Materials.cpp7
-rw-r--r--source/blender/io/collada/MeshImporter.cpp2
-rw-r--r--source/blender/io/collada/TransformReader.cpp8
-rw-r--r--source/blender/io/collada/collada_utils.cpp12
-rw-r--r--source/blender/io/gpencil/gpencil_io.h2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.hh2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc25
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.hh4
-rw-r--r--source/blender/makesdna/DNA_ID.h9
-rw-r--r--source/blender/makesdna/DNA_action_types.h4
-rw-r--r--source/blender/makesdna/DNA_anim_types.h24
-rw-r--r--source/blender/makesdna/DNA_armature_types.h7
-rw-r--r--source/blender/makesdna/DNA_asset_defaults.h7
-rw-r--r--source/blender/makesdna/DNA_asset_types.h55
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h10
-rw-r--r--source/blender/makesdna/DNA_curve_types.h2
-rw-r--r--source/blender/makesdna/DNA_documentation.h2
-rw-r--r--source/blender/makesdna/DNA_fluid_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_fluid_types.h8
-rw-r--r--source/blender/makesdna/DNA_freestyle_types.h2
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h20
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
-rw-r--r--source/blender/makesdna/DNA_ipo_types.h10
-rw-r--r--source/blender/makesdna/DNA_light_types.h2
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h9
-rw-r--r--source/blender/makesdna/DNA_linestyle_types.h8
-rw-r--r--source/blender/makesdna/DNA_mask_types.h4
-rw-r--r--source/blender/makesdna/DNA_material_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_material_types.h46
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h8
-rw-r--r--source/blender/makesdna/DNA_modifier_defaults.h3
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h23
-rw-r--r--source/blender/makesdna/DNA_node_types.h33
-rw-r--r--source/blender/makesdna/DNA_object_types.h2
-rw-r--r--source/blender/makesdna/DNA_rigidbody_types.h4
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_scene_types.h32
-rw-r--r--source/blender/makesdna/DNA_screen_types.h26
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h23
-rw-r--r--source/blender/makesdna/DNA_space_types.h39
-rw-r--r--source/blender/makesdna/DNA_texture_types.h3
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h6
-rw-r--r--source/blender/makesdna/DNA_view2d_types.h2
-rw-r--r--source/blender/makesdna/DNA_view3d_enums.h2
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h2
-rw-r--r--source/blender/makesdna/DNA_workspace_types.h5
-rw-r--r--source/blender/makesdna/DNA_xr_types.h1
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c2
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c6
-rw-r--r--source/blender/makesdna/intern/dna_rename_defs.h1
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/RNA_define.h5
-rw-r--r--source/blender/makesrna/RNA_enum_types.h16
-rw-r--r--source/blender/makesrna/RNA_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_ID.c91
-rw-r--r--source/blender/makesrna/intern/rna_access.c4
-rw-r--r--source/blender/makesrna/intern/rna_asset.c131
-rw-r--r--source/blender/makesrna/intern/rna_cloth.c1
-rw-r--r--source/blender/makesrna/intern/rna_collection.c6
-rw-r--r--source/blender/makesrna/intern/rna_context.c29
-rw-r--r--source/blender/makesrna/intern/rna_define.c55
-rw-r--r--source/blender/makesrna/intern/rna_fluid.c13
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c41
-rw-r--r--source/blender/makesrna/intern/rna_internal.h9
-rw-r--r--source/blender/makesrna/intern/rna_material.c8
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c21
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c88
-rw-r--r--source/blender/makesrna/intern/rna_object.c31
-rw-r--r--source/blender/makesrna/intern/rna_pose_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_render.c3
-rw-r--r--source/blender/makesrna/intern/rna_rna.c67
-rw-r--r--source/blender/makesrna/intern/rna_scene.c102
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c69
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c38
-rw-r--r--source/blender/makesrna/intern/rna_space.c361
-rw-r--r--source/blender/makesrna/intern/rna_ui.c35
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c182
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c2
-rw-r--r--source/blender/makesrna/intern/rna_volume.c2
-rw-r--r--source/blender/makesrna/intern/rna_wm.c4
-rw-r--r--source/blender/makesrna/intern/rna_workspace.c22
-rw-r--r--source/blender/makesrna/intern/rna_xr.c88
-rw-r--r--source/blender/modifiers/intern/MOD_array.c4
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc14
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc2
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c4
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c4
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c2
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c99
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weightvg_util.c6
-rw-r--r--source/blender/modifiers/intern/MOD_weld.c7
-rw-r--r--source/blender/nodes/CMakeLists.txt2
-rw-r--r--source/blender/nodes/NOD_geometry.h2
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh3
-rw-r--r--source/blender/nodes/NOD_math_functions.hh2
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh6
-rw-r--r--source/blender/nodes/NOD_static_types.h14
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c9
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.c10
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_common.c5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc149
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc72
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc410
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc16
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc4
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc18
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_common.c6
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_common.c6
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_rotate.c2
-rw-r--r--source/blender/python/BPY_extern.h4
-rw-r--r--source/blender/python/bmesh/bmesh_py_ops_call.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_utils.c2
-rw-r--r--source/blender/python/generic/idprop_py_api.c2
-rw-r--r--source/blender/python/generic/imbuf_py_api.c2
-rw-r--r--source/blender/python/generic/py_capi_utils.c309
-rw-r--r--source/blender/python/generic/py_capi_utils.h25
-rw-r--r--source/blender/python/gpu/gpu_py.c4
-rw-r--r--source/blender/python/gpu/gpu_py_api.c4
-rw-r--r--source/blender/python/gpu/gpu_py_batch.c6
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.c4
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.c4
-rw-r--r--source/blender/python/gpu/gpu_py_element.c12
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.c61
-rw-r--r--source/blender/python/gpu/gpu_py_matrix.c4
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c4
-rw-r--r--source/blender/python/gpu/gpu_py_platform.c4
-rw-r--r--source/blender/python/gpu/gpu_py_select.c4
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c10
-rw-r--r--source/blender/python/gpu/gpu_py_state.c4
-rw-r--r--source/blender/python/gpu/gpu_py_texture.c17
-rw-r--r--source/blender/python/gpu/gpu_py_types.c4
-rw-r--r--source/blender/python/gpu/gpu_py_uniformbuffer.c4
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_buffer.c4
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_format.c4
-rw-r--r--source/blender/python/intern/bpy_app_ffmpeg.c2
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c2
-rw-r--r--source/blender/python/intern/bpy_gizmo_wrap.c28
-rw-r--r--source/blender/python/intern/bpy_library_load.c5
-rw-r--r--source/blender/python/intern/bpy_operator.c2
-rw-r--r--source/blender/python/intern/bpy_props.c2640
-rw-r--r--source/blender/python/intern/bpy_props.h5
-rw-r--r--source/blender/python/intern/bpy_rna.c83
-rw-r--r--source/blender/python/intern/bpy_rna.h19
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c2
-rw-r--r--source/blender/python/intern/bpy_rna_callback.c114
-rw-r--r--source/blender/python/intern/bpy_rna_gizmo.c13
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c13
-rw-r--r--source/blender/python/intern/bpy_utils_units.c2
-rw-r--r--source/blender/python/mathutils/mathutils.c2
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c8
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c26
-rw-r--r--source/blender/python/mathutils/mathutils_noise.c3
-rw-r--r--source/blender/render/intern/bake.c9
-rw-r--r--source/blender/render/intern/pipeline.c1
-rw-r--r--source/blender/render/intern/render_result.c7
-rw-r--r--source/blender/render/intern/texture_image.c4
-rw-r--r--source/blender/sequencer/SEQ_effects.h3
-rw-r--r--source/blender/sequencer/SEQ_iterator.h4
-rw-r--r--source/blender/sequencer/SEQ_proxy.h2
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h2
-rw-r--r--source/blender/sequencer/SEQ_time.h1
-rw-r--r--source/blender/sequencer/SEQ_transform.h2
-rw-r--r--source/blender/sequencer/intern/effects.c92
-rw-r--r--source/blender/sequencer/intern/iterator.c32
-rw-r--r--source/blender/sequencer/intern/strip_edit.c6
-rw-r--r--source/blender/sequencer/intern/strip_time.c13
-rw-r--r--source/blender/sequencer/intern/strip_transform.c6
-rw-r--r--source/blender/shader_fx/intern/FX_ui_common.c2
-rw-r--r--source/blender/simulation/SIM_mass_spring.h2
-rw-r--r--source/blender/simulation/intern/SIM_mass_spring.cpp43
-rw-r--r--source/blender/simulation/intern/hair_volume.cpp35
-rw-r--r--source/blender/simulation/intern/implicit_blender.c26
-rw-r--r--source/blender/simulation/intern/implicit_eigen.cpp6
-rw-r--r--source/blender/windowmanager/WM_api.h12
-rw-r--r--source/blender/windowmanager/WM_types.h10
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_types.h2
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c2
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c9
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c1
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c12
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c2
-rw-r--r--source/blender/windowmanager/intern/wm_files.c2
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c4
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c5
-rw-r--r--source/blender/windowmanager/intern/wm_keymap_utils.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operator_type.c17
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c8
-rw-r--r--source/blender/windowmanager/intern/wm_splash_screen.c4
-rw-r--r--source/blender/windowmanager/intern/wm_uilist_type.c95
-rw-r--r--source/blender/windowmanager/wm_event_types.h2
-rw-r--r--source/blender/windowmanager/wm_surface.h6
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c20
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h8
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c90
-rw-r--r--source/creator/CMakeLists.txt7
-rw-r--r--source/creator/blender.map58
m---------source/tools0
-rw-r--r--tests/performance/api/config.py23
-rw-r--r--tests/performance/api/environment.py99
-rw-r--r--tests/performance/api/graph.py3
-rwxr-xr-xtests/performance/benchmark33
-rw-r--r--tests/python/CMakeLists.txt23
-rw-r--r--tests/python/bevel_operator.py178
-rw-r--r--tests/python/bl_pyapi_prop_array.py120
-rw-r--r--tests/python/boolean_operator.py20
-rw-r--r--tests/python/curve_to_mesh.py66
-rw-r--r--tests/python/deform_modifiers.py22
-rw-r--r--tests/python/geo_node_test.py33
-rw-r--r--tests/python/modifiers.py142
-rw-r--r--tests/python/modules/mesh_test.py524
-rw-r--r--tests/python/operators.py154
-rw-r--r--tests/python/physics_cloth.py10
-rw-r--r--tests/python/physics_dynamic_paint.py4
-rw-r--r--tests/python/physics_ocean.py4
-rw-r--r--tests/python/physics_particle_instance.py4
-rw-r--r--tests/python/physics_particle_system.py5
-rw-r--r--tests/python/physics_softbody.py4
883 files changed, 22589 insertions, 11022 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c5992993f91..5072977215d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -252,16 +252,6 @@ if(WITH_GHOST_X11)
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)
-else()
- # not an option for other OS's
- set(WITH_SYSTEM_GLEW OFF)
- set(WITH_SYSTEM_GLES OFF)
-endif()
-
-
-if(UNIX AND NOT APPLE)
option(WITH_SYSTEM_EIGEN3 "Use the systems Eigen3 library" OFF)
endif()
@@ -403,14 +393,14 @@ set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified
set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for")
mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH)
unset(PLATFORM_DEFAULT)
-option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON)
-option(WITH_CYCLES_DEBUG "Build Cycles with extra debug capabilities" OFF)
+option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON)
+option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF)
option(WITH_CYCLES_NATIVE_ONLY "Build Cycles with native kernel only (which fits current CPU, use for development only)" OFF)
option(WITH_CYCLES_KERNEL_ASAN "Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN is on, even if it's very slow" OFF)
mark_as_advanced(WITH_CYCLES_KERNEL_ASAN)
mark_as_advanced(WITH_CYCLES_CUBIN_COMPILER)
mark_as_advanced(WITH_CYCLES_LOGGING)
-mark_as_advanced(WITH_CYCLES_DEBUG)
+mark_as_advanced(WITH_CYCLES_DEBUG_NAN)
mark_as_advanced(WITH_CYCLES_NATIVE_ONLY)
option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON)
@@ -485,15 +475,32 @@ endif()
# OpenGL
+if(UNIX AND NOT APPLE)
+ # GLEW can only built with either GLX or EGL support and most binary
+ # distributions are built with GLX support. So we always compile GLEW
+ # with EGL support manually, and the options are no longer available.
+ set(WITH_SYSTEM_GLEW OFF)
+ set(WITH_SYSTEM_GLES ON)
+
+ # Always use EGL instead of GLX, for X11, Wayland and headless.
+ set(WITH_GL_EGL ON)
+else()
+ # System GLEW and GLES were never an option on other platforms.
+ set(WITH_SYSTEM_GLEW OFF)
+ set(WITH_SYSTEM_GLES OFF)
+
+ # Experimental EGL option.
+ option(WITH_GL_EGL "Use the EGL OpenGL system library instead of the platform specific OpenGL system library (CGL or WGL)" OFF)
+ mark_as_advanced(WITH_GL_EGL)
+endif()
+
option(WITH_OPENGL "When off limits visibility of the opengl headers to just bf_gpu and gawain (temporary option for development purposes)" ON)
option(WITH_GLEW_ES "Switches to experimental copy of GLEW that has support for OpenGL ES. (temporary option for development purposes)" OFF)
-option(WITH_GL_EGL "Use the EGL OpenGL system library instead of the platform specific OpenGL system library (CGL, glX, or WGL)" OFF)
option(WITH_GL_PROFILE_ES20 "Support using OpenGL ES 2.0. (through either EGL or the AGL/WGL/XGL 'es20' profile)" OFF)
mark_as_advanced(
WITH_OPENGL
WITH_GLEW_ES
- WITH_GL_EGL
WITH_GL_PROFILE_ES20
)
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt
index 8c739dcd68e..3a9574b4b2a 100644
--- a/build_files/build_environment/CMakeLists.txt
+++ b/build_files/build_environment/CMakeLists.txt
@@ -82,7 +82,11 @@ if(UNIX)
endif()
include(cmake/openimageio.cmake)
include(cmake/tiff.cmake)
-include(cmake/flexbison.cmake)
+if(WIN32)
+ include(cmake/flexbison.cmake)
+else()
+ include(cmake/flex.cmake)
+endif()
include(cmake/osl.cmake)
include(cmake/tbb.cmake)
include(cmake/openvdb.cmake)
diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake
index b4d96ea856f..2a84202ea02 100644
--- a/build_files/build_environment/cmake/download.cmake
+++ b/build_files/build_environment/cmake/download.cmake
@@ -93,3 +93,4 @@ download_source(GMP)
download_source(POTRACE)
download_source(HARU)
download_source(ZSTD)
+download_source(FLEX)
diff --git a/build_files/build_environment/cmake/ffmpeg.cmake b/build_files/build_environment/cmake/ffmpeg.cmake
index 2dad9c38877..81bcb6d202c 100644
--- a/build_files/build_environment/cmake/ffmpeg.cmake
+++ b/build_files/build_environment/cmake/ffmpeg.cmake
@@ -30,6 +30,7 @@ if(WIN32)
--enable-w32threads
--disable-pthreads
--enable-libopenjpeg
+ --disable-mediafoundation
)
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4")
set(FFMPEG_EXTRA_FLAGS
diff --git a/build_files/build_environment/cmake/flex.cmake b/build_files/build_environment/cmake/flex.cmake
new file mode 100644
index 00000000000..5daad6ce64b
--- /dev/null
+++ b/build_files/build_environment/cmake/flex.cmake
@@ -0,0 +1,28 @@
+# ***** 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 *****
+
+ExternalProject_Add(external_flex
+ URL file://${PACKAGE_DIR}/${FLEX_FILE}
+ URL_HASH ${FLEX_HASH_TYPE}=${FLEX_HASH}
+ DOWNLOAD_DIR ${DOWNLOAD_DIR}
+ PREFIX ${BUILD_DIR}/flex
+ CONFIGURE_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/flex/src/external_flex/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/flex
+ BUILD_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/flex/src/external_flex/ && make -j${MAKE_THREADS}
+ INSTALL_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/flex/src/external_flex/ && make install
+ INSTALL_DIR ${LIBDIR}/flex
+)
diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake
index a19c332457d..a3740559adc 100644
--- a/build_files/build_environment/cmake/harvest.cmake
+++ b/build_files/build_environment/cmake/harvest.cmake
@@ -106,6 +106,7 @@ harvest(llvm/include llvm/include "*")
harvest(llvm/bin llvm/bin "llvm-config")
harvest(llvm/lib llvm/lib "libLLVM*.a")
harvest(llvm/lib llvm/lib "libclang*.a")
+harvest(llvm/lib/clang llvm/lib/clang "*.h")
if(APPLE)
harvest(openmp/lib openmp/lib "*")
harvest(openmp/include openmp/include "*.h")
diff --git a/build_files/build_environment/cmake/ispc.cmake b/build_files/build_environment/cmake/ispc.cmake
index e8a4dedcdaa..93fc9dc4846 100644
--- a/build_files/build_environment/cmake/ispc.cmake
+++ b/build_files/build_environment/cmake/ispc.cmake
@@ -29,12 +29,13 @@ elseif(APPLE)
if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64")
set(ISPC_EXTRA_ARGS_APPLE
-DBISON_EXECUTABLE=/opt/homebrew/opt/bison/bin/bison
- -DFLEX_EXECUTABLE=/opt/homebrew/opt/flex/bin/flex
+ -DFLEX_EXECUTABLE=${LIBDIR}/flex/bin/flex
-DARM_ENABLED=On
)
else()
set(ISPC_EXTRA_ARGS_APPLE
-DBISON_EXECUTABLE=/usr/local/opt/bison/bin/bison
+ -DFLEX_EXECUTABLE=${LIBDIR}/flex/bin/flex
-DARM_ENABLED=Off
)
endif()
@@ -43,6 +44,7 @@ elseif(UNIX)
-DCMAKE_C_COMPILER=${LIBDIR}/llvm/bin/clang
-DCMAKE_CXX_COMPILER=${LIBDIR}/llvm/bin/clang++
-DARM_ENABLED=Off
+ -DFLEX_EXECUTABLE=${LIBDIR}/flex/bin/flex
)
endif()
@@ -82,4 +84,9 @@ if(WIN32)
external_ispc
external_flexbison
)
+else()
+ add_dependencies(
+ external_ispc
+ external_flex
+ )
endif()
diff --git a/build_files/build_environment/cmake/llvm.cmake b/build_files/build_environment/cmake/llvm.cmake
index cbb986410aa..7a8ce2ddfec 100644
--- a/build_files/build_environment/cmake/llvm.cmake
+++ b/build_files/build_environment/cmake/llvm.cmake
@@ -66,7 +66,11 @@ ExternalProject_Add(ll
if(MSVC)
if(BUILD_MODE STREQUAL Release)
- set(LLVM_HARVEST_COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/llvm/ ${HARVEST_TARGET}/llvm/ )
+ set(LLVM_HARVEST_COMMAND
+ ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/llvm/lib ${HARVEST_TARGET}/llvm/lib &&
+ ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/llvm/include ${HARVEST_TARGET}/llvm/include &&
+ ${CMAKE_COMMAND} -E copy ${LIBDIR}/llvm/bin/clang-format.exe ${HARVEST_TARGET}/llvm/bin/clang-format.exe
+ )
else()
set(LLVM_HARVEST_COMMAND
${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/llvm/lib/ ${HARVEST_TARGET}/llvm/debug/lib/ &&
diff --git a/build_files/build_environment/cmake/openimagedenoise.cmake b/build_files/build_environment/cmake/openimagedenoise.cmake
index f927d6c231c..aca2b4cef9f 100644
--- a/build_files/build_environment/cmake/openimagedenoise.cmake
+++ b/build_files/build_environment/cmake/openimagedenoise.cmake
@@ -45,7 +45,6 @@ ExternalProject_Add(external_openimagedenoise
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH ${OIDN_HASH_TYPE}=${OIDN_HASH}
PREFIX ${BUILD_DIR}/openimagedenoise
- PATCH_COMMAND ${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/openimagedenoise/src/external_openimagedenoise < ${PATCH_DIR}/oidn.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openimagedenoise ${DEFAULT_CMAKE_FLAGS} ${OIDN_EXTRA_ARGS}
INSTALL_DIR ${LIBDIR}/openimagedenoise
)
diff --git a/build_files/build_environment/cmake/openmp.cmake b/build_files/build_environment/cmake/openmp.cmake
index 96081c7fc49..0e5323ca513 100644
--- a/build_files/build_environment/cmake/openmp.cmake
+++ b/build_files/build_environment/cmake/openmp.cmake
@@ -22,9 +22,8 @@ ExternalProject_Add(external_openmp
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH ${OPENMP_HASH_TYPE}=${OPENMP_HASH}
PREFIX ${BUILD_DIR}/openmp
- PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/openmp/src/external_openmp < ${PATCH_DIR}/openmp.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openmp ${DEFAULT_CMAKE_FLAGS}
- INSTALL_COMMAND cd ${BUILD_DIR}/openmp/src/external_openmp-build && install_name_tool -id @executable_path/../Resources/lib/libomp.dylib runtime/src/libomp.dylib && make install
+ INSTALL_COMMAND cd ${BUILD_DIR}/openmp/src/external_openmp-build && install_name_tool -id @rpath/libomp.dylib runtime/src/libomp.dylib && make install
INSTALL_DIR ${LIBDIR}/openmp
)
diff --git a/build_files/build_environment/cmake/osl.cmake b/build_files/build_environment/cmake/osl.cmake
index 54e924b348a..05aedb1f085 100644
--- a/build_files/build_environment/cmake/osl.cmake
+++ b/build_files/build_environment/cmake/osl.cmake
@@ -20,12 +20,10 @@ if(WIN32)
set(OSL_CMAKE_CXX_STANDARD_LIBRARIES "kernel32${LIBEXT} user32${LIBEXT} gdi32${LIBEXT} winspool${LIBEXT} shell32${LIBEXT} ole32${LIBEXT} oleaut32${LIBEXT} uuid${LIBEXT} comdlg32${LIBEXT} advapi32${LIBEXT} psapi${LIBEXT}")
set(OSL_FLEX_BISON -DFLEX_EXECUTABLE=${LIBDIR}/flexbison/win_flex.exe -DBISON_EXECUTABLE=${LIBDIR}/flexbison/win_bison.exe)
set(OSL_SIMD_FLAGS -DOIIO_NOSIMD=1 -DOIIO_SIMD=sse2)
- SET(OSL_PLATFORM_FLAGS -DLINKSTATIC=ON)
else()
set(OSL_CMAKE_CXX_STANDARD_LIBRARIES)
set(OSL_FLEX_BISON)
set(OSL_OPENIMAGEIO_LIBRARY "${LIBDIR}/openimageio/lib/${LIBPREFIX}OpenImageIO${LIBEXT};${LIBDIR}/openimageio/lib/${LIBPREFIX}OpenImageIO_Util${LIBEXT};${LIBDIR}/png/lib/${LIBPREFIX}png16${LIBEXT};${LIBDIR}/jpg/lib/${LIBPREFIX}jpeg${LIBEXT};${LIBDIR}/tiff/lib/${LIBPREFIX}tiff${LIBEXT};${LIBDIR}/openexr/lib/${LIBPREFIX}IlmImf${OPENEXR_VERSION_POSTFIX}${LIBEXT}")
- SET(OSL_PLATFORM_FLAGS)
endif()
set(OSL_ILMBASE_CUSTOM_LIBRARIES "${LIBDIR}/openexr/lib/Imath${OPENEXR_VERSION_POSTFIX}.lib^^${LIBDIR}/openexr/lib/Half{OPENEXR_VERSION_POSTFIX}.lib^^${LIBDIR}/openexr/lib/IlmThread${OPENEXR_VERSION_POSTFIX}.lib^^${LIBDIR}/openexr/lib/Iex${OPENEXR_VERSION_POSTFIX}.lib")
@@ -51,12 +49,13 @@ set(OSL_EXTRA_ARGS
-DOpenImageIO_ROOT=${LIBDIR}/openimageio/
-DOSL_BUILD_TESTS=OFF
-DOSL_BUILD_MATERIALX=OFF
+ -DPNG_ROOT=${LIBDIR}/png
-DZLIB_LIBRARY=${LIBDIR}/zlib/lib/${ZLIB_LIBRARY}
-DZLIB_INCLUDE_DIR=${LIBDIR}/zlib/include/
${OSL_FLEX_BISON}
-DCMAKE_CXX_STANDARD_LIBRARIES=${OSL_CMAKE_CXX_STANDARD_LIBRARIES}
-DBUILD_SHARED_LIBS=OFF
- ${OSL_PLATFORM_FLAGS}
+ -DLINKSTATIC=ON
-DOSL_BUILD_PLUGINS=OFF
-DSTOP_ON_WARNING=OFF
-DUSE_LLVM_BITCODE=OFF
@@ -69,13 +68,9 @@ set(OSL_EXTRA_ARGS
${OSL_SIMD_FLAGS}
-Dpugixml_ROOT=${LIBDIR}/pugixml
-DUSE_PYTHON=OFF
+ -DCMAKE_CXX_STANDARD=14
)
-# Apple arm64 uses LLVM 11, LLVM 10+ requires C++14
-if (APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
- list(APPEND OSL_EXTRA_ARGS -DCMAKE_CXX_STANDARD=14)
-endif()
-
ExternalProject_Add(external_osl
URL file://${PACKAGE_DIR}/${OSL_FILE}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
@@ -93,10 +88,20 @@ add_dependencies(
ll
external_openexr
external_zlib
- external_flexbison
external_openimageio
external_pugixml
)
+if(WIN32)
+ add_dependencies(
+ external_osl
+ external_flexbison
+ )
+else()
+ add_dependencies(
+ external_osl
+ external_flex
+ )
+endif()
if(WIN32)
if(BUILD_MODE STREQUAL Release)
diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake
index a71fc4b85e4..bc220c596c1 100644
--- a/build_files/build_environment/cmake/versions.cmake
+++ b/build_files/build_environment/cmake/versions.cmake
@@ -152,35 +152,20 @@ set(OPENCOLORIO_HASH 1a2e3478b6cd9a1549f24e1b2205e3f0)
set(OPENCOLORIO_HASH_TYPE MD5)
set(OPENCOLORIO_FILE OpenColorIO-${OPENCOLORIO_VERSION}.tar.gz)
-if(BLENDER_PLATFORM_ARM)
- # Newer version required by ISPC with arm support.
- set(LLVM_VERSION 11.0.1)
- set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz)
- set(LLVM_HASH e700af40ab83463e4e9ab0ba3708312e)
- set(LLVM_HASH_TYPE MD5)
- set(LLVM_FILE llvm-project-${LLVM_VERSION}.src.tar.xz)
-
- set(OPENMP_VERSION 9.0.1)
- set(OPENMP_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${OPENMP_VERSION}/openmp-${OPENMP_VERSION}.src.tar.xz)
- set(OPENMP_HASH 6eade16057edbdecb3c4eef9daa2bfcf)
- set(OPENMP_HASH_TYPE MD5)
- set(OPENMP_FILE openmp-${OPENMP_VERSION}.src.tar.xz)
-else()
- set(LLVM_VERSION 9.0.1)
- set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.tar.xz)
- set(LLVM_HASH b4268e733dfe352960140dc07ef2efcb)
- set(LLVM_HASH_TYPE MD5)
- set(LLVM_FILE llvm-project-${LLVM_VERSION}.tar.xz)
-
- set(OPENMP_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/openmp-${LLVM_VERSION}.src.tar.xz)
- set(OPENMP_HASH 6eade16057edbdecb3c4eef9daa2bfcf)
- set(OPENMP_HASH_TYPE MD5)
- set(OPENMP_FILE openmp-${LLVM_VERSION}.src.tar.xz)
-endif()
-
-set(OPENIMAGEIO_VERSION 2.1.15.0)
+set(LLVM_VERSION 12.0.0)
+set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz)
+set(LLVM_HASH 5a4fab4d7fc84aefffb118ac2c8a4fc0)
+set(LLVM_HASH_TYPE MD5)
+set(LLVM_FILE llvm-project-${LLVM_VERSION}.src.tar.xz)
+
+set(OPENMP_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/openmp-${LLVM_VERSION}.src.tar.xz)
+set(OPENMP_HASH ac48ce3e4582ccb82f81ab59eb3fc9dc)
+set(OPENMP_HASH_TYPE MD5)
+set(OPENMP_FILE openmp-${LLVM_VERSION}.src.tar.xz)
+
+set(OPENIMAGEIO_VERSION 2.2.15.1)
set(OPENIMAGEIO_URI https://github.com/OpenImageIO/oiio/archive/Release-${OPENIMAGEIO_VERSION}.tar.gz)
-set(OPENIMAGEIO_HASH f03aa5e3ac4795af04771ee4146e9832)
+set(OPENIMAGEIO_HASH 3db5c5f0b3dc91597c75e5df09eb9072)
set(OPENIMAGEIO_HASH_TYPE MD5)
set(OPENIMAGEIO_FILE OpenImageIO-${OPENIMAGEIO_VERSION}.tar.gz)
@@ -190,9 +175,9 @@ set(TIFF_HASH 2165e7aba557463acc0664e71a3ed424)
set(TIFF_HASH_TYPE MD5)
set(TIFF_FILE tiff-${TIFF_VERSION}.tar.gz)
-set(OSL_VERSION 1.11.10.0)
+set(OSL_VERSION 1.11.14.1)
set(OSL_URI https://github.com/imageworks/OpenShadingLanguage/archive/Release-${OSL_VERSION}.tar.gz)
-set(OSL_HASH dfdc23597aeef083832cbada62211756)
+set(OSL_HASH 1abd7ce40481771a9fa937f19595d2f2)
set(OSL_HASH_TYPE MD5)
set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz)
@@ -370,12 +355,18 @@ set(PUGIXML_HASH 0c208b0664c7fb822bf1b49ad035e8fd)
set(PUGIXML_HASH_TYPE MD5)
set(PUGIXML_FILE pugixml-${PUGIXML_VERSION}.tar.gz)
-set(FLEXBISON_VERSION 2.5.5)
+set(FLEXBISON_VERSION 2.5.24)
set(FLEXBISON_URI http://prdownloads.sourceforge.net/winflexbison/win_flex_bison-${FLEXBISON_VERSION}.zip)
-set(FLEXBISON_HASH d87a3938194520d904013abef3df10ce)
+set(FLEXBISON_HASH 6b549d43e34ece0e8ed05af92daa31c4)
set(FLEXBISON_HASH_TYPE MD5)
set(FLEXBISON_FILE win_flex_bison-${FLEXBISON_VERSION}.zip)
+set(FLEX_VERSION 2.6.4)
+set(FLEX_URI https://github.com/westes/flex/releases/download/v${FLEX_VERSION}/flex-${FLEX_VERSION}.tar.gz)
+set(FLEX_HASH 2882e3179748cc9f9c23ec593d6adc8d)
+set(FLEX_HASH_TYPE MD5)
+set(FLEX_FILE flex-${FLEX_VERSION}.tar.gz)
+
# Libraries to keep Python modules static on Linux.
# NOTE: bzip.org domain does no longer belong to BZip 2 project, so we download
@@ -432,9 +423,9 @@ set(USD_HASH 1dd1e2092d085ed393c1f7c450a4155a)
set(USD_HASH_TYPE MD5)
set(USD_FILE usd-v${USD_VERSION}.tar.gz)
-set(OIDN_VERSION 1.4.0)
+set(OIDN_VERSION 1.4.1)
set(OIDN_URI https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.tar.gz)
-set(OIDN_HASH 421824019becc5b664a22a2b98332bc5)
+set(OIDN_HASH df4007b0ab93b1c41cdf223b075d01c0)
set(OIDN_HASH_TYPE MD5)
set(OIDN_FILE oidn-${OIDN_VERSION}.src.tar.gz)
@@ -444,10 +435,10 @@ set(LIBGLU_HASH 151aef599b8259efe9acd599c96ea2a3)
set(LIBGLU_HASH_TYPE MD5)
set(LIBGLU_FILE glu-${LIBGLU_VERSION}.tar.xz)
-set(MESA_VERSION 20.3.4)
+set(MESA_VERSION 21.1.5)
set(MESA_URI ftp://ftp.freedesktop.org/pub/mesa/mesa-${MESA_VERSION}.tar.xz)
-set(MESA_HASH 556338446aef8ae947a789b3e0b5e056)
-set(MESA_HASH_TYPE MD5)
+set(MESA_HASH 022c7293074aeeced2278c872db4fa693147c70f8595b076cf3f1ef81520766d)
+set(MESA_HASH_TYPE SHA256)
set(MESA_FILE mesa-${MESA_VERSION}.tar.xz)
set(NASM_VERSION 2.15.02)
@@ -468,19 +459,11 @@ set(WL_PROTOCOLS_URI https://gitlab.freedesktop.org/wayland/wayland-protocols/-/
set(WL_PROTOCOLS_HASH af5ca07e13517cdbab33504492cef54a)
set(WL_PROTOCOLS_HASH_TYPE MD5)
-if(BLENDER_PLATFORM_ARM)
- # Unreleased version with macOS arm support.
- set(ISPC_URI https://github.com/ispc/ispc/archive/f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip)
- set(ISPC_HASH d382fea18d01dbd0cd05d9e1ede36d7d)
- set(ISPC_HASH_TYPE MD5)
- set(ISPC_FILE f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip)
-else()
- set(ISPC_VERSION v1.14.1)
- set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz)
- set(ISPC_HASH 968fbc8dfd16a60ba4e32d2e0e03ea7a)
- set(ISPC_HASH_TYPE MD5)
- set(ISPC_FILE ispc-${ISPC_VERSION}.tar.gz)
-endif()
+set(ISPC_VERSION v1.16.0)
+set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz)
+set(ISPC_HASH 2e3abedbc0ea9aaec17d6562c632454d)
+set(ISPC_HASH_TYPE MD5)
+set(ISPC_FILE ispc-${ISPC_VERSION}.tar.gz)
set(GMP_VERSION 6.2.0)
set(GMP_URI https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.xz)
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index c5b7198d012..fd4f59fcda7 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -474,25 +474,25 @@ OPENEXR_FORCE_REBUILD=false
OPENEXR_SKIP=false
_with_built_openexr=false
-OIIO_VERSION="2.1.15.0"
-OIIO_VERSION_SHORT="2.1"
+OIIO_VERSION="2.2.15.1"
+OIIO_VERSION_SHORT="2.2"
OIIO_VERSION_MIN="2.1.12"
-OIIO_VERSION_MAX="2.2.10"
+OIIO_VERSION_MAX="2.3.0"
OIIO_FORCE_BUILD=false
OIIO_FORCE_REBUILD=false
OIIO_SKIP=false
-LLVM_VERSION="9.0.1"
-LLVM_VERSION_SHORT="9.0"
-LLVM_VERSION_MIN="6.0"
-LLVM_VERSION_MAX="12.0"
+LLVM_VERSION="12.0.0"
+LLVM_VERSION_SHORT="12.0"
+LLVM_VERSION_MIN="11.0"
+LLVM_VERSION_MAX="13.0"
LLVM_VERSION_FOUND=""
LLVM_FORCE_BUILD=false
LLVM_FORCE_REBUILD=false
LLVM_SKIP=false
# OSL needs to be compiled for now!
-OSL_VERSION="1.11.10.0"
+OSL_VERSION="1.11.14.1"
OSL_VERSION_SHORT="1.11"
OSL_VERSION_MIN="1.11"
OSL_VERSION_MAX="2.0"
@@ -553,7 +553,7 @@ EMBREE_FORCE_BUILD=false
EMBREE_FORCE_REBUILD=false
EMBREE_SKIP=false
-OIDN_VERSION="1.4.0"
+OIDN_VERSION="1.4.1"
OIDN_VERSION_SHORT="1.4"
OIDN_VERSION_MIN="1.4.0"
OIDN_VERSION_MAX="1.5"
@@ -561,7 +561,7 @@ OIDN_FORCE_BUILD=false
OIDN_FORCE_REBUILD=false
OIDN_SKIP=false
-ISPC_VERSION="1.14.1"
+ISPC_VERSION="1.16.0"
FFMPEG_VERSION="4.4"
FFMPEG_VERSION_SHORT="4.4"
@@ -603,9 +603,6 @@ MP3LAME_DEV=""
OPENJPEG_USE=false
OPENJPEG_DEV=""
-# Whether to use system GLEW or not (OpenSubDiv needs recent glew to work).
-NO_SYSTEM_GLEW=false
-
# Switch to english language, else some things (like check_package_DEB()) won't work!
LANG_BACK=$LANG
LANG=""
@@ -1129,7 +1126,7 @@ Those libraries should be available as packages in all recent distributions (opt
* libjpeg, libpng, libtiff, [openjpeg2], [libopenal].
* libx11, libxcursor, libxi, libxrandr, libxinerama (and other libx... as needed).
* libwayland-client0, libwayland-cursor0, libwayland-egl1, libxkbcommon0, libdbus-1-3, libegl1 (Wayland)
- * libsqlite3, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp.
+ * libsqlite3, libzstd, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp, flex.
* libsdl2, libglew, libpugixml, libpotrace, [libgmp], [libglewmx], fontconfig, [libharu/libhpdf].\""
DEPS_SPECIFIC_INFO="\"BUILDABLE DEPENDENCIES:
@@ -2316,6 +2313,7 @@ compile_OSL() {
tar -C $SRC --transform "s,(.*/?)OpenShadingLanguage-[^/]*(.*),\1OpenShadingLanguage-$OSL_VERSION\2,x" \
-xf $_src.tar.gz
fi
+ patch -d $_src -p1 < $SCRIPT_DIR/patches/osl.diff
fi
cd $_src
@@ -2338,7 +2336,6 @@ compile_OSL() {
cmake_d="-D CMAKE_BUILD_TYPE=Release"
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
- cmake_d="$cmake_d -D BUILD_TESTING=OFF"
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
cmake_d="$cmake_d -D OSL_BUILD_PLUGINS=OFF"
cmake_d="$cmake_d -D OSL_BUILD_TESTS=OFF"
@@ -2346,7 +2343,10 @@ compile_OSL() {
cmake_d="$cmake_d -D USE_PARTIO=OFF"
cmake_d="$cmake_d -D OSL_BUILD_MATERIALX=OFF"
cmake_d="$cmake_d -D USE_QT=OFF"
+ cmake_d="$cmake_d -D USE_Qt5=OFF"
cmake_d="$cmake_d -D USE_PYTHON=OFF"
+ cmake_d="$cmake_d -D USE_PARTIO=OFF"
+ cmake_d="$cmake_d -D INSTALL_DOCS=OFF"
if [ $(uname -m) != "aarch64" ]; then
cmake_d="$cmake_d -D USE_SIMD=sse2"
@@ -2368,10 +2368,7 @@ compile_OSL() {
fi
if [ -d $INST/oiio ]; then
- cmake_d="$cmake_d -D OPENIMAGEIO_ROOT_DIR=$INST/oiio"
- # HACK! SIC!!!!
- # Quiet incredible, but if root dir is given, path to lib is found, but not path to include...
- cmake_d="$cmake_d -D OPENIMAGEIO_INCLUDE_DIR=$INST/oiio/include"
+ cmake_d="$cmake_d -D OpenImageIO_ROOT=$INST/oiio"
fi
if [ ! -z $LLVM_VERSION_FOUND ]; then
@@ -3844,7 +3841,7 @@ install_DEB() {
libbz2-dev libncurses5-dev libssl-dev liblzma-dev libreadline-dev \
libopenal-dev libglew-dev yasm $THEORA_DEV $VORBIS_DEV $OGG_DEV \
libsdl2-dev libfftw3-dev patch bzip2 libxml2-dev libtinyxml-dev libjemalloc-dev \
- libgmp-dev libpugixml-dev libpotrace-dev libhpdf-dev"
+ libgmp-dev libpugixml-dev libpotrace-dev libhpdf-dev libzstd-dev"
# libglewmx-dev (broken in deb testing currently...)
VORBIS_USE=true
@@ -3985,13 +3982,9 @@ install_DEB() {
version_ge $_glew "1.7.0"
if [ $? -eq 1 ]; then
WARNING "OpenSubdiv disabled because GLEW-$_glew is not enough"
- WARNING "Blender will not use system GLEW library"
OSD_SKIP=true
- NO_SYSTEM_GLEW=true
else
WARNING "OpenSubdiv will compile with GLEW-$_glew but with limited capability"
- WARNING "Blender will not use system GLEW library"
- NO_SYSTEM_GLEW=true
fi
fi
@@ -4112,6 +4105,8 @@ install_DEB() {
PRINT ""
+ # Debian OIIO includes again libopencv, without even properly dealing with this dependency...
+ OIIO_FORCE_BUILD=true
if [ "$OIIO_SKIP" = true ]; then
WARNING "Skipping OpenImageIO installation, as requested..."
elif [ "$OIIO_FORCE_BUILD" = true ]; then
@@ -4514,7 +4509,7 @@ install_RPM() {
wget ncurses-devel readline-devel $OPENJPEG_DEV openal-soft-devel \
glew-devel yasm $THEORA_DEV $VORBIS_DEV $OGG_DEV patch \
libxml2-devel yaml-cpp-devel tinyxml-devel jemalloc-devel \
- gmp-devel pugixml-devel potrace-devel libharu-devel"
+ gmp-devel pugixml-devel potrace-devel libharu-devel libzstd-devel"
OPENJPEG_USE=true
VORBIS_USE=true
@@ -5092,10 +5087,11 @@ install_ARCH() {
BASE_DEVEL=`pacman -Sgq base-devel | sed -e 's/^gcc$/gcc-multilib/g' | paste -s -d' '`
fi
- _packages="$BASE_DEVEL git cmake fontconfig \
+ _packages="$BASE_DEVEL git cmake fontconfig flex \
libxi libxcursor libxrandr libxinerama glew libpng libtiff wget openal \
$OPENJPEG_DEV $VORBIS_DEV $OGG_DEV $THEORA_DEV yasm sdl2 fftw \
- libxml2 yaml-cpp tinyxml python-requests jemalloc gmp potrace pugixml libharu"
+ libxml2 yaml-cpp tinyxml python-requests jemalloc gmp potrace pugixml libharu \
+ zstd"
OPENJPEG_USE=true
VORBIS_USE=true
@@ -5959,12 +5955,6 @@ print_info() {
fi
fi
- if [ "$NO_SYSTEM_GLEW" = true ]; then
- _1="-D WITH_SYSTEM_GLEW=OFF"
- PRINT " $_1"
- _buildargs="$_buildargs $_1"
- fi
-
if [ "$FFMPEG_SKIP" = false ]; then
_1="-D WITH_CODEC_FFMPEG=ON"
_2="-D FFMPEG_LIBRARIES='avformat;avcodec;avutil;avdevice;swscale;swresample;lzma;rt;`print_info_ffmpeglink`'"
diff --git a/build_files/build_environment/patches/oidn.diff b/build_files/build_environment/patches/oidn.diff
deleted file mode 100644
index db1015b8958..00000000000
--- a/build_files/build_environment/patches/oidn.diff
+++ /dev/null
@@ -1,10 +0,0 @@
---- external_openimagedenoise/cmake/oidn_ispc.cmake 2021-02-15 17:29:34.000000000 +0100
-+++ external_openimagedenoise/cmake/oidn_ispc.cmake2 2021-02-15 17:29:28.000000000 +0100
-@@ -98,7 +98,7 @@
- elseif(OIDN_ARCH STREQUAL "ARM64")
- set(ISPC_ARCHITECTURE "aarch64")
- if(APPLE)
-- set(ISPC_TARGET_OS "--target-os=ios")
-+ set(ISPC_TARGET_OS "--target-os=macos")
- endif()
- endif()
diff --git a/build_files/build_environment/patches/openimageio.diff b/build_files/build_environment/patches/openimageio.diff
index 9db037db288..d05fc1f295f 100644
--- a/build_files/build_environment/patches/openimageio.diff
+++ b/build_files/build_environment/patches/openimageio.diff
@@ -34,24 +34,3 @@ diff -Naur orig/src/include/OpenImageIO/platform.h external_openimageio/src/incl
# include <windows.h>
#endif
-diff -Naur orig/src/libutil/ustring.cpp external_openimageio/src/libutil/ustring.cpp
---- orig/src/libutil/ustring.cpp 2020-05-11 05:43:52.000000000 +0200
-+++ external_openimageio/src/libutil/ustring.cpp 2020-11-26 12:06:08.000000000 +0100
-@@ -337,6 +337,8 @@
- // the std::string to make it point to our chars! In such a case, the
- // destructor will be careful not to allow a deallocation.
-
-+ // Disable internal std::string for Apple silicon based Macs
-+#if !(defined(__APPLE__) && defined(__arm64__))
- #if defined(__GNUC__) && !defined(_LIBCPP_VERSION) \
- && defined(_GLIBCXX_USE_CXX11_ABI) && _GLIBCXX_USE_CXX11_ABI
- // NEW gcc ABI
-@@ -382,7 +384,7 @@
- return;
- }
- #endif
--
-+#endif
- // Remaining cases - just assign the internal string. This may result
- // in double allocation for the chars. If you care about that, do
- // something special for your platform, much like we did for gcc and \ No newline at end of file
diff --git a/build_files/build_environment/patches/openmp.diff b/build_files/build_environment/patches/openmp.diff
deleted file mode 100644
index 201ab5c7713..00000000000
--- a/build_files/build_environment/patches/openmp.diff
+++ /dev/null
@@ -1,23 +0,0 @@
-diff --git a/runtime/src/z_Linux_asm.S b/runtime/src/z_Linux_asm.S
-index 0d8885e..42aa5ad 100644
---- a/runtime/src/z_Linux_asm.S
-+++ b/runtime/src/z_Linux_asm.S
-@@ -1540,10 +1540,12 @@ __kmp_unnamed_critical_addr:
- .comm .gomp_critical_user_,32,8
- .data
- .align 8
-- .global __kmp_unnamed_critical_addr
--__kmp_unnamed_critical_addr:
-+ .global ___kmp_unnamed_critical_addr
-+___kmp_unnamed_critical_addr:
- .8byte .gomp_critical_user_
-- .size __kmp_unnamed_critical_addr,8
-+# if !(KMP_OS_DARWIN)
-+ .size ___kmp_unnamed_critical_addr,8
-+# endif
- #endif /* KMP_ARCH_PPC64 || KMP_ARCH_AARCH64 */
-
- #if KMP_OS_LINUX
-
-
-
diff --git a/build_files/build_environment/patches/osl.diff b/build_files/build_environment/patches/osl.diff
index cd1b58bf580..54885323571 100644
--- a/build_files/build_environment/patches/osl.diff
+++ b/build_files/build_environment/patches/osl.diff
@@ -1,18 +1,3 @@
-diff -Naur OpenShadingLanguage-Release-1.9.9/src/cmake/flexbison.cmake.rej external_osl/src/cmake/flexbison.cmake.rej
---- OpenShadingLanguage-Release-1.9.9/src/cmake/flexbison.cmake.rej 1969-12-31 17:00:00 -0700
-+++ external_osl/src/cmake/flexbison.cmake.rej 2018-08-24 17:42:11 -0600
-@@ -0,0 +1,11 @@
-+--- src/cmake/flexbison.cmake 2018-05-01 16:39:02 -0600
-++++ src/cmake/flexbison.cmake 2018-08-24 10:24:03 -0600
-+@@ -77,7 +77,7 @@
-+ DEPENDS ${${compiler_headers}}
-+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
-+ ADD_CUSTOM_COMMAND ( OUTPUT ${flexoutputcxx}
-+- COMMAND ${FLEX_EXECUTABLE} -o ${flexoutputcxx} "${CMAKE_CURRENT_SOURCE_DIR}/${flexsrc}"
-++ COMMAND ${FLEX_EXECUTABLE} ${FLEX_EXTRA_OPTIONS} -o ${flexoutputcxx} "${CMAKE_CURRENT_SOURCE_DIR}/${flexsrc}"
-+ MAIN_DEPENDENCY ${flexsrc}
-+ DEPENDS ${${compiler_headers}}
-+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
diff -Naur OpenShadingLanguage-Release-1.9.9/src/include/OSL/llvm_util.h external_osl/src/include/OSL/llvm_util.h
--- OpenShadingLanguage-Release-1.9.9/src/include/OSL/llvm_util.h 2018-05-01 16:39:02 -0600
+++ external_osl/src/include/OSL/llvm_util.h 2018-08-25 14:05:00 -0600
@@ -63,19 +48,50 @@ diff -Naur org/CMakeLists.txt external_osl/CMakeLists.txt
set (OSL_NO_DEFAULT_TEXTURESYSTEM OFF CACHE BOOL "Do not use create a raw OIIO::TextureSystem")
if (OSL_NO_DEFAULT_TEXTURESYSTEM)
-diff --git a/src/liboslexec/llvm_util.cpp b/src/liboslexec/llvm_util.cpp
-index 445f6400..3d468de2 100644
---- a/src/liboslexec/llvm_util.cpp
-+++ b/src/liboslexec/llvm_util.cpp
-@@ -3430,8 +3430,9 @@ LLVM_Util::call_function (llvm::Value *func, cspan<llvm::Value *> args)
- #endif
- //llvm_gen_debug_printf (std::string("start ") + std::string(name));
- #if OSL_LLVM_VERSION >= 110
-- OSL_DASSERT(llvm::isa<llvm::Function>(func));
-- llvm::Value *r = builder().CreateCall(llvm::cast<llvm::Function>(func), llvm::ArrayRef<llvm::Value *>(args.data(), args.size()));
-+ llvm::Value* r = builder().CreateCall(
-+ llvm::cast<llvm::FunctionType>(func->getType()->getPointerElementType()), func,
-+ llvm::ArrayRef<llvm::Value*>(args.data(), args.size()));
- #else
- llvm::Value *r = builder().CreateCall (func, llvm::ArrayRef<llvm::Value *>(args.data(), args.size()));
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 990f50d69..46ef7351d 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -252,11 +252,9 @@ install (EXPORT OSL_EXPORTED_TARGETS
+ FILE ${OSL_TARGETS_EXPORT_NAME}
+ NAMESPACE ${PROJECT_NAME}::)
+
+-
+-
+-
+-osl_add_all_tests()
+-
++if (${PROJECT_NAME}_BUILD_TESTS AND NOT ${PROJECT_NAME}_IS_SUBPROJECT)
++ osl_add_all_tests()
++endif ()
+
+ if (NOT ${PROJECT_NAME}_IS_SUBPROJECT)
+ include (packaging)
+diff -Naur external_osl_orig/src/cmake/externalpackages.cmake external_osl/src/cmake/externalpackages.cmake
+--- external_osl_orig/src/cmake/externalpackages.cmake 2021-06-01 13:44:18 -0600
++++ external_osl/src/cmake/externalpackages.cmake 2021-06-28 07:44:32 -0600
+@@ -80,6 +80,7 @@
+
+
+ checked_find_package (ZLIB REQUIRED) # Needed by several packages
++checked_find_package (PNG REQUIRED) # Needed since OIIO needs it
+
+ # IlmBase & OpenEXR
+ checked_find_package (OpenEXR REQUIRED
+diff -Naur external_osl_orig/src/liboslcomp/oslcomp.cpp external_osl/src/liboslcomp/oslcomp.cpp
+--- external_osl_orig/src/liboslcomp/oslcomp.cpp 2021-06-01 13:44:18 -0600
++++ external_osl/src/liboslcomp/oslcomp.cpp 2021-06-28 09:11:06 -0600
+@@ -21,6 +21,13 @@
+ #if !defined(__STDC_CONSTANT_MACROS)
+ # define __STDC_CONSTANT_MACROS 1
#endif
++
++// clang uses CALLBACK in its templates which causes issues if it is already defined
++#ifdef _WIN32 && defined(CALLBACK)
++# undef CALLBACK
++#endif
++
++//
+ #include <clang/Basic/TargetInfo.h>
+ #include <clang/Frontend/CompilerInstance.h>
+ #include <clang/Frontend/TextDiagnosticPrinter.h>
diff --git a/build_files/cmake/cmake_netbeans_project.py b/build_files/cmake/cmake_netbeans_project.py
index cfc3ff6b6fa..a16fc42c9b7 100755
--- a/build_files/cmake/cmake_netbeans_project.py
+++ b/build_files/cmake/cmake_netbeans_project.py
@@ -82,7 +82,7 @@ def create_nb_project_main():
make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
make_exe_basename = os.path.basename(make_exe)
- # --------------- NB specific
+ # --------------- NetBeans specific.
defines = [("%s=%s" % cdef) if cdef[1] else cdef[0] for cdef in defines]
defines += [cdef.replace("#define", "").strip() for cdef in cmake_compiler_defines()]
diff --git a/build_files/cmake/config/blender_developer.cmake b/build_files/cmake/config/blender_developer.cmake
index 94f87a2d362..7be9516d9dc 100644
--- a/build_files/cmake/config/blender_developer.cmake
+++ b/build_files/cmake/config/blender_developer.cmake
@@ -7,7 +7,6 @@
set(WITH_ASSERT_ABORT ON CACHE BOOL "" FORCE)
set(WITH_BUILDINFO OFF CACHE BOOL "" FORCE)
set(WITH_COMPILER_ASAN ON CACHE BOOL "" FORCE)
-set(WITH_CYCLES_DEBUG ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_NATIVE_ONLY ON CACHE BOOL "" FORCE)
set(WITH_DOC_MANPAGE OFF CACHE BOOL "" FORCE)
set(WITH_GTESTS ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake
index 90188751fc0..70973eeda99 100644
--- a/build_files/cmake/platform/platform_apple.cmake
+++ b/build_files/cmake/platform/platform_apple.cmake
@@ -404,7 +404,7 @@ endif()
# CMake FindOpenMP doesn't know about AppleClang before 3.12, so provide custom flags.
if(WITH_OPENMP)
- if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0")
+ if(CMAKE_C_COMPILER_ID MATCHES "Clang")
# Use OpenMP from our precompiled libraries.
message(STATUS "Using ${LIBDIR}/openmp for OpenMP")
set(OPENMP_CUSTOM ON)
@@ -480,10 +480,8 @@ else()
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -mdynamic-no-pic")
endif()
-if(${XCODE_VERSION} VERSION_EQUAL 5 OR ${XCODE_VERSION} VERSION_GREATER 5)
- # Xcode 5 is always using CLANG, which has too low template depth of 128 for libmv
- string(APPEND CMAKE_CXX_FLAGS " -ftemplate-depth=1024")
-endif()
+# Clang has too low template depth of 128 for libmv.
+string(APPEND CMAKE_CXX_FLAGS " -ftemplate-depth=1024")
# Avoid conflicts with Luxrender, and other plug-ins that may use the same
# libraries as Blender with a different version or build options.
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index 7f62399ac4f..ffdbbc3f8c5 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -581,8 +581,6 @@ if(WITH_GHOST_WAYLAND)
pkg_check_modules(wayland-cursor REQUIRED wayland-cursor)
pkg_check_modules(dbus REQUIRED dbus-1)
- set(WITH_GL_EGL ON)
-
list(APPEND PLATFORM_LINKLIBS
${wayland-client_LINK_LIBRARIES}
${wayland-egl_LINK_LIBRARIES}
diff --git a/build_files/config/README.md b/build_files/config/README.md
index 6c429e4e58d..6ce601104af 100644
--- a/build_files/config/README.md
+++ b/build_files/config/README.md
@@ -1,8 +1,11 @@
Pipeline Config
===============
-This configuration file is used by buildbot new pipeline for the `update-code` step.
+This configuration file is used by buildbot new build pipeline for the `update-code` step.
-It will soon be used by the ../utils/make_update.py script.
+It will also be used by the `../utils/make_update.py` script in the near future.
-Both buildbot and developers will eventually use the same configuration file.
+NOTES:
+* Keep both `yaml` and `json` files in sync until deployment of build pipeline updates.
+* The `json` file be removed once all branches are running with the `yaml` file.
+* Expected buildbot pipeline update is *Friday, July 30th* or *Monday August, 2nd*.
diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json
index c9a5e25eaaf..d914cf85eae 100644
--- a/build_files/config/pipeline_config.json
+++ b/build_files/config/pipeline_config.json
@@ -41,7 +41,7 @@
},
"cuda11":
{
- "version": "11.3"
+ "version": "11.4"
}
},
"cmake":
diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml
new file mode 100644
index 00000000000..611df59caec
--- /dev/null
+++ b/build_files/config/pipeline_config.yaml
@@ -0,0 +1,70 @@
+#
+# Used by Buildbot build pipeline make_update.py script only for now
+# We intended to update the make_update.py in the branches to use this file eventually
+#
+update-code:
+ git:
+ submodules:
+ - branch: master
+ commit_id: HEAD
+ path: release/scripts/addons
+ - branch: master
+ commit_id: HEAD
+ path: release/scripts/addons_contrib
+ - branch: master
+ commit_id: HEAD
+ path: release/datafiles/locale
+ - branch: master
+ commit_id: HEAD
+ path: source/tools
+ svn:
+ libraries:
+ darwin-arm64:
+ branch: trunk
+ commit_id: HEAD
+ path: lib/darwin_arm64
+ darwin-x86_64:
+ branch: trunk
+ commit_id: HEAD
+ path: lib/darwin
+ linux-x86_64:
+ branch: trunk
+ commit_id: HEAD
+ path: lib/linux_centos7_x86_64
+ windows-amd64:
+ branch: trunk
+ commit_id: HEAD
+ path: lib/win64_vc15
+ tests:
+ branch: trunk
+ commit_id: HEAD
+ path: lib/tests
+ benchmarks:
+ branch: trunk
+ commit_id: HEAD
+ path: lib/benchmarks
+
+#
+# Buildbot only configs
+#
+buildbot:
+ gcc:
+ version: '9.0.0'
+ cuda10:
+ version: '10.1.0'
+ cuda11:
+ version: '11.4.0'
+ optix:
+ version: '7.1.0'
+ cmake:
+ default:
+ version: any
+ overrides: {}
+ darwin-arm64:
+ overrides: {}
+ darwin-x86_64:
+ overrides: {}
+ linux-x86_64:
+ overrides: {}
+ windows-amd64:
+ overrides: {}
diff --git a/doc/manpage/blender.1.py b/doc/manpage/blender.1.py
index 950a2119f91..020bab05a21 100755
--- a/doc/manpage/blender.1.py
+++ b/doc/manpage/blender.1.py
@@ -122,7 +122,7 @@ is a full-featured 3D application. It supports the entirety of the 3D pipeline -
'''modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
Use Blender to create 3D images and animations, films and commercials, content for games, '''
-r'''architectural and industrial visualizatons, and scientific visualizations.
+r'''architectural and industrial visualizations, and scientific visualizations.
https://www.blender.org''')
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index 4be27e0f0e8..d6c1d7b51b8 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -1047,6 +1047,7 @@ context_type_map = {
"annotation_data": ("GreasePencil", False),
"annotation_data_owner": ("ID", False),
"armature": ("Armature", False),
+ "asset_library": ("AssetLibraryReference", False),
"bone": ("Bone", False),
"brush": ("Brush", False),
"camera": ("Camera", False),
@@ -1113,6 +1114,7 @@ context_type_map = {
"texture_slot": ("MaterialTextureSlot", False),
"texture_user": ("ID", False),
"texture_user_property": ("Property", False),
+ "ui_list": ("UIList", False),
"vertex_paint_object": ("Object", False),
"view_layer": ("ViewLayer", False),
"visible_bones": ("EditBone", True),
diff --git a/extern/glog/README.blender b/extern/glog/README.blender
index 5b4dab199bf..bc2ca9f958e 100644
--- a/extern/glog/README.blender
+++ b/extern/glog/README.blender
@@ -7,3 +7,4 @@ Local modifications:
checks for functions and so are needed.
* Added special definitions of HAVE_SNPRINTF and HAVE_LIB_GFLAGS
in Windows' specific config.h.
+* Silenced syscall deprecation warnings on macOS >= 10.12.
diff --git a/extern/glog/src/raw_logging.cc b/extern/glog/src/raw_logging.cc
index 3bbfda31868..15d14f6e4f5 100644
--- a/extern/glog/src/raw_logging.cc
+++ b/extern/glog/src/raw_logging.cc
@@ -59,7 +59,7 @@
# include <unistd.h>
#endif
-#if defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)
+#if (defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)) && (!(defined OS_MACOSX))
# define safe_write(fd, s, len) syscall(SYS_write, fd, s, len)
#else
// Not so safe, but what can you do?
diff --git a/extern/glog/src/utilities.cc b/extern/glog/src/utilities.cc
index 25c4b760f1c..6387e14e123 100644
--- a/extern/glog/src/utilities.cc
+++ b/extern/glog/src/utilities.cc
@@ -259,7 +259,13 @@ pid_t GetTID() {
#endif
static bool lacks_gettid = false;
if (!lacks_gettid) {
+#ifdef OS_MACOSX
+ uint64_t tid64;
+ const int error = pthread_threadid_np(NULL, &tid64);
+ pid_t tid = error ? -1 : (pid_t)tid64;
+#else
pid_t tid = syscall(__NR_gettid);
+#endif
if (tid != -1) {
return tid;
}
diff --git a/extern/rangetree/intern/generic_alloc_impl.h b/extern/rangetree/intern/generic_alloc_impl.h
index 0f9f5184637..fd6e85a155a 100644
--- a/extern/rangetree/intern/generic_alloc_impl.h
+++ b/extern/rangetree/intern/generic_alloc_impl.h
@@ -32,7 +32,7 @@
* - #TPOOL_STRUCT: Name for pool struct name.
* - #TPOOL_CHUNK_SIZE: Chunk size (optional), use 64kb when not defined.
*
- * \note #TPOOL_ALLOC_TYPE must be at least ``sizeof(void *)``.
+ * \note #TPOOL_ALLOC_TYPE must be at least `sizeof(void *)`.
*
* Defines the API, uses #TPOOL_IMPL_PREFIX to prefix each function.
*
diff --git a/intern/atomic/intern/atomic_ops_unix.h b/intern/atomic/intern/atomic_ops_unix.h
index dc1e71cda76..b08a0e9bc28 100644
--- a/intern/atomic/intern/atomic_ops_unix.h
+++ b/intern/atomic/intern/atomic_ops_unix.h
@@ -49,9 +49,9 @@
#include "atomic_ops_utils.h"
-#if defined(__arm__)
-/* Attempt to fix compilation error on Debian armel kernel.
- * arm7 architecture does have both 32 and 64bit atomics, however
+#if defined(__arm__) || defined(__riscv)
+/* Attempt to fix compilation error on Debian armel and RISC-V kernels.
+ * Both architectures do have both 32 and 64bit atomics, however
* its gcc doesn't have __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n defined.
*/
# define JE_FORCE_SYNC_COMPARE_AND_SWAP_1
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index b01bf1bd1e2..381248e9bf1 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -307,9 +307,9 @@ if(WITH_CYCLES_LOGGING)
)
endif()
-# Debugging capabilities (debug passes etc).
-if(WITH_CYCLES_DEBUG)
- add_definitions(-DWITH_CYCLES_DEBUG)
+# NaN debugging
+if(WITH_CYCLES_DEBUG_NAN)
+ add_definitions(-DWITH_CYCLES_DEBUG_NAN)
endif()
if(NOT OPENIMAGEIO_PUGIXML_FOUND)
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index dc53c4db48f..489a883f098 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -235,9 +235,12 @@ def system_info():
def list_render_passes(scene, srl):
- # Builtin Blender passes.
+ crl = srl.cycles
+
+ # Combined pass.
yield ("Combined", "RGBA", 'COLOR')
+ # Data passes.
if srl.use_pass_z: yield ("Depth", "Z", 'VALUE')
if srl.use_pass_mist: yield ("Mist", "Z", 'VALUE')
if srl.use_pass_normal: yield ("Normal", "XYZ", 'VECTOR')
@@ -245,8 +248,8 @@ def list_render_passes(scene, srl):
if srl.use_pass_uv: yield ("UV", "UVA", 'VECTOR')
if srl.use_pass_object_index: yield ("IndexOB", "X", 'VALUE')
if srl.use_pass_material_index: yield ("IndexMA", "X", 'VALUE')
- if srl.use_pass_shadow: yield ("Shadow", "RGB", 'COLOR')
- if srl.use_pass_ambient_occlusion: yield ("AO", "RGB", 'COLOR')
+
+ # Light passes.
if srl.use_pass_diffuse_direct: yield ("DiffDir", "RGB", 'COLOR')
if srl.use_pass_diffuse_indirect: yield ("DiffInd", "RGB", 'COLOR')
if srl.use_pass_diffuse_color: yield ("DiffCol", "RGB", 'COLOR')
@@ -256,19 +259,16 @@ def list_render_passes(scene, srl):
if srl.use_pass_transmission_direct: yield ("TransDir", "RGB", 'COLOR')
if srl.use_pass_transmission_indirect: yield ("TransInd", "RGB", 'COLOR')
if srl.use_pass_transmission_color: yield ("TransCol", "RGB", 'COLOR')
+ if crl.use_pass_volume_direct: yield ("VolumeDir", "RGB", 'COLOR')
+ if crl.use_pass_volume_indirect: yield ("VolumeInd", "RGB", 'COLOR')
if srl.use_pass_emit: yield ("Emit", "RGB", 'COLOR')
if srl.use_pass_environment: yield ("Env", "RGB", 'COLOR')
+ if srl.use_pass_shadow: yield ("Shadow", "RGB", 'COLOR')
+ if srl.use_pass_ambient_occlusion: yield ("AO", "RGB", 'COLOR')
- # Cycles specific passes.
- crl = srl.cycles
+ # Debug passes.
if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE')
- if crl.pass_debug_bvh_traversed_nodes: yield ("Debug BVH Traversed Nodes", "X", 'VALUE')
- if crl.pass_debug_bvh_traversed_instances: yield ("Debug BVH Traversed Instances", "X", 'VALUE')
- if crl.pass_debug_bvh_intersections: yield ("Debug BVH Intersections", "X", 'VALUE')
- if crl.pass_debug_ray_bounces: yield ("Debug Ray Bounces", "X", 'VALUE')
if crl.pass_debug_sample_count: yield ("Debug Sample Count", "X", 'VALUE')
- if crl.use_pass_volume_direct: yield ("VolumeDir", "RGB", 'COLOR')
- if crl.use_pass_volume_indirect: yield ("VolumeInd", "RGB", 'COLOR')
# Cryptomatte passes.
crypto_depth = (srl.pass_cryptomatte_depth + 1) // 2
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 030fa19fa75..5cce696f82d 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -263,6 +263,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
name="Use Denoising",
description="Denoise the rendered image",
default=False,
+ update=update_render_passes,
)
use_preview_denoising: BoolProperty(
name="Use Viewport Denoising",
@@ -1400,30 +1401,6 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
- pass_debug_bvh_traversed_nodes: BoolProperty(
- name="Debug BVH Traversed Nodes",
- description="Store Debug BVH Traversed Nodes pass",
- default=False,
- update=update_render_passes,
- )
- pass_debug_bvh_traversed_instances: BoolProperty(
- name="Debug BVH Traversed Instances",
- description="Store Debug BVH Traversed Instances pass",
- default=False,
- update=update_render_passes,
- )
- pass_debug_bvh_intersections: BoolProperty(
- name="Debug BVH Intersections",
- description="Store Debug BVH Intersections",
- default=False,
- update=update_render_passes,
- )
- pass_debug_ray_bounces: BoolProperty(
- name="Debug Ray Bounces",
- description="Store Debug Ray Bounces pass",
- default=False,
- update=update_render_passes,
- )
pass_debug_render_time: BoolProperty(
name="Debug Render Time",
description="Render time in milliseconds per sample and pixel",
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index f5c9372e673..b91f5a73dee 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -970,29 +970,6 @@ class CYCLES_RENDER_PT_passes_crypto(CyclesButtonsPanel, ViewLayerCryptomattePan
bl_parent_id = "CYCLES_RENDER_PT_passes"
-class CYCLES_RENDER_PT_passes_debug(CyclesButtonsPanel, Panel):
- bl_label = "Debug"
- bl_context = "view_layer"
- bl_parent_id = "CYCLES_RENDER_PT_passes"
-
- @classmethod
- def poll(cls, context):
- import _cycles
- return _cycles.with_cycles_debug
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False
-
- cycles_view_layer = context.view_layer.cycles
-
- layout.prop(cycles_view_layer, "pass_debug_bvh_traversed_nodes")
- layout.prop(cycles_view_layer, "pass_debug_bvh_traversed_instances")
- layout.prop(cycles_view_layer, "pass_debug_bvh_intersections")
- layout.prop(cycles_view_layer, "pass_debug_ray_bounces")
-
-
class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, ViewLayerAOVPanel):
bl_label = "Shader AOV"
bl_context = "view_layer"
@@ -1170,7 +1147,7 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel):
col = row.column(align=True)
col.operator("object.material_slot_add", icon='ADD', text="")
col.operator("object.material_slot_remove", icon='REMOVE', text="")
-
+ col.separator()
col.menu("MATERIAL_MT_context_menu", icon='DOWNARROW_HLT', text="")
if is_sortable:
@@ -1185,16 +1162,15 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel):
row.operator("object.material_slot_select", text="Select")
row.operator("object.material_slot_deselect", text="Deselect")
- split = layout.split(factor=0.65)
+ row = layout.row()
if ob:
- split.template_ID(ob, "active_material", new="material.new")
- row = split.row()
+ row.template_ID(ob, "active_material", new="material.new")
if slot:
- row.prop(slot, "link", text="")
- else:
- row.label()
+ icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'
+ row.prop(slot, "link", text="", icon=icon_link, icon_only=True)
+
elif mat:
split.template_ID(space, "pin_id")
split.separator()
@@ -2353,7 +2329,6 @@ classes = (
CYCLES_RENDER_PT_passes_data,
CYCLES_RENDER_PT_passes_light,
CYCLES_RENDER_PT_passes_crypto,
- CYCLES_RENDER_PT_passes_debug,
CYCLES_RENDER_PT_passes_aov,
CYCLES_RENDER_PT_filter,
CYCLES_RENDER_PT_override,
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index 11158532738..ecadc78cbbf 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -1055,10 +1055,45 @@ static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob)
return BL::MeshSequenceCacheModifier(PointerRNA_NULL);
}
+/* Check whether some of "built-in" motion-related attributes are needed to be exported (includes
+ * things like velocity from cache modifier, fluid simulation).
+ *
+ * NOTE: This code is run prior to object motion blur initialization. so can not access properties
+ * set by `sync_object_motion_init()`. */
+static bool mesh_need_motion_attribute(BL::Object &b_ob, Scene *scene)
+{
+ const Scene::MotionType need_motion = scene->need_motion();
+ if (need_motion == Scene::MOTION_NONE) {
+ /* Simple case: neither motion pass nor motion blur is needed, no need in the motion related
+ * attributes. */
+ return false;
+ }
+
+ if (need_motion == Scene::MOTION_BLUR) {
+ /* A bit tricky and implicit case:
+ * - Motion blur is enabled in the scene, which implies specific number of time steps for
+ * objects.
+ * - If the object has motion blur disabled on it, it will have 0 time steps.
+ * - Motion attribute expects non-zero time steps.
+ *
+ * Avoid adding motion attributes if the motion blur will enforce 0 motion steps. */
+ PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
+ const bool use_motion = get_boolean(cobject, "use_motion_blur");
+ if (!use_motion) {
+ return false;
+ }
+ }
+
+ /* Motion pass which implies 3 motion steps, or motion blur which is not disabled on object
+ * level. */
+ return true;
+}
+
static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *mesh)
{
- if (scene->need_motion() == Scene::MOTION_NONE)
+ if (!mesh_need_motion_attribute(b_ob, scene)) {
return;
+ }
BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob);
@@ -1102,8 +1137,9 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *me
static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh)
{
- if (scene->need_motion() == Scene::MOTION_NONE)
+ if (!mesh_need_motion_attribute(b_ob, scene)) {
return;
+ }
BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob);
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index 23684b96242..e178a7db338 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -1116,14 +1116,6 @@ void *CCL_python_module_init()
PyModule_AddStringConstant(mod, "osl_version_string", "unknown");
#endif
-#ifdef WITH_CYCLES_DEBUG
- PyModule_AddObject(mod, "with_cycles_debug", Py_True);
- Py_INCREF(Py_True);
-#else
- PyModule_AddObject(mod, "with_cycles_debug", Py_False);
- Py_INCREF(Py_False);
-#endif
-
#ifdef WITH_NETWORK
PyModule_AddObject(mod, "with_network", Py_True);
Py_INCREF(Py_True);
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 0fcea2cd544..193bc2c9752 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -538,12 +538,6 @@ PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass)
MAP_PASS("BakePrimitive", PASS_BAKE_PRIMITIVE);
MAP_PASS("BakeDifferential", PASS_BAKE_DIFFERENTIAL);
-#ifdef __KERNEL_DEBUG__
- MAP_PASS("Debug BVH Traversed Nodes", PASS_BVH_TRAVERSED_NODES);
- MAP_PASS("Debug BVH Traversed Instances", PASS_BVH_TRAVERSED_INSTANCES);
- MAP_PASS("Debug BVH Intersections", PASS_BVH_INTERSECTIONS);
- MAP_PASS("Debug Ray Bounces", PASS_RAY_BOUNCES);
-#endif
MAP_PASS("Debug Render Time", PASS_RENDER_TIME);
MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER);
MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT);
@@ -641,24 +635,6 @@ vector<Pass> BlenderSync::sync_render_passes(BL::Scene &b_scene,
}
}
-#ifdef __KERNEL_DEBUG__
- if (get_boolean(crl, "pass_debug_bvh_traversed_nodes")) {
- b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_BVH_TRAVERSED_NODES, passes, "Debug BVH Traversed Nodes");
- }
- if (get_boolean(crl, "pass_debug_bvh_traversed_instances")) {
- b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes, "Debug BVH Traversed Instances");
- }
- if (get_boolean(crl, "pass_debug_bvh_intersections")) {
- b_engine.add_pass("Debug BVH Intersections", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_BVH_INTERSECTIONS, passes, "Debug BVH Intersections");
- }
- if (get_boolean(crl, "pass_debug_ray_bounces")) {
- b_engine.add_pass("Debug Ray Bounces", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_RAY_BOUNCES, passes, "Debug Ray Bounces");
- }
-#endif
if (get_boolean(crl, "pass_debug_render_time")) {
b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str());
Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time");
diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp
index 5b62292ca55..2d2fcb38705 100644
--- a/intern/cycles/device/cuda/device_cuda_impl.cpp
+++ b/intern/cycles/device/cuda/device_cuda_impl.cpp
@@ -351,9 +351,6 @@ string CUDADevice::compile_kernel_get_common_cflags(
if (extra_cflags) {
cflags += string(" ") + string(extra_cflags);
}
-# ifdef WITH_CYCLES_DEBUG
- cflags += " -D__KERNEL_DEBUG__";
-# endif
if (split) {
cflags += " -D__SPLIT__";
@@ -461,18 +458,19 @@ string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_featu
const int nvcc_cuda_version = cuewCompilerVersion();
VLOG(1) << "Found nvcc " << nvcc << ", CUDA version " << nvcc_cuda_version << ".";
- if (nvcc_cuda_version < 80) {
+ if (nvcc_cuda_version < 101) {
printf(
"Unsupported CUDA version %d.%d detected, "
- "you need CUDA 8.0 or newer.\n",
+ "you need CUDA 10.1 or newer.\n",
nvcc_cuda_version / 10,
nvcc_cuda_version % 10);
return string();
}
- else if (!(nvcc_cuda_version == 101 || nvcc_cuda_version == 102)) {
+ else if (!(nvcc_cuda_version == 101 || nvcc_cuda_version == 102 || nvcc_cuda_version == 111 ||
+ nvcc_cuda_version == 112 || nvcc_cuda_version == 113 || nvcc_cuda_version == 114)) {
printf(
"CUDA version %d.%d detected, build may succeed but only "
- "CUDA 10.1 and 10.2 are officially supported.\n",
+ "CUDA 10.1 to 11.4 are officially supported.\n",
nvcc_cuda_version / 10,
nvcc_cuda_version % 10);
}
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index c0c0aeb4aa8..be09779bd89 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -58,8 +58,8 @@
#include "util/util_function.h"
#include "util/util_logging.h"
#include "util/util_map.h"
-#include "util/util_openimagedenoise.h"
#include "util/util_opengl.h"
+#include "util/util_openimagedenoise.h"
#include "util/util_optimization.h"
#include "util/util_progress.h"
#include "util/util_system.h"
diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index 392fec4d57b..6f9a7943722 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -234,9 +234,6 @@ class OptiXDevice : public CUDADevice {
}
};
# endif
-# if OPTIX_ABI_VERSION >= 41 && defined(WITH_CYCLES_DEBUG)
- options.validationMode = OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALL;
-# endif
check_result_optix(optixDeviceContextCreate(cuContext, &options, &context));
# ifdef WITH_CYCLES_LOGGING
check_result_optix(optixDeviceContextSetLogCallback(
@@ -369,13 +366,8 @@ class OptiXDevice : public CUDADevice {
OptixModuleCompileOptions module_options = {};
module_options.maxRegisterCount = 0; // Do not set an explicit register limit
-# ifdef WITH_CYCLES_DEBUG
- module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0;
- module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
-# else
module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_3;
module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO;
-# endif
# if OPTIX_ABI_VERSION >= 41
module_options.boundValues = nullptr;
@@ -578,11 +570,7 @@ class OptiXDevice : public CUDADevice {
OptixPipelineLinkOptions link_options = {};
link_options.maxTraceDepth = 1;
-# ifdef WITH_CYCLES_DEBUG
- link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
-# else
link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO;
-# endif
# if OPTIX_ABI_VERSION < 24
link_options.overrideUsesMotionBlur = motion_blur;
# endif
diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp
index 715213175c9..31a2265700c 100644
--- a/intern/cycles/device/opencl/device_opencl_impl.cpp
+++ b/intern/cycles/device/opencl/device_opencl_impl.cpp
@@ -1968,10 +1968,6 @@ string OpenCLDevice::kernel_build_options(const string *debug_src)
build_options += "-D__KERNEL_OPENCL_DEBUG__ ";
}
-# ifdef WITH_CYCLES_DEBUG
- build_options += "-D__KERNEL_DEBUG__ ";
-# endif
-
# ifdef WITH_NANOVDB
if (info.has_nanovdb) {
build_options += "-DWITH_NANOVDB ";
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index ea0f16c9233..0ce33c51778 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -380,11 +380,16 @@ if(WITH_CYCLES_CUDA_BINARIES)
set(CUDA_VERSION "${CUDA_VERSION_MAJOR}${CUDA_VERSION_MINOR}")
# warn for other versions
- if((CUDA_VERSION MATCHES "101") OR (CUDA_VERSION MATCHES "102") OR (CUDA_VERSION MATCHES "111"))
+ if((CUDA_VERSION MATCHES "101") OR
+ (CUDA_VERSION MATCHES "102") OR
+ (CUDA_VERSION MATCHES "111") OR
+ (CUDA_VERSION MATCHES "112") OR
+ (CUDA_VERSION MATCHES "113") OR
+ (CUDA_VERSION MATCHES "114"))
else()
message(WARNING
"CUDA version ${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR} detected, "
- "build may succeed but only CUDA 10.1, 10.2 and 11.1 are officially supported")
+ "build may succeed but only CUDA 10.1 to 11.4 are officially supported")
endif()
# build for each arch
@@ -439,10 +444,6 @@ if(WITH_CYCLES_CUDA_BINARIES)
set(name ${name}_experimental)
endif()
- if(WITH_CYCLES_DEBUG)
- set(cuda_flags ${cuda_flags} -D __KERNEL_DEBUG__)
- endif()
-
if(WITH_NANOVDB)
set(cuda_flags ${cuda_flags}
-D WITH_NANOVDB
@@ -557,11 +558,6 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
--use_fast_math
-o ${output})
- if(WITH_CYCLES_DEBUG)
- set(cuda_flags ${cuda_flags}
- -D __KERNEL_DEBUG__)
- endif()
-
if(WITH_NANOVDB)
set(cuda_flags ${cuda_flags}
-D WITH_NANOVDB
diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h
index 8b2699ab807..89250a8d60a 100644
--- a/intern/cycles/kernel/bvh/bvh_traversal.h
+++ b/intern/cycles/kernel/bvh/bvh_traversal.h
@@ -67,8 +67,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
isect->prim = PRIM_NONE;
isect->object = OBJECT_NONE;
- BVH_DEBUG_INIT();
-
/* traversal loop */
do {
do {
@@ -118,7 +116,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
--stack_ptr;
}
}
- BVH_DEBUG_NEXT_NODE();
}
/* if node is leaf, fetch triangle list */
@@ -138,7 +135,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
switch (type & PRIMITIVE_ALL) {
case PRIMITIVE_TRIANGLE: {
for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
if (triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr)) {
/* shadow ray early termination */
@@ -151,7 +147,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
#if BVH_FEATURE(BVH_MOTION)
case PRIMITIVE_MOTION_TRIANGLE: {
for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
if (motion_triangle_intersect(
kg, isect, P, dir, ray->time, visibility, object, prim_addr)) {
@@ -169,7 +164,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
case PRIMITIVE_CURVE_RIBBON:
case PRIMITIVE_MOTION_CURVE_RIBBON: {
for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr);
kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL));
const bool hit = curve_intersect(
@@ -201,8 +195,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL;
node_addr = kernel_tex_fetch(__object_node, object);
-
- BVH_DEBUG_NEXT_INSTANCE();
}
}
} while (node_addr != ENTRYPOINT_SENTINEL);
diff --git a/intern/cycles/kernel/bvh/bvh_types.h b/intern/cycles/kernel/bvh/bvh_types.h
index 9f0879a2069..98e6ec25d15 100644
--- a/intern/cycles/kernel/bvh/bvh_types.h
+++ b/intern/cycles/kernel/bvh/bvh_types.h
@@ -42,33 +42,6 @@ CCL_NAMESPACE_BEGIN
#define BVH_FEATURE(f) (((BVH_FUNCTION_FEATURES) & (f)) != 0)
-/* Debugging helpers. */
-#ifdef __KERNEL_DEBUG__
-# define BVH_DEBUG_INIT() \
- do { \
- isect->num_traversed_nodes = 0; \
- isect->num_traversed_instances = 0; \
- isect->num_intersections = 0; \
- } while (0)
-# define BVH_DEBUG_NEXT_NODE() \
- do { \
- ++isect->num_traversed_nodes; \
- } while (0)
-# define BVH_DEBUG_NEXT_INTERSECTION() \
- do { \
- ++isect->num_intersections; \
- } while (0)
-# define BVH_DEBUG_NEXT_INSTANCE() \
- do { \
- ++isect->num_traversed_instances; \
- } while (0)
-#else /* __KERNEL_DEBUG__ */
-# define BVH_DEBUG_INIT()
-# define BVH_DEBUG_NEXT_NODE()
-# define BVH_DEBUG_NEXT_INTERSECTION()
-# define BVH_DEBUG_NEXT_INSTANCE()
-#endif /* __KERNEL_DEBUG__ */
-
CCL_NAMESPACE_END
#endif /* __BVH_TYPES__ */
diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h
index de65451d82c..562daf1286d 100644
--- a/intern/cycles/kernel/closure/bssrdf.h
+++ b/intern/cycles/kernel/closure/bssrdf.h
@@ -432,9 +432,9 @@ ccl_device void bssrdf_sample(const ShaderClosure *sc, float xi, float *r, float
xi *= bssrdf->channels;
if (xi < 1.0f) {
- radius = (bssrdf->radius.x > 0.0f) ?
- bssrdf->radius.x :
- (bssrdf->radius.y > 0.0f) ? bssrdf->radius.y : bssrdf->radius.z;
+ radius = (bssrdf->radius.x > 0.0f) ? bssrdf->radius.x :
+ (bssrdf->radius.y > 0.0f) ? bssrdf->radius.y :
+ bssrdf->radius.z;
}
else if (xi < 2.0f) {
xi -= 1.0f;
diff --git a/intern/cycles/kernel/filter/filter_defines.h b/intern/cycles/kernel/filter/filter_defines.h
index 0e51eeef92f..1c0ac5e2cb7 100644
--- a/intern/cycles/kernel/filter/filter_defines.h
+++ b/intern/cycles/kernel/filter/filter_defines.h
@@ -52,15 +52,14 @@ typedef struct TileInfo {
tile_buffer_6, tile_buffer_7, tile_buffer_8, tile_buffer_9
# define ccl_get_tile_buffer(id) \
(id == 0 ? tile_buffer_1 : \
- id == 1 ? \
- tile_buffer_2 : \
- id == 2 ? \
- tile_buffer_3 : \
- id == 3 ? tile_buffer_4 : \
- id == 4 ? tile_buffer_5 : \
- id == 5 ? tile_buffer_6 : \
- id == 6 ? tile_buffer_7 : \
- id == 7 ? tile_buffer_8 : tile_buffer_9)
+ id == 1 ? tile_buffer_2 : \
+ id == 2 ? tile_buffer_3 : \
+ id == 3 ? tile_buffer_4 : \
+ id == 4 ? tile_buffer_5 : \
+ id == 5 ? tile_buffer_6 : \
+ id == 6 ? tile_buffer_7 : \
+ id == 7 ? tile_buffer_8 : \
+ tile_buffer_9)
#else
# ifdef __KERNEL_CUDA__
# define CCL_FILTER_TILE_INFO ccl_global TileInfo *tile_info
diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h
index 76e0f2be02c..61653d328f1 100644
--- a/intern/cycles/kernel/kernel_accumulate.h
+++ b/intern/cycles/kernel/kernel_accumulate.h
@@ -225,13 +225,6 @@ ccl_device_inline void path_radiance_init(KernelGlobals *kg, PathRadiance *L)
L->denoising_albedo = zero_float3();
L->denoising_depth = 0.0f;
#endif
-
-#ifdef __KERNEL_DEBUG__
- L->debug_data.num_bvh_traversed_nodes = 0;
- L->debug_data.num_bvh_traversed_instances = 0;
- L->debug_data.num_bvh_intersections = 0;
- L->debug_data.num_ray_bounces = 0;
-#endif
}
ccl_device_inline void path_radiance_bsdf_bounce(KernelGlobals *kg,
@@ -595,7 +588,9 @@ ccl_device_inline void path_radiance_sum_shadowcatcher(KernelGlobals *kg,
float shadow;
if (UNLIKELY(!isfinite_safe(path_total))) {
+# ifdef __KERNEL_DEBUG_NAN__
kernel_assert(!"Non-finite total radiance along the path");
+# endif
shadow = 0.0f;
}
else if (path_total == 0.0f) {
@@ -641,7 +636,9 @@ ccl_device_inline float3 path_radiance_clamp_and_sum(KernelGlobals *kg,
/* Reject invalid value */
if (!isfinite_safe(sum)) {
+# ifdef __KERNEL_DEBUG_NAN__
kernel_assert(!"Non-finite sum in path_radiance_clamp_and_sum!");
+# endif
L_sum = zero_float3();
L->direct_diffuse = zero_float3();
@@ -667,7 +664,9 @@ ccl_device_inline float3 path_radiance_clamp_and_sum(KernelGlobals *kg,
/* Reject invalid value */
float sum = fabsf((L_sum).x) + fabsf((L_sum).y) + fabsf((L_sum).z);
if (!isfinite_safe(sum)) {
+#ifdef __KERNEL_DEBUG_NAN__
kernel_assert(!"Non-finite final sum in path_radiance_clamp_and_sum!");
+#endif
L_sum = zero_float3();
}
}
diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h
index 52be2ed87b7..8f58b8c3079 100644
--- a/intern/cycles/kernel/kernel_passes.h
+++ b/intern/cycles/kernel/kernel_passes.h
@@ -122,31 +122,6 @@ ccl_device_inline void kernel_update_denoising_features(KernelGlobals *kg,
}
#endif /* __DENOISING_FEATURES__ */
-#ifdef __KERNEL_DEBUG__
-ccl_device_inline void kernel_write_debug_passes(KernelGlobals *kg,
- ccl_global float *buffer,
- PathRadiance *L)
-{
- int flag = kernel_data.film.pass_flag;
- if (flag & PASSMASK(BVH_TRAVERSED_NODES)) {
- kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_traversed_nodes,
- L->debug_data.num_bvh_traversed_nodes);
- }
- if (flag & PASSMASK(BVH_TRAVERSED_INSTANCES)) {
- kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_traversed_instances,
- L->debug_data.num_bvh_traversed_instances);
- }
- if (flag & PASSMASK(BVH_INTERSECTIONS)) {
- kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_intersections,
- L->debug_data.num_bvh_intersections);
- }
- if (flag & PASSMASK(RAY_BOUNCES)) {
- kernel_write_pass_float(buffer + kernel_data.film.pass_ray_bounces,
- L->debug_data.num_ray_bounces);
- }
-}
-#endif /* __KERNEL_DEBUG__ */
-
#ifdef __KERNEL_CPU__
# define WRITE_ID_SLOT(buffer, depth, id, matte_weight, name) \
kernel_write_id_pass_cpu(buffer, depth * 2, id, matte_weight, kg->coverage_##name)
@@ -389,10 +364,6 @@ ccl_device_inline void kernel_write_result(KernelGlobals *kg,
}
#endif /* __DENOISING_FEATURES__ */
-#ifdef __KERNEL_DEBUG__
- kernel_write_debug_passes(kg, buffer, L);
-#endif
-
/* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping
criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte
Carlo global illumination" except that here it is applied per pixel and not in hierarchical
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index 8e34ab9af82..a050847cb68 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -70,15 +70,6 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg,
bool hit = scene_intersect(kg, ray, visibility, isect);
-#ifdef __KERNEL_DEBUG__
- if (state->flag & PATH_RAY_CAMERA) {
- L->debug_data.num_bvh_traversed_nodes += isect->num_traversed_nodes;
- L->debug_data.num_bvh_traversed_instances += isect->num_traversed_instances;
- L->debug_data.num_bvh_intersections += isect->num_intersections;
- }
- L->debug_data.num_ray_bounces++;
-#endif /* __KERNEL_DEBUG__ */
-
return hit;
}
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index bc327dd6591..5a79790ca6b 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -184,9 +184,8 @@ CCL_NAMESPACE_BEGIN
# undef __SHADER_RAYTRACE__
#endif
-/* Features that enable others */
-#ifdef WITH_CYCLES_DEBUG
-# define __KERNEL_DEBUG__
+#ifdef WITH_CYCLES_DEBUG_NAN
+# define __KERNEL_DEBUG_NAN__
#endif
#if defined(__SUBSURFACE__) || defined(__SHADER_RAYTRACE__)
@@ -358,12 +357,6 @@ typedef enum PassType {
PASS_MATERIAL_ID,
PASS_MOTION,
PASS_MOTION_WEIGHT,
-#ifdef __KERNEL_DEBUG__
- PASS_BVH_TRAVERSED_NODES,
- PASS_BVH_TRAVERSED_INSTANCES,
- PASS_BVH_INTERSECTIONS,
- PASS_RAY_BOUNCES,
-#endif
PASS_RENDER_TIME,
PASS_CRYPTOMATTE,
PASS_AOV_COLOR,
@@ -467,18 +460,6 @@ typedef enum DenoiseFlag {
DENOISING_CLEAN_ALL_PASSES = (1 << 6) - 1,
} DenoiseFlag;
-#ifdef __KERNEL_DEBUG__
-/* NOTE: This is a runtime-only struct, alignment is not
- * really important here.
- */
-typedef struct DebugData {
- int num_bvh_traversed_nodes;
- int num_bvh_traversed_instances;
- int num_bvh_intersections;
- int num_ray_bounces;
-} DebugData;
-#endif
-
typedef ccl_addr_space struct PathRadianceState {
#ifdef __PASSES__
float3 diffuse;
@@ -554,10 +535,6 @@ typedef ccl_addr_space struct PathRadiance {
float3 denoising_albedo;
float denoising_depth;
#endif /* __DENOISING_FEATURES__ */
-
-#ifdef __KERNEL_DEBUG__
- DebugData debug_data;
-#endif /* __KERNEL_DEBUG__ */
} PathRadiance;
typedef struct BsdfEval {
@@ -673,12 +650,6 @@ typedef struct Intersection {
int prim;
int object;
int type;
-
-#ifdef __KERNEL_DEBUG__
- int num_traversed_nodes;
- int num_traversed_instances;
- int num_intersections;
-#endif
} Intersection;
/* Primitives */
@@ -1272,13 +1243,6 @@ typedef struct KernelFilm {
int pass_bake_differential;
int pad;
-#ifdef __KERNEL_DEBUG__
- int pass_bvh_traversed_nodes;
- int pass_bvh_traversed_instances;
- int pass_bvh_intersections;
- int pass_ray_bounces;
-#endif
-
/* viewport rendering options */
int display_pass_stride;
int display_pass_components;
diff --git a/intern/cycles/kernel/shaders/node_voronoi_texture.osl b/intern/cycles/kernel/shaders/node_voronoi_texture.osl
index e184c26aec8..de6c697fc85 100644
--- a/intern/cycles/kernel/shaders/node_voronoi_texture.osl
+++ b/intern/cycles/kernel/shaders/node_voronoi_texture.osl
@@ -54,14 +54,18 @@ vector4 safe_divide(vector4 a, float b)
}
/*
- * Smooth Voronoi:
+ * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez.
*
+ * Smooth Voronoi:
* - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
*
- * Distance To Edge:
+ * Distance To Edge based on:
*
- * - https://www.shadertoy.com/view/llG3zy
+ * - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
+ * - https://www.shadertoy.com/view/ldl3W8
*
+ * With optimization to change -2..2 scan window to -1..1 for better performance,
+ * as explained in https://www.shadertoy.com/view/llG3zy.
*/
/* **** 1D Voronoi **** */
diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/svm_voronoi.h
index 10d17403f65..d0e7db35fab 100644
--- a/intern/cycles/kernel/svm/svm_voronoi.h
+++ b/intern/cycles/kernel/svm/svm_voronoi.h
@@ -17,14 +17,19 @@
CCL_NAMESPACE_BEGIN
/*
+ * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez.
+ *
* Smooth Voronoi:
*
* - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
*
- * Distance To Edge:
+ * Distance To Edge based on:
*
- * - https://www.shadertoy.com/view/llG3zy
+ * - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
+ * - https://www.shadertoy.com/view/ldl3W8
*
+ * With optimization to change -2..2 scan window to -1..1 for better performance,
+ * as explained in https://www.shadertoy.com/view/llG3zy.
*/
/* **** 1D Voronoi **** */
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index bf9d69cb47e..ea5a5f50f2d 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -20,6 +20,7 @@
#include "render/mesh.h"
#include "util/util_foreach.h"
+#include "util/util_logging.h"
#include "util/util_transform.h"
CCL_NAMESPACE_BEGIN
@@ -208,6 +209,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
case ATTR_ELEMENT_VERTEX_MOTION:
if (geom->geometry_type == Geometry::MESH) {
Mesh *mesh = static_cast<Mesh *>(geom);
+ DCHECK_GT(mesh->get_motion_steps(), 0);
size = (mesh->get_verts().size() + mesh->get_num_ngons()) * (mesh->get_motion_steps() - 1);
if (prim == ATTR_PRIM_SUBD) {
size -= mesh->get_num_subd_verts() * (mesh->get_motion_steps() - 1);
@@ -252,6 +254,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
case ATTR_ELEMENT_CURVE_KEY_MOTION:
if (geom->geometry_type == Geometry::HAIR) {
Hair *hair = static_cast<Hair *>(geom);
+ DCHECK_GT(hair->get_motion_steps(), 0);
size = hair->get_curve_keys().size() * (hair->get_motion_steps() - 1);
}
break;
diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp
index 0948b20628f..fcfad58995e 100644
--- a/intern/cycles/render/buffers.cpp
+++ b/intern/cycles/render/buffers.cpp
@@ -322,15 +322,6 @@ bool RenderBuffers::get_pass_rect(
pixels[0] = saturate(f * scale_exposure);
}
}
-#ifdef WITH_CYCLES_DEBUG
- else if (type == PASS_BVH_TRAVERSED_NODES || type == PASS_BVH_TRAVERSED_INSTANCES ||
- type == PASS_BVH_INTERSECTIONS || type == PASS_RAY_BOUNCES) {
- for (int i = 0; i < size; i++, in += pass_stride, pixels++) {
- float f = *in;
- pixels[0] = f * scale;
- }
- }
-#endif
else {
for (int i = 0; i < size; i++, in += pass_stride, pixels++) {
float f = *in;
diff --git a/intern/cycles/render/colorspace.cpp b/intern/cycles/render/colorspace.cpp
index 4540793f78d..3842f8e4726 100644
--- a/intern/cycles/render/colorspace.cpp
+++ b/intern/cycles/render/colorspace.cpp
@@ -385,7 +385,7 @@ void ColorSpaceManager::free_memory()
{
#ifdef WITH_OCIO
map_free_memory(cached_colorspaces);
- map_free_memory(cached_colorspaces);
+ map_free_memory(cached_processors);
#endif
}
diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp
index c4ff89fc838..5df396394c4 100644
--- a/intern/cycles/render/film.cpp
+++ b/intern/cycles/render/film.cpp
@@ -51,12 +51,6 @@ static NodeEnum *get_pass_type_enum()
pass_type_enum.insert("material_id", PASS_MATERIAL_ID);
pass_type_enum.insert("motion", PASS_MOTION);
pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT);
-#ifdef __KERNEL_DEBUG__
- pass_type_enum.insert("traversed_nodes", PASS_BVH_TRAVERSED_NODES);
- pass_type_enum.insert("traverse_instances", PASS_BVH_TRAVERSED_INSTANCES);
- pass_type_enum.insert("bvh_intersections", PASS_BVH_INTERSECTIONS);
- pass_type_enum.insert("ray_bounces", PASS_RAY_BOUNCES);
-#endif
pass_type_enum.insert("render_time", PASS_RENDER_TIME);
pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE);
pass_type_enum.insert("aov_color", PASS_AOV_COLOR);
@@ -200,15 +194,6 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name)
*/
pass.components = 0;
break;
-#ifdef WITH_CYCLES_DEBUG
- case PASS_BVH_TRAVERSED_NODES:
- case PASS_BVH_TRAVERSED_INSTANCES:
- case PASS_BVH_INTERSECTIONS:
- case PASS_RAY_BOUNCES:
- pass.components = 1;
- pass.exposure = false;
- break;
-#endif
case PASS_RENDER_TIME:
/* This pass is handled entirely on the host side. */
pass.components = 0;
@@ -570,20 +555,6 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
kfilm->pass_bake_differential = kfilm->pass_stride;
break;
-#ifdef WITH_CYCLES_DEBUG
- case PASS_BVH_TRAVERSED_NODES:
- kfilm->pass_bvh_traversed_nodes = kfilm->pass_stride;
- break;
- case PASS_BVH_TRAVERSED_INSTANCES:
- kfilm->pass_bvh_traversed_instances = kfilm->pass_stride;
- break;
- case PASS_BVH_INTERSECTIONS:
- kfilm->pass_bvh_intersections = kfilm->pass_stride;
- break;
- case PASS_RAY_BOUNCES:
- kfilm->pass_ray_bounces = kfilm->pass_stride;
- break;
-#endif
case PASS_RENDER_TIME:
break;
case PASS_CRYPTOMATTE:
diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h
index 3d884f3009b..b39dc9c8b71 100644
--- a/intern/cycles/render/graph.h
+++ b/intern/cycles/render/graph.h
@@ -356,7 +356,7 @@ class ShaderGraph : public NodeOwner {
/* This function is used to create a node of a specified type instead of
* calling 'new', and sets the graph as the owner of the node.
*/
- template<typename T, typename... Args> T *create_node(Args &&... args)
+ template<typename T, typename... Args> T *create_node(Args &&...args)
{
T *node = new T(args...);
node->set_owner(this);
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index 6cd9f1d94ff..84b5cdd4d26 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -954,7 +954,7 @@ void OSLCompiler::parameter_array(const char *name, const float f[], int arrayle
void OSLCompiler::parameter_color_array(const char *name, const array<float3> &f)
{
- /* NB: cycles float3 type is actually 4 floats! need to use an explicit array */
+ /* NOTE: cycles float3 type is actually 4 floats! need to use an explicit array. */
array<float[3]> table(f.size());
for (int i = 0; i < f.size(); ++i) {
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index e4d073e1a2f..842a341358a 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -344,7 +344,7 @@ class Scene : public NodeOwner {
* node array (e.g. Scene::geometry for Geometry nodes) and tag the appropriate
* manager for an update.
*/
- template<typename T, typename... Args> T *create_node(Args &&... args)
+ template<typename T, typename... Args> T *create_node(Args &&...args)
{
T *node = new T(args...);
node->set_owner(this);
diff --git a/intern/cycles/util/util_avxb.h b/intern/cycles/util/util_avxb.h
index 17d505c077a..15215d04ca3 100644
--- a/intern/cycles/util/util_avxb.h
+++ b/intern/cycles/util/util_avxb.h
@@ -57,7 +57,7 @@ struct avxb {
: m256(_mm256_insertf128_ps(_mm256_castps128_ps256(a), b, 1))
{
}
- __forceinline operator const __m256 &(void)const
+ __forceinline operator const __m256 &(void) const
{
return m256;
}
diff --git a/intern/cycles/util/util_avxi.h b/intern/cycles/util/util_avxi.h
index 3db646e61f4..0ae4bf271c8 100644
--- a/intern/cycles/util/util_avxi.h
+++ b/intern/cycles/util/util_avxi.h
@@ -54,7 +54,7 @@ struct avxi {
__forceinline avxi(const __m256i a) : m256(a)
{
}
- __forceinline operator const __m256i &(void)const
+ __forceinline operator const __m256i &(void) const
{
return m256;
}
diff --git a/intern/cycles/util/util_logging.cpp b/intern/cycles/util/util_logging.cpp
index 3782187d78c..8272728a7a0 100644
--- a/intern/cycles/util/util_logging.cpp
+++ b/intern/cycles/util/util_logging.cpp
@@ -26,9 +26,9 @@
CCL_NAMESPACE_BEGIN
+#ifdef WITH_CYCLES_LOGGING
static bool is_verbosity_set()
{
-#ifdef WITH_CYCLES_LOGGING
using CYCLES_GFLAGS_NAMESPACE::GetCommandLineOption;
std::string verbosity;
@@ -36,10 +36,8 @@ static bool is_verbosity_set()
return false;
}
return verbosity != "0";
-#else
- return false;
-#endif
}
+#endif
void util_logging_init(const char *argv0)
{
diff --git a/intern/cycles/util/util_logging.h b/intern/cycles/util/util_logging.h
index 3e56f0a0193..c161299acd0 100644
--- a/intern/cycles/util/util_logging.h
+++ b/intern/cycles/util/util_logging.h
@@ -40,7 +40,7 @@ class LogMessageVoidify {
LogMessageVoidify()
{
}
- void operator&(StubStream &)
+ void operator&(const StubStream &)
{
}
};
@@ -49,6 +49,36 @@ class LogMessageVoidify {
# define LOG(severity) LOG_SUPPRESS()
# define VLOG(severity) LOG_SUPPRESS()
# define VLOG_IF(severity, condition) LOG_SUPPRESS()
+
+# define CHECK(expression) LOG_SUPPRESS()
+
+# define CHECK_NOTNULL(expression) LOG_SUPPRESS()
+# define CHECK_NULL(expression) LOG_SUPPRESS()
+
+# define CHECK_NEAR(actual, expected, eps) LOG_SUPPRESS()
+
+# define CHECK_GE(a, b) LOG_SUPPRESS()
+# define CHECK_NE(a, b) LOG_SUPPRESS()
+# define CHECK_EQ(a, b) LOG_SUPPRESS()
+# define CHECK_GT(a, b) LOG_SUPPRESS()
+# define CHECK_LT(a, b) LOG_SUPPRESS()
+# define CHECK_LE(a, b) LOG_SUPPRESS()
+
+# define DCHECK(expression) LOG_SUPPRESS()
+
+# define DCHECK_NOTNULL(expression) LOG_SUPPRESS()
+# define DCHECK_NULL(expression) LOG_SUPPRESS()
+
+# define DCHECK_NEAR(actual, expected, eps) LOG_SUPPRESS()
+
+# define DCHECK_GE(a, b) LOG_SUPPRESS()
+# define DCHECK_NE(a, b) LOG_SUPPRESS()
+# define DCHECK_EQ(a, b) LOG_SUPPRESS()
+# define DCHECK_GT(a, b) LOG_SUPPRESS()
+# define DCHECK_LT(a, b) LOG_SUPPRESS()
+# define DCHECK_LE(a, b) LOG_SUPPRESS()
+
+# define LOG_ASSERT(expression) LOG_SUPPRESS()
#endif
#define VLOG_ONCE(level, flag) \
diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h
index 1113ede0f2d..38afa163db5 100644
--- a/intern/cycles/util/util_math_fast.h
+++ b/intern/cycles/util/util_math_fast.h
@@ -103,7 +103,7 @@ ccl_device float fast_sinf(float x)
* 1.19209e-07 max error
*/
int q = fast_rint(x * M_1_PI_F);
- float qf = q;
+ float qf = (float)q;
x = madd(qf, -0.78515625f * 4, x);
x = madd(qf, -0.00024187564849853515625f * 4, x);
x = madd(qf, -3.7747668102383613586e-08f * 4, x);
@@ -132,7 +132,7 @@ ccl_device float fast_cosf(float x)
{
/* Same argument reduction as fast_sinf(). */
int q = fast_rint(x * M_1_PI_F);
- float qf = q;
+ float qf = (float)q;
x = madd(qf, -0.78515625f * 4, x);
x = madd(qf, -0.00024187564849853515625f * 4, x);
x = madd(qf, -3.7747668102383613586e-08f * 4, x);
@@ -160,7 +160,7 @@ ccl_device void fast_sincosf(float x, float *sine, float *cosine)
{
/* Same argument reduction as fast_sin. */
int q = fast_rint(x * M_1_PI_F);
- float qf = q;
+ float qf = (float)q;
x = madd(qf, -0.78515625f * 4, x);
x = madd(qf, -0.00024187564849853515625f * 4, x);
x = madd(qf, -3.7747668102383613586e-08f * 4, x);
@@ -207,7 +207,7 @@ ccl_device float fast_tanf(float x)
* we sometimes need to take the reciprocal of the polynomial
*/
int q = fast_rint(x * 2.0f * M_1_PI_F);
- float qf = q;
+ float qf = (float)q;
x = madd(qf, -0.78515625f * 2, x);
x = madd(qf, -0.00024187564849853515625f * 2, x);
x = madd(qf, -3.7747668102383613586e-08f * 2, x);
@@ -407,7 +407,7 @@ ccl_device float fast_logb(float x)
x = fabsf(x);
x = clamp(x, FLT_MIN, FLT_MAX);
unsigned bits = __float_as_uint(x);
- return (int)(bits >> 23) - 127;
+ return (float)((int)(bits >> 23) - 127);
}
ccl_device float fast_exp2f(float x)
diff --git a/intern/cycles/util/util_sseb.h b/intern/cycles/util/util_sseb.h
index 4dbd5b8046e..6afce4f8909 100644
--- a/intern/cycles/util/util_sseb.h
+++ b/intern/cycles/util/util_sseb.h
@@ -57,7 +57,7 @@ struct sseb {
__forceinline sseb(const __m128 input) : m128(input)
{
}
- __forceinline operator const __m128 &(void)const
+ __forceinline operator const __m128 &(void) const
{
return m128;
}
diff --git a/intern/cycles/util/util_ssei.h b/intern/cycles/util/util_ssei.h
index cd51dbff2f1..94412fb77e7 100644
--- a/intern/cycles/util/util_ssei.h
+++ b/intern/cycles/util/util_ssei.h
@@ -57,7 +57,7 @@ struct ssei {
__forceinline ssei(const __m128i a) : m128(a)
{
}
- __forceinline operator const __m128i &(void)const
+ __forceinline operator const __m128i &(void) const
{
return m128;
}
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index db3f9bd561e..46e3888a367 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -188,8 +188,8 @@ extern GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
GHOST_GLSettings glSettings);
/**
- * Create a new offscreen context.
- * Never explicitly delete the context, use disposeContext() instead.
+ * Create a new off-screen context.
+ * Never explicitly delete the context, use #disposeContext() instead.
* \param systemhandle: The handle to the system.
* \param platform_support_callback: An optional callback to check platform support.
* \return A handle to the new context ( == NULL if creation failed).
@@ -628,7 +628,7 @@ extern void GHOST_ScreenToClient(
GHOST_WindowHandle windowhandle, int32_t inX, int32_t inY, int32_t *outX, int32_t *outY);
/**
- * Converts a point in screen coordinates to client rectangle coordinates
+ * Converts a point in client rectangle coordinates to screen coordinates.
* \param windowhandle: The handle to the window.
* \param inX: The x-coordinate in the client rectangle.
* \param inY: The y-coordinate in the client rectangle.
diff --git a/intern/ghost/GHOST_IContext.h b/intern/ghost/GHOST_IContext.h
index 278a9a40bd1..1b5f996cb54 100644
--- a/intern/ghost/GHOST_IContext.h
+++ b/intern/ghost/GHOST_IContext.h
@@ -29,8 +29,8 @@
/**
* Interface for GHOST context.
*
- * You can create a offscreen context (windowless) with the system's
- * GHOST_ISystem::createOffscreenContext method.
+ * You can create a off-screen context (windowless) with the system's
+ * #GHOST_ISystem::createOffscreenContext method.
* \see GHOST_ISystem#createOffscreenContext
*/
class GHOST_IContext {
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index 4c395f720df..05c6c9d907f 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -261,8 +261,8 @@ class GHOST_ISystem {
virtual GHOST_TSuccess disposeWindow(GHOST_IWindow *window) = 0;
/**
- * Create a new offscreen context.
- * Never explicitly delete the context, use disposeContext() instead.
+ * Create a new off-screen context.
+ * Never explicitly delete the context, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
virtual GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings) = 0;
diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h
index f870791b345..5f9bd808c8c 100644
--- a/intern/ghost/GHOST_IWindow.h
+++ b/intern/ghost/GHOST_IWindow.h
@@ -133,7 +133,7 @@ class GHOST_IWindow {
virtual void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const = 0;
/**
- * Converts a point in screen coordinates to client rectangle coordinates
+ * Converts a point in client rectangle coordinates to screen coordinates.
* \param inX: The x-coordinate in the client rectangle.
* \param inY: The y-coordinate in the client rectangle.
* \param outX: The x-coordinate on the screen.
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index ff93de4f203..94a3fd86b73 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -611,9 +611,9 @@ typedef void (*GHOST_TimerProcPtr)(struct GHOST_TimerTaskHandle__ *task, uint64_
struct GHOST_XrDrawViewInfo;
struct GHOST_XrError;
/**
- * The XR view (i.e. the OpenXR runtime) may require a different graphics library than OpenGL. An
- * offscreen texture of the viewport will then be drawn into using OpenGL, but the final texture
- * draw call will happen through another lib (say DirectX).
+ * The XR view (i.e. the OpenXR runtime) may require a different graphics library than OpenGL.
+ * An off-screen texture of the viewport will then be drawn into using OpenGL,
+ * but the final texture draw call will happen through another library (say DirectX).
*
* This enum defines the possible graphics bindings to attempt to enable.
*/
@@ -683,6 +683,10 @@ typedef struct GHOST_XrDrawViewInfo {
/** Set if the buffer should be submitted with a SRGB transfer applied. */
char expects_srgb_buffer;
+
+ /** The view that this info represents. Not necessarily the "eye index" (e.g. for quad view
+ * systems, etc). */
+ char view_idx;
} GHOST_XrDrawViewInfo;
typedef struct GHOST_XrError {
diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm
index 687173ded09..7af243846c2 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.mm
+++ b/intern/ghost/intern/GHOST_ContextCGL.mm
@@ -217,7 +217,7 @@ static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs,
attribs.push_back(NSOpenGLPFAOpenGLProfile);
attribs.push_back(coreProfile ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy);
- // Pixel Format Attributes for the windowed NSOpenGLContext
+ /* Pixel Format Attributes for the windowed NSOpenGLContext. */
attribs.push_back(NSOpenGLPFADoubleBuffer);
if (softwareGL) {
@@ -250,7 +250,8 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
static const bool needAlpha = false;
#endif
- static bool softwareGL = getenv("BLENDER_SOFTWAREGL"); // command-line argument would be better
+ /* Command-line argument would be better. */
+ static bool softwareGL = getenv("BLENDER_SOFTWAREGL");
std::vector<NSOpenGLPixelFormatAttribute> attribs;
attribs.reserve(40);
@@ -287,7 +288,7 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
if (m_metalView) {
if (m_defaultFramebuffer == 0) {
- // Create a virtual framebuffer
+ /* Create a virtual frame-buffer. */
[m_openGLContext makeCurrentContext];
metalInitFramebuffer();
initClearGL();
@@ -342,11 +343,11 @@ void GHOST_ContextCGL::metalInit()
/* clang-format on */
id<MTLDevice> device = m_metalLayer.device;
- // Create a command queue for blit/present operation
+ /* Create a command queue for blit/present operation. */
m_metalCmdQueue = (MTLCommandQueue *)[device newCommandQueue];
[m_metalCmdQueue retain];
- // Create shaders for blit operation
+ /* Create shaders for blit operation. */
NSString *source = @R"msl(
using namespace metal;
@@ -387,7 +388,7 @@ void GHOST_ContextCGL::metalInit()
"GHOST_ContextCGL::metalInit: newLibraryWithSource:options:error: failed!");
}
- // Create a render pipeline for blit operation
+ /* Create a render pipeline for blit operation. */
MTLRenderPipelineDescriptor *desc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease];
desc.fragmentFunction = [library newFunctionWithName:@"fragment_shader"];
@@ -460,7 +461,7 @@ void GHOST_ContextCGL::metalUpdateFramebuffer()
"GHOST_ContextCGL::metalUpdateFramebuffer: CVPixelBufferCreate failed!");
}
- // Create an OpenGL texture
+ /* Create an OpenGL texture. */
CVOpenGLTextureCacheRef cvGLTexCache = nil;
cvret = CVOpenGLTextureCacheCreate(kCFAllocatorDefault,
nil,
@@ -485,7 +486,7 @@ void GHOST_ContextCGL::metalUpdateFramebuffer()
unsigned int glTex;
glTex = CVOpenGLTextureGetName(cvGLTex);
- // Create a Metal texture
+ /* Create a Metal texture. */
CVMetalTextureCacheRef cvMetalTexCache = nil;
cvret = CVMetalTextureCacheCreate(
kCFAllocatorDefault, nil, m_metalLayer.device, nil, &cvMetalTexCache);
diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp
index 0fee200ea1a..82100e4b3f0 100644
--- a/intern/ghost/intern/GHOST_ContextEGL.cpp
+++ b/intern/ghost/intern/GHOST_ContextEGL.cpp
@@ -283,8 +283,8 @@ GHOST_TSuccess GHOST_ContextEGL::setSwapInterval(int interval)
GHOST_TSuccess GHOST_ContextEGL::getSwapInterval(int &intervalOut)
{
- // This is a bit of a kludge because there does not seem to
- // be a way to query the swap interval with EGL.
+ /* This is a bit of a kludge because there does not seem to
+ * be a way to query the swap interval with EGL. */
intervalOut = m_swap_interval;
return GHOST_kSuccess;
@@ -365,21 +365,21 @@ static const std::string &api_string(EGLenum api)
GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
{
- // objects have to be declared here due to the use of goto
+ /* Objects have to be declared here due to the use of `goto`. */
std::vector<EGLint> attrib_list;
EGLint num_config = 0;
if (m_stereoVisual)
fprintf(stderr, "Warning! Stereo OpenGL ES contexts are not supported.\n");
- m_stereoVisual = false; // It doesn't matter what the Window wants.
+ m_stereoVisual = false; /* It doesn't matter what the Window wants. */
if (!initContextEGLEW()) {
return GHOST_kFailure;
}
#ifdef WITH_GL_ANGLE
- // d3dcompiler_XX.dll needs to be loaded before ANGLE will work
+ /* `d3dcompiler_XX.dll` needs to be loaded before ANGLE will work. */
if (s_d3dcompiler == NULL) {
s_d3dcompiler = LoadLibrary(D3DCOMPILER);
@@ -402,7 +402,9 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor)))
goto error;
+#ifdef WITH_GHOST_DEBUG
fprintf(stderr, "EGL Version %d.%d\n", egl_major, egl_minor);
+#endif
if (!EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)))
goto error;
@@ -410,13 +412,13 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
if (!bindAPI(m_api))
goto error;
- // build attribute list
+ /* Build attribute list. */
attrib_list.reserve(20);
if (m_api == EGL_OPENGL_ES_API && EGLEW_VERSION_1_2) {
- // According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE,
- // but some implementations (ANGLE) do not seem to care.
+ /* According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE,
+ * but some implementations (ANGLE) do not seem to care. */
if (m_contextMajorVersion == 1) {
attrib_list.push_back(EGL_RENDERABLE_TYPE);
@@ -469,7 +471,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
#endif
if (m_nativeWindow == 0) {
- // off-screen surface
+ /* Off-screen surface. */
attrib_list.push_back(EGL_SURFACE_TYPE);
attrib_list.push_back(EGL_PBUFFER_BIT);
}
@@ -479,8 +481,8 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &m_config, 1, &num_config)))
goto error;
- // A common error is to assume that ChooseConfig worked because it returned EGL_TRUE
- if (num_config != 1) // num_config should be exactly 1
+ /* A common error is to assume that ChooseConfig worked because it returned EGL_TRUE. */
+ if (num_config != 1) /* `num_config` should be exactly 1. */
goto error;
if (m_nativeWindow != 0) {
diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp
index da8b1fd4941..eb49dc4f98b 100644
--- a/intern/ghost/intern/GHOST_ContextGLX.cpp
+++ b/intern/ghost/intern/GHOST_ContextGLX.cpp
@@ -32,7 +32,7 @@
#include <cstdio>
#include <cstring>
-/* needed for intel drivers (works w/ mesa-swrast & nvidia) */
+/* Needed for Intel drivers (works with MESA-software-rasterizer (`swrast`) & NVIDIA). */
#define USE_GLXEW_INIT_WORKAROUND
#ifdef USE_GLXEW_INIT_WORKAROUND
@@ -239,7 +239,7 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext()
}
attribs[i++] = 0;
- /* Some drivers don't like having a true offscreen context.
+ /* Some drivers don't like having a true off-screen context.
* Create a pixel buffer instead of a window to render to.
* even if it will never be used for drawing. */
int pbuffer_attribs[] = {GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, None};
diff --git a/intern/ghost/intern/GHOST_ContextWGL.cpp b/intern/ghost/intern/GHOST_ContextWGL.cpp
index ddb34a8afd9..b412e52a5f7 100644
--- a/intern/ghost/intern/GHOST_ContextWGL.cpp
+++ b/intern/ghost/intern/GHOST_ContextWGL.cpp
@@ -335,10 +335,11 @@ void GHOST_ContextWGL::initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD)
if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC)))
goto finalize;
- if (GLEW_CHK(glewInit()) != GLEW_OK)
+ if (GLEW_CHK(glewInit()) != GLEW_OK) {
fprintf(stderr, "Warning! Dummy GLEW/WGLEW failed to initialize properly.\n");
+ }
- // the following are not technially WGLEW, but they also require a context to work
+ /* The following are not technically WGLEW, but they also require a context to work. */
#ifndef NDEBUG
free((void *)m_dummyRenderer);
@@ -474,16 +475,15 @@ int GHOST_ContextWGL::choose_pixel_format(bool stereoVisual, bool needAlpha)
PIXELFORMATDESCRIPTOR preferredPFD = {
sizeof(PIXELFORMATDESCRIPTOR), /* size */
1, /* version */
- (DWORD)(
- PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW |
- PFD_DOUBLEBUFFER | /* support double-buffering */
- (stereoVisual ? PFD_STEREO : 0) | /* support stereo */
- (
+ (DWORD)(PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW |
+ PFD_DOUBLEBUFFER | /* support double-buffering */
+ (stereoVisual ? PFD_STEREO : 0) | /* support stereo */
+ (
#ifdef WIN32_COMPOSITING
- needAlpha ?
- PFD_SUPPORT_COMPOSITION : /* support composition for transparent background */
+ /* Support composition for transparent background. */
+ needAlpha ? PFD_SUPPORT_COMPOSITION :
#endif
- 0)),
+ 0)),
PFD_TYPE_RGBA, /* color type */
(BYTE)(needAlpha ? 32 : 24), /* preferred color depth */
0,
diff --git a/intern/ghost/intern/GHOST_DisplayManager.cpp b/intern/ghost/intern/GHOST_DisplayManager.cpp
index fe12a76753d..9abc652378a 100644
--- a/intern/ghost/intern/GHOST_DisplayManager.cpp
+++ b/intern/ghost/intern/GHOST_DisplayManager.cpp
@@ -51,7 +51,7 @@ GHOST_TSuccess GHOST_DisplayManager::initialize(void)
GHOST_TSuccess GHOST_DisplayManager::getNumDisplays(uint8_t & /*numDisplays*/) const
{
- // Don't know if we have a display...
+ /* Don't know if we have a display. */
return GHOST_kFailure;
}
@@ -120,18 +120,18 @@ GHOST_TSuccess GHOST_DisplayManager::findMatch(uint8_t display,
(int)setting.xPixels, (int)setting.yPixels, (int)setting.bpp, (int)setting.frequency};
int capabilities[4];
double field, score;
- double best = 1e12; // A big number
+ double best = 1e12; /* A big number. */
int found = 0;
- // Look at all the display modes
+ /* Look at all the display modes. */
for (int i = 0; (i < (int)m_settings[display].size()); i++) {
- // Store the capabilities of the display device
+ /* Store the capabilities of the display device. */
capabilities[0] = m_settings[display][i].xPixels;
capabilities[1] = m_settings[display][i].yPixels;
capabilities[2] = m_settings[display][i].bpp;
capabilities[3] = m_settings[display][i].frequency;
- // Match against all the fields of the display settings
+ /* Match against all the fields of the display settings. */
score = 0;
for (int j = 0; j < 4; j++) {
field = capabilities[j] - criteria[j];
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp
index 8758a27930e..dba1d305144 100644
--- a/intern/ghost/intern/GHOST_DropTargetX11.cpp
+++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp
@@ -115,8 +115,10 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11()
/* Based on: https://stackoverflow.com/a/2766963/432509 */
typedef enum DecodeState_e {
- STATE_SEARCH = 0, ///< searching for an ampersand to convert
- STATE_CONVERTING ///< convert the two proceeding characters from hex
+ /** Searching for an ampersand to convert. */
+ STATE_SEARCH = 0,
+ /** Convert the two proceeding characters from hex. */
+ STATE_CONVERTING
} DecodeState_e;
void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn)
diff --git a/intern/ghost/intern/GHOST_EventDragnDrop.h b/intern/ghost/intern/GHOST_EventDragnDrop.h
index 537717b1717..0095cedb8c8 100644
--- a/intern/ghost/intern/GHOST_EventDragnDrop.h
+++ b/intern/ghost/intern/GHOST_EventDragnDrop.h
@@ -90,7 +90,7 @@ class GHOST_EventDragnDrop : public GHOST_Event {
~GHOST_EventDragnDrop()
{
- // Free the dropped object data
+ /* Free the dropped object data. */
if (m_dragnDropEventData.data == NULL)
return;
diff --git a/intern/ghost/intern/GHOST_EventManager.cpp b/intern/ghost/intern/GHOST_EventManager.cpp
index 15befb9afcb..6ddc362ac77 100644
--- a/intern/ghost/intern/GHOST_EventManager.cpp
+++ b/intern/ghost/intern/GHOST_EventManager.cpp
@@ -108,12 +108,12 @@ GHOST_TSuccess GHOST_EventManager::addConsumer(GHOST_IEventConsumer *consumer)
GHOST_TSuccess success;
GHOST_ASSERT(consumer, "invalid consumer");
- // Check to see whether the consumer is already in our list
+ /* Check to see whether the consumer is already in our list. */
TConsumerVector::const_iterator iter = std::find(
m_consumers.begin(), m_consumers.end(), consumer);
if (iter == m_consumers.end()) {
- // Add the consumer
+ /* Add the consumer. */
m_consumers.push_back(consumer);
success = GHOST_kSuccess;
}
@@ -128,11 +128,11 @@ GHOST_TSuccess GHOST_EventManager::removeConsumer(GHOST_IEventConsumer *consumer
GHOST_TSuccess success;
GHOST_ASSERT(consumer, "invalid consumer");
- // Check to see whether the consumer is in our list
+ /* Check to see whether the consumer is in our list. */
TConsumerVector::iterator iter = std::find(m_consumers.begin(), m_consumers.end(), consumer);
if (iter != m_consumers.end()) {
- // Remove the consumer
+ /* Remove the consumer. */
m_consumers.erase(iter);
success = GHOST_kSuccess;
}
diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp
index 7c12bfe0306..d9fecda22a4 100644
--- a/intern/ghost/intern/GHOST_ISystem.cpp
+++ b/intern/ghost/intern/GHOST_ISystem.cpp
@@ -60,6 +60,8 @@ GHOST_TSuccess GHOST_ISystem::createSystem()
}
catch (const std::runtime_error &) {
/* fallback to X11. */
+ delete m_system;
+ m_system = nullptr;
}
if (!m_system) {
m_system = new GHOST_SystemX11();
diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp
index 112a266ae28..8daa07b5003 100644
--- a/intern/ghost/intern/GHOST_ImeWin32.cpp
+++ b/intern/ghost/intern/GHOST_ImeWin32.cpp
@@ -34,6 +34,8 @@ GHOST_ImeWin32::GHOST_ImeWin32()
: is_composing_(false),
ime_status_(false),
input_language_id_(LANG_USER_DEFAULT),
+ conversion_modes_(IME_CMODE_ALPHANUMERIC),
+ sentence_mode_(IME_SMODE_NONE),
system_caret_(false),
caret_rect_(-1, -1, 0, 0),
is_first(true),
@@ -59,6 +61,63 @@ bool GHOST_ImeWin32::SetInputLanguage()
return ime_status_;
}
+WORD GHOST_ImeWin32::GetInputLanguage()
+{
+ return input_language_id_;
+}
+
+void GHOST_ImeWin32::UpdateConversionStatus(HWND window_handle)
+{
+ HIMC imm_context = ::ImmGetContext(window_handle);
+ if (imm_context) {
+ if (::ImmGetOpenStatus(imm_context)) {
+ ::ImmGetConversionStatus(imm_context, &conversion_modes_, &sentence_mode_);
+ }
+ else {
+ conversion_modes_ = IME_CMODE_ALPHANUMERIC;
+ sentence_mode_ = IME_SMODE_NONE;
+ }
+ ::ImmReleaseContext(window_handle, imm_context);
+ }
+ else {
+ conversion_modes_ = IME_CMODE_ALPHANUMERIC;
+ sentence_mode_ = IME_SMODE_NONE;
+ }
+}
+
+bool GHOST_ImeWin32::IsEnglishMode()
+{
+ return (conversion_modes_ & IME_CMODE_NOCONVERSION) ||
+ !(conversion_modes_ & (IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE));
+}
+
+bool GHOST_ImeWin32::IsImeKeyEvent(char ascii)
+{
+ if (!(IsEnglishMode())) {
+ /* In Chinese, Japanese, Korean, all alpha keys are processed by IME. */
+ if ((ascii >= 'A' && ascii <= 'Z') || (ascii >= 'a' && ascii <= 'z')) {
+ return true;
+ }
+ switch (PRIMARYLANGID(GetInputLanguage())) {
+ /* In Japanese, all symbolic characters are also processed by IME. */
+ case LANG_JAPANESE: {
+ if (ascii >= ' ' && ascii <= '~') {
+ return true;
+ }
+ break;
+ }
+ /* In Chinese, some symbolic characters are also processed by IME. */
+ case LANG_CHINESE: {
+ if (ascii && strchr("!\"$'(),.:;<>?[\\]^_`", ascii)) {
+ return true;
+ }
+ break;
+ }
+ }
+ }
+ return false;
+}
+
void GHOST_ImeWin32::CreateImeWindow(HWND window_handle)
{
/**
diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h
index 4af988aef6e..bcc4b6eef7c 100644
--- a/intern/ghost/intern/GHOST_ImeWin32.h
+++ b/intern/ghost/intern/GHOST_ImeWin32.h
@@ -156,6 +156,18 @@ class GHOST_ImeWin32 {
*/
bool SetInputLanguage();
+ /* Returns the current input language id. */
+ WORD GetInputLanguage();
+
+ /* Saves the current conversion status. */
+ void UpdateConversionStatus(HWND window_handle);
+
+ /* Is the IME currently in conversion mode? */
+ bool IsEnglishMode();
+
+ /* Checks a key whether IME has to do handling. */
+ bool IsImeKeyEvent(char ascii);
+
/**
* Create the IME windows, and allocate required resources for them.
* Parameters
@@ -371,6 +383,12 @@ class GHOST_ImeWin32 {
*/
LANGID input_language_id_;
+ /* Current Conversion Mode Values. Retrieved with ImmGetConversionStatus. */
+ DWORD conversion_modes_;
+
+ /* Current Sentence Mode. Retrieved with ImmGetConversionStatus. */
+ DWORD sentence_mode_;
+
/**
* Represents whether or not the current input context has created a system
* caret to set the position of its IME candidate window.
diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp
index 079ad67f737..0317c175273 100644
--- a/intern/ghost/intern/GHOST_NDOFManager.cpp
+++ b/intern/ghost/intern/GHOST_NDOFManager.cpp
@@ -22,51 +22,51 @@
#include <limits.h>
#include <math.h>
-#include <stdio.h> // for error/info reporting
-#include <string.h> // for memory functions
+#include <stdio.h> /* For error/info reporting. */
+#include <string.h> /* For memory functions. */
#ifdef DEBUG_NDOF_MOTION
-// printable version of each GHOST_TProgress value
+/* Printable version of each GHOST_TProgress value. */
static const char *progress_string[] = {
"not started", "starting", "in progress", "finishing", "finished"};
#endif
#ifdef DEBUG_NDOF_BUTTONS
static const char *ndof_button_names[] = {
- // used internally, never sent
+ /* used internally, never sent */
"NDOF_BUTTON_NONE",
- // these two are available from any 3Dconnexion device
+ /* these two are available from any 3Dconnexion device */
"NDOF_BUTTON_MENU",
"NDOF_BUTTON_FIT",
- // standard views
+ /* standard views */
"NDOF_BUTTON_TOP",
"NDOF_BUTTON_BOTTOM",
"NDOF_BUTTON_LEFT",
"NDOF_BUTTON_RIGHT",
"NDOF_BUTTON_FRONT",
"NDOF_BUTTON_BACK",
- // more views
+ /* more views */
"NDOF_BUTTON_ISO1",
"NDOF_BUTTON_ISO2",
- // 90 degree rotations
+ /* 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",
- // device control
+ /* device control */
"NDOF_BUTTON_ROTATE",
"NDOF_BUTTON_PANZOOM",
"NDOF_BUTTON_DOMINANT",
"NDOF_BUTTON_PLUS",
"NDOF_BUTTON_MINUS",
- // keyboard emulation
+ /* keyboard emulation */
"NDOF_BUTTON_ESC",
"NDOF_BUTTON_ALT",
"NDOF_BUTTON_SHIFT",
"NDOF_BUTTON_CTRL",
- // general-purpose buttons
+ /* general-purpose buttons */
"NDOF_BUTTON_1",
"NDOF_BUTTON_2",
"NDOF_BUTTON_3",
@@ -77,17 +77,17 @@ static const char *ndof_button_names[] = {
"NDOF_BUTTON_8",
"NDOF_BUTTON_9",
"NDOF_BUTTON_10",
- // more general-purpose buttons
+ /* more general-purpose buttons */
"NDOF_BUTTON_A",
"NDOF_BUTTON_B",
"NDOF_BUTTON_C",
- // the end
+ /* the end */
"NDOF_BUTTON_LAST"};
#endif
-// shared by the latest 3Dconnexion hardware
-// SpacePilotPro uses all of these
-// smaller devices use only some, based on button mask
+/* Shared by the latest 3Dconnexion hardware
+ * SpacePilotPro uses all of these
+ * smaller devices use only some, based on button mask. */
static const NDOF_ButtonT Modern3Dx_HID_map[] = {
NDOF_BUTTON_MENU, NDOF_BUTTON_FIT, NDOF_BUTTON_TOP, NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT, NDOF_BUTTON_FRONT, NDOF_BUTTON_BOTTOM, NDOF_BUTTON_BACK,
@@ -116,15 +116,15 @@ static const NDOF_ButtonT SpaceExplorer_HID_map[] = {
NDOF_BUTTON_ROTATE,
};
-// this is the older SpacePilot (sans Pro)
-// thanks to polosson for info about this device
+/* This is the older SpacePilot (sans Pro)
+ * thanks to polosson for info about this device. */
static const NDOF_ButtonT SpacePilot_HID_map[] = {
NDOF_BUTTON_1, NDOF_BUTTON_2, NDOF_BUTTON_3, NDOF_BUTTON_4,
NDOF_BUTTON_5, NDOF_BUTTON_6, NDOF_BUTTON_TOP, NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT, NDOF_BUTTON_FRONT, NDOF_BUTTON_ESC, NDOF_BUTTON_ALT,
NDOF_BUTTON_SHIFT, NDOF_BUTTON_CTRL, NDOF_BUTTON_FIT, NDOF_BUTTON_MENU,
NDOF_BUTTON_PLUS, NDOF_BUTTON_MINUS, NDOF_BUTTON_DOMINANT, NDOF_BUTTON_ROTATE,
- NDOF_BUTTON_NONE // the CONFIG button -- what does it do?
+ NDOF_BUTTON_NONE /* the CONFIG button -- what does it do? */
};
static const NDOF_ButtonT Generic_HID_map[] = {
@@ -146,7 +146,7 @@ static const int genericButtonCount = sizeof(Generic_HID_map) / sizeof(NDOF_Butt
GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys)
: m_system(sys),
- m_deviceType(NDOF_UnknownDevice), // each platform has its own device detection code
+ m_deviceType(NDOF_UnknownDevice), /* Each platform has its own device detection code. */
m_buttonCount(genericButtonCount),
m_buttonMask(0),
m_hidMap(Generic_HID_map),
@@ -157,37 +157,37 @@ GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys)
m_motionEventPending(false),
m_deadZone(0.0f)
{
- // to avoid the rare situation where one triple is updated and
- // the other is not, initialize them both here:
+ /* To avoid the rare situation where one triple is updated and
+ * the other is not, initialize them both here: */
memset(m_translation, 0, sizeof(m_translation));
memset(m_rotation, 0, sizeof(m_rotation));
}
bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short product_id)
{
- // call this function until it returns true
- // it's a good idea to stop calling it after that, as it will "forget"
- // whichever device it already found
+ /* Call this function until it returns true
+ * it's a good idea to stop calling it after that, as it will "forget"
+ * whichever device it already found */
- // default to safe generic behavior for "unknown" devices
- // unidentified devices will emit motion events like normal
- // rogue buttons do nothing by default, but can be customized by the user
+ /* Default to safe generic behavior for "unknown" devices
+ * unidentified devices will emit motion events like normal
+ * rogue buttons do nothing by default, but can be customized by the user. */
m_deviceType = NDOF_UnknownDevice;
m_hidMap = Generic_HID_map;
m_buttonCount = genericButtonCount;
m_buttonMask = 0;
- // "mystery device" owners can help build a HID_map for their hardware
- // A few users have already contributed information about several older devices
- // that I don't have access to. Thanks!
+ /* "mystery device" owners can help build a HID_map for their hardware
+ * A few users have already contributed information about several older devices
+ * that I don't have access to. Thanks! */
switch (vendor_id) {
- case 0x046D: // Logitech (3Dconnexion was a subsidiary)
+ case 0x046D: /* Logitech (3Dconnexion was a subsidiary). */
switch (product_id) {
- // -- current devices --
- case 0xC626: // full-size SpaceNavigator
- case 0xC628: // the "for Notebooks" one
+ /* -- current devices -- */
+ case 0xC626: /* full-size SpaceNavigator */
+ case 0xC628: /* the "for Notebooks" one */
puts("ndof: using SpaceNavigator");
m_deviceType = NDOF_SpaceNavigator;
m_buttonCount = 2;
@@ -209,12 +209,12 @@ bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ
puts("ndof: using SpaceMouse Pro");
m_deviceType = NDOF_SpaceMousePro;
m_buttonCount = 27;
- // ^^ actually has 15 buttons, but their HID codes range from 0 to 26
+ /* ^^ actually has 15 buttons, but their HID codes range from 0 to 26 */
m_buttonMask = 0x07C0F137;
m_hidMap = Modern3Dx_HID_map;
break;
- // -- older devices --
+ /* -- older devices -- */
case 0xC625:
puts("ndof: using SpacePilot");
m_deviceType = NDOF_SpacePilot;
@@ -236,21 +236,21 @@ bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ
printf("ndof: unknown Logitech product %04hx\n", product_id);
}
break;
- case 0x256F: // 3Dconnexion
+ case 0x256F: /* 3Dconnexion */
switch (product_id) {
- case 0xC62E: // plugged in
- case 0xC62F: // wireless
+ case 0xC62E: /* Plugged in. */
+ case 0xC62F: /* Wireless. */
puts("ndof: using SpaceMouse Wireless");
m_deviceType = NDOF_SpaceMouseWireless;
m_buttonCount = 2;
m_hidMap = Modern3Dx_HID_map;
break;
- case 0xC631: // plugged in
- case 0xC632: // wireless
+ case 0xC631: /* Plugged in. */
+ case 0xC632: /* Wireless. */
puts("ndof: using SpaceMouse Pro Wireless");
m_deviceType = NDOF_SpaceMouseProWireless;
m_buttonCount = 27;
- // ^^ actually has 15 buttons, but their HID codes range from 0 to 26
+ /* ^^ actually has 15 buttons, but their HID codes range from 0 to 26. */
m_buttonMask = 0x07C0F137;
m_hidMap = Modern3Dx_HID_map;
break;
@@ -364,16 +364,16 @@ void GHOST_NDOFManager::updateButton(int button_number, bool press, uint64_t tim
int mask = 1 << button_number;
if (press) {
- m_buttons |= mask; // set this button's bit
+ m_buttons |= mask; /* Set this button's bit. */
}
else {
- m_buttons &= ~mask; // clear this button's bit
+ m_buttons &= ~mask; /* Clear this button's bit. */
}
}
void GHOST_NDOFManager::updateButtons(int button_bits, uint64_t time)
{
- button_bits &= m_buttonMask; // discard any "garbage" bits
+ button_bits &= m_buttonMask; /* Discard any "garbage" bits. */
int diff = m_buttons ^ button_bits;
@@ -390,11 +390,11 @@ void GHOST_NDOFManager::updateButtons(int button_bits, uint64_t time)
void GHOST_NDOFManager::setDeadZone(float dz)
{
if (dz < 0.0f) {
- // negative values don't make sense, so clamp at zero
+ /* Negative values don't make sense, so clamp at zero. */
dz = 0.0f;
}
else if (dz > 0.5f) {
- // warn the rogue user/developer, but allow it
+ /* Warn the rogue user/developer, but allow it. */
GHOST_PRINTF("ndof: dead zone of %.2f is rather high...\n", dz);
}
m_deadZone = dz;
@@ -426,22 +426,22 @@ bool GHOST_NDOFManager::sendMotionEvent()
if (!m_motionEventPending)
return false;
- m_motionEventPending = false; // any pending motion is handled right now
+ m_motionEventPending = false; /* Any pending motion is handled right now. */
GHOST_IWindow *window = m_system.getWindowManager()->getActiveWindow();
if (window == NULL) {
- m_motionState = GHOST_kNotStarted; // avoid large 'dt' times when changing windows
- return false; // delivery will fail, so don't bother sending
+ m_motionState = GHOST_kNotStarted; /* Avoid large `dt` times when changing windows. */
+ return false; /* Delivery will fail, so don't bother sending. */
}
GHOST_EventNDOFMotion *event = new GHOST_EventNDOFMotion(m_motionTime, window);
GHOST_TEventNDOFMotionData *data = (GHOST_TEventNDOFMotionData *)event->getData();
- // scale axis values here to normalize them to around +/- 1
- // they are scaled again for overall sensitivity in the WM based on user prefs
+ /* Scale axis values here to normalize them to around +/- 1
+ * they are scaled again for overall sensitivity in the WM based on user preferences. */
- const float scale = 1.0f / 350.0f; // 3Dconnexion devices send +/- 350 usually
+ const float scale = 1.0f / 350.0f; /* 3Dconnexion devices send +/- 350 usually */
data->tx = scale * m_translation[0];
data->ty = scale * m_translation[1];
@@ -451,24 +451,24 @@ bool GHOST_NDOFManager::sendMotionEvent()
data->ry = scale * m_rotation[1];
data->rz = scale * m_rotation[2];
- data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
+ data->dt = 0.001f * (m_motionTime - m_prevMotionTime); /* In seconds. */
m_prevMotionTime = m_motionTime;
bool weHaveMotion = !nearHomePosition(data, m_deadZone);
- // determine what kind of motion event to send (Starting, InProgress, Finishing)
- // and where that leaves this NDOF manager (NotStarted, InProgress, Finished)
+ /* Determine what kind of motion event to send `(Starting, InProgress, Finishing)`
+ * and where that leaves this NDOF manager `(NotStarted, InProgress, Finished)`. */
switch (m_motionState) {
case GHOST_kNotStarted:
case GHOST_kFinished:
if (weHaveMotion) {
data->progress = GHOST_kStarting;
m_motionState = GHOST_kInProgress;
- // prev motion time will be ancient, so just make up a reasonable time delta
+ /* Previous motion time will be ancient, so just make up a reasonable time delta. */
data->dt = 0.0125f;
}
else {
- // send no event and keep current state
+ /* Send no event and keep current state. */
#ifdef DEBUG_NDOF_MOTION
printf("ndof motion ignored -- %s\n", progress_string[data->progress]);
#endif
@@ -479,20 +479,22 @@ bool GHOST_NDOFManager::sendMotionEvent()
case GHOST_kInProgress:
if (weHaveMotion) {
data->progress = GHOST_kInProgress;
- // remain 'InProgress'
+ /* Remain 'InProgress'. */
}
else {
data->progress = GHOST_kFinishing;
m_motionState = GHOST_kFinished;
}
break;
- default:; // will always be one of the above
+ default:
+ /* Will always be one of the above. */
+ break;
}
#ifdef DEBUG_NDOF_MOTION
printf("ndof motion sent -- %s\n", progress_string[data->progress]);
- // show details about this motion event
+ /* Show details about this motion event. */
printf(" T=(%d,%d,%d) R=(%d,%d,%d) raw\n",
m_translation[0],
m_translation[1],
diff --git a/intern/ghost/intern/GHOST_NDOFManager.h b/intern/ghost/intern/GHOST_NDOFManager.h
index 7be129c327c..31b11a352db 100644
--- a/intern/ghost/intern/GHOST_NDOFManager.h
+++ b/intern/ghost/intern/GHOST_NDOFManager.h
@@ -28,7 +28,7 @@
typedef enum {
NDOF_UnknownDevice,
- // current devices
+ /* Current devices. */
NDOF_SpaceNavigator,
NDOF_SpaceExplorer,
NDOF_SpacePilotPro,
@@ -37,51 +37,51 @@ typedef enum {
NDOF_SpaceMouseProWireless,
NDOF_SpaceMouseEnterprise,
- // older devices
+ /* Older devices. */
NDOF_SpacePilot,
NDOF_Spaceball5000,
NDOF_SpaceTraveler
} NDOF_DeviceT;
-// NDOF device button event types
+/* NDOF device button event types */
typedef enum {
- // used internally, never sent
+ /* Used internally, never sent. */
NDOF_BUTTON_NONE,
- // these two are available from any 3Dconnexion device
+ /* These two are available from any 3Dconnexion device. */
NDOF_BUTTON_MENU,
NDOF_BUTTON_FIT,
- // standard views
+ /* Standard views. */
NDOF_BUTTON_TOP,
NDOF_BUTTON_BOTTOM,
NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT,
NDOF_BUTTON_FRONT,
NDOF_BUTTON_BACK,
- // more views
+ /* More views. */
NDOF_BUTTON_ISO1,
NDOF_BUTTON_ISO2,
- // 90 degree rotations
- // these don't all correspond to physical buttons
+ /* 90 degree rotations.
+ * These don't all correspond to physical buttons. */
NDOF_BUTTON_ROLL_CW,
NDOF_BUTTON_ROLL_CCW,
NDOF_BUTTON_SPIN_CW,
NDOF_BUTTON_SPIN_CCW,
NDOF_BUTTON_TILT_CW,
NDOF_BUTTON_TILT_CCW,
- // device control
+ /* Device control. */
NDOF_BUTTON_ROTATE,
NDOF_BUTTON_PANZOOM,
NDOF_BUTTON_DOMINANT,
NDOF_BUTTON_PLUS,
NDOF_BUTTON_MINUS,
- // keyboard emulation
+ /* Keyboard emulation. */
NDOF_BUTTON_ESC,
NDOF_BUTTON_ALT,
NDOF_BUTTON_SHIFT,
NDOF_BUTTON_CTRL,
- // general-purpose buttons
- // users can assign functions via keymap editor
+ /* General-purpose buttons.
+ * Users can assign functions via keymap editor. */
NDOF_BUTTON_1,
NDOF_BUTTON_2,
NDOF_BUTTON_3,
@@ -92,11 +92,11 @@ typedef enum {
NDOF_BUTTON_8,
NDOF_BUTTON_9,
NDOF_BUTTON_10,
- // more general-purpose buttons
+ /* More general-purpose buttons. */
NDOF_BUTTON_A,
NDOF_BUTTON_B,
NDOF_BUTTON_C,
- // the end
+ /* The end. */
NDOF_BUTTON_LAST
} NDOF_ButtonT;
@@ -107,40 +107,53 @@ class GHOST_NDOFManager {
{
}
- // whether multi-axis functionality is available (via the OS or driver)
- // does not imply that a device is plugged in or being used
+ /**
+ * Whether multi-axis functionality is available (via the OS or driver)
+ * does not imply that a device is plugged in or being used.
+ */
virtual bool available() = 0;
- // each platform's device detection should call this
- // use standard USB/HID identifiers
+ /**
+ * Each platform's device detection should call this
+ * use standard USB/HID identifiers.
+ */
bool setDevice(unsigned short vendor_id, unsigned short product_id);
- // filter out small/accidental/uncalibrated motions by
- // setting up a "dead zone" around home position
- // set to 0 to disable
- // 0.1 is a safe and reasonable value
+ /**
+ * Filter out small/accidental/un-calibrated motions by
+ * setting up a "dead zone" around home position
+ * set to 0 to disable
+ * 0.1 is a safe and reasonable value.
+ */
void setDeadZone(float);
- // the latest raw axis data from the device
- // NOTE: axis data should be in blender view coordinates
- // +X is to the right
- // +Y is up
- // +Z is out of the screen
- // for rotations, look from origin to each +axis
- // rotations are + when CCW, - when CW
- // each platform is responsible for getting axis data into this form
- // these values should not be scaled (just shuffled or flipped)
+ /**
+ * The latest raw axis data from the device.
+ *
+ * \note axis data should be in blender view coordinates
+ * - +X is to the right.
+ * - +Y is up.
+ * - +Z is out of the screen.
+ * - for rotations, look from origin to each +axis.
+ * - rotations are + when CCW, - when CW.
+ * Each platform is responsible for getting axis data into this form
+ * these values should not be scaled (just shuffled or flipped).
+ */
void updateTranslation(const int t[3], uint64_t time);
void updateRotation(const int r[3], uint64_t time);
- // the latest raw button data from the device
- // use HID button encoding (not NDOF_ButtonT)
+ /**
+ * The latest raw button data from the device
+ * use HID button encoding (not #NDOF_ButtonT).
+ */
void updateButton(int button_number, bool press, uint64_t time);
void updateButtons(int button_bits, uint64_t time);
- // NDOFButton events are sent immediately
+ /* #NDOFButton events are sent immediately */
- // processes and sends most recent raw data as an NDOFMotion event
- // returns whether an event was sent
+ /**
+ * Processes and sends most recent raw data as an #NDOFMotion event
+ * returns whether an event was sent.
+ */
bool sendMotionEvent();
protected:
@@ -157,12 +170,12 @@ class GHOST_NDOFManager {
int m_translation[3];
int m_rotation[3];
- int m_buttons; // bit field
+ int m_buttons; /* Bit field. */
- uint64_t m_motionTime; // in milliseconds
- uint64_t m_prevMotionTime; // time of most recent Motion event sent
+ uint64_t m_motionTime; /* In milliseconds. */
+ uint64_t m_prevMotionTime; /* Time of most recent motion event sent. */
GHOST_TProgress m_motionState;
bool m_motionEventPending;
- float m_deadZone; // discard motion with each component < this
+ float m_deadZone; /* Discard motion with each component < this. */
};
diff --git a/intern/ghost/intern/GHOST_Rect.cpp b/intern/ghost/intern/GHOST_Rect.cpp
index 8ef9486f35a..78c88cb0a71 100644
--- a/intern/ghost/intern/GHOST_Rect.cpp
+++ b/intern/ghost/intern/GHOST_Rect.cpp
@@ -26,14 +26,14 @@
void GHOST_Rect::inset(int32_t i)
{
if (i > 0) {
- // Grow the rectangle
+ /* Grow the rectangle. */
m_l -= i;
m_r += i;
m_t -= i;
m_b += i;
}
else if (i < 0) {
- // Shrink the rectangle, check for insets larger than half the size
+ /* Shrink the rectangle, check for insets larger than half the size. */
int32_t i2 = i * 2;
if (getWidth() > i2) {
m_l += i;
@@ -62,12 +62,12 @@ GHOST_TVisibility GHOST_Rect::getVisibility(GHOST_Rect &r) const
bool rb = isInside(r.m_r, r.m_b);
GHOST_TVisibility v;
if (lt && rt && lb && rb) {
- // All points inside, rectangle is inside this
+ /* All points inside, rectangle is inside this. */
v = GHOST_kFullyVisible;
}
else if (!(lt || rt || lb || rb)) {
- // None of the points inside
- // Check to see whether the rectangle is larger than this one
+ /* None of the points inside.
+ * Check to see whether the rectangle is larger than this one. */
if ((r.m_l < m_l) && (r.m_t < m_t) && (r.m_r > m_r) && (r.m_b > m_b)) {
v = GHOST_kPartiallyVisible;
}
@@ -76,7 +76,7 @@ GHOST_TVisibility GHOST_Rect::getVisibility(GHOST_Rect &r) const
}
}
else {
- // Some of the points inside, rectangle is partially inside
+ /* Some of the points inside, rectangle is partially inside. */
v = GHOST_kPartiallyVisible;
}
return v;
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index f6659cf50dc..d09c167cb95 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -72,7 +72,7 @@ GHOST_ITimerTask *GHOST_System::installTimer(uint64_t delay,
GHOST_TimerTask *timer = new GHOST_TimerTask(millis + delay, interval, timerProc, userData);
if (timer) {
if (m_timerManager->addTimer(timer) == GHOST_kSuccess) {
- // Check to see whether we need to fire the timer right away
+ /* Check to see whether we need to fire the timer right away. */
m_timerManager->fireTimers(millis);
}
else {
@@ -208,7 +208,7 @@ bool GHOST_System::getFullScreen(void)
void GHOST_System::dispatchEvents()
{
#ifdef WITH_INPUT_NDOF
- // NDOF Motion event is sent only once per dispatch, so do it now:
+ /* NDOF Motion event is sent only once per dispatch, so do it now: */
if (m_ndofManager) {
m_ndofManager->sendMotionEvent();
}
@@ -260,10 +260,10 @@ GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent *event)
GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bool &isDown) const
{
GHOST_ModifierKeys keys;
- // Get the state of all modifier keys
+ /* Get the state of all modifier keys. */
GHOST_TSuccess success = getModifierKeys(keys);
if (success) {
- // Isolate the state of the key requested
+ /* Isolate the state of the key requested. */
isDown = keys.get(mask);
}
return success;
@@ -272,10 +272,10 @@ GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bo
GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButtonMask mask, bool &isDown) const
{
GHOST_Buttons buttons;
- // Get the state of all mouse buttons
+ /* Get the state of all mouse buttons. */
GHOST_TSuccess success = getButtons(buttons);
if (success) {
- // Isolate the state of the mouse button requested
+ /* Isolate the state of the mouse button requested. */
isDown = buttons.get(mask);
}
return success;
@@ -311,7 +311,7 @@ GHOST_TSuccess GHOST_System::init()
m_eventPrinter = new GHOST_EventPrinter();
m_eventManager->addConsumer(m_eventPrinter);
}
-#endif // WITH_GHOST_DEBUG
+#endif /* WITH_GHOST_DEBUG */
if (m_timerManager && m_windowManager && m_eventManager) {
return GHOST_kSuccess;
@@ -359,7 +359,7 @@ GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window,
if (alphaBackground)
glSettings.flags |= GHOST_glAlphaBackground;
- /* note: don't use getCurrentDisplaySetting() because on X11 we may
+ /* NOTE: don't use #getCurrentDisplaySetting() because on X11 we may
* be zoomed in and the desktop may be bigger than the viewport. */
GHOST_ASSERT(m_displayManager,
"GHOST_System::createFullScreenWindow(): invalid display manager");
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index 9164687c5b5..16c34ff1a6d 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -113,8 +113,8 @@ class GHOST_System : public GHOST_ISystem {
GHOST_TSuccess disposeWindow(GHOST_IWindow *window);
/**
- * Create a new offscreen context.
- * Never explicitly delete the context, use disposeContext() instead.
+ * Create a new off-screen context.
+ * Never explicitly delete the context, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
virtual GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings) = 0;
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h
index 48a64b155fc..5950da6813d 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.h
+++ b/intern/ghost/intern/GHOST_SystemCocoa.h
@@ -112,8 +112,8 @@ class GHOST_SystemCocoa : public GHOST_System {
const GHOST_IWindow *parentWindow = NULL);
/**
- * Create a new offscreen context.
- * Never explicitly delete the context, use disposeContext() instead.
+ * Create a new off-screen context.
+ * Never explicitly delete the context, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings);
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 0f10d5815f4..2b4c3237c73 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -761,7 +761,7 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
}
/**
- * Create a new offscreen context.
+ * Create a new off-screen context.
* Never explicitly delete the context, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
@@ -1587,7 +1587,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
{
NSTimeInterval timestamp = [event timestamp];
if (timestamp < m_last_warp_timestamp) {
- /* After warping we can still receive older unwarped mouse events,
+ /* After warping we can still receive older unwrapped mouse events,
* ignore those. */
break;
}
diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp
index f2f1b26b8e5..0309a4f9c52 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.cpp
+++ b/intern/ghost/intern/GHOST_SystemSDL.cpp
@@ -143,7 +143,7 @@ GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings glSetti
{
GHOST_Context *context = new GHOST_ContextSDL(0,
NULL,
- 0, // profile bit
+ 0, /* Profile bit. */
3,
3,
GHOST_OPENGL_SDL_CONTEXT_FLAGS,
@@ -279,7 +279,7 @@ static GHOST_TKey convertSDLKey(SDL_Scancode key)
GXMAP(type, SDL_SCANCODE_AUDIOPLAY, GHOST_kKeyMediaPlay);
GXMAP(type, SDL_SCANCODE_AUDIOSTOP, GHOST_kKeyMediaStop);
GXMAP(type, SDL_SCANCODE_AUDIOPREV, GHOST_kKeyMediaFirst);
- // GXMAP(type,XF86XK_AudioRewind, GHOST_kKeyMediaFirst);
+ // GXMAP(type, XF86XK_AudioRewind, GHOST_kKeyMediaFirst);
GXMAP(type, SDL_SCANCODE_AUDIONEXT, GHOST_kKeyMediaLast);
default:
@@ -315,7 +315,10 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
SDL_WindowEvent &sdl_sub_evt = sdl_event->window;
GHOST_WindowSDL *window = findGhostWindow(
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
- // assert(window != NULL); // can be NULL on close window.
+ /* Can be NULL on close window. */
+#if 0
+ assert(window != NULL);
+#endif
switch (sdl_sub_evt.event) {
case SDL_WINDOWEVENT_EXPOSED:
@@ -376,14 +379,14 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
window->getCursorGrabAccum(x_accum, y_accum);
- // can't use setCursorPosition because the mouse may have no focus!
+ /* Can't use #setCursorPosition because the mouse may have no focus! */
if (x_new != x_root || y_new != y_root) {
- if (1) { //xme.time > m_last_warp) {
+ if (1 /* `xme.time > m_last_warp` */ ) {
/* when wrapping we don't need to add an event because the
- * setCursorPosition call will cause a new event after */
+ * #setCursorPosition call will cause a new event after */
SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win); /* wrap */
window->setCursorGrabAccum(x_accum + (x_root - x_new), y_accum + (y_root - y_new));
- // m_last_warp= lastEventTime(xme.time);
+ // m_last_warp = lastEventTime(xme.time);
}
else {
// setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
@@ -659,8 +662,8 @@ bool GHOST_SystemSDL::generateWindowExposeEvents()
bool GHOST_SystemSDL::processEvents(bool waitForEvent)
{
- // Get all the current events -- translate them into
- // ghost events and call base class pushEvent() method.
+ /* Get all the current events - translate them into
+ * ghost events and call base class #pushEvent() method. */
bool anyProcessed = false;
@@ -679,7 +682,7 @@ bool GHOST_SystemSDL::processEvents(bool waitForEvent)
if (maxSleep >= 0) {
SDL_WaitEventTimeout(NULL, next - getMilliSeconds());
- // SleepTillEvent(m_display, next - getMilliSeconds()); // X11
+ // SleepTillEvent(m_display, next - getMilliSeconds()); /* X11. */
}
}
}
@@ -707,10 +710,10 @@ GHOST_WindowSDL *GHOST_SystemSDL::findGhostWindow(SDL_Window *sdl_win)
if (sdl_win == NULL)
return NULL;
- // It is not entirely safe to do this as the backptr may point
- // to a window that has recently been removed.
- // We should always check the window manager's list of windows
- // and only process events on these windows.
+ /* It is not entirely safe to do this as the backptr may point
+ * to a window that has recently been removed.
+ * We should always check the window manager's list of windows
+ * and only process events on these windows. */
const std::vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows();
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 4f5e957077d..60fd175dbf7 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -259,7 +259,7 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title,
}
/**
- * Create a new offscreen context.
+ * Create a new off-screen context.
* Never explicitly delete the window, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
@@ -363,7 +363,7 @@ GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
}
/**
- * Create a new offscreen DirectX 11 context.
+ * Create a new off-screen DirectX 11 context.
* Never explicitly delete the window, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
@@ -1219,6 +1219,12 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA
ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0];
}
+#ifdef WITH_INPUT_IME
+ if (window->getImeInput()->IsImeKeyEvent(ascii)) {
+ return NULL;
+ }
+#endif /* WITH_INPUT_IME */
+
event = new GHOST_EventKey(system->getMilliSeconds(),
keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp,
window,
@@ -1419,6 +1425,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
system->handleKeyboardChange();
#ifdef WITH_INPUT_IME
window->getImeInput()->SetInputLanguage();
+ window->getImeInput()->UpdateConversionStatus(hwnd);
#endif
break;
}
@@ -1455,6 +1462,13 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
////////////////////////////////////////////////////////////////////////
// IME events, processed, read more in GHOST_IME.h
////////////////////////////////////////////////////////////////////////
+ case WM_IME_NOTIFY: {
+ /* Update conversion status when IME is changed or input mode is changed. */
+ if (wParam == IMN_SETOPENSTATUS || wParam == IMN_SETCONVERSIONMODE) {
+ window->getImeInput()->UpdateConversionStatus(hwnd);
+ }
+ break;
+ }
case WM_IME_SETCONTEXT: {
GHOST_ImeWin32 *ime = window->getImeInput();
ime->SetInputLanguage();
@@ -1466,8 +1480,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
case WM_IME_STARTCOMPOSITION: {
GHOST_ImeWin32 *ime = window->getImeInput();
eventHandled = true;
- /* remove input event before start comp event, avoid redundant input */
- eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
ime->CreateImeWindow(hwnd);
ime->ResetComposition(hwnd);
event = processImeEvent(GHOST_kEventImeCompositionStart, window, &ime->eventImeData);
@@ -2111,10 +2123,9 @@ GHOST_TSuccess GHOST_SystemWin32::showMessageBox(const char *title,
config.cbSize = sizeof(config);
config.hInstance = 0;
config.dwCommonButtons = 0;
- config.pszMainIcon = (dialog_options & GHOST_DialogError ?
- TD_ERROR_ICON :
- dialog_options & GHOST_DialogWarning ? TD_WARNING_ICON :
- TD_INFORMATION_ICON);
+ config.pszMainIcon = (dialog_options & GHOST_DialogError ? TD_ERROR_ICON :
+ dialog_options & GHOST_DialogWarning ? TD_WARNING_ICON :
+ TD_INFORMATION_ICON);
config.pszWindowTitle = L"Blender";
config.pszMainInstruction = title_16;
config.pszContent = message_16;
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 6c786aedfb1..4794982dc65 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -138,8 +138,8 @@ class GHOST_SystemWin32 : public GHOST_System {
const GHOST_IWindow *parentWindow = 0);
/**
- * Create a new offscreen context.
- * Never explicitly delete the window, use disposeContext() instead.
+ * Create a new off-screen context.
+ * Never explicitly delete the window, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings);
@@ -152,8 +152,8 @@ class GHOST_SystemWin32 : public GHOST_System {
GHOST_TSuccess disposeContext(GHOST_IContext *context);
/**
- * Create a new offscreen DirectX context.
- * Never explicitly delete the context, use disposeContext() instead.
+ * Create a new off-screen DirectX context.
+ * Never explicitly delete the context, use #disposeContext() instead.
* This is for GHOST internal, Win32 specific use, so it can be called statically.
*
* \return The new context (or 0 if creation failed).
@@ -360,8 +360,8 @@ class GHOST_SystemWin32 : public GHOST_System {
static GHOST_EventKey *processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw);
/**
- * Process special keys (VK_OEM_*), to see if current key layout
- * gives us anything special, like ! on french AZERTY.
+ * Process special keys `VK_OEM_*`, to see if current key layout
+ * gives us anything special, like `!` on French AZERTY.
* \param vKey: The virtual key from #hardKey.
* \param scanCode: The ScanCode of pressed key (similar to PS/2 Set 1).
*/
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 172fcbeb3de..9fcad8aabf7 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -390,21 +390,21 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title,
}
/**
- * Create a new offscreen context.
- * Never explicitly delete the context, use disposeContext() instead.
+ * Create a new off-screen context.
+ * Never explicitly delete the context, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSettings)
{
- // During development:
- // try 4.x compatibility profile
- // try 3.3 compatibility profile
- // fall back to 3.0 if needed
- //
- // Final Blender 2.8:
- // try 4.x core profile
- // try 3.3 core profile
- // no fallbacks
+ /* During development:
+ * try 4.x compatibility profile
+ * try 3.3 compatibility profile
+ * fall back to 3.0 if needed
+ *
+ * Final Blender 2.8:
+ * try 4.x core profile
+ * try 3.3 core profile
+ * no fall-backs. */
const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0;
@@ -1189,9 +1189,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
else {
printf("Bad keycode lookup. Keysym 0x%x Status: %s\n",
(unsigned int)key_sym,
- (status == XLookupNone ?
- "XLookupNone" :
- status == XLookupKeySym ? "XLookupKeySym" : "Unknown status"));
+ (status == XLookupNone ? "XLookupNone" :
+ status == XLookupKeySym ? "XLookupKeySym" :
+ "Unknown status"));
printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim);
}
@@ -2014,7 +2014,7 @@ void GHOST_SystemX11::getClipboard_xcout(const XEvent *evt,
return;
}
- // not using INCR mechanism, just read the property
+ /* Not using INCR mechanism, just read the property. */
XGetWindowProperty(m_display,
win,
m_atom.XCLIP_OUT,
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index 15ccde4a14b..d4803f88fbb 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -148,8 +148,8 @@ class GHOST_SystemX11 : public GHOST_System {
const GHOST_IWindow *parentWindow = 0);
/**
- * Create a new offscreen context.
- * Never explicitly delete the context, use disposeContext() instead.
+ * Create a new off-screen context.
+ * Never explicitly delete the context, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings);
diff --git a/intern/ghost/intern/GHOST_TimerManager.cpp b/intern/ghost/intern/GHOST_TimerManager.cpp
index 195135f5f85..0c88150381f 100644
--- a/intern/ghost/intern/GHOST_TimerManager.cpp
+++ b/intern/ghost/intern/GHOST_TimerManager.cpp
@@ -55,7 +55,7 @@ GHOST_TSuccess GHOST_TimerManager::addTimer(GHOST_TimerTask *timer)
{
GHOST_TSuccess success;
if (!getTimerFound(timer)) {
- // Add the timer task
+ /* Add the timer task. */
m_timers.push_back(timer);
success = GHOST_kSuccess;
}
@@ -70,7 +70,7 @@ GHOST_TSuccess GHOST_TimerManager::removeTimer(GHOST_TimerTask *timer)
GHOST_TSuccess success;
TTimerVector::iterator iter = std::find(m_timers.begin(), m_timers.end(), timer);
if (iter != m_timers.end()) {
- // Remove the timer task
+ /* Remove the timer task. */
m_timers.erase(iter);
delete timer;
success = GHOST_kSuccess;
@@ -113,14 +113,14 @@ bool GHOST_TimerManager::fireTimer(uint64_t time, GHOST_TimerTask *task)
{
uint64_t next = task->getNext();
- // Check if the timer should be fired
+ /* Check if the timer should be fired. */
if (time > next) {
- // Fire the timer
+ /* Fire the timer. */
GHOST_TimerProcPtr timerProc = task->getTimerProc();
uint64_t start = task->getStart();
timerProc(task, time - start);
- // Update the time at which we will fire it again
+ /* Update the time at which we will fire it again. */
uint64_t interval = task->getInterval();
uint64_t numCalls = (next - start) / interval;
numCalls++;
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h
index 0fd70514ac6..68ac507f0e0 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.h
+++ b/intern/ghost/intern/GHOST_WindowCocoa.h
@@ -157,7 +157,7 @@ class GHOST_WindowCocoa : public GHOST_Window {
void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const;
/**
- * Converts a point in screen coordinates to client rectangle coordinates
+ * Converts a point in client rectangle coordinates to screen coordinates.
* \param inX: The x-coordinate in the client rectangle.
* \param inY: The y-coordinate in the client rectangle.
* \param outX: The x-coordinate on the screen.
@@ -166,7 +166,7 @@ class GHOST_WindowCocoa : public GHOST_Window {
void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const;
/**
- * Converts a point in screen coordinates to client rectangle coordinates
+ * Converts a point in client rectangle coordinates to screen coordinates.
* but without the y coordinate conversion needed for ghost compatibility.
* \param inX: The x-coordinate in the client rectangle.
* \param inY: The y-coordinate in the client rectangle.
@@ -178,10 +178,10 @@ class GHOST_WindowCocoa : public GHOST_Window {
/**
* Converts a point in screen coordinates to client rectangle coordinates,
* but without the y coordinate conversion needed for ghost compatibility.
- * \param inX: The x-coordinate in the client rectangle.
- * \param inY: The y-coordinate in the client rectangle.
- * \param outX: The x-coordinate on the screen.
- * \param outY: The y-coordinate on the screen.
+ * \param inX: The x-coordinate on the screen.
+ * \param inY: The y-coordinate on the screen.
+ * \param outX: The x-coordinate in the client rectangle.
+ * \param outY: The y-coordinate in the client rectangle.
*/
void screenToClientIntern(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const;
diff --git a/intern/ghost/intern/GHOST_WindowManager.cpp b/intern/ghost/intern/GHOST_WindowManager.cpp
index eec4bc5f7d0..4cb80884209 100644
--- a/intern/ghost/intern/GHOST_WindowManager.cpp
+++ b/intern/ghost/intern/GHOST_WindowManager.cpp
@@ -45,7 +45,7 @@ GHOST_TSuccess GHOST_WindowManager::addWindow(GHOST_IWindow *window)
GHOST_TSuccess success = GHOST_kFailure;
if (window) {
if (!getWindowFound(window)) {
- // Store the pointer to the window
+ /* Store the pointer to the window. */
m_windows.push_back(window);
success = GHOST_kSuccess;
}
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index d0c8cfb9e73..71062a4b6d6 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -34,7 +34,7 @@ static constexpr size_t base_dpi = 96;
struct window_t {
GHOST_WindowWayland *w;
wl_surface *surface;
- // outputs on which the window is currently shown on
+ /* Outputs on which the window is currently shown on. */
std::unordered_set<const output_t *> outputs;
uint16_t dpi = 0;
int scale = 1;
@@ -154,8 +154,8 @@ static bool update_scale(GHOST_WindowWayland *window)
if (scale > 0 && window->scale() != scale) {
window->scale() = scale;
- // using the real DPI will cause wrong scaling of the UI
- // use a multiplier for the default DPI as workaround
+ /* Using the real DPI will cause wrong scaling of the UI
+ * use a multiplier for the default DPI as workaround. */
window->dpi() = scale * base_dpi;
wl_surface_set_buffer_scale(window->surface(), scale);
return true;
diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h
index 6ffcf99b48c..afbdf1268ee 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.h
+++ b/intern/ghost/intern/GHOST_WindowWayland.h
@@ -29,9 +29,9 @@
class GHOST_SystemWayland;
+struct output_t;
struct window_t;
struct wl_surface;
-struct output_t;
class GHOST_WindowWayland : public GHOST_Window {
public:
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index 40a658bf88b..ed5292f1712 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -183,7 +183,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const;
/**
- * Converts a point in screen coordinates to client rectangle coordinates
+ * Converts a point in client rectangle coordinates to screen coordinates.
* \param inX: The x-coordinate in the client rectangle.
* \param inY: The y-coordinate in the client rectangle.
* \param outX: The x-coordinate on the screen.
diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp
index 35280e77e22..919d11d22a9 100644
--- a/intern/ghost/intern/GHOST_XrSession.cpp
+++ b/intern/ghost/intern/GHOST_XrSession.cpp
@@ -120,7 +120,7 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &
XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
create_info.poseInReferenceSpace.orientation.w = 1.0f;
- create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
+ create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
#if 0
/* TODO
*
@@ -144,8 +144,47 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &
(void)base_pose;
#endif
- CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space),
- "Failed to create reference space.");
+ XrResult result = xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space);
+
+ if (XR_FAILED(result)) {
+ /* One of the rare cases where we don't want to immediately throw an exception on failure,
+ * since run-times are not required to support the stage reference space. Although we need the
+ * stage reference space for absolute tracking, if the runtime doesn't support it then just
+ * fallback to the local space. */
+ if (result == XR_ERROR_REFERENCE_SPACE_UNSUPPORTED) {
+ printf(
+ "Warning: XR runtime does not support stage reference space, disabling absolute "
+ "tracking.\n");
+
+ create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
+ CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space),
+ "Failed to create local reference space.");
+ }
+ else {
+ throw GHOST_XrException("Failed to create stage reference space.", result);
+ }
+ }
+ else {
+ /* Check if tracking bounds are valid. Tracking bounds may be invalid if the user did not
+ * define a tracking space via the XR runtime. */
+ XrExtent2Df extents;
+ CHECK_XR(xrGetReferenceSpaceBoundsRect(oxr.session, XR_REFERENCE_SPACE_TYPE_STAGE, &extents),
+ "Failed to get stage reference space bounds.");
+ if (extents.width == 0.0f || extents.height == 0.0f) {
+ printf(
+ "Warning: Invalid stage reference space bounds, disabling absolute tracking. To enable "
+ "absolute tracking, please define a tracking space via the XR runtime.\n");
+
+ /* Fallback to local space. */
+ if (oxr.reference_space != XR_NULL_HANDLE) {
+ CHECK_XR(xrDestroySpace(oxr.reference_space), "Failed to destroy stage reference space.");
+ }
+
+ create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
+ CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space),
+ "Failed to create local reference space.");
+ }
+ }
create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.view_space),
@@ -370,6 +409,7 @@ void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain,
XrCompositionLayerProjectionView &r_proj_layer_view,
XrSpaceLocation &view_location,
XrView &view,
+ uint32_t view_idx,
void *draw_customdata)
{
XrSwapchainImageBaseHeader *swapchain_image = swapchain.acquireDrawableSwapchainImage();
@@ -380,6 +420,8 @@ void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain,
r_proj_layer_view.fov = view.fov;
swapchain.updateCompositionLayerProjectViewSubImage(r_proj_layer_view.subImage);
+ assert(view_idx < 256);
+ draw_view_info.view_idx = (char)view_idx;
draw_view_info.expects_srgb_buffer = swapchain.isBufferSRGB();
draw_view_info.ofsx = r_proj_layer_view.subImage.imageRect.offset.x;
draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y;
@@ -429,6 +471,7 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer(
r_proj_layer_views[view_idx],
view_location,
m_oxr->views[view_idx],
+ view_idx,
draw_customdata);
}
diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h
index c871b98da46..d448585d14c 100644
--- a/intern/ghost/intern/GHOST_XrSession.h
+++ b/intern/ghost/intern/GHOST_XrSession.h
@@ -117,6 +117,7 @@ class GHOST_XrSession {
XrCompositionLayerProjectionView &r_proj_layer_view,
XrSpaceLocation &view_location,
XrView &view,
+ uint32_t view_idx,
void *draw_customdata);
void beginFrameDrawing();
void endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> &layers);
diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c
index 673821546e8..261a23a1196 100644
--- a/intern/guardedalloc/intern/mallocn.c
+++ b/intern/guardedalloc/intern/mallocn.c
@@ -67,7 +67,7 @@ const char *(*MEM_name_ptr)(void *vmemh) = MEM_lockfree_name_ptr;
void *aligned_malloc(size_t size, size_t alignment)
{
- /* posix_memalign requires alignment to be a multiple of sizeof(void *). */
+ /* #posix_memalign requires alignment to be a multiple of `sizeof(void *)`. */
assert(alignment >= ALIGNED_MALLOC_MINIMUM_ALIGNMENT);
#ifdef _WIN32
diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt
index 6fcf664d1f5..91ef1f4d038 100644
--- a/intern/libmv/CMakeLists.txt
+++ b/intern/libmv/CMakeLists.txt
@@ -217,39 +217,39 @@ if(WITH_LIBMV)
if(WITH_GTESTS)
include(GTestTesting)
- blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "" "" "")
+ blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}" "")
- BLENDER_SRC_GTEST("libmv_predict_tracks" "./libmv/autotrack/predict_tracks_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_tracks" "./libmv/autotrack/tracks_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_scoped_ptr" "./libmv/base/scoped_ptr_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_vector" "./libmv/base/vector_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_array_nd" "./libmv/image/array_nd_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_convolve" "./libmv/image/convolve_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_image" "./libmv/image/image_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_sample" "./libmv/image/sample_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_tuple" "./libmv/image/tuple_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_euclidean_resection" "./libmv/multiview/euclidean_resection_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_fundamental" "./libmv/multiview/fundamental_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_homography" "./libmv/multiview/homography_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_nviewtriangulation" "./libmv/multiview/nviewtriangulation_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_panography" "./libmv/multiview/panography_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_projection" "./libmv/multiview/projection_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_resection" "./libmv/multiview/resection_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_triangulation" "./libmv/multiview/triangulation_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_dogleg" "./libmv/numeric/dogleg_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_function_derivative" "./libmv/numeric/function_derivative_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_levenberg_marquardt" "./libmv/numeric/levenberg_marquardt_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_numeric" "./libmv/numeric/numeric_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_poly" "./libmv/numeric/poly_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_camera_intrinsics" "./libmv/simple_pipeline/camera_intrinsics_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_detect" "./libmv/simple_pipeline/detect_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_intersect" "./libmv/simple_pipeline/intersect_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_keyframe_selection" "./libmv/simple_pipeline/keyframe_selection_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_modal_solver" "./libmv/simple_pipeline/modal_solver_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_resect" "./libmv/simple_pipeline/resect_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_brute_region_tracker" "./libmv/tracking/brute_region_tracker_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_klt_region_tracker" "./libmv/tracking/klt_region_tracker_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
- BLENDER_SRC_GTEST("libmv_pyramid_region_tracker" "./libmv/tracking/pyramid_region_tracker_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_predict_tracks" "./libmv/autotrack/predict_tracks_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_tracks" "./libmv/autotrack/tracks_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_scoped_ptr" "./libmv/base/scoped_ptr_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_vector" "./libmv/base/vector_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_array_nd" "./libmv/image/array_nd_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_convolve" "./libmv/image/convolve_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_image" "./libmv/image/image_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_sample" "./libmv/image/sample_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_tuple" "./libmv/image/tuple_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_euclidean_resection" "./libmv/multiview/euclidean_resection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_fundamental" "./libmv/multiview/fundamental_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_homography" "./libmv/multiview/homography_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_nviewtriangulation" "./libmv/multiview/nviewtriangulation_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_panography" "./libmv/multiview/panography_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_projection" "./libmv/multiview/projection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_resection" "./libmv/multiview/resection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_triangulation" "./libmv/multiview/triangulation_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_dogleg" "./libmv/numeric/dogleg_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_function_derivative" "./libmv/numeric/function_derivative_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_levenberg_marquardt" "./libmv/numeric/levenberg_marquardt_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_numeric" "./libmv/numeric/numeric_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_poly" "./libmv/numeric/poly_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_camera_intrinsics" "./libmv/simple_pipeline/camera_intrinsics_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_detect" "./libmv/simple_pipeline/detect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_intersect" "./libmv/simple_pipeline/intersect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_keyframe_selection" "./libmv/simple_pipeline/keyframe_selection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_modal_solver" "./libmv/simple_pipeline/modal_solver_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_resect" "./libmv/simple_pipeline/resect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_brute_region_tracker" "./libmv/tracking/brute_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_klt_region_tracker" "./libmv/tracking/klt_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
+ blender_add_test_executable("libmv_pyramid_region_tracker" "./libmv/tracking/pyramid_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
endif()
else()
list(APPEND SRC
diff --git a/intern/memutil/MEM_Allocator.h b/intern/memutil/MEM_Allocator.h
index 50a3e978197..1dbb2d5a9f7 100644
--- a/intern/memutil/MEM_Allocator.h
+++ b/intern/memutil/MEM_Allocator.h
@@ -62,8 +62,8 @@ template<typename _Tp> struct MEM_Allocator {
return &__x;
}
- // NB: __n is permitted to be 0. The C++ standard says nothing
- // about what the return value is when __n == 0.
+ /* NOTE: `__n` is permitted to be 0.
+ * The C++ standard says nothing about what the return value is when `__n == 0`. */
_Tp *allocate(size_type __n, const void * = 0)
{
_Tp *__ret = NULL;
diff --git a/intern/memutil/MEM_CacheLimiter.h b/intern/memutil/MEM_CacheLimiter.h
index 6ca61fbcde1..a8706454de8 100644
--- a/intern/memutil/MEM_CacheLimiter.h
+++ b/intern/memutil/MEM_CacheLimiter.h
@@ -299,9 +299,9 @@ template<class T> class MEM_CacheLimiter {
if (!can_destroy_element(elem))
continue;
- /* by default 0 means highest priority element */
- /* casting a size type to int is questionable,
- but unlikely to cause problems */
+ /* By default 0 means highest priority element. */
+ /* Casting a size type to int is questionable,
+ * but unlikely to cause problems. */
int priority = -((int)(queue.size()) - i - 1);
priority = item_priority_func(elem->get()->get_data(), priority);
diff --git a/intern/numaapi/source/build_config.h b/intern/numaapi/source/build_config.h
index fdd6ff704c3..49d82aa3e87 100644
--- a/intern/numaapi/source/build_config.h
+++ b/intern/numaapi/source/build_config.h
@@ -324,6 +324,16 @@
# define ARCH_CPU_ARM64 1
# define ARCH_CPU_64_BITS 1
# define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__riscv) && __riscv_xlen == 32
+# define ARCH_CPU_RISCV_FAMILY 1
+# define ARCH_CPU_RISCV32 1
+# define ARCH_CPU_64_BITS 0
+# define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__riscv) && __riscv_xlen == 64
+# define ARCH_CPU_RISCV_FAMILY 1
+# define ARCH_CPU_RISCV64 1
+# define ARCH_CPU_64_BITS 1
+# define ARCH_CPU_LITTLE_ENDIAN 1
#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__)
# define ARCH_CPU_32_BITS 1
# define ARCH_CPU_LITTLE_ENDIAN 1
@@ -381,6 +391,9 @@
#if !defined(ARCH_CPU_PPC64_FAMILY)
# define ARCH_CPU_PPC64_FAMILY 0
#endif
+#if !defined(ARCH_CPU_RISCV_FAMILY)
+# define ARCH_CPU_RISCV_FAMILY 0
+#endif
#if !defined(ARCH_CPU_S390_FAMILY)
# define ARCH_CPU_S390_FAMILY 0
#endif
diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt
index 16334a80761..bce8a8baa84 100644
--- a/intern/opensubdiv/CMakeLists.txt
+++ b/intern/opensubdiv/CMakeLists.txt
@@ -129,5 +129,5 @@ if(WITH_GTESTS AND WITH_OPENSUBDIV)
add_definitions(${GLOG_DEFINES})
add_definitions(-DBLENDER_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE})
- BLENDER_SRC_GTEST(opensubdiv_mesh_topology_test "internal/topology/mesh_topology_test.cc" "${LIB};bf_intern_opensubdiv")
+ blender_add_test_executable(opensubdiv_mesh_topology_test "internal/topology/mesh_topology_test.cc" "${INC}" "${INC_SYS}" "${LIB};bf_intern_opensubdiv")
endif()
diff --git a/intern/openvdb/intern/openvdb_level_set.cc b/intern/openvdb/intern/openvdb_level_set.cc
index ed0020a66ce..5b01c3b0cb7 100644
--- a/intern/openvdb/intern/openvdb_level_set.cc
+++ b/intern/openvdb/intern/openvdb_level_set.cc
@@ -33,20 +33,20 @@ OpenVDBLevelSet::~OpenVDBLevelSet()
}
void OpenVDBLevelSet::mesh_to_level_set(const float *vertices,
- const unsigned int *faces,
- const unsigned int totvertices,
- const unsigned int totfaces,
+ const int *faces,
+ const int totvertices,
+ const int totfaces,
const openvdb::math::Transform::Ptr &xform)
{
std::vector<openvdb::Vec3s> points(totvertices);
std::vector<openvdb::Vec3I> triangles(totfaces);
std::vector<openvdb::Vec4I> quads;
- for (unsigned int i = 0; i < totvertices; i++) {
+ for (int i = 0; i < totvertices; i++) {
points[i] = openvdb::Vec3s(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);
}
- for (unsigned int i = 0; i < totfaces; i++) {
+ for (int i = 0; i < totfaces; i++) {
triangles[i] = openvdb::Vec3I(faces[i * 3], faces[i * 3 + 1], faces[i * 3 + 2]);
}
@@ -69,14 +69,11 @@ void OpenVDBLevelSet::volume_to_mesh(OpenVDBVolumeToMeshData *mesh,
isovalue,
adaptivity,
relax_disoriented_triangles);
- mesh->vertices = (float *)MEM_malloc_arrayN(
- out_points.size(), 3 * sizeof(float), "openvdb remesher out verts");
- mesh->quads = (unsigned int *)MEM_malloc_arrayN(
- out_quads.size(), 4 * sizeof(unsigned int), "openvdb remesh out quads");
+ mesh->vertices = (float *)MEM_malloc_arrayN(out_points.size(), sizeof(float[3]), __func__);
+ mesh->quads = (int *)MEM_malloc_arrayN(out_quads.size(), sizeof(int[4]), __func__);
mesh->triangles = NULL;
if (out_tris.size() > 0) {
- mesh->triangles = (unsigned int *)MEM_malloc_arrayN(
- out_tris.size(), 3 * sizeof(unsigned int), "openvdb remesh out tris");
+ mesh->triangles = (int *)MEM_malloc_arrayN(out_tris.size(), sizeof(int[3]), __func__);
}
mesh->totvertices = out_points.size();
diff --git a/intern/openvdb/intern/openvdb_level_set.h b/intern/openvdb/intern/openvdb_level_set.h
index 882958513fd..2c8f140c012 100644
--- a/intern/openvdb/intern/openvdb_level_set.h
+++ b/intern/openvdb/intern/openvdb_level_set.h
@@ -39,9 +39,9 @@ struct OpenVDBLevelSet {
void set_grid(const openvdb::FloatGrid::Ptr &grid);
void mesh_to_level_set(const float *vertices,
- const unsigned int *faces,
- const unsigned int totvertices,
- const unsigned int totfaces,
+ const int *faces,
+ const int totvertices,
+ const int totfaces,
const openvdb::math::Transform::Ptr &transform);
void volume_to_mesh(struct OpenVDBVolumeToMeshData *mesh,
diff --git a/intern/openvdb/openvdb_capi.cc b/intern/openvdb/openvdb_capi.cc
index e7a4bf335fc..674b394fa46 100644
--- a/intern/openvdb/openvdb_capi.cc
+++ b/intern/openvdb/openvdb_capi.cc
@@ -63,9 +63,9 @@ void OpenVDBLevelSet_free(OpenVDBLevelSet *level_set)
void OpenVDBLevelSet_mesh_to_level_set(struct OpenVDBLevelSet *level_set,
const float *vertices,
- const unsigned int *faces,
- const unsigned int totvertices,
- const unsigned int totfaces,
+ const int *faces,
+ const int totvertices,
+ const int totfaces,
OpenVDBTransform *xform)
{
level_set->mesh_to_level_set(vertices, faces, totvertices, totfaces, xform->get_transform());
@@ -73,9 +73,9 @@ void OpenVDBLevelSet_mesh_to_level_set(struct OpenVDBLevelSet *level_set,
void OpenVDBLevelSet_mesh_to_level_set_transform(struct OpenVDBLevelSet *level_set,
const float *vertices,
- const unsigned int *faces,
- const unsigned int totvertices,
- const unsigned int totfaces,
+ const int *faces,
+ const int totvertices,
+ const int totfaces,
OpenVDBTransform *transform)
{
level_set->mesh_to_level_set(vertices, faces, totvertices, totfaces, transform->get_transform());
diff --git a/intern/openvdb/openvdb_capi.h b/intern/openvdb/openvdb_capi.h
index 98d89c340bf..9333413c2fe 100644
--- a/intern/openvdb/openvdb_capi.h
+++ b/intern/openvdb/openvdb_capi.h
@@ -67,19 +67,19 @@ struct OpenVDBVolumeToMeshData {
int totvertices;
float *vertices;
- unsigned int *quads;
- unsigned int *triangles;
+ int *quads;
+ int *triangles;
};
struct OpenVDBRemeshData {
float *verts;
- unsigned int *faces;
+ int *faces;
int totfaces;
int totverts;
float *out_verts;
- unsigned int *out_faces;
- unsigned int *out_tris;
+ int *out_faces;
+ int *out_tris;
int out_totverts;
int out_totfaces;
int out_tottris;
@@ -112,15 +112,15 @@ struct OpenVDBLevelSet *OpenVDBLevelSet_create(bool initGrid, struct OpenVDBTran
void OpenVDBLevelSet_free(struct OpenVDBLevelSet *level_set);
void OpenVDBLevelSet_mesh_to_level_set(struct OpenVDBLevelSet *level_set,
const float *vertices,
- const unsigned int *faces,
- const unsigned int totvertices,
- const unsigned int totfaces,
+ const int *faces,
+ const int totvertices,
+ const int totfaces,
struct OpenVDBTransform *xform);
void OpenVDBLevelSet_mesh_to_level_set_transform(struct OpenVDBLevelSet *level_set,
const float *vertices,
- const unsigned int *faces,
- const unsigned int totvertices,
- const unsigned int totfaces,
+ const int *faces,
+ const int totvertices,
+ const int totfaces,
struct OpenVDBTransform *transform);
void OpenVDBLevelSet_volume_to_mesh(struct OpenVDBLevelSet *level_set,
struct OpenVDBVolumeToMeshData *mesh,
diff --git a/intern/quadriflow/quadriflow_capi.cpp b/intern/quadriflow/quadriflow_capi.cpp
index 53237289874..086d5f7d296 100644
--- a/intern/quadriflow/quadriflow_capi.cpp
+++ b/intern/quadriflow/quadriflow_capi.cpp
@@ -20,12 +20,12 @@
#include "MEM_guardedalloc.h"
-#include "quadriflow_capi.hpp"
#include "config.hpp"
#include "field-math.hpp"
+#include "loader.hpp"
#include "optimizer.hpp"
#include "parametrizer.hpp"
-#include "loader.hpp"
+#include "quadriflow_capi.hpp"
using namespace qflow;
@@ -217,10 +217,8 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
qrd->out_totverts = field.O_compact.size();
qrd->out_totfaces = field.F_compact.size();
- qrd->out_verts = (float *)MEM_malloc_arrayN(
- qrd->out_totverts, 3 * sizeof(float), "quadriflow remesher out verts");
- qrd->out_faces = (unsigned int *)MEM_malloc_arrayN(
- qrd->out_totfaces, 4 * sizeof(unsigned int), "quadriflow remesh out quads");
+ qrd->out_verts = (float *)MEM_malloc_arrayN(qrd->out_totverts, sizeof(float[3]), __func__);
+ qrd->out_faces = (int *)MEM_malloc_arrayN(qrd->out_totfaces, sizeof(int[4]), __func__);
for (int i = 0; i < qrd->out_totverts; i++) {
auto t = field.O_compact[i] * field.normalize_scale + field.normalize_offset;
diff --git a/intern/quadriflow/quadriflow_capi.hpp b/intern/quadriflow/quadriflow_capi.hpp
index c31fd6eff95..59af2826e15 100644
--- a/intern/quadriflow/quadriflow_capi.hpp
+++ b/intern/quadriflow/quadriflow_capi.hpp
@@ -25,12 +25,12 @@ extern "C" {
typedef struct QuadriflowRemeshData {
float *verts;
- unsigned int *faces;
+ int *faces;
int totfaces;
int totverts;
float *out_verts;
- unsigned int *out_faces;
+ int *out_faces;
int out_totverts;
int out_totfaces;
diff --git a/intern/rigidbody/RBI_api.h b/intern/rigidbody/RBI_api.h
index 2e09f8952cb..f13f321a2c6 100644
--- a/intern/rigidbody/RBI_api.h
+++ b/intern/rigidbody/RBI_api.h
@@ -64,7 +64,7 @@ typedef struct rbConstraint rbConstraint;
/* Setup ---------------------------- */
/* Create a new dynamics world instance */
-// TODO: add args to set the type of constraint solvers, etc.
+/* TODO: add args to set the type of constraint solvers, etc. */
rbDynamicsWorld *RB_dworld_new(const float gravity[3]);
/* Delete the given dynamics world, and free any extra data it may require */
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 4833954c0ac85cc407e1d5a153aa11b1d1823ec
+Subproject 62e82958a760dad775d9b3387d7fb535fd6de4c
diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c
index 6220edce28f..d51a82c482b 100644
--- a/release/datafiles/userdef/userdef_default.c
+++ b/release/datafiles/userdef/userdef_default.c
@@ -113,7 +113,7 @@ const UserDef U_default = {
.gp_eraser = 25,
.gp_settings = 0,
- /** Initialized by: #BKE_studiolight_default . */
+ /** Initialized by: #BKE_studiolight_default. */
.light_param = {{0}},
.light_ambient = {0, 0, 0},
diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c
index 441a92127ea..85532d01d5c 100644
--- a/release/datafiles/userdef/userdef_default_theme.c
+++ b/release/datafiles/userdef/userdef_default_theme.c
@@ -638,7 +638,7 @@ const bTheme U_theme_default = {
.sub_back = RGBA(0x0000003e),
},
.shade1 = RGBA(0xa0a0a000),
- .grid = RGBA(0x404040ff),
+ .grid = RGBA(0x212121ff),
.vertex_select = RGBA(0xff8500ff),
.bone_pose = RGBA(0x50c8ff50),
.cframe = RGBA(0x5680c2ff),
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject f86f25e62217264495d05f116ccb09d575fe984
+Subproject 4475cbd11a636382d57571e0f5dfeff1f90bd6b
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 5a82baad9f986722104280e8354a4427d8e9eab
+Subproject 788441f2930465bbfba8f0797b12dcef1d46694
diff --git a/release/scripts/modules/bl_app_template_utils.py b/release/scripts/modules/bl_app_template_utils.py
index 43c0c571cb0..d4f867ce524 100644
--- a/release/scripts/modules/bl_app_template_utils.py
+++ b/release/scripts/modules/bl_app_template_utils.py
@@ -163,12 +163,12 @@ def import_from_id(template_id, *, ignore_not_found=False):
return import_from_path(path, ignore_not_found=ignore_not_found)
-def activate(*, template_id=None):
+def activate(*, template_id=None, reload_scripts=False):
template_id_prev = _app_template["id"]
# not needed but may as well avoids redundant
# disable/enable for all add-ons on 'File -> New'
- if template_id_prev == template_id:
+ if not reload_scripts and template_id_prev == template_id:
return
if template_id_prev:
@@ -188,6 +188,4 @@ def reset(*, reload_scripts=False):
if _bpy.app.debug_python:
print("bl_app_template_utils.reset('%s')" % template_id)
- # TODO reload_scripts
-
- activate(template_id=template_id)
+ activate(template_id=template_id, reload_scripts=reload_scripts)
diff --git a/release/scripts/modules/bl_i18n_utils/utils_cli.py b/release/scripts/modules/bl_i18n_utils/utils_cli.py
index e18491fa042..ea74ba832d9 100644
--- a/release/scripts/modules/bl_i18n_utils/utils_cli.py
+++ b/release/scripts/modules/bl_i18n_utils/utils_cli.py
@@ -71,7 +71,7 @@ def rtl_process_po(args, settings):
po.write(kind="PO", dest=args.dst)
-def language_menu(_args, settings):
+def language_menu(args, settings):
# 'DEFAULT' and en_US are always valid, fully-translated "languages"!
stats = {"DEFAULT": 1.0, "en_US": 1.0}
diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py
index e41eeeed4fa..2cd5dddefbc 100644
--- a/release/scripts/modules/bpy_extras/asset_utils.py
+++ b/release/scripts/modules/bpy_extras/asset_utils.py
@@ -56,6 +56,17 @@ class AssetBrowserPanel:
return SpaceAssetInfo.is_asset_browser_poll(context)
+class AssetBrowserSpecificCategoryPanel(AssetBrowserPanel):
+ asset_categories = set() # Set of strings like 'ANIMATIONS', see `asset_category_items` in rna_space.c
+
+ @classmethod
+ def poll(cls, context):
+ return (
+ SpaceAssetInfo.is_asset_browser_poll(context)
+ and context.space_data.params.asset_category in cls.asset_categories
+ )
+
+
class AssetMetaDataPanel:
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py
index a50997fab5f..a5c18cee463 100644
--- a/release/scripts/modules/nodeitems_utils.py
+++ b/release/scripts/modules/nodeitems_utils.py
@@ -77,7 +77,7 @@ class NodeItem:
else:
return bpy.app.translations.contexts.default
- # NB: is a staticmethod because called with an explicit self argument
+ # NOTE: is a staticmethod because called with an explicit self argument
# NodeItemCustom sets this as a variable attribute in __init__
@staticmethod
def draw(self, layout, _context):
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index 57fcd6f9752..f9756811bde 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -139,6 +139,7 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.use_resumable_cache*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-use-resumable-cache"),
("bpy.types.fluiddomainsettings.use_spray_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-spray-particles"),
("bpy.types.fluiddomainsettings.vector_display_type*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-display-type"),
+ ("bpy.types.geometrynodecurveprimitivebeziersegment*", "modeling/geometry_nodes/curve_primitives/bezier_segment.html#bpy-types-geometrynodecurveprimitivebeziersegment"),
("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"),
("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"),
("bpy.types.materialgpencilstyle.use_stroke_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-stroke-holdout"),
@@ -215,6 +216,7 @@ url_manual_mapping = (
("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"),
("bpy.types.spacesequenceeditor.show_strip_offset*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-offset"),
("bpy.types.spacesequenceeditor.show_strip_source*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-source"),
+ ("bpy.types.spacespreadsheetrowfilter.column_name*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-column-name"),
("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"),
("bpy.types.toolsettings.use_keyframe_cycle_aware*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-cycle-aware"),
("bpy.types.toolsettings.use_keyframe_insert_auto*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-insert-auto"),
@@ -233,6 +235,7 @@ url_manual_mapping = (
("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/dimensions.html#bpy-types-rendersettings-resolution-percentage"),
("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"),
("bpy.types.spaceoutliner.use_filter_object_mesh*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-mesh"),
+ ("bpy.types.spaceoutliner.use_filter_view_layers*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-view-layers"),
("bpy.types.spacesequenceeditor.show_overexposed*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-show-overexposed"),
("bpy.types.toolsettings.use_gpencil_draw_onback*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-onback"),
("bpy.types.toolsettings.use_snap_align_rotation*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-align-rotation"),
@@ -273,6 +276,8 @@ url_manual_mapping = (
("bpy.types.spacesequenceeditor.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-safe-areas"),
("bpy.types.spacesequenceeditor.show_strip_name*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-name"),
("bpy.types.spacespreadsheet.show_only_selected*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-show-only-selected"),
+ ("bpy.types.spacespreadsheetrowfilter.operation*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-operation"),
+ ("bpy.types.spacespreadsheetrowfilter.threshold*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-threshold"),
("bpy.types.toolsettings.use_snap_grid_absolute*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-grid-absolute"),
("bpy.types.view3doverlay.show_face_orientation*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-face-orientation"),
("bpy.ops.object.blenderkit_material_thumbnail*", "addons/3d_view/blenderkit.html#bpy-ops-object-blenderkit-material-thumbnail"),
@@ -343,6 +348,7 @@ url_manual_mapping = (
("bpy.types.spaceoutliner.use_filter_complete*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-complete"),
("bpy.types.spacesequenceeditor.show_metadata*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-metadata"),
("bpy.types.spacespreadsheet.attribute_domain*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-attribute-domain"),
+ ("bpy.types.spacespreadsheetrowfilter.enabled*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-enabled"),
("bpy.types.spaceview3d.transform_orientation*", "editors/3dview/controls/orientation.html#bpy-types-spaceview3d-transform-orientation"),
("bpy.types.spaceview3d.use_local_collections*", "editors/3dview/sidebar.html#bpy-types-spaceview3d-use-local-collections"),
("bpy.types.toolsettings.use_snap_peel_object*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-peel-object"),
@@ -350,7 +356,6 @@ url_manual_mapping = (
("bpy.types.view3doverlay.wireframe_threshold*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-threshold"),
("bpy.ops.object.constraint_add_with_targets*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraint-add-with-targets"),
("bpy.ops.object.material_slot_remove_unused*", "scene_layout/object/editing/cleanup.html#bpy-ops-object-material-slot-remove-unused"),
- ("bpy.ops.object.vertex_group_copy_to_linked*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy-to-linked"),
("bpy.ops.outliner.collection_disable_render*", "editors/outliner/editing.html#bpy-ops-outliner-collection-disable-render"),
("bpy.types.brush.cloth_simulation_area_type*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-simulation-area-type"),
("bpy.types.brushgpencilsettings.fill_factor*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-factor"),
@@ -372,6 +377,7 @@ url_manual_mapping = (
("bpy.types.fluidflowsettings.use_plane_init*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-plane-init"),
("bpy.types.fluidflowsettings.velocity_coord*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-coord"),
("bpy.types.fluidflowsettings.volume_density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-volume-density"),
+ ("bpy.types.geometrynodecurvequadraticbezier*", "modeling/geometry_nodes/curve_primitives/quadratic_bezier.html#bpy-types-geometrynodecurvequadraticbezier"),
("bpy.types.materialgpencilstyle.show_stroke*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-show-stroke"),
("bpy.types.movietrackingcamera.focal_length*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-focal-length"),
("bpy.types.movietrackingcamera.pixel_aspect*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-pixel-aspect"),
@@ -471,6 +477,7 @@ url_manual_mapping = (
("bpy.types.geometrynodeattributecolorramp*", "modeling/geometry_nodes/attribute/attribute_color_ramp.html#bpy-types-geometrynodeattributecolorramp"),
("bpy.types.geometrynodeattributeproximity*", "modeling/geometry_nodes/attribute/attribute_proximity.html#bpy-types-geometrynodeattributeproximity"),
("bpy.types.geometrynodeattributerandomize*", "modeling/geometry_nodes/attribute/attribute_randomize.html#bpy-types-geometrynodeattributerandomize"),
+ ("bpy.types.geometrynodecurvequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurvequadrilateral"),
("bpy.types.geometrynodeseparatecomponents*", "modeling/geometry_nodes/geometry/separate_components.html#bpy-types-geometrynodeseparatecomponents"),
("bpy.types.geometrynodesubdivisionsurface*", "modeling/geometry_nodes/mesh/subdivision_surface.html#bpy-types-geometrynodesubdivisionsurface"),
("bpy.types.imageformatsettings.color_mode*", "render/output/properties/output.html#bpy-types-imageformatsettings-color-mode"),
@@ -490,7 +497,7 @@ url_manual_mapping = (
("bpy.types.rendersettings.use_compositing*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-compositing"),
("bpy.types.rendersettings.use_placeholder*", "render/output/properties/output.html#bpy-types-rendersettings-use-placeholder"),
("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"),
- ("bpy.types.spaceclipeditor.lock_selection*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-lock-selection"),
+ ("bpy.types.spaceclipeditor.lock_selection*", "editors/clip/introduction.html#bpy-types-spaceclipeditor-lock-selection"),
("bpy.types.spacedopesheeteditor.auto_snap*", "editors/dope_sheet/editing.html#bpy-types-spacedopesheeteditor-auto-snap"),
("bpy.types.spaceoutliner.show_mode_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-mode-column"),
("bpy.types.spacetexteditor.use_match_case*", "editors/text_editor.html#bpy-types-spacetexteditor-use-match-case"),
@@ -508,6 +515,7 @@ url_manual_mapping = (
("bpy.ops.outliner.collection_show_inside*", "editors/outliner/editing.html#bpy-ops-outliner-collection-show-inside"),
("bpy.ops.preferences.reset_default_theme*", "editors/preferences/themes.html#bpy-ops-preferences-reset-default-theme"),
("bpy.ops.sequencer.strip_transform_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-strip-transform-clear"),
+ ("bpy.ops.spreadsheet.add_row_filter_rule*", "editors/spreadsheet.html#bpy-ops-spreadsheet-add-row-filter-rule"),
("bpy.types.animdata.action_extrapolation*", "editors/nla/sidebar.html#bpy-types-animdata-action-extrapolation"),
("bpy.types.bakesettings.max_ray_distance*", "render/cycles/baking.html#bpy-types-bakesettings-max-ray-distance"),
("bpy.types.brush.multiplane_scrape_angle*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-multiplane-scrape-angle"),
@@ -695,6 +703,7 @@ url_manual_mapping = (
("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierfunctiongenerator"),
("bpy.types.geometrynodeattributeclamp*", "modeling/geometry_nodes/attribute/attribute_clamp.html#bpy-types-geometrynodeattributeclamp"),
("bpy.types.geometrynodecollectioninfo*", "modeling/geometry_nodes/input/collection_info.html#bpy-types-geometrynodecollectioninfo"),
+ ("bpy.types.geometrynodecurveendpoints*", "modeling/geometry_nodes/curve/curve_endpoints.html#bpy-types-geometrynodecurveendpoints"),
("bpy.types.geometrynodecurvesubdivide*", "modeling/geometry_nodes/curve/curve_subdivide.html#bpy-types-geometrynodecurvesubdivide"),
("bpy.types.geometrynodedeletegeometry*", "modeling/geometry_nodes/geometry/delete_geometry.html#bpy-types-geometrynodedeletegeometry"),
("bpy.types.geometrynodematerialassign*", "modeling/geometry_nodes/material/assign.html#bpy-types-geometrynodematerialassign"),
@@ -765,6 +774,7 @@ url_manual_mapping = (
("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"),
("bpy.types.geometrynodeinputmaterial*", "modeling/geometry_nodes/input/material.html#bpy-types-geometrynodeinputmaterial"),
("bpy.types.geometrynodemeshicosphere*", "modeling/geometry_nodes/mesh_primitives/icosphere.html#bpy-types-geometrynodemeshicosphere"),
+ ("bpy.types.geometrynodemeshsubdivide*", "modeling/geometry_nodes/mesh/subdivide.html#bpy-types-geometrynodemeshsubdivide"),
("bpy.types.geometrynodepointinstance*", "modeling/geometry_nodes/point/point_instance.html#bpy-types-geometrynodepointinstance"),
("bpy.types.geometrynodepointseparate*", "modeling/geometry_nodes/point/point_separate.html#bpy-types-geometrynodepointseparate"),
("bpy.types.geometrynoderesamplecurve*", "modeling/geometry_nodes/curve/resample_curve.html#bpy-types-geometrynoderesamplecurve"),
@@ -795,6 +805,7 @@ url_manual_mapping = (
("bpy.types.spline.tilt_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-tilt-interpolation"),
("bpy.types.transformorientation.name*", "editors/3dview/controls/orientation.html#bpy-types-transformorientation-name"),
("bpy.types.volumedisplay.slice_depth*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-depth"),
+ ("bpy.ops.clip.lock_selection_toggle*", "editors/clip/introduction.html#bpy-ops-clip-lock-selection-toggle"),
("bpy.ops.mesh.customdata_mask_clear*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-mesh-customdata-mask-clear"),
("bpy.ops.mesh.extrude_vertices_move*", "modeling/meshes/editing/vertex/extrude_vertices.html#bpy-ops-mesh-extrude-vertices-move"),
("bpy.ops.mesh.mod_weighted_strength*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-mod-weighted-strength"),
@@ -802,7 +813,6 @@ url_manual_mapping = (
("bpy.ops.mesh.select_interior_faces*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-interior-faces"),
("bpy.ops.mesh.select_similar_region*", "modeling/meshes/selecting/similar.html#bpy-ops-mesh-select-similar-region"),
("bpy.ops.mesh.tris_convert_to_quads*", "modeling/meshes/editing/face/triangles_quads.html#bpy-ops-mesh-tris-convert-to-quads"),
- ("bpy.ops.node.active_preview_toggle*", "modeling/geometry_nodes/introduction.html#bpy-ops-node-active-preview-toggle"),
("bpy.ops.object.datalayout_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data_layout.html#bpy-ops-object-datalayout-transfer"),
("bpy.ops.object.multires_base_apply*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-base-apply"),
("bpy.ops.object.randomize_transform*", "scene_layout/object/editing/transform/randomize.html#bpy-ops-object-randomize-transform"),
@@ -872,6 +882,7 @@ url_manual_mapping = (
("bpy.types.volumedisplay.slice_axis*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-axis"),
("bpy.ops.anim.channels_clean_empty*", "editors/nla/editing.html#bpy-ops-anim-channels-clean-empty"),
("bpy.ops.armature.select_hierarchy*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-hierarchy"),
+ ("bpy.ops.armature.switch_direction*", "animation/armatures/bones/editing/switch_direction.html#bpy-ops-armature-switch-direction"),
("bpy.ops.clip.apply_solution_scale*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-apply-solution-scale"),
("bpy.ops.clip.set_center_principal*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-center-principal"),
("bpy.ops.clip.setup_tracking_scene*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-setup-tracking-scene"),
@@ -925,7 +936,9 @@ url_manual_mapping = (
("bpy.types.functionnodeinputstring*", "modeling/geometry_nodes/input/string.html#bpy-types-functionnodeinputstring"),
("bpy.types.functionnodeinputvector*", "modeling/geometry_nodes/input/vector.html#bpy-types-functionnodeinputvector"),
("bpy.types.functionnoderandomfloat*", "modeling/geometry_nodes/input/random_float.html#bpy-types-functionnoderandomfloat"),
+ ("bpy.types.geometrynodecurvecircle*", "modeling/geometry_nodes/curve_primitives/circle.html#bpy-types-geometrynodecurvecircle"),
("bpy.types.geometrynodecurvelength*", "modeling/geometry_nodes/curve/curve_length.html#bpy-types-geometrynodecurvelength"),
+ ("bpy.types.geometrynodecurvespiral*", "modeling/geometry_nodes/curve_primitives/spiral.html#bpy-types-geometrynodecurvespiral"),
("bpy.types.geometrynodecurvetomesh*", "modeling/geometry_nodes/curve/curve_to_mesh.html#bpy-types-geometrynodecurvetomesh"),
("bpy.types.geometrynodemeshtocurve*", "modeling/geometry_nodes/curve/mesh_to_curve.html#bpy-types-geometrynodemeshtocurve"),
("bpy.types.geometrynodepointrotate*", "modeling/geometry_nodes/point/point_rotate.html#bpy-types-geometrynodepointrotate"),
@@ -989,6 +1002,7 @@ url_manual_mapping = (
("bpy.ops.sequencer.select_handles*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-handles"),
("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"),
("bpy.ops.view3d.blenderkit_search*", "addons/3d_view/blenderkit.html#bpy-ops-view3d-blenderkit-search"),
+ ("bpy.types.armature.axes_position*", "animation/armatures/properties/display.html#bpy-types-armature-axes-position"),
("bpy.types.armature.pose_position*", "animation/armatures/properties/skeleton.html#bpy-types-armature-pose-position"),
("bpy.types.bakesettings.use_clear*", "render/cycles/baking.html#bpy-types-bakesettings-use-clear"),
("bpy.types.bone.envelope_distance*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-envelope-distance"),
@@ -1024,6 +1038,7 @@ url_manual_mapping = (
("bpy.types.editbone.bbone_scalein*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scalein"),
("bpy.types.editbone.inherit_scale*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-inherit-scale"),
("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"),
+ ("bpy.types.geometrynodefloattoint*", "modeling/geometry_nodes/utilities/float_to_int.html#bpy-types-geometrynodefloattoint"),
("bpy.types.geometrynodeisviewport*", "modeling/geometry_nodes/input/is_viewport.html#bpy-types-geometrynodeisviewport"),
("bpy.types.geometrynodemeshcircle*", "modeling/geometry_nodes/mesh_primitives/circle.html#bpy-types-geometrynodemeshcircle"),
("bpy.types.geometrynodeobjectinfo*", "modeling/geometry_nodes/input/object_info.html#bpy-types-geometrynodeobjectinfo"),
@@ -1051,6 +1066,8 @@ url_manual_mapping = (
("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"),
("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"),
("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"),
+ ("bpy.ops.armature.calculate_roll*", "animation/armatures/bones/editing/bone_roll.html#bpy-ops-armature-calculate-roll"),
+ ("bpy.ops.armature.duplicate_move*", "animation/armatures/bones/editing/duplicate.html#bpy-ops-armature-duplicate-move"),
("bpy.ops.armature.select_similar*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-similar"),
("bpy.ops.clip.create_plane_track*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-create-plane-track"),
("bpy.ops.curve.spline_weight_set*", "modeling/curves/editing/other.html#bpy-ops-curve-spline-weight-set"),
@@ -1099,6 +1116,7 @@ url_manual_mapping = (
("bpy.ops.wm.operator_cheat_sheet*", "advanced/operators.html#bpy-ops-wm-operator-cheat-sheet"),
("bpy.ops.wm.previews_batch_clear*", "files/blend/previews.html#bpy-ops-wm-previews-batch-clear"),
("bpy.ops.wm.recover_last_session*", "files/blend/open_save.html#bpy-ops-wm-recover-last-session"),
+ ("bpy.types.armature.display_type*", "animation/armatures/properties/display.html#bpy-types-armature-display-type"),
("bpy.types.armature.use_mirror_x*", "animation/armatures/bones/tools/tool_settings.html#bpy-types-armature-use-mirror-x"),
("bpy.types.bakesettings.normal_b*", "render/cycles/baking.html#bpy-types-bakesettings-normal-b"),
("bpy.types.bakesettings.normal_g*", "render/cycles/baking.html#bpy-types-bakesettings-normal-g"),
@@ -1127,8 +1145,10 @@ url_manual_mapping = (
("bpy.types.editbone.bbone_rollin*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-rollin"),
("bpy.types.fluideffectorsettings*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings"),
("bpy.types.followtrackconstraint*", "animation/constraints/motion_tracking/follow_track.html#bpy-types-followtrackconstraint"),
+ ("bpy.types.geometrynodecurveline*", "modeling/geometry_nodes/curve_primitives/line.html#bpy-types-geometrynodecurveline"),
+ ("bpy.types.geometrynodecurvestar*", "modeling/geometry_nodes/curve_primitives/star.html#bpy-types-geometrynodecurvestar"),
+ ("bpy.types.geometrynodecurvetrim*", "modeling/geometry_nodes/curve/curve_trim.html#bpy-types-geometrynodecurvetrim"),
("bpy.types.geometrynodeedgesplit*", "modeling/geometry_nodes/mesh/edge_split.html#bpy-types-geometrynodeedgesplit"),
- ("bpy.types.geometrynodesubdivide*", "modeling/geometry_nodes/mesh/subdivide.html#bpy-types-geometrynodesubdivide"),
("bpy.types.geometrynodetransform*", "modeling/geometry_nodes/geometry/transform.html#bpy-types-geometrynodetransform"),
("bpy.types.gpencilsculptsettings*", "grease_pencil/properties/index.html#bpy-types-gpencilsculptsettings"),
("bpy.types.light.cutoff_distance*", "render/eevee/lighting.html#bpy-types-light-cutoff-distance"),
@@ -1247,6 +1267,8 @@ url_manual_mapping = (
("bpy.types.volumetomeshmodifier*", "modeling/modifiers/generate/volume_to_mesh.html#bpy-types-volumetomeshmodifier"),
("bpy.types.whitebalancemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-whitebalancemodifier"),
("bpy.ops.anim.channels_ungroup*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-ungroup"),
+ ("bpy.ops.armature.extrude_move*", "animation/armatures/bones/editing/extrude.html#bpy-ops-armature-extrude-move"),
+ ("bpy.ops.armature.parent_clear*", "animation/armatures/bones/editing/parenting.html#bpy-ops-armature-parent-clear"),
("bpy.ops.clip.clear_track_path*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-clear-track-path"),
("bpy.ops.clip.set_scene_frames*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-scene-frames"),
("bpy.ops.curve.handle_type_set*", "modeling/curves/editing/control_points.html#bpy-ops-curve-handle-type-set"),
@@ -1424,6 +1446,7 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"),
("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"),
("bpy.types.geometrynodeswitch*", "modeling/geometry_nodes/utilities/switch.html#bpy-types-geometrynodeswitch"),
+ ("bpy.types.geometrynodeviewer*", "modeling/geometry_nodes/output/viewer.html#bpy-types-geometrynodeviewer"),
("bpy.types.gpencilsculptguide*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide"),
("bpy.types.huecorrectmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-huecorrectmodifier"),
("bpy.types.imagepaint.stencil*", "sculpt_paint/texture_paint/tool_settings/mask.html#bpy-types-imagepaint-stencil"),
@@ -1462,7 +1485,9 @@ url_manual_mapping = (
("bpy.ops.anim.channels_group*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-group"),
("bpy.ops.anim.keyframe_clear*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-clear"),
("bpy.ops.armature.flip_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-flip-names"),
+ ("bpy.ops.armature.parent_set*", "animation/armatures/bones/editing/parenting.html#bpy-ops-armature-parent-set"),
("bpy.ops.armature.select_all*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-all"),
+ ("bpy.ops.armature.symmetrize*", "animation/armatures/bones/editing/symmetrize.html#bpy-ops-armature-symmetrize"),
("bpy.ops.clip.average_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-average-tracks"),
("bpy.ops.clip.refine_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-refine-markers"),
("bpy.ops.clip.select_grouped*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-grouped"),
@@ -1501,6 +1526,8 @@ url_manual_mapping = (
("bpy.ops.object.shade_smooth*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-smooth"),
("bpy.ops.object.voxel_remesh*", "modeling/meshes/retopology.html#bpy-ops-object-voxel-remesh"),
("bpy.ops.pose.armature_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-armature-apply"),
+ ("bpy.ops.pose.group_deselect*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-deselect"),
+ ("bpy.ops.pose.group_unassign*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-unassign"),
("bpy.ops.pose.select_grouped*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-grouped"),
("bpy.ops.poselib.pose_remove*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-remove"),
("bpy.ops.poselib.pose_rename*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-rename"),
@@ -1574,6 +1601,7 @@ url_manual_mapping = (
("bpy.types.wireframemodifier*", "modeling/modifiers/generate/wireframe.html#bpy-types-wireframemodifier"),
("bpy.types.worldmistsettings*", "render/cycles/world_settings.html#bpy-types-worldmistsettings"),
("bpy.ops.anim.channels_move*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-move"),
+ ("bpy.ops.armature.subdivide*", "animation/armatures/bones/editing/subdivide.html#bpy-ops-armature-subdivide"),
("bpy.ops.buttons.toggle_pin*", "editors/properties_editor.html#bpy-ops-buttons-toggle-pin"),
("bpy.ops.clip.filter_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-filter-tracks"),
("bpy.ops.clip.select_circle*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-circle"),
@@ -1675,6 +1703,8 @@ url_manual_mapping = (
("bpy.types.texturenodegroup*", "editors/texture_node/types/groups.html#bpy-types-texturenodegroup"),
("bpy.types.texturenodeimage*", "editors/texture_node/types/input/image.html#bpy-types-texturenodeimage"),
("bpy.types.viewlayer.use_ao*", "render/layers/introduction.html#bpy-types-viewlayer-use-ao"),
+ ("bpy.ops.armature.dissolve*", "animation/armatures/bones/editing/delete.html#bpy-ops-armature-dissolve"),
+ ("bpy.ops.armature.separate*", "animation/armatures/bones/editing/separate_bones.html#bpy-ops-armature-separate"),
("bpy.ops.clip.clean_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-clean-tracks"),
("bpy.ops.clip.delete_track*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-delete-track"),
("bpy.ops.clip.solve_camera*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-solve-camera"),
@@ -1703,6 +1733,8 @@ url_manual_mapping = (
("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"),
+ ("bpy.ops.pose.group_select*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-select"),
("bpy.ops.poselib.pose_move*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-pose-move"),
("bpy.ops.preferences.addon*", "editors/preferences/addons.html#bpy-ops-preferences-addon"),
("bpy.ops.scene.light_cache*", "render/eevee/render_settings/indirect_lighting.html#bpy-ops-scene-light-cache"),
@@ -1872,6 +1904,7 @@ url_manual_mapping = (
("bpy.ops.wm.owner_enable*", "interface/window_system/workspaces.html#bpy-ops-wm-owner-enable"),
("bpy.ops.wm.redraw_timer*", "advanced/operators.html#bpy-ops-wm-redraw-timer"),
("bpy.types.*light.shadow*", "render/eevee/lighting.html#bpy-types-light-shadow"),
+ ("bpy.types.armature.show*", "animation/armatures/properties/display.html#bpy-types-armature-show"),
("bpy.types.armaturebones*", "animation/armatures/bones/index.html#bpy-types-armaturebones"),
("bpy.types.arraymodifier*", "modeling/modifiers/generate/array.html#bpy-types-arraymodifier"),
("bpy.types.bevelmodifier*", "modeling/modifiers/generate/bevel.html#bpy-types-bevelmodifier"),
@@ -1919,6 +1952,8 @@ url_manual_mapping = (
("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"),
("bpy.types.windowmanager*", "interface/index.html#bpy-types-windowmanager"),
("bpy.ops.*.select_lasso*", "interface/selecting.html#bpy-ops-select-lasso"),
+ ("bpy.ops.armature.align*", "animation/armatures/bones/editing/transform.html#bpy-ops-armature-align"),
+ ("bpy.ops.armature.split*", "animation/armatures/bones/editing/split.html#bpy-ops-armature-split"),
("bpy.ops.clip.set_plane*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-plane"),
("bpy.ops.clip.set_scale*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-scale"),
("bpy.ops.curve.decimate*", "modeling/curves/editing/curve.html#bpy-ops-curve-decimate"),
@@ -1993,6 +2028,7 @@ url_manual_mapping = (
("bpy.types.wavemodifier*", "modeling/modifiers/deform/wave.html#bpy-types-wavemodifier"),
("bpy.types.weldmodifier*", "modeling/modifiers/generate/weld.html#bpy-types-weldmodifier"),
("bpy.types.wipesequence*", "video_editing/sequencer/strips/transitions/wipe.html#bpy-types-wipesequence"),
+ ("bpy.ops.armature.fill*", "animation/armatures/bones/editing/fill_between_joints.html#bpy-ops-armature-fill"),
("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"),
("bpy.ops.clip.set_axis*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-axis"),
("bpy.ops.file.pack_all*", "files/blend/packed_data.html#bpy-ops-file-pack-all"),
@@ -2064,6 +2100,7 @@ url_manual_mapping = (
("bpy.ops.uv.mark_seam*", "modeling/meshes/uv/unwrapping/seams.html#bpy-ops-uv-mark-seam"),
("bpy.ops.view3d.ruler*", "editors/3dview/toolbar/measure.html#bpy-ops-view3d-ruler"),
("bpy.types.areaspaces*", "interface/window_system/areas.html#bpy-types-areaspaces"),
+ ("bpy.types.bonegroups*", "animation/armatures/properties/bone_groups.html#bpy-types-bonegroups"),
("bpy.types.bpy_struct*", "files/data_blocks.html#bpy-types-bpy-struct"),
("bpy.types.collection*", "scene_layout/collections/collections.html#bpy-types-collection"),
("bpy.types.compositor*", "compositing/index.html#bpy-types-compositor"),
diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py
index 222ee43432f..eb66c961472 100644
--- a/release/scripts/presets/keyconfig/Blender.py
+++ b/release/scripts/presets/keyconfig/Blender.py
@@ -103,8 +103,8 @@ class Prefs(bpy.types.KeyConfigPreferences):
v3d_tilde_action: EnumProperty(
name="Tilde Action",
items=(
- ('OBJECT_SWITCH', "Object Switch",
- "Switch the active object under the cursor (when not in object mode)",
+ ('VIEW', "Navigate",
+ "View operations (useful for keyboards without a numpad)",
0),
('GIZMO', "Gizmos",
"Control transform gizmos",
@@ -113,7 +113,7 @@ class Prefs(bpy.types.KeyConfigPreferences):
description=(
"Action when 'Tilde' is pressed"
),
- default='OBJECT_SWITCH',
+ default='VIEW',
update=update_fn,
)
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 0af7493ed47..3527e993173 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -730,6 +730,8 @@ def km_user_interface(_params):
("anim.keyingset_button_add", {"type": 'K', "value": 'PRESS'}, None),
("anim.keyingset_button_remove", {"type": 'K', "value": 'PRESS', "alt": True}, None),
("ui.reset_default_button", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("all", True)]}),
+ # UI lists (polls check if there's a UI list under the cursor).
+ ("ui.list_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
])
return keymap
@@ -1084,7 +1086,13 @@ def km_view3d(params):
{"properties": [("use_all_regions", True), ("center", False)]}),
("view3d.view_all", {"type": 'C', "value": 'PRESS', "shift": True},
{"properties": [("center", True)]}),
- op_menu_pie("VIEW3D_MT_view_pie", {"type": 'D', "value": 'CLICK_DRAG'}),
+ op_menu_pie(
+ "VIEW3D_MT_view_pie" if params.v3d_tilde_action == 'VIEW' else "VIEW3D_MT_transform_gizmo_pie",
+ {"type": 'ACCENT_GRAVE', "value": params.pie_value},
+ ),
+ *(() if not params.use_pie_click_drag else
+ (("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'CLICK'}, None),)),
+ ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None),
("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None),
# Numpad views.
("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
@@ -1328,32 +1336,6 @@ def km_view3d(params):
op_tool_cycle("builtin.select_box", {"type": 'W', "value": 'PRESS'}),
])
- # Tilda key.
- if params.use_pie_click_drag:
- items.extend([
- ("object.transfer_mode",
- {"type": 'ACCENT_GRAVE', "value": 'CLICK' if params.use_pie_click_drag else 'PRESS'},
- None),
- op_menu_pie(
- "VIEW3D_MT_transform_gizmo_pie",
- {"type": 'ACCENT_GRAVE', "value": 'CLICK_DRAG'},
- )
- ])
- else:
- if params.v3d_tilde_action == 'OBJECT_SWITCH':
- items.append(
- ("object.transfer_mode",
- {"type": 'ACCENT_GRAVE', "value": 'PRESS'},
- {"properties": [("use_eyedropper", False)]})
- )
- else:
- items.append(
- op_menu_pie(
- "VIEW3D_MT_transform_gizmo_pie",
- {"type": 'ACCENT_GRAVE', "value": 'PRESS'},
- )
- )
-
return keymap
@@ -2679,7 +2661,8 @@ def km_sequencer(params):
{"properties": [("side", 'LEFT')]}),
("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
{"properties": [("side", 'RIGHT')]}),
-
+ ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path", 'tool_settings.use_snap_sequencer')]}),
*_template_items_context_menu("SEQUENCER_MT_context_menu", params.context_menu_event),
])
@@ -5071,6 +5054,11 @@ def km_object_non_modal(params):
("object.origin_set", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
])
else:
+ items.extend([
+ # NOTE: this shortcut (while not temporary) is not ideal, see: T89757.
+ ("object.transfer_mode", {"type": 'Q', "value": 'PRESS', "alt": True}, None),
+ ])
+
if params.use_pie_click_drag:
items.extend([
("object.mode_set", {"type": 'TAB', "value": 'CLICK'},
diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py
index 48f07a03773..c782cd0646e 100644
--- a/release/scripts/startup/bl_operators/assets.py
+++ b/release/scripts/startup/bl_operators/assets.py
@@ -19,6 +19,7 @@
# <pep8 compliant>
from __future__ import annotations
+import bpy
from bpy.types import Operator
from bpy_extras.asset_utils import (
@@ -72,7 +73,88 @@ class ASSET_OT_tag_remove(Operator):
return {'FINISHED'}
+class ASSET_OT_open_containing_blend_file(Operator):
+ """Open the blend file that contains the active asset"""
+
+ bl_idname = "asset.open_containing_blend_file"
+ bl_label = "Open Blend File"
+ bl_options = {'REGISTER'}
+
+ _process = None # Optional[subprocess.Popen]
+
+ @classmethod
+ def poll(cls, context):
+ asset_file_handle = getattr(context, 'asset_file_handle', None)
+ asset_library = getattr(context, 'asset_library', None)
+
+ if not asset_library:
+ cls.poll_message_set("No asset library selected")
+ return False
+ if not asset_file_handle:
+ cls.poll_message_set("No asset selected")
+ return False
+ if asset_file_handle.local_id:
+ cls.poll_message_set("Selected asset is contained in the current file")
+ return False
+ return True
+
+ def execute(self, context):
+ asset_file_handle = context.asset_file_handle
+ asset_library = context.asset_library
+
+ if asset_file_handle.local_id:
+ self.report({'WARNING'}, "This asset is stored in the current blend file")
+ return {'CANCELLED'}
+
+ asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library)
+ self.open_in_new_blender(asset_lib_path)
+
+ wm = context.window_manager
+ self._timer = wm.event_timer_add(0.1, window=context.window)
+ wm.modal_handler_add(self)
+
+ return {'RUNNING_MODAL'}
+
+ def modal(self, context, event):
+ if event.type != 'TIMER':
+ return {'PASS_THROUGH'}
+
+ if self._process is None:
+ self.report({'ERROR'}, "Unable to find any running process")
+ self.cancel(context)
+ return {'CANCELLED'}
+
+ returncode = self._process.poll()
+ if returncode is None:
+ # Process is still running.
+ return {'RUNNING_MODAL'}
+
+ if returncode:
+ self.report({'WARNING'}, "Blender subprocess exited with error code %d" % returncode)
+
+ # TODO(Sybren): Replace this with a generic "reload assets" operator
+ # that can run outside of the Asset Browser context.
+ if bpy.ops.file.refresh.poll():
+ bpy.ops.file.refresh()
+ if bpy.ops.asset.list_refresh.poll():
+ bpy.ops.asset.list_refresh()
+
+ self.cancel(context)
+ return {'FINISHED'}
+
+ def cancel(self, context):
+ wm = context.window_manager
+ wm.event_timer_remove(self._timer)
+
+ def open_in_new_blender(self, filepath):
+ import subprocess
+
+ cli_args = [bpy.app.binary_path, str(filepath)]
+ self._process = subprocess.Popen(cli_args)
+
+
classes = (
ASSET_OT_tag_add,
ASSET_OT_tag_remove,
+ ASSET_OT_open_containing_blend_file,
)
diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py
index 71ef89a066b..ec2887a1a74 100644
--- a/release/scripts/startup/bl_operators/geometry_nodes.py
+++ b/release/scripts/startup/bl_operators/geometry_nodes.py
@@ -81,7 +81,10 @@ class NewGeometryNodeTreeAssign(Operator):
return geometry_modifier_poll(context)
def execute(self, context):
- modifier = context.object.modifiers.active
+ if context.area.type == 'PROPERTIES':
+ modifier = context.modifier
+ else:
+ modifier = context.object.modifiers.active
if not modifier:
return {'CANCELLED'}
diff --git a/release/scripts/startup/bl_operators/rigidbody.py b/release/scripts/startup/bl_operators/rigidbody.py
index bc80500c888..7f5edac4dfb 100644
--- a/release/scripts/startup/bl_operators/rigidbody.py
+++ b/release/scripts/startup/bl_operators/rigidbody.py
@@ -60,17 +60,22 @@ class CopyRigidbodySettings(Operator):
def execute(self, context):
obj_act = context.object
- view_layer = context.view_layer
- # deselect all but mesh objects
+ # Deselect all non mesh objects and objects that
+ # already have a rigid body attached.
+ rb_objects = []
for o in context.selected_objects:
- if o.type != 'MESH':
+ if o.type != 'MESH' or o.rigid_body is not None:
o.select_set(False)
- elif o.rigid_body is None:
- # Add rigidbody to object!
- view_layer.objects.active = o
- bpy.ops.rigidbody.object_add()
- view_layer.objects.active = obj_act
+ if o.rigid_body is not None:
+ rb_objects.append(o)
+
+ bpy.ops.rigidbody.objects_add()
+
+ # Ensure that the rigid body objects
+ # we've de-selected are selected again.
+ for o in rb_objects:
+ o.select_set(True)
objects = context.selected_objects
if objects:
diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py
index 48a02a4c5c6..8f678896e61 100644
--- a/release/scripts/startup/bl_operators/sequencer.py
+++ b/release/scripts/startup/bl_operators/sequencer.py
@@ -108,14 +108,13 @@ class SequencerSplitMulticam(Operator):
if s.multicam_source == camera or camera >= s.channel:
return {'FINISHED'}
- if not s.select:
- s.select = True
-
cfra = context.scene.frame_current
- bpy.ops.sequencer.split(frame=cfra, type='SOFT', side='RIGHT')
- for s in context.scene.sequence_editor.sequences_all:
- if s.select and s.type == 'MULTICAM' and s.frame_final_start <= cfra and cfra < s.frame_final_end:
- context.scene.sequence_editor.active_strip = s
+ right_strip = s.split(frame=cfra, split_method='SOFT')
+
+ if right_strip:
+ s.select = False
+ right_strip.select = True
+ context.scene.sequence_editor.active_strip = right_strip
context.scene.sequence_editor.active_strip.multicam_source = camera
return {'FINISHED'}
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index ef705f8fe37..25484e905c3 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -117,13 +117,15 @@ def register():
for cls in mod.classes:
register_class(cls)
- # space_userprefs.py
from bpy.props import (
EnumProperty,
StringProperty,
)
- from bpy.types import WindowManager
+ from bpy.types import (
+ WindowManager,
+ )
+ # space_userprefs.py
def addon_filter_items(_self, _context):
import addon_utils
@@ -234,3 +236,21 @@ class UI_UL_list(bpy.types.UIList):
bpy.utils.register_class(UI_UL_list)
+
+
+class UI_MT_list_item_context_menu(bpy.types.Menu):
+ """
+ UI List item context menu definition. Scripts can append/prepend this to
+ add own operators to the context menu. They must check context though, so
+ their items only draw in a valid context and for the correct UI list.
+ """
+
+ bl_label = "List Item"
+ bl_idname = "UI_MT_list_item_context_menu"
+
+ def draw(self, context):
+ # Dummy function. This type is just for scripts to append their own
+ # context menu items.
+ pass
+
+bpy.utils.register_class(UI_MT_list_item_context_menu)
diff --git a/release/scripts/startup/bl_ui/properties_collection.py b/release/scripts/startup/bl_ui/properties_collection.py
index b51d7157c06..3f7c0735eec 100644
--- a/release/scripts/startup/bl_ui/properties_collection.py
+++ b/release/scripts/startup/bl_ui/properties_collection.py
@@ -86,12 +86,15 @@ class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
row = layout.row()
row.prop(collection, "lineart_usage")
- layout.prop(collection, "lineart_use_intersection_mask")
+ layout.prop(collection, "lineart_use_intersection_mask", text="Collection Mask")
- row = layout.row(align=True, heading="Masks")
- row.active = collection.lineart_use_intersection_mask
+ col = layout.column(align=True)
+ col.active = collection.lineart_use_intersection_mask
+ row = col.row(align=True, heading="Masks")
for i in range(8):
- row.prop(collection, "lineart_intersection_mask", index=i, text=str(i), toggle=True)
+ row.prop(collection, "lineart_intersection_mask", index=i, text=" ", toggle=True)
+ if i == 3:
+ row = col.row(align=True)
classes = (
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 7f4328bb25a..d9ad094ac4f 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -646,7 +646,7 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
if len(colliding_names) == 0:
return
- layout.label(text="Name Collisions: {}".format(", ".join(colliding_names)), icon='INFO')
+ layout.label(text="Name collisions: {}".format(", ".join(colliding_names)), icon='ERROR')
classes = (
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index b217e33de12..1c7f3639f0a 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -291,18 +291,18 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
mat = context.material
lineart = mat.lineart
- layout.prop(lineart, "use_material_mask")
+ layout.prop(lineart, "use_material_mask", text="Material Mask")
- row = layout.row(align=True, heading="Masks")
- row.active = lineart.use_material_mask
+ col = layout.column(align=True)
+ col.active = lineart.use_material_mask
+ row = col.row(align=True, heading="Masks")
for i in range(8):
- row.prop(lineart, "use_material_mask_bits", text=str(i), index=i, toggle=True)
+ row.prop(lineart, "use_material_mask_bits", text=" ", index=i, toggle=True)
+ if i == 3:
+ row = col.row(align=True)
row = layout.row(align=True, heading="Custom Occlusion")
- row.prop(lineart, "use_mat_occlusion", text="")
- sub = row.row(align=False)
- sub.active = lineart.use_mat_occlusion
- sub.prop(lineart, "mat_occlusion", slider=True, text="Levels")
+ row.prop(lineart, "mat_occlusion", text="Levels")
classes = (
diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py
index d168d9ab6dd..6408d4096fe 100644
--- a/release/scripts/startup/bl_ui/properties_physics_fluid.py
+++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py
@@ -734,11 +734,9 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel):
col = flow.column()
col.prop(domain, "noise_scale", text="Upres Factor")
- # TODO (sebbas): Mantaflow only supports wavelet noise. Maybe get rid of noise type field.
- col.prop(domain, "noise_type", text="Noise Method")
+ col.prop(domain, "noise_strength", text="Strength")
col = flow.column()
- col.prop(domain, "noise_strength", text="Strength")
col.prop(domain, "noise_pos_scale", text="Scale")
col.prop(domain, "noise_time_anim", text="Time")
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index 1ad88744b16..8ca93d2406c 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -552,6 +552,10 @@ class FILEBROWSER_MT_context_menu(Menu):
sub.operator_context = 'EXEC_DEFAULT'
sub.operator("file.delete", text="Delete")
+ active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context)
+ if active_asset:
+ layout.operator("asset.open_containing_blend_file")
+
layout.separator()
sub = layout.row()
@@ -592,15 +596,28 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
def draw(self, context):
layout = self.layout
- active_file = context.active_file
- active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context)
+ asset_file_handle = context.asset_file_handle
- if not active_file or not active_asset:
+ if asset_file_handle is None:
layout.label(text="No asset selected", icon='INFO')
return
- # If the active file is an ID, use its name directly so renaming is possible from right here.
- layout.prop(context.id if context.id is not None else active_file, "name", text="")
+ asset_library = context.asset_library
+ asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library)
+
+ if asset_file_handle.local_id:
+ # If the active file is an ID, use its name directly so renaming is possible from right here.
+ layout.prop(asset_file_handle.local_id, "name", text="")
+ row = layout.row()
+ row.label(text="Source: Current File")
+ else:
+ layout.prop(asset_file_handle, "name", text="")
+ col = layout.column(align=True) # Just to reduce margin.
+ col.label(text="Source:")
+ row = col.row()
+ row.label(text=asset_lib_path)
+
+ row.operator("asset.open_containing_blend_file", text="", icon='TOOL_SETTINGS')
class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index ba91b6e8d50..0a4f419362d 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -105,6 +105,10 @@ class OUTLINER_MT_context_menu(Menu):
@staticmethod
def draw_common_operators(layout):
+ layout.menu_contents("OUTLINER_MT_asset")
+
+ layout.separator()
+
layout.menu("OUTLINER_MT_context_menu_view")
layout.separator()
@@ -306,6 +310,22 @@ class OUTLINER_MT_object(Menu):
OUTLINER_MT_context_menu.draw_common_operators(layout)
+class OUTLINER_MT_asset(Menu):
+ bl_label = "Assets"
+
+ @classmethod
+ def poll(cls, context):
+ return context.preferences.experimental.use_asset_browser
+
+ def draw(self, context):
+ layout = self.layout
+
+ space = context.space_data
+
+ layout.operator("asset.mark")
+ layout.operator("asset.clear")
+
+
class OUTLINER_PT_filter(Panel):
bl_space_type = 'OUTLINER'
bl_region_type = 'HEADER'
@@ -451,6 +471,7 @@ classes = (
OUTLINER_MT_collection_visibility,
OUTLINER_MT_collection_view_layer,
OUTLINER_MT_object,
+ OUTLINER_MT_asset,
OUTLINER_MT_context_menu,
OUTLINER_MT_context_menu_view,
OUTLINER_PT_filter,
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 55714e0b0a5..30467521c3d 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -263,6 +263,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel):
layout.prop(st, "show_strip_offset", text="Offsets")
layout.prop(st, "show_fcurves", text="F-Curves")
+ layout.prop(st, "show_grid", text="Grid")
layout.separator()
@@ -1156,14 +1157,19 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
flow.prop(strip, "use_only_boost")
elif strip_type == 'SPEED':
- layout.prop(strip, "use_default_fade", text="Stretch to Input Strip Length")
- if not strip.use_default_fade:
- layout.prop(strip, "use_as_speed")
- if strip.use_as_speed:
- layout.prop(strip, "speed_factor")
- else:
- layout.prop(strip, "speed_factor", text="Frame Number")
- layout.prop(strip, "use_scale_to_length")
+ col = layout.column(align=True)
+ col.prop(strip, "speed_control", text="Speed Control")
+ if strip.speed_control == "MULTIPLY":
+ col.prop(strip, "speed_factor", text=" ")
+ elif strip.speed_control == "LENGTH":
+ col.prop(strip, "speed_length", text=" ")
+ elif strip.speed_control == "FRAME_NUMBER":
+ col.prop(strip, "speed_frame_number", text=" ")
+
+ row = layout.row(align=True)
+ row.enabled = strip.speed_control != "STRETCH"
+ row = layout.row(align=True, heading="Interpolation")
+ row.prop(strip, "use_frame_interpolate", text="")
elif strip_type == 'TRANSFORM':
col = layout.column()
@@ -1233,11 +1239,7 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
layout.prop(strip, "wrap_width", text="Wrap Width")
col = layout.column(align=True)
- if strip_type == 'SPEED':
- col.prop(strip, "multiply_speed")
- col.prop(strip, "use_frame_interpolate")
-
- elif strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}:
+ if strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}:
col.prop(strip, "use_default_fade", text="Default Fade")
if not strip.use_default_fade:
col.prop(strip, "effect_fader", text="Effect Fader")
@@ -2293,8 +2295,8 @@ class SEQUENCER_PT_snapping(Panel):
col.prop(sequencer_tool_settings, "snap_ignore_muted", text="Muted Strips")
col.prop(sequencer_tool_settings, "snap_ignore_sound", text="Sound Strips")
- col = layout.column()
- col.prop(sequencer_tool_settings, "use_snap_current_frame_to_strips")
+ col = layout.column(heading="Current Frame", align=True)
+ col.prop(sequencer_tool_settings, "use_snap_current_frame_to_strips", text="Snap to Strips")
classes = (
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index c55f637f8b2..46a6944d2ea 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -191,6 +191,11 @@ class _defs_annotate:
row.prop(tool_settings.gpencil_sculpt, "lockaxis")
elif tool_settings.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}:
row.prop(tool_settings, "use_gpencil_stroke_endpoints")
+ elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}:
+ layout.separator()
+
+ row = layout.row(align=True)
+ row.prop(tool_settings, "annotation_stroke_placement_view2d", text="Placement")
if tool.idname == "builtin.annotate_line":
layout.separator()
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 7e6fde1ebaf..91bd5f04b9d 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -619,7 +619,7 @@ class USERPREF_PT_system_os_settings(SystemPanel, CenterAlignMixIn, Panel):
split = layout.split(factor=0.4)
split.alignment = 'RIGHT'
split.label(text="")
- split.operator("file.associate_blend", text="Make Default")
+ split.operator("preferences.associate_blend", text="Make Default")
class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel):
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 1d616947db6..09820291222 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -507,8 +507,10 @@ geometry_node_categories = [
NodeItem("GeometryNodeMeshToCurve"),
NodeItem("GeometryNodeCurveToPoints"),
NodeItem("GeometryNodeCurveEndpoints"),
+ NodeItem("GeometryNodeCurveTrim"),
NodeItem("GeometryNodeCurveLength"),
NodeItem("GeometryNodeCurveReverse"),
+ NodeItem("GeometryNodeCurveSetHandles"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
NodeItem("GeometryNodeCurvePrimitiveLine"),
diff --git a/release/scripts/templates_py/custom_nodes.py b/release/scripts/templates_py/custom_nodes.py
index ca9534e7cd3..fba94e23d0d 100644
--- a/release/scripts/templates_py/custom_nodes.py
+++ b/release/scripts/templates_py/custom_nodes.py
@@ -147,7 +147,7 @@ node_categories = [
MyNodeCategory('OTHERNODES', "Other Nodes", items=[
# the node item can have additional settings,
# which are applied to new nodes
- # NB: settings values are stored as string expressions,
+ # NOTE: settings values are stored as string expressions,
# for this reason they should be converted to strings using repr()
NodeItem("CustomNodeType", label="Node A", settings={
"my_string_prop": repr("Lorem ipsum dolor sit amet"),
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 1f39257a4c2..7e92f79a523 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -53,6 +53,8 @@ int BLF_load_mem_unique(const char *name, const unsigned char *mem, int mem_size
void BLF_unload(const char *name) ATTR_NONNULL();
void BLF_unload_id(int fontid);
+char *BLF_display_name_from_file(const char *filename);
+
/* Check if font supports a particular glyph. */
bool BLF_has_glyph(int fontid, unsigned int unicode);
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index 7428798581d..9168e7aa19c 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -915,6 +915,17 @@ void BLF_draw_buffer(int fontid, const char *str, size_t len)
BLF_draw_buffer_ex(fontid, str, len, NULL);
}
+char *BLF_display_name_from_file(const char *filename)
+{
+ FontBLF *font = blf_font_new("font_name", filename);
+ if (!font) {
+ return NULL;
+ }
+ char *name = blf_display_name(font);
+ blf_font_free(font);
+ return name;
+}
+
#ifdef DEBUG
void BLF_state_print(int fontid)
{
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 2c7ffbe8e42..53c4135254a 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -66,7 +66,7 @@
/* Batching buffer for drawing. */
BatchBLF g_batch;
-/* freetype2 handle ONLY for this file!. */
+/* freetype2 handle ONLY for this file! */
static FT_Library ft_lib;
static SpinLock ft_lib_mutex;
static SpinLock blf_glyph_cache_mutex;
@@ -1465,3 +1465,11 @@ float blf_font_ascender(FontBLF *font)
blf_glyph_cache_release(font);
return ascender;
}
+
+char *blf_display_name(FontBLF *font)
+{
+ if (!font->face->family_name) {
+ return NULL;
+ }
+ return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name);
+}
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index 63e1eb999cd..35a6d019eac 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -99,6 +99,8 @@ int blf_font_width_max(struct FontBLF *font);
float blf_font_descender(struct FontBLF *font);
float blf_font_ascender(struct FontBLF *font);
+char *blf_display_name(struct FontBLF *font);
+
void blf_font_boundbox_foreach_glyph(struct FontBLF *font,
const char *str,
size_t len,
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h
index 904b7bb8718..e3954e134da 100644
--- a/source/blender/blenkernel/BKE_DerivedMesh.h
+++ b/source/blender/blenkernel/BKE_DerivedMesh.h
@@ -158,7 +158,7 @@ struct DerivedMesh {
int (*getNumPolys)(DerivedMesh *dm);
/** Copy a single vert/edge/tessellated face from the derived mesh into
- * ``*r_{vert/edge/face}``. note that the current implementation
+ * `*r_{vert/edge/face}`. note that the current implementation
* of this function can be quite slow, iterating over all
* elements (editmesh)
*/
diff --git a/source/blender/blenkernel/BKE_action.hh b/source/blender/blenkernel/BKE_action.hh
new file mode 100644
index 00000000000..b9f106d367e
--- /dev/null
+++ b/source/blender/blenkernel/BKE_action.hh
@@ -0,0 +1,35 @@
+/*
+ * 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
+ */
+#ifndef __cplusplus
+# error This is a C++ only header.
+#endif
+
+#include "BLI_function_ref.hh"
+
+struct bAction;
+struct FCurve;
+
+namespace blender::bke {
+
+using FoundFCurveCallback = blender::FunctionRef<void(FCurve *fcurve, const char *bone_name)>;
+void BKE_action_find_fcurves_with_bones(const bAction *action, FoundFCurveCallback callback);
+
+}; // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index 030560015a9..07da9d75e59 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -268,6 +268,12 @@ void animsys_evaluate_action(struct PointerRNA *ptr,
const struct AnimationEvalContext *anim_eval_context,
bool flush_to_original);
+/* Evaluate action, and blend the result into the current values (instead of overwriting fully). */
+void animsys_blend_in_action(struct PointerRNA *ptr,
+ struct bAction *act,
+ const AnimationEvalContext *anim_eval_context,
+ float blend_factor);
+
/* Evaluate Action Group */
void animsys_evaluate_action_group(struct PointerRNA *ptr,
struct bAction *act,
diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h
index c9d671597e8..fee52479cd0 100644
--- a/source/blender/blenkernel/BKE_appdir.h
+++ b/source/blender/blenkernel/BKE_appdir.h
@@ -19,6 +19,8 @@
* \ingroup bli
*/
+#include <stddef.h>
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index 86aa18e5739..e13475fd78c 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -28,7 +28,6 @@ extern "C" {
#endif
struct AnimationEvalContext;
-struct bAction;
struct BMEditMesh;
struct Bone;
struct Depsgraph;
@@ -39,6 +38,7 @@ struct Mesh;
struct Object;
struct PoseTree;
struct Scene;
+struct bAction;
struct bArmature;
struct bConstraint;
struct bGPDstroke;
@@ -207,9 +207,18 @@ void BKE_pose_where_is_bone_tail(struct bPoseChannel *pchan);
/* Evaluate the action and apply it to the pose. If any pose bones are selected, only FCurves that
* relate to those bones are evaluated. */
-void BKE_pose_apply_action(struct Object *ob,
- struct bAction *action,
- struct AnimationEvalContext *anim_eval_context);
+void BKE_pose_apply_action_selected_bones(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context);
+/* Evaluate the action and apply it to the pose. Ignore selection state of the bones. */
+void BKE_pose_apply_action_all_bones(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context);
+
+void BKE_pose_apply_action_blend(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context,
+ float blend_factor);
void vec_roll_to_mat3(const float vec[3], const float roll, float r_mat[3][3]);
void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_mat[3][3]);
diff --git a/source/blender/blenkernel/BKE_armature.hh b/source/blender/blenkernel/BKE_armature.hh
new file mode 100644
index 00000000000..e3f5b528156
--- /dev/null
+++ b/source/blender/blenkernel/BKE_armature.hh
@@ -0,0 +1,48 @@
+/*
+ * 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
+ */
+#ifndef __cplusplus
+# error This is a C++ only header.
+#endif
+
+#include "BKE_armature.h"
+
+#include "BLI_function_ref.hh"
+#include "BLI_set.hh"
+
+namespace blender::bke {
+
+struct SelectedBonesResult {
+ bool all_bones_selected = true;
+ bool no_bones_selected = true;
+};
+
+using SelectedBoneCallback = blender::FunctionRef<void(Bone *bone)>;
+SelectedBonesResult BKE_armature_find_selected_bones(const bArmature *armature,
+ SelectedBoneCallback callback);
+
+using BoneNameSet = blender::Set<std::string>;
+/**
+ * Return a set of names of the selected bones. An empty set means "ignore bone
+ * selection", which either means all bones are selected, or none are.
+ */
+BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature);
+
+}; // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h
index d1f543b1f38..50eb2859279 100644
--- a/source/blender/blenkernel/BKE_asset.h
+++ b/source/blender/blenkernel/BKE_asset.h
@@ -26,6 +26,7 @@
extern "C" {
#endif
+struct AssetLibraryReference;
struct BlendDataReader;
struct BlendWriter;
struct ID;
@@ -45,6 +46,8 @@ struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData *
const char *name);
void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag);
+void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref);
+
struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data,
const struct ID *owner_id);
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index dffd70c077f..e76e3ed8fe0 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,13 +39,13 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 11
+#define BLENDER_FILE_SUBVERSION 15
/* 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
* was written with too new a version. */
-#define BLENDER_FILE_MIN_VERSION 290
-#define BLENDER_FILE_MIN_SUBVERSION 0
+#define BLENDER_FILE_MIN_VERSION 300
+#define BLENDER_FILE_MIN_SUBVERSION 11
/** User readable version string. */
const char *BKE_blender_version_string(void);
diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h
index a0e3d5dc142..dbf285feb92 100644
--- a/source/blender/blenkernel/BKE_cloth.h
+++ b/source/blender/blenkernel/BKE_cloth.h
@@ -39,14 +39,14 @@ struct Scene;
#define DO_INLINE MALWAYS_INLINE
-/* goal defines */
+/* Goal defines. */
#define SOFTGOALSNAP 0.999f
/* This is approximately the smallest number that can be
* represented by a float, given its precision. */
#define ALMOST_ZERO FLT_EPSILON
-/* Bits to or into the ClothVertex.flags. */
+/* Bits to or into the #ClothVertex.flags. */
typedef enum eClothVertexFlag {
CLOTH_VERT_FLAG_PINNED = (1 << 0),
CLOTH_VERT_FLAG_NOSELFCOLL = (1 << 1), /* vertex NOT used for self collisions */
@@ -150,7 +150,7 @@ typedef struct ClothSpring {
float target[3];
} ClothSpring;
-// some macro enhancements for vector treatment
+/* Some macro enhancements for vector treatment. */
#define VECSUBADDSS(v1, v2, aS, v3, bS) \
{ \
*(v1) -= *(v2)*aS + *(v3)*bS; \
@@ -211,9 +211,8 @@ typedef enum {
CLOTH_SPRING_FLAG_NEEDED = (1 << 2), /* Springs has values to be applied. */
} CLOTH_SPRINGS_FLAGS;
-/////////////////////////////////////////////////
-// collision.c
-////////////////////////////////////////////////
+/* -------------------------------------------------------------------- */
+/* collision.c */
struct CollPair;
@@ -225,20 +224,17 @@ typedef struct ColliderContacts {
int totcollisions;
} ColliderContacts;
-// needed for implicit.c
+/* needed for implicit.c */
int cloth_bvh_collision(struct Depsgraph *depsgraph,
struct Object *ob,
struct ClothModifierData *clmd,
float step,
float dt);
-////////////////////////////////////////////////
+/* -------------------------------------------------------------------- */
+/* cloth.c */
-/////////////////////////////////////////////////
-// cloth.c
-////////////////////////////////////////////////
-
-// needed for modifier.c
+/* Needed for modifier.c */
void cloth_free_modifier_extern(struct ClothModifierData *clmd);
void cloth_free_modifier(struct ClothModifierData *clmd);
void clothModifier_do(struct ClothModifierData *clmd,
@@ -250,18 +246,16 @@ void clothModifier_do(struct ClothModifierData *clmd,
int cloth_uses_vgroup(struct ClothModifierData *clmd);
-// needed for collision.c
+/* Needed for collision.c */
void bvhtree_update_from_cloth(struct ClothModifierData *clmd, bool moving, bool self);
-// needed for button_object.c
+/* Needed for button_object.c */
void cloth_clear_cache(struct Object *ob, struct ClothModifierData *clmd, float framenr);
void cloth_parallel_transport_hair_frame(float mat[3][3],
const float dir_old[3],
const float dir_new[3]);
-////////////////////////////////////////////////
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 50aa6027840..8917580689d 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -23,6 +23,9 @@
* \ingroup bke
*/
+/* XXX temporary, until AssetHandle is designed properly and queries can return a pointer to it. */
+#include "DNA_asset_types.h"
+
#include "DNA_listBase.h"
#include "DNA_object_enums.h"
#include "RNA_types.h"
@@ -357,6 +360,9 @@ int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list);
+const struct AssetLibraryReference *CTX_wm_asset_library(const bContext *C);
+struct AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid);
+
bool CTX_wm_interface_locked(const bContext *C);
/* Gets pointer to the dependency graph.
diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h
index c7c5f59cab2..5e474c0c5ac 100644
--- a/source/blender/blenkernel/BKE_curve.h
+++ b/source/blender/blenkernel/BKE_curve.h
@@ -97,7 +97,6 @@ struct BoundBox *BKE_curve_boundbox_get(struct Object *ob);
void BKE_curve_texspace_calc(struct Curve *cu);
void BKE_curve_texspace_ensure(struct Curve *cu);
-void BKE_curve_texspace_get(struct Curve *cu, float r_loc[3], float r_size[3]);
bool BKE_curve_minmax(struct Curve *cu, bool use_radius, float min[3], float max[3]);
bool BKE_curve_center_median(struct Curve *cu, float cent[3]);
diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index c4db8ee925e..7a44553c565 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -501,7 +501,7 @@ enum {
CD_FAKE = 1 << 8,
/* Vertices. */
- CD_FAKE_MDEFORMVERT = CD_FAKE | CD_MDEFORMVERT, /* *sigh* due to how vgroups are stored :( . */
+ CD_FAKE_MDEFORMVERT = CD_FAKE | CD_MDEFORMVERT, /* *sigh* due to how vgroups are stored :(. */
CD_FAKE_SHAPEKEY = CD_FAKE |
CD_SHAPEKEY, /* Not available as real CD layer in non-bmesh context. */
diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h
index 0ab126a70ae..f4221d57428 100644
--- a/source/blender/blenkernel/BKE_deform.h
+++ b/source/blender/blenkernel/BKE_deform.h
@@ -30,6 +30,7 @@ extern "C" {
struct BlendDataReader;
struct BlendWriter;
+struct ID;
struct ListBase;
struct MDeformVert;
struct MEdge;
@@ -37,7 +38,6 @@ struct MLoop;
struct MPoly;
struct Object;
struct bDeformGroup;
-struct ID;
bool BKE_object_supports_vertex_groups(const struct Object *ob);
const struct ListBase *BKE_object_defgroup_list(const struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h
index ce7df62389f..ffd8ac42c63 100644
--- a/source/blender/blenkernel/BKE_editmesh.h
+++ b/source/blender/blenkernel/BKE_editmesh.h
@@ -32,8 +32,8 @@ extern "C" {
#endif
struct BMLoop;
-struct BMesh;
struct BMPartialUpdate;
+struct BMesh;
struct BMeshCalcTessellation_Params;
struct BoundBox;
struct Depsgraph;
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 77e827bf6f2..42e9ce82278 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -35,12 +35,12 @@
#include "BKE_geometry_set.h"
struct Collection;
+struct Curve;
+struct CurveEval;
struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
-struct Curve;
-struct CurveEval;
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 1f9c3e766aa..92e70b41e7b 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -49,22 +49,22 @@ struct bGPDlayer_Mask;
struct bGPDstroke;
struct bGPdata;
-#define GPENCIL_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE))
+#define GPENCIL_SIMPLIFY(scene) (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)
#define GPENCIL_SIMPLIFY_ONPLAY(playing) \
(((playing == true) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY)) || \
((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY) == 0))
#define GPENCIL_SIMPLIFY_FILL(scene, playing) \
- ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \
+ ((GPENCIL_SIMPLIFY_ONPLAY(playing) && GPENCIL_SIMPLIFY(scene) && \
(scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FILL)))
#define GPENCIL_SIMPLIFY_MODIF(scene) \
((GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_MODIFIER)))
#define GPENCIL_SIMPLIFY_FX(scene, playing) \
- ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \
+ ((GPENCIL_SIMPLIFY_ONPLAY(playing) && GPENCIL_SIMPLIFY(scene) && \
(scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FX)))
#define GPENCIL_SIMPLIFY_TINT(scene) \
- ((GPENCIL_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_TINT))
+ (GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_TINT))
#define GPENCIL_SIMPLIFY_AA(scene) \
- ((GPENCIL_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_AA))
+ (GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_AA))
/* Vertex Color macros. */
#define GPENCIL_USE_VERTEX_COLOR(toolsettings) \
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index 404f344919c..c8af1a91725 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -98,6 +98,9 @@ struct LayerCollection *BKE_layer_collection_from_index(struct ViewLayer *view_l
const int index);
int BKE_layer_collection_findindex(struct ViewLayer *view_layer, const struct LayerCollection *lc);
+void BKE_layer_collection_resync_forbid(void);
+void BKE_layer_collection_resync_allow(void);
+
void BKE_main_collection_sync(const struct Main *bmain);
void BKE_scene_collection_sync(const struct Scene *scene);
void BKE_layer_collection_sync(const struct Scene *scene, struct ViewLayer *view_layer);
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index 27076d908e7..c6658ff424a 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -42,8 +42,8 @@
extern "C" {
#endif
-struct Collection;
struct BlendFileReadReport;
+struct Collection;
struct ID;
struct IDOverrideLibrary;
struct IDOverrideLibraryProperty;
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index ed930fe539d..ae60a5563b5 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -48,6 +48,7 @@ struct BLI_mempool;
struct BlendThumbnail;
struct GHash;
struct GSet;
+struct IDNameLib_Map;
struct ImBuf;
struct Library;
struct MainLock;
@@ -191,6 +192,9 @@ typedef struct Main {
*/
struct MainIDRelations *relations;
+ /* IDMap of IDs. Currently used when reading (expanding) libraries. */
+ struct IDNameLib_Map *id_map;
+
struct MainLock *lock;
} Main;
diff --git a/source/blender/blenkernel/BKE_main_idmap.h b/source/blender/blenkernel/BKE_main_idmap.h
index bffb12a5136..ff69883f0fb 100644
--- a/source/blender/blenkernel/BKE_main_idmap.h
+++ b/source/blender/blenkernel/BKE_main_idmap.h
@@ -50,8 +50,13 @@ struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain,
const int idmap_types) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL(1);
void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map) ATTR_NONNULL();
+
+void BKE_main_idmap_insert_id(struct IDNameLib_Map *id_map, struct ID *id) ATTR_NONNULL();
+void BKE_main_idmap_remove_id(struct IDNameLib_Map *id_map, struct ID *id) ATTR_NONNULL();
+
struct Main *BKE_main_idmap_main_get(struct IDNameLib_Map *id_map) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
+
struct ID *BKE_main_idmap_lookup_name(struct IDNameLib_Map *id_map,
short id_type,
const char *name,
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 12560ebed7b..e3be9cd8ef8 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -126,8 +126,8 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval);
struct Mesh *BKE_mesh_copy_for_eval(struct Mesh *source, bool reference);
/* These functions construct a new Mesh,
- * contrary to BKE_mesh_from_nurbs which modifies ob itself. */
-struct Mesh *BKE_mesh_new_nomain_from_curve(struct Object *ob);
+ * contrary to BKE_mesh_to_curve_nurblist which modifies ob itself. */
+struct Mesh *BKE_mesh_new_nomain_from_curve(const struct Object *ob);
struct Mesh *BKE_mesh_new_nomain_from_curve_displist(const struct Object *ob,
const struct ListBase *dispbase);
@@ -143,32 +143,11 @@ int BKE_mesh_mface_index_validate(struct MFace *mface,
struct Mesh *BKE_mesh_from_object(struct Object *ob);
void BKE_mesh_assign_object(struct Main *bmain, struct Object *ob, struct Mesh *me);
void BKE_mesh_from_metaball(struct ListBase *lb, struct Mesh *me);
-int BKE_mesh_nurbs_to_mdata(struct Object *ob,
- struct MVert **r_allvert,
- int *r_totvert,
- struct MEdge **r_alledge,
- int *r_totedge,
- struct MLoop **r_allloop,
- struct MPoly **r_allpoly,
- int *r_totloop,
- int *r_totpoly);
-int BKE_mesh_nurbs_displist_to_mdata(const struct Object *ob,
- const struct ListBase *dispbase,
- struct MVert **r_allvert,
- int *r_totvert,
- struct MEdge **r_alledge,
- int *r_totedge,
- struct MLoop **r_allloop,
- struct MPoly **r_allpoly,
- struct MLoopUV **r_alluv,
- int *r_totloop,
- int *r_totpoly);
void BKE_mesh_from_nurbs_displist(struct Main *bmain,
struct Object *ob,
struct ListBase *dispbase,
const char *obdata_name,
bool temporary);
-void BKE_mesh_from_nurbs(struct Main *bmain, struct Object *ob);
void BKE_mesh_to_curve_nurblist(const struct Mesh *me,
struct ListBase *nurblist,
const int edge_users_test);
@@ -420,6 +399,12 @@ void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr,
const char data_type);
void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr);
void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr);
+
+void BKE_lnor_spacearr_tls_init(MLoopNorSpaceArray *lnors_spacearr,
+ MLoopNorSpaceArray *lnors_spacearr_tls);
+void BKE_lnor_spacearr_tls_join(MLoopNorSpaceArray *lnors_spacearr,
+ MLoopNorSpaceArray *lnors_spacearr_tls);
+
MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr);
void BKE_lnor_space_define(MLoopNorSpace *lnor_space,
const float lnor[3],
diff --git a/source/blender/blenkernel/BKE_mesh_iterators.h b/source/blender/blenkernel/BKE_mesh_iterators.h
index 103e7b5b78f..a65f25ee182 100644
--- a/source/blender/blenkernel/BKE_mesh_iterators.h
+++ b/source/blender/blenkernel/BKE_mesh_iterators.h
@@ -41,6 +41,7 @@ void BKE_mesh_foreach_mapped_vert(struct Mesh *mesh,
MeshForeachFlag flag);
void BKE_mesh_foreach_mapped_edge(
struct Mesh *mesh,
+ int tot_edges,
void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]),
void *userData);
void BKE_mesh_foreach_mapped_loop(struct Mesh *mesh,
diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h
index 327c13c7ce4..0518f303744 100644
--- a/source/blender/blenkernel/BKE_mesh_mapping.h
+++ b/source/blender/blenkernel/BKE_mesh_mapping.h
@@ -249,7 +249,10 @@ int *BKE_mesh_calc_smoothgroups(const struct MEdge *medge,
((CHECK_TYPE_ANY( \
_tri, unsigned int *, int *, int[3], const unsigned int *, const int *, const int[3]), \
CHECK_TYPE_ANY(_v, unsigned int, const unsigned int, int, const int)), \
- (((_tri)[0] == _v) ? 0 : ((_tri)[1] == _v) ? 1 : ((_tri)[2] == _v) ? 2 : -1))
+ (((_tri)[0] == _v) ? 0 : \
+ ((_tri)[1] == _v) ? 1 : \
+ ((_tri)[2] == _v) ? 2 : \
+ -1))
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
index 2265fa6e105..5887db59ff2 100644
--- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
+++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
@@ -33,33 +33,23 @@ extern "C" {
struct Mesh;
-/* OpenVDB Voxel Remesher */
-#ifdef WITH_OPENVDB
-struct OpenVDBLevelSet *BKE_mesh_remesh_voxel_ovdb_mesh_to_level_set_create(
- struct Mesh *mesh, struct OpenVDBTransform *transform);
-struct Mesh *BKE_mesh_remesh_voxel_ovdb_volume_to_mesh_nomain(struct OpenVDBLevelSet *level_set,
- double isovalue,
- double adaptivity,
- bool relax_disoriented_triangles);
-#endif
-
-struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct Mesh *mesh);
-struct Mesh *BKE_mesh_remesh_voxel_to_mesh_nomain(struct Mesh *mesh,
- float voxel_size,
- float adaptivity,
- float isovalue);
-struct Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(struct Mesh *mesh,
- int target_faces,
- int seed,
- bool preserve_sharp,
- bool preserve_boundary,
- bool adaptive_scale,
- void *update_cb,
- void *update_cb_data);
+struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const struct Mesh *mesh);
+struct Mesh *BKE_mesh_remesh_voxel(const struct Mesh *mesh,
+ float voxel_size,
+ float adaptivity,
+ float isovalue);
+struct Mesh *BKE_mesh_remesh_quadriflow(const struct Mesh *mesh,
+ int target_faces,
+ int seed,
+ bool preserve_sharp,
+ bool preserve_boundary,
+ bool adaptive_scale,
+ void (*update_cb)(void *, float progress, int *cancel),
+ void *update_cb_data);
/* Data reprojection functions */
void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source);
-void BKE_remesh_reproject_vertex_paint(struct Mesh *target, struct Mesh *source);
+void BKE_remesh_reproject_vertex_paint(struct Mesh *target, const struct Mesh *source);
void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, struct Mesh *source);
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
index dc747ba5b42..2fbf7372a09 100644
--- a/source/blender/blenkernel/BKE_mesh_sample.hh
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -40,8 +40,6 @@ using fn::GMutableSpan;
using fn::GSpan;
using fn::GVArray;
-Span<MLoopTri> get_mesh_looptris(const Mesh &mesh);
-
void sample_point_attribute(const Mesh &mesh,
Span<int> looptri_indices,
Span<float3> bary_coords,
diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h
index b223d3872ff..aed8c44a031 100644
--- a/source/blender/blenkernel/BKE_mesh_types.h
+++ b/source/blender/blenkernel/BKE_mesh_types.h
@@ -27,7 +27,6 @@ typedef enum eMeshBatchDirtyMode {
BKE_MESH_BATCH_DIRTY_SELECT,
BKE_MESH_BATCH_DIRTY_SELECT_PAINT,
BKE_MESH_BATCH_DIRTY_SHADING,
- BKE_MESH_BATCH_DIRTY_DEFORM,
BKE_MESH_BATCH_DIRTY_UVEDIT_ALL,
BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT,
} eMeshBatchDirtyMode;
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index b1a38ec5700..cecb3118038 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1274,6 +1274,11 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
#define CMP_CRYPTOMATTE_SRC_RENDER 0
#define CMP_CRYPTOMATTE_SRC_IMAGE 1
+/* Default SMAA configuration values. */
+#define CMP_DEFAULT_SMAA_THRESHOLD 1.0f
+#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,
@@ -1459,6 +1464,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_PRIMITIVE_LINE 1068
#define GEO_NODE_CURVE_ENDPOINTS 1069
#define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070
+#define GEO_NODE_CURVE_TRIM 1071
+#define GEO_NODE_CURVE_SET_HANDLES 1072
/** \} */
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index a16822fd7dd..4724e6dfab6 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -374,10 +374,6 @@ void BKE_object_runtime_free_data(struct Object *object);
void BKE_object_batch_cache_dirty_tag(struct Object *ob);
void BKE_object_data_batch_cache_dirty_tag(struct ID *object_data);
-void BKE_object_data_eval_batch_cache_dirty_tag(struct Depsgraph *depsgraph,
- struct ID *object_data);
-void BKE_object_data_eval_batch_cache_deform_tag(struct Depsgraph *depsgraph,
- struct ID *object_data);
/* this function returns a superset of the scenes selection based on relationships */
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index fed155626ed..0b08bbfeff5 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -332,6 +332,9 @@ typedef void (*uiListFilterItemsFunc)(struct uiList *ui_list,
struct PointerRNA *,
const char *propname);
+/* Listen to notifiers. Only for lists defined in C. */
+typedef void (*uiListListener)(struct uiList *ui_list, wmRegionListenerParams *params);
+
typedef struct uiListType {
struct uiListType *next, *prev;
@@ -341,6 +344,9 @@ typedef struct uiListType {
uiListDrawFilterFunc draw_filter;
uiListFilterItemsFunc filter_items;
+ /* For lists defined in C only. */
+ uiListListener listener;
+
/* RNA integration */
ExtensionRNA rna_ext;
} uiListType;
diff --git a/source/blender/blenkernel/BKE_shrinkwrap.h b/source/blender/blenkernel/BKE_shrinkwrap.h
index bcf702ea797..70aeb37d995 100644
--- a/source/blender/blenkernel/BKE_shrinkwrap.h
+++ b/source/blender/blenkernel/BKE_shrinkwrap.h
@@ -118,7 +118,7 @@ void BKE_shrinkwrap_mesh_nearest_surface_deform(struct bContext *C,
struct Object *ob_source,
struct Object *ob_target);
-/* Used in object_remesh.c to preserve the details and volume in the voxel remesher */
+/* Used in object_remesh.cc to preserve the details and volume in the voxel remesher */
void BKE_shrinkwrap_remesh_target_project(struct Mesh *src_me,
struct Mesh *target_me,
struct Object *ob_target);
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index f85e62768f7..53485ecbd6b 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -306,6 +306,7 @@ class BezierSpline final : public Spline {
blender::MutableSpan<HandleType> handle_types_right();
blender::Span<blender::float3> handle_positions_right() const;
blender::MutableSpan<blender::float3> handle_positions_right();
+ void ensure_auto_handles() const;
void translate(const blender::float3 &translation) override;
void transform(const blender::float4x4 &matrix) override;
@@ -353,8 +354,6 @@ class BezierSpline final : public Spline {
void correct_end_tangents() const final;
void copy_settings(Spline &dst) const final;
void copy_data(Spline &dst) const final;
-
- void ensure_auto_handles() const;
};
/**
@@ -544,6 +543,7 @@ struct CurveEval {
blender::Span<SplinePtr> splines() const;
blender::MutableSpan<SplinePtr> splines();
+ bool has_spline_with_type(const Spline::Type type) const;
void resize(const int size);
void add_spline(SplinePtr spline);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 2c25b940578..78bfe8c9afb 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SRC
intern/CCGSubSurf_util.c
intern/DerivedMesh.cc
intern/action.c
+ intern/action_bones.cc
intern/action_mirror.c
intern/addon.c
intern/anim_data.c
@@ -77,6 +78,7 @@ set(SRC
intern/anim_visualization.c
intern/appdir.c
intern/armature.c
+ intern/armature_selection.cc
intern/armature_deform.c
intern/armature_pose.cc
intern/armature_update.c
@@ -143,7 +145,7 @@ set(SRC
intern/geometry_set_instances.cc
intern/gpencil.c
intern/gpencil_curve.c
- intern/gpencil_geom.c
+ intern/gpencil_geom.cc
intern/gpencil_modifier.c
intern/hair.c
intern/icons.cc
@@ -192,7 +194,7 @@ set(SRC
intern/mesh_mirror.c
intern/mesh_normals.cc
intern/mesh_remap.c
- intern/mesh_remesh_voxel.c
+ intern/mesh_remesh_voxel.cc
intern/mesh_runtime.c
intern/mesh_sample.cc
intern/mesh_tangent.c
@@ -287,6 +289,7 @@ set(SRC
BKE_DerivedMesh.h
BKE_action.h
+ BKE_action.hh
BKE_addon.h
BKE_anim_data.h
BKE_anim_path.h
@@ -294,6 +297,7 @@ set(SRC
BKE_animsys.h
BKE_appdir.h
BKE_armature.h
+ BKE_armature.hh
BKE_asset.h
BKE_attribute.h
BKE_attribute_access.hh
diff --git a/source/blender/blenkernel/intern/action_bones.cc b/source/blender/blenkernel/intern/action_bones.cc
new file mode 100644
index 00000000000..b8d185e6a81
--- /dev/null
+++ b/source/blender/blenkernel/intern/action_bones.cc
@@ -0,0 +1,48 @@
+/*
+ * 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_action.hh"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "DNA_action_types.h"
+#include "DNA_anim_types.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::bke {
+
+void BKE_action_find_fcurves_with_bones(const bAction *action, FoundFCurveCallback callback)
+{
+ LISTBASE_FOREACH (FCurve *, fcu, &action->curves) {
+ char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones[");
+ if (!bone_name) {
+ continue;
+ }
+ callback(fcu, bone_name);
+ MEM_freeN(bone_name);
+ }
+}
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c
index 69e0091444b..48472dfc9b3 100644
--- a/source/blender/blenkernel/intern/action_mirror.c
+++ b/source/blender/blenkernel/intern/action_mirror.c
@@ -322,6 +322,25 @@ static void action_flip_pchan(Object *ob_arm,
/* Move back to bone-space space, using the flipped bone if it exists. */
mul_m4_m4m4(chan_mat, arm_mat_inv, chan_mat);
+ /* The rest pose having an X-axis that is not mapping to a left/right direction (so aligned
+ * with the Y or Z axis) creates issues when flipping the pose. Instead of a negative scale on
+ * the X-axis, it turns into a 180 degree rotation over the Y-axis.
+ * This has only been observed with bones that can't be flipped,
+ * hence the check for `pchan_flip`. */
+ const float unit_x[4] = {1.0f, 0.0f, 0.0f, 0.0f};
+ const bool is_problematic = pchan_flip == NULL &&
+ fabsf(dot_v4v4(pchan->bone->arm_mat[0], unit_x)) <= 1e-6;
+ if (is_problematic) {
+ /* Matrix needs to flip both the X and Z axes to come out right. */
+ float extra_mat[4][4] = {
+ {-1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, -1.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f},
+ };
+ mul_m4_m4m4(chan_mat, extra_mat, chan_mat);
+ }
+
BKE_pchan_apply_mat4(&pchan_temp, chan_mat, false);
/* Write the values back to the F-curves. */
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 6164d4921b0..fae75762cde 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -148,8 +148,11 @@ KeyingSet *BKE_keyingset_add(
/* allocate new KeyingSet */
ks = MEM_callocN(sizeof(KeyingSet), "KeyingSet");
- BLI_strncpy(
- ks->idname, (idname) ? idname : (name) ? name : DATA_("KeyingSet"), sizeof(ks->idname));
+ BLI_strncpy(ks->idname,
+ (idname) ? idname :
+ (name) ? name :
+ DATA_("KeyingSet"),
+ sizeof(ks->idname));
BLI_strncpy(ks->name, (name) ? name : (idname) ? idname : DATA_("Keying Set"), sizeof(ks->name));
ks->flag = flag;
@@ -621,6 +624,115 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr,
}
}
+/* This function assumes that the quaternion is fully keyed, and is stored in array index order. */
+static void animsys_quaternion_evaluate_fcurves(PathResolvedRNA quat_rna,
+ FCurve *first_fcurve,
+ const AnimationEvalContext *anim_eval_context,
+ float r_quaternion[4])
+{
+ FCurve *quat_curve_fcu = first_fcurve;
+ for (int prop_index = 0; prop_index < 4; ++prop_index, quat_curve_fcu = quat_curve_fcu->next) {
+ /* Big fat assumption that the quaternion is fully keyed, and stored in order. */
+ BLI_assert(STREQ(quat_curve_fcu->rna_path, first_fcurve->rna_path) &&
+ quat_curve_fcu->array_index == prop_index);
+
+ quat_rna.prop_index = prop_index;
+ r_quaternion[prop_index] = calculate_fcurve(&quat_rna, quat_curve_fcu, anim_eval_context);
+ }
+}
+
+/* This function assumes that the quaternion is fully keyed, and is stored in array index order. */
+static void animsys_blend_fcurves_quaternion(PathResolvedRNA *anim_rna,
+ FCurve *first_fcurve,
+ const AnimationEvalContext *anim_eval_context,
+ const float blend_factor)
+{
+ float current_quat[4];
+ RNA_property_float_get_array(&anim_rna->ptr, anim_rna->prop, current_quat);
+
+ float target_quat[4];
+ animsys_quaternion_evaluate_fcurves(*anim_rna, first_fcurve, anim_eval_context, target_quat);
+
+ float blended_quat[4];
+ interp_qt_qtqt(blended_quat, current_quat, target_quat, blend_factor);
+
+ RNA_property_float_set_array(&anim_rna->ptr, anim_rna->prop, blended_quat);
+}
+
+/* LERP between current value (blend_factor=0.0) and the value from the FCurve (blend_factor=1.0)
+ */
+static void animsys_blend_in_fcurves(PointerRNA *ptr,
+ ListBase *fcurves,
+ const AnimationEvalContext *anim_eval_context,
+ const float blend_factor)
+{
+ char *channel_to_skip = NULL;
+ int num_channels_to_skip = 0;
+ LISTBASE_FOREACH (FCurve *, fcu, fcurves) {
+
+ if (num_channels_to_skip) {
+ /* For skipping already-handled rotation channels. Rotation channels are handled per group,
+ * and not per individual channel. */
+ BLI_assert(channel_to_skip != NULL);
+ if (STREQ(channel_to_skip, fcu->rna_path)) {
+ /* This is indeed the channel we want to skip. */
+ num_channels_to_skip--;
+ continue;
+ }
+ }
+
+ if (!is_fcurve_evaluatable(fcu)) {
+ continue;
+ }
+
+ PathResolvedRNA anim_rna;
+ if (!BKE_animsys_rna_path_resolve(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) {
+ continue;
+ }
+
+ if (STREQ(RNA_property_identifier(anim_rna.prop), "rotation_quaternion")) {
+ animsys_blend_fcurves_quaternion(&anim_rna, fcu, anim_eval_context, blend_factor);
+
+ /* Skip the next three channels, because those have already been handled here. */
+ MEM_SAFE_FREE(channel_to_skip);
+ channel_to_skip = BLI_strdup(fcu->rna_path);
+ num_channels_to_skip = 3;
+ continue;
+ }
+ /* TODO(Sybren): do something similar as above for Euler and Axis/Angle representations. */
+
+ const float fcurve_value = calculate_fcurve(&anim_rna, fcu, anim_eval_context);
+
+ float current_value;
+ float value_to_write;
+ if (BKE_animsys_read_from_rna_path(&anim_rna, &current_value)) {
+ value_to_write = (1 - blend_factor) * current_value + blend_factor * fcurve_value;
+
+ switch (RNA_property_type(anim_rna.prop)) {
+ case PROP_BOOLEAN:
+ /* Without this, anything less than 1.0 is converted to 'False' by
+ * ANIMSYS_FLOAT_AS_BOOL(). This is probably not desirable for blends, where anything
+ * above a 50% blend should act more like the FCurve than like the current value. */
+ case PROP_INT:
+ case PROP_ENUM:
+ value_to_write = roundf(value_to_write);
+ break;
+ default:
+ /* All other types are just handled as float, and value_to_write is already correct. */
+ break;
+ }
+ }
+ else {
+ /* Unable to read the current value for blending, so just apply the FCurve value instead. */
+ value_to_write = fcurve_value;
+ }
+
+ BKE_animsys_write_to_rna_path(&anim_rna, value_to_write);
+ }
+
+ MEM_SAFE_FREE(channel_to_skip);
+}
+
/* ***************************************** */
/* Driver Evaluation */
@@ -769,6 +881,16 @@ void animsys_evaluate_action(PointerRNA *ptr,
animsys_evaluate_fcurves(ptr, &act->curves, anim_eval_context, flush_to_original);
}
+/* Evaluate Action and blend it into the current values of the animated properties. */
+void animsys_blend_in_action(PointerRNA *ptr,
+ bAction *act,
+ const AnimationEvalContext *anim_eval_context,
+ const float blend_factor)
+{
+ action_idcode_patch_check(ptr->owner_id, act);
+ animsys_blend_in_fcurves(ptr, &act->curves, anim_eval_context, blend_factor);
+}
+
/* ***************************************** */
/* NLA System - Evaluation */
@@ -1828,7 +1950,7 @@ static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs,
return;
}
default:
- BLI_assert("Mix mode should've been handled");
+ BLI_assert_msg(0, "Mix mode should've been handled");
}
return;
}
@@ -1841,7 +1963,7 @@ static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs,
return;
}
default:
- BLI_assert("Blend mode should've been handled");
+ BLI_assert_msg(0, "Blend mode should've been handled");
}
}
@@ -1991,7 +2113,7 @@ static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
return;
}
default:
- BLI_assert("Mix mode should've been handled");
+ BLI_assert_msg(0, "Mix mode should've been handled");
}
return;
}
@@ -2004,7 +2126,7 @@ static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
return;
}
default:
- BLI_assert("Blend mode should've been handled");
+ BLI_assert_msg(0, "Blend mode should've been handled");
}
}
diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c
index 5e9259f05bb..5f721b49361 100644
--- a/source/blender/blenkernel/intern/armature_deform.c
+++ b/source/blender/blenkernel/intern/armature_deform.c
@@ -501,7 +501,7 @@ static void armature_deform_coords_impl(const Object *ob_arm,
BLI_assert(0);
}
- if (ELEM(ob_target->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
+ if (BKE_object_supports_vertex_groups(ob_target)) {
/* get the def_nr for the overall armature vertex group if present */
armature_def_nr = BKE_object_defgroup_name_index(ob_target, defgrp_name);
@@ -529,11 +529,9 @@ static void armature_deform_coords_impl(const Object *ob_arm,
dverts_len = gps_target->totpoints;
}
}
- }
- /* get a vertex-deform-index to posechannel array */
- if (deformflag & ARM_DEF_VGROUP) {
- if (ELEM(ob_target->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
+ /* get a vertex-deform-index to posechannel array */
+ if (deformflag & ARM_DEF_VGROUP) {
/* if we have a Mesh, only use dverts if it has them */
if (em_target) {
cd_dvert_offset = CustomData_get_offset(&em_target->bm->vdata, CD_MDEFORMVERT);
diff --git a/source/blender/blenkernel/intern/armature_pose.cc b/source/blender/blenkernel/intern/armature_pose.cc
index ca11692372b..a62a32d9633 100644
--- a/source/blender/blenkernel/intern/armature_pose.cc
+++ b/source/blender/blenkernel/intern/armature_pose.cc
@@ -23,9 +23,11 @@
* \ingroup bke
*/
+#include "BKE_action.hh"
#include "BKE_animsys.h"
-#include "BKE_armature.h"
+#include "BKE_armature.hh"
+#include "BLI_function_ref.hh"
#include "BLI_set.hh"
#include "DNA_action_types.h"
@@ -35,19 +37,65 @@
#include "RNA_access.h"
+using namespace blender::bke;
+
namespace {
-using BoneNameSet = blender::Set<std::string>;
-// Forward declarations.
-BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose);
+using ActionApplier =
+ blender::FunctionRef<void(PointerRNA *, bAction *, const AnimationEvalContext *)>;
+
+/* Forward declarations. */
void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
const BoneNameSet &selected_bone_names);
void pose_apply_restore_fcurves(bAction *action);
+
+void pose_apply(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context,
+ ActionApplier applier);
+
} // namespace
-void BKE_pose_apply_action(struct Object *ob,
- struct bAction *action,
- struct AnimationEvalContext *anim_eval_context)
+void BKE_pose_apply_action_selected_bones(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context)
+{
+ auto evaluate_and_apply =
+ [](PointerRNA *ptr, bAction *act, const AnimationEvalContext *anim_eval_context) {
+ animsys_evaluate_action(ptr, act, anim_eval_context, false);
+ };
+
+ pose_apply(ob, action, anim_eval_context, evaluate_and_apply);
+}
+
+void BKE_pose_apply_action_all_bones(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context)
+{
+ PointerRNA pose_owner_ptr;
+ RNA_id_pointer_create(&ob->id, &pose_owner_ptr);
+ animsys_evaluate_action(&pose_owner_ptr, action, anim_eval_context, false);
+}
+
+void BKE_pose_apply_action_blend(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context,
+ const float blend_factor)
+{
+ auto evaluate_and_blend = [blend_factor](PointerRNA *ptr,
+ bAction *act,
+ const AnimationEvalContext *anim_eval_context) {
+ animsys_blend_in_action(ptr, act, anim_eval_context, blend_factor);
+ };
+
+ pose_apply(ob, action, anim_eval_context, evaluate_and_blend);
+}
+
+namespace {
+void pose_apply(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context,
+ ActionApplier applier)
{
bPose *pose = ob->pose;
if (pose == nullptr) {
@@ -55,7 +103,7 @@ void BKE_pose_apply_action(struct Object *ob,
}
const bArmature *armature = (bArmature *)ob->data;
- const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(armature, pose);
+ const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature);
const bool limit_to_selected_bones = !selected_bone_names.is_empty();
if (limit_to_selected_bones) {
@@ -67,38 +115,14 @@ void BKE_pose_apply_action(struct Object *ob,
/* Apply the Action. */
PointerRNA pose_owner_ptr;
RNA_id_pointer_create(&ob->id, &pose_owner_ptr);
- animsys_evaluate_action(&pose_owner_ptr, action, anim_eval_context, false);
+
+ applier(&pose_owner_ptr, action, anim_eval_context);
if (limit_to_selected_bones) {
pose_apply_restore_fcurves(action);
}
}
-namespace {
-BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose)
-{
- BoneNameSet selected_bone_names;
- bool all_bones_selected = true;
- bool no_bones_selected = true;
-
- LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
- const bool is_selected = PBONE_SELECTED(armature, pchan->bone);
- all_bones_selected &= is_selected;
- no_bones_selected &= !is_selected;
-
- if (is_selected) {
- /* Bone names are unique, so no need to check for duplicates. */
- selected_bone_names.add_new(pchan->name);
- }
- }
-
- /* If no bones are selected, act as if all are. */
- if (all_bones_selected || no_bones_selected) {
- return BoneNameSet(); /* An empty set means "ignore bone selection". */
- }
- return selected_bone_names;
-}
-
void pose_apply_restore_fcurves(bAction *action)
{
/* TODO(Sybren): Restore the FCurve flags, instead of just erasing the 'disabled' flag. */
@@ -110,24 +134,13 @@ void pose_apply_restore_fcurves(bAction *action)
void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
const BoneNameSet &selected_bone_names)
{
- LISTBASE_FOREACH (FCurve *, fcu, &action->curves) {
- if (!fcu->rna_path || !strstr(fcu->rna_path, "pose.bones[")) {
- continue;
- }
-
- /* Get bone name, and check if this bone is selected. */
- char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones[");
- if (!bone_name) {
- continue;
- }
- const bool is_selected = selected_bone_names.contains(bone_name);
- MEM_freeN(bone_name);
- if (is_selected) {
- continue;
+ auto disable_unselected_fcurve = [&](FCurve *fcu, const char *bone_name) {
+ const bool is_bone_selected = selected_bone_names.contains(bone_name);
+ if (!is_bone_selected) {
+ fcu->flag |= FCURVE_DISABLED;
}
-
- fcu->flag |= FCURVE_DISABLED;
- }
+ };
+ BKE_action_find_fcurves_with_bones(action, disable_unselected_fcurve);
}
} // namespace
diff --git a/source/blender/blenkernel/intern/armature_selection.cc b/source/blender/blenkernel/intern/armature_selection.cc
new file mode 100644
index 00000000000..c7dbc9d05b1
--- /dev/null
+++ b/source/blender/blenkernel/intern/armature_selection.cc
@@ -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.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_armature.hh"
+
+#include "BLI_listbase.h"
+
+#include "DNA_action_types.h"
+#include "DNA_armature_types.h"
+
+namespace blender::bke {
+
+namespace {
+
+void find_selected_bones__visit_bone(const bArmature *armature,
+ SelectedBoneCallback callback,
+ SelectedBonesResult &result,
+ Bone *bone)
+{
+ const bool is_selected = PBONE_SELECTED(armature, bone);
+ result.all_bones_selected &= is_selected;
+ result.no_bones_selected &= !is_selected;
+
+ if (is_selected) {
+ callback(bone);
+ }
+
+ LISTBASE_FOREACH (Bone *, child_bone, &bone->childbase) {
+ find_selected_bones__visit_bone(armature, callback, result, child_bone);
+ }
+}
+} // namespace
+
+SelectedBonesResult BKE_armature_find_selected_bones(const bArmature *armature,
+ SelectedBoneCallback callback)
+{
+ SelectedBonesResult result;
+ LISTBASE_FOREACH (Bone *, root_bone, &armature->bonebase) {
+ find_selected_bones__visit_bone(armature, callback, result, root_bone);
+ }
+
+ return result;
+}
+
+BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature)
+{
+ BoneNameSet selected_bone_names;
+
+ /* Iterate over the selected bones to fill the set of bone names. */
+ auto callback = [&](Bone *bone) { selected_bone_names.add(bone->name); };
+ SelectedBonesResult result = BKE_armature_find_selected_bones(armature, callback);
+
+ /* If no bones are selected, act as if all are. */
+ if (result.all_bones_selected || result.no_bones_selected) {
+ return BoneNameSet();
+ }
+
+ return selected_bone_names;
+}
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc
index 589337d9d01..47853deec3e 100644
--- a/source/blender/blenkernel/intern/armature_test.cc
+++ b/source/blender/blenkernel/intern/armature_test.cc
@@ -17,10 +17,13 @@
* All rights reserved.
*/
-#include "BKE_armature.h"
+#include "BKE_armature.hh"
+#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "DNA_armature_types.h"
+
#include "testing/testing.h"
namespace blender::bke::tests {
@@ -157,4 +160,80 @@ TEST(vec_roll_to_mat3_normalized, Rotationmatrix)
}
}
+class BKE_armature_find_selected_bones_test : public testing::Test {
+ protected:
+ bArmature arm;
+ Bone bone1, bone2, bone3;
+
+ void SetUp() override
+ {
+ strcpy(bone1.name, "bone1");
+ strcpy(bone2.name, "bone2");
+ strcpy(bone3.name, "bone3");
+
+ arm.bonebase = {nullptr, nullptr};
+ bone1.childbase = {nullptr, nullptr};
+ bone2.childbase = {nullptr, nullptr};
+ bone3.childbase = {nullptr, nullptr};
+
+ BLI_addtail(&arm.bonebase, &bone1); // bone1 is root bone
+ BLI_addtail(&arm.bonebase, &bone2); // bone2 is root bone
+ BLI_addtail(&bone2.childbase, &bone3); // bone3 has bone2 as parent
+
+ // Make sure the armature & its bones are visible, to make them selectable.
+ arm.layer = bone1.layer = bone2.layer = bone3.layer = 1;
+ }
+};
+
+TEST_F(BKE_armature_find_selected_bones_test, some_bones_selected)
+{
+ bone1.flag = BONE_SELECTED;
+ bone2.flag = 0;
+ bone3.flag = BONE_SELECTED;
+
+ std::vector<Bone *> seen_bones;
+ auto callback = [&](Bone *bone) { seen_bones.push_back(bone); };
+
+ SelectedBonesResult result = BKE_armature_find_selected_bones(&arm, callback);
+
+ ASSERT_EQ(seen_bones.size(), 2) << "Expected 2 selected bones, got " << seen_bones.size();
+ EXPECT_EQ(seen_bones[0], &bone1);
+ EXPECT_EQ(seen_bones[1], &bone3);
+
+ EXPECT_FALSE(result.all_bones_selected); // Bone 2 was not selected.
+ EXPECT_FALSE(result.no_bones_selected); // Bones 1 and 3 were selected.
+}
+
+TEST_F(BKE_armature_find_selected_bones_test, no_bones_selected)
+{
+ bone1.flag = bone2.flag = bone3.flag = 0;
+
+ std::vector<Bone *> seen_bones;
+ auto callback = [&](Bone *bone) { seen_bones.push_back(bone); };
+
+ SelectedBonesResult result = BKE_armature_find_selected_bones(&arm, callback);
+
+ EXPECT_TRUE(seen_bones.empty()) << "Expected no selected bones, got " << seen_bones.size();
+ EXPECT_FALSE(result.all_bones_selected);
+ EXPECT_TRUE(result.no_bones_selected);
+}
+
+TEST_F(BKE_armature_find_selected_bones_test, all_bones_selected)
+{
+ bone1.flag = bone2.flag = bone3.flag = BONE_SELECTED;
+
+ std::vector<Bone *> seen_bones;
+ auto callback = [&](Bone *bone) { seen_bones.push_back(bone); };
+
+ SelectedBonesResult result = BKE_armature_find_selected_bones(&arm, callback);
+
+ ASSERT_EQ(seen_bones.size(), 3) << "Expected 3 selected bones, got " << seen_bones.size();
+ EXPECT_EQ(seen_bones[0], &bone1);
+ EXPECT_EQ(seen_bones[1], &bone2);
+ EXPECT_EQ(seen_bones[2], &bone3);
+
+ EXPECT_TRUE(result.all_bones_selected);
+ EXPECT_FALSE(result.no_bones_selected);
+}
+
} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc
index b5a7f5e37a6..f74018b20c5 100644
--- a/source/blender/blenkernel/intern/asset.cc
+++ b/source/blender/blenkernel/intern/asset.cc
@@ -110,6 +110,11 @@ void BKE_asset_metadata_tag_remove(AssetMetaData *asset_data, AssetTag *tag)
BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags);
}
+void BKE_asset_library_reference_init_default(AssetLibraryReference *library_ref)
+{
+ memcpy(library_ref, DNA_struct_default_get(AssetLibraryReference), sizeof(*library_ref));
+}
+
/* Queries -------------------------------------------- */
PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data),
diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c
index ad2be4ffe30..e9444cf88a6 100644
--- a/source/blender/blenkernel/intern/attribute.c
+++ b/source/blender/blenkernel/intern/attribute.c
@@ -39,6 +39,7 @@
#include "BKE_attribute.h"
#include "BKE_customdata.h"
+#include "BKE_editmesh.h"
#include "BKE_hair.h"
#include "BKE_pointcloud.h"
#include "BKE_report.h"
@@ -63,14 +64,28 @@ static void get_domains(ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
}
case ID_ME: {
Mesh *mesh = (Mesh *)id;
- info[ATTR_DOMAIN_POINT].customdata = &mesh->vdata;
- info[ATTR_DOMAIN_POINT].length = mesh->totvert;
- info[ATTR_DOMAIN_EDGE].customdata = &mesh->edata;
- info[ATTR_DOMAIN_EDGE].length = mesh->totedge;
- info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata;
- info[ATTR_DOMAIN_CORNER].length = mesh->totloop;
- info[ATTR_DOMAIN_FACE].customdata = &mesh->pdata;
- info[ATTR_DOMAIN_FACE].length = mesh->totpoly;
+ BMEditMesh *em = mesh->edit_mesh;
+ if (em != NULL) {
+ BMesh *bm = em->bm;
+ info[ATTR_DOMAIN_POINT].customdata = &bm->vdata;
+ info[ATTR_DOMAIN_POINT].length = bm->totvert;
+ info[ATTR_DOMAIN_EDGE].customdata = &bm->edata;
+ info[ATTR_DOMAIN_EDGE].length = bm->totedge;
+ info[ATTR_DOMAIN_CORNER].customdata = &bm->ldata;
+ info[ATTR_DOMAIN_CORNER].length = bm->totloop;
+ info[ATTR_DOMAIN_FACE].customdata = &bm->pdata;
+ info[ATTR_DOMAIN_FACE].length = bm->totface;
+ }
+ else {
+ info[ATTR_DOMAIN_POINT].customdata = &mesh->vdata;
+ info[ATTR_DOMAIN_POINT].length = mesh->totvert;
+ info[ATTR_DOMAIN_EDGE].customdata = &mesh->edata;
+ info[ATTR_DOMAIN_EDGE].length = mesh->totedge;
+ info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata;
+ info[ATTR_DOMAIN_CORNER].length = mesh->totloop;
+ info[ATTR_DOMAIN_FACE].customdata = &mesh->pdata;
+ info[ATTR_DOMAIN_FACE].length = mesh->totpoly;
+ }
break;
}
case ID_HA: {
@@ -146,7 +161,24 @@ CustomDataLayer *BKE_id_attribute_new(
return NULL;
}
- CustomData_add_layer_named(customdata, type, CD_DEFAULT, NULL, info[domain].length, name);
+ switch (GS(id->name)) {
+ case ID_ME: {
+ Mesh *me = (Mesh *)id;
+ BMEditMesh *em = me->edit_mesh;
+ if (em != NULL) {
+ BM_data_layer_add_named(em->bm, customdata, type, name);
+ }
+ else {
+ CustomData_add_layer_named(customdata, type, CD_DEFAULT, NULL, info[domain].length, name);
+ }
+ break;
+ }
+ default: {
+ CustomData_add_layer_named(customdata, type, CD_DEFAULT, NULL, info[domain].length, name);
+ break;
+ }
+ }
+
const int index = CustomData_get_named_layer_index(customdata, type, name);
return (index == -1) ? NULL : &(customdata->layers[index]);
}
@@ -168,8 +200,26 @@ bool BKE_id_attribute_remove(ID *id, CustomDataLayer *layer, ReportList *reports
return false;
}
- const int length = BKE_id_attribute_data_length(id, layer);
- CustomData_free_layer(customdata, layer->type, length, index);
+ switch (GS(id->name)) {
+ case ID_ME: {
+ Mesh *me = (Mesh *)id;
+ BMEditMesh *em = me->edit_mesh;
+ if (em != NULL) {
+ BM_data_layer_free(em->bm, customdata, layer->type);
+ }
+ else {
+ const int length = BKE_id_attribute_data_length(id, layer);
+ CustomData_free_layer(customdata, layer->type, length, index);
+ }
+ break;
+ }
+ default: {
+ const int length = BKE_id_attribute_data_length(id, layer);
+ CustomData_free_layer(customdata, layer->type, length, index);
+ break;
+ }
+ }
+
return true;
}
@@ -316,7 +366,7 @@ CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *laye
for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) {
CustomData *customdata = info[domain].customdata;
- if (customdata && customdata->layers) {
+ if (customdata && customdata->layers && customdata->totlayer) {
if (customdata->layers == layers) {
use_next = true;
}
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 912cc66920e..61827be08e5 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -622,6 +622,7 @@ UserDef *BKE_blendfile_userdef_from_defaults(void)
"io_scene_obj",
"io_scene_x3d",
"cycles",
+ "pose_library",
};
for (int i = 0; i < ARRAY_SIZE(addons); i++) {
bAddon *addon = BKE_addon_new();
@@ -735,10 +736,12 @@ bool BKE_blendfile_userdef_write_all(ReportList *reports)
if (ok_write) {
printf("ok\n");
+ BKE_report(reports, RPT_INFO, "Preferences saved");
}
else {
printf("fail\n");
ok = false;
+ BKE_report(reports, RPT_ERROR, "Saving preferences failed");
}
}
else {
diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c
index e69173cc1d5..9caf416cd0c 100644
--- a/source/blender/blenkernel/intern/boids.c
+++ b/source/blender/blenkernel/intern/boids.c
@@ -231,7 +231,7 @@ static bool rule_avoid_collision(BoidRule *rule,
int n, neighbors = 0, nearest = 0;
bool ret = 0;
- // check deflector objects first
+ /* Check deflector objects first. */
if (acbr->options & BRULE_ACOLL_WITH_DEFLECTORS && bbd->sim->colliders) {
ParticleCollision col;
BVHTreeRayHit hit;
@@ -293,7 +293,7 @@ static bool rule_avoid_collision(BoidRule *rule,
}
}
- // check boids in own system
+ /* Check boids in own system. */
if (acbr->options & BRULE_ACOLL_WITH_BOIDS) {
neighbors = BLI_kdtree_3d_range_search_with_len_squared_cb(bbd->sim->psys->tree,
pa->prev_state.co,
@@ -823,11 +823,13 @@ static boid_rule_cb boid_rules[] = {
rule_follow_leader,
rule_average_speed,
rule_fight,
- // rule_help,
- // rule_protect,
- // rule_hide,
- // rule_follow_path,
- // rule_follow_wall,
+#if 0
+ rule_help,
+ rule_protect,
+ rule_hide,
+ rule_follow_path,
+ rule_follow_wall,
+#endif
};
static void set_boid_values(BoidValues *val, BoidSettings *boids, ParticleData *pa)
@@ -1069,11 +1071,13 @@ static BoidState *get_boid_state(BoidSettings *boids, ParticleData *pa)
return state;
}
-// static int boid_condition_is_true(BoidCondition *cond)
-//{
-// /* TODO */
-// return 0;
-//}
+
+#if 0 /* TODO */
+static int boid_condition_is_true(BoidCondition *cond)
+{
+ return 0;
+}
+#endif
/* determines the velocity the boid wants to have */
void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa)
@@ -1085,7 +1089,6 @@ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa)
BoidParticle *bpa = pa->boid;
ParticleSystem *psys = bbd->sim->psys;
int rand;
- // BoidCondition *cond;
if (bpa->data.health <= 0.0f) {
pa->alive = PARS_DYING;
@@ -1093,15 +1096,17 @@ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa)
return;
}
- // planned for near future
- // cond = state->conditions.first;
- // for (; cond; cond=cond->next) {
- // if (boid_condition_is_true(cond)) {
- // pa->boid->state_id = cond->state_id;
- // state = get_boid_state(boids, pa);
- // break; /* only first true condition is used */
- // }
- //}
+ /* Planned for near future. */
+#if 0
+ BoidCondition *cond = state->conditions.first;
+ for (; cond; cond = cond->next) {
+ if (boid_condition_is_true(cond)) {
+ pa->boid->state_id = cond->state_id;
+ state = get_boid_state(boids, pa);
+ break; /* only first true condition is used */
+ }
+ }
+#endif
zero_v3(bbd->wanted_co);
bbd->wanted_speed = 0.0f;
@@ -1507,20 +1512,22 @@ void boid_body(BoidBrainData *bbd, ParticleData *pa)
}
case eBoidMode_Climbing: {
boid_climb(boids, pa, ground_co, ground_nor);
- // float nor[3];
- // copy_v3_v3(nor, ground_nor);
-
- ///* gather apparent gravity to r_ve */
- // madd_v3_v3fl(pa->r_ve, ground_nor, -1.0);
- // normalize_v3(pa->r_ve);
-
- ///* raise boid it's size from surface */
- // mul_v3_fl(nor, pa->size * boids->height);
- // add_v3_v3v3(pa->state.co, ground_co, nor);
-
- ///* remove normal component from velocity */
- // project_v3_v3v3(v, pa->state.vel, ground_nor);
- // sub_v3_v3v3(pa->state.vel, pa->state.vel, v);
+#if 0
+ float nor[3];
+ copy_v3_v3(nor, ground_nor);
+
+ /* Gather apparent gravity to r_ve. */
+ madd_v3_v3fl(pa->r_ve, ground_nor, -1.0);
+ normalize_v3(pa->r_ve);
+
+ /* Raise boid it's size from surface. */
+ mul_v3_fl(nor, pa->size * boids->height);
+ add_v3_v3v3(pa->state.co, ground_co, nor);
+
+ /* Remove normal component from velocity. */
+ project_v3_v3v3(v, pa->state.vel, ground_nor);
+ sub_v3_v3v3(pa->state.vel, pa->state.vel, v);
+#endif
break;
}
case eBoidMode_OnLand: {
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index 8678a659c0a..0fa58a74f2b 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -262,17 +262,19 @@ static bool do_init_cloth(Object *ob, ClothModifierData *clmd, Mesh *result, int
static int do_step_cloth(
Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, Mesh *result, int framenr)
{
+ /* simulate 1 frame forward */
ClothVertex *verts = NULL;
Cloth *cloth;
ListBase *effectors = NULL;
MVert *mvert;
unsigned int i = 0;
int ret = 0;
+ bool vert_mass_changed = false;
- /* simulate 1 frame forward */
cloth = clmd->clothObject;
verts = cloth->verts;
mvert = result->mvert;
+ vert_mass_changed = verts->mass != clmd->sim_parms->mass;
/* force any pinned verts to their constrained location. */
for (i = 0; i < clmd->clothObject->mvert_num; i++, verts++) {
@@ -283,6 +285,11 @@ static int do_step_cloth(
/* Get the current position. */
copy_v3_v3(verts->xconst, mvert[i].co);
mul_m4_v3(ob->obmat, verts->xconst);
+
+ if (vert_mass_changed) {
+ verts->mass = clmd->sim_parms->mass;
+ SIM_mass_spring_set_implicit_vertex_mass(cloth->implicit, i, verts->mass);
+ }
}
effectors = BKE_effectors_create(depsgraph, ob, NULL, clmd->sim_parms->effector_weights, false);
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index c354115e030..b90214f1814 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -1809,125 +1809,6 @@ bool BKE_collection_objects_select(ViewLayer *view_layer, Collection *collection
/** \name Collection move (outliner drag & drop)
* \{ */
-/* Local temporary storage for layer collection flags. */
-typedef struct LayerCollectionFlag {
- struct LayerCollectionFlag *next, *prev;
- /** The view layer for the collections being moved, NULL for their children. */
- ViewLayer *view_layer;
- /** The original #LayerCollection's collection field. */
- Collection *collection;
- /** The original #LayerCollection's flag. */
- int flag;
- /** Corresponds to #LayerCollection->layer_collections. */
- ListBase children;
-} LayerCollectionFlag;
-
-static void layer_collection_flags_store_recursive(const LayerCollection *layer_collection,
- LayerCollectionFlag *flag)
-{
- flag->collection = layer_collection->collection;
- flag->flag = layer_collection->flag;
-
- LISTBASE_FOREACH (const LayerCollection *, child, &layer_collection->layer_collections) {
- LayerCollectionFlag *child_flag = MEM_callocN(sizeof(LayerCollectionFlag), __func__);
- BLI_addtail(&flag->children, child_flag);
- layer_collection_flags_store_recursive(child, child_flag);
- }
-}
-
-/**
- * For every view layer, find the \a collection and save flags
- * for it and its children in a temporary tree structure.
- */
-static void layer_collection_flags_store(Main *bmain,
- const Collection *collection,
- ListBase *r_layer_level_list)
-{
- BLI_listbase_clear(r_layer_level_list);
- LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(
- view_layer, collection);
- /* Skip this view layer if the collection isn't found for some reason. */
- if (layer_collection == NULL) {
- continue;
- }
-
- /* Store the flags for the collection and all of its children. */
- LayerCollectionFlag *flag = MEM_callocN(sizeof(LayerCollectionFlag), __func__);
- flag->view_layer = view_layer;
-
- /* Recursively save flags from collection children. */
- layer_collection_flags_store_recursive(layer_collection, flag);
-
- BLI_addtail(r_layer_level_list, flag);
- }
- }
-}
-
-static void layer_collection_flags_free_recursive(LayerCollectionFlag *flag)
-{
- LISTBASE_FOREACH (LayerCollectionFlag *, child, &flag->children) {
- layer_collection_flags_free_recursive(child);
- }
-
- BLI_freelistN(&flag->children);
-}
-
-static void layer_collection_flags_restore_recursive(LayerCollection *layer_collection,
- LayerCollectionFlag *flag)
-{
- /* There should be a flag struct for every layer collection. */
- BLI_assert(BLI_listbase_count(&layer_collection->layer_collections) ==
- BLI_listbase_count(&flag->children));
- /* The flag and the layer collection should actually correspond. */
- BLI_assert(flag->collection == layer_collection->collection);
-
- LayerCollectionFlag *child_flag = flag->children.first;
- LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) {
- layer_collection_flags_restore_recursive(child, child_flag);
-
- child_flag = child_flag->next;
- }
-
- /* We treat exclude as a special case.
- *
- * If in a different view layer the parent collection was disabled (e.g., background)
- * and now we moved a new collection to be part of the background this collection should
- * probably be disabled.
- *
- * NOTE: If we were to also keep the exclude flag we would need to re-sync the collections.
- */
- layer_collection->flag = flag->flag | (layer_collection->flag & LAYER_COLLECTION_EXCLUDE);
-}
-
-/**
- * Restore a collection's (and its children's) flags for each view layer
- * from the structure built in #layer_collection_flags_store.
- */
-static void layer_collection_flags_restore(ListBase *flags, const Collection *collection)
-{
- LISTBASE_FOREACH (LayerCollectionFlag *, flag, flags) {
- ViewLayer *view_layer = flag->view_layer;
- /* The top level of flag structs must have this set. */
- BLI_assert(view_layer != NULL);
-
- LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(
- view_layer, collection);
- /* Check that the collection is still in the scene (and therefore its view layers). In most
- * cases this is true, but if we move a sub-collection shared by several scenes to a collection
- * local to the target scene, it is effectively removed from every other scene's hierarchy
- * (e.g. moving into current scene's master collection). Then the other scene's view layers
- * won't contain a matching layer collection anymore, so there is nothing to restore to. */
- if (layer_collection != NULL) {
- layer_collection_flags_restore_recursive(layer_collection, flag);
- }
- layer_collection_flags_free_recursive(flag);
- }
-
- BLI_freelistN(flags);
-}
-
bool BKE_collection_move(Main *bmain,
Collection *to_parent,
Collection *from_parent,
@@ -1968,26 +1849,7 @@ bool BKE_collection_move(Main *bmain,
}
}
- /* Make sure we store the flag of the layer collections before we remove and re-create them.
- * Otherwise they will get lost and everything will be copied from the new parent collection.
- * Don't use flag syncing when moving a collection to a different scene, as it no longer exists
- * in the same view layers anyway. */
- const bool do_flag_sync = BKE_scene_find_from_collection(bmain, to_parent) ==
- BKE_scene_find_from_collection(bmain, collection);
- ListBase layer_flags;
- if (do_flag_sync) {
- layer_collection_flags_store(bmain, collection, &layer_flags);
- }
-
- /* Create and remove layer collections. */
- BKE_main_collection_sync(bmain);
-
- /* Restore the original layer collection flags and free their temporary storage. */
- if (do_flag_sync) {
- layer_collection_flags_restore(&layer_flags, collection);
- }
-
- /* We need to sync it again to pass the correct flags to the collections objects. */
+ /* Update layer collections. */
BKE_main_collection_sync(bmain);
return true;
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 1028790856c..dced945bea0 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -1448,6 +1448,36 @@ int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list)
return ctx_data_collection_get(C, "editable_gpencil_strokes", list);
}
+const AssetLibraryReference *CTX_wm_asset_library(const bContext *C)
+{
+ return ctx_data_pointer_get(C, "asset_library");
+}
+
+AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid)
+{
+ AssetHandle *asset_handle_p =
+ (AssetHandle *)CTX_data_pointer_get_type(C, "asset_handle", &RNA_AssetHandle).data;
+ if (asset_handle_p) {
+ *r_is_valid = true;
+ return *asset_handle_p;
+ }
+
+ /* If the asset handle was not found in context directly, try if there's an active file with
+ * asset data there instead. Not nice to have this here, would be better to have this in
+ * `ED_asset.h`, but we can't include that in BKE. Even better would be not needing this at all
+ * and being able to have editors return this in the usual `context` callback. But that would
+ * require returning a non-owning pointer, which we don't have in the Asset Browser (yet). */
+ FileDirEntry *file =
+ (FileDirEntry *)CTX_data_pointer_get_type(C, "active_file", &RNA_FileSelectEntry).data;
+ if (file && file->asset_data) {
+ *r_is_valid = true;
+ return (AssetHandle){.file_data = file};
+ }
+
+ *r_is_valid = false;
+ return (AssetHandle){0};
+}
+
Depsgraph *CTX_data_depsgraph_pointer(const bContext *C)
{
Main *bmain = CTX_data_main(C);
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 24615dd8c2b..f1369254347 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -332,16 +332,6 @@ IDTypeInfo IDType_ID_CU = {
.lib_override_apply_post = NULL,
};
-static int cu_isectLL(const float v1[3],
- const float v2[3],
- const float v3[3],
- const float v4[3],
- short cox,
- short coy,
- float *lambda,
- float *mu,
- float vec[3]);
-
/* frees editcurve entirely */
void BKE_curve_editfont_free(Curve *cu)
{
@@ -566,18 +556,6 @@ void BKE_curve_texspace_ensure(Curve *cu)
}
}
-void BKE_curve_texspace_get(Curve *cu, float r_loc[3], float r_size[3])
-{
- BKE_curve_texspace_ensure(cu);
-
- if (r_loc) {
- copy_v3_v3(r_loc, cu->loc);
- }
- if (r_size) {
- copy_v3_v3(r_size, cu->size);
- }
-}
-
bool BKE_nurbList_index_get_co(ListBase *nurb, const int index, float r_co[3])
{
int tot = 0;
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 8e1577ab072..5c18f6f3807 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -48,6 +48,22 @@ blender::MutableSpan<SplinePtr> CurveEval::splines()
return splines_;
}
+/**
+ * \return True if the curve contains a spline with the given type.
+ *
+ * \note If you are looping over all of the splines in the same scope anyway,
+ * it's better to avoid calling this function, in case there are many splines.
+ */
+bool CurveEval::has_spline_with_type(const Spline::Type type) const
+{
+ for (const SplinePtr &spline : this->splines()) {
+ if (spline->type() == type) {
+ return true;
+ }
+ }
+ return false;
+}
+
void CurveEval::resize(const int size)
{
splines_.resize(size);
diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc
index a4ffaa8b10b..99dc1db9d38 100644
--- a/source/blender/blenkernel/intern/displist.cc
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -903,6 +903,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
Mesh *modified = nullptr;
float(*vertCos)[3] = nullptr;
+ int totvert = 0;
for (; md; md = md->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
@@ -929,7 +930,6 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (mti->type == eModifierTypeType_OnlyDeform ||
(mti->type == eModifierTypeType_DeformOrConstruct && !modified)) {
if (modified) {
- int totvert = 0;
if (!vertCos) {
vertCos = BKE_mesh_vert_coords_alloc(modified, &totvert);
}
@@ -939,7 +939,6 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
mti->deformVerts(md, &mectx_deform, modified, vertCos, totvert);
}
else {
- int totvert = 0;
if (!vertCos) {
vertCos = displist_vert_coords_alloc(dispbase, &totvert);
}
@@ -1668,7 +1667,7 @@ void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
const int tot = (dl->type == DL_INDEX3) ? dl->nr : dl->nr * dl->parts;
for (const int i : IndexRange(tot)) {
- minmax_v3v3_v3(min, max, &dl->verts[i]);
+ minmax_v3v3_v3(min, max, &dl->verts[i * 3]);
}
if (tot != 0) {
doit = true;
diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c
index 9cae74e4e9a..83e03ef44f5 100644
--- a/source/blender/blenkernel/intern/editmesh.c
+++ b/source/blender/blenkernel/intern/editmesh.c
@@ -306,7 +306,7 @@ const float (*BKE_editmesh_vert_coords_when_deformed(struct Depsgraph *depsgraph
}
else if ((em->mesh_eval_final != NULL) &&
(em->mesh_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. . */
+ /* If this is an edit-mesh type, leave NULL as we can use the vertex coords. */
}
else {
/* Constructive modifiers have been used, we need to allocate coordinates. */
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 1b628b16802..fc1721eaf3a 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -547,7 +547,7 @@ static float eff_calc_visibility(ListBase *colliders,
return visibility;
}
-// noise function for wind e.g.
+/* Noise function for wind e.g. */
static float wind_func(struct RNG *rng, float strength)
{
int random = (BLI_rng_get_int(rng) + 1) % 128; /* max 2357 */
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 8f47a7e75d4..5decc7a1792 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -918,7 +918,7 @@ void BKE_fcurve_active_keyframe_set(FCurve *fcu, const BezTriple *active_bezt)
}
/* The active keyframe should always be selected. */
- BLI_assert(BEZT_ISSEL_ANY(active_bezt) || !"active keyframe must be selected");
+ BLI_assert_msg(BEZT_ISSEL_ANY(active_bezt), "active keyframe must be selected");
fcu->active_keyframe_index = (int)offset;
}
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 2b48683a3a8..92fd220549a 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -1545,7 +1545,7 @@ static void emit_from_particles(Object *flow_ob,
float dt)
{
if (ffs && ffs->psys && ffs->psys->part &&
- ELEM(ffs->psys->part->type, PART_EMITTER, PART_FLUID)) // is particle system selected
+ ELEM(ffs->psys->part->type, PART_EMITTER, PART_FLUID)) /* Is particle system selected. */
{
ParticleSimulationData sim;
ParticleSystem *psys = ffs->psys;
@@ -5005,7 +5005,6 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd,
tfds->noise_pos_scale = fds->noise_pos_scale;
tfds->noise_time_anim = fds->noise_time_anim;
tfds->noise_scale = fds->noise_scale;
- tfds->noise_type = fds->noise_type;
/* liquid domain options */
tfds->flip_ratio = fds->flip_ratio;
diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c
index dbd48005e9e..641c003d456 100644
--- a/source/blender/blenkernel/intern/fmodifier.c
+++ b/source/blender/blenkernel/intern/fmodifier.c
@@ -688,7 +688,7 @@ static float fcm_cycles_time(
ofs = lastkey[0];
}
}
- if ((ELEM(0, side, mode))) {
+ if (ELEM(0, side, mode)) {
return evaltime;
}
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index b5c49dbb8b2..0b6ba966974 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -854,17 +854,9 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
return {};
}
- bool curve_has_bezier_spline = false;
- for (SplinePtr &spline : curve->splines()) {
- if (spline->type() == Spline::Type::Bezier) {
- curve_has_bezier_spline = true;
- break;
- }
- }
-
/* Use the regular position virtual array when there aren't any Bezier splines
* to avoid the overhead of checking the spline type for every point. */
- if (!curve_has_bezier_spline) {
+ if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
}
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index af500ab3427..ef93a3f9b3f 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -823,12 +823,15 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
const int vertex_group_index = BLI_findstringindex(
&mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name));
if (vertex_group_index < 0) {
return {};
}
- if (mesh == nullptr || mesh->dvert == nullptr) {
+ if (mesh->dvert == nullptr) {
static const float default_value = 0.0f;
return {std::make_unique<fn::GVArray_For_SingleValueRef>(
CPPType::get<float>(), mesh->totvert, &default_value),
@@ -874,15 +877,15 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
Mesh *mesh = mesh_component.get_for_write();
+ if (mesh == nullptr) {
+ return true;
+ }
const int vertex_group_index = BLI_findstringindex(
&mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name));
if (vertex_group_index < 0) {
return false;
}
- if (mesh == nullptr) {
- return true;
- }
if (mesh->dvert == nullptr) {
return true;
}
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 47c8df03375..90a97264c8f 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -565,6 +565,7 @@ static PointCloud *join_pointcloud_position_attribute(Span<GeometryInstanceGroup
}
PointCloud *new_pointcloud = BKE_pointcloud_new_nomain(totpoint);
+ MutableSpan new_positions{(float3 *)new_pointcloud->co, new_pointcloud->totpoint};
/* Transform each instance's point locations into the new point cloud. */
int offset = 0;
@@ -576,9 +577,7 @@ static PointCloud *join_pointcloud_position_attribute(Span<GeometryInstanceGroup
}
for (const float4x4 &transform : set_group.transforms) {
for (const int i : IndexRange(pointcloud->totpoint)) {
- const float3 old_position = pointcloud->co[i];
- const float3 new_position = transform * old_position;
- copy_v3_v3(new_pointcloud->co[offset + i], new_position);
+ new_positions[offset + i] = transform * float3(pointcloud->co[i]);
}
offset += pointcloud->totpoint;
}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index e13b9d543d7..38397f8f307 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -2730,7 +2730,7 @@ void BKE_gpencil_visible_stroke_advanced_iter(ViewLayer *view_layer,
int cfra)
{
bGPdata *gpd = (bGPdata *)ob->data;
- const bool is_multiedit = ((GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) && (!GPENCIL_PLAY_ON(gpd)));
+ const bool is_multiedit = (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd) && (!GPENCIL_PLAY_ON(gpd)));
const bool is_onion = do_onion && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0);
const bool is_drawing = (gpd->runtime.sbuffer_used > 0);
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.cc
index 4dcd94fdeec..f8a07939096 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.cc
@@ -21,22 +21,24 @@
* \ingroup bke
*/
-#include <math.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cmath>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include "CLG_log.h"
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_float3.hh"
#include "BLI_ghash.h"
#include "BLI_hash.h"
#include "BLI_heap.h"
#include "BLI_math_vector.h"
#include "BLI_polyfill_2d.h"
+#include "BLI_span.hh"
#include "BLT_translation.h"
@@ -61,6 +63,9 @@
#include "DEG_depsgraph_query.h"
+using blender::float3;
+using blender::Span;
+
/* GP Object - Boundbox Support */
/**
*Get min/max coordinate bounds for single stroke.
@@ -75,20 +80,26 @@ bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps,
float r_min[3],
float r_max[3])
{
- const bGPDspoint *pt;
- int i;
- bool changed = false;
-
- if (ELEM(NULL, gps, r_min, r_max)) {
+ if (gps == nullptr) {
return false;
}
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {
- minmax_v3v3_v3(r_min, r_max, &pt->x);
+ bool changed = false;
+ if (use_select) {
+ for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) {
+ if (pt.flag & GP_SPOINT_SELECT) {
+ minmax_v3v3_v3(r_min, r_max, &pt.x);
+ changed = true;
+ }
+ }
+ }
+ else {
+ for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) {
+ minmax_v3v3_v3(r_min, r_max, &pt.x);
changed = true;
}
}
+
return changed;
}
@@ -105,14 +116,14 @@ bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
INIT_MINMAX(r_min, r_max);
- if (gpd == NULL) {
+ if (gpd == nullptr) {
return changed;
}
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
bGPDframe *gpf = gpl->actframe;
- if (gpf != NULL) {
+ if (gpf != nullptr) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
changed |= BKE_gpencil_stroke_minmax(gps, false, r_min, r_max);
}
@@ -129,11 +140,11 @@ bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
*/
void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
{
- float min[3], max[3], tot[3];
-
+ float3 min;
+ float3 max;
BKE_gpencil_data_minmax(gpd, min, max);
- add_v3_v3v3(tot, min, max);
+ const float3 tot = min + max;
mul_v3_v3fl(r_centroid, tot, 0.5f);
}
@@ -153,20 +164,18 @@ void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
*/
static void boundbox_gpencil(Object *ob)
{
- BoundBox *bb;
- bGPdata *gpd;
- float min[3], max[3];
-
- if (ob->runtime.bb == NULL) {
- ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
}
- bb = ob->runtime.bb;
- gpd = ob->data;
+ BoundBox *bb = ob->runtime.bb;
+ bGPdata *gpd = (bGPdata *)ob->data;
+ float3 min;
+ float3 max;
if (!BKE_gpencil_data_minmax(gpd, min, max)) {
- min[0] = min[1] = min[2] = -1.0f;
- max[0] = max[1] = max[2] = 1.0f;
+ min = float3(-1);
+ max = float3(1);
}
BKE_boundbox_init_from_minmax(bb, min, max);
@@ -181,8 +190,8 @@ static void boundbox_gpencil(Object *ob)
*/
BoundBox *BKE_gpencil_boundbox_get(Object *ob)
{
- if (ELEM(NULL, ob, ob->data)) {
- return NULL;
+ if (ELEM(nullptr, ob, ob->data)) {
+ return nullptr;
}
bGPdata *gpd = (bGPdata *)ob->data;
@@ -196,9 +205,9 @@ BoundBox *BKE_gpencil_boundbox_get(Object *ob)
/* Update orig object's boundbox with re-computed evaluated values. This function can be
* called with the evaluated object and need update the original object bound box data
* to keep both values synchronized. */
- if (!ELEM(ob_orig, NULL, ob)) {
- if (ob_orig->runtime.bb == NULL) {
- ob_orig->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
+ if (!ELEM(ob_orig, nullptr, ob)) {
+ if (ob_orig->runtime.bb == nullptr) {
+ ob_orig->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
}
for (int i = 0; i < 8; i++) {
copy_v3_v3(ob_orig->runtime.bb->vec[i], ob->runtime.bb->vec[i]);
@@ -227,7 +236,7 @@ static int stroke_march_next_point(const bGPDstroke *gps,
float step_start[3];
float point[3];
int next_point_index = index_next_pt;
- bGPDspoint *pt = NULL;
+ bGPDspoint *pt = nullptr;
if (!(next_point_index < gps->totpoints)) {
return -1;
@@ -295,7 +304,7 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
float step_start[3];
float point[3];
int next_point_index = index_next_pt;
- bGPDspoint *pt = NULL;
+ bGPDspoint *pt = nullptr;
if (!(next_point_index < gps->totpoints)) {
return -1;
@@ -336,7 +345,7 @@ static int stroke_march_count(const bGPDstroke *gps, const float dist)
int point_count = 0;
float point[3];
int next_point_index = 1;
- bGPDspoint *pt = NULL;
+ bGPDspoint *pt = nullptr;
pt = &gps->points[0];
copy_v3_v3(point, &pt->x);
@@ -369,14 +378,14 @@ static void stroke_defvert_create_nr_list(MDeformVert *dv_list,
for (j = 0; j < dv->totweight; j++) {
bool found = false;
dw = &dv->dw[j];
- for (ld = result->first; ld; ld = ld->next) {
+ for (ld = (LinkData *)result->first; ld; ld = ld->next) {
if (ld->data == POINTER_FROM_INT(dw->def_nr)) {
found = true;
break;
}
}
if (!found) {
- ld = MEM_callocN(sizeof(LinkData), "def_nr_item");
+ ld = (LinkData *)MEM_callocN(sizeof(LinkData), "def_nr_item");
ld->data = POINTER_FROM_INT(dw->def_nr);
BLI_addtail(result, ld);
tw++;
@@ -391,14 +400,15 @@ static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase
{
int i, j;
LinkData *ld;
- MDeformVert *dst = MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
+ MDeformVert *dst = (MDeformVert *)MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
for (i = 0; i < count; i++) {
- dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * totweight, "new_deformWeight");
+ dst[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * totweight,
+ "new_deformWeight");
dst[i].totweight = totweight;
j = 0;
/* re-assign deform groups */
- for (ld = def_nr_list->first; ld; ld = ld->next) {
+ for (ld = (LinkData *)def_nr_list->first; ld; ld = ld->next) {
dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data);
j++;
}
@@ -429,10 +439,10 @@ static void stroke_interpolate_deform_weights(
bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select)
{
bGPDspoint *pt = gps->points;
- bGPDspoint *pt1 = NULL;
- bGPDspoint *pt2 = NULL;
+ bGPDspoint *pt1 = nullptr;
+ bGPDspoint *pt2 = nullptr;
LinkData *ld;
- ListBase def_nr_list = {0};
+ ListBase def_nr_list = {nullptr};
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
return false;
@@ -440,12 +450,13 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
/* TODO: Implement feature point preservation. */
int count = stroke_march_count(gps, dist);
- bGPDspoint *new_pt = MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled");
- MDeformVert *new_dv = NULL;
+ bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count,
+ "gp_stroke_points_sampled");
+ MDeformVert *new_dv = nullptr;
int result_totweight;
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight);
new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list);
}
@@ -513,7 +524,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
/* Free original weight data. */
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
- while ((ld = BLI_pophead(&def_nr_list))) {
+ while ((ld = (LinkData *)BLI_pophead(&def_nr_list))) {
MEM_freeN(ld);
}
@@ -610,26 +621,27 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
if (new_count == 1) {
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->points);
- gps->points = NULL;
- gps->dvert = NULL;
+ gps->points = nullptr;
+ gps->dvert = nullptr;
gps->totpoints = 0;
return false;
}
- new_pt = MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
+ new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
for (int i = 0; i < new_count; i++) {
memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
}
if (gps->dvert) {
- new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_trimmed");
+ new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count,
+ "gp_stroke_dverts_trimmed");
for (int i = 0; i < new_count; i++) {
dv = &gps->dvert[i + index_from];
new_dv[i].flag = dv->flag;
new_dv[i].totweight = dv->totweight;
- new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
- "gp_stroke_dverts_dw_trimmed");
+ new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+ "gp_stroke_dverts_dw_trimmed");
for (int j = 0; j < dv->totweight; j++) {
new_dv[i].dw[j].weight = dv->dw[j].weight;
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
@@ -685,14 +697,14 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
}
if (gps->dvert) {
- new_dv = MEM_callocN(sizeof(MDeformVert) * new_count,
- "gp_stroke_dverts_remaining(MDeformVert)");
+ new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count,
+ "gp_stroke_dverts_remaining(MDeformVert)");
for (int i = 0; i < new_count; i++) {
dv = &gps->dvert[i + before_index];
new_dv[i].flag = dv->flag;
new_dv[i].totweight = dv->totweight;
- new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
- "gp_stroke_dverts_dw_remaining(MDeformWeight)");
+ new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+ "gp_stroke_dverts_dw_remaining(MDeformWeight)");
for (int j = 0; j < dv->totweight; j++) {
new_dv[i].dw[j].weight = dv->dw[j].weight;
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
@@ -1301,11 +1313,12 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
/* allocate memory for temporary areas */
gps->tot_triangles = gps->totpoints - 2;
- uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
- "GP Stroke temp triangulation");
- float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints,
- "GP Stroke temp 2d points");
- float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
+ uint(*tmp_triangles)[3] = (uint(*)[3])MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
+ "GP Stroke temp triangulation");
+ float(*points2d)[2] = (float(*)[2])MEM_mallocN(sizeof(*points2d) * gps->totpoints,
+ "GP Stroke temp 2d points");
+ float(*uv)[2] = (float(*)[2])MEM_mallocN(sizeof(*uv) * gps->totpoints,
+ "GP Stroke temp 2d uv data");
int direction = 0;
@@ -1326,8 +1339,8 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
/* Save triangulation data. */
if (gps->tot_triangles > 0) {
MEM_SAFE_FREE(gps->triangles);
- gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
- "GP Stroke triangulation");
+ gps->triangles = (bGPDtriangle *)MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
+ "GP Stroke triangulation");
for (int i = 0; i < gps->tot_triangles; i++) {
memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3]));
@@ -1344,7 +1357,7 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
MEM_freeN(gps->triangles);
}
- gps->triangles = NULL;
+ gps->triangles = nullptr;
}
/* clear memory */
@@ -1359,7 +1372,7 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
*/
void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
{
- if (gps == NULL || gps->totpoints == 0) {
+ if (gps == nullptr || gps->totpoints == 0) {
return;
}
@@ -1379,11 +1392,11 @@ void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
*/
void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps)
{
- if (gps == NULL) {
+ if (gps == nullptr) {
return;
}
- if (gps->editcurve != NULL) {
+ if (gps->editcurve != nullptr) {
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
/* curve geometry was updated: stroke needs recalculation */
if (gps->flag & GP_STROKE_NEEDS_CURVE_UPDATE) {
@@ -1519,20 +1532,20 @@ bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps)
if (intersect) {
/* save points */
- bGPDspoint *old_points = MEM_dupallocN(gps->points);
- MDeformVert *old_dvert = NULL;
- MDeformVert *dvert_src = NULL;
+ bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
+ MDeformVert *old_dvert = nullptr;
+ MDeformVert *dvert_src = nullptr;
- if (gps->dvert != NULL) {
- old_dvert = MEM_dupallocN(gps->dvert);
+ if (gps->dvert != nullptr) {
+ old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
}
/* resize gps */
int newtot = end - start + 1;
- gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
+ gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
+ if (gps->dvert != nullptr) {
+ gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
}
for (int i = 0; i < newtot; i++) {
@@ -1540,7 +1553,7 @@ bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps)
bGPDspoint *pt_src = &old_points[idx];
bGPDspoint *pt_new = &gps->points[i];
memcpy(pt_new, pt_src, sizeof(bGPDspoint));
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
dvert_src = &old_dvert[idx];
MDeformVert *dvert = &gps->dvert[i];
memcpy(dvert, dvert_src, sizeof(MDeformVert));
@@ -1570,8 +1583,8 @@ bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps)
*/
bool BKE_gpencil_stroke_close(bGPDstroke *gps)
{
- bGPDspoint *pt1 = NULL;
- bGPDspoint *pt2 = NULL;
+ bGPDspoint *pt1 = nullptr;
+ bGPDspoint *pt2 = nullptr;
/* Only can close a stroke with 3 points or more. */
if (gps->totpoints < 3) {
@@ -1605,9 +1618,9 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps)
/* Resize stroke array. */
int old_tot = gps->totpoints;
gps->totpoints += tot_newpoints;
- gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
+ gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
+ if (gps->dvert != nullptr) {
+ gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
}
/* Generate new points */
@@ -1629,7 +1642,7 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps)
interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step);
/* Set weights. */
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
MDeformVert *dvert1 = &gps->dvert[old_tot - 1];
MDeformWeight *dw1 = BKE_defvert_ensure_index(dvert1, 0);
float weight_1 = dw1 ? dw1->weight : 0.0f;
@@ -1663,7 +1676,7 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps)
void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag)
{
bGPDspoint *pt;
- MDeformVert *dvert = NULL;
+ MDeformVert *dvert = nullptr;
int i;
int tot = gps->totpoints; /* number of points in new buffer */
@@ -1693,30 +1706,32 @@ void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps,
}
else {
/* just copy all points to keep into a smaller buffer */
- bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
+ bGPDspoint *new_points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * tot,
+ "new gp stroke points copy");
bGPDspoint *npt = new_points;
- MDeformVert *new_dvert = NULL;
- MDeformVert *ndvert = NULL;
+ MDeformVert *new_dvert = nullptr;
+ MDeformVert *ndvert = nullptr;
- if (gps->dvert != NULL) {
- new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
+ if (gps->dvert != nullptr) {
+ new_dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * tot,
+ "new gp stroke weights copy");
ndvert = new_dvert;
}
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ (gps->dvert != nullptr) ? dvert = gps->dvert : nullptr;
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if ((pt->flag & tag) == 0) {
*npt = *pt;
npt++;
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
*ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
ndvert++;
}
}
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
dvert++;
}
}
@@ -1789,15 +1804,15 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
*/
void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float epsilon)
{
- bGPDspoint *old_points = MEM_dupallocN(gps->points);
+ bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
int totpoints = gps->totpoints;
- char *marked = NULL;
+ char *marked = nullptr;
char work;
int start = 0;
int end = gps->totpoints - 1;
- marked = MEM_callocN(totpoints, "GP marked array");
+ marked = (char *)MEM_callocN(totpoints, "GP marked array");
marked[start] = 1;
marked[end] = 1;
@@ -1849,11 +1864,11 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e
}
/* adding points marked */
- MDeformVert *old_dvert = NULL;
- MDeformVert *dvert_src = NULL;
+ MDeformVert *old_dvert = nullptr;
+ MDeformVert *dvert_src = nullptr;
- if (gps->dvert != NULL) {
- old_dvert = MEM_dupallocN(gps->dvert);
+ if (gps->dvert != nullptr) {
+ old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
}
/* resize gps */
int j = 0;
@@ -1863,7 +1878,7 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e
if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
memcpy(pt, pt_src, sizeof(bGPDspoint));
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
dvert_src = &old_dvert[i];
MDeformVert *dvert = &gps->dvert[j];
memcpy(dvert, dvert_src, sizeof(MDeformVert));
@@ -1874,7 +1889,7 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e
j++;
}
else {
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
dvert_src = &old_dvert[i];
BKE_gpencil_free_point_weights(dvert_src);
}
@@ -1903,12 +1918,12 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
}
/* save points */
- bGPDspoint *old_points = MEM_dupallocN(gps->points);
- MDeformVert *old_dvert = NULL;
- MDeformVert *dvert_src = NULL;
+ bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
+ MDeformVert *old_dvert = nullptr;
+ MDeformVert *dvert_src = nullptr;
- if (gps->dvert != NULL) {
- old_dvert = MEM_dupallocN(gps->dvert);
+ if (gps->dvert != nullptr) {
+ old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
}
/* resize gps */
@@ -1918,9 +1933,9 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
}
newtot += 2;
- gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
+ gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
+ if (gps->dvert != nullptr) {
+ gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
}
int j = 0;
@@ -1930,7 +1945,7 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
memcpy(pt, pt_src, sizeof(bGPDspoint));
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
dvert_src = &old_dvert[i];
MDeformVert *dvert = &gps->dvert[j];
memcpy(dvert, dvert_src, sizeof(MDeformVert));
@@ -1941,7 +1956,7 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
j++;
}
else {
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
dvert_src = &old_dvert[i];
BKE_gpencil_free_point_weights(dvert_src);
}
@@ -1966,25 +1981,25 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int type)
{
bGPDspoint *temp_points;
- MDeformVert *temp_dverts = NULL;
- MDeformVert *dvert = NULL;
- MDeformVert *dvert_final = NULL;
- MDeformVert *dvert_next = NULL;
+ MDeformVert *temp_dverts = nullptr;
+ MDeformVert *dvert = nullptr;
+ MDeformVert *dvert_final = nullptr;
+ MDeformVert *dvert_next = nullptr;
int totnewpoints, oldtotpoints;
int i2;
for (int s = 0; s < level; s++) {
totnewpoints = gps->totpoints - 1;
/* duplicate points in a temp area */
- temp_points = MEM_dupallocN(gps->points);
+ temp_points = (bGPDspoint *)MEM_dupallocN(gps->points);
oldtotpoints = gps->totpoints;
/* resize the points arrays */
gps->totpoints += totnewpoints;
- gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
- if (gps->dvert != NULL) {
- temp_dverts = MEM_dupallocN(gps->dvert);
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
+ gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
+ if (gps->dvert != nullptr) {
+ temp_dverts = (MDeformVert *)MEM_dupallocN(gps->dvert);
+ gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
}
/* move points from last to first to new place */
@@ -2002,7 +2017,7 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
pt_final->runtime.idx_orig = pt->runtime.idx_orig;
copy_v4_v4(pt_final->vert_color, pt->vert_color);
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
dvert = &temp_dverts[i];
dvert_final = &gps->dvert[i2];
dvert_final->totweight = dvert->totweight;
@@ -2023,17 +2038,17 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt_final->time = interpf(pt->time, next->time, 0.5f);
- pt_final->runtime.pt_orig = NULL;
+ pt_final->runtime.pt_orig = nullptr;
pt_final->flag = 0;
interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
dvert = &temp_dverts[i];
dvert_next = &temp_dverts[i + 1];
dvert_final = &gps->dvert[i2];
dvert_final->totweight = dvert->totweight;
- dvert_final->dw = MEM_dupallocN(dvert->dw);
+ dvert_final->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
/* interpolate weight values */
for (int d = 0; d < dvert->totweight; d++) {
@@ -2055,7 +2070,7 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
/* Move points to smooth stroke (not simple type). */
if (type != GP_SUBDIV_SIMPLE) {
/* duplicate points in a temp area with the new subdivide data */
- temp_points = MEM_dupallocN(gps->points);
+ temp_points = (bGPDspoint *)MEM_dupallocN(gps->points);
/* extreme points are not changed */
for (int i = 0; i < gps->totpoints - 2; i++) {
@@ -2079,13 +2094,14 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
/**
* Reduce a series of points when the distance is below a threshold.
- * Special case for first and last points (both are keeped) for other points,
+ * Special case for first and last points (both are kept) for other points,
* the merge point always is at first point.
- * \param gpd: Grease pencil data-block
- * \param gpf: Grease Pencil frame
- * \param gps: Grease Pencil stroke
- * \param threshold: Distance between points
- * \param use_unselected: Set to true to analyze all stroke and not only selected points
+ *
+ * \param gpd: Grease pencil data-block.
+ * \param gpf: Grease Pencil frame.
+ * \param gps: Grease Pencil stroke.
+ * \param threshold: Distance between points.
+ * \param use_unselected: Set to true to analyze all stroke and not only selected points.
*/
void BKE_gpencil_stroke_merge_distance(bGPdata *gpd,
bGPDframe *gpf,
@@ -2093,8 +2109,8 @@ void BKE_gpencil_stroke_merge_distance(bGPdata *gpd,
const float threshold,
const bool use_unselected)
{
- bGPDspoint *pt = NULL;
- bGPDspoint *pt_next = NULL;
+ bGPDspoint *pt = nullptr;
+ bGPDspoint *pt_next = nullptr;
float tagged = false;
/* Use square distance to speed up loop */
const float th_square = threshold * threshold;
@@ -2160,7 +2176,7 @@ void BKE_gpencil_stroke_merge_distance(bGPdata *gpd,
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
-typedef struct GpEdge {
+struct GpEdge {
uint v1, v2;
/* Coordinates. */
float v1_co[3], v2_co[3];
@@ -2169,7 +2185,7 @@ typedef struct GpEdge {
/* Direction of the segment. */
float vec[3];
int flag;
-} GpEdge;
+};
static int gpencil_next_edge(
GpEdge *gp_edges, int totedges, GpEdge *gped_init, const float threshold, const bool reverse)
@@ -2261,14 +2277,14 @@ static void gpencil_generate_edgeloops(Object *ob,
}
/* Arrays for all edge vertices (forward and backward) that form a edge loop.
- * This is reused for each edgeloop to create gpencil stroke. */
- uint *stroke = MEM_callocN(sizeof(uint) * me->totedge * 2, __func__);
- uint *stroke_fw = MEM_callocN(sizeof(uint) * me->totedge, __func__);
- uint *stroke_bw = MEM_callocN(sizeof(uint) * me->totedge, __func__);
+ * This is reused for each edge-loop to create gpencil stroke. */
+ uint *stroke = (uint *)MEM_callocN(sizeof(uint) * me->totedge * 2, __func__);
+ uint *stroke_fw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__);
+ uint *stroke_bw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__);
/* Create array with all edges. */
- GpEdge *gp_edges = MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
- GpEdge *gped = NULL;
+ GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
+ GpEdge *gped = nullptr;
for (int i = 0; i < me->totedge; i++) {
MEdge *ed = &me->medge[i];
gped = &gp_edges[i];
@@ -2321,7 +2337,7 @@ static void gpencil_generate_edgeloops(Object *ob,
/* Look backward edges. */
int totbw = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_bw, e, angle, true);
- BLI_ghash_free(v_table, NULL, NULL);
+ BLI_ghash_free(v_table, nullptr, nullptr);
/* Join both arrays. */
int array_len = 0;
@@ -2423,7 +2439,7 @@ static int gpencil_material_find_index_by_name(Object *ob, const char *name)
{
for (int i = 0; i < ob->totcol; i++) {
Material *ma = BKE_object_material_get(ob, i + 1);
- if ((ma != NULL) && (ma->gp_style != NULL) && (STREQ(ma->id.name + 2, name))) {
+ if ((ma != nullptr) && (ma->gp_style != nullptr) && (STREQ(ma->id.name + 2, name))) {
return i;
}
}
@@ -2453,7 +2469,7 @@ static void make_element_name(const char *obname, const char *name, const int ma
* \param scene: Original scene.
* \param ob_gp: Grease pencil object to add strokes.
* \param ob_mesh: Mesh to convert.
- * \param angle: Limit angle to consider a edgeloop ends.
+ * \param angle: Limit angle to consider a edge-loop ends.
* \param thickness: Thickness of the strokes.
* \param offset: Offset along the normals.
* \param matrix: Transformation matrix.
@@ -2474,7 +2490,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
const bool use_seams,
const bool use_faces)
{
- if (ELEM(NULL, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) {
+ if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == nullptr)) {
return false;
}
@@ -2511,7 +2527,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
/* Create Layer and Frame. */
bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
- if (gpl_fill == NULL) {
+ if (gpl_fill == nullptr) {
gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
@@ -2524,11 +2540,11 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
int mat_idx = 0;
Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
make_element_name(
- ob_mesh->id.name + 2, (ma != NULL) ? ma->id.name + 2 : "Fill", 64, element_name);
+ ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name);
mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
if (mat_idx == -1) {
float color[4];
- if (ma != NULL) {
+ if (ma != nullptr) {
copy_v3_v3(color, &ma->r);
color[3] = 1.0f;
}
@@ -2567,7 +2583,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
- if (gpl_stroke == NULL) {
+ if (gpl_stroke == nullptr) {
gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
@@ -2589,7 +2605,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
*/
void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
{
- if (gpd == NULL) {
+ if (gpd == nullptr) {
return;
}
@@ -2625,7 +2641,7 @@ int BKE_gpencil_stroke_point_count(const bGPdata *gpd)
{
int total_points = 0;
- if (gpd == NULL) {
+ if (gpd == nullptr) {
return 0;
}
@@ -2650,7 +2666,7 @@ int BKE_gpencil_stroke_point_count(const bGPdata *gpd)
/* Used for "move only origins" in object_data_transform.c */
void BKE_gpencil_point_coords_get(bGPdata *gpd, GPencilPointCoordinates *elem_data)
{
- if (gpd == NULL) {
+ if (gpd == nullptr) {
return;
}
@@ -2681,7 +2697,7 @@ void BKE_gpencil_point_coords_get(bGPdata *gpd, GPencilPointCoordinates *elem_da
/* Used for "move only origins" in object_data_transform.c */
void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates *elem_data)
{
- if (gpd == NULL) {
+ if (gpd == nullptr) {
return;
}
@@ -2717,7 +2733,7 @@ void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd,
const GPencilPointCoordinates *elem_data,
const float mat[4][4])
{
- if (gpd == NULL) {
+ if (gpd == nullptr) {
return;
}
@@ -2818,24 +2834,24 @@ void BKE_gpencil_stroke_flip(bGPDstroke *gps)
* that should be kept when splitting up a stroke. Used in:
* gpencil_stroke_delete_tagged_points()
*/
-typedef struct tGPDeleteIsland {
+struct tGPDeleteIsland {
int start_idx;
int end_idx;
-} tGPDeleteIsland;
+};
static void gpencil_stroke_join_islands(bGPdata *gpd,
bGPDframe *gpf,
bGPDstroke *gps_first,
bGPDstroke *gps_last)
{
- bGPDspoint *pt = NULL;
- bGPDspoint *pt_final = NULL;
+ bGPDspoint *pt = nullptr;
+ bGPDspoint *pt_final = nullptr;
const int totpoints = gps_first->totpoints + gps_last->totpoints;
/* create new stroke */
bGPDstroke *join_stroke = BKE_gpencil_stroke_duplicate(gps_first, false, true);
- join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
+ join_stroke->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
join_stroke->totpoints = totpoints;
join_stroke->flag &= ~GP_STROKE_CYCLIC;
@@ -2868,17 +2884,17 @@ static void gpencil_stroke_join_islands(bGPdata *gpd,
}
/* Copy over vertex weight data (if available) */
- if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) {
- join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__);
- MDeformVert *dvert_src = NULL;
- MDeformVert *dvert_dst = NULL;
+ if ((gps_first->dvert != nullptr) || (gps_last->dvert != nullptr)) {
+ join_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * totpoints, __func__);
+ MDeformVert *dvert_src = nullptr;
+ MDeformVert *dvert_dst = nullptr;
/* Copy weights (last before). */
e1 = 0;
e2 = 0;
for (int i = 0; i < totpoints; i++) {
dvert_dst = &join_stroke->dvert[i];
- dvert_src = NULL;
+ dvert_src = nullptr;
if (i < gps_last->totpoints) {
if (gps_last->dvert) {
dvert_src = &gps_last->dvert[e1];
@@ -2893,7 +2909,7 @@ static void gpencil_stroke_join_islands(bGPdata *gpd,
}
if ((dvert_src) && (dvert_src->dw)) {
- dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ dvert_dst->dw = (MDeformWeight *)MEM_dupallocN(dvert_src->dw);
}
}
}
@@ -2934,13 +2950,13 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
bool select,
int limit)
{
- tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2,
- "gp_point_islands");
+ tGPDeleteIsland *islands = (tGPDeleteIsland *)MEM_callocN(
+ sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
bool in_island = false;
int num_islands = 0;
- bGPDstroke *new_stroke = NULL;
- bGPDstroke *gps_first = NULL;
+ bGPDstroke *new_stroke = nullptr;
+ bGPDstroke *gps_first = nullptr;
const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC);
/* First Pass: Identify start/end of islands */
@@ -2982,7 +2998,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
new_stroke = BKE_gpencil_stroke_duplicate(gps, false, true);
/* if cyclic and first stroke, save to join later */
- if ((is_cyclic) && (gps_first == NULL)) {
+ if ((is_cyclic) && (gps_first == nullptr)) {
gps_first = new_stroke;
}
@@ -2992,17 +3008,17 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
new_stroke->totpoints = island->end_idx - island->start_idx + 1;
/* Copy over the relevant point data */
- new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints,
- "gp delete stroke fragment");
+ new_stroke->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints,
+ "gp delete stroke fragment");
memcpy(new_stroke->points,
gps->points + island->start_idx,
sizeof(bGPDspoint) * new_stroke->totpoints);
/* Copy over vertex weight data (if available) */
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
/* Copy over the relevant vertex-weight points */
- new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints,
- "gp delete stroke fragment weight");
+ new_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints,
+ "gp delete stroke fragment weight");
memcpy(new_stroke->dvert,
gps->dvert + island->start_idx,
sizeof(MDeformVert) * new_stroke->totpoints);
@@ -3013,7 +3029,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
MDeformVert *dvert_src = &gps->dvert[e];
MDeformVert *dvert_dst = &new_stroke->dvert[i];
if (dvert_src->dw) {
- dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ dvert_dst->dw = (MDeformWeight *)MEM_dupallocN(dvert_src->dw);
}
e++;
}
@@ -3047,7 +3063,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
/* Add new stroke to the frame or delete if below limit */
if ((limit > 0) && (new_stroke->totpoints <= limit)) {
if (gps_first == new_stroke) {
- gps_first = NULL;
+ gps_first = nullptr;
}
BKE_gpencil_free_stroke(new_stroke);
}
@@ -3064,7 +3080,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
}
}
/* if cyclic, need to join last stroke with first stroke */
- if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) {
+ if ((is_cyclic) && (gps_first != nullptr) && (gps_first != new_stroke)) {
gpencil_stroke_join_islands(gpd, gpf, gps_first, new_stroke);
}
}
@@ -3086,13 +3102,13 @@ void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd,
bGPDcurve *gpc,
int tag_flags)
{
- if (gpc == NULL) {
+ if (gpc == nullptr) {
return;
}
const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
const int idx_last = gpc->tot_curve_points - 1;
- bGPDstroke *gps_first = NULL;
- bGPDstroke *gps_last = NULL;
+ bGPDstroke *gps_first = nullptr;
+ bGPDstroke *gps_last = nullptr;
int idx_start = 0;
int idx_end = 0;
@@ -3123,11 +3139,11 @@ void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd,
}
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, false, false);
- new_stroke->points = NULL;
+ new_stroke->points = nullptr;
new_stroke->flag &= ~GP_STROKE_CYCLIC;
new_stroke->editcurve = BKE_gpencil_stroke_editcurve_new(island_length);
- if (gps_first == NULL) {
+ if (gps_first == nullptr) {
gps_first = new_stroke;
}
@@ -3155,15 +3171,15 @@ void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd,
}
/* join first and last stroke if cyclic */
- if (is_cyclic && gps_first != NULL && gps_last != NULL && gps_first != gps_last) {
+ if (is_cyclic && gps_first != nullptr && gps_last != nullptr && gps_first != gps_last) {
bGPDcurve *gpc_first = gps_first->editcurve;
bGPDcurve *gpc_last = gps_last->editcurve;
int first_tot_points = gpc_first->tot_curve_points;
int old_tot_points = gpc_last->tot_curve_points;
gpc_last->tot_curve_points = first_tot_points + old_tot_points;
- gpc_last->curve_points = MEM_recallocN(gpc_last->curve_points,
- sizeof(bGPDcurve_point) * gpc_last->tot_curve_points);
+ gpc_last->curve_points = (bGPDcurve_point *)MEM_recallocN(
+ gpc_last->curve_points, sizeof(bGPDcurve_point) * gpc_last->tot_curve_points);
/* copy data from first to last */
memcpy(gpc_last->curve_points + old_tot_points,
gpc_first->curve_points,
@@ -3196,14 +3212,16 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps,
{
bGPDspoint *newpoint;
- gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
- if (gps->dvert != NULL) {
- gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1));
+ gps->points = (bGPDspoint *)MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
+ if (gps->dvert != nullptr) {
+ gps->dvert = (MDeformVert *)MEM_reallocN(gps->dvert,
+ sizeof(MDeformVert) * (gps->totpoints + 1));
}
else {
/* If destination has weight add weight to origin. */
- if (dvert != NULL) {
- gps->dvert = MEM_callocN(sizeof(MDeformVert) * (gps->totpoints + 1), __func__);
+ if (dvert != nullptr) {
+ gps->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (gps->totpoints + 1),
+ __func__);
}
}
@@ -3219,16 +3237,16 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps,
newpoint->time = point->time + deltatime;
copy_v4_v4(newpoint->vert_color, point->vert_color);
- if (gps->dvert != NULL) {
+ if (gps->dvert != nullptr) {
MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1];
- if (dvert != NULL) {
+ if (dvert != nullptr) {
newdvert->totweight = dvert->totweight;
- newdvert->dw = MEM_dupallocN(dvert->dw);
+ newdvert->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
}
else {
newdvert->totweight = 0;
- newdvert->dw = NULL;
+ newdvert->dw = nullptr;
}
}
}
@@ -3246,7 +3264,7 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
float deltatime = 0.0f;
/* sanity checks */
- if (ELEM(NULL, gps_a, gps_b)) {
+ if (ELEM(nullptr, gps_a, gps_b)) {
return;
}
@@ -3308,11 +3326,11 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
point = gps_a->points[gps_a->totpoints - 1];
deltatime = point.time;
- gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, 0.0f);
+ gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, 0.0f);
/* 2nd: add one head point to finish invisible area */
point = gps_b->points[0];
- gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, deltatime);
+ gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, deltatime);
}
const float ratio = (fit_thickness && gps_a->thickness > 0.0f) ?
@@ -3321,7 +3339,7 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
/* 3rd: add all points */
for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
- MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : NULL;
+ MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : nullptr;
gpencil_stroke_copy_point(
gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime);
}
@@ -3340,16 +3358,16 @@ void BKE_gpencil_stroke_copy_to_keyframes(
if (gpf->framenum != cfra) {
bGPDframe *gpf_new = BKE_gpencil_layer_frame_find(gpl, cfra);
- if (gpf_new == NULL) {
+ if (gpf_new == nullptr) {
gpf_new = BKE_gpencil_frame_addnew(gpl, cfra);
}
- if (gpf_new == NULL) {
+ if (gpf_new == nullptr) {
continue;
}
bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, true, true);
- if (gps_new == NULL) {
+ if (gps_new == nullptr) {
continue;
}
@@ -3363,38 +3381,38 @@ void BKE_gpencil_stroke_copy_to_keyframes(
}
/* Free hash table. */
- BLI_ghash_free(frame_list, NULL, NULL);
+ BLI_ghash_free(frame_list, nullptr, nullptr);
}
/* Stroke Uniform Subdivide ------------------------------------- */
-typedef struct tSamplePoint {
+struct tSamplePoint {
struct tSamplePoint *next, *prev;
float x, y, z;
float pressure, strength, time;
float vertex_color[4];
struct MDeformWeight *dw;
int totweight;
-} tSamplePoint;
+};
-typedef struct tSampleEdge {
+struct tSampleEdge {
float length_sq;
tSamplePoint *from;
tSamplePoint *to;
-} tSampleEdge;
+};
/* Helper: creates a tSamplePoint from a bGPDspoint and (optionally) a MDeformVert. */
static tSamplePoint *new_sample_point_from_gp_point(const bGPDspoint *pt, const MDeformVert *dvert)
{
- tSamplePoint *new_pt = MEM_callocN(sizeof(tSamplePoint), __func__);
+ tSamplePoint *new_pt = (tSamplePoint *)MEM_callocN(sizeof(tSamplePoint), __func__);
copy_v3_v3(&new_pt->x, &pt->x);
new_pt->pressure = pt->pressure;
new_pt->strength = pt->strength;
new_pt->time = pt->time;
copy_v4_v4((float *)&new_pt->vertex_color, (float *)&pt->vert_color);
- if (dvert != NULL) {
+ if (dvert != nullptr) {
new_pt->totweight = dvert->totweight;
- new_pt->dw = MEM_callocN(sizeof(MDeformWeight) * new_pt->totweight, __func__);
+ new_pt->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * new_pt->totweight, __func__);
for (uint i = 0; i < new_pt->totweight; ++i) {
MDeformWeight *dw = &new_pt->dw[i];
MDeformWeight *dw_from = &dvert->dw[i];
@@ -3409,7 +3427,7 @@ static tSamplePoint *new_sample_point_from_gp_point(const bGPDspoint *pt, const
* the edge. */
static tSampleEdge *new_sample_edge_from_sample_points(tSamplePoint *from, tSamplePoint *to)
{
- tSampleEdge *new_edge = MEM_callocN(sizeof(tSampleEdge), __func__);
+ tSampleEdge *new_edge = (tSampleEdge *)MEM_callocN(sizeof(tSampleEdge), __func__);
new_edge->from = from;
new_edge->to = to;
new_edge->length_sq = len_squared_v3v3(&from->x, &to->x);
@@ -3431,27 +3449,27 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
const bool select)
{
/* Stroke needs at least two points and strictly less points than the target number. */
- if (gps == NULL || gps->totpoints < 2 || gps->totpoints >= target_number) {
+ if (gps == nullptr || gps->totpoints < 2 || gps->totpoints >= target_number) {
return;
}
const int totpoints = gps->totpoints;
- const bool has_dverts = (gps->dvert != NULL);
+ const bool has_dverts = (gps->dvert != nullptr);
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC);
- ListBase points = {NULL, NULL};
+ ListBase points = {nullptr, nullptr};
Heap *edges = BLI_heap_new();
/* Add all points into list. */
for (uint32_t i = 0; i < totpoints; ++i) {
bGPDspoint *pt = &gps->points[i];
- MDeformVert *dvert = has_dverts ? &gps->dvert[i] : NULL;
+ MDeformVert *dvert = has_dverts ? &gps->dvert[i] : nullptr;
tSamplePoint *sp = new_sample_point_from_gp_point(pt, dvert);
BLI_addtail(&points, sp);
}
/* Iterate over edges and insert them into the heap. */
- for (tSamplePoint *pt = ((tSamplePoint *)points.first)->next; pt != NULL; pt = pt->next) {
+ for (tSamplePoint *pt = ((tSamplePoint *)points.first)->next; pt != nullptr; pt = pt->next) {
tSampleEdge *se = new_sample_edge_from_sample_points(pt->prev, pt);
/* BLI_heap is a min-heap, but we need the largest key to be at the top, so we take the
* negative of the squared length. */
@@ -3459,8 +3477,8 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
}
if (is_cyclic) {
- tSamplePoint *sp_first = points.first;
- tSamplePoint *sp_last = points.last;
+ tSamplePoint *sp_first = (tSamplePoint *)points.first;
+ tSamplePoint *sp_last = (tSamplePoint *)points.last;
tSampleEdge *se = new_sample_edge_from_sample_points(sp_last, sp_first);
BLI_heap_insert(edges, -(se->length_sq), se);
}
@@ -3469,12 +3487,12 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
BLI_assert(num_points_needed > 0);
while (num_points_needed > 0) {
- tSampleEdge *se = BLI_heap_pop_min(edges);
+ tSampleEdge *se = (tSampleEdge *)BLI_heap_pop_min(edges);
tSamplePoint *sp = se->from;
tSamplePoint *sp_next = se->to;
/* Subdivide the edge. */
- tSamplePoint *new_sp = MEM_callocN(sizeof(tSamplePoint), __func__);
+ tSamplePoint *new_sp = (tSamplePoint *)MEM_callocN(sizeof(tSamplePoint), __func__);
interp_v3_v3v3(&new_sp->x, &sp->x, &sp_next->x, 0.5f);
new_sp->pressure = interpf(sp->pressure, sp_next->pressure, 0.5f);
new_sp->strength = interpf(sp->strength, sp_next->strength, 0.5f);
@@ -3485,7 +3503,8 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
0.5f);
if (sp->dw && sp_next->dw) {
new_sp->totweight = MIN2(sp->totweight, sp_next->totweight);
- new_sp->dw = MEM_callocN(sizeof(MDeformWeight) * new_sp->totweight, __func__);
+ new_sp->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * new_sp->totweight,
+ __func__);
for (uint32_t i = 0; i < new_sp->totweight; ++i) {
MDeformWeight *dw = &new_sp->dw[i];
MDeformWeight *dw_from = &sp->dw[i];
@@ -3509,13 +3528,13 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
BLI_heap_free(edges, (HeapFreeFP)MEM_freeN);
gps->totpoints = target_number;
- gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
+ gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
if (has_dverts) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
+ gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
}
/* Convert list back to stroke point array. */
- tSamplePoint *sp = points.first;
+ tSamplePoint *sp = (tSamplePoint *)points.first;
for (uint32_t i = 0; i < gps->totpoints && sp; ++i, sp = sp->next) {
bGPDspoint *pt = &gps->points[i];
MDeformVert *dvert = &gps->dvert[i];
@@ -3528,7 +3547,7 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
if (sp->dw) {
dvert->totweight = sp->totweight;
- dvert->dw = MEM_callocN(sizeof(MDeformWeight) * dvert->totweight, __func__);
+ dvert->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dvert->totweight, __func__);
for (uint32_t j = 0; j < dvert->totweight; ++j) {
MDeformWeight *dw = &dvert->dw[j];
MDeformWeight *dw_from = &sp->dw[j];
@@ -3549,7 +3568,7 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
/* Free the sample points. Important to use the mutable loop here because we are erasing the list
* elements. */
LISTBASE_FOREACH_MUTABLE (tSamplePoint *, temp, &points) {
- if (temp->dw != NULL) {
+ if (temp->dw != nullptr) {
MEM_freeN(temp->dw);
}
MEM_SAFE_FREE(temp);
@@ -3601,14 +3620,14 @@ void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d,
/* ----------------------------------------------------------------------------- */
/* Stroke to perimeter */
-typedef struct tPerimeterPoint {
+struct tPerimeterPoint {
struct tPerimeterPoint *next, *prev;
float x, y, z;
-} tPerimeterPoint;
+};
static tPerimeterPoint *new_perimeter_point(const float pt[3])
{
- tPerimeterPoint *new_pt = MEM_callocN(sizeof(tPerimeterPoint), __func__);
+ tPerimeterPoint *new_pt = (tPerimeterPoint *)MEM_callocN(sizeof(tPerimeterPoint), __func__);
copy_v3_v3(&new_pt->x, pt);
return new_pt;
}
@@ -3771,14 +3790,14 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
{
/* sanity check */
if (gps->totpoints < 1) {
- return NULL;
+ return nullptr;
}
float defaultpixsize = 1000.0f / gpd->pixfactor;
float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
- ListBase *perimeter_right_side = MEM_callocN(sizeof(ListBase), __func__);
- ListBase *perimeter_left_side = MEM_callocN(sizeof(ListBase), __func__);
+ ListBase *perimeter_right_side = (ListBase *)MEM_callocN(sizeof(ListBase), __func__);
+ ListBase *perimeter_left_side = (ListBase *)MEM_callocN(sizeof(ListBase), __func__);
int num_perimeter_points = 0;
bGPDspoint *first = &gps->points[0];
@@ -4018,7 +4037,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
const float diff_mat[4][4])
{
if (gps->totpoints == 0) {
- return NULL;
+ return nullptr;
}
bGPDstroke *gps_temp = BKE_gpencil_stroke_duplicate(gps, true, false);
const bool cyclic = ((gps_temp->flag & GP_STROKE_CYCLIC) != 0);
@@ -4026,8 +4045,8 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
/* If Cyclic, add a new point. */
if (cyclic && (gps_temp->totpoints > 1)) {
gps_temp->totpoints++;
- gps_temp->points = MEM_recallocN(gps_temp->points,
- sizeof(*gps_temp->points) * gps_temp->totpoints);
+ gps_temp->points = (bGPDspoint *)MEM_recallocN(
+ gps_temp->points, sizeof(*gps_temp->points) * gps_temp->totpoints);
bGPDspoint *pt_src = &gps_temp->points[0];
bGPDspoint *pt_dst = &gps_temp->points[gps_temp->totpoints - 1];
copy_v3_v3(&pt_dst->x, &pt_src->x);
@@ -4043,7 +4062,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
gpd, gpl, gps_temp, subdivisions, &num_perimeter_points);
if (num_perimeter_points == 0) {
- return NULL;
+ return nullptr;
}
/* Create new stroke. */
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index 0e2219e6c7f..4db527e5b42 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -273,7 +273,7 @@ int BKE_gpencil_time_modifier_cfra(Depsgraph *depsgraph,
if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) {
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
- if ((GPENCIL_MODIFIER_EDIT(md, is_edit)) && (!is_render)) {
+ if (GPENCIL_MODIFIER_EDIT(md, is_edit) && (!is_render)) {
continue;
}
@@ -838,7 +838,7 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) {
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
- if ((GPENCIL_MODIFIER_EDIT(md, is_edit)) && (!is_render)) {
+ if (GPENCIL_MODIFIER_EDIT(md, is_edit) && (!is_render)) {
continue;
}
diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc
index 7e9f81f9c83..12a0a1e3ae7 100644
--- a/source/blender/blenkernel/intern/icons.cc
+++ b/source/blender/blenkernel/intern/icons.cc
@@ -667,7 +667,7 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv)
BKE_previewimg_finish(prv, i);
}
else {
- /* Only for old files that didn't write the flag . */
+ /* Only for old files that didn't write the flag. */
prv->flag[i] |= PRV_UNFINISHED;
}
}
diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c
index 9e3d5a162ae..360bad3e786 100644
--- a/source/blender/blenkernel/intern/image_save.c
+++ b/source/blender/blenkernel/intern/image_save.c
@@ -135,8 +135,8 @@ static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf)
/**
* \return success.
- * \note ``ima->filepath`` and ``ibuf->name`` should end up the same.
- * \note for multiview the first ``ibuf`` is important to get the settings.
+ * \note `ima->filepath` and `ibuf->name` should end up the same.
+ * \note for multi-view the first `ibuf` is important to get the settings.
*/
static bool image_save_single(ReportList *reports,
Image *ima,
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 60e42c09ff3..ae1863f0a47 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -23,7 +23,10 @@
#include <string.h>
+#include "CLG_log.h"
+
#include "BLI_listbase.h"
+#include "BLI_mempool.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.h"
@@ -63,6 +66,8 @@
#include "BLO_read_write.h"
+static CLG_LogRef LOG = {"bke.layercollection"};
+
/* Set of flags which are dependent on a collection settings. */
static const short g_base_collection_flags = (BASE_VISIBLE_DEPSGRAPH | BASE_VISIBLE_VIEWLAYER |
BASE_SELECTABLE | BASE_ENABLED_VIEWPORT |
@@ -174,8 +179,8 @@ static ViewLayer *view_layer_add(const char *name)
BLI_strncpy_utf8(view_layer->name, name, sizeof(view_layer->name));
/* Pure rendering pipeline settings. */
- view_layer->layflag = 0x7FFF; /* solid ztra halo edge strand */
- view_layer->passflag = SCE_PASS_COMBINED | SCE_PASS_Z;
+ view_layer->layflag = SCE_LAY_FLAG_DEFAULT;
+ view_layer->passflag = SCE_PASS_COMBINED;
view_layer->pass_alpha_threshold = 0.5f;
view_layer->cryptomatte_levels = 6;
view_layer->cryptomatte_flag = VIEW_LAYER_CRYPTOMATTE_ACCURATE;
@@ -737,9 +742,231 @@ int BKE_layer_collection_findindex(ViewLayer *view_layer, const LayerCollection
* in at least one layer collection. That list is also synchronized here, and
* stores state like selection. */
+/* This API allows to temporarily forbid resync of LayerCollections.
+ *
+ * This can greatly improve performances in cases where those functions get
+ * called a lot (e.g. during massive remappings of IDs).
+ *
+ * Usage of these should be done very carefully though. In particular, calling
+ * code must ensures it resync LayerCollections before any UI/Event loop
+ * handling can happen.
+ *
+ * WARNING: This is not threadsafe at all, only use from main thread.
+ *
+ * NOTE: It is probably needed to use #BKE_main_collection_sync_remap instead
+ * of just #BKE_main_collection_sync after disabling LayerCollection resync,
+ * unless it is absolutely certain that no ID remapping (or any other process
+ * that may invalidate the caches) will happen while it is disabled.
+ *
+ * NOTE: This is a quick and safe band-aid around the long-known issue
+ * regarding this resync process.
+ * Proper fix would be to make resync itself lazy, i.e. only happen
+ * when actually needed.
+ * See also T73411.
+ */
+static bool no_resync = false;
+
+void BKE_layer_collection_resync_forbid(void)
+{
+ no_resync = true;
+}
+
+void BKE_layer_collection_resync_allow(void)
+{
+ no_resync = false;
+}
+
+typedef struct LayerCollectionResync {
+ struct LayerCollectionResync *prev, *next;
+
+ /* Temp data used to generate a queue during valid layer search. See
+ * #layer_collection_resync_find. */
+ struct LayerCollectionResync *queue_next;
+
+ /* LayerCollection and Collection wrapped by this data. */
+ LayerCollection *layer;
+ Collection *collection;
+
+ /* Hierarchical relationships in the old, existing ViewLayer state (except for newly created
+ * layers). */
+ struct LayerCollectionResync *parent_layer_resync;
+ ListBase children_layer_resync;
+
+ /* This layer still points to a valid collection. */
+ bool is_usable;
+ /* This layer is still valid as a parent, i.e. at least one of its original layer children is
+ * usable and matches one of its current children collections. */
+ bool is_valid_as_parent;
+ /* This layer is still valid as a child, i.e. its original layer parent is usable and matches one
+ * of its current parents collections. */
+ bool is_valid_as_child;
+ /* This layer is still fully valid in the new collection hierarchy, i.e. itself and all of its
+ * parents fully match the current collection hierarchy.
+ * OR
+ * This layer has already been re-used to match the new collections hierarchy. */
+ bool is_used;
+} LayerCollectionResync;
+
+static LayerCollectionResync *layer_collection_resync_create_recurse(
+ LayerCollectionResync *parent_layer_resync, LayerCollection *layer, BLI_mempool *mempool)
+{
+ LayerCollectionResync *layer_resync = BLI_mempool_calloc(mempool);
+
+ layer_resync->layer = layer;
+ layer_resync->collection = layer->collection;
+ layer_resync->parent_layer_resync = parent_layer_resync;
+ if (parent_layer_resync != NULL) {
+ BLI_addtail(&parent_layer_resync->children_layer_resync, layer_resync);
+ }
+
+ layer_resync->is_usable = (layer->collection != NULL);
+ layer_resync->is_valid_as_child =
+ layer_resync->is_usable && (parent_layer_resync == NULL ||
+ (parent_layer_resync->is_usable &&
+ BLI_findptr(&parent_layer_resync->layer->collection->children,
+ layer->collection,
+ offsetof(CollectionChild, collection)) != NULL));
+ if (layer_resync->is_valid_as_child) {
+ layer_resync->is_used = parent_layer_resync != NULL ? parent_layer_resync->is_used : true;
+ }
+ else {
+ layer_resync->is_used = false;
+ }
+
+ if (BLI_listbase_is_empty(&layer->layer_collections)) {
+ layer_resync->is_valid_as_parent = layer_resync->is_usable;
+ }
+ else {
+ LISTBASE_FOREACH (LayerCollection *, child_layer, &layer->layer_collections) {
+ LayerCollectionResync *child_layer_resync = layer_collection_resync_create_recurse(
+ layer_resync, child_layer, mempool);
+ if (layer_resync->is_usable && child_layer_resync->is_valid_as_child) {
+ layer_resync->is_valid_as_parent = true;
+ }
+ }
+ }
+
+ CLOG_INFO(&LOG,
+ 4,
+ "Old LayerCollection for %s is...\n\tusable: %d\n\tvalid parent: %d\n\tvalid child: "
+ "%d\n\tused: %d\n",
+ layer_resync->collection ? layer_resync->collection->id.name : "<NONE>",
+ layer_resync->is_usable,
+ layer_resync->is_valid_as_parent,
+ layer_resync->is_valid_as_child,
+ layer_resync->is_used);
+
+ return layer_resync;
+}
+
+static LayerCollectionResync *layer_collection_resync_find(LayerCollectionResync *layer_resync,
+ Collection *child_collection)
+{
+ /* Given the given parent, valid layer collection, find in the old hierarchy the best possible
+ * unused layer matching the given child collection.
+ *
+ * This uses the following heuristics:
+ * - Prefer a layer descendant of the given parent one if possible.
+ * - Prefer a layer as closely related as possible from the given parent.
+ * - Do not used layers that are not head (highest possible ancestor) of a local valid hierarchy
+ * branch, since we can assume we could then re-use its ancestor instead.
+ *
+ * A queue is used to ensure this order of preferences.
+ */
+
+ BLI_assert(layer_resync->collection != child_collection);
+ BLI_assert(child_collection != NULL);
+
+ LayerCollectionResync *current_layer_resync = NULL;
+ LayerCollectionResync *root_layer_resync = layer_resync;
+
+ LayerCollectionResync *queue_head = layer_resync, *queue_tail = layer_resync;
+ layer_resync->queue_next = NULL;
+
+ while (queue_head != NULL) {
+ current_layer_resync = queue_head;
+ queue_head = current_layer_resync->queue_next;
+
+ if (current_layer_resync->collection == child_collection &&
+ (current_layer_resync->parent_layer_resync == layer_resync ||
+ (!current_layer_resync->is_used && !current_layer_resync->is_valid_as_child))) {
+ /* This layer is a valid candidate, because its collection matches the seeked one, AND:
+ * - It is a direct child of the initial given parent ('unchanged hierarchy' case), OR
+ * - It is not currently used, and not part of a valid hierarchy (sub-)chain.
+ */
+ break;
+ }
+
+ /* Else, add all its direct children for further searching. */
+ LISTBASE_FOREACH (LayerCollectionResync *,
+ child_layer_resync,
+ &current_layer_resync->children_layer_resync) {
+ /* Add to tail of the queue. */
+ queue_tail->queue_next = child_layer_resync;
+ child_layer_resync->queue_next = NULL;
+ queue_tail = child_layer_resync;
+ if (queue_head == NULL) {
+ queue_head = queue_tail;
+ }
+ }
+
+ /* If all descendants from current layer have been processed, go one step higher and
+ * process all of its other siblings. */
+ if (queue_head == NULL && root_layer_resync->parent_layer_resync != NULL) {
+ LISTBASE_FOREACH (LayerCollectionResync *,
+ sibling_layer_resync,
+ &root_layer_resync->parent_layer_resync->children_layer_resync) {
+ if (sibling_layer_resync == root_layer_resync) {
+ continue;
+ }
+ /* Add to tail of the queue. */
+ queue_tail->queue_next = sibling_layer_resync;
+ sibling_layer_resync->queue_next = NULL;
+ queue_tail = sibling_layer_resync;
+ if (queue_head == NULL) {
+ queue_head = queue_tail;
+ }
+ }
+ root_layer_resync = root_layer_resync->parent_layer_resync;
+ }
+
+ current_layer_resync = NULL;
+ }
+
+ return current_layer_resync;
+}
+
+static void layer_collection_resync_unused_layers_free(ViewLayer *view_layer,
+ LayerCollectionResync *layer_resync)
+{
+ LISTBASE_FOREACH (
+ LayerCollectionResync *, child_layer_resync, &layer_resync->children_layer_resync) {
+ layer_collection_resync_unused_layers_free(view_layer, child_layer_resync);
+ }
+
+ if (!layer_resync->is_used) {
+ CLOG_INFO(&LOG,
+ 4,
+ "Freeing unused LayerCollection for %s",
+ layer_resync->collection != NULL ? layer_resync->collection->id.name :
+ "<Deleted Collection>");
+
+ if (layer_resync->layer == view_layer->active_collection) {
+ view_layer->active_collection = NULL;
+ }
+
+ /* We do not want to go recursive here, this is handled through the LayerCollectionResync data
+ * wrapper. */
+ MEM_freeN(layer_resync->layer);
+ layer_resync->layer = NULL;
+ layer_resync->collection = NULL;
+ layer_resync->is_usable = false;
+ }
+}
+
static void layer_collection_objects_sync(ViewLayer *view_layer,
LayerCollection *layer,
- ListBase *new_object_bases,
+ ListBase *r_lb_new_object_bases,
const short collection_restrict,
const short layer_restrict,
const ushort local_collections_bits)
@@ -765,9 +992,9 @@ static void layer_collection_objects_sync(ViewLayer *view_layer,
* been moved to the new base list and the first/last test ensure that
* case also works. */
base = *base_p;
- if (!ELEM(base, new_object_bases->first, new_object_bases->last)) {
+ if (!ELEM(base, r_lb_new_object_bases->first, r_lb_new_object_bases->last)) {
BLI_remlink(&view_layer->object_bases, base);
- BLI_addtail(new_object_bases, base);
+ BLI_addtail(r_lb_new_object_bases, base);
}
}
else {
@@ -775,7 +1002,7 @@ static void layer_collection_objects_sync(ViewLayer *view_layer,
base = object_base_new(cob->ob);
base->local_collections_bits = local_collections_bits;
*base_p = base;
- BLI_addtail(new_object_bases, base);
+ BLI_addtail(r_lb_new_object_bases, base);
}
if ((collection_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0) {
@@ -805,124 +1032,146 @@ static void layer_collection_objects_sync(ViewLayer *view_layer,
}
static void layer_collection_sync(ViewLayer *view_layer,
- const ListBase *lb_collections,
- ListBase *lb_layer_collections,
- ListBase *new_object_bases,
- short parent_exclude,
- short parent_restrict,
- short parent_layer_restrict,
- unsigned short parent_local_collections_bits)
-{
- /* TODO: support recovery after removal of intermediate collections, reordering, ..
- * For local edits we can make editing operating do the appropriate thing, but for
- * linking we can only sync after the fact. */
-
- /* Remove layer collections that no longer have a corresponding scene collection. */
- LISTBASE_FOREACH_MUTABLE (LayerCollection *, lc, lb_layer_collections) {
- /* Note that ID remap can set lc->collection to NULL when deleting collections. */
- Collection *collection = (lc->collection) ?
- BLI_findptr(lb_collections,
- lc->collection,
- offsetof(CollectionChild, collection)) :
- NULL;
-
- if (!collection) {
- if (lc == view_layer->active_collection) {
- view_layer->active_collection = NULL;
- }
-
- /* Free recursively. */
- layer_collection_free(view_layer, lc);
- BLI_freelinkN(lb_layer_collections, lc);
- }
- }
+ LayerCollectionResync *layer_resync,
+ BLI_mempool *layer_resync_mempool,
+ ListBase *r_lb_new_object_bases,
+ const short parent_layer_flag,
+ const short parent_collection_restrict,
+ const short parent_layer_restrict,
+ const ushort parent_local_collections_bits)
+{
+ /* This function assumes current 'parent' layer collection is already fully (re)synced and valid
+ * regarding current Collection hierarchy.
+ *
+ * It will process all the children collections of the collection from the given 'parent' layer,
+ * re-use or create layer collections for each of them, and ensure orders also match.
+ *
+ * Then it will ensure that the objects owned by the given parent collection have a proper base.
+ *
+ * NOTE: This process is recursive.
+ */
- /* Add layer collections for any new scene collections, and ensure order is the same. */
+ /* Temporary storage for all valid (new or reused) children layers. */
ListBase new_lb_layer = {NULL, NULL};
- LISTBASE_FOREACH (const CollectionChild *, child, lb_collections) {
- Collection *collection = child->collection;
- LayerCollection *lc = BLI_findptr(
- lb_layer_collections, collection, offsetof(LayerCollection, collection));
+ BLI_assert(layer_resync->is_used);
+
+ LISTBASE_FOREACH (CollectionChild *, child, &layer_resync->collection->children) {
+ Collection *child_collection = child->collection;
+ LayerCollectionResync *child_layer_resync = layer_collection_resync_find(layer_resync,
+ child_collection);
+
+ if (child_layer_resync != NULL) {
+ BLI_assert(child_layer_resync->collection != NULL);
+ BLI_assert(child_layer_resync->layer != NULL);
+ BLI_assert(child_layer_resync->is_usable);
+
+ if (child_layer_resync->is_used) {
+ CLOG_INFO(&LOG,
+ 4,
+ "Found same existing LayerCollection for %s as child of %s",
+ child_collection->id.name,
+ layer_resync->collection->id.name);
+ }
+ else {
+ CLOG_INFO(&LOG,
+ 4,
+ "Found a valid unused LayerCollection for %s as child of %s, re-using it",
+ child_collection->id.name,
+ layer_resync->collection->id.name);
+ }
+
+ child_layer_resync->is_used = true;
- if (lc) {
- BLI_remlink(lb_layer_collections, lc);
- BLI_addtail(&new_lb_layer, lc);
+ /* NOTE: Do not move the resync wrapper to match the new layer hierarchy, so that the old
+ * parenting info remains available. In case a search for a valid layer in the children of
+ * the current is required again, the old parenting hierarchy is needed as reference, not the
+ * new one.
+ */
+ BLI_remlink(&child_layer_resync->parent_layer_resync->layer->layer_collections,
+ child_layer_resync->layer);
+ BLI_addtail(&new_lb_layer, child_layer_resync->layer);
}
else {
- lc = layer_collection_add(&new_lb_layer, collection);
- lc->flag = parent_exclude;
+ CLOG_INFO(&LOG,
+ 4,
+ "No available LayerCollection for %s as child of %s, creating a new one",
+ child_collection->id.name,
+ layer_resync->collection->id.name);
+
+ LayerCollection *child_layer = layer_collection_add(&new_lb_layer, child_collection);
+ child_layer->flag = parent_layer_flag;
+
+ child_layer_resync = BLI_mempool_calloc(layer_resync_mempool);
+ child_layer_resync->collection = child_collection;
+ child_layer_resync->layer = child_layer;
+ child_layer_resync->is_usable = true;
+ child_layer_resync->is_used = true;
+ child_layer_resync->is_valid_as_child = true;
+ child_layer_resync->is_valid_as_parent = true;
+ /* NOTE: Needs to be added to the layer_resync hierarchy so that the resync wrapper gets
+ * freed at the end. */
+ child_layer_resync->parent_layer_resync = layer_resync;
+ BLI_addtail(&layer_resync->children_layer_resync, child_layer_resync);
}
- unsigned short local_collections_bits = parent_local_collections_bits &
- lc->local_collections_bits;
+ LayerCollection *child_layer = child_layer_resync->layer;
+
+ const ushort child_local_collections_bits = parent_local_collections_bits &
+ child_layer->local_collections_bits;
/* Tag linked collection as a weak reference so we keep the layer
* collection pointer on file load and remember exclude state. */
- id_lib_indirect_weak_link(&collection->id);
+ id_lib_indirect_weak_link(&child_collection->id);
/* Collection restrict is inherited. */
- short child_restrict = parent_restrict;
+ short child_collection_restrict = parent_collection_restrict;
short child_layer_restrict = parent_layer_restrict;
- if (!(collection->flag & COLLECTION_IS_MASTER)) {
- child_restrict |= collection->flag;
- child_layer_restrict |= lc->flag;
+ if (!(child_collection->flag & COLLECTION_IS_MASTER)) {
+ child_collection_restrict |= child_collection->flag;
+ child_layer_restrict |= child_layer->flag;
}
/* Sync child collections. */
layer_collection_sync(view_layer,
- &collection->children,
- &lc->layer_collections,
- new_object_bases,
- lc->flag,
- child_restrict,
+ child_layer_resync,
+ layer_resync_mempool,
+ r_lb_new_object_bases,
+ child_layer->flag,
+ child_collection_restrict,
child_layer_restrict,
- local_collections_bits);
+ child_local_collections_bits);
- /* Layer collection exclude is not inherited, we can skip the remaining process, including
- * object bases synchronization. */
- lc->runtime_flag = 0;
- if (lc->flag & LAYER_COLLECTION_EXCLUDE) {
+ /* Layer collection exclude is not inherited. */
+ child_layer->runtime_flag = 0;
+ if (child_layer->flag & LAYER_COLLECTION_EXCLUDE) {
continue;
}
/* We separate restrict viewport and visible view layer because a layer collection can be
* hidden in the view layer yet (locally) visible in a viewport (if it is not restricted). */
- if (child_restrict & COLLECTION_RESTRICT_VIEWPORT) {
- lc->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT;
+ if (child_collection_restrict & COLLECTION_RESTRICT_VIEWPORT) {
+ child_layer->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT;
}
- if (((lc->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) &&
+ if (((child_layer->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) &&
((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) {
- lc->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER;
- }
-
- layer_collection_objects_sync(view_layer,
- lc,
- new_object_bases,
- child_restrict,
- child_layer_restrict,
- local_collections_bits);
- }
-
- /* Free potentially remaining unused layer collections in old list.
- * NOTE: While this does not happen in typical situations, some corner cases (like remapping
- * several different collections to a single one) can lead to this list having extra unused
- * items. */
- LISTBASE_FOREACH_MUTABLE (LayerCollection *, lc, lb_layer_collections) {
- if (lc == view_layer->active_collection) {
- view_layer->active_collection = NULL;
+ child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER;
}
-
- /* Free recursively. */
- layer_collection_free(view_layer, lc);
- BLI_freelinkN(lb_layer_collections, lc);
}
- BLI_assert(BLI_listbase_is_empty(lb_layer_collections));
/* Replace layer collection list with new one. */
- *lb_layer_collections = new_lb_layer;
- BLI_assert(BLI_listbase_count(lb_collections) == BLI_listbase_count(lb_layer_collections));
+ layer_resync->layer->layer_collections = new_lb_layer;
+ BLI_assert(BLI_listbase_count(&layer_resync->collection->children) ==
+ BLI_listbase_count(&new_lb_layer));
+
+ /* Update bases etc. for objects. */
+ layer_collection_objects_sync(view_layer,
+ layer_resync->layer,
+ r_lb_new_object_bases,
+ parent_collection_restrict,
+ parent_layer_restrict,
+ parent_local_collections_bits);
}
/**
@@ -932,11 +1181,21 @@ static void layer_collection_sync(ViewLayer *view_layer,
*/
void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
{
+ if (no_resync) {
+ return;
+ }
+
if (!scene->master_collection) {
/* Happens for old files that don't have versioning applied yet. */
return;
}
+ /* 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. */
+ if (BLI_listbase_is_empty(&view_layer->layer_collections)) {
+ layer_collection_add(&view_layer->layer_collections, scene->master_collection);
+ }
+
/* Free cache. */
MEM_SAFE_FREE(view_layer->object_bases_array);
@@ -951,21 +1210,29 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
base->flag_from_collection &= ~g_base_collection_flags;
}
+ /* Generate temporary data representing the old layers hierarchy, and how well it matches the
+ * new collections hierarchy. */
+ BLI_mempool *layer_resync_mempool = BLI_mempool_create(
+ sizeof(LayerCollectionResync), 1024, 1024, BLI_MEMPOOL_NOP);
+ LayerCollectionResync *master_layer_resync = layer_collection_resync_create_recurse(
+ NULL, view_layer->layer_collections.first, layer_resync_mempool);
+
/* Generate new layer connections and object bases when collections changed. */
- CollectionChild child = {.next = NULL, .prev = NULL, .collection = scene->master_collection};
- const ListBase collections = {.first = &child, .last = &child};
ListBase new_object_bases = {.first = NULL, .last = NULL};
-
const short parent_exclude = 0, parent_restrict = 0, parent_layer_restrict = 0;
layer_collection_sync(view_layer,
- &collections,
- &view_layer->layer_collections,
+ master_layer_resync,
+ layer_resync_mempool,
&new_object_bases,
parent_exclude,
parent_restrict,
parent_layer_restrict,
~(0));
+ layer_collection_resync_unused_layers_free(view_layer, master_layer_resync);
+ BLI_mempool_destroy(layer_resync_mempool);
+ master_layer_resync = NULL;
+
/* Any remaining object bases are to be removed. */
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (view_layer->basact == base) {
@@ -996,6 +1263,10 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
void BKE_scene_collection_sync(const Scene *scene)
{
+ if (no_resync) {
+ return;
+ }
+
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
BKE_layer_collection_sync(scene, view_layer);
}
@@ -1003,6 +1274,10 @@ void BKE_scene_collection_sync(const Scene *scene)
void BKE_main_collection_sync(const Main *bmain)
{
+ if (no_resync) {
+ return;
+ }
+
/* TODO: if a single collection changed, figure out which
* scenes it belongs to and only update those. */
@@ -1017,6 +1292,10 @@ void BKE_main_collection_sync(const Main *bmain)
void BKE_main_collection_sync_remap(const Main *bmain)
{
+ if (no_resync) {
+ return;
+ }
+
/* On remapping of object or collection pointers free caches. */
/* TODO: try to make this faster */
@@ -1236,7 +1515,7 @@ void BKE_layer_collection_isolate_global(Scene *scene,
bool hide_it = extend && (lc->runtime_flag & LAYER_COLLECTION_VISIBLE_VIEW_LAYER);
if (!extend) {
- /* Hide all collections . */
+ /* Hide all collections. */
LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) {
layer_collection_flag_set_recursive(lc_iter, LAYER_COLLECTION_HIDE);
}
@@ -1319,6 +1598,10 @@ static void layer_collection_local_sync(ViewLayer *view_layer,
void BKE_layer_collection_local_sync(ViewLayer *view_layer, const View3D *v3d)
{
+ if (no_resync) {
+ return;
+ }
+
const unsigned short local_collections_uuid = v3d->local_collections_uuid;
/* Reset flags and set the bases visible by default. */
@@ -1336,6 +1619,10 @@ void BKE_layer_collection_local_sync(ViewLayer *view_layer, const View3D *v3d)
*/
void BKE_layer_collection_local_sync_all(const Main *bmain)
{
+ if (no_resync) {
+ return;
+ }
+
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 9871bf5dc83..8e67547b719 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -772,7 +772,11 @@ static void lib_override_library_create_post_process(Main *bmain,
/* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we
* do not do anything about it. */
- BKE_main_collection_sync(bmain);
+ /* We need to use the `_remap` version here as we prevented any LayerCollection resync during the
+ * whole liboverride resyncing, which involves a lot of ID remapping.
+ *
+ * Otherwise, cached Base GHash e.g. can contain invalid stale data. */
+ BKE_main_collection_sync_remap(bmain);
/* We create a set of all objects referenced into the scene by its hierarchy of collections.
* NOTE: This is different that the list of bases, since objects in excluded collections etc.
@@ -1013,6 +1017,15 @@ bool BKE_lib_override_library_resync(Main *bmain,
ID *id_root_reference = id_root->override_library->reference;
+ if (id_root_reference->tag & LIB_TAG_MISSING) {
+ BKE_reportf(reports != NULL ? reports->reports : NULL,
+ RPT_ERROR,
+ "impossible to resync data-block %s and its dependencies, as its linked reference "
+ "is missing",
+ id_root->name + 2);
+ return false;
+ }
+
BKE_main_relations_create(bmain, 0);
LibOverrideGroupTagData data = {.bmain = bmain,
.scene = scene,
@@ -1722,6 +1735,11 @@ void BKE_lib_override_library_main_resync(Main *bmain,
COLLECTION_RESTRICT_RENDER;
}
+ /* Necessary to improve performances, and prevent layers matching override sub-collections to be
+ * lost when re-syncing the parent override collection.
+ * Ref. T73411. */
+ BKE_layer_collection_resync_forbid();
+
int library_indirect_level = lib_override_libraries_index_define(bmain);
while (library_indirect_level >= 0) {
/* Update overrides from each indirect level separately. */
@@ -1734,6 +1752,8 @@ void BKE_lib_override_library_main_resync(Main *bmain,
library_indirect_level--;
}
+ BKE_layer_collection_resync_allow();
+
/* 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);
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index 655b6d3732c..bb33f5f9f87 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -38,6 +38,7 @@
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
+#include "BKE_main_idmap.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -194,6 +195,10 @@ void BKE_main_free(Main *mainvar)
BKE_main_relations_free(mainvar);
}
+ if (mainvar->id_map) {
+ BKE_main_idmap_destroy(mainvar->id_map);
+ }
+
BLI_spin_end((SpinLock *)mainvar->lock);
MEM_freeN(mainvar->lock);
MEM_freeN(mainvar);
diff --git a/source/blender/blenkernel/intern/main_idmap.c b/source/blender/blenkernel/intern/main_idmap.c
index 1d362db4432..c75365a788d 100644
--- a/source/blender/blenkernel/intern/main_idmap.c
+++ b/source/blender/blenkernel/intern/main_idmap.c
@@ -21,6 +21,7 @@
#include "BLI_ghash.h"
#include "BLI_listbase.h"
+#include "BLI_mempool.h"
#include "BLI_utildefines.h"
#include "DNA_ID.h"
@@ -49,17 +50,15 @@
* \{ */
struct IDNameLib_Key {
- /** ``ID.name + 2``: without the ID type prefix, since each id type gets its own 'map' */
+ /** `ID.name + 2`: without the ID type prefix, since each id type gets its own 'map'. */
const char *name;
- /** ``ID.lib``: */
+ /** `ID.lib`: */
const Library *lib;
};
struct IDNameLib_TypeMap {
GHash *map;
short id_type;
- /* only for storage of keys in the ghash, avoid many single allocs */
- struct IDNameLib_Key *keys;
};
/**
@@ -71,6 +70,9 @@ struct IDNameLib_Map {
struct Main *bmain;
struct GSet *valid_id_pointers;
int idmap_types;
+
+ /* For storage of keys for the TypeMap ghash, avoids many single allocs. */
+ BLI_mempool *type_maps_keys_pool;
};
static struct IDNameLib_TypeMap *main_idmap_from_idcode(struct IDNameLib_Map *id_map,
@@ -115,6 +117,7 @@ struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain,
BLI_assert(type_map->id_type != 0);
}
BLI_assert(index == INDEX_ID_MAX);
+ id_map->type_maps_keys_pool = NULL;
if (idmap_types & MAIN_IDMAP_TYPE_UUID) {
ID *id;
@@ -148,6 +151,60 @@ struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain,
return id_map;
}
+void BKE_main_idmap_insert_id(struct IDNameLib_Map *id_map, ID *id)
+{
+ if (id_map->idmap_types & MAIN_IDMAP_TYPE_NAME) {
+ const short id_type = GS(id->name);
+ struct IDNameLib_TypeMap *type_map = main_idmap_from_idcode(id_map, id_type);
+
+ /* No need to do anything if map has not been lazily created yet. */
+ if (LIKELY(type_map != NULL) && type_map->map != NULL) {
+ BLI_assert(id_map->type_maps_keys_pool != NULL);
+
+ struct IDNameLib_Key *key = BLI_mempool_alloc(id_map->type_maps_keys_pool);
+ key->name = id->name + 2;
+ key->lib = id->lib;
+ BLI_ghash_insert(type_map->map, key, id);
+ }
+ }
+
+ if (id_map->idmap_types & MAIN_IDMAP_TYPE_UUID) {
+ BLI_assert(id_map->uuid_map != NULL);
+ BLI_assert(id->session_uuid != MAIN_ID_SESSION_UUID_UNSET);
+ void **id_ptr_v;
+ const bool existing_key = BLI_ghash_ensure_p(
+ id_map->uuid_map, POINTER_FROM_UINT(id->session_uuid), &id_ptr_v);
+ BLI_assert(existing_key == false);
+ UNUSED_VARS_NDEBUG(existing_key);
+
+ *id_ptr_v = id;
+ }
+}
+
+void BKE_main_idmap_remove_id(struct IDNameLib_Map *id_map, ID *id)
+{
+ if (id_map->idmap_types & MAIN_IDMAP_TYPE_NAME) {
+ const short id_type = GS(id->name);
+ struct IDNameLib_TypeMap *type_map = main_idmap_from_idcode(id_map, id_type);
+
+ /* No need to do anything if map has not been lazily created yet. */
+ if (LIKELY(type_map != NULL) && type_map->map != NULL) {
+ BLI_assert(id_map->type_maps_keys_pool != NULL);
+
+ /* NOTE: We cannot free the key from the MemPool here, would need new API from GHash to also
+ * retrieve key pointer. Not a big deal for now */
+ BLI_ghash_remove(type_map->map, &(struct IDNameLib_Key){id->name + 2, id->lib}, NULL, NULL);
+ }
+ }
+
+ if (id_map->idmap_types & MAIN_IDMAP_TYPE_UUID) {
+ BLI_assert(id_map->uuid_map != NULL);
+ BLI_assert(id->session_uuid != MAIN_ID_SESSION_UUID_UNSET);
+
+ BLI_ghash_remove(id_map->uuid_map, POINTER_FROM_UINT(id->session_uuid), NULL, NULL);
+ }
+}
+
struct Main *BKE_main_idmap_main_get(struct IDNameLib_Map *id_map)
{
return id_map->bmain;
@@ -181,20 +238,17 @@ ID *BKE_main_idmap_lookup_name(struct IDNameLib_Map *id_map,
return NULL;
}
- /* lazy init */
+ /* Lazy init. */
if (type_map->map == NULL) {
- ListBase *lb = which_libbase(id_map->bmain, id_type);
- const int lb_len = BLI_listbase_count(lb);
- if (lb_len == 0) {
- return NULL;
+ if (id_map->type_maps_keys_pool == NULL) {
+ id_map->type_maps_keys_pool = BLI_mempool_create(
+ sizeof(struct IDNameLib_Key), 1024, 1024, BLI_MEMPOOL_NOP);
}
- type_map->map = BLI_ghash_new_ex(idkey_hash, idkey_cmp, __func__, lb_len);
- type_map->keys = MEM_mallocN(sizeof(struct IDNameLib_Key) * lb_len, __func__);
-
- GHash *map = type_map->map;
- struct IDNameLib_Key *key = type_map->keys;
- for (ID *id = lb->first; id; id = id->next, key++) {
+ GHash *map = type_map->map = BLI_ghash_new(idkey_hash, idkey_cmp, __func__);
+ ListBase *lb = which_libbase(id_map->bmain, id_type);
+ for (ID *id = lb->first; id; id = id->next) {
+ struct IDNameLib_Key *key = BLI_mempool_alloc(id_map->type_maps_keys_pool);
key->name = id->name + 2;
key->lib = id->lib;
BLI_ghash_insert(map, key, id);
@@ -235,14 +289,19 @@ void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map)
if (type_map->map) {
BLI_ghash_free(type_map->map, NULL, NULL);
type_map->map = NULL;
- MEM_freeN(type_map->keys);
}
}
+ if (id_map->type_maps_keys_pool != NULL) {
+ BLI_mempool_destroy(id_map->type_maps_keys_pool);
+ id_map->type_maps_keys_pool = NULL;
+ }
}
if (id_map->idmap_types & MAIN_IDMAP_TYPE_UUID) {
BLI_ghash_free(id_map->uuid_map, NULL, NULL);
}
+ BLI_assert(id_map->type_maps_keys_pool == NULL);
+
if (id_map->valid_id_pointers != NULL) {
BLI_gset_free(id_map->valid_id_pointers, NULL);
}
diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c
index af0047680f2..81c161a4a7d 100644
--- a/source/blender/blenkernel/intern/mask_rasterize.c
+++ b/source/blender/blenkernel/intern/mask_rasterize.c
@@ -937,7 +937,7 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle,
ListBase isect_remedgebase = {NULL, NULL};
/* now we have all the splines */
- face_coords = MEM_mallocN((sizeof(float[3])) * sf_vert_tot, "maskrast_face_coords");
+ face_coords = MEM_mallocN(sizeof(float[3]) * sf_vert_tot, "maskrast_face_coords");
/* init bounds */
BLI_rctf_init_minmax(&bounds);
diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c
index 760febaca91..9dd583b4c6b 100644
--- a/source/blender/blenkernel/intern/mball_tessellate.c
+++ b/source/blender/blenkernel/intern/mball_tessellate.c
@@ -305,7 +305,7 @@ static void build_bvh_spatial(PROCESS *process,
/**
* Computes density from given metaball at given position.
- * Metaball equation is: ``(1 - r^2 / R^2)^3 * s``
+ * Metaball equation is: `(1 - r^2 / R^2)^3 * s`
*
* r = distance from center
* R = metaball radius
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index c4186ad3180..8d74002ad79 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -312,7 +312,7 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
mesh->totselect = 0;
}
- if ((BLO_read_requires_endian_switch(reader)) && mesh->tface) {
+ if (BLO_read_requires_endian_switch(reader) && mesh->tface) {
TFace *tf = mesh->tface;
for (int i = 0; i < mesh->totface; i++, tf++) {
BLI_endian_switch_uint32_array(tf->col, 4);
@@ -932,10 +932,6 @@ void BKE_mesh_copy_parameters(Mesh *me_dst, const Mesh *me_src)
copy_v3_v3(me_dst->loc, me_src->loc);
copy_v3_v3(me_dst->size, me_src->size);
- /* Some callers call this on existing meshes, so free the existing vertex groups first. */
- BLI_freelistN(&me_dst->vertex_group_names);
- BKE_defgroup_copy_list(&me_dst->vertex_group_names, &me_src->vertex_group_names);
-
me_dst->vertex_group_active_index = me_src->vertex_group_active_index;
}
@@ -952,6 +948,10 @@ void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src)
BKE_mesh_copy_parameters(me_dst, me_src);
+ /* Copy vertex group names. */
+ BLI_assert(BLI_listbase_is_empty(&me_dst->vertex_group_names));
+ BKE_defgroup_copy_list(&me_dst->vertex_group_names, &me_src->vertex_group_names);
+
/* Copy materials. */
if (me_dst->mat != NULL) {
MEM_freeN(me_dst->mat);
@@ -1874,6 +1874,8 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac
}
mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL;
+ mesh->runtime.cd_dirty_poly &= ~CD_MASK_NORMAL;
+ mesh->runtime.cd_dirty_loop &= ~CD_MASK_NORMAL;
}
void BKE_mesh_calc_normals_split(Mesh *mesh)
diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
index 798d9562150..3086f117707 100644
--- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
@@ -97,6 +97,8 @@ class MeshesToIMeshInfo {
/* Transformation matrix to transform a coordinate in the corresponding
* Mesh to the local space of the first Mesh. */
Array<float4x4> to_target_transform;
+ /* For each input mesh, whether or not their transform is negative. */
+ Array<bool> has_negative_transform;
/* For each input mesh, how to remap the material slot numbers to
* the material slots in the first mesh. */
Span<Array<short>> material_remaps;
@@ -277,6 +279,7 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
r_info->mesh_edge_offset = Array<int>(nmeshes);
r_info->mesh_poly_offset = Array<int>(nmeshes);
r_info->to_target_transform = Array<float4x4>(nmeshes);
+ r_info->has_negative_transform = Array<bool>(nmeshes);
r_info->material_remaps = material_remaps;
int v = 0;
int e = 0;
@@ -309,6 +312,12 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
const float4x4 objn_mat = (obmats[mi] == nullptr) ? float4x4::identity() :
clean_obmat(*obmats[mi]);
r_info->to_target_transform[mi] = inv_target_mat * objn_mat;
+ r_info->has_negative_transform[mi] = objn_mat.is_negative();
+
+ /* All meshes 1 and up will be transformed into the local space of operand 0.
+ * Historical behavior of the modifier has been to flip the faces of any meshes
+ * that would have a negative transform if you do that. */
+ bool need_face_flip = r_info->has_negative_transform[mi] != r_info->has_negative_transform[0];
Vector<Vert *> verts(me->totvert);
Span<MVert> mverts = Span(me->mvert, me->totvert);
@@ -346,14 +355,21 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
for (const MPoly &poly : Span(me->mpoly, me->totpoly)) {
int flen = poly.totloop;
- face_vert.clear();
- face_edge_orig.clear();
+ face_vert.resize(flen);
+ face_edge_orig.resize(flen);
const MLoop *l = &me->mloop[poly.loopstart];
for (int i = 0; i < flen; ++i) {
int mverti = r_info->mesh_vert_offset[mi] + l->v;
const Vert *fv = r_info->mesh_to_imesh_vert[mverti];
- face_vert.append(fv);
- face_edge_orig.append(e + l->e);
+ if (need_face_flip) {
+ face_vert[flen - i - 1] = fv;
+ int iedge = i < flen - 1 ? flen - i - 2 : flen - 1;
+ face_edge_orig[iedge] = e + l->e;
+ }
+ else {
+ face_vert[i] = fv;
+ face_edge_orig[i] = e + l->e;
+ }
++l;
}
r_info->mesh_to_imesh_face[f] = arena.add_face(face_vert, f, face_edge_orig);
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index e777eb7ffe9..0e4fe91e577 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -212,54 +212,19 @@ static void make_edges_mdata_extend(
}
/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */
-/* return non-zero on error */
-int BKE_mesh_nurbs_to_mdata(Object *ob,
- MVert **r_allvert,
- int *r_totvert,
- MEdge **r_alledge,
- int *r_totedge,
- MLoop **r_allloop,
- MPoly **r_allpoly,
- int *r_totloop,
- int *r_totpoly)
-{
- ListBase disp = {NULL, NULL};
-
- if (ob->runtime.curve_cache) {
- disp = ob->runtime.curve_cache->disp;
- }
-
- return BKE_mesh_nurbs_displist_to_mdata(ob,
- &disp,
- r_allvert,
- r_totvert,
- r_alledge,
- r_totedge,
- r_allloop,
- r_allpoly,
- NULL,
- r_totloop,
- r_totpoly);
-}
-
-/* BMESH: this doesn't calculate all edges from polygons,
- * only free standing edges are calculated */
-
-/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */
/* use specified dispbase */
-int BKE_mesh_nurbs_displist_to_mdata(const Object *ob,
- const ListBase *dispbase,
- MVert **r_allvert,
- int *r_totvert,
- MEdge **r_alledge,
- int *r_totedge,
- MLoop **r_allloop,
- MPoly **r_allpoly,
- MLoopUV **r_alluv,
- int *r_totloop,
- int *r_totpoly)
+static int mesh_nurbs_displist_to_mdata(const Curve *cu,
+ const ListBase *dispbase,
+ MVert **r_allvert,
+ int *r_totvert,
+ MEdge **r_alledge,
+ int *r_totedge,
+ MLoop **r_allloop,
+ MPoly **r_allpoly,
+ MLoopUV **r_alluv,
+ int *r_totloop,
+ int *r_totpoly)
{
- const Curve *cu = ob->data;
MVert *mvert;
MPoly *mpoly;
MLoop *mloop;
@@ -272,7 +237,7 @@ int BKE_mesh_nurbs_displist_to_mdata(const Object *ob,
/* 2d polys are filled with DL_INDEX3 displists */
(CU_DO_2DFILL(cu) == false) ||
/* surf polys are never filled */
- (ob->type == OB_SURF));
+ BKE_curve_type_get(cu) == OB_SURF);
/* count */
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
@@ -527,17 +492,17 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *
MLoopUV *alluv = NULL;
int totvert, totedge, totloop, totpoly;
- if (BKE_mesh_nurbs_displist_to_mdata(ob,
- dispbase,
- &allvert,
- &totvert,
- &alledge,
- &totedge,
- &allloop,
- &allpoly,
- &alluv,
- &totloop,
- &totpoly) != 0) {
+ if (mesh_nurbs_displist_to_mdata(ob->data,
+ dispbase,
+ &allvert,
+ &totvert,
+ &alledge,
+ &totedge,
+ &allloop,
+ &allpoly,
+ &alluv,
+ &totloop,
+ &totpoly) != 0) {
/* Error initializing mdata. This often happens when curve is empty */
return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
}
@@ -571,7 +536,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *
return mesh;
}
-Mesh *BKE_mesh_new_nomain_from_curve(Object *ob)
+Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob)
{
ListBase disp = {NULL, NULL};
@@ -589,7 +554,6 @@ void BKE_mesh_from_nurbs_displist(
Object *ob1;
Mesh *me_eval = (Mesh *)ob->runtime.data_eval;
Mesh *me;
- Curve *cu;
MVert *allvert = NULL;
MEdge *alledge = NULL;
MLoop *allloop = NULL;
@@ -597,20 +561,20 @@ void BKE_mesh_from_nurbs_displist(
MPoly *allpoly = NULL;
int totvert, totedge, totloop, totpoly;
- cu = ob->data;
+ Curve *cu = ob->data;
if (me_eval == NULL) {
- if (BKE_mesh_nurbs_displist_to_mdata(ob,
- dispbase,
- &allvert,
- &totvert,
- &alledge,
- &totedge,
- &allloop,
- &allpoly,
- &alluv,
- &totloop,
- &totpoly) != 0) {
+ if (mesh_nurbs_displist_to_mdata(cu,
+ dispbase,
+ &allvert,
+ &totvert,
+ &alledge,
+ &totedge,
+ &allloop,
+ &allpoly,
+ &alluv,
+ &totloop,
+ &totpoly) != 0) {
/* Error initializing */
return;
}
@@ -702,18 +666,6 @@ void BKE_mesh_from_nurbs_displist(
}
}
-void BKE_mesh_from_nurbs(Main *bmain, Object *ob)
-{
- Curve *cu = (Curve *)ob->data;
- ListBase disp = {NULL, NULL};
-
- if (ob->runtime.curve_cache) {
- disp = ob->runtime.curve_cache->disp;
- }
-
- BKE_mesh_from_nurbs_displist(bmain, ob, &disp, cu->id.name, false);
-}
-
typedef struct EdgeLink {
struct EdgeLink *next, *prev;
void *edge;
@@ -1152,8 +1104,8 @@ static Mesh *mesh_new_from_curve_type_object(Object *object)
BKE_mesh_from_nurbs_displist(
NULL, temp_object, &temp_object->runtime.curve_cache->disp, curve->id.name + 2, true);
- /* BKE_mesh_from_nurbs changes the type to a mesh, check it worked. If it didn't the curve did
- * not have any segments or otherwise would have generated an empty mesh. */
+ /* BKE_mesh_from_nurbs_displist changes the type to a mesh, check it worked. If it didn't
+ * the curve did not have any segments or otherwise would have generated an empty mesh. */
if (temp_object->type != OB_MESH) {
BKE_id_free(NULL, temp_object->data);
BKE_id_free(NULL, temp_object);
diff --git a/source/blender/blenkernel/intern/mesh_iterators.c b/source/blender/blenkernel/intern/mesh_iterators.c
index 5ecf5ae316d..7a776b0ecb7 100644
--- a/source/blender/blenkernel/intern/mesh_iterators.c
+++ b/source/blender/blenkernel/intern/mesh_iterators.c
@@ -95,9 +95,14 @@ void BKE_mesh_foreach_mapped_vert(Mesh *mesh,
}
}
-/* Copied from cdDM_foreachMappedEdge */
+/**
+ * Copied from #cdDM_foreachMappedEdge.
+ * \param tot_edges: Number of original edges. Used to avoid calling the callback with invalid
+ * edge indices.
+ */
void BKE_mesh_foreach_mapped_edge(
Mesh *mesh,
+ const int tot_edges,
void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]),
void *userData)
{
@@ -138,7 +143,7 @@ void BKE_mesh_foreach_mapped_edge(
func(userData, orig, mv[med->v1].co, mv[med->v2].co);
}
}
- else {
+ else if (mesh->totedge == tot_edges) {
for (int i = 0; i < mesh->totedge; i++, med++) {
func(userData, i, mv[med->v1].co, mv[med->v2].co);
}
diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c
index ca6c60557a6..d28bb9c0744 100644
--- a/source/blender/blenkernel/intern/mesh_mapping.c
+++ b/source/blender/blenkernel/intern/mesh_mapping.c
@@ -539,8 +539,8 @@ void BKE_mesh_edge_poly_map_create(MeshElemMap **r_map,
* \param totfinal: The size of \a final_origindex
* \param final_origindex: The size of the final array.
*
- * \note ``totsource`` could be ``totpoly``,
- * ``totfinal`` could be ``tottessface`` and ``final_origindex`` its ORIGINDEX customdata.
+ * \note `totsource` could be `totpoly`,
+ * `totfinal` could be `tottessface` and `final_origindex` its ORIGINDEX custom-data.
* This would allow an MPoly to loop over its tessfaces.
*/
void BKE_mesh_origindex_map_create(MeshElemMap **r_map,
diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc
index 2fe132fc684..f496d6eada1 100644
--- a/source/blender/blenkernel/intern/mesh_normals.cc
+++ b/source/blender/blenkernel/intern/mesh_normals.cc
@@ -530,6 +530,36 @@ void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr,
lnors_spacearr->data_type = data_type;
}
+/**
+ * Utility for multi-threaded calculation that ensures
+ * `lnors_spacearr_tls` doesn't share memory with `lnors_spacearr`
+ * that would cause it not to be thread safe.
+ *
+ * \note This works as long as threads never operate on the same loops at once.
+ */
+void BKE_lnor_spacearr_tls_init(MLoopNorSpaceArray *lnors_spacearr,
+ MLoopNorSpaceArray *lnors_spacearr_tls)
+{
+ *lnors_spacearr_tls = *lnors_spacearr;
+ lnors_spacearr_tls->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+}
+
+/**
+ * Utility for multi-threaded calculation
+ * that merges `lnors_spacearr_tls` into `lnors_spacearr`.
+ */
+void BKE_lnor_spacearr_tls_join(MLoopNorSpaceArray *lnors_spacearr,
+ MLoopNorSpaceArray *lnors_spacearr_tls)
+{
+ BLI_assert(lnors_spacearr->data_type == lnors_spacearr_tls->data_type);
+ BLI_assert(lnors_spacearr->mem != lnors_spacearr_tls->mem);
+ lnors_spacearr->num_spaces += lnors_spacearr_tls->num_spaces;
+ BLI_memarena_merge(lnors_spacearr->mem, lnors_spacearr_tls->mem);
+ BLI_memarena_free(lnors_spacearr_tls->mem);
+ lnors_spacearr_tls->mem = nullptr;
+ BKE_lnor_spacearr_clear(lnors_spacearr_tls);
+}
+
void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr)
{
lnors_spacearr->num_spaces = 0;
diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
index 5a90d1f6ea5..8364b0ec024 100644
--- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c
+++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
@@ -21,22 +21,21 @@
* \ingroup bke
*/
-#include <ctype.h>
-#include <float.h>
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
+#include <cctype>
+#include <cfloat>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
#include "MEM_guardedalloc.h"
-#include "BLI_blenlib.h"
-#include "BLI_math.h"
-#include "BLI_utildefines.h"
+#include "BLI_array.hh"
+#include "BLI_float3.hh"
+#include "BLI_index_range.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
#include "BKE_bvhutils.h"
#include "BKE_customdata.h"
@@ -56,137 +55,43 @@
# include "quadriflow_capi.hpp"
#endif
-#ifdef WITH_OPENVDB
-struct OpenVDBLevelSet *BKE_mesh_remesh_voxel_ovdb_mesh_to_level_set_create(
- Mesh *mesh, struct OpenVDBTransform *transform)
-{
- BKE_mesh_runtime_looptri_recalc(mesh);
- const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh);
- MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(mesh),
- "remesh_looptri");
- BKE_mesh_runtime_verttri_from_looptri(
- verttri, mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(mesh));
-
- uint totfaces = BKE_mesh_runtime_looptri_len(mesh);
- uint totverts = mesh->totvert;
- float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts");
- uint *faces = (uint *)MEM_malloc_arrayN(totfaces * 3, sizeof(uint), "remesh_input_faces");
-
- for (uint i = 0; i < totverts; i++) {
- MVert *mvert = &mesh->mvert[i];
- verts[i * 3] = mvert->co[0];
- verts[i * 3 + 1] = mvert->co[1];
- verts[i * 3 + 2] = mvert->co[2];
- }
-
- for (uint i = 0; i < totfaces; i++) {
- MVertTri *vt = &verttri[i];
- faces[i * 3] = vt->tri[0];
- faces[i * 3 + 1] = vt->tri[1];
- faces[i * 3 + 2] = vt->tri[2];
- }
-
- struct OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, NULL);
- OpenVDBLevelSet_mesh_to_level_set(level_set, verts, faces, totverts, totfaces, transform);
-
- MEM_freeN(verts);
- MEM_freeN(faces);
- MEM_freeN(verttri);
-
- return level_set;
-}
-
-Mesh *BKE_mesh_remesh_voxel_ovdb_volume_to_mesh_nomain(struct OpenVDBLevelSet *level_set,
- double isovalue,
- double adaptivity,
- bool relax_disoriented_triangles)
-{
- struct OpenVDBVolumeToMeshData output_mesh;
- OpenVDBLevelSet_volume_to_mesh(
- level_set, &output_mesh, isovalue, adaptivity, relax_disoriented_triangles);
-
- Mesh *mesh = BKE_mesh_new_nomain(output_mesh.totvertices,
- 0,
- 0,
- (output_mesh.totquads * 4) + (output_mesh.tottriangles * 3),
- output_mesh.totquads + output_mesh.tottriangles);
-
- for (int i = 0; i < output_mesh.totvertices; i++) {
- copy_v3_v3(mesh->mvert[i].co, &output_mesh.vertices[i * 3]);
- }
-
- MPoly *mp = mesh->mpoly;
- MLoop *ml = mesh->mloop;
- for (int i = 0; i < output_mesh.totquads; i++, mp++, ml += 4) {
- mp->loopstart = (int)(ml - mesh->mloop);
- mp->totloop = 4;
-
- ml[0].v = output_mesh.quads[i * 4 + 3];
- ml[1].v = output_mesh.quads[i * 4 + 2];
- ml[2].v = output_mesh.quads[i * 4 + 1];
- ml[3].v = output_mesh.quads[i * 4];
- }
-
- for (int i = 0; i < output_mesh.tottriangles; i++, mp++, ml += 3) {
- mp->loopstart = (int)(ml - mesh->mloop);
- mp->totloop = 3;
-
- ml[0].v = output_mesh.triangles[i * 3 + 2];
- ml[1].v = output_mesh.triangles[i * 3 + 1];
- ml[2].v = output_mesh.triangles[i * 3];
- }
-
- BKE_mesh_calc_edges(mesh, false, false);
- BKE_mesh_calc_normals(mesh);
-
- MEM_freeN(output_mesh.quads);
- MEM_freeN(output_mesh.vertices);
-
- if (output_mesh.tottriangles > 0) {
- MEM_freeN(output_mesh.triangles);
- }
-
- return mesh;
-}
-#endif
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
#ifdef WITH_QUADRIFLOW
-static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh,
- int target_faces,
- int seed,
- bool preserve_sharp,
- bool preserve_boundary,
- bool adaptive_scale,
- void *update_cb,
- void *update_cb_data)
+static Mesh *remesh_quadriflow(const Mesh *input_mesh,
+ int target_faces,
+ int seed,
+ bool preserve_sharp,
+ bool preserve_boundary,
+ bool adaptive_scale,
+ void (*update_cb)(void *, float progress, int *cancel),
+ void *update_cb_data)
{
/* Ensure that the triangulated mesh data is up to data */
- BKE_mesh_runtime_looptri_recalc(input_mesh);
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh);
- /* Gather the required data for export to the internal quadiflow mesh format */
- MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh),
- "remesh_looptri");
+ /* Gather the required data for export to the internal quadriflow mesh format. */
+ MVertTri *verttri = (MVertTri *)MEM_callocN(
+ sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh), "remesh_looptri");
BKE_mesh_runtime_verttri_from_looptri(
verttri, input_mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(input_mesh));
- uint totfaces = BKE_mesh_runtime_looptri_len(input_mesh);
- uint totverts = input_mesh->totvert;
- float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts");
- uint *faces = (uint *)MEM_malloc_arrayN(totfaces * 3, sizeof(uint), "remesh_input_faces");
+ const int totfaces = BKE_mesh_runtime_looptri_len(input_mesh);
+ const int totverts = input_mesh->totvert;
+ Array<float3> verts(totverts);
+ Array<int> faces(totfaces * 3);
- for (uint i = 0; i < totverts; i++) {
- MVert *mvert = &input_mesh->mvert[i];
- verts[i * 3] = mvert->co[0];
- verts[i * 3 + 1] = mvert->co[1];
- verts[i * 3 + 2] = mvert->co[2];
+ for (const int i : IndexRange(totverts)) {
+ verts[i] = input_mesh->mvert[i].co;
}
- for (uint i = 0; i < totfaces; i++) {
- MVertTri *vt = &verttri[i];
- faces[i * 3] = vt->tri[0];
- faces[i * 3 + 1] = vt->tri[1];
- faces[i * 3 + 2] = vt->tri[2];
+ for (const int i : IndexRange(totfaces)) {
+ MVertTri &vt = verttri[i];
+ faces[i * 3] = vt.tri[0];
+ faces[i * 3 + 1] = vt.tri[1];
+ faces[i * 3 + 2] = vt.tri[2];
}
/* Fill out the required input data */
@@ -194,56 +99,52 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh,
qrd.totfaces = totfaces;
qrd.totverts = totverts;
- qrd.verts = verts;
- qrd.faces = faces;
+ qrd.verts = (float *)verts.data();
+ qrd.faces = faces.data();
qrd.target_faces = target_faces;
qrd.preserve_sharp = preserve_sharp;
qrd.preserve_boundary = preserve_boundary;
qrd.adaptive_scale = adaptive_scale;
- qrd.minimum_cost_flow = 0;
- qrd.aggresive_sat = 0;
+ qrd.minimum_cost_flow = false;
+ qrd.aggresive_sat = false;
qrd.rng_seed = seed;
- qrd.out_faces = NULL;
+ qrd.out_faces = nullptr;
/* Run the remesher */
QFLOW_quadriflow_remesh(&qrd, update_cb, update_cb_data);
- MEM_freeN(verts);
- MEM_freeN(faces);
MEM_freeN(verttri);
- if (qrd.out_faces == NULL) {
+ if (qrd.out_faces == nullptr) {
/* The remeshing was canceled */
- return NULL;
+ return nullptr;
}
if (qrd.out_totfaces == 0) {
/* Meshing failed */
MEM_freeN(qrd.out_faces);
MEM_freeN(qrd.out_verts);
- return NULL;
+ return nullptr;
}
/* Construct the new output mesh */
- Mesh *mesh = BKE_mesh_new_nomain(
- qrd.out_totverts, 0, 0, (qrd.out_totfaces * 4), qrd.out_totfaces);
+ Mesh *mesh = BKE_mesh_new_nomain(qrd.out_totverts, 0, 0, qrd.out_totfaces * 4, qrd.out_totfaces);
- for (int i = 0; i < qrd.out_totverts; i++) {
+ for (const int i : IndexRange(qrd.out_totverts)) {
copy_v3_v3(mesh->mvert[i].co, &qrd.out_verts[i * 3]);
}
- MPoly *mp = mesh->mpoly;
- MLoop *ml = mesh->mloop;
- for (int i = 0; i < qrd.out_totfaces; i++, mp++, ml += 4) {
- mp->loopstart = (int)(ml - mesh->mloop);
- mp->totloop = 4;
-
- ml[0].v = qrd.out_faces[i * 4];
- ml[1].v = qrd.out_faces[i * 4 + 1];
- ml[2].v = qrd.out_faces[i * 4 + 2];
- ml[3].v = qrd.out_faces[i * 4 + 3];
+ for (const int i : IndexRange(qrd.out_totfaces)) {
+ MPoly &poly = mesh->mpoly[i];
+ const int loopstart = i * 4;
+ poly.loopstart = loopstart;
+ poly.totloop = 4;
+ mesh->mloop[loopstart].v = qrd.out_faces[loopstart];
+ mesh->mloop[loopstart + 1].v = qrd.out_faces[loopstart + 1];
+ mesh->mloop[loopstart + 2].v = qrd.out_faces[loopstart + 2];
+ mesh->mloop[loopstart + 3].v = qrd.out_faces[loopstart + 3];
}
BKE_mesh_calc_edges(mesh, false, false);
@@ -256,28 +157,27 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh,
}
#endif
-Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(Mesh *mesh,
- int target_faces,
- int seed,
- bool preserve_sharp,
- bool preserve_boundary,
- bool adaptive_scale,
- void *update_cb,
- void *update_cb_data)
+Mesh *BKE_mesh_remesh_quadriflow(const Mesh *mesh,
+ int target_faces,
+ int seed,
+ bool preserve_sharp,
+ bool preserve_boundary,
+ bool adaptive_scale,
+ void (*update_cb)(void *, float progress, int *cancel),
+ void *update_cb_data)
{
- Mesh *new_mesh = NULL;
#ifdef WITH_QUADRIFLOW
if (target_faces <= 0) {
target_faces = -1;
}
- new_mesh = BKE_mesh_remesh_quadriflow(mesh,
- target_faces,
- seed,
- preserve_sharp,
- preserve_boundary,
- adaptive_scale,
- update_cb,
- update_cb_data);
+ return remesh_quadriflow(mesh,
+ target_faces,
+ seed,
+ preserve_sharp,
+ preserve_boundary,
+ adaptive_scale,
+ update_cb,
+ update_cb_data);
#else
UNUSED_VARS(mesh,
target_faces,
@@ -287,55 +187,143 @@ Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(Mesh *mesh,
adaptive_scale,
update_cb,
update_cb_data);
+ return nullptr;
#endif
- return new_mesh;
}
-Mesh *BKE_mesh_remesh_voxel_to_mesh_nomain(Mesh *mesh,
- float voxel_size,
- float adaptivity,
- float isovalue)
+#ifdef WITH_OPENVDB
+static struct OpenVDBLevelSet *remesh_voxel_level_set_create(const Mesh *mesh,
+ struct OpenVDBTransform *transform)
+{
+ const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh);
+ MVertTri *verttri = (MVertTri *)MEM_callocN(
+ sizeof(*verttri) * BKE_mesh_runtime_looptri_len(mesh), "remesh_looptri");
+ BKE_mesh_runtime_verttri_from_looptri(
+ verttri, mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(mesh));
+
+ const int totfaces = BKE_mesh_runtime_looptri_len(mesh);
+ const int totverts = mesh->totvert;
+ Array<float3> verts(totverts);
+ Array<int> faces(totfaces * 3);
+
+ for (const int i : IndexRange(totverts)) {
+ verts[i] = mesh->mvert[i].co;
+ }
+
+ for (const int i : IndexRange(totfaces)) {
+ MVertTri &vt = verttri[i];
+ faces[i * 3] = vt.tri[0];
+ faces[i * 3 + 1] = vt.tri[1];
+ faces[i * 3 + 2] = vt.tri[2];
+ }
+
+ struct OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, nullptr);
+ OpenVDBLevelSet_mesh_to_level_set(
+ level_set, (const float *)verts.data(), faces.data(), totverts, totfaces, transform);
+
+ MEM_freeN(verttri);
+
+ return level_set;
+}
+
+static Mesh *remesh_voxel_volume_to_mesh(struct OpenVDBLevelSet *level_set,
+ double isovalue,
+ double adaptivity,
+ bool relax_disoriented_triangles)
+{
+ struct OpenVDBVolumeToMeshData output_mesh;
+ OpenVDBLevelSet_volume_to_mesh(
+ level_set, &output_mesh, isovalue, adaptivity, relax_disoriented_triangles);
+
+ Mesh *mesh = BKE_mesh_new_nomain(output_mesh.totvertices,
+ 0,
+ 0,
+ (output_mesh.totquads * 4) + (output_mesh.tottriangles * 3),
+ output_mesh.totquads + output_mesh.tottriangles);
+
+ for (const int i : IndexRange(output_mesh.totvertices)) {
+ copy_v3_v3(mesh->mvert[i].co, &output_mesh.vertices[i * 3]);
+ }
+
+ for (const int i : IndexRange(output_mesh.totquads)) {
+ MPoly &poly = mesh->mpoly[i];
+ const int loopstart = i * 4;
+ poly.loopstart = loopstart;
+ poly.totloop = 4;
+ mesh->mloop[loopstart].v = output_mesh.quads[loopstart];
+ mesh->mloop[loopstart + 1].v = output_mesh.quads[loopstart + 1];
+ mesh->mloop[loopstart + 2].v = output_mesh.quads[loopstart + 2];
+ mesh->mloop[loopstart + 3].v = output_mesh.quads[loopstart + 3];
+ }
+
+ const int triangle_poly_start = output_mesh.totquads;
+ const int triangle_loop_start = output_mesh.totquads * 4;
+ for (const int i : IndexRange(output_mesh.tottriangles)) {
+ MPoly &poly = mesh->mpoly[triangle_poly_start + i];
+ const int loopstart = triangle_loop_start + i * 3;
+ poly.loopstart = loopstart;
+ poly.totloop = 3;
+ mesh->mloop[loopstart].v = output_mesh.triangles[i * 3 + 2];
+ mesh->mloop[loopstart + 1].v = output_mesh.triangles[i * 3 + 1];
+ mesh->mloop[loopstart + 2].v = output_mesh.triangles[i * 3];
+ }
+
+ BKE_mesh_calc_edges(mesh, false, false);
+ BKE_mesh_calc_normals(mesh);
+
+ MEM_freeN(output_mesh.quads);
+ MEM_freeN(output_mesh.vertices);
+
+ if (output_mesh.tottriangles > 0) {
+ MEM_freeN(output_mesh.triangles);
+ }
+
+ return mesh;
+}
+#endif
+
+Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh,
+ const float voxel_size,
+ const float adaptivity,
+ const float isovalue)
{
- Mesh *new_mesh = NULL;
#ifdef WITH_OPENVDB
- struct OpenVDBLevelSet *level_set;
struct OpenVDBTransform *xform = OpenVDBTransform_create();
OpenVDBTransform_create_linear_transform(xform, (double)voxel_size);
- level_set = BKE_mesh_remesh_voxel_ovdb_mesh_to_level_set_create(mesh, xform);
- new_mesh = BKE_mesh_remesh_voxel_ovdb_volume_to_mesh_nomain(
+ struct OpenVDBLevelSet *level_set = remesh_voxel_level_set_create(mesh, xform);
+ Mesh *new_mesh = remesh_voxel_volume_to_mesh(
level_set, (double)isovalue, (double)adaptivity, false);
OpenVDBLevelSet_free(level_set);
OpenVDBTransform_free(xform);
+ return new_mesh;
#else
UNUSED_VARS(mesh, voxel_size, adaptivity, isovalue);
+ return nullptr;
#endif
- return new_mesh;
}
void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source)
{
- BVHTreeFromMesh bvhtree = {
- .nearest_callback = NULL,
- };
+ BVHTreeFromMesh bvhtree = {{nullptr}};
BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
- MVert *target_verts = CustomData_get_layer(&target->vdata, CD_MVERT);
+ MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
float *target_mask;
if (CustomData_has_layer(&target->vdata, CD_PAINT_MASK)) {
- target_mask = CustomData_get_layer(&target->vdata, CD_PAINT_MASK);
+ target_mask = (float *)CustomData_get_layer(&target->vdata, CD_PAINT_MASK);
}
else {
- target_mask = CustomData_add_layer(
- &target->vdata, CD_PAINT_MASK, CD_CALLOC, NULL, target->totvert);
+ target_mask = (float *)CustomData_add_layer(
+ &target->vdata, CD_PAINT_MASK, CD_CALLOC, nullptr, target->totvert);
}
float *source_mask;
if (CustomData_has_layer(&source->vdata, CD_PAINT_MASK)) {
- source_mask = CustomData_get_layer(&source->vdata, CD_PAINT_MASK);
+ source_mask = (float *)CustomData_get_layer(&source->vdata, CD_PAINT_MASK);
}
else {
- source_mask = CustomData_add_layer(
- &source->vdata, CD_PAINT_MASK, CD_CALLOC, NULL, source->totvert);
+ source_mask = (float *)CustomData_add_layer(
+ &source->vdata, CD_PAINT_MASK, CD_CALLOC, nullptr, source->totvert);
}
for (int i = 0; i < target->totvert; i++) {
@@ -354,30 +342,28 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source)
void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
{
- BVHTreeFromMesh bvhtree = {
- .nearest_callback = NULL,
- };
+ BVHTreeFromMesh bvhtree = {{nullptr}};
- const MPoly *target_polys = CustomData_get_layer(&target->pdata, CD_MPOLY);
- const MVert *target_verts = CustomData_get_layer(&target->vdata, CD_MVERT);
- const MLoop *target_loops = CustomData_get_layer(&target->ldata, CD_MLOOP);
+ const MPoly *target_polys = (const MPoly *)CustomData_get_layer(&target->pdata, CD_MPOLY);
+ const MVert *target_verts = (const MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
+ const MLoop *target_loops = (const MLoop *)CustomData_get_layer(&target->ldata, CD_MLOOP);
int *target_face_sets;
if (CustomData_has_layer(&target->pdata, CD_SCULPT_FACE_SETS)) {
- target_face_sets = CustomData_get_layer(&target->pdata, CD_SCULPT_FACE_SETS);
+ target_face_sets = (int *)CustomData_get_layer(&target->pdata, CD_SCULPT_FACE_SETS);
}
else {
- target_face_sets = CustomData_add_layer(
- &target->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, target->totpoly);
+ target_face_sets = (int *)CustomData_add_layer(
+ &target->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, nullptr, target->totpoly);
}
- int *source_face_sets;
+ const int *source_face_sets;
if (CustomData_has_layer(&source->pdata, CD_SCULPT_FACE_SETS)) {
- source_face_sets = CustomData_get_layer(&source->pdata, CD_SCULPT_FACE_SETS);
+ source_face_sets = (const int *)CustomData_get_layer(&source->pdata, CD_SCULPT_FACE_SETS);
}
else {
- source_face_sets = CustomData_add_layer(
- &source->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, source->totpoly);
+ source_face_sets = (const int *)CustomData_add_layer(
+ &source->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, nullptr, source->totpoly);
}
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(source);
@@ -401,11 +387,9 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
free_bvhtree_from_mesh(&bvhtree);
}
-void BKE_remesh_reproject_vertex_paint(Mesh *target, Mesh *source)
+void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source)
{
- BVHTreeFromMesh bvhtree = {
- .nearest_callback = NULL,
- };
+ BVHTreeFromMesh bvhtree = {{nullptr}};
BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
int tot_color_layer = CustomData_number_of_layers(&source->vdata, CD_PROP_COLOR);
@@ -413,11 +397,13 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, Mesh *source)
for (int layer_n = 0; layer_n < tot_color_layer; layer_n++) {
const char *layer_name = CustomData_get_layer_name(&source->vdata, CD_PROP_COLOR, layer_n);
CustomData_add_layer_named(
- &target->vdata, CD_PROP_COLOR, CD_CALLOC, NULL, target->totvert, layer_name);
+ &target->vdata, CD_PROP_COLOR, CD_CALLOC, nullptr, target->totvert, layer_name);
- MPropCol *target_color = CustomData_get_layer_n(&target->vdata, CD_PROP_COLOR, layer_n);
- MVert *target_verts = CustomData_get_layer(&target->vdata, CD_MVERT);
- MPropCol *source_color = CustomData_get_layer_n(&source->vdata, CD_PROP_COLOR, layer_n);
+ MPropCol *target_color = (MPropCol *)CustomData_get_layer_n(
+ &target->vdata, CD_PROP_COLOR, layer_n);
+ MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
+ const MPropCol *source_color = (const MPropCol *)CustomData_get_layer_n(
+ &source->vdata, CD_PROP_COLOR, layer_n);
for (int i = 0; i < target->totvert; i++) {
BVHTreeNearest nearest;
nearest.index = -1;
@@ -432,20 +418,16 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, Mesh *source)
free_bvhtree_from_mesh(&bvhtree);
}
-struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct Mesh *mesh)
+struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh)
{
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- BMesh *bm;
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+
+ const BMeshCreateParams bmesh_create_params = {true};
+ BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params);
+
+ BMeshFromMeshParams bmesh_from_mesh_params{};
+ bmesh_from_mesh_params.calc_face_normal = true;
+ BM_mesh_bm_from_me(bm, mesh, &bmesh_from_mesh_params);
BMVert *v;
BMEdge *ed, *ed_next;
@@ -456,8 +438,8 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct Mesh *mesh)
BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
BM_ITER_MESH_MUTABLE (f, f_next, &iter_a, bm, BM_FACES_OF_MESH) {
BMVert *v1, *v2;
- v1 = NULL;
- v2 = NULL;
+ v1 = nullptr;
+ v2 = nullptr;
BM_ITER_ELEM (v, &iter_b, f, BM_VERTS_OF_FACE) {
if (BM_vert_edge_count(v) == 3) {
if (v1) {
@@ -470,7 +452,7 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct Mesh *mesh)
}
if (v1 && v2 && (v1 != v2) && !BM_edge_exists(v1, v2)) {
BM_face_kill(bm, f);
- BMEdge *e = BM_edge_create(bm, v1, v2, NULL, BM_CREATE_NOP);
+ BMEdge *e = BM_edge_create(bm, v1, v2, nullptr, BM_CREATE_NOP);
BM_elem_flag_set(e, BM_ELEM_TAG, true);
}
}
@@ -532,13 +514,10 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct Mesh *mesh)
BM_ELEM_TAG);
BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
- Mesh *result = BKE_mesh_from_bmesh_nomain(bm,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }),
- mesh);
+ BMeshToMeshParams bmesh_to_mesh_params{};
+ bmesh_to_mesh_params.calc_object_remap = false;
+ Mesh *result = BKE_mesh_from_bmesh_nomain(bm, &bmesh_to_mesh_params, mesh);
- BKE_id_free(NULL, mesh);
BM_mesh_free(bm);
return result;
}
diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc
index bd46407d060..5388f6e530e 100644
--- a/source/blender/blenkernel/intern/mesh_sample.cc
+++ b/source/blender/blenkernel/intern/mesh_sample.cc
@@ -24,14 +24,6 @@
namespace blender::bke::mesh_surface_sample {
-Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
-{
- /* This only updates a cache and can be considered to be logically const. */
- const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
- const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
- return {looptris, looptris_len};
-}
-
template<typename T>
BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
@@ -39,7 +31,8 @@ BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
- const Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
@@ -85,7 +78,8 @@ BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
@@ -130,7 +124,8 @@ void sample_face_attribute(const Mesh &mesh,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
for (const int i : data_out.index_range()) {
const int looptri_index = looptri_indices[i];
@@ -172,7 +167,8 @@ Span<float3> MeshAttributeInterpolator::ensure_barycentric_coords()
}
bary_coords_.reinitialize(positions_.size());
- Span<MLoopTri> looptris = get_mesh_looptris(*mesh_);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_),
+ BKE_mesh_runtime_looptri_len(mesh_)};
for (const int i : bary_coords_.index_range()) {
const int looptri_index = looptri_indices_[i];
@@ -199,7 +195,8 @@ Span<float3> MeshAttributeInterpolator::ensure_nearest_weights()
}
nearest_weights_.reinitialize(positions_.size());
- Span<MLoopTri> looptris = get_mesh_looptris(*mesh_);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_),
+ BKE_mesh_runtime_looptri_len(mesh_)};
for (const int i : nearest_weights_.index_range()) {
const int looptri_index = looptri_indices_[i];
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 633ec1707f9..9888e23a7bd 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -506,7 +506,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
if (node->storage) {
/* could be handlerized at some point, now only 1 exception still */
- if ((ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY)) &&
+ if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) &&
ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) {
BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage);
}
@@ -1212,10 +1212,12 @@ static void update_typeinfo(Main *bmain,
FOREACH_NODETREE_END;
}
-/* Try to initialize all typeinfo in a node tree.
- * NB: In general undefined typeinfo is a perfectly valid case,
+/**
+ * Try to initialize all type-info in a node tree.
+ *
+ * \note In general undefined type-info is a perfectly valid case,
* the type may just be registered later.
- * In that case the update_typeinfo function will set typeinfo on registration
+ * In that case the update_typeinfo function will set type-info on registration
* and do necessary updates.
*/
void ntreeSetTypes(const struct bContext *C, bNodeTree *ntree)
@@ -2502,6 +2504,22 @@ bool nodeLinkIsHidden(const bNodeLink *link)
return nodeSocketIsHidden(link->fromsock) || nodeSocketIsHidden(link->tosock);
}
+/* Adjust the indices of links connected to the given multi input socket after deleting the link at
+ * `deleted_index`. This function also works if the link has not yet been deleted. */
+static void adjust_multi_input_indices_after_removed_link(bNodeTree *ntree,
+ bNodeSocket *sock,
+ int deleted_index)
+{
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ /* We only need to adjust those with a greater index, because the others will have the same
+ * index. */
+ if (link->tosock != sock || link->multi_input_socket_index <= deleted_index) {
+ continue;
+ }
+ link->multi_input_socket_index -= 1;
+ }
+}
+
void nodeInternalRelink(bNodeTree *ntree, bNode *node)
{
/* store link pointers in output sockets, for efficient lookup */
@@ -2535,10 +2553,18 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
ntree->update |= NTREE_UPDATE_LINKS;
}
else {
+ if (link->tosock->flag & SOCK_MULTI_INPUT) {
+ adjust_multi_input_indices_after_removed_link(
+ ntree, link->tosock, link->multi_input_socket_index);
+ }
nodeRemLink(ntree, link);
}
}
else {
+ if (link->tosock->flag & SOCK_MULTI_INPUT) {
+ adjust_multi_input_indices_after_removed_link(
+ ntree, link->tosock, link->multi_input_socket_index);
+ };
nodeRemLink(ntree, link);
}
}
@@ -2989,6 +3015,11 @@ void nodeUnlinkNode(bNodeTree *ntree, bNode *node)
}
if (lb) {
+ /* Only bother adjusting if the socket is not on the node we're deleting. */
+ if (link->tonode != node && link->tosock->flag & SOCK_MULTI_INPUT) {
+ adjust_multi_input_indices_after_removed_link(
+ ntree, link->tosock, link->multi_input_socket_index);
+ }
LISTBASE_FOREACH (bNodeSocket *, sock, lb) {
if (link->fromsock == sock || link->tosock == sock) {
nodeRemLink(ntree, link);
@@ -3143,7 +3174,7 @@ static void free_localized_node_groups(bNodeTree *ntree)
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- if ((ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) && node->id) {
+ if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id) {
bNodeTree *ngroup = (bNodeTree *)node->id;
ntreeFreeTree(ngroup);
MEM_freeN(ngroup);
@@ -3335,7 +3366,7 @@ bNodeTree *ntreeLocalize(bNodeTree *ntree)
ltree->id.tag |= LIB_TAG_LOCALIZED;
LISTBASE_FOREACH (bNode *, node, &ltree->nodes) {
- if ((ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) && node->id) {
+ if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id) {
node->id = (ID *)ntreeLocalize((bNodeTree *)node->id);
}
}
@@ -5117,9 +5148,11 @@ static void registerGeometryNodes()
register_node_type_geo_curve_primitive_star();
register_node_type_geo_curve_resample();
register_node_type_geo_curve_reverse();
+ register_node_type_geo_curve_set_handles();
register_node_type_geo_curve_subdivide();
register_node_type_geo_curve_to_mesh();
register_node_type_geo_curve_to_points();
+ register_node_type_geo_curve_trim();
register_node_type_geo_delete_geometry();
register_node_type_geo_edge_split();
register_node_type_geo_input_material();
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 941db80b76c..89de37d6e4b 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1653,7 +1653,7 @@ static void object_update_from_subsurf_ccg(Object *object)
*
* All this is defeating all the designs we need to follow to allow safe
* threaded evaluation, but this is as good as we can make it within the
- * current sculpt//evaluated mesh design. This is also how we've survived
+ * current sculpt/evaluated mesh design. This is also how we've survived
* with old DerivedMesh based solutions. So, while this is all wrong and
* needs reconsideration, doesn't seem to be a big stopper for real
* production artists.
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 7cdea14e9bd..7e15ac5de5d 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -388,31 +388,12 @@ void BKE_object_batch_cache_dirty_tag(Object *ob)
BKE_object_data_batch_cache_dirty_tag(ob->data);
}
-void BKE_object_data_eval_batch_cache_dirty_tag(Depsgraph *depsgraph, ID *object_data)
-{
- DEG_debug_print_eval(depsgraph, __func__, object_data->name, object_data);
- BKE_object_data_batch_cache_dirty_tag(object_data);
-}
-
-void BKE_object_data_eval_batch_cache_deform_tag(Depsgraph *depsgraph, ID *object_data)
-{
- DEG_debug_print_eval(depsgraph, __func__, object_data->name, object_data);
- switch (GS(object_data->name)) {
- case ID_ME:
- BKE_mesh_batch_cache_dirty_tag((Mesh *)object_data, BKE_MESH_BATCH_DIRTY_DEFORM);
- break;
- default:
- /* Only mesh is currently supported. Fallback to dirty all for other datablocks types. */
- BKE_object_data_batch_cache_dirty_tag(object_data);
- break;
- }
-}
-
void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob);
BLI_assert(ob->type != OB_ARMATURE);
BKE_object_handle_data_update(depsgraph, scene, ob);
+ BKE_object_batch_cache_dirty_tag(ob);
}
void BKE_object_eval_ptcache_reset(Depsgraph *depsgraph, Scene *scene, Object *object)
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 1af66fa090b..a1fa6aae1ce 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -880,7 +880,7 @@ static int palettecolor_compare_luminance(const void *a1, const void *a2)
void BKE_palette_sort_hsv(tPaletteColorHSV *color_array, const int totcol)
{
- /* Sort by Hue , Saturation and Value. */
+ /* Sort by Hue, Saturation and Value. */
qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_hsv);
}
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index db1fbb56125..f2f3c5d4ca6 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -4418,12 +4418,12 @@ void psys_get_texture(
if ((event & mtex->mapto) & PAMAP_TIME) {
/* the first time has to set the base value for time regardless of blend mode */
- if ((setvars & MAP_PA_TIME) == 0) {
+ if ((setvars & PAMAP_TIME) == 0) {
int flip = (mtex->timefac < 0.0f);
float timefac = fabsf(mtex->timefac);
ptex->time *= 1.0f - timefac;
ptex->time += timefac * ((flip) ? 1.0f - value : value);
- setvars |= MAP_PA_TIME;
+ setvars |= PAMAP_TIME;
}
else {
ptex->time = texture_value_blend(def, ptex->time, value, mtex->timefac, blend);
diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c
index d3d7f02ecad..93cffcf7164 100644
--- a/source/blender/blenkernel/intern/pbvh_bmesh.c
+++ b/source/blender/blenkernel/intern/pbvh_bmesh.c
@@ -1168,12 +1168,12 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
}
/**
- * The 2 new faces created and assigned to ``f_new`` have their
+ * The 2 new faces created and assigned to `f_new` have their
* verts & edges shuffled around.
*
* - faces wind anticlockwise in this example.
- * - original edge is ``(v1, v2)``
- * - original face is ``(v1, v2, v3)``
+ * - original edge is `(v1, v2)`
+ * - original face is `(v1, v2, v3)`
*
* <pre>
* + v3(v_opp)
@@ -1189,8 +1189,8 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
* (first) (second)
* </pre>
*
- * - f_new (first): ``v_tri=(v1, v4, v3), e_tri=(e1, e5, e4)``
- * - f_new (second): ``v_tri=(v4, v2, v3), e_tri=(e2, e3, e5)``
+ * - f_new (first): `v_tri=(v1, v4, v3), e_tri=(e1, e5, e4)`
+ * - f_new (second): `v_tri=(v4, v2, v3), e_tri=(e2, e3, e5)`
*/
/* Create two new faces */
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index cc5a8536a5a..3f75d0963c6 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -231,7 +231,6 @@ static void scene_init_data(ID *id)
/* Sequencer */
scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
- scene->toolsettings->snap_flag |= SCE_SNAP_SEQ;
for (size_t i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) {
scene->orientation_slots[i].index_custom = -1;
@@ -2105,7 +2104,7 @@ Object *BKE_scene_object_find_by_name(const Scene *scene, const char *name)
/**
* Sets the active scene, mainly used when running in background mode
- * (``--scene`` command line argument).
+ * (`--scene` command line argument).
* This is also called to set the scene directly, bypassing windowing code.
* Otherwise #WM_window_set_active_scene is used when changing scenes by the user.
*/
@@ -3242,9 +3241,9 @@ void BKE_scene_multiview_filepath_get(SceneRenderView *srv, const char *filepath
}
/**
- * When multiview is not used the filepath is as usual (e.g., ``Image.jpg``).
+ * When multiview is not used the filepath is as usual (e.g., `Image.jpg`).
* When multiview is on, even if only one view is enabled the view is incorporated
- * into the file name (e.g., ``Image_L.jpg``). That allows for the user to re-render
+ * into the file name (e.g., `Image_L.jpg`). That allows for the user to re-render
* individual views.
*/
void BKE_scene_multiview_view_filepath_get(const RenderData *rd,
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 73658c3184e..c3885b5dcf7 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -682,19 +682,13 @@ void BKE_area_region_free(SpaceType *st, ARegion *region)
BKE_area_region_panels_free(&region->panels);
LISTBASE_FOREACH (uiList *, uilst, &region->ui_lists) {
- if (uilst->dyn_data) {
- uiListDyn *dyn_data = uilst->dyn_data;
- if (dyn_data->items_filter_flags) {
- MEM_freeN(dyn_data->items_filter_flags);
- }
- if (dyn_data->items_filter_neworder) {
- MEM_freeN(dyn_data->items_filter_neworder);
- }
- MEM_freeN(dyn_data);
+ if (uilst->dyn_data && uilst->dyn_data->free_runtime_data_fn) {
+ uilst->dyn_data->free_runtime_data_fn(uilst);
}
if (uilst->properties) {
IDP_FreeProperty(uilst->properties);
}
+ MEM_SAFE_FREE(uilst->dyn_data);
}
if (region->gizmo_map != NULL) {
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
index 6234cdf87e2..a7caae967f6 100644
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -410,7 +410,7 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
const int index = offset - lengths.begin();
- const int next_index = (index == this->size() - 1) ? 0 : index + 1;
+ const int next_index = (index == this->evaluated_points_size() - 1) ? 0 : index + 1;
const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
const float factor = (length - previous_length) / (lengths[index] - previous_length);
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index cff2c5cc562..a1b45c2ac7d 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -423,7 +423,7 @@ static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *
/* get some info from CCGSubSurf */
totface = ccgSubSurf_getNumFaces(uvss);
- /* edgeSize = ccgSubSurf_getEdgeSize(uvss); */ /*UNUSED*/
+ // edgeSize = ccgSubSurf_getEdgeSize(uvss); /* UNUSED */
gridSize = ccgSubSurf_getGridSize(uvss);
gridFaces = gridSize - 1;
diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c
index d9f02ce4c75..4581d410444 100644
--- a/source/blender/blenkernel/intern/unit.c
+++ b/source/blender/blenkernel/intern/unit.c
@@ -1303,7 +1303,7 @@ const char *BKE_unit_identifier_get(const void *usys_pt, int index)
{
const bUnitDef *unit = ((const bUnitCollection *)usys_pt)->units + index;
if (unit->identifier == NULL) {
- BLI_assert(false && "identifier for this unit is not specified yet");
+ BLI_assert_msg(0, "identifier for this unit is not specified yet");
}
return unit->identifier;
}
diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c
index 533107b2bf6..059dc68b1dc 100644
--- a/source/blender/blenkernel/intern/workspace.c
+++ b/source/blender/blenkernel/intern/workspace.c
@@ -29,6 +29,7 @@
#include "BLT_translation.h"
+#include "BKE_asset.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
@@ -53,6 +54,13 @@
/* -------------------------------------------------------------------- */
+static void workspace_init_data(ID *id)
+{
+ WorkSpace *workspace = (WorkSpace *)id;
+
+ BKE_asset_library_reference_init_default(&workspace->asset_library);
+}
+
static void workspace_free_data(ID *id)
{
WorkSpace *workspace = (WorkSpace *)id;
@@ -180,7 +188,7 @@ IDTypeInfo IDType_ID_WS = {
.translation_context = BLT_I18NCONTEXT_ID_WORKSPACE,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA,
- .init_data = NULL,
+ .init_data = workspace_init_data,
.copy_data = NULL,
.free_data = workspace_free_data,
.make_local = NULL,
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 560ae30967f..9b3103a638b 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -1747,9 +1747,10 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
rd->ffcodecdata.type = FFMPEG_MPEG2;
rd->ffcodecdata.video_bitrate = 6000;
- /* Don't set resolution, see T21351.
- * rd->xsch = 720;
- * rd->ysch = isntsc ? 480 : 576; */
+# if 0 /* Don't set resolution, see T21351. */
+ rd->xsch = 720;
+ rd->ysch = isntsc ? 480 : 576;
+# endif
rd->ffcodecdata.gop_size = isntsc ? 18 : 15;
rd->ffcodecdata.rc_max_rate = 9000;
diff --git a/source/blender/blenlib/BLI_alloca.h b/source/blender/blenlib/BLI_alloca.h
index 92567ed35fa..9e3bd98537d 100644
--- a/source/blender/blenlib/BLI_alloca.h
+++ b/source/blender/blenlib/BLI_alloca.h
@@ -28,9 +28,9 @@
#if defined(__GNUC__) || defined(__clang__)
# if defined(__cplusplus) && (__cplusplus > 199711L)
-# define BLI_array_alloca(arr, realsize) (decltype(arr)) alloca(sizeof(*arr) * (realsize))
+# define BLI_array_alloca(arr, realsize) (decltype(arr))alloca(sizeof(*arr) * (realsize))
# else
-# define BLI_array_alloca(arr, realsize) (typeof(arr)) alloca(sizeof(*arr) * (realsize))
+# define BLI_array_alloca(arr, realsize) (typeof(arr))alloca(sizeof(*arr) * (realsize))
# endif
#else
# define BLI_array_alloca(arr, realsize) alloca(sizeof(*arr) * (realsize))
diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h
index e40a79dad21..6bf29a6168f 100644
--- a/source/blender/blenlib/BLI_array.h
+++ b/source/blender/blenlib/BLI_array.h
@@ -58,7 +58,7 @@ void _bli_array_grow_func(void **arr_p,
/** \name Public defines
* \{ */
-/** use ``sizeof(*(arr))`` to ensure the array exists and is an array */
+/** use `sizeof(*(arr))` to ensure the array exists and is an array */
#define BLI_array_declare(arr) \
int _##arr##_len = ((void)(sizeof(*(arr))), 0); \
void *_##arr##_static = NULL
diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh
index 76dfcf0b57d..d93bd7f6f76 100644
--- a/source/blender/blenlib/BLI_color.hh
+++ b/source/blender/blenlib/BLI_color.hh
@@ -34,11 +34,11 @@ namespace blender {
* Usage:
*
* Convert a theme byte color to a linearrgb premultiplied.
- * ```
+ * \code{.cc}
* ColorTheme4b theme_color;
* ColorSceneLinear4f<eAlpha::Premultiplied> linearrgb_color =
* BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha();
- * ```
+ * \endcode
*
* The API is structured to make most use of inlining. Most notable are space
* conversions done via `BLI_color_convert_to*` functions.
diff --git a/source/blender/blenlib/BLI_compiler_typecheck.h b/source/blender/blenlib/BLI_compiler_typecheck.h
index d9c2bfc1d58..18be2190c1b 100644
--- a/source/blender/blenlib/BLI_compiler_typecheck.h
+++ b/source/blender/blenlib/BLI_compiler_typecheck.h
@@ -86,7 +86,7 @@
/**
* CHECK_TYPE_ANY: handy macro, eg:
- * ``CHECK_TYPE_ANY(var, Foo *, Bar *, Baz *)``
+ * `CHECK_TYPE_ANY(var, Foo *, Bar *, Baz *)`
*
* excuse ridiculously long generated args.
* \code{.py}
diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h
index d42bd6af637..658dcadadce 100644
--- a/source/blender/blenlib/BLI_delaunay_2d.h
+++ b/source/blender/blenlib/BLI_delaunay_2d.h
@@ -110,6 +110,10 @@ extern "C" {
* If zero is supplied for epsilon, an internal value of 1e-8 used
* instead, since this code will not work correctly if it is not allowed
* to merge "too near" vertices.
+ *
+ * Normally the output will contain mappings from outputs to inputs.
+ * If this is not needed, set need_ids to false and the execution may be much
+ * faster in some circumstances.
*/
typedef struct CDT_input {
int verts_len;
@@ -121,6 +125,7 @@ typedef struct CDT_input {
int *faces_start_table;
int *faces_len_table;
float epsilon;
+ bool need_ids;
} CDT_input;
/**
@@ -140,6 +145,7 @@ typedef struct CDT_input {
* a run-together array and a "start" and "len" extra array,
* similar triples are used to represent the output to input
* mapping of vertices, edges, and faces.
+ * These are only set if need_ids is true in the input.
*
* Those triples are:
* - verts_orig, verts_orig_start_table, verts_orig_len_table
@@ -236,6 +242,7 @@ template<typename Arith_t> class CDT_input {
Array<std::pair<int, int>> edge;
Array<Vector<int>> face;
Arith_t epsilon{0};
+ bool need_ids{true};
};
template<typename Arith_t> class CDT_result {
@@ -243,6 +250,7 @@ template<typename Arith_t> class CDT_result {
Array<vec2<Arith_t>> vert;
Array<std::pair<int, int>> edge;
Array<Vector<int>> face;
+ /* The orig vectors are only populated if the need_ids input field is true. */
/** For each output vert, which input verts correspond to it? */
Array<Vector<int>> vert_orig;
/**
diff --git a/source/blender/blenlib/BLI_dlrbTree.h b/source/blender/blenlib/BLI_dlrbTree.h
index 8c20e3d3988..03aab8d2895 100644
--- a/source/blender/blenlib/BLI_dlrbTree.h
+++ b/source/blender/blenlib/BLI_dlrbTree.h
@@ -111,20 +111,22 @@ void BLI_dlrbTree_linkedlist_sync(DLRBT_Tree *tree);
/* Searching ------------------------------------ */
/* Find the node which matches or is the closest to the requested node */
-DLRBT_Node *BLI_dlrbTree_search(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data);
+DLRBT_Node *BLI_dlrbTree_search(const DLRBT_Tree *tree,
+ DLRBT_Comparator_FP cmp_cb,
+ void *search_data);
/* Find the node which exactly matches the required data */
-DLRBT_Node *BLI_dlrbTree_search_exact(DLRBT_Tree *tree,
+DLRBT_Node *BLI_dlrbTree_search_exact(const DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data);
/* Find the node which occurs immediately before the best matching node */
-DLRBT_Node *BLI_dlrbTree_search_prev(DLRBT_Tree *tree,
+DLRBT_Node *BLI_dlrbTree_search_prev(const DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data);
/* Find the node which occurs immediately after the best matching node */
-DLRBT_Node *BLI_dlrbTree_search_next(DLRBT_Tree *tree,
+DLRBT_Node *BLI_dlrbTree_search_next(const DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data);
@@ -137,7 +139,8 @@ short BLI_dlrbTree_contains(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *
*/
/* Add the given data to the tree, and return the node added */
-// NOTE: for duplicates, the update_cb is called (if available), and the existing node is returned
+/* NOTE: for duplicates, the update_cb is called (if available),
+ * and the existing node is returned. */
DLRBT_Node *BLI_dlrbTree_add(DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
DLRBT_NAlloc_FP new_cb,
@@ -145,7 +148,7 @@ DLRBT_Node *BLI_dlrbTree_add(DLRBT_Tree *tree,
void *data);
/* Remove the given element from the tree and balance again */
-// FIXME: this is not implemented yet...
+/* FIXME: this is not implemented yet... */
// void BLI_dlrbTree_remove(DLRBT_Tree *tree, DLRBT_Node *node);
/* Node Operations (Manual) --------------------- */
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index 396b0b1bd21..347ce2caa34 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -51,8 +51,14 @@ struct float4x4 {
{
BLI_ASSERT_UNIT_V3(forward);
BLI_ASSERT_UNIT_V3(up);
+
+ /* Negate the cross product so that the resulting matrix has determinant 1 (instead of -1).
+ * Without the negation, the result would be a so called improper rotation. That means it
+ * contains a reflection. Such an improper rotation matrix could not be converted to another
+ * representation of a rotation such as euler angles. */
+ const float3 cross = -float3::cross(forward, up);
+
float4x4 matrix;
- const float3 cross = float3::cross(forward, up);
matrix.values[0][0] = forward.x;
matrix.values[1][0] = cross.x;
matrix.values[2][0] = up.x;
@@ -212,6 +218,11 @@ struct float4x4 {
return result;
}
+ bool is_negative() const
+ {
+ return is_negative_m4(ptr());
+ }
+
uint64_t hash() const
{
uint64_t h = 435109;
diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h
index e708b327bd4..a2c5c6349a5 100644
--- a/source/blender/blenlib/BLI_ghash.h
+++ b/source/blender/blenlib/BLI_ghash.h
@@ -77,7 +77,7 @@ enum {
/* -------------------------------------------------------------------- */
/** \name GHash API
*
- * Defined in ``BLI_ghash.c``
+ * Defined in `BLI_ghash.c`
* \{ */
GHash *BLI_ghash_new_ex(GHashHashFP hashfp,
@@ -333,11 +333,11 @@ double BLI_gset_calc_quality(GSet *gs);
/* -------------------------------------------------------------------- */
/** \name GHash/GSet Utils
*
- * Defined in ``BLI_ghash_utils.c``
+ * Defined in `BLI_ghash_utils.c`
* \{ */
/**
- * Callbacks for GHash (``BLI_ghashutil_``)
+ * Callbacks for GHash (`BLI_ghashutil_`)
*
* \note '_p' suffix denotes void pointer arg,
* so we can have functions that take correctly typed args too.
diff --git a/source/blender/blenlib/BLI_inplace_priority_queue.hh b/source/blender/blenlib/BLI_inplace_priority_queue.hh
index e76cb8504a3..f0f0dd9b517 100644
--- a/source/blender/blenlib/BLI_inplace_priority_queue.hh
+++ b/source/blender/blenlib/BLI_inplace_priority_queue.hh
@@ -33,8 +33,8 @@ namespace blender {
template<
/* Type of the elements in the underlying array. */
typename T,
- /* Binary function that takes two `const T &` inputs and returns true, when the first input has
- greater priority than the second. */
+ /* Binary function that takes two `const T &` inputs and returns true,
+ * when the first input has greater priority than the second. */
typename FirstHasHigherPriority = std::greater<T>>
class InplacePriorityQueue {
private:
diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh
index 7de6bcfdd98..bb14a6008a7 100644
--- a/source/blender/blenlib/BLI_linear_allocator.hh
+++ b/source/blender/blenlib/BLI_linear_allocator.hh
@@ -121,7 +121,7 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
* You must not call `delete` on the returned value.
* Instead, only the destructor has to be called.
*/
- template<typename T, typename... Args> destruct_ptr<T> construct(Args &&... args)
+ template<typename T, typename... Args> destruct_ptr<T> construct(Args &&...args)
{
void *buffer = this->allocate(sizeof(T), alignof(T));
T *value = new (buffer) T(std::forward<Args>(args)...);
@@ -134,7 +134,7 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
* the constructed elements.
*/
template<typename T, typename... Args>
- MutableSpan<T> construct_array(int64_t size, Args &&... args)
+ MutableSpan<T> construct_array(int64_t size, Args &&...args)
{
MutableSpan<T> array = this->allocate_array<T>(size);
for (const int64_t i : IndexRange(size)) {
@@ -186,7 +186,7 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
}
template<typename T, typename... Args>
- Span<T *> construct_elements_and_pointer_array(int64_t n, Args &&... args)
+ Span<T *> construct_elements_and_pointer_array(int64_t n, Args &&...args)
{
MutableSpan<void *> void_pointers = this->allocate_elements_and_pointer_array(
n, sizeof(T), alignof(T));
diff --git a/source/blender/blenlib/BLI_link_utils.h b/source/blender/blenlib/BLI_link_utils.h
index e1e4a8dbd4a..7aa7300da16 100644
--- a/source/blender/blenlib/BLI_link_utils.h
+++ b/source/blender/blenlib/BLI_link_utils.h
@@ -20,7 +20,7 @@
* \ingroup bli
* \brief Single link-list utility macros. (header only api).
*
- * Use this api when the structure defines its own ``next`` pointer
+ * Use this api when the structure defines its own `next` pointer
* and a double linked list such as #ListBase isn't needed.
*/
diff --git a/source/blender/blenlib/BLI_linklist_stack.h b/source/blender/blenlib/BLI_linklist_stack.h
index 8a5e94a7b56..d07bc40c923 100644
--- a/source/blender/blenlib/BLI_linklist_stack.h
+++ b/source/blender/blenlib/BLI_linklist_stack.h
@@ -28,7 +28,7 @@
* \note These macros follow STACK_* macros defined in 'BLI_utildefines.h'
* and should be kept (mostly) interchangeable.
*
- * \note ``_##var##_type`` is a dummy variable only used for typechecks.
+ * \note `_##var##_type` is a dummy variable only used for type-checks.
*/
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh
index 4d254960f34..0e7d7c06049 100644
--- a/source/blender/blenlib/BLI_map.hh
+++ b/source/blender/blenlib/BLI_map.hh
@@ -248,7 +248,7 @@ class Map {
this->add_new_as(std::move(key), std::move(value));
}
template<typename ForwardKey, typename... ForwardValue>
- void add_new_as(ForwardKey &&key, ForwardValue &&... value)
+ void add_new_as(ForwardKey &&key, ForwardValue &&...value)
{
this->add_new__impl(
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
@@ -278,7 +278,7 @@ class Map {
return this->add_as(std::move(key), std::move(value));
}
template<typename ForwardKey, typename... ForwardValue>
- bool add_as(ForwardKey &&key, ForwardValue &&... value)
+ bool add_as(ForwardKey &&key, ForwardValue &&...value)
{
return this->add__impl(
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
@@ -308,7 +308,7 @@ class Map {
return this->add_overwrite_as(std::move(key), std::move(value));
}
template<typename ForwardKey, typename... ForwardValue>
- bool add_overwrite_as(ForwardKey &&key, ForwardValue &&... value)
+ bool add_overwrite_as(ForwardKey &&key, ForwardValue &&...value)
{
return this->add_overwrite__impl(
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
@@ -414,7 +414,7 @@ class Map {
return this->pop_default_as(key, std::move(default_value));
}
template<typename ForwardKey, typename... ForwardValue>
- Value pop_default_as(const ForwardKey &key, ForwardValue &&... default_value)
+ Value pop_default_as(const ForwardKey &key, ForwardValue &&...default_value)
{
Slot *slot = this->lookup_slot_ptr(key, hash_(key));
if (slot == nullptr) {
@@ -526,7 +526,7 @@ class Map {
return this->lookup_default_as(key, default_value);
}
template<typename ForwardKey, typename... ForwardValue>
- Value lookup_default_as(const ForwardKey &key, ForwardValue &&... default_value) const
+ Value lookup_default_as(const ForwardKey &key, ForwardValue &&...default_value) const
{
const Value *ptr = this->lookup_ptr_as(key);
if (ptr != nullptr) {
@@ -558,7 +558,7 @@ class Map {
return this->lookup_or_add_as(std::move(key), std::move(value));
}
template<typename ForwardKey, typename... ForwardValue>
- Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&... value)
+ Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&...value)
{
return this->lookup_or_add__impl(
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
@@ -1058,7 +1058,7 @@ class Map {
}
template<typename ForwardKey, typename... ForwardValue>
- void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
+ void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&...value)
{
BLI_assert(!this->contains_as(key));
@@ -1075,7 +1075,7 @@ class Map {
}
template<typename ForwardKey, typename... ForwardValue>
- bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
+ bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&...value)
{
this->ensure_can_add();
@@ -1148,7 +1148,7 @@ class Map {
}
template<typename ForwardKey, typename... ForwardValue>
- Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
+ Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&...value)
{
this->ensure_can_add();
@@ -1166,7 +1166,7 @@ class Map {
}
template<typename ForwardKey, typename... ForwardValue>
- bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
+ bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&...value)
{
auto create_func = [&](Value *ptr) {
new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)...);
@@ -1283,13 +1283,13 @@ template<typename Key, typename Value> class StdUnorderedMapWrapper {
}
template<typename ForwardKey, typename... ForwardValue>
- void add_new(ForwardKey &&key, ForwardValue &&... value)
+ void add_new(ForwardKey &&key, ForwardValue &&...value)
{
map_.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)});
}
template<typename ForwardKey, typename... ForwardValue>
- bool add(ForwardKey &&key, ForwardValue &&... value)
+ bool add(ForwardKey &&key, ForwardValue &&...value)
{
return map_
.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)})
diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh
index 1b4ca11af41..7658ae51fd9 100644
--- a/source/blender/blenlib/BLI_map_slots.hh
+++ b/source/blender/blenlib/BLI_map_slots.hh
@@ -196,7 +196,7 @@ template<typename Key, typename Value> class SimpleMapSlot {
* constructed by calling the constructor with the given key/value as parameter.
*/
template<typename ForwardKey, typename... ForwardValue>
- void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
+ void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&...value)
{
BLI_assert(!this->is_occupied());
new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
@@ -316,7 +316,7 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot
}
template<typename ForwardKey, typename... ForwardValue>
- void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
+ void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&...value)
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
diff --git a/source/blender/blenlib/BLI_math.h b/source/blender/blenlib/BLI_math.h
index 3b61c0feb51..5768b098d2f 100644
--- a/source/blender/blenlib/BLI_math.h
+++ b/source/blender/blenlib/BLI_math.h
@@ -26,32 +26,32 @@
*
* \section mathabbrev Abbreviations
*
- * - ``fl`` = float
- * - ``db`` = double
- * - ``v2`` = vec2 = vector 2
- * - ``v3`` = vec3 = vector 3
- * - ``v4`` = vec4 = vector 4
- * - ``vn`` = vec4 = vector N dimensions, *passed as an arg, after the vector*.
- * - ``qt`` = quat = quaternion
- * - ``dq`` = dquat = dual quaternion
- * - ``m2`` = mat2 = matrix 2x2
- * - ``m3`` = mat3 = matrix 3x3
- * - ``m4`` = mat4 = matrix 4x4
- * - ``eul`` = euler rotation
- * - ``eulO`` = euler with order
- * - ``plane`` = plane 4, (vec3, distance)
- * - ``plane3`` = plane 3 (same as a ``plane`` with a zero 4th component)
+ * - `fl` = float
+ * - `db` = double
+ * - `v2` = vec2 = vector 2
+ * - `v3` = vec3 = vector 3
+ * - `v4` = vec4 = vector 4
+ * - `vn` = vec4 = vector N dimensions, *passed as an arg, after the vector*.
+ * - `qt` = quat = quaternion
+ * - `dq` = dquat = dual quaternion
+ * - `m2` = mat2 = matrix 2x2
+ * - `m3` = mat3 = matrix 3x3
+ * - `m4` = mat4 = matrix 4x4
+ * - `eul` = euler rotation
+ * - `eulO` = euler with order
+ * - `plane` = plane 4, (vec3, distance)
+ * - `plane3` = plane 3 (same as a `plane` with a zero 4th component)
*
* \subsection mathabbrev_all Function Type Abbreviations
*
* For non float versions of functions (which typically operate on floats),
* use single suffix abbreviations.
*
- * - ``_d`` = double
- * - ``_i`` = int
- * - ``_u`` = unsigned int
- * - ``_char`` = char
- * - ``_uchar`` = unsigned char
+ * - `_d` = double
+ * - `_i` = int
+ * - `_u` = unsigned int
+ * - `_char` = char
+ * - `_uchar` = unsigned char
*
* \section mathvarnames Variable Names
*
diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh
index e5a698f25f1..6a98c2dcc1c 100644
--- a/source/blender/blenlib/BLI_resource_scope.hh
+++ b/source/blender/blenlib/BLI_resource_scope.hh
@@ -148,7 +148,7 @@ class ResourceScope : NonCopyable, NonMovable {
/**
* Utility method to construct an instance of type T that will be owned by the ResourceScope.
*/
- template<typename T, typename... Args> T &construct(const char *name, Args &&... args)
+ template<typename T, typename... Args> T &construct(const char *name, Args &&...args)
{
destruct_ptr<T> value_ptr = m_allocator.construct<T>(std::forward<Args>(args)...);
T &value_ref = *value_ptr;
diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh
index d66316a95d9..c9c355288b6 100644
--- a/source/blender/blenlib/BLI_stack.hh
+++ b/source/blender/blenlib/BLI_stack.hh
@@ -233,7 +233,7 @@ class Stack {
this->push_as(std::move(value));
}
/* This is similar to `std::stack::emplace`. */
- template<typename... ForwardT> void push_as(ForwardT &&... value)
+ template<typename... ForwardT> void push_as(ForwardT &&...value)
{
if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(1);
diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh
index 0cff8fa7fb4..257a0ba143e 100644
--- a/source/blender/blenlib/BLI_string_ref.hh
+++ b/source/blender/blenlib/BLI_string_ref.hh
@@ -208,6 +208,18 @@ class StringRefBase {
constexpr int64_t find_first_not_of(char c, int64_t pos = 0) const;
constexpr int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const;
constexpr int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const;
+
+ /**
+ * Return a new StringRef that does not contain leading and trailing whitespace.
+ */
+ constexpr StringRef trim() const;
+
+ /**
+ * Return a new StringRef that removes all the leading and trailing characters
+ * that occur in `characters_to_remove`.
+ */
+ constexpr StringRef trim(StringRef characters_to_remove) const;
+ constexpr StringRef trim(char character_to_remove) const;
};
/**
@@ -540,4 +552,34 @@ constexpr inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) co
return this->find_last_not_of(StringRef(&c, 1), pos);
}
+constexpr StringRef StringRefBase::trim() const
+{
+ return this->trim(" \t\r\n");
+}
+
+constexpr StringRef StringRefBase::trim(const char character_to_remove) const
+{
+ return this->trim(StringRef(&character_to_remove, 1));
+}
+
+/**
+ * Return a new StringRef that removes all the leading and trailing characters
+ * that occur in `characters_to_remove`.
+ */
+constexpr StringRef StringRefBase::trim(StringRef characters_to_remove) const
+{
+ const int64_t find_front = this->find_first_not_of(characters_to_remove);
+ if (find_front == not_found) {
+ return StringRef();
+ }
+ const int64_t find_end = this->find_last_not_of(characters_to_remove);
+ /* `find_end` cannot be `not_found`, because that means the string is only
+ * `characters_to_remove`, in which case `find_front` would already have
+ * been `not_found`. */
+ BLI_assert_msg(find_end != not_found,
+ "forward search found characters-to-not-remove, but backward search did not");
+ const int64_t substr_len = find_end - find_front + 1;
+ return this->substr(find_front, substr_len);
+}
+
} // namespace blender
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index da02c4f4ae1..d2b94a6d8ef 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -445,7 +445,7 @@ class Vector {
this->append_as(std::move(value));
}
/* This is similar to `std::vector::emplace_back`. */
- template<typename... ForwardValue> void append_as(ForwardValue &&... value)
+ template<typename... ForwardValue> void append_as(ForwardValue &&...value)
{
this->ensure_space_for_one();
this->append_unchecked_as(std::forward<ForwardValue>(value)...);
@@ -487,7 +487,7 @@ class Vector {
{
this->append_unchecked_as(std::move(value));
}
- template<typename... ForwardT> void append_unchecked_as(ForwardT &&... value)
+ template<typename... ForwardT> void append_unchecked_as(ForwardT &&...value)
{
BLI_assert(end_ < capacity_end_);
new (end_) T(std::forward<ForwardT>(value)...);
diff --git a/source/blender/blenlib/intern/BLI_dynstr.c b/source/blender/blenlib/intern/BLI_dynstr.c
index 86784557b25..8f7f722c71b 100644
--- a/source/blender/blenlib/intern/BLI_dynstr.c
+++ b/source/blender/blenlib/intern/BLI_dynstr.c
@@ -291,7 +291,7 @@ int BLI_dynstr_get_len(const DynStr *ds)
/**
* Get a DynStr's contents as a c-string.
* The \a rets argument must be allocated to be at
- * least the size of ``BLI_dynstr_get_len(ds) + 1``.
+ * least the size of `BLI_dynstr_get_len(ds) + 1`.
*
* \param ds: The DynStr of interest.
* \param rets: The string to fill.
diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c
index 46e599b7cf3..2c9285e418a 100644
--- a/source/blender/blenlib/intern/BLI_ghash.c
+++ b/source/blender/blenlib/intern/BLI_ghash.c
@@ -1270,7 +1270,7 @@ void BLI_gset_flag_clear(GSet *gs, uint flag)
/* -------------------------------------------------------------------- */
/** \name GSet Combined Key/Value Usage
*
- * \note Not typical ``set`` use, only use when the pointer identity matters.
+ * \note Not typical `set` use, only use when the pointer identity matters.
* This can be useful when the key references data stored outside the GSet.
* \{ */
diff --git a/source/blender/blenlib/intern/BLI_ghash_utils.c b/source/blender/blenlib/intern/BLI_ghash_utils.c
index 182c27aed6d..b9144009304 100644
--- a/source/blender/blenlib/intern/BLI_ghash_utils.c
+++ b/source/blender/blenlib/intern/BLI_ghash_utils.c
@@ -138,8 +138,8 @@ size_t BLI_ghashutil_combine_hash(size_t hash_a, size_t hash_b)
* This function implements the widely used "djb" hash apparently posted
* by Daniel Bernstein to comp.lang.c some time ago. The 32 bit
* unsigned hash value starts at 5381 and for each byte 'c' in the
- * string, is updated: ``hash = hash * 33 + c``. This
- * function uses the signed value of each byte.
+ * string, is updated: `hash = hash * 33 + c`.
+ * This function uses the signed value of each byte.
*
* NOTE: this is the same hash method that glib 2.34.0 uses.
*/
diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c
index 25939323b73..674654c99a8 100644
--- a/source/blender/blenlib/intern/BLI_kdopbvh.c
+++ b/source/blender/blenlib/intern/BLI_kdopbvh.c
@@ -22,7 +22,7 @@
* \brief BVH-tree implementation.
*
* k-DOP BVH (Discrete Oriented Polytope, Bounding Volume Hierarchy).
- * A k-DOP is represented as k/2 pairs of min , max values for k/2 directions (intervals, "slabs").
+ * A k-DOP is represented as k/2 pairs of min, max values for k/2 directions (intervals, "slabs").
*
* See: http://www.gris.uni-tuebingen.de/people/staff/jmezger/papers/bvh.pdf
*
@@ -868,7 +868,7 @@ static void non_recursive_bvh_div_nodes(const BVHTree *tree,
* \{ */
/**
- * \note many callers don't check for ``NULL`` return.
+ * \note many callers don't check for `NULL` return.
*/
BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
{
diff --git a/source/blender/blenlib/intern/BLI_memarena.c b/source/blender/blenlib/intern/BLI_memarena.c
index de2ce44be44..0ab27a5adad 100644
--- a/source/blender/blenlib/intern/BLI_memarena.c
+++ b/source/blender/blenlib/intern/BLI_memarena.c
@@ -185,6 +185,9 @@ void *BLI_memarena_calloc(MemArena *ma, size_t size)
*
* \note Useful for multi-threaded tasks that need a thread-local #MemArena
* that is kept after the multi-threaded operation is completed.
+ *
+ * \note Avoid accumulating memory pools where possible
+ * as any unused memory in `ma_src` is wasted every merge.
*/
void BLI_memarena_merge(MemArena *ma_dst, MemArena *ma_src)
{
diff --git a/source/blender/blenlib/intern/BLI_memiter.c b/source/blender/blenlib/intern/BLI_memiter.c
index 3f255ce0e16..effbe5da5c4 100644
--- a/source/blender/blenlib/intern/BLI_memiter.c
+++ b/source/blender/blenlib/intern/BLI_memiter.c
@@ -73,7 +73,7 @@ typedef struct BLI_memiter_chunk {
struct BLI_memiter_chunk *next;
/**
* internal format is:
- * ``[next_pointer, size:data, size:data, ..., negative_offset]``
+ * `[next_pointer, size:data, size:data, ..., negative_offset]`
*
* Where negative offset rewinds to the start.
*/
diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c
index 5263af2ae56..f968799326a 100644
--- a/source/blender/blenlib/intern/BLI_mempool.c
+++ b/source/blender/blenlib/intern/BLI_mempool.c
@@ -63,9 +63,9 @@
#endif
/**
- * Important that this value is an is _not_ aligned with ``sizeof(void *)``.
+ * Important that this value is an is _not_ aligned with `sizeof(void *)`.
* So having a pointer to 2/4/8... aligned memory is enough to ensure
- * the freeword will never be used.
+ * the `freeword` will never be used.
* To be safe, use a word that's the same in both directions.
*/
#define FREEWORD \
diff --git a/source/blender/blenlib/intern/BLI_mmap.c b/source/blender/blenlib/intern/BLI_mmap.c
index 71510b62ba1..c25e89df818 100644
--- a/source/blender/blenlib/intern/BLI_mmap.c
+++ b/source/blender/blenlib/intern/BLI_mmap.c
@@ -31,11 +31,11 @@
#ifndef WIN32
# include <signal.h>
# include <stdlib.h>
-# include <sys/mman.h> // for mmap
-# include <unistd.h> // for read close
+# include <sys/mman.h> /* For mmap. */
+# include <unistd.h> /* For read close. */
#else
# include "BLI_winstuff.h"
-# include <io.h> // for open close read
+# include <io.h> /* For open close read. */
#endif
struct BLI_mmap_file {
diff --git a/source/blender/blenlib/intern/DLRB_tree.c b/source/blender/blenlib/intern/DLRB_tree.c
index 09234dcfa26..436b9b8d782 100644
--- a/source/blender/blenlib/intern/DLRB_tree.c
+++ b/source/blender/blenlib/intern/DLRB_tree.c
@@ -128,7 +128,9 @@ void BLI_dlrbTree_linkedlist_sync(DLRBT_Tree *tree)
/* Tree Search Utilities */
/* Find the node which matches or is the closest to the requested node */
-DLRBT_Node *BLI_dlrbTree_search(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
+DLRBT_Node *BLI_dlrbTree_search(const DLRBT_Tree *tree,
+ DLRBT_Comparator_FP cmp_cb,
+ void *search_data)
{
DLRBT_Node *node = (tree) ? tree->root : NULL;
short found = 0;
@@ -174,7 +176,7 @@ DLRBT_Node *BLI_dlrbTree_search(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, vo
}
/* Find the node which exactly matches the required data */
-DLRBT_Node *BLI_dlrbTree_search_exact(DLRBT_Tree *tree,
+DLRBT_Node *BLI_dlrbTree_search_exact(const DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data)
{
@@ -222,7 +224,7 @@ DLRBT_Node *BLI_dlrbTree_search_exact(DLRBT_Tree *tree,
}
/* Find the node which occurs immediately before the best matching node */
-DLRBT_Node *BLI_dlrbTree_search_prev(DLRBT_Tree *tree,
+DLRBT_Node *BLI_dlrbTree_search_prev(const DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data)
{
@@ -253,7 +255,7 @@ DLRBT_Node *BLI_dlrbTree_search_prev(DLRBT_Tree *tree,
}
/* Find the node which occurs immediately after the best matching node */
-DLRBT_Node *BLI_dlrbTree_search_next(DLRBT_Tree *tree,
+DLRBT_Node *BLI_dlrbTree_search_next(const DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data)
{
diff --git a/source/blender/blenlib/intern/array_store.c b/source/blender/blenlib/intern/array_store.c
index e1a7ee98ce5..ee06d8b6347 100644
--- a/source/blender/blenlib/intern/array_store.c
+++ b/source/blender/blenlib/intern/array_store.c
@@ -1403,16 +1403,16 @@ static BChunkList *bchunk_list_from_data_merge(const BArrayInfo *info,
* Create a new array store, which can store any number of arrays
* as long as their stride matches.
*
- * \param stride: ``sizeof()`` each element,
+ * \param stride: `sizeof()` each element,
*
- * \note while a stride of ``1`` will always work,
+ * \note while a stride of `1` will always work,
* its less efficient since duplicate chunks of memory will be searched
* at positions unaligned with the array data.
*
* \param chunk_count: Number of elements to split each chunk into.
* - A small value increases the ability to de-duplicate chunks,
* but adds overhead by increasing the number of chunks to look up when searching for duplicates,
- * as well as some overhead constructing the original array again, with more calls to ``memcpy``.
+ * as well as some overhead constructing the original array again, with more calls to `memcpy`.
* - Larger values reduce the *book keeping* overhead,
* but increase the chance a small,
* isolated change will cause a larger amount of data to be duplicated.
@@ -1436,8 +1436,8 @@ BArrayStore *BLI_array_store_create(uint stride, uint chunk_count)
bs->info.accum_steps = BCHUNK_HASH_TABLE_ACCUMULATE_STEPS - 1;
/* Triangle number, identifying now much read-ahead we need:
* https://en.wikipedia.org/wiki/Triangular_number (+ 1) */
- bs->info.accum_read_ahead_len = (uint)(
- (((bs->info.accum_steps * (bs->info.accum_steps + 1))) / 2) + 1);
+ bs->info.accum_read_ahead_len =
+ (uint)((((bs->info.accum_steps * (bs->info.accum_steps + 1))) / 2) + 1);
bs->info.accum_read_ahead_bytes = bs->info.accum_read_ahead_len * stride;
#else
bs->info.accum_read_ahead_bytes = BCHUNK_HASH_LEN * stride;
diff --git a/source/blender/blenlib/intern/convexhull_2d.c b/source/blender/blenlib/intern/convexhull_2d.c
index cb4ef54bfb7..233a1430fe7 100644
--- a/source/blender/blenlib/intern/convexhull_2d.c
+++ b/source/blender/blenlib/intern/convexhull_2d.c
@@ -188,7 +188,7 @@ static int pointref_cmp_yx(const void *a_, const void *b_)
* \param points: An array of 2D points.
* \param n: The number of points in points.
* \param r_points: An array of the convex hull vertex indices (max is n).
- * _must_ be allocated as ``n * 2`` because of how its used internally,
+ * _must_ be allocated as `n * 2` because of how its used internally,
* even though the final result will be no more than \a n in size.
* \returns the number of points in r_points.
*/
diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc
index 24a58103a10..4582ea69d9b 100644
--- a/source/blender/blenlib/intern/delaunay_2d.cc
+++ b/source/blender/blenlib/intern/delaunay_2d.cc
@@ -19,6 +19,7 @@
*/
#include <algorithm>
+#include <atomic>
#include <fstream>
#include <iostream>
#include <sstream>
@@ -29,6 +30,8 @@
#include "BLI_math_boolean.hh"
#include "BLI_math_mpq.hh"
#include "BLI_mpq2.hh"
+#include "BLI_set.hh"
+#include "BLI_task.hh"
#include "BLI_vector.hh"
#include "BLI_delaunay_2d.h"
@@ -77,8 +80,8 @@ template<> double math_to_double<double>(const double v)
* Define a templated 2D arrangement of vertices, edges, and faces.
* The #SymEdge data structure is the basis for a structure that allows
* easy traversal to neighboring (by topology) geometric elements.
- * Each of #CDTVert, #CDTEdge, and #CDTFace have an input_id linked list,
- * whose nodes contain integers that keep track of which input verts, edges,
+ * Each of #CDTVert, #CDTEdge, and #CDTFace have an input_id set,
+ * which contain integers that keep track of which input verts, edges,
* and faces, respectively, that the element was derived from.
*
* While this could be cleaned up some, it is usable by other routines in Blender
@@ -195,8 +198,8 @@ template<typename T> struct CDTVert {
FatCo<T> co;
/** Some edge attached to it. */
SymEdge<T> *symedge{nullptr};
- /** List of corresponding vertex input ids. */
- LinkNode *input_ids{nullptr};
+ /** Set of corresponding vertex input ids. Not used if don't need_ids. */
+ blender::Set<int> input_ids;
/** Index into array that #CDTArrangement keeps. */
int index{-1};
/** Index of a CDTVert that this has merged to. -1 if no merge. */
@@ -209,8 +212,10 @@ template<typename T> struct CDTVert {
};
template<typename Arith_t> struct CDTEdge {
- /** List of input edge ids that this is part of. */
- LinkNode *input_ids{nullptr};
+ /** Set of input edge ids that this is part of.
+ * If don't need_ids, then should contain 0 if it is a constrained edge,
+ * else empty. */
+ blender::Set<int> input_ids;
/** The directed edges for this edge. */
SymEdge<Arith_t> symedges[2]{SymEdge<Arith_t>(), SymEdge<Arith_t>()};
@@ -220,8 +225,10 @@ template<typename Arith_t> struct CDTEdge {
template<typename Arith_t> struct CDTFace {
/** A symedge in face; only used during output, so only valid then. */
SymEdge<Arith_t> *symedge{nullptr};
- /** List of input face ids that this is part of. */
- LinkNode *input_ids{nullptr};
+ /** Set of input face ids that this is part of.
+ * If don't need_ids, then should contain 0 if it is part of a constrained face,
+ * else empty. */
+ blender::Set<int> input_ids;
/** Used by algorithms operating on CDT structures. */
int visit_index{0};
/** Marks this face no longer used. */
@@ -334,27 +341,30 @@ template<typename T> class CDT_state {
int face_edge_offset;
/** How close before coords considered equal. */
T epsilon;
+ /** Do we need to track ids? */
+ bool need_ids;
- explicit CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon);
+ explicit CDT_state(
+ int num_input_verts, int num_input_edges, int num_input_faces, T epsilon, bool need_ids);
};
template<typename T> CDTArrangement<T>::~CDTArrangement()
{
for (int i : this->verts.index_range()) {
CDTVert<T> *v = this->verts[i];
- BLI_linklist_free(v->input_ids, nullptr);
+ v->input_ids.clear();
delete v;
this->verts[i] = nullptr;
}
for (int i : this->edges.index_range()) {
CDTEdge<T> *e = this->edges[i];
- BLI_linklist_free(e->input_ids, nullptr);
+ e->input_ids.clear();
delete e;
this->edges[i] = nullptr;
}
for (int i : this->faces.index_range()) {
CDTFace<T> *f = this->faces[i];
- BLI_linklist_free(f->input_ids, nullptr);
+ f->input_ids.clear();
delete f;
this->faces[i] = nullptr;
}
@@ -431,11 +441,11 @@ template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_state<
if (se) {
os << " edges out:\n";
do {
- if (se->next == NULL) {
+ if (se->next == nullptr) {
os << " [NULL] next/rot symedge, se=" << trunc_ptr(se) << "\n";
break;
}
- if (se->next->next == NULL) {
+ if (se->next->next == nullptr) {
os << " [NULL] next-next/rot symedge, se=" << trunc_ptr(se) << "\n";
break;
}
@@ -495,6 +505,7 @@ template<typename T> void cdt_draw(const std::string &label, const CDTArrangemen
constexpr int vert_radius = 3;
constexpr bool draw_vert_labels = true;
constexpr bool draw_edge_labels = false;
+ constexpr bool draw_face_labels = false;
if (cdt.verts.size() == 0) {
return;
@@ -559,7 +570,7 @@ template<typename T> void cdt_draw(const std::string &label, const CDTArrangemen
const CDTVert<T> *v = e->symedges[1].vert;
const vec2<double> &uco = u->co.approx;
const vec2<double> &vco = v->co.approx;
- int strokew = e->input_ids == nullptr ? thin_line : thick_line;
+ int strokew = e->input_ids.size() == 0 ? thin_line : thick_line;
f << R"(<line fill="none" stroke="black" stroke-width=")" << strokew << "\" x1=\""
<< SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\""
<< SY(vco[1]) << "\">\n";
@@ -586,6 +597,54 @@ template<typename T> void cdt_draw(const std::string &label, const CDTArrangemen
++i;
}
+ if (draw_face_labels) {
+ for (const CDTFace<T> *face : cdt.faces) {
+ if (!face->deleted) {
+ /* Since may not have prepared output yet, need a slow find of a SymEdge for this face. */
+ const SymEdge<T> *se_face_start = nullptr;
+ for (const CDTEdge<T> *e : cdt.edges) {
+ if (e->symedges[0].face == face) {
+ se_face_start = &e->symedges[0];
+ break;
+ }
+ if (e->symedges[1].face == face) {
+ se_face_start = &e->symedges[1];
+ }
+ }
+ if (se_face_start != nullptr) {
+ /* Find center of face. */
+ int face_nverts = 0;
+ vec2<double> cen(0.0, 0.0);
+ if (face == cdt.outer_face) {
+ cen.x = minx;
+ cen.y = miny;
+ }
+ else {
+ const SymEdge<T> *se = se_face_start;
+ do {
+ if (se->face == face) {
+ cen = cen + se->vert->co.approx;
+ face_nverts++;
+ }
+ } while ((se = se->next) != se_face_start);
+ if (face_nverts > 0) {
+ cen = cen / double(face_nverts);
+ }
+ }
+ f << "<text x=\"" << SX(cen[0]) << "\" y=\"" << SY(cen[1]) << "\""
+ << " font-size=\"small\">[";
+ f << trunc_ptr(face);
+ if (face->input_ids.size() > 0) {
+ for (int id : face->input_ids) {
+ f << " " << id;
+ }
+ }
+ f << "]</text>\n";
+ }
+ }
+ }
+ }
+
append = true;
# undef SX
# undef SY
@@ -754,7 +813,6 @@ template<> CDTVert<double>::CDTVert(const vec2<double> &pt)
this->co.exact = pt;
this->co.approx = pt;
this->co.abs_approx = pt; /* Not used, so doesn't matter. */
- this->input_ids = nullptr;
this->symedge = nullptr;
this->index = -1;
this->merge_to_index = -1;
@@ -767,7 +825,6 @@ template<> CDTVert<mpq_class>::CDTVert(const vec2<mpq_class> &pt)
this->co.exact = pt;
this->co.approx = double2(pt.x.get_d(), pt.y.get_d());
this->co.abs_approx = double2(fabs(this->co.approx.x), fabs(this->co.approx.y));
- this->input_ids = nullptr;
this->symedge = nullptr;
this->index = -1;
this->merge_to_index = -1;
@@ -824,35 +881,21 @@ template<typename T> void CDTArrangement<T>::reserve(int num_verts, int num_edge
}
template<typename T>
-CDT_state<T>::CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon)
+CDT_state<T>::CDT_state(
+ int num_input_verts, int num_input_edges, int num_input_faces, T epsilon, bool need_ids)
{
this->input_vert_tot = num_input_verts;
this->cdt.reserve(num_input_verts, num_input_edges, num_input_faces);
this->cdt.outer_face = this->cdt.add_face();
this->epsilon = epsilon;
+ this->need_ids = need_ids;
this->visit_count = 0;
}
-static bool id_in_list(const LinkNode *id_list, int id)
-{
- const LinkNode *ln;
-
- for (ln = id_list; ln != nullptr; ln = ln->next) {
- if (POINTER_AS_INT(ln->link) == id) {
- return true;
- }
- }
- return false;
-}
-
/* Is any id in (range_start, range_start+1, ... , range_end) in id_list? */
-static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end)
+static bool id_range_in_list(const blender::Set<int> &id_list, int range_start, int range_end)
{
- const LinkNode *ln;
- int id;
-
- for (ln = id_list; ln != nullptr; ln = ln->next) {
- id = POINTER_AS_INT(ln->link);
+ for (int id : id_list) {
if (id >= range_start && id <= range_end) {
return true;
}
@@ -860,19 +903,15 @@ static bool id_range_in_list(const LinkNode *id_list, int range_start, int range
return false;
}
-static void add_to_input_ids(LinkNode **dst, int input_id)
+static void add_to_input_ids(blender::Set<int> &dst, int input_id)
{
- if (!id_in_list(*dst, input_id)) {
- BLI_linklist_prepend(dst, POINTER_FROM_INT(input_id));
- }
+ dst.add(input_id);
}
-static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src)
+static void add_list_to_input_ids(blender::Set<int> &dst, const blender::Set<int> &src)
{
- const LinkNode *ln;
-
- for (ln = src; ln != nullptr; ln = ln->next) {
- add_to_input_ids(dst, POINTER_AS_INT(ln->link));
+ for (int value : src) {
+ dst.add(value);
}
}
@@ -883,7 +922,7 @@ template<typename T> inline bool is_border_edge(const CDTEdge<T> *e, const CDT_s
template<typename T> inline bool is_constrained_edge(const CDTEdge<T> *e)
{
- return e->input_ids != nullptr;
+ return e->input_ids.size() > 0;
}
template<typename T> inline bool is_deleted_edge(const CDTEdge<T> *e)
@@ -979,7 +1018,7 @@ template<typename T> CDTEdge<T> *CDTArrangement<T>::add_diagonal(SymEdge<T> *s1,
for (SymEdge<T> *se = s2; se != sdiag; se = se->next) {
se->face = fnew;
}
- add_list_to_input_ids(&fnew->input_ids, fold->input_ids);
+ add_list_to_input_ids(fnew->input_ids, fold->input_ids);
return ediag;
}
@@ -1058,7 +1097,7 @@ template<typename T> CDTEdge<T> *CDTArrangement<T>::split_edge(SymEdge<T> *se, T
if (newsesym->vert->symedge == sesym) {
newsesym->vert->symedge = newsesym;
}
- add_list_to_input_ids(&e->input_ids, se->edge->input_ids);
+ add_list_to_input_ids(e->input_ids, se->edge->input_ids);
return e;
}
@@ -1880,7 +1919,7 @@ void add_edge_constraint(
SymEdge<T> *t = find_symedge_between_verts(v1, v2);
if (t != nullptr) {
/* Segment already there. */
- add_to_input_ids(&t->edge->input_ids, input_id);
+ add_to_input_ids(t->edge->input_ids, input_id);
if (r_edges != nullptr) {
BLI_linklist_append(&edge_list, t->edge);
*r_edges = edge_list.list;
@@ -2041,7 +2080,7 @@ void add_edge_constraint(
BLI_assert(cd_prev->lambda == 0.0);
BLI_assert(cd_prev->out->next->vert == cd->vert);
edge = cd_prev->out->edge;
- add_to_input_ids(&edge->input_ids, input_id);
+ add_to_input_ids(edge->input_ids, input_id);
if (r_edges != nullptr) {
BLI_linklist_append(&edge_list, edge);
}
@@ -2054,7 +2093,7 @@ void add_edge_constraint(
else {
edge = cdt_state->cdt.add_diagonal(tstart, t);
}
- add_to_input_ids(&edge->input_ids, input_id);
+ add_to_input_ids(edge->input_ids, input_id);
if (r_edges != nullptr) {
BLI_linklist_append(&edge_list, edge);
}
@@ -2093,7 +2132,8 @@ template<typename T> void add_edge_constraints(CDT_state<T> *cdt_state, const CD
}
CDTVert<T> *v1 = cdt_state->cdt.get_vert_resolve_merge(iv1);
CDTVert<T> *v2 = cdt_state->cdt.get_vert_resolve_merge(iv2);
- add_edge_constraint(cdt_state, v1, v2, i, nullptr);
+ int id = cdt_state->need_ids ? i : 0;
+ add_edge_constraint(cdt_state, v1, v2, id, nullptr);
}
cdt_state->face_edge_offset = ne;
}
@@ -2132,7 +2172,7 @@ void add_face_ids(
continue;
}
face->visit_index = visit;
- add_to_input_ids(&face->input_ids, face_id);
+ add_to_input_ids(face->input_ids, face_id);
SymEdge<T> *se_start = se;
for (se = se->next; se != se_start; se = se->next) {
if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) {
@@ -2168,7 +2208,10 @@ static int power_of_10_greater_equal_to(int x)
* order around each face in turn. And then the next face starts at
* cdt->face_edge_offset beyond the start for the previous face.
*/
-template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CDT_input<T> &input)
+template<typename T>
+void add_face_constraints(CDT_state<T> *cdt_state,
+ const CDT_input<T> &input,
+ CDT_output_type output_type)
{
int nv = input.vert.size();
int nf = input.face.size();
@@ -2206,7 +2249,8 @@ template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CD
CDTVert<T> *v1 = cdt->get_vert_resolve_merge(iv1);
CDTVert<T> *v2 = cdt->get_vert_resolve_merge(iv2);
LinkNode *edge_list;
- add_edge_constraint(cdt_state, v1, v2, face_edge_id, &edge_list);
+ int id = cdt_state->need_ids ? face_edge_id : 0;
+ add_edge_constraint(cdt_state, v1, v2, id, &edge_list);
/* Set a new face_symedge0 each time since earlier ones may not
* survive later symedge splits. Really, just want the one when
* i == flen -1, but this code guards against that one somehow
@@ -2224,7 +2268,16 @@ template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CD
}
int fedge_end = fedge_start + flen - 1;
if (face_symedge0 != nullptr) {
- add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end);
+ /* We need to propagate face ids to all faces that represent #f, if #need_ids.
+ * Even if `need_ids == false`, we need to propagate at least the fact that
+ * the face ids set would be non-empty if the output type is one of the ones
+ * making valid BMesh faces. */
+ int id = cdt_state->need_ids ? f : 0;
+ add_face_ids(cdt_state, face_symedge0, id, fedge_start, fedge_end);
+ if (cdt_state->need_ids || (output_type == CDT_CONSTRAINTS_VALID_BMESH ||
+ output_type == CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES)) {
+ add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end);
+ }
}
fstart += flen;
}
@@ -2349,7 +2402,7 @@ template<typename T> void remove_non_constraint_edges_leave_valid_bmesh(CDT_stat
CDTFace<T> *fleft = se->face;
CDTFace<T> *fright = sym(se)->face;
if (fleft != cdt->outer_face && fright != cdt->outer_face &&
- (fleft->input_ids != nullptr || fright->input_ids != nullptr)) {
+ (fleft->input_ids.size() > 0 || fright->input_ids.size() > 0)) {
/* Is there another #SymEdge with same left and right faces?
* Or is there a vertex not part of e touching the same left and right faces? */
for (SymEdge<T> *se2 = se->next; dissolve && se2 != se; se2 = se2->next) {
@@ -2507,30 +2560,33 @@ template<typename T> void detect_holes(CDT_state<T> *cdt_state)
mid.exact[1] = (f->symedge->vert->co.exact[1] + f->symedge->next->vert->co.exact[1] +
f->symedge->next->next->vert->co.exact[1]) /
3;
- int hits = 0;
+ std::atomic<int> hits = 0;
/* TODO: Use CDT data structure here to greatly reduce search for intersections! */
- for (const CDTEdge<T> *e : cdt->edges) {
- if (!is_deleted_edge(e) && is_constrained_edge(e)) {
- if (e->symedges[0].face->visit_index == e->symedges[1].face->visit_index) {
- continue; /* Don't count hits on edges between faces in same region. */
- }
- auto isect = vec2<T>::isect_seg_seg(ray_end.exact,
- mid.exact,
- e->symedges[0].vert->co.exact,
- e->symedges[1].vert->co.exact);
- switch (isect.kind) {
- case vec2<T>::isect_result::LINE_LINE_CROSS: {
- hits++;
- break;
+ threading::parallel_for(cdt->edges.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ const CDTEdge<T> *e = cdt->edges[i];
+ if (!is_deleted_edge(e) && is_constrained_edge(e)) {
+ if (e->symedges[0].face->visit_index == e->symedges[1].face->visit_index) {
+ continue; /* Don't count hits on edges between faces in same region. */
+ }
+ auto isect = vec2<T>::isect_seg_seg(ray_end.exact,
+ mid.exact,
+ e->symedges[0].vert->co.exact,
+ e->symedges[1].vert->co.exact);
+ switch (isect.kind) {
+ case vec2<T>::isect_result::LINE_LINE_CROSS: {
+ hits++;
+ break;
+ }
+ case vec2<T>::isect_result::LINE_LINE_EXACT:
+ case vec2<T>::isect_result::LINE_LINE_NONE:
+ case vec2<T>::isect_result::LINE_LINE_COLINEAR:
+ break;
}
- case vec2<T>::isect_result::LINE_LINE_EXACT:
- case vec2<T>::isect_result::LINE_LINE_NONE:
- case vec2<T>::isect_result::LINE_LINE_COLINEAR:
- break;
}
}
- }
- f->hole = (hits % 2) == 0;
+ });
+ f->hole = (hits.load() % 2) == 0;
}
/* Finally, propagate hole status to all holes of a region. */
@@ -2631,25 +2687,31 @@ CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state,
for (int i = 0; i < verts_size; ++i) {
CDTVert<T> *v = cdt->verts[i];
if (v->merge_to_index != -1) {
- if (i < cdt_state->input_vert_tot) {
- add_to_input_ids(&cdt->verts[v->merge_to_index]->input_ids, i);
+ if (cdt_state->need_ids) {
+ if (i < cdt_state->input_vert_tot) {
+ add_to_input_ids(cdt->verts[v->merge_to_index]->input_ids, i);
+ }
}
vert_to_output_map[i] = vert_to_output_map[v->merge_to_index];
}
}
}
result.vert = Array<vec2<T>>(nv);
- result.vert_orig = Array<Vector<int>>(nv);
+ if (cdt_state->need_ids) {
+ result.vert_orig = Array<Vector<int>>(nv);
+ }
int i_out = 0;
for (int i = 0; i < verts_size; ++i) {
CDTVert<T> *v = cdt->verts[i];
if (v->merge_to_index == -1) {
result.vert[i_out] = v->co.exact;
- if (i < cdt_state->input_vert_tot) {
- result.vert_orig[i_out].append(i);
- }
- for (LinkNode *ln = v->input_ids; ln; ln = ln->next) {
- result.vert_orig[i_out].append(POINTER_AS_INT(ln->link));
+ if (cdt_state->need_ids) {
+ if (i < cdt_state->input_vert_tot) {
+ result.vert_orig[i_out].append(i);
+ }
+ for (int vert : v->input_ids) {
+ result.vert_orig[i_out].append(vert);
+ }
}
++i_out;
}
@@ -2660,15 +2722,19 @@ CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state,
return !is_deleted_edge(e);
});
result.edge = Array<std::pair<int, int>>(ne);
- result.edge_orig = Array<Vector<int>>(ne);
+ if (cdt_state->need_ids) {
+ result.edge_orig = Array<Vector<int>>(ne);
+ }
int e_out = 0;
for (const CDTEdge<T> *e : cdt->edges) {
if (!is_deleted_edge(e)) {
int vo1 = vert_to_output_map[e->symedges[0].vert->index];
int vo2 = vert_to_output_map[e->symedges[1].vert->index];
result.edge[e_out] = std::pair<int, int>(vo1, vo2);
- for (LinkNode *ln = e->input_ids; ln; ln = ln->next) {
- result.edge_orig[e_out].append(POINTER_AS_INT(ln->link));
+ if (cdt_state->need_ids) {
+ for (int edge : e->input_ids) {
+ result.edge_orig[e_out].append(edge);
+ }
}
++e_out;
}
@@ -2679,7 +2745,9 @@ CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state,
return !f->deleted && f != cdt->outer_face;
});
result.face = Array<Vector<int>>(nf);
- result.face_orig = Array<Vector<int>>(nf);
+ if (cdt_state->need_ids) {
+ result.face_orig = Array<Vector<int>>(nf);
+ }
int f_out = 0;
for (const CDTFace<T> *f : cdt->faces) {
if (!f->deleted && f != cdt->outer_face) {
@@ -2690,8 +2758,10 @@ CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state,
result.face[f_out].append(vert_to_output_map[se->vert->index]);
se = se->next;
} while (se != se_start);
- for (LinkNode *ln = f->input_ids; ln; ln = ln->next) {
- result.face_orig[f_out].append(POINTER_AS_INT(ln->link));
+ if (cdt_state->need_ids) {
+ for (int face : f->input_ids) {
+ result.face_orig[f_out].append(face);
+ }
}
++f_out;
}
@@ -2716,11 +2786,11 @@ CDT_result<T> delaunay_calc(const CDT_input<T> &input, CDT_output_type output_ty
int nv = input.vert.size();
int ne = input.edge.size();
int nf = input.face.size();
- CDT_state<T> cdt_state(nv, ne, nf, input.epsilon);
+ CDT_state<T> cdt_state(nv, ne, nf, input.epsilon, input.need_ids);
add_input_verts(&cdt_state, input);
initial_triangulation(&cdt_state.cdt);
add_edge_constraints(&cdt_state, input);
- add_face_constraints(&cdt_state, input);
+ add_face_constraints(&cdt_state, input, output_type);
return get_cdt_output(&cdt_state, input, output_type);
}
@@ -2772,6 +2842,7 @@ extern "C" ::CDT_result *BLI_delaunay_2d_cdt_calc(const ::CDT_input *input,
}
}
in.epsilon = static_cast<double>(input->epsilon);
+ in.need_ids = input->need_ids;
blender::meshintersect::CDT_result<double> res = blender::meshintersect::delaunay_2d_calc(
in, output_type);
@@ -2784,74 +2855,101 @@ extern "C" ::CDT_result *BLI_delaunay_2d_cdt_calc(const ::CDT_input *input,
int tot_e_orig = 0;
int tot_f_orig = 0;
int tot_f_lens = 0;
- for (int v = 0; v < nv; ++v) {
- tot_v_orig += res.vert_orig[v].size();
- }
- for (int e = 0; e < ne; ++e) {
- tot_e_orig += res.edge_orig[e].size();
+ if (input->need_ids) {
+ for (int v = 0; v < nv; ++v) {
+ tot_v_orig += res.vert_orig[v].size();
+ }
+ for (int e = 0; e < ne; ++e) {
+ tot_e_orig += res.edge_orig[e].size();
+ }
}
for (int f = 0; f < nf; ++f) {
- tot_f_orig += res.face_orig[f].size();
+ if (input->need_ids) {
+ tot_f_orig += res.face_orig[f].size();
+ }
tot_f_lens += res.face[f].size();
}
output->vert_coords = static_cast<decltype(output->vert_coords)>(
MEM_malloc_arrayN(nv, sizeof(output->vert_coords[0]), __func__));
- output->verts_orig = static_cast<int *>(MEM_malloc_arrayN(tot_v_orig, sizeof(int), __func__));
- output->verts_orig_start_table = static_cast<int *>(
- MEM_malloc_arrayN(nv, sizeof(int), __func__));
- output->verts_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nv, sizeof(int), __func__));
output->edges = static_cast<decltype(output->edges)>(
MEM_malloc_arrayN(ne, sizeof(output->edges[0]), __func__));
- output->edges_orig = static_cast<int *>(MEM_malloc_arrayN(tot_e_orig, sizeof(int), __func__));
- output->edges_orig_start_table = static_cast<int *>(
- MEM_malloc_arrayN(ne, sizeof(int), __func__));
- output->edges_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(ne, sizeof(int), __func__));
output->faces = static_cast<int *>(MEM_malloc_arrayN(tot_f_lens, sizeof(int), __func__));
output->faces_start_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
output->faces_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
- output->faces_orig = static_cast<int *>(MEM_malloc_arrayN(tot_f_orig, sizeof(int), __func__));
- output->faces_orig_start_table = static_cast<int *>(
- MEM_malloc_arrayN(nf, sizeof(int), __func__));
- output->faces_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ if (input->need_ids) {
+ output->verts_orig = static_cast<int *>(MEM_malloc_arrayN(tot_v_orig, sizeof(int), __func__));
+ output->verts_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(nv, sizeof(int), __func__));
+ output->verts_orig_len_table = static_cast<int *>(
+ MEM_malloc_arrayN(nv, sizeof(int), __func__));
+ output->edges_orig = static_cast<int *>(MEM_malloc_arrayN(tot_e_orig, sizeof(int), __func__));
+ output->edges_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(ne, sizeof(int), __func__));
+ output->edges_orig_len_table = static_cast<int *>(
+ MEM_malloc_arrayN(ne, sizeof(int), __func__));
+ output->faces_orig = static_cast<int *>(MEM_malloc_arrayN(tot_f_orig, sizeof(int), __func__));
+ output->faces_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ output->faces_orig_len_table = static_cast<int *>(
+ MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ }
+ else {
+ output->verts_orig = nullptr;
+ output->verts_orig_start_table = nullptr;
+ output->verts_orig_len_table = nullptr;
+ output->edges_orig = nullptr;
+ output->edges_orig_start_table = nullptr;
+ output->edges_orig_len_table = nullptr;
+ output->faces_orig = nullptr;
+ output->faces_orig_start_table = nullptr;
+ output->faces_orig_len_table = nullptr;
+ }
int v_orig_index = 0;
for (int v = 0; v < nv; ++v) {
output->vert_coords[v][0] = static_cast<float>(res.vert[v][0]);
output->vert_coords[v][1] = static_cast<float>(res.vert[v][1]);
- int this_start = v_orig_index;
- output->verts_orig_start_table[v] = this_start;
- for (int j : res.vert_orig[v].index_range()) {
- output->verts_orig[v_orig_index++] = res.vert_orig[v][j];
+ if (input->need_ids) {
+ int this_start = v_orig_index;
+ output->verts_orig_start_table[v] = this_start;
+ for (int j : res.vert_orig[v].index_range()) {
+ output->verts_orig[v_orig_index++] = res.vert_orig[v][j];
+ }
+ output->verts_orig_len_table[v] = v_orig_index - this_start;
}
- output->verts_orig_len_table[v] = v_orig_index - this_start;
}
int e_orig_index = 0;
for (int e = 0; e < ne; ++e) {
output->edges[e][0] = res.edge[e].first;
output->edges[e][1] = res.edge[e].second;
- int this_start = e_orig_index;
- output->edges_orig_start_table[e] = this_start;
- for (int j : res.edge_orig[e].index_range()) {
- output->edges_orig[e_orig_index++] = res.edge_orig[e][j];
+ if (input->need_ids) {
+ int this_start = e_orig_index;
+ output->edges_orig_start_table[e] = this_start;
+ for (int j : res.edge_orig[e].index_range()) {
+ output->edges_orig[e_orig_index++] = res.edge_orig[e][j];
+ }
+ output->edges_orig_len_table[e] = e_orig_index - this_start;
}
- output->edges_orig_len_table[e] = e_orig_index - this_start;
}
int f_orig_index = 0;
int f_index = 0;
for (int f = 0; f < nf; ++f) {
+
output->faces_start_table[f] = f_index;
int flen = res.face[f].size();
output->faces_len_table[f] = flen;
for (int j = 0; j < flen; ++j) {
output->faces[f_index++] = res.face[f][j];
}
- int this_start = f_orig_index;
- output->faces_orig_start_table[f] = this_start;
- for (int k : res.face_orig[f].index_range()) {
- output->faces_orig[f_orig_index++] = res.face_orig[f][k];
+ if (input->need_ids) {
+ int this_start = f_orig_index;
+ output->faces_orig_start_table[f] = this_start;
+ for (int k : res.face_orig[f].index_range()) {
+ output->faces_orig[f_orig_index++] = res.face_orig[f][k];
+ }
+ output->faces_orig_len_table[f] = f_orig_index - this_start;
}
- output->faces_orig_len_table[f] = f_orig_index - this_start;
}
return output;
}
@@ -2863,14 +2961,16 @@ extern "C" void BLI_delaunay_2d_cdt_free(::CDT_result *result)
MEM_freeN(result->faces);
MEM_freeN(result->faces_start_table);
MEM_freeN(result->faces_len_table);
- MEM_freeN(result->verts_orig);
- MEM_freeN(result->verts_orig_start_table);
- MEM_freeN(result->verts_orig_len_table);
- MEM_freeN(result->edges_orig);
- MEM_freeN(result->edges_orig_start_table);
- MEM_freeN(result->edges_orig_len_table);
- MEM_freeN(result->faces_orig);
- MEM_freeN(result->faces_orig_start_table);
- MEM_freeN(result->faces_orig_len_table);
+ if (result->verts_orig) {
+ MEM_freeN(result->verts_orig);
+ MEM_freeN(result->verts_orig_start_table);
+ MEM_freeN(result->verts_orig_len_table);
+ MEM_freeN(result->edges_orig);
+ MEM_freeN(result->edges_orig_start_table);
+ MEM_freeN(result->edges_orig_len_table);
+ MEM_freeN(result->faces_orig);
+ MEM_freeN(result->faces_orig_start_table);
+ MEM_freeN(result->faces_orig_len_table);
+ }
MEM_freeN(result);
}
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index 1a00142ddb1..ac034d2b5cd 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -919,8 +919,8 @@ static int delete_soft(const char *file, const char **error_message)
Class NSStringClass = objc_getClass("NSString");
SEL stringWithUTF8StringSel = sel_registerName("stringWithUTF8String:");
- id pathString = ((id(*)(Class, SEL, const char *))objc_msgSend)(
- NSStringClass, stringWithUTF8StringSel, file);
+ id pathString = ((
+ id(*)(Class, SEL, const char *))objc_msgSend)(NSStringClass, stringWithUTF8StringSel, file);
Class NSFileManagerClass = objc_getClass("NSFileManager");
SEL defaultManagerSel = sel_registerName("defaultManager");
@@ -931,8 +931,8 @@ static int delete_soft(const char *file, const char **error_message)
id nsurl = ((id(*)(Class, SEL, id))objc_msgSend)(NSURLClass, fileURLWithPathSel, pathString);
SEL trashItemAtURLSel = sel_registerName("trashItemAtURL:resultingItemURL:error:");
- BOOL deleteSuccessful = ((BOOL(*)(id, SEL, id, id, id))objc_msgSend)(
- fileManager, trashItemAtURLSel, nsurl, nil, nil);
+ BOOL deleteSuccessful = ((
+ BOOL(*)(id, SEL, id, id, id))objc_msgSend)(fileManager, trashItemAtURLSel, nsurl, nil, nil);
if (deleteSuccessful) {
ret = 0;
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 58b109eca10..6e3846e59c6 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -410,7 +410,7 @@ MINLINE float pingpongf(float value, float scale)
return fabsf(fractf((value - scale) / (scale * 2.0f)) * scale * 2.0f - scale);
}
-// Square.
+/* Square. */
MINLINE int square_s(short a)
{
@@ -442,7 +442,7 @@ MINLINE double square_d(double a)
return a * a;
}
-// Cube.
+/* Cube. */
MINLINE int cube_s(short a)
{
@@ -474,7 +474,7 @@ MINLINE double cube_d(double a)
return a * a * a;
}
-// Min/max
+/* Min/max */
MINLINE float min_ff(float a, float b)
{
@@ -807,9 +807,9 @@ MINLINE unsigned char unit_float_to_uchar_clamp(float val)
MINLINE unsigned short unit_float_to_ushort_clamp(float val)
{
- return (unsigned short)((val >= 1.0f - 0.5f / 65535) ?
- 65535 :
- (val <= 0.0f) ? 0 : (val * 65535.0f + 0.5f));
+ return (unsigned short)((val >= 1.0f - 0.5f / 65535) ? 65535 :
+ (val <= 0.0f) ? 0 :
+ (val * 65535.0f + 0.5f));
}
#define unit_float_to_ushort_clamp(val) \
((CHECK_TYPE_INLINE(val, float)), unit_float_to_ushort_clamp(val))
diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c
index da97e697f2f..14eb78648e0 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -685,10 +685,12 @@ static void blackbody_temperature_to_rgb(float rgb[3], float t)
rgb[2] = 0.0f;
}
else {
- int i = (t >= 6365.0f) ?
- 5 :
- (t >= 3315.0f) ? 4 :
- (t >= 1902.0f) ? 3 : (t >= 1449.0f) ? 2 : (t >= 1167.0f) ? 1 : 0;
+ int i = (t >= 6365.0f) ? 5 :
+ (t >= 3315.0f) ? 4 :
+ (t >= 1902.0f) ? 3 :
+ (t >= 1449.0f) ? 2 :
+ (t >= 1167.0f) ? 1 :
+ 0;
const float *r = blackbody_table_r[i];
const float *g = blackbody_table_g[i];
diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c
index 4c50c1c7af8..ad4b844175f 100644
--- a/source/blender/blenlib/intern/math_color_inline.c
+++ b/source/blender/blenlib/intern/math_color_inline.c
@@ -276,14 +276,14 @@ MINLINE void cpack_cpy_3ub(unsigned char r_col[3], const unsigned int pack)
* https://en.wikipedia.org/wiki/Relative_luminance
*
* Real values are:
- * ``Y = 0.2126390059(R) + 0.7151686788(G) + 0.0721923154(B)``
+ * `Y = 0.2126390059(R) + 0.7151686788(G) + 0.0721923154(B)`
* according to: "Derivation of Basic Television Color Equations", RP 177-1993
*
* As this sums slightly above 1.0, the document recommends to use:
- * ``0.2126(R) + 0.7152(G) + 0.0722(B)``, as used here.
+ * `0.2126(R) + 0.7152(G) + 0.0722(B)`, as used here.
*
* The high precision values are used to calculate the rounded byte weights so they add up to 255:
- * ``54(R) + 182(G) + 19(B)``
+ * `54(R) + 182(G) + 19(B)`
*/
MINLINE float rgb_to_grayscale(const float rgb[3])
{
@@ -322,16 +322,14 @@ MINLINE int compare_rgb_uchar(const unsigned char col_a[3],
/* Return triangle noise in [-0.5..1.5[ range */
MINLINE float dither_random_value(float s, float t)
{
- /* Original code from https://www.shadertoy.com/view/4t2SDh */
- /* The noise reshaping technique does not work on CPU.
- * We generate and merge two distribution instead */
- /* Uniform noise in [0..1[ range */
- float nrnd0 = sinf(s * 12.9898f + t * 78.233f) * 43758.5453f;
- float nrnd1 = sinf(s * 19.9898f + t * 119.233f) * 43798.5453f;
- nrnd0 -= floorf(nrnd0);
- nrnd1 -= floorf(nrnd1);
+ /* Uniform noise in [0..1[ range, using common GLSL hash function.
+ * https://stackoverflow.com/questions/12964279/whats-the-origin-of-this-glsl-rand-one-liner. */
+ float hash0 = sinf(s * 12.9898f + t * 78.233f) * 43758.5453f;
+ float hash1 = sinf(s * 19.9898f + t * 119.233f) * 43798.5453f;
+ hash0 -= floorf(hash0);
+ hash1 -= floorf(hash1);
/* Convert uniform distribution into triangle-shaped distribution. */
- return nrnd0 + nrnd1 - 0.5f;
+ return hash0 + hash0 - 0.5f;
}
MINLINE void float_to_byte_dither_v3(
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 80f0008c7eb..803291e4a3b 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -165,7 +165,7 @@ float area_squared_poly_v3(const float verts[][3], unsigned int nr)
/**
* Scalar cross product of a 2d polygon.
*
- * - equivalent to ``area * 2``
+ * - equivalent to `area * 2`
* - useful for checking polygon winding (a positive value is clockwise).
*/
float cross_poly_v2(const float verts[][2], unsigned int nr)
@@ -518,7 +518,7 @@ float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3])
}
/**
- * Check if \a p is inside the 2x planes defined by ``(v1, v2, v3)``
+ * Check if \a p is inside the 2x planes defined by `(v1, v2, v3)`
* where the 3x points define 2x planes.
*
* \param axis_ref: used when v1,v2,v3 form a line and to check if the corner is concave/convex.
@@ -527,7 +527,7 @@ float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3])
* (it just defines the planes).
*
* \return the lowest squared distance to either of the planes.
- * where ``(return < 0.0)`` is outside.
+ * where `(return < 0.0)` is outside.
*
* <pre>
* v1
@@ -1421,7 +1421,7 @@ int isect_seg_seg_v2_lambda_mu_db(const double v1[2],
* \return r_p1, r_p2: Intersection coordinates.
*
* \note The order of assignment for intersection points (\a r_p1, \a r_p2) is predictable,
- * based on the direction defined by ``l2 - l1``,
+ * based on the direction defined by `l2 - l1`,
* this direction compared with the normal of each point on the sphere:
* \a r_p1 always has a >= 0.0 dot product.
* \a r_p2 always has a <= 0.0 dot product.
@@ -3426,7 +3426,7 @@ float ray_point_factor_v3(const float p[3],
/**
* A simplified version of #closest_to_line_v3
- * we only need to return the ``lambda``
+ * we only need to return the `lambda`
*
* \param epsilon: avoid approaching divide-by-zero.
* Passing a zero will just check for nonzero division.
diff --git a/source/blender/blenlib/intern/math_geom_inline.c b/source/blender/blenlib/intern/math_geom_inline.c
index 655d3fcc4c0..1757b0dd525 100644
--- a/source/blender/blenlib/intern/math_geom_inline.c
+++ b/source/blender/blenlib/intern/math_geom_inline.c
@@ -272,7 +272,7 @@ MINLINE float shell_angle_to_dist(const float angle)
return (UNLIKELY(angle < SMALL_NUMBER)) ? 1.0f : fabsf(1.0f / cosf(angle));
}
/**
- * equivalent to ``shell_angle_to_dist(angle_normalized_v3v3(a, b))``
+ * Equivalent to `shell_angle_to_dist(angle_normalized_v3v3(a, b))`.
*/
MINLINE float shell_v3v3_normalized_to_dist(const float a[3], const float b[3])
{
@@ -282,7 +282,7 @@ MINLINE float shell_v3v3_normalized_to_dist(const float a[3], const float b[3])
return (UNLIKELY(angle_cos < SMALL_NUMBER)) ? 1.0f : (1.0f / angle_cos);
}
/**
- * equivalent to ``shell_angle_to_dist(angle_normalized_v2v2(a, b))``
+ * Equivalent to `shell_angle_to_dist(angle_normalized_v2v2(a, b))`.
*/
MINLINE float shell_v2v2_normalized_to_dist(const float a[2], const float b[2])
{
@@ -293,7 +293,7 @@ MINLINE float shell_v2v2_normalized_to_dist(const float a[2], const float b[2])
}
/**
- * equivalent to ``shell_angle_to_dist(angle_normalized_v3v3(a, b) / 2)``
+ * Equivalent to `shell_angle_to_dist(angle_normalized_v3v3(a, b) / 2)`.
*/
MINLINE float shell_v3v3_mid_normalized_to_dist(const float a[3], const float b[3])
{
@@ -307,7 +307,7 @@ MINLINE float shell_v3v3_mid_normalized_to_dist(const float a[3], const float b[
}
/**
- * equivalent to ``shell_angle_to_dist(angle_normalized_v2v2(a, b) / 2)``
+ * Equivalent to `shell_angle_to_dist(angle_normalized_v2v2(a, b) / 2)`.
*/
MINLINE float shell_v2v2_mid_normalized_to_dist(const float a[2], const float b[2])
{
diff --git a/source/blender/blenlib/intern/math_interp.c b/source/blender/blenlib/intern/math_interp.c
index 04fae6a0e68..bd48edf70c0 100644
--- a/source/blender/blenlib/intern/math_interp.c
+++ b/source/blender/blenlib/intern/math_interp.c
@@ -625,7 +625,7 @@ void BLI_ewa_filter(const int width,
* Use a different radius based on interpolation switch,
* just enough to anti-alias when interpolation is off,
* and slightly larger to make result a bit smoother than bilinear interpolation when
- * interpolation is on (minimum values: const float rmin = intpol ? 1.0f : 0.5f;) */
+ * interpolation is on (minimum values: `const float rmin = intpol ? 1.0f : 0.5f;`) */
const float rmin = (intpol ? 1.5625f : 0.765625f) / ff2;
BLI_ewa_imp2radangle(A, B, C, F, &a, &b, &th, &ecc);
if ((b2 = b * b) < rmin) {
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index 5920788821c..b605c3eeead 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -276,7 +276,7 @@ void mul_m4_m4m4_uniq(float R[4][4], const float A[4][4], const float B[4][4])
{
BLI_assert(!ELEM(R, A, B));
- /* matrix product: R[j][k] = A[j][i] . B[i][k] */
+ /* Matrix product: `R[j][k] = A[j][i] . B[i][k]`. */
#ifdef BLI_HAVE_SSE2
__m128 A0 = _mm_loadu_ps(A[0]);
__m128 A1 = _mm_loadu_ps(A[1]);
@@ -321,7 +321,7 @@ void mul_m4_m4m4_db_uniq(double R[4][4], const double A[4][4], const double B[4]
{
BLI_assert(!ELEM(R, A, B));
- /* matrix product: R[j][k] = A[j][i] . B[i][k] */
+ /* Matrix product: `R[j][k] = A[j][i] . B[i][k]`. */
R[0][0] = B[0][0] * A[0][0] + B[0][1] * A[1][0] + B[0][2] * A[2][0] + B[0][3] * A[3][0];
R[0][1] = B[0][0] * A[0][1] + B[0][1] * A[1][1] + B[0][2] * A[2][1] + B[0][3] * A[3][1];
@@ -349,7 +349,7 @@ void mul_m4db_m4db_m4fl_uniq(double R[4][4], const double A[4][4], const float B
/* Remove second check since types don't match. */
BLI_assert(!ELEM(R, A /*, B */));
- /* matrix product: R[j][k] = A[j][i] . B[i][k] */
+ /* Matrix product: `R[j][k] = A[j][i] . B[i][k]`. */
R[0][0] = B[0][0] * A[0][0] + B[0][1] * A[1][0] + B[0][2] * A[2][0] + B[0][3] * A[3][0];
R[0][1] = B[0][0] * A[0][1] + B[0][1] * A[1][1] + B[0][2] * A[2][1] + B[0][3] * A[3][1];
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index db9ece81c59..55f7a152b83 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -1312,7 +1312,7 @@ MINLINE bool is_one_v3(const float v[3])
/* -------------------------------------------------------------------- */
/** \name Vector Comparison
*
- * \note use ``value <= limit``, so a limit of zero doesn't fail on an exact match.
+ * \note use `value <= limit`, so a limit of zero doesn't fail on an exact match.
* \{ */
MINLINE bool equals_v2v2(const float v1[2], const float v2[2])
diff --git a/source/blender/blenlib/intern/memory_utils.c b/source/blender/blenlib/intern/memory_utils.c
index 5ca7b96c136..4bb93877401 100644
--- a/source/blender/blenlib/intern/memory_utils.c
+++ b/source/blender/blenlib/intern/memory_utils.c
@@ -19,7 +19,7 @@
* \brief Generic memory manipulation API.
*
* This is to extend on existing functions
- * such as ``memcpy`` & ``memcmp``.
+ * such as `memcpy` & `memcmp`.
*/
#include <string.h>
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index 8b8850c7cdb..90ffebdb422 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -151,7 +151,7 @@ class TriMeshTopology : NonCopyable {
* Else return NO_INDEX. */
int other_tri_if_manifold(Edge e, int t) const
{
- auto p = edge_tri_.lookup_ptr(e);
+ const auto *p = edge_tri_.lookup_ptr(e);
if (p != nullptr && (*p)->size() == 2) {
return ((**p)[0] == t) ? (**p)[1] : (**p)[0];
}
@@ -204,7 +204,7 @@ TriMeshTopology::TriMeshTopology(const IMesh &tm)
}
edges->append_non_duplicates(e);
- auto p = edge_tri_.lookup_ptr(Edge(v, vnext));
+ auto *p = edge_tri_.lookup_ptr(Edge(v, vnext));
if (p == nullptr) {
edge_tri_.add_new(e, new Vector<int>{t});
}
@@ -238,7 +238,7 @@ TriMeshTopology::~TriMeshTopology()
Vector<Vector<int> *> values;
/* Deconstructing is faster in parallel, so it is worth building an array of things to delete. */
- for (auto item : edge_tri_.values()) {
+ for (auto *item : edge_tri_.values()) {
values.append(item);
}
diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc
index f91dd762e70..5651e52799e 100644
--- a/source/blender/blenlib/intern/mesh_intersect.cc
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -1165,8 +1165,8 @@ static int filter_plane_side(const double3 &p,
* interesect_tri_tri and helper functions.
* This code uses the algorithm of Guigue and Devillers, as described
* in "Faster Triangle-Triangle Intersection Tests".
- * Adapted from github code by Eric Haines:
- * github.com/erich666/jgt-code/tree/master/Volume_08/Number_1/Guigue2003
+ * Adapted from code by Eric Haines:
+ * https://github.com/erich666/jgt-code/tree/master/Volume_08/Number_1/Guigue2003
*/
/**
@@ -1875,6 +1875,16 @@ static void do_cdt(CDT_data &cd)
}
}
+/* Find an original edge index that goes with the edge that the CDT output edge
+ * that goes between verts i0 and i1 (in the CDT output vert indexing scheme).
+ * There may be more than one: if so, prefer one that was originally a face edge.
+ * The input to CDT for a triangle with some intersecting segments from other triangles
+ * will have both edges and a face constraint for the main triangle (this is redundant
+ * but allows us to discover which face edge goes with which output edges).
+ * If there is any face edge, return one of those as the original.
+ * If there is no face edge but there is another edge in the input problem, then that
+ * edge must have come from intersection with another triangle, so set *r_is_intersect
+ * to true in that case. */
static int get_cdt_edge_orig(
int i0, int i1, const CDT_data &cd, const IMesh &in_tm, bool *r_is_intersect)
{
@@ -1900,35 +1910,50 @@ static int get_cdt_edge_orig(
}
/* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */
- /* TODO: if edge has origs from more than on part of the nary input,
+ /* TODO: if edge has origs from more than one part of the nary input,
* then want to set *r_is_intersect to true. */
+ int face_eorig = NO_INDEX;
+ bool have_non_face_eorig = false;
for (int orig_index : cd.cdt_out.edge_orig[e]) {
/* orig_index encodes the triangle and pos within the triangle of the input edge. */
if (orig_index >= foff) {
- int in_face_index = (orig_index / foff) - 1;
- int pos = orig_index % foff;
- /* We need to retrieve the edge orig field from the Face used to populate the
- * in_face_index'th face of the CDT, at the pos'th position of the face. */
- int in_tm_face_index = cd.input_face[in_face_index];
- BLI_assert(in_tm_face_index < in_tm.face_size());
- const Face *facep = in_tm.face(in_tm_face_index);
- BLI_assert(pos < facep->size());
- bool is_rev = cd.is_reversed[in_face_index];
- int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos];
- if (eorig != NO_INDEX) {
- return eorig;
+ if (face_eorig == NO_INDEX) {
+ int in_face_index = (orig_index / foff) - 1;
+ int pos = orig_index % foff;
+ /* We need to retrieve the edge orig field from the Face used to populate the
+ * in_face_index'th face of the CDT, at the pos'th position of the face. */
+ int in_tm_face_index = cd.input_face[in_face_index];
+ BLI_assert(in_tm_face_index < in_tm.face_size());
+ const Face *facep = in_tm.face(in_tm_face_index);
+ BLI_assert(pos < facep->size());
+ bool is_rev = cd.is_reversed[in_face_index];
+ int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos];
+ if (eorig != NO_INDEX) {
+ face_eorig = eorig;
+ }
}
}
else {
- /* This edge came from an edge input to the CDT problem,
- * so it is an intersect edge. */
- *r_is_intersect = true;
- /* TODO: maybe there is an orig index:
- * This happens if an input edge was formed by an input face having
- * an edge that is co-planar with the cluster, while the face as a whole is not. */
- return NO_INDEX;
+ if (!have_non_face_eorig) {
+ have_non_face_eorig = true;
+ }
+ if (face_eorig != NO_INDEX && have_non_face_eorig) {
+ /* Only need at most one orig for each type. */
+ break;
+ }
}
}
+ if (face_eorig != NO_INDEX) {
+ return face_eorig;
+ }
+ if (have_non_face_eorig) {
+ /* This must have been an input to the CDT problem that was an intersection edge. */
+ /* TODO: maybe there is an orig index:
+ * This happens if an input edge was formed by an input face having
+ * an edge that is co-planar with the cluster, while the face as a whole is not. */
+ *r_is_intersect = true;
+ return NO_INDEX;
+ }
return NO_INDEX;
}
diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c
index 01aad5b078f..9850de69b5a 100644
--- a/source/blender/blenlib/intern/noise.c
+++ b/source/blender/blenlib/intern/noise.c
@@ -1294,13 +1294,12 @@ float BLI_noise_generic_turbulence(
* source code in the book "Texturing and Modeling: A procedural approach"
*/
-/*
- * Procedural fBm evaluated at "point"; returns value stored in "value".
+/**
+ * Procedural `fBm` evaluated at "point"; returns value stored in "value".
*
- * Parameters:
- * ``H'' is the fractal increment parameter
- * ``lacunarity'' is the gap between successive frequencies
- * ``octaves'' is the number of frequencies in the fBm
+ * \param H: is the fractal increment parameter.
+ * \param lacunarity: is the gap between successive frequencies.
+ * \param octaves: is the number of frequencies in the `fBm`.
*/
float BLI_noise_mg_fbm(
float x, float y, float z, float H, float lacunarity, float octaves, int noisebasis)
@@ -1359,15 +1358,16 @@ float BLI_noise_mg_fbm(
} /* fBm() */
-/*
- * Procedural multifractal evaluated at "point";
+/**
+ * Procedural multi-fractal evaluated at "point";
* returns value stored in "value".
*
- * Parameters:
- * ``H'' determines the highest fractal dimension
- * ``lacunarity'' is gap between successive frequencies
- * ``octaves'' is the number of frequencies in the fBm
- * ``offset'' is the zero offset, which determines multifractality (NOT USED??)
+ * \param H: determines the highest fractal dimension.
+ * \param lacunarity: is gap between successive frequencies.
+ * \param octaves: is the number of frequencies in the `fBm`.
+ *
+ * \note There used to be a parameter called `offset`, old docs read:
+ * is the zero offset, which determines multi-fractality.
*/
/* this one is in fact rather confusing,
@@ -1429,15 +1429,14 @@ float BLI_noise_mg_multi_fractal(
} /* multifractal() */
-/*
+/**
* Heterogeneous procedural terrain function: stats by altitude method.
* Evaluated at "point"; returns value stored in "value".
*
- * Parameters:
- * ``H'' determines the fractal dimension of the roughest areas
- * ``lacunarity'' is the gap between successive frequencies
- * ``octaves'' is the number of frequencies in the fBm
- * ``offset'' raises the terrain from `sea level'
+ * \param H: Determines the fractal dimension of the roughest areas.
+ * \param lacunarity: Is the gap between successive frequencies.
+ * \param octaves: Is the number of frequencies in the `fBm`.
+ * \param offset: Raises the terrain from `sea level`.
*/
float BLI_noise_mg_hetero_terrain(float x,
float y,
diff --git a/source/blender/blenlib/intern/polyfill_2d.c b/source/blender/blenlib/intern/polyfill_2d.c
index 817572ba85c..9af98359199 100644
--- a/source/blender/blenlib/intern/polyfill_2d.c
+++ b/source/blender/blenlib/intern/polyfill_2d.c
@@ -77,9 +77,9 @@ typedef signed char eSign;
#ifdef USE_KDTREE
/**
* Spatial optimization for point-in-triangle intersection checks.
- * The simple version of this algorithm is ``O(n^2)`` complexity
+ * The simple version of this algorithm is `O(n^2)` complexity
* (every point needing to check the triangle defined by every other point),
- * Using a binary-tree reduces the complexity to ``O(n log n)``
+ * Using a binary-tree reduces the complexity to `O(n log n)`
* plus some overhead of creating the tree.
*
* This is a single purpose KDTree based on BLI_kdtree with some modifications
@@ -898,7 +898,7 @@ void BLI_polyfill_calc_arena(const float (*coords)[2],
* \param coords_sign: Pass this when we know the sign in advance to avoid extra calculations.
*
* \param r_tris: This array is filled in with triangle indices in clockwise order.
- * The length of the array must be ``coords_tot - 2``.
+ * The length of the array must be `coords_tot - 2`.
* Indices are guaranteed to be assigned to unique triangles, with valid indices,
* even in the case of degenerate input (self intersecting polygons, zero area ears... etc).
*/
diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c
index 6e5a3e961a5..006a3798dcd 100644
--- a/source/blender/blenlib/intern/smallhash.c
+++ b/source/blender/blenlib/intern/smallhash.c
@@ -34,11 +34,11 @@
* Otherwise #GHash should be used instead.
*
* #SmallHashEntry.key
- * - ``SMHASH_KEY_UNUSED`` means the key in the cell has not been initialized.
+ * - `SMHASH_KEY_UNUSED` means the key in the cell has not been initialized.
*
* #SmallHashEntry.val
- * - ``SMHASH_CELL_UNUSED`` means this cell is inside a key series.
- * - ``SMHASH_CELL_FREE`` means this cell terminates a key series.
+ * - `SMHASH_CELL_UNUSED` means this cell is inside a key series.
+ * - `SMHASH_CELL_FREE` means this cell terminates a key series.
*
* Note that the values and keys are often pointers or index values,
* use the maximum values to avoid real pointers colliding with magic numbers.
diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c
index 61d095658a3..5541d75bc73 100644
--- a/source/blender/blenlib/intern/string.c
+++ b/source/blender/blenlib/intern/string.c
@@ -232,7 +232,7 @@ size_t BLI_vsnprintf(char *__restrict buffer,
}
/**
- * A version of #BLI_vsnprintf that returns ``strlen(buffer)``
+ * A version of #BLI_vsnprintf that returns `strlen(buffer)`
*/
size_t BLI_vsnprintf_rlen(char *__restrict buffer,
size_t maxncpy,
@@ -278,7 +278,7 @@ size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict
}
/**
- * A version of #BLI_snprintf that returns ``strlen(dst)``
+ * A version of #BLI_snprintf that returns `strlen(dst)`
*/
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
{
diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c
index 6a2ed8fac2c..d2666c6fe63 100644
--- a/source/blender/blenlib/intern/string_utils.c
+++ b/source/blender/blenlib/intern/string_utils.c
@@ -182,8 +182,8 @@ void BLI_string_flip_side_name(char *r_name,
/* We first check the case with a .### extension, let's find the last period */
if (isdigit(r_name[len - 1])) {
- index = strrchr(r_name, '.'); /* last occurrence. */
- if (index && isdigit(index[1])) { /* doesn't handle case bone.1abc2 correct..., whatever! */
+ index = strrchr(r_name, '.'); /* Last occurrence. */
+ if (index && isdigit(index[1])) { /* Doesn't handle case `bone.1abc2` correct..., whatever! */
if (strip_number == false) {
BLI_strncpy(number, index, name_len);
}
@@ -194,7 +194,7 @@ void BLI_string_flip_side_name(char *r_name,
BLI_strncpy(prefix, r_name, name_len);
- /* first case; separator . - _ with extensions r R l L. */
+ /* First case; separator (`.` or `_`) with extensions in `r R l L`. */
if ((len > 1) && is_char_sep(r_name[len - 2])) {
is_set = true;
switch (r_name[len - 1]) {
diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c
index 87330cf4899..66d0b44cfb3 100644
--- a/source/blender/blenlib/intern/system.c
+++ b/source/blender/blenlib/intern/system.c
@@ -184,7 +184,7 @@ size_t BLI_system_memory_max_in_megabytes(void)
/* Maximum addressable bytes on this platform.
*
* NOTE: Due to the shift arithmetic this is a half of the memory. */
- const size_t limit_bytes_half = (((size_t)1) << ((sizeof(size_t[8])) - 1));
+ const size_t limit_bytes_half = (((size_t)1) << (sizeof(size_t[8]) - 1));
/* Convert it to megabytes and return. */
return (limit_bytes_half >> 20) * 2;
}
diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c
index 33af4894b48..6378d88e2b1 100644
--- a/source/blender/blenlib/intern/task_iterator.c
+++ b/source/blender/blenlib/intern/task_iterator.c
@@ -180,14 +180,9 @@ static void task_parallel_iterator_no_threads(const TaskParallelSettings *settin
{
/* Prepare user's TLS data. */
void *userdata_chunk = settings->userdata_chunk;
- const size_t userdata_chunk_size = settings->userdata_chunk_size;
- void *userdata_chunk_local = NULL;
- const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
- if (use_userdata_chunk) {
- userdata_chunk_local = MALLOCA(userdata_chunk_size);
- memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
+ if (userdata_chunk) {
if (settings->func_init != NULL) {
- settings->func_init(state->userdata, userdata_chunk_local);
+ settings->func_init(state->userdata, userdata_chunk);
}
}
@@ -196,12 +191,11 @@ static void task_parallel_iterator_no_threads(const TaskParallelSettings *settin
parallel_iterator_func_do(state, userdata_chunk);
- if (use_userdata_chunk) {
+ if (userdata_chunk) {
if (settings->func_free != NULL) {
/* `func_free` should only free data that was created during execution of `func`. */
- settings->func_free(state->userdata, userdata_chunk_local);
+ settings->func_free(state->userdata, userdata_chunk);
}
- MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size);
}
}
@@ -409,29 +403,22 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
TaskParallelMempoolFunc func,
const TaskParallelSettings *settings)
{
- TaskPool *task_pool;
- ParallelMempoolState state;
- int i, num_threads, num_tasks;
-
- if (BLI_mempool_len(mempool) == 0) {
+ if (UNLIKELY(BLI_mempool_len(mempool) == 0)) {
return;
}
void *userdata_chunk = settings->userdata_chunk;
const size_t userdata_chunk_size = settings->userdata_chunk_size;
- void *userdata_chunk_local = NULL;
void *userdata_chunk_array = NULL;
const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
if (!settings->use_threading) {
TaskParallelTLS tls = {NULL};
if (use_userdata_chunk) {
- userdata_chunk_local = MALLOCA(userdata_chunk_size);
- memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
if (settings->func_init != NULL) {
- settings->func_init(state.userdata, userdata_chunk_local);
+ settings->func_init(userdata, userdata_chunk);
}
- tls.userdata_chunk = userdata_chunk_local;
+ tls.userdata_chunk = userdata_chunk;
}
BLI_mempool_iter iter;
@@ -442,23 +429,25 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
func(userdata, item, &tls);
}
- if (settings->func_free != NULL) {
- /* `func_free` should only free data that was created during execution of `func`. */
- settings->func_free(userdata, userdata_chunk_local);
+ if (use_userdata_chunk) {
+ if (settings->func_free != NULL) {
+ /* `func_free` should only free data that was created during execution of `func`. */
+ settings->func_free(userdata, userdata_chunk);
+ }
}
- MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size);
return;
}
- task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH);
- num_threads = BLI_task_scheduler_num_threads();
+ ParallelMempoolState state;
+ TaskPool *task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH);
+ const int num_threads = BLI_task_scheduler_num_threads();
/* The idea here is to prevent creating task for each of the loop iterations
* and instead have tasks which are evenly distributed across CPU cores and
* pull next item to be crunched using the threaded-aware BLI_mempool_iter.
*/
- num_tasks = num_threads + 2;
+ const int num_tasks = num_threads + 2;
state.userdata = userdata;
state.func = func;
@@ -470,7 +459,8 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
ParallelMempoolTaskData *mempool_iterator_data = mempool_iter_threadsafe_create(
mempool, (size_t)num_tasks);
- for (i = 0; i < num_tasks; i++) {
+ for (int i = 0; i < num_tasks; i++) {
+ void *userdata_chunk_local = NULL;
if (use_userdata_chunk) {
userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
@@ -489,7 +479,7 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
if (use_userdata_chunk) {
if ((settings->func_free != NULL) || (settings->func_reduce != NULL)) {
- for (i = 0; i < num_tasks; i++) {
+ for (int i = 0; i < num_tasks; i++) {
if (settings->func_reduce) {
settings->func_reduce(
userdata, userdata_chunk, mempool_iterator_data[i].tls.userdata_chunk);
diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc
index 6250c1b9986..cbb5bf34477 100644
--- a/source/blender/blenlib/intern/task_pool.cc
+++ b/source/blender/blenlib/intern/task_pool.cc
@@ -532,7 +532,7 @@ bool BLI_task_pool_current_canceled(TaskPool *pool)
case TASK_POOL_BACKGROUND_SERIAL:
return background_task_pool_canceled(pool);
}
- BLI_assert("BLI_task_pool_canceled: Control flow should not come here!");
+ BLI_assert_msg(0, "BLI_task_pool_canceled: Control flow should not come here!");
return false;
}
diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c
index 7d7436411ac..69c383061fc 100644
--- a/source/blender/blenlib/intern/timecode.c
+++ b/source/blender/blenlib/intern/timecode.c
@@ -39,7 +39,7 @@
* Generate time-code/frame number string and store in \a str
*
* \param str: destination string
- * \param maxncpy: maximum number of characters to copy ``sizeof(str)``
+ * \param maxncpy: maximum number of characters to copy `sizeof(str)`
* \param brevity_level: special setting for #View2D grid drawing,
* used to specify how detailed we need to be
* \param time_seconds: time total time in seconds
@@ -115,7 +115,7 @@ size_t BLI_timecode_string_from_time(char *str,
str, maxncpy, "%s%02d:%02d+%02d", neg, minutes, seconds, frames);
}
else {
- rlen = BLI_snprintf_rlen(str, maxncpy, "%s%d+%02d", neg, seconds, frames);
+ rlen = BLI_snprintf_rlen(str, maxncpy, "%s00:%02d+%02d", neg, seconds, frames);
}
}
else {
@@ -199,7 +199,7 @@ size_t BLI_timecode_string_from_time(char *str,
* Generate time string and store in \a str
*
* \param str: destination string
- * \param maxncpy: maximum number of characters to copy ``sizeof(str)``
+ * \param maxncpy: maximum number of characters to copy `sizeof(str)`
* \param time_seconds: time total time in seconds
* \return length of \a str
*/
@@ -229,7 +229,7 @@ size_t BLI_timecode_string_from_time_simple(char *str,
* Generate time string and store in \a str
*
* \param str: destination string
- * \param maxncpy: maximum number of characters to copy ``sizeof(str)``
+ * \param maxncpy: maximum number of characters to copy `sizeof(str)`
* \param brevity_level: special setting for #View2D grid drawing,
* used to specify how detailed we need to be
* \param time_seconds: time total time in seconds
diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c
index beeae175869..d5c9c5cd5e6 100644
--- a/source/blender/blenlib/intern/winstuff.c
+++ b/source/blender/blenlib/intern/winstuff.c
@@ -30,7 +30,7 @@
# include "MEM_guardedalloc.h"
-# define WIN32_SKIP_HKEY_PROTECTION // need to use HKEY
+# define WIN32_SKIP_HKEY_PROTECTION /* Need to use HKEY. */
# include "BLI_path_util.h"
# include "BLI_string.h"
# include "BLI_utildefines.h"
diff --git a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc
index 08a3818e18f..f221036419e 100644
--- a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc
+++ b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc
@@ -241,7 +241,7 @@ template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_result
for (int i : r.edge.index_range()) {
os << "e" << i << " = (" << r.edge[i].first << ", " << r.edge[i].second << ")\n";
os << " orig: ";
- for (int j : r.edge_orig[i].size()) {
+ for (int j : r.edge_orig[i].index_range()) {
os << r.edge_orig[i][j] << " ";
}
os << "\n";
@@ -797,6 +797,55 @@ template<typename T> void crosssegs_test()
}
}
+template<typename T> void cutacrosstri_test()
+{
+ /* Right triangle with horizontal segment exactly crossing in the middle. */
+ const char *spec = R"(5 1 1
+ 0.0 0.0
+ 1.0 0.0
+ 0.0 1.0
+ 0.0 0.5
+ 0.5 0.5
+ 3 4
+ 0 1 2
+ )";
+
+ CDT_input<T> in = fill_input_from_string<T>(spec);
+ CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
+ EXPECT_EQ(out.vert.size(), 5);
+ EXPECT_EQ(out.edge.size(), 7);
+ EXPECT_EQ(out.face.size(), 3);
+ int v0_out = get_orig_index(out.vert_orig, 0);
+ int v1_out = get_orig_index(out.vert_orig, 1);
+ int v2_out = get_orig_index(out.vert_orig, 2);
+ int v3_out = get_orig_index(out.vert_orig, 3);
+ int v4_out = get_orig_index(out.vert_orig, 4);
+ EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1 && v4_out != -1);
+ if (out.face.size() == 3) {
+ int e0_out = get_orig_index(out.edge_orig, 0);
+ EXPECT_NE(e0_out, -1);
+ int fe0_out = get_output_edge_index(out, v0_out, v1_out);
+ EXPECT_NE(fe0_out, -1);
+ int fe1a_out = get_output_edge_index(out, v1_out, v4_out);
+ EXPECT_NE(fe1a_out, -1);
+ int fe1b_out = get_output_edge_index(out, v4_out, v2_out);
+ EXPECT_NE(fe1b_out, -1);
+ if (fe1a_out != 0 && fe1b_out != 0) {
+ EXPECT_EQ(e0_out, get_orig_index(out.edge_orig, 0));
+ EXPECT_TRUE(out.edge_orig[fe1a_out].size() == 1 && out.edge_orig[fe1a_out][0] == 11);
+ EXPECT_TRUE(out.edge_orig[fe1b_out].size() == 1 && out.edge_orig[fe1b_out][0] == 11);
+ }
+ int e_diag = get_output_edge_index(out, v0_out, v4_out);
+ EXPECT_NE(e_diag, -1);
+ if (e_diag != -1) {
+ EXPECT_EQ(out.edge_orig[e_diag].size(), 0);
+ }
+ }
+ if (DO_DRAW) {
+ graph_draw<T>("CutAcrossTri", out.vert, out.edge, out.face);
+ }
+}
+
template<typename T> void diamondcross_test()
{
/* Diamond with constraint edge from top to bottom. Some dup verts. */
@@ -1470,6 +1519,11 @@ TEST(delaunay_d, CrossSegs)
crosssegs_test<double>();
}
+TEST(delaunay_d, CutAcrossTri)
+{
+ cutacrosstri_test<double>();
+}
+
TEST(delaunay_d, DiamondCross)
{
diamondcross_test<double>();
@@ -1610,6 +1664,11 @@ TEST(delaunay_m, CrossSegs)
crosssegs_test<mpq_class>();
}
+TEST(delaunay_m, CutAcrossTri)
+{
+ cutacrosstri_test<mpq_class>();
+}
+
TEST(delaunay_m, DiamondCross)
{
diamondcross_test<mpq_class>();
@@ -1697,14 +1756,40 @@ TEST(delaunay_d, CintTwoFace)
input.faces_len_table = faces_len;
input.faces_start_table = faces_start;
input.epsilon = 1e-5f;
+ input.need_ids = false;
::CDT_result *output = BLI_delaunay_2d_cdt_calc(&input, CDT_FULL);
BLI_delaunay_2d_cdt_free(output);
}
+
+TEST(delaunay_d, CintTwoFaceNoIds)
+{
+ float vert_coords[][2] = {
+ {0.0, 0.0}, {1.0, 0.0}, {0.5, 1.0}, {1.1, 1.0}, {1.1, 0.0}, {1.6, 1.0}};
+ int faces[] = {0, 1, 2, 3, 4, 5};
+ int faces_len[] = {3, 3};
+ int faces_start[] = {0, 3};
+
+ ::CDT_input input;
+ input.verts_len = 6;
+ input.edges_len = 0;
+ input.faces_len = 2;
+ input.vert_coords = vert_coords;
+ input.edges = nullptr;
+ input.faces = faces;
+ input.faces_len_table = faces_len;
+ input.faces_start_table = faces_start;
+ input.epsilon = 1e-5f;
+ input.need_ids = true;
+ ::CDT_result *output = BLI_delaunay_2d_cdt_calc(&input, CDT_FULL);
+ BLI_delaunay_2d_cdt_free(output);
+}
+
#endif
#if DO_TEXT_TESTS
template<typename T>
-void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_output_type otype)
+void text_test(
+ int num_arc_points, int num_lets_per_line, int num_lines, CDT_output_type otype, bool need_ids)
{
constexpr bool print_timing = true;
/*
@@ -1789,7 +1874,7 @@ void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_out
vec2<T> start_co = b_vert[arc_origin_vert];
vec2<T> end_co = b_vert[arc_terminal_vert];
vec2<T> center_co = 0.5 * (start_co + end_co);
- BLI_assert(start_co[0] == end_co[2]);
+ BLI_assert(start_co[0] == end_co[0]);
double radius = abs(math_to_double<T>(end_co[1] - center_co[1]));
double angle_delta = M_PI / (num_arc_points + 1);
int start_vert = b_before_arcs_in.vert.size() + arc * num_arc_points;
@@ -1843,12 +1928,18 @@ void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_out
}
}
in.epsilon = b_before_arcs_in.epsilon;
+ in.need_ids = need_ids;
double tstart = PIL_check_seconds_timer();
CDT_result<T> out = delaunay_2d_calc(in, otype);
double tend = PIL_check_seconds_timer();
if (print_timing) {
std::cout << "time = " << tend - tstart << "\n";
}
+ if (!need_ids) {
+ EXPECT_EQ(out.vert_orig.size(), 0);
+ EXPECT_EQ(out.edge_orig.size(), 0);
+ EXPECT_EQ(out.face_orig.size(), 0);
+ }
if (DO_DRAW) {
std::string label = "Text arcpts=" + std::to_string(num_arc_points);
if (num_lets_per_line > 1) {
@@ -1857,25 +1948,103 @@ void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_out
if (num_lines > 1) {
label += " lines=" + std::to_string(num_lines);
}
+ if (!need_ids) {
+ label += " no_ids";
+ }
+ if (otype != CDT_INSIDE_WITH_HOLES) {
+ label += " otype=" + std::to_string(otype);
+ }
graph_draw<T>(label, out.vert, out.edge, out.face);
}
}
TEST(delaunay_d, TextB10)
{
- text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES);
+ text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES, true);
+}
+
+TEST(delaunay_d, TextB10_noids)
+{
+ text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES, false);
+}
+
+TEST(delaunay_d, TextB10_inside)
+{
+ text_test<double>(10, 1, 1, CDT_INSIDE, true);
+}
+
+TEST(delaunay_d, TextB10_inside_noids)
+{
+ text_test<double>(10, 1, 1, CDT_INSIDE, false);
+}
+
+TEST(delaunay_d, TextB10_constraints)
+{
+ text_test<double>(10, 1, 1, CDT_CONSTRAINTS, true);
+}
+
+TEST(delaunay_d, TextB10_constraints_noids)
+{
+ text_test<double>(10, 1, 1, CDT_CONSTRAINTS, false);
+}
+
+TEST(delaunay_d, TextB10_constraints_valid_bmesh)
+{
+ text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH, true);
+}
+
+TEST(delaunay_d, TextB10_constraints_valid_bmesh_noids)
+{
+ text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH, false);
+}
+
+TEST(delaunay_d, TextB10_constraints_valid_bmesh_with_holes)
+{
+ text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES, true);
+}
+
+TEST(delaunay_d, TextB10_constraints_valid_bmesh_with_holes_noids)
+{
+ text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES, false);
}
TEST(delaunay_d, TextB200)
{
- text_test<double>(200, 1, 1, CDT_INSIDE_WITH_HOLES);
+ text_test<double>(200, 1, 1, CDT_INSIDE_WITH_HOLES, true);
}
TEST(delaunay_d, TextB10_10_10)
{
- text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES);
+ text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES, true);
+}
+
+TEST(delaunay_d, TextB10_10_10_noids)
+{
+ text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES, false);
}
+# ifdef WITH_GMP
+TEST(delaunay_m, TextB10)
+{
+ text_test<mpq_class>(10, 1, 1, CDT_INSIDE_WITH_HOLES, true);
+}
+
+TEST(delaunay_m, TextB200)
+{
+ text_test<mpq_class>(200, 1, 1, CDT_INSIDE_WITH_HOLES, true);
+}
+
+TEST(delaunay_m, TextB10_10_10)
+{
+ text_test<mpq_class>(10, 10, 10, CDT_INSIDE_WITH_HOLES, true);
+}
+
+TEST(delaunay_m, TextB10_10_10_noids)
+{
+ text_test<mpq_class>(10, 10, 10, CDT_INSIDE_WITH_HOLES, false);
+}
+# endif
+
#endif
#if DO_RANDOM_TESTS
diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc
index 24fa7f1a476..0329fc156c0 100644
--- a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc
+++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc
@@ -866,11 +866,11 @@ static void fill_sphere_data(int nrings,
};
Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */
/*
- * (x, y , z) is given from inclination theta and azimuth phi,
- * where 0 <= theta <= pi; 0 <= phi <= 2pi.
- * x = radius * sin(theta) cos(phi)
- * y = radius * sin(theta) sin(phi)
- * z = radius * cos(theta)
+ * (x, y, z) is given from inclination theta and azimuth phi,
+ * where: `0 <= theta <= pi; 0 <= phi <= 2pi`.
+ * `x = radius * sin(theta) cos(phi)`
+ * `y = radius * sin(theta) sin(phi)`
+ * `z = radius * cos(theta)`
*/
for (int s = 0; s < nsegs; ++s) {
double phi = s * delta_phi;
diff --git a/source/blender/blenlib/tests/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc
index fb8b894bfd5..4ecea8031ca 100644
--- a/source/blender/blenlib/tests/BLI_string_ref_test.cc
+++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc
@@ -278,6 +278,44 @@ TEST(string_ref, DropSuffixLargeN)
EXPECT_EQ(ref2, "");
}
+TEST(string_ref, TrimArbitrary)
+{
+ StringRef ref1("test");
+ StringRef ref2(" test ");
+ StringRef ref3(" \t Urož with spaces ");
+ StringRef ref4("žžžžleepyžžž");
+ EXPECT_EQ(ref1.trim("t"), "es");
+ EXPECT_EQ(ref1.trim("te"), "s");
+ EXPECT_EQ(ref1.trim("test"), "");
+ EXPECT_EQ(ref2.trim("t"), " test ");
+ EXPECT_EQ(ref2.trim(""), " test ");
+ EXPECT_EQ(ref3.trim(" "), "\t Urož with spaces"); /* TAB should be kept. */
+ EXPECT_EQ(ref4.trim("ž"), "leepy");
+}
+
+TEST(string_ref, TrimWhitespace)
+{
+ StringRef ref1("test");
+ StringRef ref2(" test ");
+ StringRef ref3(" \t Urož with spaces ");
+ StringRef ref4(" \t \n\r \t ");
+ EXPECT_EQ(ref1.trim(), "test");
+ EXPECT_EQ(ref2.trim(), "test");
+ EXPECT_EQ(ref3.trim(), "Urož with spaces");
+ EXPECT_EQ(ref4.trim(), "");
+}
+
+TEST(string_ref, TrimCharacter)
+{
+ StringRef ref1("test");
+ StringRef ref2(" test ");
+ StringRef ref3("does this work?");
+ EXPECT_EQ(ref1.trim('t'), "es");
+ EXPECT_EQ(ref1.trim('p'), "test");
+ EXPECT_EQ(ref2.trim(' '), "test");
+ EXPECT_EQ(ref3.trim('\000'), "does this work?");
+}
+
TEST(string_ref, Substr)
{
StringRef ref("hello world");
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 03fb4149d7b..e48c305fc4b 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -464,6 +464,13 @@ void blo_join_main(ListBase *mainlist)
Main *tojoin, *mainl;
mainl = mainlist->first;
+
+ if (mainl->id_map != NULL) {
+ /* Cannot keep this since we add some IDs from joined mains. */
+ BKE_main_idmap_destroy(mainl->id_map);
+ mainl->id_map = NULL;
+ }
+
while ((tojoin = mainl->next)) {
add_main_to_main(mainl, tojoin);
BLI_remlink(mainlist, tojoin);
@@ -502,6 +509,12 @@ void blo_split_main(ListBase *mainlist, Main *main)
return;
}
+ if (main->id_map != NULL) {
+ /* Cannot keep this since we remove some IDs from given main. */
+ BKE_main_idmap_destroy(main->id_map);
+ main->id_map = NULL;
+ }
+
/* (Library.temp_index -> Main), lookup table */
const uint lib_main_array_len = BLI_listbase_count(&main->libraries);
Main **lib_main_array = MEM_malloc_arrayN(lib_main_array_len, sizeof(*lib_main_array), __func__);
@@ -2444,7 +2457,7 @@ static void direct_link_id_common(
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag)
{
if (!BLO_read_data_is_undo(reader)) {
- /* When actually reading a file , we do want to reset/re-generate session uuids.
+ /* When actually reading a file, we do want to reset/re-generate session uuids.
* In undo case, we want to re-use existing ones. */
id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
}
@@ -3209,6 +3222,10 @@ static ID *create_placeholder(Main *mainvar, const short idcode, const char *idn
BLI_addtail(lb, ph_id);
id_sort_by_name(lb, ph_id, NULL);
+ if (mainvar->id_map != NULL) {
+ BKE_main_idmap_insert_id(mainvar->id_map, ph_id);
+ }
+
if ((tag & LIB_TAG_TEMP_MAIN) == 0) {
BKE_lib_libblock_session_uuid_ensure(ph_id);
}
@@ -3667,6 +3684,10 @@ static BHead *read_libblock(FileData *fd,
if (r_id) {
*r_id = id_old;
}
+ if (main->id_map != NULL) {
+ BKE_main_idmap_insert_id(main->id_map, id_old);
+ }
+
return blo_bhead_next(fd, bhead);
}
}
@@ -3725,6 +3746,11 @@ static BHead *read_libblock(FileData *fd,
}
direct_link_id(fd, main, id_tag, id, id_old);
+
+ if (main->id_map != NULL) {
+ BKE_main_idmap_insert_id(main->id_map, id);
+ }
+
return blo_bhead_next(fd, bhead);
}
@@ -3748,6 +3774,13 @@ static BHead *read_libblock(FileData *fd,
else if (id_old) {
/* For undo, store contents read into id at id_old. */
read_libblock_undo_restore_at_old_address(fd, main, id, id_old);
+
+ if (main->id_map != NULL) {
+ BKE_main_idmap_insert_id(main->id_map, id_old);
+ }
+ }
+ else if (main->id_map != NULL) {
+ BKE_main_idmap_insert_id(main->id_map, id);
}
return bhead;
@@ -4299,6 +4332,8 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
fd->mainlist = NULL; /* Safety, this is local variable, shall not be used afterward. */
+ BLI_assert(bfd->main->id_map == NULL);
+
return bfd;
}
@@ -4443,9 +4478,16 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname)
static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead)
{
+ if (mainvar->id_map == NULL) {
+ mainvar->id_map = BKE_main_idmap_create(mainvar, false, NULL, MAIN_IDMAP_TYPE_NAME);
+ }
+ BLI_assert(BKE_main_idmap_main_get(mainvar->id_map) == mainvar);
+
const char *idname = blo_bhead_id_name(fd, bhead);
- /* which_libbase can be NULL, intentionally not using idname+2 */
- return BLI_findstring(which_libbase(mainvar, GS(idname)), idname, offsetof(ID, name));
+
+ ID *id = BKE_main_idmap_lookup_name(mainvar->id_map, GS(idname), idname + 2, mainvar->curlib);
+ BLI_assert(id == BLI_findstring(which_libbase(mainvar, GS(idname)), idname, offsetof(ID, name)));
+ return id;
}
/** \} */
@@ -5196,6 +5238,10 @@ static void library_link_end(Main *mainl,
Main *mainvar;
Library *curlib;
+ if (mainl->id_map == NULL) {
+ mainl->id_map = BKE_main_idmap_create(mainl, false, NULL, MAIN_IDMAP_TYPE_NAME);
+ }
+
/* expander now is callback function */
BLO_main_expander(expand_doit_library);
@@ -5401,6 +5447,9 @@ static void read_library_linked_ids(FileData *basefd,
ID *id_next = id->next;
if ((id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) && !(id->flag & LIB_INDIRECT_WEAK_LINK)) {
BLI_remlink(lbarray[a], id);
+ if (mainvar->id_map != NULL) {
+ BKE_main_idmap_remove_id(mainvar->id_map, id);
+ }
/* When playing with lib renaming and such, you may end with cases where
* you have more than one linked ID of the same data-block from same
@@ -5569,6 +5618,10 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
if (fd) {
do_it = true;
+
+ if (mainptr->id_map == NULL) {
+ mainptr->id_map = BKE_main_idmap_create(mainptr, false, NULL, MAIN_IDMAP_TYPE_NAME);
+ }
}
/* Read linked data-locks for each link placeholder, and replace
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 8a7bc375ea9..e56c1995363 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -132,7 +132,7 @@ static void sequencer_init_preview_region(ARegion *region)
region->v2d.max[0] = 12000.0f;
region->v2d.max[1] = 12000.0f;
region->v2d.cur = region->v2d.tot;
- region->v2d.align = V2D_ALIGN_FREE; // (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
+ region->v2d.align = V2D_ALIGN_FREE; /* `(V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y)` */
region->v2d.keeptot = V2D_KEEPTOT_FREE;
}
@@ -655,8 +655,10 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
Tex *tx;
ParticleSettings *part;
Object *ob;
- // PTCacheID *pid;
- // ListBase pidlist;
+#if 0
+ PTCacheID *pid;
+ ListBase pidlist;
+#endif
bSound *sound;
Sequence *seq;
@@ -766,12 +768,15 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
/* set old pointcaches to have disk cache flag */
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- // BKE_ptcache_ids_from_object(&pidlist, ob);
+#if 0
+ BKE_ptcache_ids_from_object(&pidlist, ob);
- // for (pid = pidlist.first; pid; pid = pid->next)
- // pid->cache->flag |= PTCACHE_DISK_CACHE;
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ pid->cache->flag |= PTCACHE_DISK_CACHE;
+ }
- // BLI_freelistN(&pidlist);
+ BLI_freelistN(&pidlist);
+#endif
}
/* type was a mixed flag & enum. move the 2d flag elsewhere */
@@ -789,18 +794,23 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
Tex *tex;
Scene *sce;
ToolSettings *ts;
- // PTCacheID *pid;
- // ListBase pidlist;
+#if 0
+ PTCacheID *pid;
+ ListBase pidlist;
+#endif
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- // BKE_ptcache_ids_from_object(&pidlist, ob);
+#if 0
+ BKE_ptcache_ids_from_object(&pidlist, ob);
- // for (pid = pidlist.first; pid; pid = pid->next) {
- // if (BLI_listbase_is_empty(pid->ptcaches))
- // pid->ptcaches->first = pid->ptcaches->last = pid->cache;
- //}
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (BLI_listbase_is_empty(pid->ptcaches)) {
+ pid->ptcaches->first = pid->ptcaches->last = pid->cache;
+ }
+ }
- // BLI_freelistN(&pidlist);
+ BLI_freelistN(&pidlist);
+#endif
if (ob->totcol && ob->matbits == NULL) {
int a;
@@ -842,7 +852,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
Object *ob;
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- if (ob->flag & 8192) { // OB_POSEMODE = 8192
+ if (ob->flag & 8192) { /* OB_POSEMODE = 8192. */
ob->mode |= OB_MODE_POSE;
}
}
@@ -1395,7 +1405,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
}
if ((sce->r.ffcodecdata.flags & FFMPEG_MULTIPLEX_AUDIO) == 0) {
- sce->r.ffcodecdata.audio_codec = 0x0; // CODEC_ID_NONE
+ sce->r.ffcodecdata.audio_codec = 0x0; /* `CODEC_ID_NONE` */
}
SEQ_ALL_BEGIN (sce->ed, seq) {
@@ -1735,7 +1745,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
/* New Settings */
if (!MAIN_VERSION_ATLEAST(bmain, 252, 5)) {
- brush->flag |= BRUSH_SPACE_ATTEN; // explicitly enable adaptive space
+ brush->flag |= BRUSH_SPACE_ATTEN; /* Explicitly enable adaptive space. */
/* spacing was originally in pixels, convert it to percentage for new version
* size should not be zero due to sanity check above
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index 5bf4d3b68b5..858f5d85a90 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -2149,7 +2149,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
- /* NB: scene->nodetree is a local ID block, has been direct_link'ed */
+ /* NOTE: `scene->nodetree` is a local ID block, has been direct_link'ed. */
if (scene->nodetree) {
scene->nodetree->active_viewer_key = active_viewer_key;
}
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 776f6c54363..1d46c0d5790 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -1115,8 +1115,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "char", "gpencil_v3d_align")) {
ts->gpencil_v3d_align = GP_PROJECT_VIEWSPACE;
ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE;
- ts->gpencil_seq_align = GP_PROJECT_VIEWSPACE;
- ts->gpencil_ima_align = GP_PROJECT_VIEWSPACE;
}
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 0645380c4cb..af05c4b902f 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -1939,7 +1939,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
ViewLayer *view_layer;
for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
view_layer->flag |= VIEW_LAYER_FREESTYLE;
- view_layer->layflag = 0x7FFF; /* solid ztra halo edge strand */
+ view_layer->layflag = 0x7FFF; /* solid Z-transparency halo edge strand. */
view_layer->passflag = SCE_PASS_COMBINED | SCE_PASS_Z;
view_layer->pass_alpha_threshold = 0.5f;
BKE_freestyle_config_init(&view_layer->freestyle_config);
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 3c58c29c162..9aec18ea279 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -29,15 +29,20 @@
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_collection_types.h"
+#include "DNA_curve_types.h"
#include "DNA_genfile.h"
#include "DNA_listBase.h"
+#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
#include "DNA_text_types.h"
+#include "DNA_workspace_types.h"
#include "BKE_action.h"
#include "BKE_animsys.h"
+#include "BKE_asset.h"
#include "BKE_collection.h"
#include "BKE_deform.h"
+#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -46,11 +51,10 @@
#include "BLO_readfile.h"
#include "MEM_guardedalloc.h"
#include "readfile.h"
-#include "versioning_common.h"
#include "SEQ_sequencer.h"
-#include "MEM_guardedalloc.h"
+#include "RNA_access.h"
#include "versioning_common.h"
@@ -97,13 +101,87 @@ static void move_vertex_group_names_to_object_data(Main *bmain)
if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
ListBase *new_defbase = BKE_object_defgroup_list_mutable(object);
- /* Clear the list in case the it was already assigned from another object. */
- BLI_freelistN(new_defbase);
- *new_defbase = object->defbase;
+ /* Choose the longest vertex group name list among all linked duplicates. */
+ if (BLI_listbase_count(&object->defbase) < BLI_listbase_count(new_defbase)) {
+ BLI_freelistN(&object->defbase);
+ }
+ else {
+ /* Clear the list in case the it was already assigned from another object. */
+ BLI_freelistN(new_defbase);
+ *new_defbase = object->defbase;
+ }
}
}
}
+static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const ListBase *seqbase)
+{
+ /* Old SpeedControlVars->flags. */
+#define SEQ_SPEED_INTEGRATE (1 << 0)
+#define SEQ_SPEED_COMPRESS_IPO_Y (1 << 2)
+
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (seq->type == SEQ_TYPE_SPEED) {
+ SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
+ const char *substr = NULL;
+ float globalSpeed = v->globalSpeed;
+ if (seq->flag & SEQ_USE_EFFECT_DEFAULT_FADE) {
+ if (globalSpeed == 1.0f) {
+ v->speed_control_type = SEQ_SPEED_STRETCH;
+ }
+ else {
+ v->speed_control_type = SEQ_SPEED_MULTIPLY;
+ v->speed_fader = globalSpeed *
+ ((float)seq->seq1->len /
+ max_ff((float)(seq->seq1->enddisp - seq->seq1->start), 1.0f));
+ }
+ }
+ else if (v->flags & SEQ_SPEED_INTEGRATE) {
+ v->speed_control_type = SEQ_SPEED_MULTIPLY;
+ v->speed_fader = seq->speed_fader * globalSpeed;
+ }
+ else if (v->flags & SEQ_SPEED_COMPRESS_IPO_Y) {
+ globalSpeed *= 100.0f;
+ v->speed_control_type = SEQ_SPEED_LENGTH;
+ v->speed_fader_length = seq->speed_fader * globalSpeed;
+ substr = "speed_length";
+ }
+ else {
+ v->speed_control_type = SEQ_SPEED_FRAME_NUMBER;
+ v->speed_fader_frame_number = (int)(seq->speed_fader * globalSpeed);
+ substr = "speed_frame_number";
+ }
+
+ v->flags &= ~(SEQ_SPEED_INTEGRATE | SEQ_SPEED_COMPRESS_IPO_Y);
+
+ if (substr || globalSpeed != 1.0f) {
+ FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
+ if (fcu) {
+ if (globalSpeed != 1.0f) {
+ for (int i = 0; i < fcu->totvert; i++) {
+ BezTriple *bezt = &fcu->bezt[i];
+ bezt->vec[0][1] *= globalSpeed;
+ bezt->vec[1][1] *= globalSpeed;
+ bezt->vec[2][1] *= globalSpeed;
+ }
+ }
+ if (substr) {
+ char *new_path = BLI_str_replaceN(fcu->rna_path, "speed_factor", substr);
+ MEM_freeN(fcu->rna_path);
+ fcu->rna_path = new_path;
+ }
+ }
+ }
+ }
+ else if (seq->type == SEQ_TYPE_META) {
+ do_versions_sequencer_speed_effect_recursive(scene, &seq->seqbase);
+ }
+ }
+
+#undef SEQ_SPEED_INTEGRATE
+#undef SEQ_SPEED_COMPRESS_IPO_Y
+}
+
void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
{
if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
@@ -152,6 +230,14 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
move_vertex_group_names_to_object_data(bmain);
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 13)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->ed != NULL) {
+ do_versions_sequencer_speed_effect_recursive(scene, &scene->ed->seqbase);
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -527,6 +613,82 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
tool_settings->snap_uv_mode &= ~(1 << 4);
}
}
+ LISTBASE_FOREACH (Material *, mat, &bmain->materials) {
+ if (!(mat->lineart.flags & LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS)) {
+ mat->lineart.mat_occlusion = 1;
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 13)) {
+ /* Convert Surface Deform to sparse-capable bind structure. */
+ if (!DNA_struct_elem_find(
+ fd->filesdna, "SurfaceDeformModifierData", "int", "num_mesh_verts")) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_SurfaceDeform) {
+ SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+ if (smd->num_bind_verts && smd->verts) {
+ smd->num_mesh_verts = smd->num_bind_verts;
+
+ for (unsigned int i = 0; i < smd->num_bind_verts; i++) {
+ smd->verts[i].vertex_idx = i;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!DNA_struct_elem_find(
+ fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library")) {
+ LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
+ BKE_asset_library_reference_init_default(&workspace->asset_library);
+ }
+ }
+
+ if (!DNA_struct_elem_find(
+ fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library")) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
+ if (space->spacetype == SPACE_FILE) {
+ SpaceFile *sfile = (SpaceFile *)space;
+ if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) {
+ continue;
+ }
+ BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library);
+ }
+ }
+ }
+ }
+ }
+
+ /* Set default 2D annotation placement. */
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ ToolSettings *ts = scene->toolsettings;
+ ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR;
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 14)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ ToolSettings *tool_settings = scene->toolsettings;
+ tool_settings->snap_flag &= ~SCE_SNAP_SEQ;
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 15)) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_SEQ) {
+ SpaceSeq *sseq = (SpaceSeq *)sl;
+ sseq->flag |= SEQ_SHOW_GRID;
+ }
+ }
+ }
+ }
}
/**
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 10b24532014..8362e001ea6 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -183,7 +183,8 @@ static void blo_update_defaults_screen(bScreen *screen,
else if (area->spacetype == SPACE_SEQ) {
SpaceSeq *seq = area->spacedata.first;
seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY |
- SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_DURATION;
+ SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_DURATION |
+ SEQ_SHOW_GRID;
seq->render_size = SEQ_RENDER_SIZE_PROXY_100;
seq->flag |= SEQ_USE_PROXIES;
@@ -318,6 +319,11 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene)
/* Rename render layers. */
BKE_view_layer_rename(bmain, scene, scene->view_layers.first, "View Layer");
+ /* Disable Z pass by default. */
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ view_layer->passflag &= ~SCE_PASS_Z;
+ }
+
/* New EEVEE defaults. */
scene->eevee.bloom_intensity = 0.05f;
scene->eevee.bloom_clamp = 0.0f;
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 42b27f57e2c..c409f0a71fc 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -286,6 +286,11 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
FROM_DEFAULT_V4_UCHAR(space_spreadsheet.selected_highlight);
}
+ if (!USER_VERSION_ATLEAST(300, 15)) {
+ copy_v4_uchar(btheme->space_sequencer.grid, 33);
+ btheme->space_sequencer.grid[3] = 255;
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -883,6 +888,10 @@ void blo_do_versions_userdef(UserDef *userdef)
userdef->sequencer_proxy_setup = USER_SEQ_PROXY_SETUP_AUTOMATIC;
}
+ if (!USER_VERSION_ATLEAST(293, 13)) {
+ BKE_addon_ensure(&userdef->addons, "pose_library");
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index fc29b1d8915..12839a155e4 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -69,7 +69,7 @@
* - write #TEST (#RenderInfo struct. 128x128 blend file preview is optional).
* - write #GLOB (#FileGlobal struct) (some global vars).
* - write #DNA1 (#SDNA struct)
- * - write #USER (#UserDef struct) if filename is ``~/.config/blender/X.XX/config/startup.blend``.
+ * - write #USER (#UserDef struct) if filename is `~/.config/blender/X.XX/config/startup.blend`.
*/
#include <fcntl.h>
diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h
index a5241f6b36d..40db423ba2f 100644
--- a/source/blender/bmesh/bmesh.h
+++ b/source/blender/bmesh/bmesh.h
@@ -37,7 +37,7 @@
*
* BMHeader flags should **never** be read or written to by bmesh operators (see Operators below).
*
- * Access to header flags is done with ``BM_elem_flag_*()`` functions.
+ * Access to header flags is done with `BM_elem_flag_*()` functions.
* \subsection bm_faces Faces
*
* Faces in BMesh are stored as a circular linked list of loops. Loops store per-face-vertex data
@@ -55,7 +55,7 @@
*
* - BMLoop#v - pointer to the vertex associated with this loop.
* - BMLoop#e - pointer to the edge associated with this loop,
- * between verts ``(loop->v, loop->next->v)``
+ * between verts `(loop->v, loop->next->v)`
* - BMLoop#f - pointer to the face associated with this loop.
* \subsection bm_two_side_face 2-Sided Faces
*
@@ -113,7 +113,7 @@
*
* These slots are identified by name, using strings.
*
- * Access to slots is done with ``BMO_slot_***()`` functions.
+ * Access to slots is done with `BMO_slot_***()` functions.
* \subsection bm_tool_flags Tool Flags
*
* The BMesh API provides a set of flags for faces, edges and vertices,
@@ -126,7 +126,7 @@
* These flags should not be confused with header flags, which are used to store persistent flags
* (e.g. selection, hide status, etc).
*
- * Access to tool flags is done with ``BMO_elem_flag_***()`` functions.
+ * Access to tool flags is done with `BMO_elem_flag_***()` functions.
*
* \warning Operators are **never** allowed to read or write to header flags.
* They act entirely on the data inside their input slots.
@@ -162,14 +162,14 @@
*
* These conventions should be used throughout the bmesh module.
*
- * - ``bmesh_kernel_*()`` - Low level API, for primitive functions that others are built ontop of.
- * - ``bmesh_***()`` - Low level API function.
- * - ``bm_***()`` - 'static' functions, not a part of the API at all,
+ * - `bmesh_kernel_*()` - Low level API, for primitive functions that others are built ontop of.
+ * - `bmesh_***()` - Low level API function.
+ * - `bm_***()` - 'static' functions, not a part of the API at all,
* but use prefix since they operate on BMesh data.
- * - ``BM_***()`` - High level BMesh API function for use anywhere.
- * - ``BMO_***()`` - High level operator API function for use anywhere.
- * - ``bmo_***()`` - Low level / internal operator API functions.
- * - ``_bm_***()`` - Functions which are called via macros only.
+ * - `BM_***()` - High level BMesh API function for use anywhere.
+ * - `BMO_***()` - High level operator API function for use anywhere.
+ * - `bmo_***()` - Low level / internal operator API functions.
+ * - `_bm_***()` - Functions which are called via macros only.
*
* \section bm_todo BMesh TODO's
*
diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h
index 423d64ba62c..82c76c8ae4f 100644
--- a/source/blender/bmesh/bmesh_class.h
+++ b/source/blender/bmesh/bmesh_class.h
@@ -185,7 +185,7 @@ typedef struct BMLoop {
struct BMFace *f;
/**
- * Other loops connected to this edge,.
+ * Other loops connected to this edge.
*
* This is typically use for accessing an edges faces,
* however this is done by stepping over it's loops.
diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c
index ee51a03d9fb..6f7b2cbc79f 100644
--- a/source/blender/bmesh/intern/bmesh_construct.c
+++ b/source/blender/bmesh/intern/bmesh_construct.c
@@ -800,7 +800,7 @@ short BM_edge_flag_to_mflag(BMEdge *e)
((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) |
((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) |
((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) |
- ((BM_edge_is_wire(e)) ? ME_LOOSEEDGE : 0) | /* not typical */
+ (BM_edge_is_wire(e) ? ME_LOOSEEDGE : 0) | /* not typical */
ME_EDGERENDER);
}
char BM_face_flag_to_mflag(BMFace *f)
diff --git a/source/blender/bmesh/intern/bmesh_edgeloop.c b/source/blender/bmesh/intern/bmesh_edgeloop.c
index f937ccbccc5..ab14ec23fad 100644
--- a/source/blender/bmesh/intern/bmesh_edgeloop.c
+++ b/source/blender/bmesh/intern/bmesh_edgeloop.c
@@ -209,8 +209,7 @@ static void vs_add(
/* This edge stores a direct path back to the original vertex so we can
* backtrack without having to store an array of previous verts. */
- /* WARNING - setting the edge is not common practice
- * but currently harmless, take care. */
+ /* WARNING: Setting the edge is not common practice but currently harmless, take care. */
BLI_assert(BM_vert_in_edge(e_prev, v));
v->e = e_prev;
diff --git a/source/blender/bmesh/intern/bmesh_iterators.h b/source/blender/bmesh/intern/bmesh_iterators.h
index 4bb83492548..ab4427e6968 100644
--- a/source/blender/bmesh/intern/bmesh_iterators.h
+++ b/source/blender/bmesh/intern/bmesh_iterators.h
@@ -89,7 +89,7 @@ extern const char bm_iter_itype_htype_map[BM_ITYPE_MAX];
# define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype) \
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL); \
ele ? ((void)((iter)->count = BM_iter_mesh_count(itype, bm)), \
- (void)(ele_next = BM_iter_step(iter)), \
+ (void)(BM_CHECK_TYPE_ELEM_ASSIGN(ele_next) = BM_iter_step(iter)), \
1) : \
0; \
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = ele_next)
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index 456275cf157..bd0504b038a 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -24,8 +24,8 @@
struct BMAllocTemplate;
struct BMLoopNorEditDataArray;
-struct MLoopNorSpaceArray;
struct BMPartialUpdate;
+struct MLoopNorSpaceArray;
void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c
index dea6561fe9a..6dfaa0ca688 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_normals.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c
@@ -40,6 +40,16 @@
#include "intern/bmesh_private.h"
+/* Smooth angle to use when tagging edges is disabled entirely. */
+#define EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS -FLT_MAX
+
+static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3],
+ BMEdge *e,
+ const float split_angle_cos);
+static void bm_edge_tag_from_smooth(const float (*fnos)[3],
+ BMEdge *e,
+ const float split_angle_cos);
+
/* -------------------------------------------------------------------- */
/** \name Update Vertex & Face Normals
* \{ */
@@ -394,9 +404,7 @@ void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
* Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normal_vcos
*/
static void bm_mesh_edges_sharp_tag(BMesh *bm,
- const float (*vnos)[3],
const float (*fnos)[3],
- float (*r_lnos)[3],
const float split_angle,
const bool do_sharp_edges_tag)
{
@@ -407,58 +415,23 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm,
const bool check_angle = (split_angle < (float)M_PI);
const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f;
- {
- char htype = BM_VERT | BM_LOOP;
- if (fnos) {
- htype |= BM_FACE;
- }
- BM_mesh_elem_index_ensure(bm, htype);
+ if (fnos) {
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
}
- /* This first loop checks which edges are actually smooth,
- * and pre-populate lnos with vnos (as if they were all smooth). */
- BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
- BMLoop *l_a, *l_b;
-
- BM_elem_index_set(e, i); /* set_inline */
- BM_elem_flag_disable(e, BM_ELEM_TAG); /* Clear tag (means edge is sharp). */
-
- /* An edge with only two loops, might be smooth... */
- if (BM_edge_loop_pair(e, &l_a, &l_b)) {
- bool is_angle_smooth = true;
- if (check_angle) {
- const float *no_a = fnos ? fnos[BM_elem_index_get(l_a->f)] : l_a->f->no;
- const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no;
- is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle_cos);
+ if (do_sharp_edges_tag) {
+ BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
+ BM_elem_index_set(e, i); /* set_inline */
+ if (e->l != NULL) {
+ bm_edge_tag_from_smooth_and_set_sharp(fnos, e, split_angle_cos);
}
-
- /* We only tag edges that are *really* smooth:
- * If the angle between both its polys' normals is below split_angle value,
- * and it is tagged as such,
- * and both its faces are smooth,
- * and both its faces have compatible (non-flipped) normals,
- * i.e. both loops on the same edge do not share the same vertex.
- */
- if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) &&
- BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
- if (is_angle_smooth) {
- const float *no;
- BM_elem_flag_enable(e, BM_ELEM_TAG);
-
- /* linked vertices might be fully smooth, copy their normals to loop ones. */
- if (r_lnos) {
- no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no;
- copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no);
- no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no;
- copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no);
- }
- }
- else if (do_sharp_edges_tag) {
- /* Note that we do not care about the other sharp-edge cases
- * (sharp poly, non-manifold edge, etc.),
- * only tag edge as sharp when it is due to angle threshold. */
- BM_elem_flag_disable(e, BM_ELEM_SMOOTH);
- }
+ }
+ }
+ else {
+ BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
+ BM_elem_index_set(e, i); /* set_inline */
+ if (e->l != NULL) {
+ bm_edge_tag_from_smooth(fnos, e, split_angle_cos);
}
}
}
@@ -479,7 +452,7 @@ void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
return;
}
- bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true);
+ bm_mesh_edges_sharp_tag(bm, NULL, split_angle, true);
}
/** \} */
@@ -526,6 +499,600 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
}
/**
+ * Called for all faces loops.
+ *
+ * - All loops must have #BM_ELEM_TAG cleared.
+ * - Loop indices must be valid.
+ *
+ * \note When custom normals are present, the order of loops can be important.
+ * Loops with lower indices must be passed before loops with higher indices (for each vertex).
+ * This is needed since the first loop sets the reference point for the custom normal offsets.
+ *
+ * \return The number of loops that were handled (for early exit when all have been handled).
+ */
+static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*fnos)[3],
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool has_clnors,
+ /* Cache. */
+ BLI_Stack *edge_vectors,
+ /* Iterate. */
+ BMLoop *l_curr,
+ /* Result. */
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr)
+{
+ BLI_assert((bm->elem_index_dirty & (BM_FACE | BM_LOOP)) == 0);
+ BLI_assert((vcos == NULL) || ((bm->elem_index_dirty & BM_VERT) == 0));
+ UNUSED_VARS_NDEBUG(bm);
+
+ int handled = 0;
+
+ /* Temp normal stack. */
+ BLI_SMALLSTACK_DECLARE(normal, float *);
+ /* Temp clnors stack. */
+ BLI_SMALLSTACK_DECLARE(clnors, short *);
+ /* Temp edge vectors stack, only used when computing lnor spacearr. */
+
+ /* A smooth edge, we have to check for cyclic smooth fan case.
+ * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge
+ * as 'entry point', otherwise we can skip it. */
+
+ /* NOTE: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store
+ * mlfan_pivot's in a stack, to avoid having to fan again around
+ * the vert during actual computation of clnor & clnorspace. However, this would complicate
+ * the code, add more memory usage, and
+ * BM_vert_step_fan_loop() is quite cheap in term of CPU cycles,
+ * so really think it's not worth it. */
+ if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
+ (BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !BM_loop_check_cyclic_smooth_fan(l_curr))) {
+ }
+ else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
+ !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) {
+ /* Simple case (both edges around that vertex are sharp in related polygon),
+ * this vertex just takes its poly normal.
+ */
+ const int l_curr_index = BM_elem_index_get(l_curr);
+ const float *no = fnos ? fnos[BM_elem_index_get(l_curr->f)] : l_curr->f->no;
+ copy_v3_v3(r_lnos[l_curr_index], no);
+
+ /* If needed, generate this (simple!) lnor space. */
+ if (r_lnors_spacearr) {
+ float vec_curr[3], vec_prev[3];
+ MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr);
+
+ {
+ const BMVert *v_pivot = l_curr->v;
+ const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
+ const BMVert *v_1 = l_curr->next->v;
+ const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co;
+ const BMVert *v_2 = l_curr->prev->v;
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ BLI_assert(v_1 == BM_edge_other_vert(l_curr->e, v_pivot));
+ BLI_assert(v_2 == BM_edge_other_vert(l_curr->prev->e, v_pivot));
+
+ sub_v3_v3v3(vec_curr, co_1, co_pivot);
+ normalize_v3(vec_curr);
+ sub_v3_v3v3(vec_prev, co_2, co_pivot);
+ normalize_v3(vec_prev);
+ }
+
+ BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL);
+ /* We know there is only one loop in this space,
+ * no need to create a linklist in this case... */
+ BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true);
+
+ if (has_clnors) {
+ const short(*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] :
+ (const void *)BM_ELEM_CD_GET_VOID_P(
+ l_curr, cd_loop_clnors_offset);
+ BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]);
+ }
+ }
+ handled = 1;
+ }
+ /* We *do not need* to check/tag loops as already computed!
+ * Due to the fact a loop only links to one of its two edges,
+ * a same fan *will never be walked more than once!*
+ * Since we consider edges having neighbor faces with inverted (flipped) normals as sharp,
+ * we are sure that no fan will be skipped, even only considering the case
+ * (sharp curr_edge, smooth prev_edge), and not the alternative
+ * (smooth curr_edge, sharp prev_edge).
+ * All this due/thanks to link between normals and loop ordering.
+ */
+ else {
+ /* We have to fan around current vertex, until we find the other non-smooth edge,
+ * and accumulate face normals into the vertex!
+ * Note in case this vertex has only one sharp edge,
+ * this is a waste because the normal is the same as the vertex normal,
+ * but I do not see any easy way to detect that (would need to count number of sharp edges
+ * per vertex, I doubt the additional memory usage would be worth it, especially as it
+ * should not be a common case in real-life meshes anyway).
+ */
+ BMVert *v_pivot = l_curr->v;
+ BMEdge *e_next;
+ const BMEdge *e_org = l_curr->e;
+ BMLoop *lfan_pivot, *lfan_pivot_next;
+ int lfan_pivot_index;
+ float lnor[3] = {0.0f, 0.0f, 0.0f};
+ float vec_curr[3], vec_next[3], vec_org[3];
+
+ /* We validate clnors data on the fly - cheapest way to do! */
+ int clnors_avg[2] = {0, 0};
+ const short(*clnor_ref)[2] = NULL;
+ int clnors_nbr = 0;
+ bool clnors_invalid = false;
+
+ const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
+
+ MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) : NULL;
+
+ BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors));
+
+ lfan_pivot = l_curr;
+ lfan_pivot_index = BM_elem_index_get(lfan_pivot);
+ e_next = lfan_pivot->e; /* Current edge here, actually! */
+
+ /* Only need to compute previous edge's vector once,
+ * then we can just reuse old current one! */
+ {
+ const BMVert *v_2 = lfan_pivot->next->v;
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ BLI_assert(v_2 == BM_edge_other_vert(e_next, v_pivot));
+
+ sub_v3_v3v3(vec_org, co_2, co_pivot);
+ normalize_v3(vec_org);
+ copy_v3_v3(vec_curr, vec_org);
+
+ if (r_lnors_spacearr) {
+ BLI_stack_push(edge_vectors, vec_org);
+ }
+ }
+
+ while (true) {
+ /* Much simpler than in sibling code with basic Mesh data! */
+ lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
+ if (lfan_pivot_next) {
+ BLI_assert(lfan_pivot_next->v == v_pivot);
+ }
+ else {
+ /* next edge is non-manifold, we have to find it ourselves! */
+ e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
+ }
+
+ /* Compute edge vector.
+ * NOTE: We could pre-compute those into an array, in the first iteration,
+ * instead of computing them twice (or more) here.
+ * However, time gained is not worth memory and time lost,
+ * given the fact that this code should not be called that much in real-life meshes.
+ */
+ {
+ const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ sub_v3_v3v3(vec_next, co_2, co_pivot);
+ normalize_v3(vec_next);
+ }
+
+ {
+ /* Code similar to accumulate_vertex_normals_poly_v3. */
+ /* Calculate angle between the two poly edges incident on this vertex. */
+ const BMFace *f = lfan_pivot->f;
+ const float fac = saacos(dot_v3v3(vec_next, vec_curr));
+ const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
+ /* Accumulate */
+ madd_v3_v3fl(lnor, no, fac);
+
+ if (has_clnors) {
+ /* Accumulate all clnors, if they are not all equal we have to fix that! */
+ const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] :
+ (const void *)BM_ELEM_CD_GET_VOID_P(
+ lfan_pivot, cd_loop_clnors_offset);
+ if (clnors_nbr) {
+ clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]);
+ }
+ else {
+ clnor_ref = clnor;
+ }
+ clnors_avg[0] += (*clnor)[0];
+ clnors_avg[1] += (*clnor)[1];
+ clnors_nbr++;
+ /* We store here a pointer to all custom lnors processed. */
+ BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
+ }
+ }
+
+ /* We store here a pointer to all loop-normals processed. */
+ BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]);
+
+ if (r_lnors_spacearr) {
+ /* Assign current lnor space to current 'vertex' loop. */
+ BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false);
+ if (e_next != e_org) {
+ /* We store here all edges-normalized vectors processed. */
+ BLI_stack_push(edge_vectors, vec_next);
+ }
+ }
+
+ handled += 1;
+
+ if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
+ /* Next edge is sharp, we have finished with this fan of faces around this vert! */
+ break;
+ }
+
+ /* Copy next edge vector to current one. */
+ copy_v3_v3(vec_curr, vec_next);
+ /* Next pivot loop to current one. */
+ lfan_pivot = lfan_pivot_next;
+ lfan_pivot_index = BM_elem_index_get(lfan_pivot);
+ }
+
+ {
+ float lnor_len = normalize_v3(lnor);
+
+ /* If we are generating lnor spacearr, we can now define the one for this fan. */
+ if (r_lnors_spacearr) {
+ if (UNLIKELY(lnor_len == 0.0f)) {
+ /* Use vertex normal as fallback! */
+ copy_v3_v3(lnor, r_lnos[lfan_pivot_index]);
+ lnor_len = 1.0f;
+ }
+
+ BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors);
+
+ if (has_clnors) {
+ if (clnors_invalid) {
+ short *clnor;
+
+ clnors_avg[0] /= clnors_nbr;
+ clnors_avg[1] /= clnors_nbr;
+ /* Fix/update all clnors of this fan with computed average value. */
+
+ /* Prints continuously when merge custom normals, so commenting. */
+ /* printf("Invalid clnors in this fan!\n"); */
+
+ while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
+ // print_v2("org clnor", clnor);
+ clnor[0] = (short)clnors_avg[0];
+ clnor[1] = (short)clnors_avg[1];
+ }
+ // print_v2("new clnors", clnors_avg);
+ }
+ else {
+ /* We still have to consume the stack! */
+ while (BLI_SMALLSTACK_POP(clnors)) {
+ /* pass */
+ }
+ }
+ BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor);
+ }
+ }
+
+ /* In case we get a zero normal here, just use vertex normal already set! */
+ if (LIKELY(lnor_len != 0.0f)) {
+ /* Copy back the final computed normal into all related loop-normals. */
+ float *nor;
+
+ while ((nor = BLI_SMALLSTACK_POP(normal))) {
+ copy_v3_v3(nor, lnor);
+ }
+ }
+ else {
+ /* We still have to consume the stack! */
+ while (BLI_SMALLSTACK_POP(normal)) {
+ /* pass */
+ }
+ }
+ }
+
+ /* Tag related vertex as sharp, to avoid fanning around it again
+ * (in case it was a smooth one). */
+ if (r_lnors_spacearr) {
+ BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG);
+ }
+ }
+ return handled;
+}
+
+static int bm_loop_index_cmp(const void *a, const void *b)
+{
+ BLI_assert(BM_elem_index_get((BMLoop *)a) != BM_elem_index_get((BMLoop *)b));
+ if (BM_elem_index_get((BMLoop *)a) < BM_elem_index_get((BMLoop *)b)) {
+ return -1;
+ }
+ return 1;
+}
+
+/**
+ * We only tag edges that are *really* smooth when the following conditions are met:
+ * - The angle between both its polygons normals is below split_angle value.
+ * - The edge is tagged as smooth.
+ * - The faces of the edge are tagged as smooth.
+ * - The faces of the edge have compatible (non-flipped) topological normal (winding),
+ * i.e. both loops on the same edge do not share the same vertex.
+ */
+BLI_INLINE bool bm_edge_is_smooth_no_angle_test(const BMEdge *e,
+ const BMLoop *l_a,
+ const BMLoop *l_b)
+{
+ return (
+ /* The face is manifold. */
+ (l_a->radial_next == l_b) &&
+ /* Faces have winding that faces the same way. */
+ (l_a->v != l_b->v) &&
+ /* The edge is smooth. */
+ BM_elem_flag_test(e, BM_ELEM_SMOOTH) &&
+ /* Both faces are smooth. */
+ BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH));
+}
+
+static void bm_edge_tag_from_smooth(const float (*fnos)[3], BMEdge *e, const float split_angle_cos)
+{
+ BLI_assert(e->l != NULL);
+ BMLoop *l_a = e->l, *l_b = l_a->radial_next;
+ bool is_smooth = false;
+ if (bm_edge_is_smooth_no_angle_test(e, l_a, l_b)) {
+ if (split_angle_cos != -1.0f) {
+ const float dot = (fnos == NULL) ? dot_v3v3(l_a->f->no, l_b->f->no) :
+ dot_v3v3(fnos[BM_elem_index_get(l_a->f)],
+ fnos[BM_elem_index_get(l_b->f)]);
+ if (dot >= split_angle_cos) {
+ is_smooth = true;
+ }
+ }
+ else {
+ is_smooth = true;
+ }
+ }
+
+ /* Perform `BM_elem_flag_set(e, BM_ELEM_TAG, is_smooth)`
+ * NOTE: This will be set by multiple threads however it will be set to the same value. */
+
+ /* No need for atomics here as this is a single byte. */
+ char *hflag_p = &e->head.hflag;
+ if (is_smooth) {
+ *hflag_p = *hflag_p | BM_ELEM_TAG;
+ }
+ else {
+ *hflag_p = *hflag_p & ~BM_ELEM_TAG;
+ }
+}
+
+/**
+ * A version of #bm_edge_tag_from_smooth that sets sharp edges
+ * when they would be considered smooth but exceed the split angle .
+ *
+ * \note This doesn't have the same atomic requirement as #bm_edge_tag_from_smooth
+ * since it isn't run from multiple threads at once.
+ */
+static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3],
+ BMEdge *e,
+ const float split_angle_cos)
+{
+ BLI_assert(e->l != NULL);
+ BMLoop *l_a = e->l, *l_b = l_a->radial_next;
+ bool is_smooth = false;
+ if (bm_edge_is_smooth_no_angle_test(e, l_a, l_b)) {
+ if (split_angle_cos != -1.0f) {
+ const float dot = (fnos == NULL) ? dot_v3v3(l_a->f->no, l_b->f->no) :
+ dot_v3v3(fnos[BM_elem_index_get(l_a->f)],
+ fnos[BM_elem_index_get(l_b->f)]);
+ if (dot >= split_angle_cos) {
+ is_smooth = true;
+ }
+ else {
+ /* Note that we do not care about the other sharp-edge cases
+ * (sharp poly, non-manifold edge, etc.),
+ * only tag edge as sharp when it is due to angle threshold. */
+ BM_elem_flag_disable(e, BM_ELEM_SMOOTH);
+ }
+ }
+ else {
+ is_smooth = true;
+ }
+ }
+
+ BM_elem_flag_set(e, BM_ELEM_TAG, is_smooth);
+}
+
+/**
+ * Operate on all vertices loops.
+ * operating on vertices this is needed for multi-threading
+ * so there is a guarantee that each thread has isolated loops.
+ */
+static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild,
+ const float split_angle_cos,
+ /* TLS */
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ BLI_Stack *edge_vectors,
+ /* Iterate over. */
+ BMVert *v)
+{
+ /* Respecting face order is necessary so the initial starting loop is consistent
+ * with looping over loops of all faces.
+ *
+ * Logically we could sort the loops by their index & loop over them
+ * however it's faster to use the lowest index of an un-ordered list
+ * since it's common that smooth vertices only ever need to pick one loop
+ * which then handles all the others.
+ *
+ * Sorting is only performed when multiple fans are found. */
+ const bool has_clnors = true;
+ LinkNode *loops_of_vert = NULL;
+ int loops_of_vert_count = 0;
+ const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS);
+
+ /* The loop with the lowest index. */
+ {
+ LinkNode *link_best;
+ uint index_best = UINT_MAX;
+ BMEdge *e_curr_iter = v->e;
+ do { /* Edges of vertex. */
+ BMLoop *l_curr = e_curr_iter->l;
+ if (l_curr == NULL) {
+ continue;
+ }
+
+ if (do_edge_tag) {
+ bm_edge_tag_from_smooth(fnos, e_curr_iter, split_angle_cos);
+ }
+
+ do { /* Radial loops. */
+ if (l_curr->v != v) {
+ continue;
+ }
+ if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
+ !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) {
+ continue;
+ }
+ BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
+ BLI_linklist_prepend_alloca(&loops_of_vert, l_curr);
+ loops_of_vert_count += 1;
+
+ const uint index_test = (uint)BM_elem_index_get(l_curr);
+ if (index_best > index_test) {
+ index_best = index_test;
+ link_best = loops_of_vert;
+ }
+ } while ((l_curr = l_curr->radial_next) != e_curr_iter->l);
+ } while ((e_curr_iter = BM_DISK_EDGE_NEXT(e_curr_iter, v)) != v->e);
+
+ if (UNLIKELY(loops_of_vert == NULL)) {
+ return;
+ }
+
+ /* Immediately pop the best element.
+ * The order doesn't matter, so swap the links as it's simpler than tracking
+ * reference to `link_best`. */
+ if (link_best != loops_of_vert) {
+ SWAP(void *, link_best->link, loops_of_vert->link);
+ }
+ }
+
+ bool loops_of_vert_is_sorted = false;
+
+ /* Keep track of the number of loops that have been assigned. */
+ int loops_of_vert_handled = 0;
+
+ while (loops_of_vert != NULL) {
+ BMLoop *l_best = loops_of_vert->link;
+ loops_of_vert = loops_of_vert->next;
+
+ BLI_assert(l_best->v == v);
+ loops_of_vert_handled += bm_mesh_loops_calc_normals_for_loop(bm,
+ vcos,
+ fnos,
+ clnors_data,
+ cd_loop_clnors_offset,
+ has_clnors,
+ edge_vectors,
+ l_best,
+ r_lnos,
+ r_lnors_spacearr);
+
+ /* Check if an early exit is possible without an exhaustive inspection of every loop
+ * where 1 loop's fan extends out to all remaining loops.
+ * This is a common case for smooth vertices. */
+ BLI_assert(loops_of_vert_handled <= loops_of_vert_count);
+ if (loops_of_vert_handled == loops_of_vert_count) {
+ break;
+ }
+
+ /* Note on sorting, in some cases it will be faster to scan for the lowest index each time.
+ * However in the worst case this is `O(N^2)`, so use a single sort call instead. */
+ if (!loops_of_vert_is_sorted) {
+ if (loops_of_vert && loops_of_vert->next) {
+ loops_of_vert = BLI_linklist_sort(loops_of_vert, bm_loop_index_cmp);
+ loops_of_vert_is_sorted = true;
+ }
+ }
+ }
+}
+
+/**
+ * A simplified version of #bm_mesh_loops_calc_normals_for_vert_with_clnors
+ * that can operate on loops in any order.
+ */
+static void bm_mesh_loops_calc_normals_for_vert_without_clnors(
+ BMesh *bm,
+ const float (*vcos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ const bool do_rebuild,
+ const float split_angle_cos,
+ /* TLS */
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ BLI_Stack *edge_vectors,
+ /* Iterate over. */
+ BMVert *v)
+{
+ const bool has_clnors = false;
+ const short(*clnors_data)[2] = NULL;
+ const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS);
+ const int cd_loop_clnors_offset = -1;
+
+ BMEdge *e_curr_iter;
+
+ /* Unfortunately a loop is needed just to clear loop-tags. */
+ e_curr_iter = v->e;
+ do { /* Edges of vertex. */
+ BMLoop *l_curr = e_curr_iter->l;
+ if (l_curr == NULL) {
+ continue;
+ }
+
+ if (do_edge_tag) {
+ bm_edge_tag_from_smooth(fnos, e_curr_iter, split_angle_cos);
+ }
+
+ do { /* Radial loops. */
+ if (l_curr->v != v) {
+ continue;
+ }
+ BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
+ } while ((l_curr = l_curr->radial_next) != e_curr_iter->l);
+ } while ((e_curr_iter = BM_DISK_EDGE_NEXT(e_curr_iter, v)) != v->e);
+
+ e_curr_iter = v->e;
+ do { /* Edges of vertex. */
+ BMLoop *l_curr = e_curr_iter->l;
+ if (l_curr == NULL) {
+ continue;
+ }
+ do { /* Radial loops. */
+ if (l_curr->v != v) {
+ continue;
+ }
+ if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
+ !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) {
+ continue;
+ }
+ bm_mesh_loops_calc_normals_for_loop(bm,
+ vcos,
+ fnos,
+ clnors_data,
+ cd_loop_clnors_offset,
+ has_clnors,
+ edge_vectors,
+ l_curr,
+ r_lnos,
+ r_lnors_spacearr);
+ } while ((l_curr = l_curr->radial_next) != e_curr_iter->l);
+ } while ((e_curr_iter = BM_DISK_EDGE_NEXT(e_curr_iter, v)) != v->e);
+}
+
+/**
* BMesh version of BKE_mesh_normals_loop_split() in `mesh_evaluate.cc`
* Will use first clnors_data array, and fallback to cd_loop_clnors_offset
* (use NULL and -1 to not use clnors).
@@ -533,26 +1100,24 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
* \note This sets #BM_ELEM_TAG which is used in tool code (e.g. T84426).
* we could add a low-level API flag for this, see #BM_ELEM_API_FLAG_ENABLE and friends.
*/
-static void bm_mesh_loops_calc_normals(BMesh *bm,
- const float (*vcos)[3],
- const float (*fnos)[3],
- float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- const short (*clnors_data)[2],
- const int cd_loop_clnors_offset,
- const bool do_rebuild)
+static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild,
+ const float split_angle)
{
BMIter fiter;
BMFace *f_curr;
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
+ const bool check_angle = (split_angle < (float)M_PI);
+ const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f;
MLoopNorSpaceArray _lnors_spacearr = {NULL};
- /* Temp normal stack. */
- BLI_SMALLSTACK_DECLARE(normal, float *);
- /* Temp clnors stack. */
- BLI_SMALLSTACK_DECLARE(clnors, short *);
- /* Temp edge vectors stack, only used when computing lnor spacearr. */
BLI_Stack *edge_vectors = NULL;
{
@@ -573,6 +1138,10 @@ static void bm_mesh_loops_calc_normals(BMesh *bm,
edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
}
+ if (split_angle_cos != -1.0f) {
+ bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false);
+ }
+
/* Clear all loops' tags (means none are to be skipped for now). */
int index_face, index_loop = 0;
BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) {
@@ -601,277 +1170,249 @@ static void bm_mesh_loops_calc_normals(BMesh *bm,
!(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) {
continue;
}
- /* A smooth edge, we have to check for cyclic smooth fan case.
- * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge
- * as 'entry point', otherwise we can skip it. */
-
- /* NOTE: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store
- * mlfan_pivot's in a stack, to avoid having to fan again around
- * the vert during actual computation of clnor & clnorspace. However, this would complicate
- * the code, add more memory usage, and
- * BM_vert_step_fan_loop() is quite cheap in term of CPU cycles,
- * so really think it's not worth it. */
- if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
- (BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !BM_loop_check_cyclic_smooth_fan(l_curr))) {
- }
- else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
- !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) {
- /* Simple case (both edges around that vertex are sharp in related polygon),
- * this vertex just takes its poly normal.
- */
- const int l_curr_index = BM_elem_index_get(l_curr);
- const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no;
- copy_v3_v3(r_lnos[l_curr_index], no);
-
- /* If needed, generate this (simple!) lnor space. */
- if (r_lnors_spacearr) {
- float vec_curr[3], vec_prev[3];
- MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr);
-
- {
- const BMVert *v_pivot = l_curr->v;
- const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
- const BMVert *v_1 = l_curr->next->v;
- const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co;
- const BMVert *v_2 = l_curr->prev->v;
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
-
- BLI_assert(v_1 == BM_edge_other_vert(l_curr->e, v_pivot));
- BLI_assert(v_2 == BM_edge_other_vert(l_curr->prev->e, v_pivot));
-
- sub_v3_v3v3(vec_curr, co_1, co_pivot);
- normalize_v3(vec_curr);
- sub_v3_v3v3(vec_prev, co_2, co_pivot);
- normalize_v3(vec_prev);
- }
-
- BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL);
- /* We know there is only one loop in this space,
- * no need to create a linklist in this case... */
- BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true);
-
- if (has_clnors) {
- const short(*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] :
- (const void *)BM_ELEM_CD_GET_VOID_P(
- l_curr, cd_loop_clnors_offset);
- BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]);
- }
- }
- }
- /* We *do not need* to check/tag loops as already computed!
- * Due to the fact a loop only links to one of its two edges,
- * a same fan *will never be walked more than once!*
- * Since we consider edges having neighbor faces with inverted (flipped) normals as sharp,
- * we are sure that no fan will be skipped, even only considering the case
- * (sharp curr_edge, smooth prev_edge), and not the alternative
- * (smooth curr_edge, sharp prev_edge).
- * All this due/thanks to link between normals and loop ordering.
- */
- else {
- /* We have to fan around current vertex, until we find the other non-smooth edge,
- * and accumulate face normals into the vertex!
- * Note in case this vertex has only one sharp edge,
- * this is a waste because the normal is the same as the vertex normal,
- * but I do not see any easy way to detect that (would need to count number of sharp edges
- * per vertex, I doubt the additional memory usage would be worth it, especially as it
- * should not be a common case in real-life meshes anyway).
- */
- BMVert *v_pivot = l_curr->v;
- BMEdge *e_next;
- const BMEdge *e_org = l_curr->e;
- BMLoop *lfan_pivot, *lfan_pivot_next;
- int lfan_pivot_index;
- float lnor[3] = {0.0f, 0.0f, 0.0f};
- float vec_curr[3], vec_next[3], vec_org[3];
-
- /* We validate clnors data on the fly - cheapest way to do! */
- int clnors_avg[2] = {0, 0};
- const short(*clnor_ref)[2] = NULL;
- int clnors_nbr = 0;
- bool clnors_invalid = false;
-
- const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
-
- MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) :
- NULL;
-
- BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors));
-
- lfan_pivot = l_curr;
- lfan_pivot_index = BM_elem_index_get(lfan_pivot);
- e_next = lfan_pivot->e; /* Current edge here, actually! */
-
- /* Only need to compute previous edge's vector once,
- * then we can just reuse old current one! */
- {
- const BMVert *v_2 = lfan_pivot->next->v;
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
-
- BLI_assert(v_2 == BM_edge_other_vert(e_next, v_pivot));
-
- sub_v3_v3v3(vec_org, co_2, co_pivot);
- normalize_v3(vec_org);
- copy_v3_v3(vec_curr, vec_org);
+ bm_mesh_loops_calc_normals_for_loop(bm,
+ vcos,
+ fnos,
+ clnors_data,
+ cd_loop_clnors_offset,
+ has_clnors,
+ edge_vectors,
+ l_curr,
+ r_lnos,
+ r_lnors_spacearr);
+ } while ((l_curr = l_curr->next) != l_first);
+ }
- if (r_lnors_spacearr) {
- BLI_stack_push(edge_vectors, vec_org);
- }
- }
+ if (r_lnors_spacearr) {
+ BLI_stack_free(edge_vectors);
+ if (r_lnors_spacearr == &_lnors_spacearr) {
+ BKE_lnor_spacearr_free(r_lnors_spacearr);
+ }
+ }
+}
- while (true) {
- /* Much simpler than in sibling code with basic Mesh data! */
- lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
- if (lfan_pivot_next) {
- BLI_assert(lfan_pivot_next->v == v_pivot);
- }
- else {
- /* next edge is non-manifold, we have to find it ourselves! */
- e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
- }
+typedef struct BMLoopsCalcNormalsWithCoordsData {
+ /* Read-only data. */
+ const float (*fnos)[3];
+ const float (*vcos)[3];
+ BMesh *bm;
+ const short (*clnors_data)[2];
+ const int cd_loop_clnors_offset;
+ const bool do_rebuild;
+ const float split_angle_cos;
+
+ /* Output. */
+ float (*r_lnos)[3];
+ MLoopNorSpaceArray *r_lnors_spacearr;
+} BMLoopsCalcNormalsWithCoordsData;
+
+typedef struct BMLoopsCalcNormalsWithCoords_TLS {
+ BLI_Stack *edge_vectors;
+
+ /** Copied from #BMLoopsCalcNormalsWithCoordsData.r_lnors_spacearr when it's not NULL. */
+ MLoopNorSpaceArray *lnors_spacearr;
+ MLoopNorSpaceArray lnors_spacearr_buf;
+} BMLoopsCalcNormalsWithCoords_TLS;
+
+static void bm_mesh_loops_calc_normals_for_vert_init_fn(const void *__restrict userdata,
+ void *__restrict chunk)
+{
+ const BMLoopsCalcNormalsWithCoordsData *data = userdata;
+ BMLoopsCalcNormalsWithCoords_TLS *tls_data = chunk;
+ if (data->r_lnors_spacearr) {
+ tls_data->edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
+ BKE_lnor_spacearr_tls_init(data->r_lnors_spacearr, &tls_data->lnors_spacearr_buf);
+ tls_data->lnors_spacearr = &tls_data->lnors_spacearr_buf;
+ }
+ else {
+ tls_data->lnors_spacearr = NULL;
+ }
+}
- /* Compute edge vector.
- * NOTE: We could pre-compute those into an array, in the first iteration,
- * instead of computing them twice (or more) here.
- * However, time gained is not worth memory and time lost,
- * given the fact that this code should not be called that much in real-life meshes.
- */
- {
- const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+static void bm_mesh_loops_calc_normals_for_vert_reduce_fn(const void *__restrict userdata,
+ void *__restrict UNUSED(chunk_join),
+ void *__restrict chunk)
+{
+ const BMLoopsCalcNormalsWithCoordsData *data = userdata;
+ BMLoopsCalcNormalsWithCoords_TLS *tls_data = chunk;
- sub_v3_v3v3(vec_next, co_2, co_pivot);
- normalize_v3(vec_next);
- }
+ if (data->r_lnors_spacearr) {
+ BKE_lnor_spacearr_tls_join(data->r_lnors_spacearr, tls_data->lnors_spacearr);
+ }
+}
- {
- /* Code similar to accumulate_vertex_normals_poly_v3. */
- /* Calculate angle between the two poly edges incident on this vertex. */
- const BMFace *f = lfan_pivot->f;
- const float fac = saacos(dot_v3v3(vec_next, vec_curr));
- const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
- /* Accumulate */
- madd_v3_v3fl(lnor, no, fac);
-
- if (has_clnors) {
- /* Accumulate all clnors, if they are not all equal we have to fix that! */
- const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] :
- (const void *)BM_ELEM_CD_GET_VOID_P(
- lfan_pivot, cd_loop_clnors_offset);
- if (clnors_nbr) {
- clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] ||
- (*clnor_ref)[1] != (*clnor)[1]);
- }
- else {
- clnor_ref = clnor;
- }
- clnors_avg[0] += (*clnor)[0];
- clnors_avg[1] += (*clnor)[1];
- clnors_nbr++;
- /* We store here a pointer to all custom lnors processed. */
- BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
- }
- }
+static void bm_mesh_loops_calc_normals_for_vert_free_fn(const void *__restrict userdata,
+ void *__restrict chunk)
+{
+ const BMLoopsCalcNormalsWithCoordsData *data = userdata;
+ BMLoopsCalcNormalsWithCoords_TLS *tls_data = chunk;
- /* We store here a pointer to all loop-normals processed. */
- BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]);
+ if (data->r_lnors_spacearr) {
+ BLI_stack_free(tls_data->edge_vectors);
+ }
+}
- if (r_lnors_spacearr) {
- /* Assign current lnor space to current 'vertex' loop. */
- BKE_lnor_space_add_loop(
- r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false);
- if (e_next != e_org) {
- /* We store here all edges-normalized vectors processed. */
- BLI_stack_push(edge_vectors, vec_next);
- }
- }
+static void bm_mesh_loops_calc_normals_for_vert_with_clnors_fn(
+ void *userdata, MempoolIterData *mp_v, const TaskParallelTLS *__restrict tls)
+{
+ BMVert *v = (BMVert *)mp_v;
+ if (v->e == NULL) {
+ return;
+ }
+ BMLoopsCalcNormalsWithCoordsData *data = userdata;
+ BMLoopsCalcNormalsWithCoords_TLS *tls_data = tls->userdata_chunk;
+ bm_mesh_loops_calc_normals_for_vert_with_clnors(data->bm,
+ data->vcos,
+ data->fnos,
+ data->r_lnos,
+
+ data->clnors_data,
+ data->cd_loop_clnors_offset,
+ data->do_rebuild,
+ data->split_angle_cos,
+ /* Thread local. */
+ tls_data->lnors_spacearr,
+ tls_data->edge_vectors,
+ /* Iterate over. */
+ v);
+}
- if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
- /* Next edge is sharp, we have finished with this fan of faces around this vert! */
- break;
- }
+static void bm_mesh_loops_calc_normals_for_vert_without_clnors_fn(
+ void *userdata, MempoolIterData *mp_v, const TaskParallelTLS *__restrict tls)
+{
+ BMVert *v = (BMVert *)mp_v;
+ if (v->e == NULL) {
+ return;
+ }
+ BMLoopsCalcNormalsWithCoordsData *data = userdata;
+ BMLoopsCalcNormalsWithCoords_TLS *tls_data = tls->userdata_chunk;
+ bm_mesh_loops_calc_normals_for_vert_without_clnors(data->bm,
+ data->vcos,
+ data->fnos,
+ data->r_lnos,
+
+ data->do_rebuild,
+ data->split_angle_cos,
+ /* Thread local. */
+ tls_data->lnors_spacearr,
+ tls_data->edge_vectors,
+ /* Iterate over. */
+ v);
+}
- /* Copy next edge vector to current one. */
- copy_v3_v3(vec_curr, vec_next);
- /* Next pivot loop to current one. */
- lfan_pivot = lfan_pivot_next;
- lfan_pivot_index = BM_elem_index_get(lfan_pivot);
- }
+static void bm_mesh_loops_calc_normals__multi_threaded(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild,
+ const float split_angle)
+{
+ const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
+ const bool check_angle = (split_angle < (float)M_PI);
+ const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f;
- {
- float lnor_len = normalize_v3(lnor);
+ MLoopNorSpaceArray _lnors_spacearr = {NULL};
- /* If we are generating lnor spacearr, we can now define the one for this fan. */
- if (r_lnors_spacearr) {
- if (UNLIKELY(lnor_len == 0.0f)) {
- /* Use vertex normal as fallback! */
- copy_v3_v3(lnor, r_lnos[lfan_pivot_index]);
- lnor_len = 1.0f;
- }
+ {
+ char htype = BM_LOOP;
+ if (vcos) {
+ htype |= BM_VERT;
+ }
+ if (fnos) {
+ htype |= BM_FACE;
+ }
+ /* Face/Loop indices are set inline below. */
+ BM_mesh_elem_index_ensure(bm, htype);
+ }
- BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors);
-
- if (has_clnors) {
- if (clnors_invalid) {
- short *clnor;
-
- clnors_avg[0] /= clnors_nbr;
- clnors_avg[1] /= clnors_nbr;
- /* Fix/update all clnors of this fan with computed average value. */
-
- /* Prints continuously when merge custom normals, so commenting. */
- /* printf("Invalid clnors in this fan!\n"); */
-
- while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
- // print_v2("org clnor", clnor);
- clnor[0] = (short)clnors_avg[0];
- clnor[1] = (short)clnors_avg[1];
- }
- // print_v2("new clnors", clnors_avg);
- }
- else {
- /* We still have to consume the stack! */
- while (BLI_SMALLSTACK_POP(clnors)) {
- /* pass */
- }
- }
- BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor);
- }
- }
+ if (!r_lnors_spacearr && has_clnors) {
+ /* We need to compute lnor spacearr if some custom lnor data are given to us! */
+ r_lnors_spacearr = &_lnors_spacearr;
+ }
+ if (r_lnors_spacearr) {
+ BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
+ }
- /* In case we get a zero normal here, just use vertex normal already set! */
- if (LIKELY(lnor_len != 0.0f)) {
- /* Copy back the final computed normal into all related loop-normals. */
- float *nor;
+ /* We now know edges that can be smoothed (they are tagged),
+ * and edges that will be hard (they aren't).
+ * Now, time to generate the normals.
+ */
- while ((nor = BLI_SMALLSTACK_POP(normal))) {
- copy_v3_v3(nor, lnor);
- }
- }
- else {
- /* We still have to consume the stack! */
- while (BLI_SMALLSTACK_POP(normal)) {
- /* pass */
- }
- }
- }
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
- /* Tag related vertex as sharp, to avoid fanning around it again
- * (in case it was a smooth one). */
- if (r_lnors_spacearr) {
- BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG);
- }
- }
- } while ((l_curr = l_curr->next) != l_first);
- }
+ BMLoopsCalcNormalsWithCoords_TLS tls = {NULL};
+
+ settings.userdata_chunk = &tls;
+ settings.userdata_chunk_size = sizeof(tls);
+
+ settings.func_init = bm_mesh_loops_calc_normals_for_vert_init_fn;
+ settings.func_reduce = bm_mesh_loops_calc_normals_for_vert_reduce_fn;
+ settings.func_free = bm_mesh_loops_calc_normals_for_vert_free_fn;
+
+ BMLoopsCalcNormalsWithCoordsData data = {
+ .bm = bm,
+ .vcos = vcos,
+ .fnos = fnos,
+ .r_lnos = r_lnos,
+ .r_lnors_spacearr = r_lnors_spacearr,
+ .clnors_data = clnors_data,
+ .cd_loop_clnors_offset = cd_loop_clnors_offset,
+ .do_rebuild = do_rebuild,
+ .split_angle_cos = split_angle_cos,
+ };
+
+ BM_iter_parallel(bm,
+ BM_VERTS_OF_MESH,
+ has_clnors ? bm_mesh_loops_calc_normals_for_vert_with_clnors_fn :
+ bm_mesh_loops_calc_normals_for_vert_without_clnors_fn,
+ &data,
+ &settings);
if (r_lnors_spacearr) {
- BLI_stack_free(edge_vectors);
if (r_lnors_spacearr == &_lnors_spacearr) {
BKE_lnor_spacearr_free(r_lnors_spacearr);
}
}
}
+static void bm_mesh_loops_calc_normals(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild,
+ const float split_angle)
+{
+ if (bm->totloop < BM_OMP_LIMIT) {
+ bm_mesh_loops_calc_normals__single_threaded(bm,
+ vcos,
+ fnos,
+ r_lnos,
+ r_lnors_spacearr,
+ clnors_data,
+ cd_loop_clnors_offset,
+ do_rebuild,
+ split_angle);
+ }
+ else {
+ bm_mesh_loops_calc_normals__multi_threaded(bm,
+ vcos,
+ fnos,
+ r_lnos,
+ r_lnors_spacearr,
+ clnors_data,
+ cd_loop_clnors_offset,
+ do_rebuild,
+ split_angle);
+ }
+}
+
/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
@@ -1062,7 +1603,6 @@ static void bm_mesh_loops_assign_normal_data(BMesh *bm,
*/
static void bm_mesh_loops_custom_normals_set(BMesh *bm,
const float (*vcos)[3],
- const float (*vnos)[3],
const float (*fnos)[3],
MLoopNorSpaceArray *r_lnors_spacearr,
short (*r_clnors_data)[2],
@@ -1080,12 +1620,19 @@ static void bm_mesh_loops_custom_normals_set(BMesh *bm,
/* Tag smooth edges and set lnos from vnos when they might be completely smooth...
* When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, vnos, fnos, cur_lnors, (float)M_PI, false);
+ bm_mesh_edges_sharp_tag(bm, fnos, (float)M_PI, false);
/* Finish computing lnos by accumulating face normals
* in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
+ bm_mesh_loops_calc_normals(bm,
+ vcos,
+ fnos,
+ cur_lnors,
+ r_lnors_spacearr,
+ r_clnors_data,
+ cd_loop_clnors_offset,
+ false,
+ EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS);
/* Extract new normals from the data layer if necessary. */
float(*custom_lnors)[3] = new_lnors;
@@ -1118,8 +1665,15 @@ static void bm_mesh_loops_custom_normals_set(BMesh *bm,
* spacearr/smooth fans matching the given custom lnors. */
BKE_lnor_spacearr_clear(r_lnors_spacearr);
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
+ bm_mesh_loops_calc_normals(bm,
+ vcos,
+ fnos,
+ cur_lnors,
+ r_lnors_spacearr,
+ r_clnors_data,
+ cd_loop_clnors_offset,
+ false,
+ EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS);
}
/* And we just have to convert plain object-space custom normals to our
@@ -1167,40 +1721,6 @@ static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm,
}
}
-#if 0 /* Unused currently */
-/**
- * \brief BMesh Compute Loop Normals
- *
- * Updates the loop normals of a mesh.
- * Assumes vertex and face normals are valid (else call BM_mesh_normals_update() first)!
- */
-void BM_mesh_loop_normals_update(BMesh *bm,
- const bool use_split_normals,
- const float split_angle,
- float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- const short (*clnors_data)[2],
- const int cd_loop_clnors_offset)
-{
- const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
-
- if (use_split_normals) {
- /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
- * When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos);
-
- /* Finish computing lnos by accumulating face normals
- * in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
- }
- else {
- BLI_assert(!r_lnors_spacearr);
- bm_mesh_loops_calc_normals_no_autosmooth(bm, NULL, NULL, r_lnos);
- }
-}
-#endif
-
/**
* \brief BMesh Compute Loop Normals from/to external data.
*
@@ -1223,14 +1743,15 @@ void BM_loops_calc_normal_vcos(BMesh *bm,
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
if (use_split_normals) {
- /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
- * When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false);
-
- /* Finish computing lnos by accumulating face normals
- * in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild);
+ bm_mesh_loops_calc_normals(bm,
+ vcos,
+ fnos,
+ r_lnos,
+ r_lnors_spacearr,
+ clnors_data,
+ cd_loop_clnors_offset,
+ do_rebuild,
+ has_clnors ? (float)M_PI : split_angle);
}
else {
BLI_assert(!r_lnors_spacearr);
@@ -1788,7 +2309,6 @@ void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
bm_mesh_loops_custom_normals_set(bm,
NULL,
NULL,
- NULL,
bm->lnor_spacearr,
NULL,
cd_custom_normal_offset,
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index 3978959a425..b63a09a97a6 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -245,7 +245,7 @@ static BMOpDefine bmo_region_extend_def = {
* Edge Rotate.
*
* Rotates edges topologically. Also known as "spin edge" to some people.
- * Simple example: ``[/] becomes [|] then [\]``.
+ * Simple example: `[/] becomes [|] then [\]`.
*/
static BMOpDefine bmo_rotate_edges_def = {
"rotate_edges",
@@ -1942,7 +1942,7 @@ static BMOpDefine bmo_inset_region_def = {
};
/*
- * Edgeloop Offset.
+ * Edge-loop Offset.
*
* Creates edge loops based on simple edge-outset method.
*/
diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c
index 0aab10e7b1a..51ae47adacc 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.c
+++ b/source/blender/bmesh/intern/bmesh_polygon.c
@@ -316,7 +316,7 @@ float BM_face_calc_perimeter_with_mat3(const BMFace *f, const float mat3[3][3])
/**
* Utility function to calculate the edge which is most different from the other two.
*
- * \return The first edge index, where the second vertex is ``(index + 1) % 3``.
+ * \return The first edge index, where the second vertex is `(index + 1) % 3`.
*/
static int bm_vert_tri_find_unique_edge(BMVert *verts[3])
{
diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h
index 2c32cd39002..5be7f4a5f3b 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.h
+++ b/source/blender/bmesh/intern/bmesh_polygon.h
@@ -20,8 +20,8 @@
* \ingroup bmesh
*/
-struct Heap;
struct BMPartialUpdate;
+struct Heap;
#include "BLI_compiler_attrs.h"
diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
index 99e50b35d97..86a7d8153f0 100644
--- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
+++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
@@ -337,7 +337,7 @@ static bool bm_face_split_edgenet_find_loop_walk(BMVert *v_init,
/* in rare cases there may be edges with a single connecting vertex */
if (e_next != e_first) {
do {
- if ((BM_ELEM_API_FLAG_TEST(e_next, EDGE_NET)) &&
+ if (BM_ELEM_API_FLAG_TEST(e_next, EDGE_NET) &&
(bm_edge_flagged_radial_count(e_next) < 2)) {
BMVert *v_next;
diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c
index bc881040e4e..cb5764b1c91 100644
--- a/source/blender/bmesh/intern/bmesh_query.c
+++ b/source/blender/bmesh/intern/bmesh_query.c
@@ -766,7 +766,7 @@ bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb)
}
/**
- * Fast alternative to ``(BM_vert_edge_count(v) == 2)``
+ * Fast alternative to `(BM_vert_edge_count(v) == 2)`.
*/
bool BM_vert_is_edge_pair(const BMVert *v)
{
@@ -779,7 +779,7 @@ bool BM_vert_is_edge_pair(const BMVert *v)
}
/**
- * Fast alternative to ``(BM_vert_edge_count(v) == 2)``
+ * Fast alternative to `(BM_vert_edge_count(v) == 2)`
* that checks both edges connect to the same faces.
*/
bool BM_vert_is_edge_pair_manifold(const BMVert *v)
@@ -896,7 +896,7 @@ int BM_vert_face_count_at_most(const BMVert *v, int count_max)
/**
* Return true if the vertex is connected to _any_ faces.
*
- * same as ``BM_vert_face_count(v) != 0`` or ``BM_vert_find_first_loop(v) == NULL``
+ * same as `BM_vert_face_count(v) != 0` or `BM_vert_find_first_loop(v) == NULL`.
*/
bool BM_vert_face_check(const BMVert *v)
{
diff --git a/source/blender/bmesh/intern/bmesh_query.h b/source/blender/bmesh/intern/bmesh_query.h
index 5627f3820c2..021358f81ad 100644
--- a/source/blender/bmesh/intern/bmesh_query.h
+++ b/source/blender/bmesh/intern/bmesh_query.h
@@ -272,7 +272,7 @@ int BM_mesh_calc_edge_groups_as_arrays(BMesh *bm,
int (**r_groups)[3]) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL(1, 2, 3, 4, 5);
-/* not really any good place to put this */
+/* Not really any good place to put this. */
float bmesh_subd_falloff_calc(const int falloff, float val) ATTR_WARN_UNUSED_RESULT;
#include "bmesh_query_inline.h"
diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c
index cfdce0b749b..d5d72cd4ba3 100644
--- a/source/blender/bmesh/intern/bmesh_structure.c
+++ b/source/blender/bmesh/intern/bmesh_structure.c
@@ -592,7 +592,7 @@ int bmesh_radial_facevert_count_at_most(const BMLoop *l, const BMVert *v, const
/**
* \brief RADIAL CHECK FACE VERT
*
- * Quicker check for ``bmesh_radial_facevert_count(...) != 0``
+ * Quicker check for `bmesh_radial_facevert_count(...) != 0`.
*/
bool bmesh_radial_facevert_check(const BMLoop *l, const BMVert *v)
{
diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c
index 40f09d7e719..e66afcd88d9 100644
--- a/source/blender/bmesh/intern/bmesh_walkers_impl.c
+++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c
@@ -842,7 +842,7 @@ static void *bmw_IslandManifoldWalker_step(BMWalker *walker)
/* utility function to see if an edge is a part of an ngon boundary */
static bool bm_edge_is_single(BMEdge *e)
{
- return ((BM_edge_is_boundary(e)) && (e->l->f->len > 4) &&
+ return (BM_edge_is_boundary(e) && (e->l->f->len > 4) &&
(BM_edge_is_boundary(e->l->next->e) || BM_edge_is_boundary(e->l->prev->e)));
}
diff --git a/source/blender/bmesh/operators/bmo_connect_concave.c b/source/blender/bmesh/operators/bmo_connect_concave.c
index bc1111676a3..15ed930afd8 100644
--- a/source/blender/bmesh/operators/bmo_connect_concave.c
+++ b/source/blender/bmesh/operators/bmo_connect_concave.c
@@ -51,10 +51,10 @@ static int bm_edge_length_cmp(const void *a_, const void *b_)
const BMEdge *e_a = *(const void **)a_;
const BMEdge *e_b = *(const void **)b_;
- int e_a_concave = ((BM_elem_flag_test(e_a->v1, BM_ELEM_TAG)) &&
- (BM_elem_flag_test(e_a->v2, BM_ELEM_TAG)));
- int e_b_concave = ((BM_elem_flag_test(e_b->v1, BM_ELEM_TAG)) &&
- (BM_elem_flag_test(e_b->v2, BM_ELEM_TAG)));
+ int e_a_concave = (BM_elem_flag_test(e_a->v1, BM_ELEM_TAG) &&
+ BM_elem_flag_test(e_a->v2, BM_ELEM_TAG));
+ int e_b_concave = (BM_elem_flag_test(e_b->v1, BM_ELEM_TAG) &&
+ BM_elem_flag_test(e_b->v2, BM_ELEM_TAG));
/* merge edges between concave edges last since these
* are most likely to remain and be the main dividers */
diff --git a/source/blender/bmesh/operators/bmo_edgenet.c b/source/blender/bmesh/operators/bmo_edgenet.c
index 8e4b0732feb..7f70c452af3 100644
--- a/source/blender/bmesh/operators/bmo_edgenet.c
+++ b/source/blender/bmesh/operators/bmo_edgenet.c
@@ -93,7 +93,7 @@ static BMEdge *edge_next(BMesh *bm, BMEdge *e)
for (i = 0; i < 2; i++) {
BM_ITER_ELEM (e2, &iter, i ? e->v2 : e->v1, BM_EDGES_OF_VERT) {
- if ((BMO_edge_flag_test(bm, e2, EDGE_MARK)) &&
+ if (BMO_edge_flag_test(bm, e2, EDGE_MARK) &&
(BMO_edge_flag_test(bm, e2, EDGE_VIS) == false) && (e2 != e)) {
return e2;
}
diff --git a/source/blender/bmesh/operators/bmo_fill_grid.c b/source/blender/bmesh/operators/bmo_fill_grid.c
index 2e09b21c9cc..6734cc60cad 100644
--- a/source/blender/bmesh/operators/bmo_fill_grid.c
+++ b/source/blender/bmesh/operators/bmo_fill_grid.c
@@ -645,20 +645,20 @@ void bmo_grid_fill_exec(BMesh *bm, BMOperator *op)
bm_edgeloop_flag_set(estore_a, BM_ELEM_HIDDEN, true);
bm_edgeloop_flag_set(estore_b, BM_ELEM_HIDDEN, true);
- if ((BM_mesh_edgeloops_find_path(
- bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_first, v_b_first)) &&
- (BM_mesh_edgeloops_find_path(
- bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_last, v_b_last))) {
+ if (BM_mesh_edgeloops_find_path(
+ bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_first, v_b_first) &&
+ BM_mesh_edgeloops_find_path(
+ bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_last, v_b_last)) {
estore_rail_a = eloops_rail.first;
estore_rail_b = eloops_rail.last;
}
else {
BM_mesh_edgeloops_free(&eloops_rail);
- if ((BM_mesh_edgeloops_find_path(
- bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_first, v_b_last)) &&
- (BM_mesh_edgeloops_find_path(
- bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_last, v_b_first))) {
+ if (BM_mesh_edgeloops_find_path(
+ bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_first, v_b_last) &&
+ BM_mesh_edgeloops_find_path(
+ bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_last, v_b_first)) {
estore_rail_a = eloops_rail.first;
estore_rail_b = eloops_rail.last;
BM_edgeloop_flip(bm, estore_b);
diff --git a/source/blender/bmesh/operators/bmo_subdivide_edgering.c b/source/blender/bmesh/operators/bmo_subdivide_edgering.c
index d015b715a69..6a80f360f59 100644
--- a/source/blender/bmesh/operators/bmo_subdivide_edgering.c
+++ b/source/blender/bmesh/operators/bmo_subdivide_edgering.c
@@ -860,12 +860,12 @@ static bool bm_edgering_pair_order_is_flipped(BMesh *UNUSED(bm),
/* step around any fan-faces on both sides */
do {
v_iter_a_step = v_iter_a_step->next;
- } while (v_iter_a_step && ((BM_edge_exists(v_iter_a_step->data, v_iter_b_first->data)) ||
- (BM_edge_exists(v_iter_a_step->data, v_iter_b_first->next->data))));
+ } while (v_iter_a_step && (BM_edge_exists(v_iter_a_step->data, v_iter_b_first->data) ||
+ BM_edge_exists(v_iter_a_step->data, v_iter_b_first->next->data)));
do {
v_iter_b_step = v_iter_b_step->next;
- } while (v_iter_b_step && ((BM_edge_exists(v_iter_b_step->data, v_iter_a_first->data)) ||
- (BM_edge_exists(v_iter_b_step->data, v_iter_a_first->next->data))));
+ } while (v_iter_b_step && (BM_edge_exists(v_iter_b_step->data, v_iter_a_first->data) ||
+ BM_edge_exists(v_iter_b_step->data, v_iter_a_first->next->data)));
v_iter_a_step = v_iter_a_step ? v_iter_a_step->prev : lb_a->last;
v_iter_b_step = v_iter_b_step ? v_iter_b_step->prev : lb_b->last;
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 1e8ef9737d3..0f99f04ad57 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -384,7 +384,7 @@ typedef struct BevelParams {
// #pragma GCC diagnostic ignored "-Wpadded"
-/* Only for debugging, this file shouldn't be in blender repo. */
+/* Only for debugging, this file shouldn't be in blender repository. */
// #include "bevdebug.c"
/* Use the unused _BM_ELEM_TAG_ALT flag to flag the 'long' loops (parallel to beveled edge)
@@ -1495,8 +1495,9 @@ static void offset_meet(BevelParams *bp,
}
}
-/* Chosen so 1/sin(BEVEL_GOOD_ANGLE) is about 4, giving that expansion factor to bevel width. */
-#define BEVEL_GOOD_ANGLE 0.25f
+/* This was changed from 0.25f to fix bug T86768.
+ * Original bug T44961 remains fixed with this value. */
+#define BEVEL_GOOD_ANGLE 0.0001f
/**
* Calculate the meeting point between e1 and e2 (one of which should have zero offsets),
diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c
index 4ae5bfb7fb2..924538490ad 100644
--- a/source/blender/bmesh/tools/bmesh_region_match.c
+++ b/source/blender/bmesh/tools/bmesh_region_match.c
@@ -1336,7 +1336,7 @@ static void bm_vert_fasthash_destroy(UUIDFashMatch *fm)
* Take a face-region and return a list of matching face-regions.
*
* \param faces_region: A single, contiguous face-region.
- * \return A list of matching null-terminated face-region arrays.
+ * \return A list of matching null-terminated face-region arrays.
*/
int BM_mesh_region_match(BMesh *bm,
BMFace **faces_region,
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 20b56ceb55f..000ba298c2d 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -49,8 +49,11 @@ set(SRC
COM_compositor.h
COM_defines.h
+ intern/COM_BufferArea.h
intern/COM_BufferOperation.cc
intern/COM_BufferOperation.h
+ intern/COM_BufferRange.h
+ intern/COM_BuffersIterator.h
intern/COM_CPUDevice.cc
intern/COM_CPUDevice.h
intern/COM_ChunkOrder.cc
@@ -632,3 +635,19 @@ if(CXX_WARN_NO_SUGGEST_OVERRIDE)
endif()
add_dependencies(bf_compositor smaa_areatex_header)
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ tests/COM_BufferArea_test.cc
+ tests/COM_BufferRange_test.cc
+ tests/COM_BuffersIterator_test.cc
+ )
+ set(TEST_INC
+ )
+ set(TEST_LIB
+ bf_compositor
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+endif()
+
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 9f8e6f10215..900f29db44c 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -18,6 +18,9 @@
#pragma once
+#include "BLI_index_range.hh"
+#include "BLI_rect.h"
+
namespace blender::compositor {
enum class eExecutionModel {
@@ -63,6 +66,7 @@ constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType
constexpr int COM_DATA_TYPE_VECTOR_CHANNELS = COM_data_type_num_channels(DataType::Vector);
constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color);
+constexpr float COM_COLOR_TRANSPARENT[4] = {0.0f, 0.0f, 0.0f, 0.0f};
constexpr float COM_VECTOR_ZERO[3] = {0.0f, 0.0f, 0.0f};
constexpr float COM_VALUE_ZERO[1] = {0.0f};
constexpr float COM_VALUE_ONE[1] = {1.0f};
@@ -109,4 +113,24 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
+constexpr IndexRange XRange(const rcti &area)
+{
+ return IndexRange(area.xmin, area.xmax - area.xmin);
+}
+
+constexpr IndexRange YRange(const rcti &area)
+{
+ return IndexRange(area.ymin, area.ymax - area.ymin);
+}
+
+constexpr IndexRange XRange(const rcti *area)
+{
+ return XRange(*area);
+}
+
+constexpr IndexRange YRange(const rcti *area)
+{
+ return YRange(*area);
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferArea.h b/source/blender/compositor/intern/COM_BufferArea.h
new file mode 100644
index 00000000000..6f7756ecbfc
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferArea.h
@@ -0,0 +1,215 @@
+/*
+ * 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 "BLI_assert.h"
+#include "BLI_rect.h"
+#include <iterator>
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+template<typename T> class BufferAreaIterator;
+
+/**
+ * A rectangle area of buffer elements.
+ */
+template<typename T> class BufferArea : rcti {
+ public:
+ using Iterator = BufferAreaIterator<T>;
+ using ConstIterator = BufferAreaIterator<const T>;
+
+ private:
+ T *buffer_;
+ /* Number of elements in a buffer row. */
+ int buffer_width_;
+ /* Buffer element stride. */
+ int elem_stride_;
+
+ public:
+ constexpr BufferArea() = default;
+
+ /**
+ * Create a buffer area containing given rectangle area.
+ */
+ constexpr BufferArea(T *buffer, int buffer_width, const rcti &area, int elem_stride = 1)
+ : rcti(area), buffer_(buffer), buffer_width_(buffer_width), elem_stride_(elem_stride)
+ {
+ }
+
+ /**
+ * Create a buffer area containing whole buffer with no offsets.
+ */
+ constexpr BufferArea(T *buffer, int buffer_width, int buffer_height, int elem_stride = 1)
+ : buffer_(buffer), buffer_width_(buffer_width), elem_stride_(elem_stride)
+ {
+ BLI_rcti_init(this, 0, buffer_width, 0, buffer_height);
+ }
+
+ constexpr friend bool operator==(const BufferArea &a, const BufferArea &b)
+ {
+ return a.buffer_ == b.buffer_ && BLI_rcti_compare(&a, &b) && a.elem_stride_ == b.elem_stride_;
+ }
+
+ constexpr const rcti &get_rect() const
+ {
+ return *this;
+ }
+
+ /**
+ * Number of elements in a row.
+ */
+ constexpr int width() const
+ {
+ return BLI_rcti_size_x(this);
+ }
+
+ /**
+ * Number of elements in a column.
+ */
+ constexpr int height() const
+ {
+ return BLI_rcti_size_y(this);
+ }
+
+ constexpr Iterator begin()
+ {
+ return begin_iterator<Iterator>();
+ }
+
+ constexpr Iterator end()
+ {
+ return end_iterator<Iterator>();
+ }
+
+ constexpr ConstIterator begin() const
+ {
+ return begin_iterator<ConstIterator>();
+ }
+
+ constexpr ConstIterator end() const
+ {
+ return end_iterator<ConstIterator>();
+ }
+
+ private:
+ template<typename TIterator> constexpr TIterator begin_iterator() const
+ {
+ T *end_ptr = get_end_ptr();
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(buffer_, end_ptr, 1, 1, 1);
+ }
+
+ T *begin_ptr = buffer_ + (intptr_t)this->ymin * buffer_width_ * elem_stride_ +
+ (intptr_t)this->xmin * elem_stride_;
+ return TIterator(begin_ptr, end_ptr, buffer_width_, BLI_rcti_size_x(this), elem_stride_);
+ }
+
+ template<typename TIterator> constexpr TIterator end_iterator() const
+ {
+ T *end_ptr = get_end_ptr();
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(end_ptr, end_ptr, 1, 1, 1);
+ }
+
+ return TIterator(end_ptr, end_ptr, buffer_width_, BLI_rcti_size_x(this), elem_stride_);
+ }
+
+ T *get_end_ptr() const
+ {
+ if (elem_stride_ == 0) {
+ return buffer_ + 1;
+ }
+ return buffer_ + (intptr_t)(this->ymax - 1) * buffer_width_ * elem_stride_ +
+ (intptr_t)this->xmax * elem_stride_;
+ }
+};
+
+template<typename T> class BufferAreaIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = T *;
+ using pointer = T *const *;
+ using reference = T *const &;
+ using difference_type = std::ptrdiff_t;
+
+ private:
+ int elem_stride_;
+ int row_stride_;
+ /* Stride between a row end and the next row start. */
+ int rows_gap_;
+ T *current_;
+ const T *row_end_;
+ const T *end_;
+
+ public:
+ constexpr BufferAreaIterator() = default;
+
+ constexpr BufferAreaIterator(
+ T *current, const T *end, int buffer_width, int area_width, int elem_stride = 1)
+ : elem_stride_(elem_stride),
+ row_stride_(buffer_width * elem_stride),
+ rows_gap_(row_stride_ - area_width * elem_stride),
+ current_(current),
+ row_end_(current + area_width * elem_stride),
+ end_(end)
+ {
+ }
+
+ constexpr BufferAreaIterator &operator++()
+ {
+ current_ += elem_stride_;
+ BLI_assert(current_ <= row_end_);
+ if (current_ == row_end_) {
+ BLI_assert(current_ <= end_);
+ if (current_ == end_) {
+ return *this;
+ }
+ current_ += rows_gap_;
+ row_end_ += row_stride_;
+ }
+ return *this;
+ }
+
+ constexpr BufferAreaIterator operator++(int) const
+ {
+ BufferAreaIterator copied_iterator = *this;
+ ++copied_iterator;
+ return copied_iterator;
+ }
+
+ constexpr friend bool operator!=(const BufferAreaIterator &a, const BufferAreaIterator &b)
+ {
+ return a.current_ != b.current_;
+ }
+
+ constexpr friend bool operator==(const BufferAreaIterator &a, const BufferAreaIterator &b)
+ {
+ return a.current_ == b.current_;
+ }
+
+ constexpr T *operator*() const
+ {
+ return current_;
+ }
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferRange.h b/source/blender/compositor/intern/COM_BufferRange.h
new file mode 100644
index 00000000000..ffdf1f2f1e5
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferRange.h
@@ -0,0 +1,171 @@
+/*
+ * 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 "BLI_assert.h"
+#include "BLI_rect.h"
+
+#include <iterator>
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+template<typename T> class BufferRangeIterator;
+
+/**
+ * A range of buffer elements.
+ */
+template<typename T> class BufferRange {
+ public:
+ using Iterator = BufferRangeIterator<T>;
+ using ConstIterator = BufferRangeIterator<const T>;
+
+ private:
+ T *start_;
+ /* Number of elements in the range. */
+ int64_t size_;
+ /* Buffer element stride. */
+ int elem_stride_;
+
+ public:
+ constexpr BufferRange() = default;
+
+ /**
+ * Create a buffer range of elements from a given element index.
+ */
+ constexpr BufferRange(T *buffer, int64_t start_elem_index, int64_t size, int elem_stride = 1)
+ : start_(buffer + start_elem_index * elem_stride), size_(size), elem_stride_(elem_stride)
+ {
+ }
+
+ constexpr friend bool operator==(const BufferRange &a, const BufferRange &b)
+ {
+ return a.start_ == b.start_ && a.size_ == b.size_ && a.elem_stride_ == b.elem_stride_;
+ }
+
+ /**
+ * Access an element in the range. Index is relative to range start.
+ */
+ constexpr T *operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->size());
+ return start_ + index * elem_stride_;
+ }
+
+ /**
+ * Get the number of elements in the range.
+ */
+ constexpr int64_t size() const
+ {
+ return size_;
+ }
+
+ constexpr Iterator begin()
+ {
+ return begin_iterator<Iterator>();
+ }
+
+ constexpr Iterator end()
+ {
+ return end_iterator<Iterator>();
+ }
+
+ constexpr ConstIterator begin() const
+ {
+ return begin_iterator<ConstIterator>();
+ }
+
+ constexpr ConstIterator end() const
+ {
+ return end_iterator<ConstIterator>();
+ }
+
+ private:
+ template<typename TIterator> constexpr TIterator begin_iterator() const
+ {
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(start_, 1);
+ }
+
+ return TIterator(start_, elem_stride_);
+ }
+
+ template<typename TIterator> constexpr TIterator end_iterator() const
+ {
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(start_ + 1, 1);
+ }
+
+ return TIterator(start_ + size_ * elem_stride_, elem_stride_);
+ }
+};
+
+template<typename T> class BufferRangeIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = T *;
+ using pointer = T *const *;
+ using reference = T *const &;
+ using difference_type = std::ptrdiff_t;
+
+ private:
+ T *current_;
+ int elem_stride_;
+
+ public:
+ constexpr BufferRangeIterator() = default;
+
+ constexpr BufferRangeIterator(T *current, int elem_stride = 1)
+ : current_(current), elem_stride_(elem_stride)
+ {
+ }
+
+ constexpr BufferRangeIterator &operator++()
+ {
+ current_ += elem_stride_;
+ return *this;
+ }
+
+ constexpr BufferRangeIterator operator++(int) const
+ {
+ BufferRangeIterator copied_iterator = *this;
+ ++copied_iterator;
+ return copied_iterator;
+ }
+
+ constexpr friend bool operator!=(const BufferRangeIterator &a, const BufferRangeIterator &b)
+ {
+ return a.current_ != b.current_;
+ }
+
+ constexpr friend bool operator==(const BufferRangeIterator &a, const BufferRangeIterator &b)
+ {
+ return a.current_ == b.current_;
+ }
+
+ constexpr T *operator*() const
+ {
+ return current_;
+ }
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BuffersIterator.h b/source/blender/compositor/intern/COM_BuffersIterator.h
new file mode 100644
index 00000000000..bfe0b7a3d45
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BuffersIterator.h
@@ -0,0 +1,195 @@
+/*
+ * 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 "BLI_rect.h"
+#include "BLI_vector.hh"
+
+namespace blender::compositor {
+
+/**
+ * Builds an iterator for simultaneously iterating an area of elements in an output buffer and any
+ * number of input buffers. It's not a standard C++ iterator and it does not support neither
+ * deference, equality or postfix increment operators.
+ */
+template<typename T> class BuffersIteratorBuilder {
+ public:
+ class Iterator {
+ int x_start_;
+ int x_end_;
+ const T *out_end_;
+ int out_elem_stride_;
+ /* Stride between an output row end and the next row start. */
+ int out_rows_gap_;
+
+ struct In {
+ int elem_stride;
+ int rows_gap;
+ const T *in;
+ };
+ Vector<In, 6> ins_;
+
+ friend class BuffersIteratorBuilder;
+
+ public:
+ int x;
+ int y;
+ /** Current output element. */
+ T *out;
+
+ public:
+ /**
+ * Get current element from an input.
+ */
+ const T *in(int input_index) const
+ {
+ BLI_assert(input_index < ins_.size());
+ return ins_[input_index].in;
+ }
+
+ int get_num_inputs() const
+ {
+ return ins_.size();
+ }
+
+ /**
+ * Has the end of the area been reached.
+ */
+ bool is_end() const
+ {
+ return out >= out_end_;
+ }
+
+ /**
+ * Go to the next element in the area.
+ */
+ void next()
+ {
+ out += out_elem_stride_;
+ for (In &in : ins_) {
+ in.in += in.elem_stride;
+ }
+ x++;
+ if (x == x_end_) {
+ x = x_start_;
+ y++;
+ out += out_rows_gap_;
+ for (In &in : ins_) {
+ in.in += in.rows_gap;
+ }
+ }
+ }
+
+ Iterator &operator++()
+ {
+ this->next();
+ return *this;
+ }
+ };
+
+ private:
+ Iterator iterator_;
+ rcti area_;
+ bool is_built_;
+
+ public:
+ /**
+ * Create a buffers iterator builder to iterate given output buffer area.
+ * \param output: Output buffer.
+ * \param buffer_area: Whole output buffer area (may have offset position).
+ * \param iterated_area: Area to be iterated in all buffers.
+ * \param elem_stride: Output buffer element stride.
+ */
+ BuffersIteratorBuilder(T *output,
+ const rcti &buffer_area,
+ const rcti &iterated_area,
+ int elem_stride = 1)
+ : area_(iterated_area), is_built_(false)
+ {
+ BLI_assert(BLI_rcti_inside_rcti(&buffer_area, &iterated_area));
+ iterator_.x = iterated_area.xmin;
+ iterator_.y = iterated_area.ymin;
+ iterator_.x_start_ = iterated_area.xmin;
+ iterator_.x_end_ = iterated_area.xmax;
+
+ iterator_.out_elem_stride_ = elem_stride;
+ const int buffer_width = BLI_rcti_size_x(&buffer_area);
+ intptr_t out_row_stride = buffer_width * elem_stride;
+ iterator_.out_rows_gap_ = out_row_stride - BLI_rcti_size_x(&iterated_area) * elem_stride;
+ const int out_start_x = iterated_area.xmin - buffer_area.xmin;
+ const int out_start_y = iterated_area.ymin - buffer_area.ymin;
+ iterator_.out = output + (intptr_t)out_start_y * out_row_stride +
+ (intptr_t)out_start_x * elem_stride;
+ const T *out_row_end_ = iterator_.out +
+ (intptr_t)BLI_rcti_size_x(&iterated_area) * elem_stride;
+ iterator_.out_end_ = out_row_end_ +
+ (intptr_t)out_row_stride * (BLI_rcti_size_y(&iterated_area) - 1);
+ }
+
+ /**
+ * Create a buffers iterator builder to iterate given output buffer with no offsets.
+ */
+ BuffersIteratorBuilder(T *output, int buffer_width, int buffer_height, int elem_stride = 1)
+ : BuffersIteratorBuilder(output,
+ {0, buffer_width, 0, buffer_height},
+ {0, buffer_width, 0, buffer_height},
+ elem_stride)
+ {
+ }
+
+ /**
+ * Add an input buffer to be iterated. It must contain iterated area.
+ */
+ void add_input(const T *input, const rcti &buffer_area, int elem_stride = 1)
+ {
+ BLI_assert(!is_built_);
+ BLI_assert(BLI_rcti_inside_rcti(&buffer_area, &area_));
+ typename Iterator::In in;
+ in.elem_stride = elem_stride;
+ const int buffer_width = BLI_rcti_size_x(&buffer_area);
+ in.rows_gap = buffer_width * elem_stride - BLI_rcti_size_x(&area_) * elem_stride;
+ const int in_start_x = area_.xmin - buffer_area.xmin;
+ const int in_start_y = area_.ymin - buffer_area.ymin;
+ in.in = input + in_start_y * buffer_width * elem_stride + in_start_x * elem_stride;
+ iterator_.ins_.append(std::move(in));
+ }
+
+ /**
+ * Add an input buffer to be iterated with no offsets. It must contain iterated area.
+ */
+ void add_input(const T *input, int buffer_width, int elem_stride = 1)
+ {
+ rcti buffer_area;
+ BLI_rcti_init(&buffer_area, 0, buffer_width, 0, area_.ymax);
+ add_input(input, buffer_area, elem_stride);
+ }
+
+ /**
+ * Build the iterator.
+ */
+ BuffersIteratorBuilder::Iterator build()
+ {
+ is_built_ = true;
+ return iterator_;
+ }
+};
+
+template<typename T> using BuffersIterator = typename BuffersIteratorBuilder<T>::Iterator;
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc
index f20324de342..5b48ff8fc08 100644
--- a/source/blender/compositor/intern/COM_ConstantFolder.cc
+++ b/source/blender/compositor/intern/COM_ConstantFolder.cc
@@ -53,12 +53,12 @@ static bool is_constant_foldable(NodeOperation *operation)
return false;
}
-static Vector<NodeOperation *> find_constant_foldable_operations(Span<NodeOperation *> operations)
+static Set<NodeOperation *> find_constant_foldable_operations(Span<NodeOperation *> operations)
{
- Vector<NodeOperation *> foldable_ops;
+ Set<NodeOperation *> foldable_ops;
for (NodeOperation *op : operations) {
if (is_constant_foldable(op)) {
- foldable_ops.append(op);
+ foldable_ops.add(op);
}
}
return foldable_ops;
@@ -94,6 +94,7 @@ ConstantOperation *ConstantFolder::fold_operation(NodeOperation *operation)
const DataType data_type = operation->getOutputSocket()->getDataType();
MemoryBuffer fold_buf(data_type, first_elem_area_);
Vector<MemoryBuffer *> input_bufs = get_constant_input_buffers(operation);
+ operation->init_data();
operation->render(&fold_buf, {first_elem_area_}, input_bufs);
MemoryBuffer *constant_buf = create_constant_buffer(data_type);
@@ -132,7 +133,7 @@ Vector<MemoryBuffer *> ConstantFolder::get_constant_input_buffers(NodeOperation
/** Returns constant operations resulted from folded operations. */
Vector<ConstantOperation *> ConstantFolder::try_fold_operations(Span<NodeOperation *> operations)
{
- Vector<NodeOperation *> foldable_ops = find_constant_foldable_operations(operations);
+ Set<NodeOperation *> foldable_ops = find_constant_foldable_operations(operations);
if (foldable_ops.size() == 0) {
return Vector<ConstantOperation *>();
}
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index 18973bb5a00..1983eb190e2 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -518,7 +518,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperation *first = nullptr;
ScaleOperation *scaleOperation = nullptr;
if (doScale) {
- scaleOperation = new ScaleOperation(fromSocket->getDataType());
+ scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
first = scaleOperation;
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index abef4517b3e..5443974cbb0 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -31,6 +31,8 @@ extern "C" {
#include "BKE_appdir.h"
#include "BKE_node.h"
#include "DNA_node_types.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
}
#include "COM_ExecutionSystem.h"
@@ -50,7 +52,7 @@ std::string DebugInfo::m_current_node_name;
std::string DebugInfo::m_current_op_name;
DebugInfo::GroupStateMap DebugInfo::m_group_states;
-static std::string operation_class_name(NodeOperation *op)
+static std::string operation_class_name(const NodeOperation *op)
{
std::string full_name = typeid(*op).name();
/* Remove name-spaces. */
@@ -452,4 +454,46 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name)
}
}
+static std::string get_operations_export_dir()
+{
+ return std::string(BKE_tempdir_session()) + "COM_operations" + SEP_STR;
+}
+
+void DebugInfo::export_operation(const NodeOperation *op, MemoryBuffer *render)
+{
+ ImBuf *ibuf = IMB_allocFromBuffer(nullptr,
+ render->getBuffer(),
+ render->getWidth(),
+ render->getHeight(),
+ render->get_num_channels());
+
+ const std::string file_name = operation_class_name(op) + "_" + std::to_string(op->get_id()) +
+ ".png";
+ const std::string path = get_operations_export_dir() + file_name;
+ BLI_make_existing_file(path.c_str());
+ IMB_saveiff(ibuf, path.c_str(), ibuf->flags);
+ IMB_freeImBuf(ibuf);
+}
+
+void DebugInfo::delete_operation_exports()
+{
+ const std::string dir = get_operations_export_dir();
+ if (BLI_exists(dir.c_str())) {
+ struct direntry *file_list;
+ int num_files = BLI_filelist_dir_contents(dir.c_str(), &file_list);
+ for (int i = 0; i < num_files; i++) {
+ direntry *file = &file_list[i];
+ const eFileAttributes file_attrs = BLI_file_attributes(file->path);
+ if (file_attrs & FILE_ATTR_ANY_LINK) {
+ continue;
+ }
+
+ if (BLI_is_file(file->path) && BLI_path_extension_check(file->path, ".png")) {
+ BLI_delete(file->path, false, false);
+ }
+ }
+ BLI_filelist_free(file_list, num_files);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index 53461e13f48..23d99c7e529 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -30,6 +30,9 @@ namespace blender::compositor {
static constexpr bool COM_EXPORT_GRAPHVIZ = false;
static constexpr bool COM_GRAPHVIZ_SHOW_NODE_NAME = false;
+/* Saves operations results to image files. */
+static constexpr bool COM_EXPORT_OPERATION_BUFFERS = false;
+
class Node;
class ExecutionSystem;
class ExecutionGroup;
@@ -75,6 +78,9 @@ class DebugInfo {
m_group_states[execution_group] = EG_WAIT;
}
}
+ if (COM_EXPORT_OPERATION_BUFFERS) {
+ delete_operation_exports();
+ }
};
static void node_added(const Node *node)
@@ -118,6 +124,14 @@ class DebugInfo {
}
};
+ static void operation_rendered(const NodeOperation *op, MemoryBuffer *render)
+ {
+ /* Don't export constant operations as there are too many and it's rarely useful. */
+ if (COM_EXPORT_OPERATION_BUFFERS && render && !render->is_a_single_elem()) {
+ export_operation(op, render);
+ }
+ }
+
static void graphviz(const ExecutionSystem *system, StringRefNull name = "");
protected:
@@ -133,6 +147,9 @@ class DebugInfo {
const char *name, const char *color, const char *style, char *str, int maxlen);
static int graphviz_legend(char *str, int maxlen, bool has_execution_groups);
static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen);
+
+ static void export_operation(const NodeOperation *op, MemoryBuffer *render);
+ static void delete_operation_exports();
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Enums.cc b/source/blender/compositor/intern/COM_Enums.cc
index d218de92544..2f20d2652ba 100644
--- a/source/blender/compositor/intern/COM_Enums.cc
+++ b/source/blender/compositor/intern/COM_Enums.cc
@@ -17,9 +17,28 @@
*/
#include "COM_Enums.h"
+#include "BLI_rect.h"
namespace blender::compositor {
+void expand_area_for_sampler(rcti &area, PixelSampler sampler)
+{
+ switch (sampler) {
+ case PixelSampler::Nearest:
+ break;
+ case PixelSampler::Bilinear:
+ area.xmax += 1;
+ area.ymax += 1;
+ break;
+ case PixelSampler::Bicubic:
+ area.xmin -= 1;
+ area.xmax += 2;
+ area.ymin -= 1;
+ area.ymax += 2;
+ break;
+ }
+}
+
std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority)
{
switch (priority) {
diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h
index 519e7df940e..7e5a1b73132 100644
--- a/source/blender/compositor/intern/COM_Enums.h
+++ b/source/blender/compositor/intern/COM_Enums.h
@@ -22,6 +22,8 @@
#include <ostream>
+struct rcti;
+
namespace blender::compositor {
/**
@@ -85,6 +87,13 @@ enum class eWorkPackageType {
CustomFunction = 1
};
+enum class PixelSampler {
+ Nearest = 0,
+ Bilinear = 1,
+ Bicubic = 2,
+};
+void expand_area_for_sampler(rcti &area, PixelSampler sampler);
+
std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority);
std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state);
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc
index 60caf22be1b..c92e292c74f 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.cc
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc
@@ -43,6 +43,7 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
const ColorManagedDisplaySettings *displaySettings,
const char *viewName)
{
+ num_work_threads_ = WorkScheduler::get_num_cpu_threads();
this->m_context.setViewName(viewName);
this->m_context.setScene(scene);
this->m_context.setbNodeTree(editingtree);
@@ -112,6 +113,9 @@ void ExecutionSystem::set_operations(const Vector<NodeOperation *> &operations,
void ExecutionSystem::execute()
{
DebugInfo::execute_started(this);
+ for (NodeOperation *op : m_operations) {
+ op->init_data();
+ }
execution_model_->execute(*this);
}
@@ -127,7 +131,7 @@ void ExecutionSystem::execute_work(const rcti &work_rect,
/* Split work vertically to maximize continuous memory. */
const int work_height = BLI_rcti_size_y(&work_rect);
- const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height);
+ const int num_sub_works = MIN2(num_work_threads_, work_height);
const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works;
int remaining_height = work_height - split_height * num_sub_works;
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h
index 38c3432a8ec..bce96db52c7 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.h
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.h
@@ -31,6 +31,7 @@ class ExecutionGroup;
#include "DNA_node_types.h"
#include "BLI_vector.hh"
+#include "atomic_ops.h"
namespace blender::compositor {
@@ -150,6 +151,11 @@ class ExecutionSystem {
*/
ExecutionModel *execution_model_;
+ /**
+ * Number of cpu threads available for work execution.
+ */
+ int num_work_threads_;
+
ThreadMutex work_mutex_;
ThreadCondition work_finished_cond_;
@@ -201,6 +207,27 @@ class ExecutionSystem {
void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func);
+ /**
+ * Multi-threaded execution of given work function passing work_rect splits as argument.
+ * Once finished, caller thread will call reduce_func for each thread result.
+ */
+ template<typename TResult>
+ void execute_work(const rcti &work_rect,
+ std::function<TResult(const rcti &split_rect)> work_func,
+ TResult &join,
+ std::function<void(TResult &join, const TResult &chunk)> reduce_func)
+ {
+ Array<TResult> chunks(num_work_threads_);
+ int num_started = 0;
+ execute_work(work_rect, [&](const rcti &split_rect) {
+ const int current = atomic_fetch_and_add_int32(&num_started, 1);
+ chunks[current] = work_func(split_rect);
+ });
+ for (const int i : IndexRange(num_started)) {
+ reduce_func(join, chunks[i]);
+ }
+ }
+
bool is_breaked() const;
private:
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
index 3b0a9172871..bd3a481d691 100644
--- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -20,6 +20,7 @@
#include "COM_Debug.h"
#include "COM_ExecutionGroup.h"
#include "COM_ReadBufferOperation.h"
+#include "COM_ViewerOperation.h"
#include "COM_WorkScheduler.h"
#include "BLT_translation.h"
@@ -100,8 +101,13 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op)
const bool has_outputs = op->getNumberOfOutputSockets() > 0;
MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr;
- Span<rcti> areas = active_buffers_.get_areas_to_render(op);
- op->render(op_buf, areas, input_bufs);
+ if (op->getWidth() > 0 && op->getHeight() > 0) {
+ Span<rcti> areas = active_buffers_.get_areas_to_render(op);
+ op->render(op_buf, areas, input_bufs);
+ DebugInfo::operation_rendered(op, op_buf);
+ }
+ /* Even if operation has no resolution set the empty buffer. It will be clipped with a
+ * TranslateOperation from convert resolutions if linked to an operation with resolution. */
active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf));
operation_finished(op);
@@ -117,10 +123,16 @@ void FullFrameExecutionModel::render_operations()
WorkScheduler::start(this->context_);
for (eCompositorPriority priority : priorities_) {
for (NodeOperation *op : operations_) {
- if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) {
+ const bool has_size = op->getWidth() > 0 && op->getHeight() > 0;
+ const bool is_priority_output = op->isOutputOperation(is_rendering) &&
+ op->getRenderPriority() == priority;
+ if (is_priority_output && has_size) {
render_output_dependencies(op);
render_operation(op);
}
+ else if (is_priority_output && !has_size && op->isActiveViewerOutput()) {
+ static_cast<ViewerOperation *>(op)->clear_display_buffer();
+ }
}
}
WorkScheduler::stop();
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index c7bddddd0e6..6b954072a9a 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -129,6 +129,20 @@ void MemoryBuffer::clear()
memset(m_buffer, 0, buffer_len() * m_num_channels * sizeof(float));
}
+BuffersIterator<float> MemoryBuffer::iterate_with(Span<MemoryBuffer *> inputs)
+{
+ return iterate_with(inputs, m_rect);
+}
+
+BuffersIterator<float> MemoryBuffer::iterate_with(Span<MemoryBuffer *> inputs, const rcti &area)
+{
+ BuffersIteratorBuilder<float> builder(m_buffer, m_rect, area, elem_stride);
+ for (MemoryBuffer *input : inputs) {
+ builder.add_input(input->getBuffer(), input->get_rect(), input->elem_stride);
+ }
+ return builder.build();
+}
+
/**
* Converts a single elem buffer to a full size buffer (allocates memory for all
* elements in resolution).
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index 4ad0872b0b7..ae12c444dc1 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -18,6 +18,9 @@
#pragma once
+#include "COM_BufferArea.h"
+#include "COM_BufferRange.h"
+#include "COM_BuffersIterator.h"
#include "COM_ExecutionGroup.h"
#include "COM_MemoryProxy.h"
@@ -186,6 +189,25 @@ class MemoryBuffer {
return m_buffer + get_coords_offset(x, y);
}
+ void read_elem(int x, int y, float *out) const
+ {
+ memcpy(out, get_elem(x, y), m_num_channels * sizeof(float));
+ }
+
+ void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
+ {
+ switch (sampler) {
+ case PixelSampler::Nearest:
+ this->read_elem(x, y, out);
+ break;
+ case PixelSampler::Bilinear:
+ case PixelSampler::Bicubic:
+ /* No bicubic. Current implementation produces fuzzy results. */
+ this->readBilinear(out, x, y);
+ break;
+ }
+ }
+
/**
* Get channel value at given coordinates.
*/
@@ -239,6 +261,32 @@ class MemoryBuffer {
}
/**
+ * Get all buffer elements as a range with no offsets.
+ */
+ BufferRange<float> as_range()
+ {
+ return BufferRange<float>(m_buffer, 0, buffer_len(), elem_stride);
+ }
+
+ BufferRange<const float> as_range() const
+ {
+ return BufferRange<const float>(m_buffer, 0, buffer_len(), elem_stride);
+ }
+
+ BufferArea<float> get_buffer_area(const rcti &area)
+ {
+ return BufferArea<float>(m_buffer, getWidth(), area, elem_stride);
+ }
+
+ BufferArea<const float> get_buffer_area(const rcti &area) const
+ {
+ return BufferArea<const float>(m_buffer, getWidth(), area, elem_stride);
+ }
+
+ BuffersIterator<float> iterate_with(Span<MemoryBuffer *> inputs);
+ BuffersIterator<float> iterate_with(Span<MemoryBuffer *> inputs, const rcti &area);
+
+ /**
* \brief get the data of this MemoryBuffer
* \note buffer should already be available in memory
*/
@@ -301,7 +349,7 @@ class MemoryBuffer {
inline void wrap_pixel(float &x,
float &y,
MemoryBufferExtend extend_x,
- MemoryBufferExtend extend_y)
+ MemoryBufferExtend extend_y) const
{
const float w = (float)getWidth();
const float h = (float)getHeight();
@@ -398,7 +446,7 @@ class MemoryBuffer {
float x,
float y,
MemoryBufferExtend extend_x = MemoryBufferExtend::Clip,
- MemoryBufferExtend extend_y = MemoryBufferExtend::Clip)
+ MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) const
{
float u = x;
float v = y;
diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc
index 4e115cb3f2f..575e8446abe 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -100,6 +100,11 @@ void NodeOperation::setResolutionInputSocketIndex(unsigned int index)
{
this->m_resolutionInputSocketIndex = index;
}
+
+void NodeOperation::init_data()
+{
+ /* Pass. */
+}
void NodeOperation::initExecution()
{
/* pass */
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index fb9ec1e7a83..934007d25ce 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -75,12 +75,6 @@ enum class ResizeMode {
Stretch = NS_CR_STRETCH,
};
-enum class PixelSampler {
- Nearest = 0,
- Bilinear = 1,
- Bicubic = 2,
-};
-
class NodeOperationInput {
private:
NodeOperation *m_operation;
@@ -424,6 +418,12 @@ class NodeOperation {
exec_system_ = system;
}
+ /**
+ * Initializes operation data needed after operations are linked and resolutions determined. For
+ * rendering heap memory data use initExecution().
+ */
+ virtual void init_data();
+
virtual void initExecution();
/**
diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc
index 9798dabd035..b51e79f2dea 100644
--- a/source/blender/compositor/nodes/COM_IDMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc
@@ -17,9 +17,9 @@
*/
#include "COM_IDMaskNode.h"
-#include "COM_AntiAliasOperation.h"
#include "COM_ExecutionSystem.h"
#include "COM_IDMaskOperation.h"
+#include "COM_SMAAOperation.h"
namespace blender::compositor {
@@ -42,11 +42,27 @@ void IDMaskNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
else {
- AntiAliasOperation *antiAliasOperation = new AntiAliasOperation();
- converter.addOperation(antiAliasOperation);
+ SMAAEdgeDetectionOperation *operation1 = nullptr;
- converter.addLink(operation->getOutputSocket(), antiAliasOperation->getInputSocket(0));
- converter.mapOutputSocket(getOutputSocket(0), antiAliasOperation->getOutputSocket(0));
+ operation1 = new SMAAEdgeDetectionOperation();
+ converter.addOperation(operation1);
+
+ converter.addLink(operation->getOutputSocket(0), operation1->getInputSocket(0));
+
+ /* Blending Weight Calculation Pixel Shader (Second Pass). */
+ SMAABlendingWeightCalculationOperation *operation2 =
+ new SMAABlendingWeightCalculationOperation();
+ converter.addOperation(operation2);
+
+ converter.addLink(operation1->getOutputSocket(), operation2->getInputSocket(0));
+
+ /* Neighborhood Blending Pixel Shader (Third Pass). */
+ SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation();
+ converter.addOperation(operation3);
+
+ converter.addLink(operation->getOutputSocket(0), operation3->getInputSocket(0));
+ converter.addLink(operation2->getOutputSocket(), operation3->getInputSocket(1));
+ converter.mapOutputSocket(getOutputSocket(0), operation3->getOutputSocket());
}
}
diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc
index 253ca542c04..6744e98ecdb 100644
--- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc
+++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc
@@ -143,7 +143,7 @@ void RenderLayersNode::missingSocketLink(NodeConverter &converter, NodeOutput *o
break;
}
default: {
- BLI_assert("!Unexpected data type");
+ BLI_assert_msg(0, "Unexpected data type");
return;
}
}
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc
index 50d2902f375..819d2e72f30 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.cc
+++ b/source/blender/compositor/nodes/COM_ScaleNode.cc
@@ -43,7 +43,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
switch (bnode->custom1) {
case CMP_SCALE_RELATIVE: {
- ScaleOperation *operation = new ScaleOperation();
+ ScaleRelativeOperation *operation = new ScaleRelativeOperation();
converter.addOperation(operation);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
@@ -59,7 +59,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
scaleFactorOperation->setValue(context.getRenderPercentageAsFactor());
converter.addOperation(scaleFactorOperation);
- ScaleOperation *operation = new ScaleOperation();
+ ScaleRelativeOperation *operation = new ScaleRelativeOperation();
converter.addOperation(operation);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
index fc72b48eca2..0262f653d1a 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
@@ -43,7 +43,7 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
MovieClip *clip = (MovieClip *)editorNode->id;
bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0;
- ScaleOperation *scaleOperation = new ScaleOperation();
+ ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
scaleOperation->setSampler((PixelSampler)editorNode->custom1);
RotateOperation *rotateOperation = new RotateOperation();
rotateOperation->setDoDegree2RadConversion(false);
diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc
index cd12939ab43..e1deaf616a4 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.cc
+++ b/source/blender/compositor/nodes/COM_TransformNode.cc
@@ -40,7 +40,7 @@ void TransformNode::convertToOperations(NodeConverter &converter,
NodeInput *angleInput = this->getInputSocket(3);
NodeInput *scaleInput = this->getInputSocket(4);
- ScaleOperation *scaleOperation = new ScaleOperation();
+ ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
converter.addOperation(scaleOperation);
RotateOperation *rotateOperation = new RotateOperation();
diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc
index 9938d4a85ed..15bb19660dc 100644
--- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc
@@ -105,6 +105,64 @@ void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, Pi
}
}
+void BoxMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ MaskFunc mask_func;
+ switch (m_maskType) {
+ case CMP_NODE_MASKTYPE_ADD:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? MAX2(mask[0], value[0]) : mask[0];
+ };
+ break;
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? CLAMPIS(mask[0] - value[0], 0, 1) : mask[0];
+ };
+ break;
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? mask[0] * value[0] : 0;
+ };
+ break;
+ case CMP_NODE_MASKTYPE_NOT:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ if (is_inside) {
+ return mask[0] > 0.0f ? 0.0f : value[0];
+ }
+ return mask[0];
+ };
+ break;
+ }
+ apply_mask(output, area, inputs, mask_func);
+}
+
+void BoxMaskOperation::apply_mask(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs,
+ MaskFunc mask_func)
+{
+ const float op_w = this->getWidth();
+ const float op_h = this->getHeight();
+ const float half_w = this->m_data->width / 2.0f;
+ const float half_h = this->m_data->height / 2.0f;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float op_ry = it.y / op_h;
+ const float dy = (op_ry - this->m_data->y) / m_aspectRatio;
+ const float op_rx = it.x / op_w;
+ const float dx = op_rx - this->m_data->x;
+ const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy);
+ const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy);
+
+ const bool inside = (rx > this->m_data->x - half_w && rx < this->m_data->x + half_w &&
+ ry > this->m_data->y - half_h && ry < this->m_data->y + half_h);
+ const float *mask = it.in(0);
+ const float *value = it.in(1);
+ *it.out = mask_func(inside, mask, value);
+ }
+}
+
void BoxMaskOperation::deinitExecution()
{
this->m_inputMask = nullptr;
diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h
index fdec7bdd8ca..4c48dde844a 100644
--- a/source/blender/compositor/operations/COM_BoxMaskOperation.h
+++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h
@@ -18,12 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class BoxMaskOperation : public NodeOperation {
+class BoxMaskOperation : public MultiThreadedOperation {
private:
+ using MaskFunc = std::function<float(bool is_inside, const float *mask, const float *value)>;
+
/**
* Cached reference to the inputProgram
*/
@@ -64,6 +66,16 @@ class BoxMaskOperation : public NodeOperation {
{
this->m_maskType = maskType;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ void apply_mask(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs,
+ MaskFunc mask_func);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc
index 92cab47318a..7878eca2bbd 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.cc
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc
@@ -28,6 +28,7 @@ BrightnessOperation::BrightnessOperation()
this->addOutputSocket(DataType::Color);
this->m_inputProgram = nullptr;
this->m_use_premultiply = false;
+ flags.can_be_constant = true;
}
void BrightnessOperation::setUsePremultiply(bool use_premultiply)
@@ -85,6 +86,50 @@ void BrightnessOperation::executePixelSampled(float output[4],
}
}
+void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ float tmp_color[4];
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *in_color = it.in(0);
+ const float brightness = *it.in(1) / 100.0f;
+ const float contrast = *it.in(2);
+ float delta = contrast / 200.0f;
+ /*
+ * The algorithm is by Werner D. Streidt
+ * (http://visca.com/ffactory/archives/5-99/msg00021.html)
+ * Extracted of OpenCV demhist.c
+ */
+ float a, b;
+ if (contrast > 0) {
+ a = 1.0f - delta * 2.0f;
+ a = 1.0f / max_ff(a, FLT_EPSILON);
+ b = a * (brightness - delta);
+ }
+ else {
+ delta *= -1;
+ a = max_ff(1.0f - delta * 2.0f, 0.0f);
+ b = a * brightness + delta;
+ }
+ const float *color;
+ if (this->m_use_premultiply) {
+ premul_to_straight_v4_v4(tmp_color, in_color);
+ color = tmp_color;
+ }
+ else {
+ color = in_color;
+ }
+ it.out[0] = a * color[0] + b;
+ it.out[1] = a * color[1] + b;
+ it.out[2] = a * color[2] + b;
+ it.out[3] = color[3];
+ if (this->m_use_premultiply) {
+ straight_to_premul_v4(it.out);
+ }
+ }
+}
+
void BrightnessOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.h b/source/blender/compositor/operations/COM_BrightnessOperation.h
index 7c33e0b35ec..64b4fa0dbe2 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.h
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class BrightnessOperation : public NodeOperation {
+class BrightnessOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -52,6 +52,10 @@ class BrightnessOperation : public NodeOperation {
void deinitExecution() override;
void setUsePremultiply(bool use_premultiply);
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
index a7ea49aed8d..7457ac9e227 100644
--- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
+++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
@@ -19,6 +19,7 @@
#include "COM_CalculateMeanOperation.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "COM_ExecutionSystem.h"
#include "IMB_colormanagement.h"
@@ -128,4 +129,93 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile)
this->m_result = sum / pixels;
}
+void CalculateMeanOperation::setSetting(int setting)
+{
+ this->m_setting = setting;
+ switch (setting) {
+ case 1: {
+ setting_func_ = IMB_colormanagement_get_luminance;
+ break;
+ }
+ case 2: {
+ setting_func_ = [](const float *elem) { return elem[0]; };
+ break;
+ }
+ case 3: {
+ setting_func_ = [](const float *elem) { return elem[1]; };
+ break;
+ }
+ case 4: {
+ setting_func_ = [](const float *elem) { return elem[2]; };
+ break;
+ }
+ case 5: {
+ setting_func_ = [](const float *elem) {
+ float yuv[3];
+ rgb_to_yuv(elem[0], elem[1], elem[2], &yuv[0], &yuv[1], &yuv[2], BLI_YUV_ITU_BT709);
+ return yuv[0];
+ };
+ break;
+ }
+ }
+}
+
+void CalculateMeanOperation::get_area_of_interest(int input_idx,
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ NodeOperation *operation = getInputOperation(input_idx);
+ r_input_area.xmin = 0;
+ r_input_area.ymin = 0;
+ r_input_area.xmax = operation->getWidth();
+ r_input_area.ymax = operation->getHeight();
+}
+
+void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ if (!this->m_iscalculated) {
+ MemoryBuffer *input = inputs[0];
+ m_result = calc_mean(input);
+ this->m_iscalculated = true;
+ }
+}
+
+void CalculateMeanOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> UNUSED(inputs))
+{
+ output->fill(area, &m_result);
+}
+
+float CalculateMeanOperation::calc_mean(const MemoryBuffer *input)
+{
+ PixelsSum total = {0};
+ exec_system_->execute_work<PixelsSum>(
+ input->get_rect(),
+ [=](const rcti &split) { return calc_area_sum(input, split); },
+ total,
+ [](PixelsSum &join, const PixelsSum &chunk) {
+ join.sum += chunk.sum;
+ join.num_pixels += chunk.num_pixels;
+ });
+ return total.num_pixels == 0 ? 0.0f : total.sum / total.num_pixels;
+}
+
+using PixelsSum = CalculateMeanOperation::PixelsSum;
+PixelsSum CalculateMeanOperation::calc_area_sum(const MemoryBuffer *input, const rcti &area)
+{
+ PixelsSum result = {0};
+ for (const float *elem : input->get_buffer_area(area)) {
+ if (elem[3] <= 0.0f) {
+ continue;
+ }
+ result.sum += setting_func_(elem);
+ result.num_pixels++;
+ }
+ return result;
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.h b/source/blender/compositor/operations/COM_CalculateMeanOperation.h
index 8b3bf281c93..779ca79b38a 100644
--- a/source/blender/compositor/operations/COM_CalculateMeanOperation.h
+++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.h
@@ -18,8 +18,9 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_node_types.h"
+#include <functional>
namespace blender::compositor {
@@ -27,7 +28,13 @@ namespace blender::compositor {
* \brief base class of CalculateMean, implementing the simple CalculateMean
* \ingroup operation
*/
-class CalculateMeanOperation : public NodeOperation {
+class CalculateMeanOperation : public MultiThreadedOperation {
+ public:
+ struct PixelsSum {
+ float sum;
+ int num_pixels;
+ };
+
protected:
/**
* \brief Cached reference to the reader
@@ -37,6 +44,7 @@ class CalculateMeanOperation : public NodeOperation {
bool m_iscalculated;
float m_result;
int m_setting;
+ std::function<float(const float *elem)> setting_func_;
public:
CalculateMeanOperation();
@@ -61,13 +69,24 @@ class CalculateMeanOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
- void setSetting(int setting)
- {
- this->m_setting = setting;
- }
+ void setSetting(int setting);
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+
+ virtual void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ virtual void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
protected:
void calculateMean(MemoryBuffer *tile);
+ float calc_mean(const MemoryBuffer *input);
+
+ private:
+ PixelsSum calc_area_sum(const MemoryBuffer *input, const rcti &area);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
index ed554b9ac06..494b66cb888 100644
--- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
+++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
@@ -19,6 +19,7 @@
#include "COM_CalculateStandardDeviationOperation.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "COM_ExecutionSystem.h"
#include "IMB_colormanagement.h"
@@ -96,4 +97,50 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect)
return nullptr;
}
+void CalculateStandardDeviationOperation::update_memory_buffer_started(
+ MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span<MemoryBuffer *> inputs)
+{
+ if (!this->m_iscalculated) {
+ const MemoryBuffer *input = inputs[0];
+ const float mean = CalculateMeanOperation::calc_mean(input);
+
+ PixelsSum total = {0};
+ exec_system_->execute_work<PixelsSum>(
+ input->get_rect(),
+ [=](const rcti &split) { return calc_area_sum(input, split, mean); },
+ total,
+ [](PixelsSum &join, const PixelsSum &chunk) {
+ join.sum += chunk.sum;
+ join.num_pixels += chunk.num_pixels;
+ });
+ this->m_standardDeviation = total.num_pixels <= 1 ?
+ 0.0f :
+ sqrt(total.sum / (float)(total.num_pixels - 1));
+ this->m_iscalculated = true;
+ }
+}
+
+void CalculateStandardDeviationOperation::update_memory_buffer_partial(
+ MemoryBuffer *output, const rcti &area, Span<MemoryBuffer *> UNUSED(inputs))
+{
+ output->fill(area, &m_standardDeviation);
+}
+
+using PixelsSum = CalculateMeanOperation::PixelsSum;
+PixelsSum CalculateStandardDeviationOperation::calc_area_sum(const MemoryBuffer *input,
+ const rcti &area,
+ const float mean)
+{
+ PixelsSum result = {0};
+ for (const float *elem : input->get_buffer_area(area)) {
+ if (elem[3] <= 0.0f) {
+ continue;
+ }
+ const float value = setting_func_(elem);
+ result.sum += (value - mean) * (value - mean);
+ result.num_pixels++;
+ }
+ return result;
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
index bc4aca69546..20de4cf4701 100644
--- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
+++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
@@ -40,6 +40,17 @@ class CalculateStandardDeviationOperation : public CalculateMeanOperation {
void executePixel(float output[4], int x, int y, void *data) override;
void *initializeTileData(rcti *rect) override;
+
+ void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ PixelsSum calc_area_sum(const MemoryBuffer *input, const rcti &area, float mean);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
index c4099a6d33d..55ae19ad194 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
@@ -25,7 +25,7 @@
namespace blender::compositor {
-// this part has been copied from the double edge mask
+/* This part has been copied from the double edge mask. */
static void do_adjacentKeepBorders(unsigned int t,
unsigned int rw,
const unsigned int *limask,
@@ -35,163 +35,163 @@ static void do_adjacentKeepBorders(unsigned int t,
unsigned int *rsize)
{
int x;
- unsigned int isz = 0; // inner edge size
- unsigned int osz = 0; // outer edge size
- unsigned int gsz = 0; // gradient fill area size
+ unsigned int isz = 0; /* Inner edge size. */
+ unsigned int osz = 0; /* Outer edge size. */
+ unsigned int gsz = 0; /* Gradient fill area size. */
/* Test the four corners */
- /* upper left corner */
+ /* Upper left corner. */
x = t - rw + 1;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel underneath, or to the right, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel underneath, or to the right, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + 1] && lomask[x + 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
- /* upper right corner */
+ /* Upper right corner. */
x = t;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel underneath, or to the left, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel underneath, or to the left, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x - 1] && lomask[x - 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
- /* lower left corner */
+ /* Lower left corner. */
x = 0;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel above, or to the right, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel above, or to the right, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x + 1] && lomask[x + 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
- /* lower right corner */
+ /* Lower right corner. */
x = rw - 1;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel above, or to the left, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel above, or to the left, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x - 1] && lomask[x - 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
/* Test the TOP row of pixels in buffer, except corners */
for (x = t - 1; x >= (t - rw) + 2; x--) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel to the right, or to the left, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel to the right, or to the left, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
}
/* Test the BOTTOM row of pixels in buffer, except corners */
for (x = rw - 2; x; x--) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel to the right, or to the left, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel to the right, or to the left, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
}
/* Test the LEFT edge of pixels in buffer, except corners */
for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel underneath, or above, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel underneath, or above, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
}
/* Test the RIGHT edge of pixels in buffer, except corners */
for (x = t - rw; x > rw; x -= rw) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel underneath, or above, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel underneath, or above, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
}
- rsize[0] = isz; // fill in our return sizes for edges + fill
+ rsize[0] = isz; /* Fill in our return sizes for edges + fill. */
rsize[1] = osz;
rsize[2] = gsz;
}
@@ -205,214 +205,218 @@ static void do_adjacentBleedBorders(unsigned int t,
unsigned int *rsize)
{
int x;
- unsigned int isz = 0; // inner edge size
- unsigned int osz = 0; // outer edge size
- unsigned int gsz = 0; // gradient fill area size
+ unsigned int isz = 0; /* Inner edge size. */
+ unsigned int osz = 0; /* Outer edge size. */
+ unsigned int gsz = 0; /* Gradient fill area size. */
/* Test the four corners */
- /* upper left corner */
+ /* Upper left corner. */
x = t - rw + 1;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel underneath, or to the right, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel underneath, or to the right, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + 1] && lomask[x + 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x - rw] ||
- !lomask[x + 1]) { // test if outer mask is empty underneath or to the right
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x + 1]) { /* Test if outer mask is empty underneath or to the right. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
- /* upper right corner */
+ /* Upper right corner. */
x = t;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel underneath, or to the left, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel underneath, or to the left, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x - 1] && lomask[x - 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x - rw] ||
- !lomask[x - 1]) { // test if outer mask is empty underneath or to the left
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x - 1]) { /* Test if outer mask is empty underneath or to the left. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
- /* lower left corner */
+ /* Lower left corner. */
x = 0;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel above, or to the right, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel above, or to the right, are empty in the inner mask,
+ * But filled in the outer mask. */
if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x + 1] && lomask[x + 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- if (!lomask[x + rw] || !lomask[x + 1]) { // test if outer mask is empty above or to the right
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ if (!lomask[x + rw] ||
+ !lomask[x + 1]) { /* Test if outer mask is empty above or to the right. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
- /* lower right corner */
+ /* Lower right corner. */
x = rw - 1;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel above, or to the left, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel above, or to the left, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x - 1] && lomask[x - 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- if (!lomask[x + rw] || !lomask[x - 1]) { // test if outer mask is empty above or to the left
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ if (!lomask[x + rw] ||
+ !lomask[x - 1]) { /* Test if outer mask is empty above or to the left. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
/* Test the TOP row of pixels in buffer, except corners */
for (x = t - 1; x >= (t - rw) + 2; x--) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel to the left, or to the right, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel to the left, or to the right, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x - 1] ||
- !lomask[x + 1]) { // test if outer mask is empty to the left or to the right
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x + 1]) { /* Test if outer mask is empty to the left or to the right. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
}
/* Test the BOTTOM row of pixels in buffer, except corners */
for (x = rw - 2; x; x--) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel to the left, or to the right, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel to the left, or to the right, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x - 1] ||
- !lomask[x + 1]) { // test if outer mask is empty to the left or to the right
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x + 1]) { /* Test if outer mask is empty to the left or to the right. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
}
/* Test the LEFT edge of pixels in buffer, except corners */
for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel underneath, or above, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel underneath, or above, are empty in the inner mask,
+ * but filled in the outer mask. */
if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ if (!lomask[x - rw] ||
+ !lomask[x + rw]) { /* Test if outer mask is empty underneath or above. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
}
/* Test the RIGHT edge of pixels in buffer, except corners */
for (x = t - rw; x > rw; x -= rw) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if pixel underneath, or above, are empty in the inner mask,
- // but filled in the outer mask
+ /* Test if pixel underneath, or above, are empty in the inner mask,
+ * But filled in the outer mask. */
if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ if (!lomask[x - rw] ||
+ !lomask[x + rw]) { /* Test if outer mask is empty underneath or above. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
}
- rsize[0] = isz; // fill in our return sizes for edges + fill
+ rsize[0] = isz; /* Fill in our return sizes for edges + fill. */
rsize[1] = osz;
rsize[2] = gsz;
}
@@ -426,155 +430,155 @@ static void do_allKeepBorders(unsigned int t,
unsigned int *rsize)
{
int x;
- unsigned int isz = 0; // inner edge size
- unsigned int osz = 0; // outer edge size
- unsigned int gsz = 0; // gradient fill area size
- /* Test the four corners */
- /* upper left corner */
+ unsigned int isz = 0; /* Inner edge size. */
+ unsigned int osz = 0; /* Outer edge size. */
+ unsigned int gsz = 0; /* Gradient fill area size. */
+ /* Test the four corners. */
+ /* Upper left corner. */
x = t - rw + 1;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if the inner mask is empty underneath or to the right
+ /* Test if the inner mask is empty underneath or to the right. */
if (!limask[x - rw] || !limask[x + 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
- /* upper right corner */
+ /* Upper right corner. */
x = t;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if the inner mask is empty underneath or to the left
+ /* Test if the inner mask is empty underneath or to the left. */
if (!limask[x - rw] || !limask[x - 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
- /* lower left corner */
+ /* Lower left corner. */
x = 0;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty above or to the right
+ /* Test if inner mask is empty above or to the right. */
if (!limask[x + rw] || !limask[x + 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
- /* lower right corner */
+ /* Lower right corner. */
x = rw - 1;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty above or to the left
+ /* Test if inner mask is empty above or to the left. */
if (!limask[x + rw] || !limask[x - 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
/* Test the TOP row of pixels in buffer, except corners */
for (x = t - 1; x >= (t - rw) + 2; x--) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty to the left or to the right
+ /* Test if inner mask is empty to the left or to the right. */
if (!limask[x - 1] || !limask[x + 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
}
/* Test the BOTTOM row of pixels in buffer, except corners */
for (x = rw - 2; x; x--) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty to the left or to the right
+ /* Test if inner mask is empty to the left or to the right. */
if (!limask[x - 1] || !limask[x + 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
}
/* Test the LEFT edge of pixels in buffer, except corners */
for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty underneath or above
+ /* Test if inner mask is empty underneath or above. */
if (!limask[x - rw] || !limask[x + rw]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
}
/* Test the RIGHT edge of pixels in buffer, except corners */
for (x = t - rw; x > rw; x -= rw) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty underneath or above
+ /* Test if inner mask is empty underneath or above. */
if (!limask[x - rw] || !limask[x + rw]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
}
- rsize[0] = isz; // fill in our return sizes for edges + fill
+ rsize[0] = isz; /* Fill in our return sizes for edges + fill. */
rsize[1] = osz;
rsize[2] = gsz;
}
@@ -588,207 +592,210 @@ static void do_allBleedBorders(unsigned int t,
unsigned int *rsize)
{
int x;
- unsigned int isz = 0; // inner edge size
- unsigned int osz = 0; // outer edge size
- unsigned int gsz = 0; // gradient fill area size
+ unsigned int isz = 0; /* Inner edge size. */
+ unsigned int osz = 0; /* Outer edge size. */
+ unsigned int gsz = 0; /* Gradient fill area size. */
/* Test the four corners */
- /* upper left corner */
+ /* Upper left corner. */
x = t - rw + 1;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if the inner mask is empty underneath or to the right
+ /* Test if the inner mask is empty underneath or to the right. */
if (!limask[x - rw] || !limask[x + 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x - rw] ||
- !lomask[x + 1]) { // test if outer mask is empty underneath or to the right
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x + 1]) { /* Test if outer mask is empty underneath or to the right. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
- /* upper right corner */
+ /* Upper right corner. */
x = t;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if the inner mask is empty underneath or to the left
+ /* Test if the inner mask is empty underneath or to the left. */
if (!limask[x - rw] || !limask[x - 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- if (!lomask[x - rw] || !lomask[x - 1]) { // test if outer mask is empty above or to the left
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ if (!lomask[x - rw] ||
+ !lomask[x - 1]) { /* Test if outer mask is empty above or to the left. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
- /* lower left corner */
+ /* Lower left corner. */
x = 0;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty above or to the right
+ /* Test if inner mask is empty above or to the right. */
if (!limask[x + rw] || !limask[x + 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x + rw] ||
- !lomask[x + 1]) { // test if outer mask is empty underneath or to the right
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x + 1]) { /* Test if outer mask is empty underneath or to the right. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
- /* lower right corner */
+ /* Lower right corner. */
x = rw - 1;
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty above or to the left
+ /* Test if inner mask is empty above or to the left. */
if (!limask[x + rw] || !limask[x - 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x + rw] ||
- !lomask[x - 1]) { // test if outer mask is empty underneath or to the left
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x - 1]) { /* Test if outer mask is empty underneath or to the left. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
/* Test the TOP row of pixels in buffer, except corners */
for (x = t - 1; x >= (t - rw) + 2; x--) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty to the left or to the right
+ /* Test if inner mask is empty to the left or to the right. */
if (!limask[x - 1] || !limask[x + 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x - 1] ||
- !lomask[x + 1]) { // test if outer mask is empty to the left or to the right
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x + 1]) { /* Test if outer mask is empty to the left or to the right. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
}
/* Test the BOTTOM row of pixels in buffer, except corners */
for (x = rw - 2; x; x--) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty to the left or to the right
+ /* Test if inner mask is empty to the left or to the right. */
if (!limask[x - 1] || !limask[x + 1]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
if (!lomask[x - 1] ||
- !lomask[x + 1]) { // test if outer mask is empty to the left or to the right
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ !lomask[x + 1]) { /* Test if outer mask is empty to the left or to the right. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
}
/* Test the LEFT edge of pixels in buffer, except corners */
for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty underneath or above
+ /* Test if inner mask is empty underneath or above. */
if (!limask[x - rw] || !limask[x + rw]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ if (!lomask[x - rw] ||
+ !lomask[x + rw]) { /* Test if outer mask is empty underneath or above. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
}
/* Test the RIGHT edge of pixels in buffer, except corners */
for (x = t - rw; x > rw; x -= rw) {
- // test if inner mask is filled
+ /* Test if inner mask is filled. */
if (limask[x]) {
- // test if inner mask is empty underneath or above
+ /* Test if inner mask is empty underneath or above. */
if (!limask[x - rw] || !limask[x + rw]) {
- isz++; // increment inner edge size
- lres[x] = 4; // flag pixel as inner edge
+ isz++; /* Increment inner edge size. */
+ lres[x] = 4; /* Flag pixel as inner edge. */
}
else {
- res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
+ res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */
}
}
- else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
- if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above
- osz++; // increment outer edge size
- lres[x] = 3; // flag pixel as outer edge
+ else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */
+ if (!lomask[x - rw] ||
+ !lomask[x + rw]) { /* Test if outer mask is empty underneath or above. */
+ osz++; /* Increment outer edge size. */
+ lres[x] = 3; /* Flag pixel as outer edge. */
}
else {
- gsz++; // increment the gradient pixel count
- lres[x] = 2; // flag pixel as gradient
+ gsz++; /* Increment the gradient pixel count. */
+ lres[x] = 2; /* Flag pixel as gradient. */
}
}
}
- rsize[0] = isz; // fill in our return sizes for edges + fill
+ rsize[0] = isz; /* Fill in our return sizes for edges + fill. */
rsize[1] = osz;
rsize[2] = gsz;
}
@@ -804,13 +811,14 @@ static void do_allEdgeDetection(unsigned int t,
unsigned int in_osz,
unsigned int in_gsz)
{
- int x; // x = pixel loop counter
- int a; // a = pixel loop counter
- int dx; // dx = delta x
- int pix_prevRow; // pix_prevRow = pixel one row behind the one we are testing in a loop
- int pix_nextRow; // pix_nextRow = pixel one row in front of the one we are testing in a loop
- int pix_prevCol; // pix_prevCol = pixel one column behind the one we are testing in a loop
- int pix_nextCol; // pix_nextCol = pixel one column in front of the one we are testing in a loop
+ int x; /* Pixel loop counter. */
+ int a; /* Pixel loop counter. */
+ int dx; /* Delta x. */
+ int pix_prevRow; /* Pixel one row behind the one we are testing in a loop. */
+ int pix_nextRow; /* Pixel one row in front of the one we are testing in a loop. */
+ int pix_prevCol; /* Pixel one column behind the one we are testing in a loop. */
+ int pix_nextCol; /* Pixel one column in front of the one we are testing in a loop. */
+
/* Test all rows between the FIRST and LAST rows, excluding left and right edges */
for (x = (t - rw) + 1, dx = x - (rw - 2); dx > rw; x -= rw, dx -= rw) {
a = x - 2;
@@ -819,8 +827,8 @@ static void do_allEdgeDetection(unsigned int t,
pix_prevCol = a + 1;
pix_nextCol = a - 1;
while (a > dx - 2) {
- if (!limask[a]) { // if the inner mask is empty
- if (lomask[a]) { // if the outer mask is full
+ if (!limask[a]) { /* If the inner mask is empty. */
+ if (lomask[a]) { /* If the outer mask is full. */
/*
* Next we test all 4 directions around the current pixel: next/prev/up/down
* The test ensures that the outer mask is empty and that the inner mask
@@ -831,23 +839,23 @@ static void do_allEdgeDetection(unsigned int t,
(!lomask[pix_prevCol] && !limask[pix_prevCol]) ||
(!lomask[pix_nextRow] && !limask[pix_nextRow]) ||
(!lomask[pix_prevRow] && !limask[pix_prevRow])) {
- in_osz++; // increment the outer boundary pixel count
- lres[a] = 3; // flag pixel as part of outer edge
+ in_osz++; /* Increment the outer boundary pixel count. */
+ lres[a] = 3; /* Flag pixel as part of outer edge. */
}
- else { // it's not a boundary pixel, but it is a gradient pixel
- in_gsz++; // increment the gradient pixel count
- lres[a] = 2; // flag pixel as gradient
+ else { /* It's not a boundary pixel, but it is a gradient pixel. */
+ in_gsz++; /* Increment the gradient pixel count. */
+ lres[a] = 2; /* Flag pixel as gradient. */
}
}
}
else {
if (!limask[pix_nextCol] || !limask[pix_prevCol] || !limask[pix_nextRow] ||
!limask[pix_prevRow]) {
- in_isz++; // increment the inner boundary pixel count
- lres[a] = 4; // flag pixel as part of inner edge
+ in_isz++; /* Increment the inner boundary pixel count. */
+ lres[a] = 4; /* Flag pixel as part of inner edge. */
}
else {
- res[a] = 1.0f; // pixel is part of inner mask, but not at an edge
+ res[a] = 1.0f; /* Pixel is part of inner mask, but not at an edge. */
}
}
a--;
@@ -858,7 +866,7 @@ static void do_allEdgeDetection(unsigned int t,
}
}
- rsize[0] = in_isz; // fill in our return sizes for edges + fill
+ rsize[0] = in_isz; /* Fill in our return sizes for edges + fill. */
rsize[1] = in_osz;
rsize[2] = in_gsz;
}
@@ -874,13 +882,13 @@ static void do_adjacentEdgeDetection(unsigned int t,
unsigned int in_osz,
unsigned int in_gsz)
{
- int x; // x = pixel loop counter
- int a; // a = pixel loop counter
- int dx; // dx = delta x
- int pix_prevRow; // pix_prevRow = pixel one row behind the one we are testing in a loop
- int pix_nextRow; // pix_nextRow = pixel one row in front of the one we are testing in a loop
- int pix_prevCol; // pix_prevCol = pixel one column behind the one we are testing in a loop
- int pix_nextCol; // pix_nextCol = pixel one column in front of the one we are testing in a loop
+ int x; /* Pixel loop counter. */
+ int a; /* Pixel loop counter. */
+ int dx; /* Delta x. */
+ int pix_prevRow; /* Pixel one row behind the one we are testing in a loop. */
+ int pix_nextRow; /* Pixel one row in front of the one we are testing in a loop. */
+ int pix_prevCol; /* Pixel one column behind the one we are testing in a loop. */
+ int pix_nextCol; /* Pixel one column in front of the one we are testing in a loop. */
/* Test all rows between the FIRST and LAST rows, excluding left and right edges */
for (x = (t - rw) + 1, dx = x - (rw - 2); dx > rw; x -= rw, dx -= rw) {
a = x - 2;
@@ -889,8 +897,8 @@ static void do_adjacentEdgeDetection(unsigned int t,
pix_prevCol = a + 1;
pix_nextCol = a - 1;
while (a > dx - 2) {
- if (!limask[a]) { // if the inner mask is empty
- if (lomask[a]) { // if the outer mask is full
+ if (!limask[a]) { /* If the inner mask is empty. */
+ if (lomask[a]) { /* If the outer mask is full. */
/*
* Next we test all 4 directions around the current pixel: next/prev/up/down
* The test ensures that the outer mask is empty and that the inner mask
@@ -901,12 +909,12 @@ static void do_adjacentEdgeDetection(unsigned int t,
(!lomask[pix_prevCol] && !limask[pix_prevCol]) ||
(!lomask[pix_nextRow] && !limask[pix_nextRow]) ||
(!lomask[pix_prevRow] && !limask[pix_prevRow])) {
- in_osz++; // increment the outer boundary pixel count
- lres[a] = 3; // flag pixel as part of outer edge
+ in_osz++; /* Increment the outer boundary pixel count. */
+ lres[a] = 3; /* Flag pixel as part of outer edge. */
}
- else { // it's not a boundary pixel, but it is a gradient pixel
- in_gsz++; // increment the gradient pixel count
- lres[a] = 2; // flag pixel as gradient
+ else { /* It's not a boundary pixel, but it is a gradient pixel. */
+ in_gsz++; /* Increment the gradient pixel count. */
+ lres[a] = 2; /* Flag pixel as gradient. */
}
}
}
@@ -915,22 +923,22 @@ static void do_adjacentEdgeDetection(unsigned int t,
(!limask[pix_prevCol] && lomask[pix_prevCol]) ||
(!limask[pix_nextRow] && lomask[pix_nextRow]) ||
(!limask[pix_prevRow] && lomask[pix_prevRow])) {
- in_isz++; // increment the inner boundary pixel count
- lres[a] = 4; // flag pixel as part of inner edge
+ in_isz++; /* Increment the inner boundary pixel count. */
+ lres[a] = 4; /* Flag pixel as part of inner edge. */
}
else {
- res[a] = 1.0f; // pixel is part of inner mask, but not at an edge
+ res[a] = 1.0f; /* Pixel is part of inner mask, but not at an edge. */
}
}
a--;
- pix_prevRow--; // advance all four "surrounding" pixel pointers
+ pix_prevRow--; /* Advance all four "surrounding" pixel pointers. */
pix_nextRow--;
pix_prevCol--;
pix_nextCol--;
}
}
- rsize[0] = in_isz; // fill in our return sizes for edges + fill
+ rsize[0] = in_isz; /* Fill in our return sizes for edges + fill. */
rsize[1] = in_osz;
rsize[2] = in_gsz;
}
@@ -945,12 +953,12 @@ static void do_createEdgeLocationBuffer(unsigned int t,
unsigned int isz,
unsigned int gsz)
{
- int x; // x = pixel loop counter
- int a; // a = temporary pixel index buffer loop counter
- unsigned int ud; // ud = unscaled edge distance
- unsigned int dmin; // dmin = minimum edge distance
+ int x; /* Pixel loop counter. */
+ int a; /* Temporary pixel index buffer loop counter. */
+ unsigned int ud; /* Unscaled edge distance. */
+ unsigned int dmin; /* Minimum edge distance. */
- unsigned int rsl; // long used for finding fast 1.0/sqrt
+ unsigned int rsl; /* Long used for finding fast `1.0/sqrt`. */
unsigned int gradientFillOffset;
/* For looping inner edge pixel indexes, represents current position from offset. */
@@ -980,11 +988,11 @@ static void do_createEdgeLocationBuffer(unsigned int t,
*
* Example: 9 by 9 pixel block
*
- * . = pixel non-white in both outer and inner mask
- * o = pixel white in outer, but not inner mask, adjacent to "." pixel
- * g = pixel white in outer, but not inner mask, not adjacent to "." pixel
- * i = pixel white in inner mask, adjacent to "g" or "." pixel
- * F = pixel white in inner mask, only adjacent to other pixels white in the inner mask
+ * `.` = Pixel non-white in both outer and inner mask.
+ * `o` = Pixel white in outer, but not inner mask, adjacent to "." pixel.
+ * `g` = Pixel white in outer, but not inner mask, not adjacent to "." pixel.
+ * `i` = Pixel white in inner mask, adjacent to "g" or "." pixel.
+ * `F` = Pixel white in inner mask, only adjacent to other pixels white in the inner mask.
*
*
* ......... <----- pixel #80
@@ -1025,36 +1033,37 @@ static void do_createEdgeLocationBuffer(unsigned int t,
*/
/* clang-format on */
- gradientFillOffset = 0; // since there are likely "more" of these, put it first. :)
- *innerEdgeOffset = gradientFillOffset + gsz; // set start of inner edge indexes
- *outerEdgeOffset = (*innerEdgeOffset) + isz; // set start of outer edge indexes
- /* set the accumulators to correct positions */ // set up some accumulator variables for loops
- gradientAccum = gradientFillOffset; // each accumulator variable starts at its respective
- innerAccum = *innerEdgeOffset; // section's offset so when we start filling, each
- outerAccum = *outerEdgeOffset; // section fills up its allocated space in gbuf
- // uses dmin=row, rsl=col
+ gradientFillOffset = 0; /* Since there are likely "more" of these, put it first. :). */
+ *innerEdgeOffset = gradientFillOffset + gsz; /* Set start of inner edge indexes. */
+ *outerEdgeOffset = (*innerEdgeOffset) + isz; /* Set start of outer edge indexes. */
+ /* Set the accumulators to correct positions */ /* Set up some accumulator variables for loops.
+ */
+ gradientAccum = gradientFillOffset; /* Each accumulator variable starts at its respective. */
+ innerAccum = *innerEdgeOffset; /* Section's offset so when we start filling, each. */
+ outerAccum = *outerEdgeOffset; /* Section fills up its allocated space in gbuf. */
+ /* Uses `dmin=row`, `rsl=col`. */
for (x = 0, dmin = 0; x < t; x += rw, dmin++) {
for (rsl = 0; rsl < rw; rsl++) {
a = x + rsl;
- if (lres[a] == 2) { // it is a gradient pixel flagged by 2
- ud = gradientAccum << 1; // double the index to reach correct unsigned short location
- gbuf[ud] = dmin; // insert pixel's row into gradient pixel location buffer
- gbuf[ud + 1] = rsl; // insert pixel's column into gradient pixel location buffer
- gradientAccum++; // increment gradient index buffer pointer
+ if (lres[a] == 2) { /* It is a gradient pixel flagged by 2. */
+ ud = gradientAccum << 1; /* Double the index to reach correct unsigned short location. */
+ gbuf[ud] = dmin; /* Insert pixel's row into gradient pixel location buffer. */
+ gbuf[ud + 1] = rsl; /* Insert pixel's column into gradient pixel location buffer. */
+ gradientAccum++; /* Increment gradient index buffer pointer. */
}
- else if (lres[a] == 3) { // it is an outer edge pixel flagged by 3
- ud = outerAccum << 1; // double the index to reach correct unsigned short location
- gbuf[ud] = dmin; // insert pixel's row into outer edge pixel location buffer
- gbuf[ud + 1] = rsl; // insert pixel's column into outer edge pixel location buffer
- outerAccum++; // increment outer edge index buffer pointer
- res[a] = 0.0f; // set output pixel intensity now since it won't change later
+ else if (lres[a] == 3) { /* It is an outer edge pixel flagged by 3. */
+ ud = outerAccum << 1; /* Double the index to reach correct unsigned short location. */
+ gbuf[ud] = dmin; /* Insert pixel's row into outer edge pixel location buffer. */
+ gbuf[ud + 1] = rsl; /* Insert pixel's column into outer edge pixel location buffer. */
+ outerAccum++; /* Increment outer edge index buffer pointer. */
+ res[a] = 0.0f; /* Set output pixel intensity now since it won't change later. */
}
- else if (lres[a] == 4) { // it is an inner edge pixel flagged by 4
- ud = innerAccum << 1; // double int index to reach correct unsigned short location
- gbuf[ud] = dmin; // insert pixel's row into inner edge pixel location buffer
- gbuf[ud + 1] = rsl; // insert pixel's column into inner edge pixel location buffer
- innerAccum++; // increment inner edge index buffer pointer
- res[a] = 1.0f; // set output pixel intensity now since it won't change later
+ else if (lres[a] == 4) { /* It is an inner edge pixel flagged by 4. */
+ ud = innerAccum << 1; /* Double int index to reach correct unsigned short location. */
+ gbuf[ud] = dmin; /* Insert pixel's row into inner edge pixel location buffer. */
+ gbuf[ud + 1] = rsl; /* Insert pixel's column into inner edge pixel location buffer. */
+ innerAccum++; /* Increment inner edge index buffer pointer. */
+ res[a] = 1.0f; /* Set output pixel intensity now since it won't change later. */
}
}
}
@@ -1069,21 +1078,21 @@ static void do_fillGradientBuffer(unsigned int rw,
unsigned int innerEdgeOffset,
unsigned int outerEdgeOffset)
{
- int x; // x = pixel loop counter
- int a; // a = temporary pixel index buffer loop counter
- int fsz; // size of the frame
- unsigned int rsl; // long used for finding fast 1.0/sqrt
- float rsf; // float used for finding fast 1.0/sqrt
- const float rsopf = 1.5f; // constant float used for finding fast 1.0/sqrt
+ int x; /* Pixel loop counter. */
+ int a; /* Temporary pixel index buffer loop counter. */
+ int fsz; /* Size of the frame. */
+ unsigned int rsl; /* Long used for finding fast `1.0/sqrt`. */
+ float rsf; /* Float used for finding fast `1.0/sqrt`. */
+ const float rsopf = 1.5f; /* Constant float used for finding fast `1.0/sqrt`. */
unsigned int gradientFillOffset;
unsigned int t;
- unsigned int ud; // ud = unscaled edge distance
- unsigned int dmin; // dmin = minimum edge distance
- float odist; // odist = current outer edge distance
- float idist; // idist = current inner edge distance
- int dx; // dx = X-delta (used for distance proportion calculation)
- int dy; // dy = Y-delta (used for distance proportion calculation)
+ unsigned int ud; /* Unscaled edge distance. */
+ unsigned int dmin; /* Minimum edge distance. */
+ float odist; /* Current outer edge distance. */
+ float idist; /* Current inner edge distance. */
+ int dx; /* X-delta (used for distance proportion calculation) */
+ int dy; /* Y-delta (used for distance proportion calculation) */
/*
* The general algorithm used to color each gradient pixel is:
@@ -1099,9 +1108,9 @@ static void do_fillGradientBuffer(unsigned int rw,
* outside edge.
*
* In an image where:
- * . = blank (black) pixels, not covered by inner mask or outer mask
- * + = desired gradient pixels, covered only by outer mask
- * * = white full mask pixels, covered by at least inner mask
+ * `.` = Blank (black) pixels, not covered by inner mask or outer mask.
+ * `+` = Desired gradient pixels, covered only by outer mask.
+ * `*` = White full mask pixels, covered by at least inner mask.
*
* ...............................
* ...............+++++++++++.....
@@ -1146,92 +1155,95 @@ static void do_fillGradientBuffer(unsigned int rw,
for (x = gsz - 1; x >= 0; x--) {
gradientFillOffset = x << 1;
- t = gbuf[gradientFillOffset]; // calculate column of pixel indexed by gbuf[x]
- fsz = gbuf[gradientFillOffset + 1]; // calculate row of pixel indexed by gbuf[x]
- dmin = 0xffffffff; // reset min distance to edge pixel
+ t = gbuf[gradientFillOffset]; /* Calculate column of pixel indexed by `gbuf[x]`. */
+ fsz = gbuf[gradientFillOffset + 1]; /* Calculate row of pixel indexed by `gbuf[x]`. */
+ dmin = 0xffffffff; /* Reset min distance to edge pixel. */
for (a = outerEdgeOffset + osz - 1; a >= outerEdgeOffset;
- a--) { // loop through all outer edge buffer pixels
+ a--) { /* Loop through all outer edge buffer pixels. */
ud = a << 1;
- dy = t - gbuf[ud]; // set dx to gradient pixel column - outer edge pixel row
- dx = fsz - gbuf[ud + 1]; // set dy to gradient pixel row - outer edge pixel column
- ud = dx * dx + dy * dy; // compute sum of squares
- if (ud < dmin) { // if our new sum of squares is less than the current minimum
- dmin = ud; // set a new minimum equal to the new lower value
+ dy = t - gbuf[ud]; /* Set dx to gradient pixel column - outer edge pixel row. */
+ dx = fsz - gbuf[ud + 1]; /* Set dy to gradient pixel row - outer edge pixel column. */
+ ud = dx * dx + dy * dy; /* Compute sum of squares. */
+ if (ud < dmin) { /* If our new sum of squares is less than the current minimum. */
+ dmin = ud; /* Set a new minimum equal to the new lower value. */
}
}
- odist = (float)(dmin); // cast outer min to a float
- rsf = odist * 0.5f; //
- rsl = *(unsigned int *)&odist; // use some peculiar properties of the way bits are stored
- rsl = 0x5f3759df - (rsl >> 1); // in floats vs. unsigned ints to compute an approximate
- odist = *(float *)&rsl; // reciprocal square root
+ odist = (float)(dmin); /* Cast outer min to a float. */
+ rsf = odist * 0.5f;
+ rsl = *(unsigned int *)&odist; /* Use some peculiar properties of the way bits are stored. */
+ rsl = 0x5f3759df - (rsl >> 1); /* In floats vs. unsigned ints to compute an approximate. */
+ odist = *(float *)&rsl; /* Reciprocal square root. */
odist = odist * (rsopf - (rsf * odist *
- odist)); // -- ** this line can be iterated for more accuracy ** --
- dmin = 0xffffffff; // reset min distance to edge pixel
+ odist)); /* -- This line can be iterated for more accuracy. -- */
+ dmin = 0xffffffff; /* Reset min distance to edge pixel. */
for (a = innerEdgeOffset + isz - 1; a >= innerEdgeOffset;
- a--) { // loop through all inside edge pixels
+ a--) { /* Loop through all inside edge pixels. */
ud = a << 1;
- dy = t - gbuf[ud]; // compute delta in Y from gradient pixel to inside edge pixel
- dx = fsz - gbuf[ud + 1]; // compute delta in X from gradient pixel to inside edge pixel
- ud = dx * dx + dy * dy; // compute sum of squares
- if (ud < dmin) { // if our new sum of squares is less than the current minimum we've found
- dmin = ud; // set a new minimum equal to the new lower value
+ dy = t - gbuf[ud]; /* Compute delta in Y from gradient pixel to inside edge pixel. */
+ dx = fsz - gbuf[ud + 1]; /* Compute delta in X from gradient pixel to inside edge pixel. */
+ ud = dx * dx + dy * dy; /* Compute sum of squares. */
+ if (ud <
+ dmin) { /* If our new sum of squares is less than the current minimum we've found. */
+ dmin = ud; /* Set a new minimum equal to the new lower value. */
}
}
- idist = (float)(dmin); // cast inner min to a float
- rsf = idist * 0.5f; //
- rsl = *(unsigned int *)&idist; //
- rsl = 0x5f3759df - (rsl >> 1); // see notes above
- idist = *(float *)&rsl; //
- idist = idist * (rsopf - (rsf * idist * idist)); //
- /*
- * Note once again that since we are using reciprocals of distance values our
+
+ /* Cast inner min to a float. */
+ idist = (float)(dmin);
+ rsf = idist * 0.5f;
+ rsl = *(unsigned int *)&idist;
+
+ /* See notes above. */
+ rsl = 0x5f3759df - (rsl >> 1);
+ idist = *(float *)&rsl;
+ idist = idist * (rsopf - (rsf * idist * idist));
+
+ /* NOTE: once again that since we are using reciprocals of distance values our
* proportion is already the correct intensity, and does not need to be
- * subtracted from 1.0 like it would have if we used real distances.
- */
+ * subtracted from 1.0 like it would have if we used real distances. */
- /*
- * Here we reconstruct the pixel's memory location in the CompBuf by
- * Pixel Index = Pixel Column + ( Pixel Row * Row Width )
- */
+ /* Here we reconstruct the pixel's memory location in the CompBuf by
+ * `Pixel Index = Pixel Column + ( Pixel Row * Row Width )`. */
res[gbuf[gradientFillOffset + 1] + (gbuf[gradientFillOffset] * rw)] =
- (idist / (idist + odist)); // set intensity
+ (idist / (idist + odist)); /* Set intensity. */
}
}
-// end of copy
+/* End of copy. */
void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float *res)
{
- unsigned int *lres; // lres = unsigned int pointer to output pixel buffer (for bit operations)
- unsigned int *limask; // limask = unsigned int pointer to inner mask (for bit operations)
- unsigned int *lomask; // lomask = unsigned int pointer to outer mask (for bit operations)
-
- int rw; // rw = pixel row width
- int t; // t = total number of pixels in buffer - 1 (used for loop starts)
- int fsz; // size of the frame
-
- unsigned int isz = 0; // size (in pixels) of inside edge pixel index buffer
- unsigned int osz = 0; // size (in pixels) of outside edge pixel index buffer
- unsigned int gsz = 0; // size (in pixels) of gradient pixel index buffer
- unsigned int rsize[3]; // size storage to pass to helper functions
+ unsigned int *lres; /* Pointer to output pixel buffer (for bit operations). */
+ unsigned int *limask; /* Pointer to inner mask (for bit operations). */
+ unsigned int *lomask; /* Pointer to outer mask (for bit operations). */
+
+ int rw; /* Pixel row width. */
+ int t; /* Total number of pixels in buffer - 1 (used for loop starts). */
+ int fsz; /* Size of the frame. */
+
+ unsigned int isz = 0; /* Size (in pixels) of inside edge pixel index buffer. */
+ unsigned int osz = 0; /* Size (in pixels) of outside edge pixel index buffer. */
+ unsigned int gsz = 0; /* Size (in pixels) of gradient pixel index buffer. */
+ unsigned int rsize[3]; /* Size storage to pass to helper functions. */
unsigned int innerEdgeOffset =
- 0; // offset into final buffer where inner edge pixel indexes start
+ 0; /* Offset into final buffer where inner edge pixel indexes start. */
unsigned int outerEdgeOffset =
- 0; // offset into final buffer where outer edge pixel indexes start
+ 0; /* Offset into final buffer where outer edge pixel indexes start. */
- unsigned short *gbuf; // gradient/inner/outer pixel location index buffer
+ unsigned short *gbuf; /* Gradient/inner/outer pixel location index buffer. */
- if (true) { // if both input sockets have some data coming in...
+ if (true) { /* If both input sockets have some data coming in... */
- rw = this->getWidth(); // width of a row of pixels
- t = (rw * this->getHeight()) - 1; // determine size of the frame
+ rw = this->getWidth(); /* Width of a row of pixels. */
+ t = (rw * this->getHeight()) - 1; /* Determine size of the frame. */
memset(res,
0,
- sizeof(float) * (t + 1)); // clear output buffer (not all pixels will be written later)
+ sizeof(float) *
+ (t + 1)); /* Clear output buffer (not all pixels will be written later). */
- lres = (unsigned int *)res; // unsigned int pointer to output buffer (for bit level ops)
- limask = (unsigned int *)imask; // unsigned int pointer to input mask (for bit level ops)
- lomask = (unsigned int *)omask; // unsigned int pointer to output mask (for bit level ops)
+ lres = (unsigned int *)res; /* Pointer to output buffer (for bit level ops).. */
+ limask = (unsigned int *)imask; /* Pointer to input mask (for bit level ops).. */
+ lomask = (unsigned int *)omask; /* Pointer to output mask (for bit level ops).. */
/*
* The whole buffer is broken up into 4 parts. The four CORNERS, the FIRST and LAST rows, the
@@ -1258,52 +1270,52 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float
*
* Each version has slightly different criteria for detecting an edge pixel.
*/
- if (this->m_adjacentOnly) { // if "adjacent only" inner edge mode is turned on
- if (this->m_keepInside) { // if "keep inside" buffer edge mode is turned on
+ if (this->m_adjacentOnly) { /* If "adjacent only" inner edge mode is turned on. */
+ if (this->m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */
do_adjacentKeepBorders(t, rw, limask, lomask, lres, res, rsize);
}
- else { // "bleed out" buffer edge mode is turned on
+ else { /* "bleed out" buffer edge mode is turned on. */
do_adjacentBleedBorders(t, rw, limask, lomask, lres, res, rsize);
}
- // set up inner edge, outer edge, and gradient buffer sizes after border pass
+ /* Set up inner edge, outer edge, and gradient buffer sizes after border pass. */
isz = rsize[0];
osz = rsize[1];
gsz = rsize[2];
- // detect edges in all non-border pixels in the buffer
+ /* Detect edges in all non-border pixels in the buffer. */
do_adjacentEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz);
}
- else { // "all" inner edge mode is turned on
- if (this->m_keepInside) { // if "keep inside" buffer edge mode is turned on
+ else { /* "all" inner edge mode is turned on. */
+ if (this->m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */
do_allKeepBorders(t, rw, limask, lomask, lres, res, rsize);
}
- else { // "bleed out" buffer edge mode is turned on
+ else { /* "bleed out" buffer edge mode is turned on. */
do_allBleedBorders(t, rw, limask, lomask, lres, res, rsize);
}
- // set up inner edge, outer edge, and gradient buffer sizes after border pass
+ /* Set up inner edge, outer edge, and gradient buffer sizes after border pass. */
isz = rsize[0];
osz = rsize[1];
gsz = rsize[2];
- // detect edges in all non-border pixels in the buffer
+ /* Detect edges in all non-border pixels in the buffer. */
do_allEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz);
}
- // set edge and gradient buffer sizes once again...
- // the sizes in rsize[] may have been modified
- // by the do_*EdgeDetection() function.
+ /* Set edge and gradient buffer sizes once again...
+ * the sizes in rsize[] may have been modified
+ * by the `do_*EdgeDetection()` function. */
isz = rsize[0];
osz = rsize[1];
gsz = rsize[2];
- // calculate size of pixel index buffer needed
+ /* Calculate size of pixel index buffer needed. */
fsz = gsz + isz + osz;
- // allocate edge/gradient pixel index buffer
+ /* Allocate edge/gradient pixel index buffer. */
gbuf = (unsigned short *)MEM_callocN(sizeof(unsigned short) * fsz * 2, "DEM");
do_createEdgeLocationBuffer(
t, rw, lres, res, gbuf, &innerEdgeOffset, &outerEdgeOffset, isz, gsz);
do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, innerEdgeOffset, outerEdgeOffset);
- // free the gradient index buffer
+ /* Free the gradient index buffer. */
MEM_freeN(gbuf);
}
}
@@ -1318,6 +1330,7 @@ DoubleEdgeMaskOperation::DoubleEdgeMaskOperation()
this->m_adjacentOnly = false;
this->m_keepInside = false;
this->flags.complex = true;
+ is_output_rendered_ = false;
}
bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/,
@@ -1382,4 +1395,43 @@ void DoubleEdgeMaskOperation::deinitExecution()
}
}
+void DoubleEdgeMaskOperation::get_area_of_interest(int UNUSED(input_idx),
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ r_input_area.xmax = this->getWidth();
+ r_input_area.xmin = 0;
+ r_input_area.ymax = this->getHeight();
+ r_input_area.ymin = 0;
+}
+
+void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output,
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ if (!is_output_rendered_) {
+ /* Ensure full buffers to work with no strides. */
+ MemoryBuffer *input_inner_mask = inputs[0];
+ MemoryBuffer *inner_mask = input_inner_mask->is_a_single_elem() ? input_inner_mask->inflate() :
+ input_inner_mask;
+ MemoryBuffer *input_outer_mask = inputs[1];
+ MemoryBuffer *outer_mask = input_outer_mask->is_a_single_elem() ? input_outer_mask->inflate() :
+ input_outer_mask;
+
+ BLI_assert(output->getWidth() == this->getWidth());
+ BLI_assert(output->getHeight() == this->getHeight());
+ /* TODO(manzanilla): Once tiled implementation is removed, use execution system to run
+ * multi-threaded where possible. */
+ doDoubleEdgeMask(inner_mask->getBuffer(), outer_mask->getBuffer(), output->getBuffer());
+ is_output_rendered_ = true;
+
+ if (inner_mask != input_inner_mask) {
+ delete inner_mask;
+ }
+ if (outer_mask != input_outer_mask) {
+ delete outer_mask;
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
index e956e8edc3e..45a80bbbbf0 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -31,8 +31,12 @@ class DoubleEdgeMaskOperation : public NodeOperation {
SocketReader *m_inputInnerMask;
bool m_adjacentOnly;
bool m_keepInside;
+
+ /* TODO(manzanilla): To be removed with tiled implementation. */
float *m_cachedInstance;
+ bool is_output_rendered_;
+
public:
DoubleEdgeMaskOperation();
@@ -66,6 +70,12 @@ class DoubleEdgeMaskOperation : public NodeOperation {
{
this->m_keepInside = keepInside;
}
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+
+ void update_memory_buffer(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
index 5a4503fecec..eb1fd98a590 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "DNA_node_types.h"
+#include <functional>
+
namespace blender::compositor {
EllipseMaskOperation::EllipseMaskOperation()
@@ -114,6 +116,77 @@ void EllipseMaskOperation::executePixelSampled(float output[4],
}
}
+void EllipseMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ MaskFunc mask_func;
+ switch (m_maskType) {
+ case CMP_NODE_MASKTYPE_ADD:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? MAX2(mask[0], value[0]) : mask[0];
+ };
+ break;
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? CLAMPIS(mask[0] - value[0], 0, 1) : mask[0];
+ };
+ break;
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? mask[0] * value[0] : 0;
+ };
+ break;
+ case CMP_NODE_MASKTYPE_NOT:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ if (is_inside) {
+ return mask[0] > 0.0f ? 0.0f : value[0];
+ }
+ return mask[0];
+ };
+ break;
+ }
+ apply_mask(output, area, inputs, mask_func);
+}
+
+void EllipseMaskOperation::apply_mask(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs,
+ MaskFunc mask_func)
+{
+ const MemoryBuffer *input_mask = inputs[0];
+ const MemoryBuffer *input_value = inputs[1];
+ const float op_w = this->getWidth();
+ const float op_h = this->getHeight();
+ const float half_w = this->m_data->width / 2.0f;
+ const float half_h = this->m_data->height / 2.0f;
+ const float tx = half_w * half_w;
+ const float ty = half_h * half_h;
+ for (const int y : YRange(area)) {
+ const float op_ry = y / op_h;
+ const float dy = (op_ry - this->m_data->y) / m_aspectRatio;
+ float *out = output->get_elem(area.xmin, y);
+ const float *mask = input_mask->get_elem(area.xmin, y);
+ const float *value = input_value->get_elem(area.xmin, y);
+ for (const int x : XRange(area)) {
+ const float op_rx = x / op_w;
+ const float dx = op_rx - this->m_data->x;
+ const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy);
+ const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy);
+ float sx = rx - this->m_data->x;
+ sx *= sx;
+ float sy = ry - this->m_data->y;
+ sy *= sy;
+ const bool inside = ((sx / tx) + (sy / ty)) < 1.0f;
+ out[0] = mask_func(inside, mask, value);
+
+ mask += input_mask->elem_stride;
+ value += input_value->elem_stride;
+ out += output->elem_stride;
+ }
+ }
+}
+
void EllipseMaskOperation::deinitExecution()
{
this->m_inputMask = nullptr;
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h
index 64afe0145cf..fba3f979d26 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h
@@ -18,12 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class EllipseMaskOperation : public NodeOperation {
+class EllipseMaskOperation : public MultiThreadedOperation {
private:
+ using MaskFunc = std::function<float(bool is_inside, const float *mask, const float *value)>;
+
/**
* Cached reference to the inputProgram
*/
@@ -64,6 +66,16 @@ class EllipseMaskOperation : public NodeOperation {
{
this->m_maskType = maskType;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ void apply_mask(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs,
+ MaskFunc mask_func);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
index 2be6e4d1be7..3804e6ec646 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
@@ -126,7 +126,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
float *buffer = src->getBuffer();
const uint8_t num_channels = src->get_num_channels();
- // <0.5 not valid, though can have a possibly useful sort of sharpening effect
+ /* <0.5 not valid, though can have a possibly useful sort of sharpening effect. */
if (sigma < 0.5f) {
return;
}
@@ -135,8 +135,8 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
xy = 3;
}
- // XXX The YVV macro defined below explicitly expects sources of at least 3x3 pixels,
- // so just skipping blur along faulty direction if src's def is below that limit!
+ /* XXX The YVV macro defined below explicitly expects sources of at least 3x3 pixels,
+ * so just skipping blur along faulty direction if src's def is below that limit! */
if (src_width < 3) {
xy &= ~1;
}
@@ -147,32 +147,32 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
return;
}
- // see "Recursive Gabor Filtering" by Young/VanVliet
- // all factors here in double.prec.
- // Required, because for single.prec it seems to blow up if sigma > ~200
+ /* See "Recursive Gabor Filtering" by Young/VanVliet
+ * all factors here in double-precision.
+ * Required, because for single-precision floating point seems to blow up if `sigma > ~200`. */
if (sigma >= 3.556f) {
q = 0.9804f * (sigma - 3.556f) + 2.5091f;
}
- else { // sigma >= 0.5
+ else { /* `sigma >= 0.5`. */
q = (0.0561f * sigma + 0.5784f) * sigma - 0.2568f;
}
q2 = q * q;
sc = (1.1668 + q) * (3.203729649 + (2.21566 + q) * q);
- // no gabor filtering here, so no complex multiplies, just the regular coefs.
- // all negated here, so as not to have to recalc Triggs/Sdika matrix
+ /* No gabor filtering here, so no complex multiplies, just the regular coefficients.
+ * all negated here, so as not to have to recalc Triggs/Sdika matrix. */
cf[1] = q * (5.788961737 + (6.76492 + 3.0 * q) * q) / sc;
cf[2] = -q2 * (3.38246 + 3.0 * q) / sc;
- // 0 & 3 unchanged
+ /* 0 & 3 unchanged. */
cf[3] = q2 * q / sc;
cf[0] = 1.0 - cf[1] - cf[2] - cf[3];
- // Triggs/Sdika border corrections,
- // it seems to work, not entirely sure if it is actually totally correct,
- // Besides J.M.Geusebroek's anigauss.c (see http://www.science.uva.nl/~mark),
- // found one other implementation by Cristoph Lampert,
- // but neither seem to be quite the same, result seems to be ok so far anyway.
- // Extra scale factor here to not have to do it in filter,
- // though maybe this had something to with the precision errors
+ /* Triggs/Sdika border corrections,
+ * it seems to work, not entirely sure if it is actually totally correct,
+ * Besides J.M.Geusebroek's `anigauss.c` (see http://www.science.uva.nl/~mark),
+ * found one other implementation by Cristoph Lampert,
+ * but neither seem to be quite the same, result seems to be ok so far anyway.
+ * Extra scale factor here to not have to do it in filter,
+ * though maybe this had something to with the precision errors */
sc = cf[0] / ((1.0 + cf[1] - cf[2] + cf[3]) * (1.0 - cf[1] - cf[2] - cf[3]) *
(1.0 + cf[2] + (cf[1] - cf[3]) * cf[3]));
tsM[0] = sc * (-cf[3] * cf[1] + 1.0 - cf[3] * cf[3] - cf[2]);
@@ -210,12 +210,12 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
} \
(void)0
- // intermediate buffers
+ /* Intermediate buffers. */
sz = MAX2(src_width, src_height);
X = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss X buf");
Y = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss Y buf");
W = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss W buf");
- if (xy & 1) { // H
+ if (xy & 1) { /* H. */
int offset;
for (y = 0; y < src_height; y++) {
const int yx = y * src_width;
@@ -232,7 +232,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
}
}
}
- if (xy & 2) { // V
+ if (xy & 2) { /* V. */
int offset;
const int add = src_width * num_channels;
@@ -257,7 +257,6 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
#undef YVV
}
-///
FastGaussianBlurValueOperation::FastGaussianBlurValueOperation()
{
this->addInputSocket(DataType::Value);
@@ -336,8 +335,6 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
}
}
- // newBuf->
-
this->m_iirgaus = copy;
}
unlockMutex();
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h
index 69555524274..08794c8db22 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.h
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.h
@@ -70,304 +70,184 @@ class MathBaseOperation : public NodeOperation {
class MathAddOperation : public MathBaseOperation {
public:
- MathAddOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathSubtractOperation : public MathBaseOperation {
public:
- MathSubtractOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathMultiplyOperation : public MathBaseOperation {
public:
- MathMultiplyOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathDivideOperation : public MathBaseOperation {
public:
- MathDivideOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathSineOperation : public MathBaseOperation {
public:
- MathSineOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathCosineOperation : public MathBaseOperation {
public:
- MathCosineOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathTangentOperation : public MathBaseOperation {
public:
- MathTangentOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathHyperbolicSineOperation : public MathBaseOperation {
public:
- MathHyperbolicSineOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathHyperbolicCosineOperation : public MathBaseOperation {
public:
- MathHyperbolicCosineOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathHyperbolicTangentOperation : public MathBaseOperation {
public:
- MathHyperbolicTangentOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathArcSineOperation : public MathBaseOperation {
public:
- MathArcSineOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathArcCosineOperation : public MathBaseOperation {
public:
- MathArcCosineOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathArcTangentOperation : public MathBaseOperation {
public:
- MathArcTangentOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathPowerOperation : public MathBaseOperation {
public:
- MathPowerOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathLogarithmOperation : public MathBaseOperation {
public:
- MathLogarithmOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathMinimumOperation : public MathBaseOperation {
public:
- MathMinimumOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathMaximumOperation : public MathBaseOperation {
public:
- MathMaximumOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathRoundOperation : public MathBaseOperation {
public:
- MathRoundOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathLessThanOperation : public MathBaseOperation {
public:
- MathLessThanOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathGreaterThanOperation : public MathBaseOperation {
public:
- MathGreaterThanOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathModuloOperation : public MathBaseOperation {
public:
- MathModuloOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathAbsoluteOperation : public MathBaseOperation {
public:
- MathAbsoluteOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathRadiansOperation : public MathBaseOperation {
public:
- MathRadiansOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathDegreesOperation : public MathBaseOperation {
public:
- MathDegreesOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathArcTan2Operation : public MathBaseOperation {
public:
- MathArcTan2Operation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathFloorOperation : public MathBaseOperation {
public:
- MathFloorOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathCeilOperation : public MathBaseOperation {
public:
- MathCeilOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathFractOperation : public MathBaseOperation {
public:
- MathFractOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathSqrtOperation : public MathBaseOperation {
public:
- MathSqrtOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathInverseSqrtOperation : public MathBaseOperation {
public:
- MathInverseSqrtOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathSignOperation : public MathBaseOperation {
public:
- MathSignOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathExponentOperation : public MathBaseOperation {
public:
- MathExponentOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathTruncOperation : public MathBaseOperation {
public:
- MathTruncOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathSnapOperation : public MathBaseOperation {
public:
- MathSnapOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathWrapOperation : public MathBaseOperation {
public:
- MathWrapOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathPingpongOperation : public MathBaseOperation {
public:
- MathPingpongOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathCompareOperation : public MathBaseOperation {
public:
- MathCompareOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathMultiplyAddOperation : public MathBaseOperation {
public:
- MathMultiplyAddOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathSmoothMinOperation : public MathBaseOperation {
public:
- MathSmoothMinOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathSmoothMaxOperation : public MathBaseOperation {
public:
- MathSmoothMaxOperation() : MathBaseOperation()
- {
- }
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc
index 58fa09fa2a8..77ecbf60356 100644
--- a/source/blender/compositor/operations/COM_MixOperation.cc
+++ b/source/blender/compositor/operations/COM_MixOperation.cc
@@ -35,6 +35,7 @@ MixBaseOperation::MixBaseOperation()
this->m_inputColor2Operation = nullptr;
this->setUseValueAlphaMultiply(false);
this->setUseClamp(false);
+ flags.can_be_constant = true;
}
void MixBaseOperation::initExecution()
@@ -97,6 +98,45 @@ void MixBaseOperation::deinitExecution()
this->m_inputColor2Operation = nullptr;
}
+void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_value = inputs[0];
+ const MemoryBuffer *input_color1 = inputs[1];
+ const MemoryBuffer *input_color2 = inputs[2];
+ const int width = BLI_rcti_size_x(&area);
+ PixelCursor p;
+ p.out_stride = output->elem_stride;
+ p.value_stride = input_value->elem_stride;
+ p.color1_stride = input_color1->elem_stride;
+ p.color2_stride = input_color2->elem_stride;
+ for (const int y : YRange(area)) {
+ p.out = output->get_elem(area.xmin, y);
+ p.row_end = p.out + width * output->elem_stride;
+ p.value = input_value->get_elem(area.xmin, y);
+ p.color1 = input_color1->get_elem(area.xmin, y);
+ p.color2 = input_color2->get_elem(area.xmin, y);
+ update_memory_buffer_row(p);
+ }
+}
+
+void MixBaseOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ p.out[0] = value_m * p.color1[0] + value * p.color2[0];
+ p.out[1] = value_m * p.color1[1] + value * p.color2[1];
+ p.out[2] = value_m * p.color1[2] + value * p.color2[2];
+ p.out[3] = p.color1[3];
+ p.next();
+ }
+}
+
/* ******** Mix Add Operation ******** */
void MixAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
@@ -121,6 +161,23 @@ void MixAddOperation::executePixelSampled(float output[4], float x, float y, Pix
clampIfNeeded(output);
}
+void MixAddOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ p.out[0] = p.color1[0] + value * p.color2[0];
+ p.out[1] = p.color1[1] + value * p.color2[1];
+ p.out[2] = p.color1[2] + value * p.color2[2];
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Blend Operation ******** */
void MixBlendOperation::executePixelSampled(float output[4],
@@ -150,6 +207,24 @@ void MixBlendOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixBlendOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ float value_m = 1.0f - value;
+ p.out[0] = value_m * p.color1[0] + value * p.color2[0];
+ p.out[1] = value_m * p.color1[1] + value * p.color2[1];
+ p.out[2] = value_m * p.color1[2] + value * p.color2[2];
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Burn Operation ******** */
void MixColorBurnOperation::executePixelSampled(float output[4],
@@ -228,6 +303,48 @@ void MixColorBurnOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixColorBurnOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ float tmp = value_m + value * p.color2[0];
+ if (tmp <= 0.0f) {
+ p.out[0] = 0.0f;
+ }
+ else {
+ tmp = 1.0f - (1.0f - p.color1[0]) / tmp;
+ p.out[0] = CLAMPIS(tmp, 0.0f, 1.0f);
+ }
+
+ tmp = value_m + value * p.color2[1];
+ if (tmp <= 0.0f) {
+ p.out[1] = 0.0f;
+ }
+ else {
+ tmp = 1.0f - (1.0f - p.color1[1]) / tmp;
+ p.out[1] = CLAMPIS(tmp, 0.0f, 1.0f);
+ }
+
+ tmp = value_m + value * p.color2[2];
+ if (tmp <= 0.0f) {
+ p.out[2] = 0.0f;
+ }
+ else {
+ tmp = 1.0f - (1.0f - p.color1[2]) / tmp;
+ p.out[2] = CLAMPIS(tmp, 0.0f, 1.0f);
+ }
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Color Operation ******** */
void MixColorOperation::executePixelSampled(float output[4],
@@ -268,6 +385,36 @@ void MixColorOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixColorOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ float colH, colS, colV;
+ rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
+ if (colS != 0.0f) {
+ float rH, rS, rV;
+ float tmpr, tmpg, tmpb;
+ rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
+ hsv_to_rgb(colH, colS, rV, &tmpr, &tmpg, &tmpb);
+ p.out[0] = (value_m * p.color1[0]) + (value * tmpr);
+ p.out[1] = (value_m * p.color1[1]) + (value * tmpg);
+ p.out[2] = (value_m * p.color1[2]) + (value * tmpb);
+ }
+ else {
+ copy_v3_v3(p.out, p.color1);
+ }
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Darken Operation ******** */
void MixDarkenOperation::executePixelSampled(float output[4],
@@ -296,6 +443,24 @@ void MixDarkenOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixDarkenOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ float value_m = 1.0f - value;
+ p.out[0] = min_ff(p.color1[0], p.color2[0]) * value + p.color1[0] * value_m;
+ p.out[1] = min_ff(p.color1[1], p.color2[1]) * value + p.color1[1] * value_m;
+ p.out[2] = min_ff(p.color1[2], p.color2[2]) * value + p.color1[2] * value_m;
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Difference Operation ******** */
void MixDifferenceOperation::executePixelSampled(float output[4],
@@ -324,6 +489,24 @@ void MixDifferenceOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixDifferenceOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ p.out[0] = value_m * p.color1[0] + value * fabsf(p.color1[0] - p.color2[0]);
+ p.out[1] = value_m * p.color1[1] + value * fabsf(p.color1[1] - p.color2[1]);
+ p.out[2] = value_m * p.color1[2] + value * fabsf(p.color1[2] - p.color2[2]);
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Difference Operation ******** */
void MixDivideOperation::executePixelSampled(float output[4],
@@ -369,6 +552,41 @@ void MixDivideOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixDivideOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ if (p.color2[0] != 0.0f) {
+ p.out[0] = value_m * (p.color1[0]) + value * (p.color1[0]) / p.color2[0];
+ }
+ else {
+ p.out[0] = 0.0f;
+ }
+ if (p.color2[1] != 0.0f) {
+ p.out[1] = value_m * (p.color1[1]) + value * (p.color1[1]) / p.color2[1];
+ }
+ else {
+ p.out[1] = 0.0f;
+ }
+ if (p.color2[2] != 0.0f) {
+ p.out[2] = value_m * (p.color1[2]) + value * (p.color1[2]) / p.color2[2];
+ }
+ else {
+ p.out[2] = 0.0f;
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Dodge Operation ******** */
void MixDodgeOperation::executePixelSampled(float output[4],
@@ -452,6 +670,64 @@ void MixDodgeOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixDodgeOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+
+ float tmp;
+ if (p.color1[0] != 0.0f) {
+ tmp = 1.0f - value * p.color2[0];
+ if (tmp <= 0.0f) {
+ p.out[0] = 1.0f;
+ }
+ else {
+ p.out[0] = p.color1[0] / tmp;
+ CLAMP_MAX(p.out[0], 1.0f);
+ }
+ }
+ else {
+ p.out[0] = 0.0f;
+ }
+
+ if (p.color1[1] != 0.0f) {
+ tmp = 1.0f - value * p.color2[1];
+ if (tmp <= 0.0f) {
+ p.out[1] = 1.0f;
+ }
+ else {
+ p.out[1] = p.color1[1] / tmp;
+ CLAMP_MAX(p.out[1], 1.0f);
+ }
+ }
+ else {
+ p.out[1] = 0.0f;
+ }
+
+ if (p.color1[2] != 0.0f) {
+ tmp = 1.0f - value * p.color2[2];
+ if (tmp <= 0.0f) {
+ p.out[2] = 1.0f;
+ }
+ else {
+ p.out[2] = p.color1[2] / tmp;
+ CLAMP_MAX(p.out[2], 1.0f);
+ }
+ }
+ else {
+ p.out[2] = 0.0f;
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Glare Operation ******** */
void MixGlareOperation::executePixelSampled(float output[4],
@@ -487,6 +763,33 @@ void MixGlareOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixGlareOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ const float value = p.value[0];
+ /* Linear interpolation between 3 cases:
+ * value=-1:output=input value=0:output=input+glare value=1:output=glare
+ */
+ float input_weight;
+ float glare_weight;
+ if (value < 0.0f) {
+ input_weight = 1.0f;
+ glare_weight = 1.0f + value;
+ }
+ else {
+ input_weight = 1.0f - value;
+ glare_weight = 1.0f;
+ }
+ p.out[0] = input_weight * MAX2(p.color1[0], 0.0f) + glare_weight * p.color2[0];
+ p.out[1] = input_weight * MAX2(p.color1[1], 0.0f) + glare_weight * p.color2[1];
+ p.out[2] = input_weight * MAX2(p.color1[2], 0.0f) + glare_weight * p.color2[2];
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Hue Operation ******** */
void MixHueOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
@@ -524,6 +827,36 @@ void MixHueOperation::executePixelSampled(float output[4], float x, float y, Pix
clampIfNeeded(output);
}
+void MixHueOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ float colH, colS, colV;
+ rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
+ if (colS != 0.0f) {
+ float rH, rS, rV;
+ float tmpr, tmpg, tmpb;
+ rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
+ hsv_to_rgb(colH, rS, rV, &tmpr, &tmpg, &tmpb);
+ p.out[0] = value_m * p.color1[0] + value * tmpr;
+ p.out[1] = value_m * p.color1[1] + value * tmpg;
+ p.out[2] = value_m * p.color1[2] + value * tmpb;
+ }
+ else {
+ copy_v3_v3(p.out, p.color1);
+ }
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Lighten Operation ******** */
void MixLightenOperation::executePixelSampled(float output[4],
@@ -570,6 +903,30 @@ void MixLightenOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixLightenOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+
+ float tmp = value * p.color2[0];
+ p.out[0] = MAX2(tmp, p.color1[0]);
+
+ tmp = value * p.color2[1];
+ p.out[1] = MAX2(tmp, p.color1[1]);
+
+ tmp = value * p.color2[2];
+ p.out[2] = MAX2(tmp, p.color1[2]);
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Linear Light Operation ******** */
void MixLinearLightOperation::executePixelSampled(float output[4],
@@ -613,6 +970,39 @@ void MixLinearLightOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixLinearLightOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ if (p.color2[0] > 0.5f) {
+ p.out[0] = p.color1[0] + value * (2.0f * (p.color2[0] - 0.5f));
+ }
+ else {
+ p.out[0] = p.color1[0] + value * (2.0f * (p.color2[0]) - 1.0f);
+ }
+ if (p.color2[1] > 0.5f) {
+ p.out[1] = p.color1[1] + value * (2.0f * (p.color2[1] - 0.5f));
+ }
+ else {
+ p.out[1] = p.color1[1] + value * (2.0f * (p.color2[1]) - 1.0f);
+ }
+ if (p.color2[2] > 0.5f) {
+ p.out[2] = p.color1[2] + value * (2.0f * (p.color2[2] - 0.5f));
+ }
+ else {
+ p.out[2] = p.color1[2] + value * (2.0f * (p.color2[2]) - 1.0f);
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Multiply Operation ******** */
void MixMultiplyOperation::executePixelSampled(float output[4],
@@ -641,6 +1031,25 @@ void MixMultiplyOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixMultiplyOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ p.out[0] = p.color1[0] * (value_m + value * p.color2[0]);
+ p.out[1] = p.color1[1] * (value_m + value * p.color2[1]);
+ p.out[2] = p.color1[2] * (value_m + value * p.color2[2]);
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Overlay Operation ******** */
void MixOverlayOperation::executePixelSampled(float output[4],
@@ -686,6 +1095,40 @@ void MixOverlayOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixOverlayOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ if (p.color1[0] < 0.5f) {
+ p.out[0] = p.color1[0] * (value_m + 2.0f * value * p.color2[0]);
+ }
+ else {
+ p.out[0] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[0])) * (1.0f - p.color1[0]);
+ }
+ if (p.color1[1] < 0.5f) {
+ p.out[1] = p.color1[1] * (value_m + 2.0f * value * p.color2[1]);
+ }
+ else {
+ p.out[1] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[1])) * (1.0f - p.color1[1]);
+ }
+ if (p.color1[2] < 0.5f) {
+ p.out[2] = p.color1[2] * (value_m + 2.0f * value * p.color2[2]);
+ }
+ else {
+ p.out[2] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]);
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Saturation Operation ******** */
void MixSaturationOperation::executePixelSampled(float output[4],
@@ -723,6 +1166,33 @@ void MixSaturationOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixSaturationOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ float rH, rS, rV;
+ rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
+ if (rS != 0.0f) {
+ float colH, colS, colV;
+ rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
+ hsv_to_rgb(rH, (value_m * rS + value * colS), rV, &p.out[0], &p.out[1], &p.out[2]);
+ }
+ else {
+ copy_v3_v3(p.out, p.color1);
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Screen Operation ******** */
void MixScreenOperation::executePixelSampled(float output[4],
@@ -752,6 +1222,25 @@ void MixScreenOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixScreenOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ p.out[0] = 1.0f - (value_m + value * (1.0f - p.color2[0])) * (1.0f - p.color1[0]);
+ p.out[1] = 1.0f - (value_m + value * (1.0f - p.color2[1])) * (1.0f - p.color1[1]);
+ p.out[2] = 1.0f - (value_m + value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]);
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Soft Light Operation ******** */
void MixSoftLightOperation::executePixelSampled(float output[4],
@@ -793,6 +1282,34 @@ void MixSoftLightOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixSoftLightOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ float scr, scg, scb;
+
+ /* First calculate non-fac based Screen mix. */
+ scr = 1.0f - (1.0f - p.color2[0]) * (1.0f - p.color1[0]);
+ scg = 1.0f - (1.0f - p.color2[1]) * (1.0f - p.color1[1]);
+ scb = 1.0f - (1.0f - p.color2[2]) * (1.0f - p.color1[2]);
+
+ p.out[0] = value_m * p.color1[0] +
+ value * ((1.0f - p.color1[0]) * p.color2[0] * p.color1[0] + p.color1[0] * scr);
+ p.out[1] = value_m * p.color1[1] +
+ value * ((1.0f - p.color1[1]) * p.color2[1] * p.color1[1] + p.color1[1] * scg);
+ p.out[2] = value_m * p.color1[2] +
+ value * ((1.0f - p.color1[2]) * p.color2[2] * p.color1[2] + p.color1[2] * scb);
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Subtract Operation ******** */
void MixSubtractOperation::executePixelSampled(float output[4],
@@ -820,6 +1337,23 @@ void MixSubtractOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixSubtractOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ p.out[0] = p.color1[0] - value * p.color2[0];
+ p.out[1] = p.color1[1] - value * p.color2[1];
+ p.out[2] = p.color1[2] - value * p.color2[2];
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Value Operation ******** */
void MixValueOperation::executePixelSampled(float output[4],
@@ -851,4 +1385,25 @@ void MixValueOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixValueOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ float value_m = 1.0f - value;
+
+ float rH, rS, rV;
+ float colH, colS, colV;
+ rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
+ rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
+ hsv_to_rgb(rH, rS, (value_m * rV + value * colV), &p.out[0], &p.out[1], &p.out[2]);
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h
index 6c241bc5762..7ef9d78d58f 100644
--- a/source/blender/compositor/operations/COM_MixOperation.h
+++ b/source/blender/compositor/operations/COM_MixOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -27,8 +27,29 @@ namespace blender::compositor {
* it assumes we are in sRGB color space.
*/
-class MixBaseOperation : public NodeOperation {
+class MixBaseOperation : public MultiThreadedOperation {
protected:
+ struct PixelCursor {
+ float *out;
+ const float *row_end;
+ const float *value;
+ const float *color1;
+ const float *color2;
+ int out_stride;
+ int value_stride;
+ int color1_stride;
+ int color2_stride;
+
+ void next()
+ {
+ BLI_assert(out < row_end);
+ out += out_stride;
+ value += value_stride;
+ color1 += color1_stride;
+ color2 += color2_stride;
+ }
+ };
+
/**
* Prefetched reference to the inputProgram
*/
@@ -81,101 +102,165 @@ class MixBaseOperation : public NodeOperation {
{
this->m_useClamp = value;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) final;
+
+ protected:
+ virtual void update_memory_buffer_row(PixelCursor &p);
};
class MixAddOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixBlendOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixColorBurnOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixColorOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixDarkenOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixDifferenceOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixDivideOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixDodgeOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixGlareOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixHueOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixLightenOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixLinearLightOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixMultiplyOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixOverlayOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixSaturationOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixScreenOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixSoftLightOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixSubtractOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixValueOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc
index d93a75407c4..e520b928edf 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc
@@ -116,6 +116,18 @@ void MovieClipBaseOperation::executePixelSampled(float output[4],
}
}
+void MovieClipBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> UNUSED(inputs))
+{
+ if (m_movieClipBuffer) {
+ output->copy_from(m_movieClipBuffer, area);
+ }
+ else {
+ output->fill(area, COM_COLOR_TRANSPARENT);
+ }
+}
+
MovieClipOperation::MovieClipOperation() : MovieClipBaseOperation()
{
this->addOutputSocket(DataType::Color);
@@ -136,4 +148,16 @@ void MovieClipAlphaOperation::executePixelSampled(float output[4],
output[0] = result[3];
}
+void MovieClipAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> UNUSED(inputs))
+{
+ if (m_movieClipBuffer) {
+ output->copy_from(m_movieClipBuffer, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0);
+ }
+ else {
+ output->fill(area, COM_VALUE_ZERO);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h
index c853ea43762..0a0c4c00f81 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.h
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.h
@@ -19,7 +19,7 @@
#pragma once
#include "BLI_listbase.h"
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_movieclip_types.h"
#include "IMB_imbuf_types.h"
@@ -28,7 +28,7 @@ namespace blender::compositor {
/**
* Base class for movie clip
*/
-class MovieClipBaseOperation : public NodeOperation {
+class MovieClipBaseOperation : public MultiThreadedOperation {
protected:
MovieClip *m_movieClip;
MovieClipUser *m_movieClipUser;
@@ -67,6 +67,10 @@ class MovieClipBaseOperation : public NodeOperation {
this->m_framenumber = framenumber;
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class MovieClipOperation : public MovieClipBaseOperation {
@@ -78,6 +82,10 @@ class MovieClipAlphaOperation : public MovieClipBaseOperation {
public:
MovieClipAlphaOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc
index c3647a39909..b078d85372d 100644
--- a/source/blender/compositor/operations/COM_SMAAOperation.cc
+++ b/source/blender/compositor/operations/COM_SMAAOperation.cc
@@ -19,6 +19,7 @@
*/
#include "COM_SMAAOperation.h"
+#include "BKE_node.h"
#include "BLI_math.h"
#include "COM_SMAAAreaTexture.h"
@@ -166,8 +167,8 @@ SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation()
this->flags.complex = true;
this->m_imageReader = nullptr;
this->m_valueReader = nullptr;
- this->m_threshold = 0.1f;
- this->m_contrast_limit = 2.0f;
+ this->setThreshold(CMP_DEFAULT_SMAA_THRESHOLD);
+ this->setLocalContrastAdaptationFactor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT);
}
void SMAAEdgeDetectionOperation::initExecution()
@@ -297,7 +298,7 @@ SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation()
this->addOutputSocket(DataType::Color);
this->flags.complex = true;
this->m_imageReader = nullptr;
- this->m_corner_rounding = 25;
+ this->setCornerRounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING);
}
void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect)
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index f03b9fcf34d..5410b2c832a 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -17,6 +17,7 @@
*/
#include "COM_ScaleOperation.h"
+#include "COM_ConstantOperation.h"
namespace blender::compositor {
@@ -52,13 +53,51 @@ ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation()
this->m_inputXOperation = nullptr;
this->m_inputYOperation = nullptr;
}
+
+float ScaleOperation::get_constant_scale(const int input_op_idx, const float factor)
+{
+ const bool is_constant = getInputOperation(input_op_idx)->get_flags().is_constant_operation;
+ if (is_constant) {
+ return ((ConstantOperation *)getInputOperation(input_op_idx))->get_constant_elem()[0] * factor;
+ }
+
+ return 1.0f;
+}
+
+float ScaleOperation::get_constant_scale_x()
+{
+ return get_constant_scale(1, get_relative_scale_x_factor());
+}
+
+float ScaleOperation::get_constant_scale_y()
+{
+ return get_constant_scale(2, get_relative_scale_y_factor());
+}
+
+BLI_INLINE float scale_coord(const int coord, const float center, const float relative_scale)
+{
+ return center + (coord - center) / relative_scale;
+}
+
+void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y)
+{
+ rect.xmin = scale_coord(rect.xmin, m_centerX, scale_x);
+ rect.xmax = scale_coord(rect.xmax, m_centerX, scale_x);
+ rect.ymin = scale_coord(rect.ymin, m_centerY, scale_y);
+ rect.ymax = scale_coord(rect.ymax, m_centerY, scale_y);
+}
+
+void ScaleOperation::init_data()
+{
+ m_centerX = getWidth() / 2.0f;
+ m_centerY = getHeight() / 2.0f;
+}
+
void ScaleOperation::initExecution()
{
this->m_inputOperation = this->getInputSocketReader(0);
this->m_inputXOperation = this->getInputSocketReader(1);
this->m_inputYOperation = this->getInputSocketReader(2);
- this->m_centerX = this->getWidth() / 2.0;
- this->m_centerY = this->getHeight() / 2.0;
}
void ScaleOperation::deinitExecution()
@@ -68,7 +107,52 @@ void ScaleOperation::deinitExecution()
this->m_inputYOperation = nullptr;
}
-void ScaleOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
+void ScaleOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ r_input_area = output_area;
+ if (input_idx != 0 || m_variable_size) {
+ return;
+ }
+
+ float scale_x = get_constant_scale_x();
+ float scale_y = get_constant_scale_y();
+ scale_area(r_input_area, scale_x, scale_y);
+ expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
+}
+
+void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[0];
+ MemoryBuffer *input_x = inputs[1];
+ MemoryBuffer *input_y = inputs[2];
+ const float scale_x_factor = get_relative_scale_x_factor();
+ const float scale_y_factor = get_relative_scale_y_factor();
+ BuffersIterator<float> it = output->iterate_with({input_x, input_y}, area);
+ for (; !it.is_end(); ++it) {
+ const float rel_scale_x = *it.in(0) * scale_x_factor;
+ const float rel_scale_y = *it.in(1) * scale_y_factor;
+ const float scaled_x = scale_coord(it.x, m_centerX, rel_scale_x);
+ const float scaled_y = scale_coord(it.y, m_centerY, rel_scale_y);
+ input_img->read_elem_sampled(scaled_x, scaled_y, (PixelSampler)m_sampler, it.out);
+ }
+}
+
+ScaleRelativeOperation::ScaleRelativeOperation() : ScaleOperation()
+{
+}
+
+ScaleRelativeOperation::ScaleRelativeOperation(DataType data_type) : ScaleOperation(data_type)
+{
+}
+
+void ScaleRelativeOperation::executePixelSampled(float output[4],
+ float x,
+ float y,
+ PixelSampler sampler)
{
PixelSampler effective_sampler = getEffectiveSampler(sampler);
@@ -86,9 +170,9 @@ void ScaleOperation::executePixelSampled(float output[4], float x, float y, Pixe
this->m_inputOperation->readSampled(output, nx, ny, effective_sampler);
}
-bool ScaleOperation::determineDependingAreaOfInterest(rcti *input,
- ReadBufferOperation *readOperation,
- rcti *output)
+bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input,
+ ReadBufferOperation *readOperation,
+ rcti *output)
{
rcti newInput;
if (!m_variable_size) {
@@ -115,34 +199,6 @@ bool ScaleOperation::determineDependingAreaOfInterest(rcti *input,
return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
-// SCALE ABSOLUTE
-ScaleAbsoluteOperation::ScaleAbsoluteOperation() : BaseScaleOperation()
-{
- this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Value);
- this->addInputSocket(DataType::Value);
- this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
- this->m_inputOperation = nullptr;
- this->m_inputXOperation = nullptr;
- this->m_inputYOperation = nullptr;
-}
-void ScaleAbsoluteOperation::initExecution()
-{
- this->m_inputOperation = this->getInputSocketReader(0);
- this->m_inputXOperation = this->getInputSocketReader(1);
- this->m_inputYOperation = this->getInputSocketReader(2);
- this->m_centerX = this->getWidth() / 2.0;
- this->m_centerY = this->getHeight() / 2.0;
-}
-
-void ScaleAbsoluteOperation::deinitExecution()
-{
- this->m_inputOperation = nullptr;
- this->m_inputXOperation = nullptr;
- this->m_inputYOperation = nullptr;
-}
-
void ScaleAbsoluteOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -202,8 +258,7 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
newInput.ymax = this->getHeight();
newInput.ymin = 0;
}
-
- return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
+ return ScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
// Absolute fixed size
@@ -215,11 +270,12 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation()
this->m_inputOperation = nullptr;
this->m_is_offset = false;
}
-void ScaleFixedSizeOperation::initExecution()
+
+void ScaleFixedSizeOperation::init_data()
{
- this->m_inputOperation = this->getInputSocketReader(0);
- this->m_relX = this->m_inputOperation->getWidth() / (float)this->m_newWidth;
- this->m_relY = this->m_inputOperation->getHeight() / (float)this->m_newHeight;
+ const NodeOperation *input_op = getInputOperation(0);
+ this->m_relX = input_op->getWidth() / (float)this->m_newWidth;
+ this->m_relY = input_op->getHeight() / (float)this->m_newHeight;
/* *** all the options below are for a fairly special case - camera framing *** */
if (this->m_offsetX != 0.0f || this->m_offsetY != 0.0f) {
@@ -237,8 +293,8 @@ void ScaleFixedSizeOperation::initExecution()
if (this->m_is_aspect) {
/* apply aspect from clip */
- const float w_src = this->m_inputOperation->getWidth();
- const float h_src = this->m_inputOperation->getHeight();
+ const float w_src = input_op->getWidth();
+ const float h_src = input_op->getHeight();
/* destination aspect is already applied from the camera frame */
const float w_dst = this->m_newWidth;
@@ -267,6 +323,11 @@ void ScaleFixedSizeOperation::initExecution()
/* *** end framing options *** */
}
+void ScaleFixedSizeOperation::initExecution()
+{
+ this->m_inputOperation = this->getInputSocketReader(0);
+}
+
void ScaleFixedSizeOperation::deinitExecution()
{
this->m_inputOperation = nullptr;
@@ -315,4 +376,38 @@ void ScaleFixedSizeOperation::determineResolution(unsigned int resolution[2],
resolution[1] = this->m_newHeight;
}
+void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ r_input_area.xmax = (output_area.xmax - m_offsetX) * this->m_relX;
+ r_input_area.xmin = (output_area.xmin - m_offsetX) * this->m_relX;
+ r_input_area.ymax = (output_area.ymax - m_offsetY) * this->m_relY;
+ r_input_area.ymin = (output_area.ymin - m_offsetY) * this->m_relY;
+ expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
+}
+
+void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[0];
+ PixelSampler sampler = (PixelSampler)m_sampler;
+ BuffersIterator<float> it = output->iterate_with({}, area);
+ if (this->m_is_offset) {
+ for (; !it.is_end(); ++it) {
+ const float nx = (it.x - this->m_offsetX) * this->m_relX;
+ const float ny = (it.y - this->m_offsetY) * this->m_relY;
+ input_img->read_elem_sampled(nx, ny, sampler, it.out);
+ }
+ }
+ else {
+ for (; !it.is_end(); ++it) {
+ input_img->read_elem_sampled(it.x * this->m_relX, it.y * this->m_relY, sampler, it.out);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h
index 2f9a7be92e6..62a2cabc8e6 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.h
+++ b/source/blender/compositor/operations/COM_ScaleOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class BaseScaleOperation : public NodeOperation {
+class BaseScaleOperation : public MultiThreadedOperation {
public:
void setSampler(PixelSampler sampler)
{
@@ -46,7 +46,7 @@ class BaseScaleOperation : public NodeOperation {
};
class ScaleOperation : public BaseScaleOperation {
- private:
+ protected:
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
SocketReader *m_inputYOperation;
@@ -56,31 +56,59 @@ class ScaleOperation : public BaseScaleOperation {
public:
ScaleOperation();
ScaleOperation(DataType data_type);
- bool determineDependingAreaOfInterest(rcti *input,
- ReadBufferOperation *readOperation,
- rcti *output) override;
- void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
-};
-class ScaleAbsoluteOperation : public BaseScaleOperation {
- SocketReader *m_inputOperation;
- SocketReader *m_inputXOperation;
- SocketReader *m_inputYOperation;
- float m_centerX;
- float m_centerY;
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ protected:
+ virtual float get_relative_scale_x_factor() = 0;
+ virtual float get_relative_scale_y_factor() = 0;
+
+ private:
+ float get_constant_scale(int input_op_idx, float factor);
+ float get_constant_scale_x();
+ float get_constant_scale_y();
+ void scale_area(rcti &rect, float scale_x, float scale_y);
+};
+class ScaleRelativeOperation : public ScaleOperation {
public:
- ScaleAbsoluteOperation();
+ ScaleRelativeOperation();
+ ScaleRelativeOperation(DataType data_type);
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ float get_relative_scale_x_factor() override
+ {
+ return 1.0f;
+ }
+ float get_relative_scale_y_factor() override
+ {
+ return 1.0f;
+ }
+};
- void initExecution() override;
- void deinitExecution() override;
+class ScaleAbsoluteOperation : public ScaleOperation {
+ public:
+ bool determineDependingAreaOfInterest(rcti *input,
+ ReadBufferOperation *readOperation,
+ rcti *output) override;
+ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ float get_relative_scale_x_factor() override
+ {
+ return 1.0f / getWidth();
+ }
+ float get_relative_scale_y_factor() override
+ {
+ return 1.0f / getHeight();
+ }
};
class ScaleFixedSizeOperation : public BaseScaleOperation {
@@ -108,6 +136,7 @@ class ScaleFixedSizeOperation : public BaseScaleOperation {
unsigned int preferredResolution[2]) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
void setNewWidth(int width)
@@ -131,6 +160,11 @@ class ScaleFixedSizeOperation : public BaseScaleOperation {
this->m_offsetX = x;
this->m_offsetY = y;
}
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
index ff117841e8e..bd82b6397ad 100644
--- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc
+++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
@@ -46,14 +46,14 @@ void SunBeamsOperation::initExecution()
* (u,v) is used to designate sector space coordinates
*
* For a target point (x,y) the sector should be chosen such that
- * ``u >= v >= 0``
+ * `u >= v >= 0`
* This removes the need to handle all sorts of special cases.
*
* Template parameters:
- * fxu : buffer increment in x for sector u+1
- * fxv : buffer increment in x for sector v+1
- * fyu : buffer increment in y for sector u+1
- * fyv : buffer increment in y for sector v+1
+ * \param fxu: buffer increment in x for sector `u + 1`.
+ * \param fxv: buffer increment in x for sector `v + 1`.
+ * \param fyu: buffer increment in y for sector `u + 1`.
+ * \param fyv: buffer increment in y for sector `v + 1`.
*/
template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc
index 059a289ae4d..c8e0844d35f 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.cc
+++ b/source/blender/compositor/operations/COM_TextureOperation.cc
@@ -157,14 +157,8 @@ void TextureBaseOperation::executePixelSampled(float output[4],
m_sceneColorManage,
false);
- if (texres.talpha) {
- output[3] = texres.ta;
- }
- else {
- output[3] = texres.tin;
- }
-
- if ((retval & TEX_RGB)) {
+ output[3] = texres.talpha ? texres.ta : texres.tin;
+ if (retval & TEX_RGB) {
output[0] = texres.tr;
output[1] = texres.tg;
output[2] = texres.tb;
@@ -174,4 +168,67 @@ void TextureBaseOperation::executePixelSampled(float output[4],
}
}
+void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const int op_width = this->getWidth();
+ const int op_height = this->getHeight();
+ const float center_x = op_width / 2;
+ const float center_y = op_height / 2;
+ TexResult tex_result = {0};
+ float vec[3];
+ const int thread_id = WorkScheduler::current_thread_id();
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *tex_offset = it.in(0);
+ const float *tex_size = it.in(1);
+ float u = (it.x - center_x) / op_width * 2;
+ float v = (it.y - center_y) / op_height * 2;
+
+ /* When no interpolation/filtering happens in multitex() force nearest interpolation.
+ * We do it here because (a) we can't easily say multitex() that we want nearest
+ * interpolation and (b) in such configuration multitex() simply floor's the value
+ * which often produces artifacts.
+ */
+ if (m_texture != nullptr && (m_texture->imaflag & TEX_INTERPOL) == 0) {
+ u += 0.5f / center_x;
+ v += 0.5f / center_y;
+ }
+
+ vec[0] = tex_size[0] * (u + tex_offset[0]);
+ vec[1] = tex_size[1] * (v + tex_offset[1]);
+ vec[2] = tex_size[2] * tex_offset[2];
+
+ const int retval = multitex_ext(this->m_texture,
+ vec,
+ nullptr,
+ nullptr,
+ 0,
+ &tex_result,
+ thread_id,
+ m_pool,
+ m_sceneColorManage,
+ false);
+
+ it.out[3] = tex_result.talpha ? tex_result.ta : 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;
+ }
+ else {
+ it.out[0] = it.out[1] = it.out[2] = it.out[3];
+ }
+ }
+}
+
+void TextureAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ MemoryBuffer texture(DataType::Color, area);
+ TextureBaseOperation::update_memory_buffer_partial(&texture, area, inputs);
+ output->copy_from(&texture, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0);
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h
index 6fec9ab8f33..1e95cb270d0 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.h
+++ b/source/blender/compositor/operations/COM_TextureOperation.h
@@ -19,7 +19,7 @@
#pragma once
#include "BLI_listbase.h"
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_texture_types.h"
#include "MEM_guardedalloc.h"
@@ -33,7 +33,7 @@ namespace blender::compositor {
*
* \todo Rename to operation.
*/
-class TextureBaseOperation : public NodeOperation {
+class TextureBaseOperation : public MultiThreadedOperation {
private:
Tex *m_texture;
const RenderData *m_rd;
@@ -71,6 +71,10 @@ class TextureBaseOperation : public NodeOperation {
{
this->m_sceneColorManage = sceneColorManage;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class TextureOperation : public TextureBaseOperation {
@@ -81,6 +85,10 @@ class TextureAlphaOperation : public TextureBaseOperation {
public:
TextureAlphaOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc
index 860f56e23fa..37a45ac32cb 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.cc
+++ b/source/blender/compositor/operations/COM_ViewerOperation.cc
@@ -191,10 +191,11 @@ void ViewerOperation::initImage()
BLI_thread_unlock(LOCK_DRAW_IMAGE);
}
-void ViewerOperation::updateImage(rcti *rect)
+void ViewerOperation::updateImage(const rcti *rect)
{
+ float *buffer = m_outputBuffer;
IMB_partial_display_buffer_update(this->m_ibuf,
- this->m_outputBuffer,
+ buffer,
nullptr,
getWidth(),
0,
@@ -218,4 +219,44 @@ eCompositorPriority ViewerOperation::getRenderPriority() const
return eCompositorPriority::Low;
}
+void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ if (!m_outputBuffer) {
+ return;
+ }
+
+ MemoryBuffer output_buffer(
+ m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight());
+ const MemoryBuffer *input_image = inputs[0];
+ output_buffer.copy_from(input_image, area);
+ if (this->m_useAlphaInput) {
+ const MemoryBuffer *input_alpha = inputs[1];
+ output_buffer.copy_from(input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3);
+ }
+
+ if (m_depthBuffer) {
+ MemoryBuffer depth_buffer(
+ m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight());
+ const MemoryBuffer *input_depth = inputs[2];
+ depth_buffer.copy_from(input_depth, area);
+ }
+
+ updateImage(&area);
+}
+
+void ViewerOperation::clear_display_buffer()
+{
+ BLI_assert(isActiveViewerOutput());
+ initImage();
+ size_t buf_bytes = (size_t)m_ibuf->y * m_ibuf->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float);
+ if (buf_bytes > 0) {
+ memset(m_outputBuffer, 0, buf_bytes);
+ rcti display_area;
+ BLI_rcti_init(&display_area, 0, m_ibuf->x, 0, m_ibuf->y);
+ updateImage(&display_area);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h
index c0f13ff79fc..06ac501a535 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.h
+++ b/source/blender/compositor/operations/COM_ViewerOperation.h
@@ -20,15 +20,17 @@
#include "BKE_global.h"
#include "BLI_rect.h"
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_image_types.h"
namespace blender::compositor {
-class ViewerOperation : public NodeOperation {
+class ViewerOperation : public MultiThreadedOperation {
private:
+ /* TODO(manzanilla): To be removed together with tiled implementation. */
float *m_outputBuffer;
float *m_depthBuffer;
+
Image *m_image;
ImageUser *m_imageUser;
bool m_active;
@@ -125,8 +127,14 @@ class ViewerOperation : public NodeOperation {
this->m_displaySettings = displaySettings;
}
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ void clear_display_buffer();
+
private:
- void updateImage(rcti *rect);
+ void updateImage(const rcti *rect);
void initImage();
};
diff --git a/source/blender/compositor/tests/COM_BufferArea_test.cc b/source/blender/compositor/tests/COM_BufferArea_test.cc
new file mode 100644
index 00000000000..8dad0b0fea2
--- /dev/null
+++ b/source/blender/compositor/tests/COM_BufferArea_test.cc
@@ -0,0 +1,141 @@
+/*
+ * 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 "testing/testing.h"
+
+#include "COM_BufferArea.h"
+
+namespace blender::compositor::tests {
+
+static rcti create_rect(int width, int height)
+{
+ rcti rect;
+ BLI_rcti_init(&rect, 0, width, 0, height);
+ return rect;
+}
+
+static rcti create_rect(int width, int height, int offset)
+{
+ rcti rect;
+ BLI_rcti_init(&rect, offset, offset + width, offset, offset + height);
+ return rect;
+}
+
+TEST(BufferArea, BufferConstructor)
+{
+ const int width = 2;
+ const int height = 3;
+ BufferArea<float> area(nullptr, width, height, 4);
+ EXPECT_EQ(area.width(), width);
+ EXPECT_EQ(area.height(), height);
+ rcti rect = create_rect(width, height);
+ EXPECT_TRUE(BLI_rcti_compare(&area.get_rect(), &rect));
+}
+
+TEST(BufferArea, AreaConstructor)
+{
+ const int buf_width = 5;
+ const int area_width = 1;
+ const int area_height = 3;
+ rcti area_rect = create_rect(area_width, area_height, 1);
+ BufferArea<float> area(nullptr, buf_width, area_rect, 4);
+ EXPECT_EQ(area.width(), area_width);
+ EXPECT_EQ(area.height(), area_height);
+ EXPECT_TRUE(BLI_rcti_compare(&area.get_rect(), &area_rect));
+}
+
+static void fill_buffer_with_indexes(float *buf, int buf_len)
+{
+ for (int i = 0; i < buf_len; i++) {
+ buf[i] = i;
+ }
+}
+
+static void test_single_elem_iteration(float *buffer, BufferArea<float> area)
+{
+ int elems_count = 0;
+ for (float *elem : area) {
+ EXPECT_EQ(elem, buffer);
+ elems_count++;
+ }
+ EXPECT_EQ(elems_count, 1);
+}
+
+static void test_full_buffer_iteration(
+ float *buf, int buf_width, int buf_len, int num_channels, BufferArea<float> area)
+{
+ fill_buffer_with_indexes(buf, buf_len);
+ rcti rect = area.get_rect();
+ int x = rect.xmin;
+ int y = rect.ymin;
+ for (float *elem : area) {
+ for (int ch = 0; ch < num_channels; ch++) {
+ const int buf_index = y * buf_width * num_channels + x * num_channels + ch;
+ EXPECT_NEAR(elem[ch], buf_index, FLT_EPSILON);
+ }
+ x++;
+ if (x == rect.xmax) {
+ y++;
+ x = rect.xmin;
+ }
+ }
+ EXPECT_EQ(x, rect.xmin);
+ EXPECT_EQ(y, rect.ymax);
+}
+
+TEST(BufferArea, SingleElemBufferIteration)
+{
+ const int buf_width = 4;
+ const int buf_height = 5;
+ const int area_width = 2;
+ const int area_height = 3;
+ const int num_channels = 4;
+ const int stride = 0;
+ float buf[num_channels];
+ {
+ BufferArea area(buf, buf_width, buf_height, stride);
+ test_single_elem_iteration(buf, area);
+ }
+ {
+ rcti area_rect = create_rect(area_width, area_height, 1);
+ BufferArea area(buf, buf_width, area_rect, stride);
+ test_single_elem_iteration(buf, area);
+ }
+}
+
+TEST(BufferArea, FullBufferIteration)
+{
+ const int buf_width = 4;
+ const int area_width = 2;
+ const int area_height = 3;
+ const int buf_height = (area_height + 1);
+ const int num_channels = 4;
+ const int buf_len = buf_height * buf_width * num_channels;
+ float buf[buf_len];
+ {
+ BufferArea area(buf, buf_width, buf_height, num_channels);
+ test_full_buffer_iteration(buf, buf_width, buf_len, num_channels, area);
+ }
+ {
+ rcti area_rect = create_rect(area_width, area_height, 1);
+ BufferArea area(buf, buf_width, area_rect, num_channels);
+ test_full_buffer_iteration(buf, buf_width, buf_len, num_channels, area);
+ }
+}
+
+} // namespace blender::compositor::tests
diff --git a/source/blender/compositor/tests/COM_BufferRange_test.cc b/source/blender/compositor/tests/COM_BufferRange_test.cc
new file mode 100644
index 00000000000..9ebeb181fa6
--- /dev/null
+++ b/source/blender/compositor/tests/COM_BufferRange_test.cc
@@ -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.
+ */
+
+#include "testing/testing.h"
+
+#include "COM_BufferRange.h"
+
+namespace blender::compositor::tests {
+
+TEST(BufferRange, Constructor)
+{
+ const int size = 5;
+ BufferRange<float> range(nullptr, 1, size, 4);
+ EXPECT_EQ(range.size(), size);
+}
+
+static void fill_buffer_with_indexes(float *buf, int buf_len)
+{
+ for (int i = 0; i < buf_len; i++) {
+ buf[i] = i;
+ }
+}
+
+TEST(BufferRange, Subscript)
+{
+ const int start = 2;
+ const int size = 4;
+ const int num_channels = 3;
+ const int buf_len = (start + size) * num_channels;
+ float buf[buf_len];
+
+ BufferRange<float> range(buf, start, size, num_channels);
+
+ fill_buffer_with_indexes(buf, buf_len);
+ int buf_index = start * num_channels;
+ for (int i = 0; i < size; i++) {
+ const float *elem = range[i];
+ for (int ch = 0; ch < num_channels; ch++) {
+ EXPECT_NEAR(elem[ch], buf_index, FLT_EPSILON);
+ buf_index++;
+ }
+ }
+ EXPECT_EQ(buf_index, buf_len);
+}
+
+TEST(BufferRange, SingleElemBufferIteration)
+{
+ const int start = 1;
+ const int size = 3;
+ const int num_channels = 4;
+ float buf[num_channels];
+ const int stride = 0;
+ BufferRange<float> range(buf, start, size, stride);
+
+ int elems_count = 0;
+ for (float *elem : range) {
+ EXPECT_EQ(elem, buf);
+ elems_count++;
+ }
+ EXPECT_EQ(elems_count, 1);
+}
+
+TEST(BufferRange, FullBufferIteration)
+{
+ const int start = 2;
+ const int size = 5;
+ const int num_channels = 4;
+ const int buf_len = (start + size) * num_channels;
+ float buf[buf_len];
+ BufferRange<float> range(buf, start, size, num_channels);
+
+ fill_buffer_with_indexes(buf, buf_len);
+ int buf_index = start * num_channels;
+ for (float *elem : range) {
+ for (int ch = 0; ch < num_channels; ch++) {
+ EXPECT_NEAR(elem[ch], buf_index, FLT_EPSILON);
+ buf_index++;
+ }
+ }
+ EXPECT_EQ(buf_index, buf_len);
+}
+
+} // namespace blender::compositor::tests
diff --git a/source/blender/compositor/tests/COM_BuffersIterator_test.cc b/source/blender/compositor/tests/COM_BuffersIterator_test.cc
new file mode 100644
index 00000000000..0a288cdc5f0
--- /dev/null
+++ b/source/blender/compositor/tests/COM_BuffersIterator_test.cc
@@ -0,0 +1,299 @@
+/*
+ * 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 "testing/testing.h"
+
+#include "BLI_array.hh"
+#include "COM_BuffersIterator.h"
+
+namespace blender::compositor::tests {
+
+constexpr int BUFFER_WIDTH = 5;
+constexpr int BUFFER_HEIGHT = 4;
+constexpr int BUFFER_OFFSET_X = 5;
+constexpr int BUFFER_OFFSET_Y = 6;
+constexpr int NUM_CHANNELS = 4;
+constexpr int FULL_BUFFER_LEN = BUFFER_WIDTH * BUFFER_HEIGHT * NUM_CHANNELS;
+constexpr int SINGLE_ELEM_BUFFER_LEN = NUM_CHANNELS;
+constexpr int NUM_INPUTS = 2;
+
+static float *create_buffer(int len)
+{
+ return (float *)MEM_callocN(len * sizeof(float), "COM_BuffersIteratorTest");
+}
+
+static const float *create_input_buffer(int input_idx, bool is_a_single_elem)
+{
+ const int len = is_a_single_elem ? SINGLE_ELEM_BUFFER_LEN : FULL_BUFFER_LEN;
+ float *buf = create_buffer(len);
+ /* Fill buffer with variable data. */
+ for (int i = 0; i < len; i++) {
+ buf[i] = input_idx * 1.5f * (i + 1) + i * 0.9f;
+ }
+ return buf;
+}
+
+using IterFunc = std::function<void(BuffersIterator<float> &it, const rcti &area)>;
+using ValidateElemFunc = std::function<void(float *out, Span<const float *> ins, int x, int y)>;
+
+class BuffersIteratorTest : public testing::Test {
+ private:
+ float *output_;
+ bool use_offsets_;
+ bool use_single_elem_inputs_;
+ bool use_inputs_;
+
+ static rcti buffer_area;
+ static rcti buffer_offset_area;
+ static Array<const float *, NUM_INPUTS> single_elem_inputs;
+ static Array<const float *, NUM_INPUTS> full_buffer_inputs;
+
+ public:
+ void set_inputs_enabled(bool value)
+ {
+ use_inputs_ = value;
+ }
+
+ void test_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func = {})
+ {
+ use_single_elem_inputs_ = false;
+ validate_iteration(iter_func, validate_elem_func);
+ if (use_inputs_) {
+ use_single_elem_inputs_ = true;
+ validate_iteration(iter_func, validate_elem_func);
+ }
+ }
+
+ protected:
+ static void SetUpTestCase()
+ {
+ BLI_rcti_init(&buffer_area, 0, BUFFER_WIDTH, 0, BUFFER_HEIGHT);
+ BLI_rcti_init(&buffer_offset_area,
+ BUFFER_OFFSET_X,
+ BUFFER_OFFSET_X + BUFFER_WIDTH,
+ BUFFER_OFFSET_Y,
+ BUFFER_OFFSET_Y + BUFFER_HEIGHT);
+ for (int i = 0; i < NUM_INPUTS; i++) {
+ single_elem_inputs[i] = create_input_buffer(i, true);
+ full_buffer_inputs[i] = create_input_buffer(i, false);
+ }
+ }
+
+ static void TearDownTestCase()
+ {
+ for (int i = 0; i < NUM_INPUTS; i++) {
+ MEM_freeN((void *)single_elem_inputs[i]);
+ single_elem_inputs[i] = nullptr;
+ MEM_freeN((void *)full_buffer_inputs[i]);
+ full_buffer_inputs[i] = nullptr;
+ }
+ }
+
+ void SetUp() override
+ {
+ use_offsets_ = false;
+ use_single_elem_inputs_ = false;
+ use_inputs_ = false;
+ output_ = create_buffer(FULL_BUFFER_LEN);
+ }
+
+ void TearDown() override
+ {
+ MEM_freeN(output_);
+ }
+
+ private:
+ void validate_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func)
+ {
+ {
+ use_offsets_ = false;
+ BuffersIterator<float> it = iterate();
+ iter_func(it, buffer_area);
+ validate_result(buffer_area, validate_elem_func);
+ }
+ {
+ use_offsets_ = true;
+ BuffersIterator<float> it = offset_iterate(buffer_offset_area);
+ iter_func(it, buffer_offset_area);
+ validate_result(buffer_offset_area, validate_elem_func);
+ }
+ {
+ use_offsets_ = true;
+ rcti area = buffer_offset_area;
+ area.xmin += 1;
+ area.ymin += 1;
+ area.xmax -= 1;
+ area.ymax -= 1;
+ BuffersIterator<float> it = offset_iterate(area);
+ iter_func(it, area);
+ validate_result(area, validate_elem_func);
+ }
+ }
+
+ void validate_result(rcti &area, ValidateElemFunc validate_elem_func)
+ {
+ Span<const float *> inputs = get_inputs();
+ Array<const float *> ins(inputs.size());
+ for (int y = area.ymin; y < area.ymax; y++) {
+ for (int x = area.xmin; x < area.xmax; x++) {
+ const int out_offset = get_buffer_relative_y(y) * BUFFER_WIDTH * NUM_CHANNELS +
+ get_buffer_relative_x(x) * NUM_CHANNELS;
+ float *out = &output_[out_offset];
+
+ const int in_offset = use_single_elem_inputs_ ? 0 : out_offset;
+ for (int i = 0; i < inputs.size(); i++) {
+ ins[i] = &inputs[i][in_offset];
+ }
+
+ if (validate_elem_func) {
+ validate_elem_func(out, ins, x, y);
+ }
+ }
+ }
+ }
+
+ Span<const float *> get_inputs()
+ {
+ if (use_inputs_) {
+ return use_single_elem_inputs_ ? single_elem_inputs : full_buffer_inputs;
+ }
+ return {};
+ }
+
+ int get_buffer_relative_x(int x)
+ {
+ return use_offsets_ ? x - BUFFER_OFFSET_X : x;
+ }
+ int get_buffer_relative_y(int y)
+ {
+ return use_offsets_ ? y - BUFFER_OFFSET_Y : y;
+ }
+
+ /** Iterates whole buffers with no offsets. */
+ BuffersIterator<float> iterate()
+ {
+ BLI_assert(!use_offsets_);
+ BuffersIteratorBuilder<float> builder(output_, BUFFER_WIDTH, BUFFER_HEIGHT, NUM_CHANNELS);
+ if (use_inputs_) {
+ const int input_stride = use_single_elem_inputs_ ? 0 : NUM_CHANNELS;
+ for (const float *input : get_inputs()) {
+ builder.add_input(input, BUFFER_WIDTH, input_stride);
+ }
+ }
+ return builder.build();
+ }
+
+ /** Iterates a given buffers area with default offsets. */
+ BuffersIterator<float> offset_iterate(const rcti &area)
+ {
+ BLI_assert(use_offsets_);
+ const rcti &buf_area = buffer_offset_area;
+ BuffersIteratorBuilder<float> builder(output_, buf_area, area, NUM_CHANNELS);
+ if (use_inputs_) {
+ const int input_stride = use_single_elem_inputs_ ? 0 : NUM_CHANNELS;
+ for (const float *input : get_inputs()) {
+ builder.add_input(input, buf_area, input_stride);
+ }
+ }
+ return builder.build();
+ }
+};
+
+rcti BuffersIteratorTest::buffer_area;
+rcti BuffersIteratorTest::buffer_offset_area;
+Array<const float *, NUM_INPUTS> BuffersIteratorTest::single_elem_inputs(NUM_INPUTS);
+Array<const float *, NUM_INPUTS> BuffersIteratorTest::full_buffer_inputs(NUM_INPUTS);
+
+static void iterate_coordinates(BuffersIterator<float> &it, const rcti &area)
+{
+ int x = area.xmin;
+ int y = area.ymin;
+ for (; !it.is_end(); ++it) {
+ EXPECT_EQ(x, it.x);
+ EXPECT_EQ(y, it.y);
+ x++;
+ if (x == area.xmax) {
+ x = area.xmin;
+ y++;
+ }
+ }
+ EXPECT_EQ(x, area.xmin);
+ EXPECT_EQ(y, area.ymax);
+}
+
+TEST_F(BuffersIteratorTest, CoordinatesIterationWithNoInputs)
+{
+ set_inputs_enabled(false);
+ test_iteration(iterate_coordinates);
+}
+
+TEST_F(BuffersIteratorTest, CoordinatesIterationWithInputs)
+{
+ set_inputs_enabled(true);
+ test_iteration(iterate_coordinates);
+}
+
+TEST_F(BuffersIteratorTest, OutputIteration)
+{
+ set_inputs_enabled(false);
+ test_iteration(
+ [](BuffersIterator<float> &it, const rcti &UNUSED(area)) {
+ EXPECT_EQ(it.get_num_inputs(), 0);
+ for (; !it.is_end(); ++it) {
+ const int dummy = it.y * BUFFER_WIDTH + it.x;
+ it.out[0] = dummy + 1.0f;
+ it.out[1] = dummy + 2.0f;
+ it.out[2] = dummy + 3.0f;
+ it.out[3] = dummy + 4.0f;
+ }
+ },
+ [](float *out, Span<const float *> UNUSED(ins), const int x, const int y) {
+ const int dummy = y * BUFFER_WIDTH + x;
+ EXPECT_NEAR(out[0], dummy + 1.0f, FLT_EPSILON);
+ EXPECT_NEAR(out[1], dummy + 2.0f, FLT_EPSILON);
+ EXPECT_NEAR(out[2], dummy + 3.0f, FLT_EPSILON);
+ EXPECT_NEAR(out[3], dummy + 4.0f, FLT_EPSILON);
+ });
+}
+
+TEST_F(BuffersIteratorTest, OutputAndInputsIteration)
+{
+ set_inputs_enabled(true);
+ test_iteration(
+ [](BuffersIterator<float> &it, const rcti &UNUSED(area)) {
+ EXPECT_EQ(it.get_num_inputs(), NUM_INPUTS);
+ for (; !it.is_end(); ++it) {
+ const float *in1 = it.in(0);
+ const float *in2 = it.in(1);
+ it.out[0] = in1[0] + in2[0];
+ it.out[1] = in1[1] + in2[3];
+ it.out[2] = in1[2] - in2[2];
+ it.out[3] = in1[3] - in2[1];
+ }
+ },
+ [](float *out, Span<const float *> ins, const int UNUSED(x), const int UNUSED(y)) {
+ const float *in1 = ins[0];
+ const float *in2 = ins[1];
+ EXPECT_NEAR(out[0], in1[0] + in2[0], FLT_EPSILON);
+ EXPECT_NEAR(out[1], in1[1] + in2[3], FLT_EPSILON);
+ EXPECT_NEAR(out[2], in1[2] - in2[2], FLT_EPSILON);
+ EXPECT_NEAR(out[3], in1[3] - in2[1], FLT_EPSILON);
+ });
+}
+
+} // namespace blender::compositor::tests
diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c
index 2023260c168..ba7120daa92 100644
--- a/source/blender/datatoc/datatoc_icon.c
+++ b/source/blender/datatoc/datatoc_icon.c
@@ -172,8 +172,9 @@ static bool write_png(const char *name, const uint *pixels, const int width, con
/* set the individual row-pointers to point at the correct offsets */
for (i = 0; i < height; i++) {
- row_pointers[height - 1 - i] = (png_bytep)(
- ((const unsigned char *)pixels) + (i * width) * bytesperpixel * sizeof(unsigned char));
+ row_pointers[height - 1 - i] = (png_bytep)(((const unsigned char *)pixels) +
+ (i * width) * bytesperpixel *
+ sizeof(unsigned char));
}
/* write out the entire image data in one call */
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 42c9cccceed..c029d203574 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -33,6 +33,7 @@ struct Depsgraph;
/* ------------------------------------------------ */
struct CacheFile;
+struct Collection;
struct CustomData_MeshMasks;
struct ID;
struct Main;
@@ -40,7 +41,6 @@ struct Object;
struct Scene;
struct Simulation;
struct bNodeTree;
-struct Collection;
#include "BLI_sys_types.h"
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index e561d0b653c..8d1074d912f 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -1496,7 +1496,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry(Object *object, bool is_ob
add_operation_node(
&object->id,
NodeType::BATCH_CACHE,
- OperationCode::BATCH_UPDATE_SELECT,
+ OperationCode::GEOMETRY_SELECT_UPDATE,
[object_cow](::Depsgraph *depsgraph) { BKE_object_select_update(depsgraph, object_cow); });
}
@@ -1517,37 +1517,33 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool
if (key) {
build_shapekeys(key);
}
-
- /* Geometry evaluation. */
- /* Entry operation, takes care of initialization, and some other
- * relations which needs to be run prior to actual geometry evaluation. */
- op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
- op_node->set_as_entry();
-
- add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM);
-
+ /* Nodes for result of obdata's evaluation, and geometry
+ * evaluation on object. */
const ID_Type id_type = GS(obdata->name);
switch (id_type) {
case ID_ME: {
- add_operation_node(obdata,
- NodeType::GEOMETRY,
- OperationCode::GEOMETRY_EVAL,
- [obdata_cow](::Depsgraph *depsgraph) {
- BKE_mesh_eval_geometry(depsgraph, (Mesh *)obdata_cow);
- });
+ op_node = add_operation_node(obdata,
+ NodeType::GEOMETRY,
+ OperationCode::GEOMETRY_EVAL,
+ [obdata_cow](::Depsgraph *depsgraph) {
+ BKE_mesh_eval_geometry(depsgraph, (Mesh *)obdata_cow);
+ });
+ op_node->set_as_entry();
break;
}
case ID_MB: {
- add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ op_node->set_as_entry();
break;
}
case ID_CU: {
- add_operation_node(obdata,
- NodeType::GEOMETRY,
- OperationCode::GEOMETRY_EVAL,
- [obdata_cow](::Depsgraph *depsgraph) {
- BKE_curve_eval_geometry(depsgraph, (Curve *)obdata_cow);
- });
+ op_node = add_operation_node(obdata,
+ NodeType::GEOMETRY,
+ OperationCode::GEOMETRY_EVAL,
+ [obdata_cow](::Depsgraph *depsgraph) {
+ BKE_curve_eval_geometry(depsgraph, (Curve *)obdata_cow);
+ });
+ op_node->set_as_entry();
/* Make sure objects used for bevel.taper are in the graph.
* NOTE: This objects might be not linked to the scene. */
Curve *cu = (Curve *)obdata;
@@ -1563,41 +1559,47 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool
break;
}
case ID_LT: {
- add_operation_node(obdata,
- NodeType::GEOMETRY,
- OperationCode::GEOMETRY_EVAL,
- [obdata_cow](::Depsgraph *depsgraph) {
- BKE_lattice_eval_geometry(depsgraph, (Lattice *)obdata_cow);
- });
+ op_node = add_operation_node(obdata,
+ NodeType::GEOMETRY,
+ OperationCode::GEOMETRY_EVAL,
+ [obdata_cow](::Depsgraph *depsgraph) {
+ BKE_lattice_eval_geometry(depsgraph, (Lattice *)obdata_cow);
+ });
+ op_node->set_as_entry();
break;
}
case ID_GD: {
/* GPencil evaluation operations. */
- add_operation_node(obdata,
- NodeType::GEOMETRY,
- OperationCode::GEOMETRY_EVAL,
- [obdata_cow](::Depsgraph *depsgraph) {
- BKE_gpencil_frame_active_set(depsgraph, (bGPdata *)obdata_cow);
- });
+ op_node = add_operation_node(obdata,
+ NodeType::GEOMETRY,
+ OperationCode::GEOMETRY_EVAL,
+ [obdata_cow](::Depsgraph *depsgraph) {
+ BKE_gpencil_frame_active_set(depsgraph,
+ (bGPdata *)obdata_cow);
+ });
+ op_node->set_as_entry();
break;
}
case ID_HA: {
- add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ op_node->set_as_entry();
break;
}
case ID_PT: {
- add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ op_node->set_as_entry();
break;
}
case ID_VO: {
/* Volume frame update. */
- add_operation_node(obdata,
- NodeType::GEOMETRY,
- OperationCode::GEOMETRY_EVAL,
- [obdata_cow](::Depsgraph *depsgraph) {
- BKE_volume_eval_geometry(depsgraph, (Volume *)obdata_cow);
- });
+ op_node = add_operation_node(obdata,
+ NodeType::GEOMETRY,
+ OperationCode::GEOMETRY_EVAL,
+ [obdata_cow](::Depsgraph *depsgraph) {
+ BKE_volume_eval_geometry(depsgraph, (Volume *)obdata_cow);
+ });
+ op_node->set_as_entry();
break;
}
default:
@@ -1611,22 +1613,10 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool
/* Batch cache. */
add_operation_node(obdata,
NodeType::BATCH_CACHE,
- OperationCode::BATCH_UPDATE_SELECT,
+ OperationCode::GEOMETRY_SELECT_UPDATE,
[obdata_cow](::Depsgraph *depsgraph) {
BKE_object_data_select_update(depsgraph, obdata_cow);
});
- add_operation_node(obdata,
- NodeType::BATCH_CACHE,
- OperationCode::BATCH_UPDATE_DEFORM,
- [obdata_cow](::Depsgraph *depsgraph) {
- BKE_object_data_eval_batch_cache_deform_tag(depsgraph, obdata_cow);
- });
- add_operation_node(obdata,
- NodeType::BATCH_CACHE,
- OperationCode::BATCH_UPDATE_ALL,
- [obdata_cow](::Depsgraph *depsgraph) {
- BKE_object_data_eval_batch_cache_dirty_tag(depsgraph, obdata_cow);
- });
}
void DepsgraphNodeBuilder::build_armature(bArmature *armature)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 415145c8fa1..c7c6fafa512 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -647,7 +647,7 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll
/* Only create geometry relations to child objects, if they have a geometry component. */
OperationKey object_geometry_key{
- &cob->ob->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT};
+ &cob->ob->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL};
if (find_node(object_geometry_key) != nullptr) {
add_relation(object_geometry_key, collection_geometry_key, "Collection Geometry");
}
@@ -1098,8 +1098,7 @@ void DepsgraphRelationBuilder::build_object_pointcache(Object *object)
}
else {
flag = FLAG_GEOMETRY;
- OperationKey geometry_key(
- &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
+ OperationKey geometry_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
add_relation(point_cache_key, geometry_key, "Point Cache -> Geometry");
}
BLI_assert(flag != -1);
@@ -1869,8 +1868,7 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
void DepsgraphRelationBuilder::build_particle_systems(Object *object)
{
TimeSourceKey time_src_key;
- OperationKey obdata_ubereval_key(
- &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
+ OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
OperationKey eval_init_key(
&object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_INIT);
OperationKey eval_done_key(
@@ -2018,8 +2016,7 @@ void DepsgraphRelationBuilder::build_particle_system_visualization_object(Object
{
OperationKey psys_key(
&object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_EVAL, psys->name);
- OperationKey obdata_ubereval_key(
- &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
+ OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
ComponentKey dup_ob_key(&draw_object->id, NodeType::TRANSFORM);
add_relation(dup_ob_key, psys_key, "Particle Object Visualization");
if (draw_object->type == OB_MBALL) {
@@ -2076,15 +2073,15 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
/* Get nodes for result of obdata's evaluation, and geometry evaluation
* on object. */
ComponentKey obdata_geom_key(obdata, NodeType::GEOMETRY);
- OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ ComponentKey geom_key(&object->id, NodeType::GEOMETRY);
/* Link components to each other. */
- add_relation(obdata_geom_key, obdata_ubereval_key, "Object Geometry Base Data");
-
+ add_relation(obdata_geom_key, geom_key, "Object Geometry Base Data");
+ OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
/* Special case: modifiers evaluation queries scene for various things like
* data mask to be used. We add relation here to ensure object is never
* evaluated prior to Scene's CoW is ready. */
OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL);
- Relation *rel = add_relation(scene_key, geom_init_key, "CoW Relation");
+ Relation *rel = add_relation(scene_key, obdata_ubereval_key, "CoW Relation");
rel->flag |= RELATION_FLAG_NO_FLUSH;
/* Modifiers */
if (object->modifiers.first != nullptr) {
@@ -2094,13 +2091,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->updateDepsgraph) {
- DepsNodeHandle handle = create_node_handle(geom_init_key);
+ DepsNodeHandle handle = create_node_handle(obdata_ubereval_key);
ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
mti->updateDepsgraph(md, &ctx);
}
if (BKE_object_modifier_use_time(object, md)) {
TimeSourceKey time_src_key;
- add_relation(time_src_key, geom_init_key, "Time Source");
+ add_relation(time_src_key, obdata_ubereval_key, "Time Source");
}
}
}
@@ -2113,13 +2110,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
(GpencilModifierType)md->type);
if (mti->updateDepsgraph) {
- DepsNodeHandle handle = create_node_handle(geom_init_key);
+ DepsNodeHandle handle = create_node_handle(obdata_ubereval_key);
ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
mti->updateDepsgraph(md, &ctx, graph_->mode);
}
if (BKE_object_modifier_gpencil_use_time(object, md)) {
TimeSourceKey time_src_key;
- add_relation(time_src_key, geom_init_key, "Time Source");
+ add_relation(time_src_key, obdata_ubereval_key, "Time Source");
}
}
}
@@ -2131,13 +2128,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
LISTBASE_FOREACH (ShaderFxData *, fx, &object->shader_fx) {
const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info((ShaderFxType)fx->type);
if (fxi->updateDepsgraph) {
- DepsNodeHandle handle = create_node_handle(geom_init_key);
+ DepsNodeHandle handle = create_node_handle(obdata_ubereval_key);
ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
fxi->updateDepsgraph(fx, &ctx);
}
if (BKE_object_shaderfx_use_time(object, fx)) {
TimeSourceKey time_src_key;
- add_relation(time_src_key, geom_init_key, "Time Source");
+ add_relation(time_src_key, obdata_ubereval_key, "Time Source");
}
}
}
@@ -2163,7 +2160,6 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
add_relation(mom_transform_key, mom_geom_key, "Metaball Motherball Transform -> Geometry");
}
else {
- ComponentKey geom_key(&object->id, NodeType::GEOMETRY);
ComponentKey transform_key(&object->id, NodeType::TRANSFORM);
add_relation(geom_key, mom_geom_key, "Metaball Motherball");
add_relation(transform_key, mom_geom_key, "Metaball Motherball");
@@ -2178,7 +2174,9 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
* Ideally we need to get rid of this relation. */
if (object_particles_depends_on_time(object)) {
TimeSourceKey time_key;
- add_relation(time_key, geom_init_key, "Legacy particle time");
+ OperationKey obdata_ubereval_key(
+ &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ add_relation(time_key, obdata_ubereval_key, "Legacy particle time");
}
/* Object data data-block. */
build_object_data_geometry_datablock((ID *)object->data);
@@ -2200,33 +2198,12 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
add_relation(final_geometry_key, synchronize_key, "Synchronize to Original");
/* Batch cache. */
OperationKey object_data_select_key(
- obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_SELECT);
+ obdata, NodeType::BATCH_CACHE, OperationCode::GEOMETRY_SELECT_UPDATE);
OperationKey object_select_key(
- &object->id, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_SELECT);
-
+ &object->id, NodeType::BATCH_CACHE, OperationCode::GEOMETRY_SELECT_UPDATE);
add_relation(object_data_select_key, object_select_key, "Data Selection -> Object Selection");
- add_relation(final_geometry_key,
- object_select_key,
- "Object Geometry -> Select Update",
- RELATION_FLAG_NO_FLUSH);
-
- OperationKey object_data_geom_deform_key(
- obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM);
- OperationKey object_data_geom_init_key(
- obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
-
- OperationKey object_data_batch_deform_key(
- obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_DEFORM);
- OperationKey object_data_batch_all_key(
- obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_ALL);
-
- add_relation(geom_init_key, object_data_batch_all_key, "Object Geometry -> Batch Update All");
-
add_relation(
- object_data_geom_init_key, object_data_batch_all_key, "Data Init -> Batch Update All");
- add_relation(object_data_geom_deform_key,
- object_data_batch_deform_key,
- "Data Deform -> Batch Update Deform");
+ geom_key, object_select_key, "Object Geometry -> Select Update", RELATION_FLAG_NO_FLUSH);
}
void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
@@ -2244,13 +2221,8 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
build_shapekeys(key);
}
/* Link object data evaluation node to exit operation. */
- OperationKey obdata_geom_deform_key(
- obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM);
- OperationKey obdata_geom_init_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
OperationKey obdata_geom_eval_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
OperationKey obdata_geom_done_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE);
- add_relation(obdata_geom_init_key, obdata_geom_eval_key, "ObData Init -> Geom Eval");
- add_relation(obdata_geom_deform_key, obdata_geom_eval_key, "ObData Deform -> Geom Eval");
add_relation(obdata_geom_eval_key, obdata_geom_done_key, "ObData Geom Eval Done");
/* Type-specific links. */
const ID_Type id_type = GS(obdata->name);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
index 8e3960e1a15..bdabd67cc07 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
@@ -153,8 +153,8 @@ bool RNANodeQuery::contains(const char *prop_identifier, const char *rna_path_co
return false;
}
- // If substr != prop_identifier, it means that the substring is found further in prop_identifier,
- // and that thus index -1 is a valid memory location.
+ /* If substr != prop_identifier, it means that the substring is found further in prop_identifier,
+ * and that thus index -1 is a valid memory location. */
const bool start_ok = substr == prop_identifier || substr[-1] == '.';
if (!start_ok) {
return false;
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 34b33e9a6c0..ab93464d09a 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -117,7 +117,7 @@ void depsgraph_select_tag_to_component_opcode(const ID *id,
}
else if (is_selectable_data_id_type(id_type)) {
*component_type = NodeType::BATCH_CACHE;
- *operation_code = OperationCode::BATCH_UPDATE_SELECT;
+ *operation_code = OperationCode::GEOMETRY_SELECT_UPDATE;
}
else {
*component_type = NodeType::COPY_ON_WRITE;
@@ -168,11 +168,6 @@ void depsgraph_tag_to_component_opcode(const ID *id,
break;
case ID_RECALC_GEOMETRY:
depsgraph_geometry_tag_to_component(id, component_type);
- *operation_code = OperationCode::GEOMETRY_EVAL_INIT;
- break;
- case ID_RECALC_GEOMETRY_DEFORM:
- depsgraph_geometry_tag_to_component(id, component_type);
- *operation_code = OperationCode::GEOMETRY_EVAL_DEFORM;
break;
case ID_RECALC_ANIMATION:
*component_type = NodeType::ANIMATION;
@@ -713,8 +708,6 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag)
return "GEOMETRY";
case ID_RECALC_GEOMETRY_ALL_MODES:
return "GEOMETRY_ALL_MODES";
- case ID_RECALC_GEOMETRY_DEFORM:
- return "GEOMETRY_DEFORM";
case ID_RECALC_ANIMATION:
return "ANIMATION";
case ID_RECALC_PSYS_REDO:
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 915b9fedcec..ad88cf656ad 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -103,7 +103,7 @@ void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_nod
::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(state->graph);
/* Sanity checks. */
- BLI_assert(!operation_node->is_noop() && "NOOP nodes should not actually be scheduled");
+ BLI_assert_msg(!operation_node->is_noop(), "NOOP nodes should not actually be scheduled");
/* Perform operation. */
if (state->do_stats) {
const double start_time = PIL_check_seconds_timer();
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 346eba5bbc2..a844d23b558 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
@@ -348,7 +348,7 @@ bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene)
/* For the given scene get view layer which corresponds to an original for the
* scene's evaluated one. This depends on how the scene is pulled into the
- * dependency graph. */
+ * dependency graph. */
ViewLayer *get_original_view_layer(const Depsgraph *depsgraph, const IDNode *id_node)
{
if (id_node->linked_state == DEG_ID_LINKED_DIRECTLY) {
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
index 2cbb0b52e34..a015491e2d7 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
@@ -58,15 +58,15 @@
#include "intern/eval/deg_eval_copy_on_write.h"
-// Invalidate data-block data when update is flushed on it.
-//
-// The idea of this is to help catching cases when area is accessing data which
-// is not yet evaluated, which could happen due to missing relations. The issue
-// is that usually that data will be kept from previous frame, and it looks to
-// be plausible.
-//
-// This ensures that data does not look plausible, making it much easier to
-// catch usage of invalid state.
+/* Invalidate data-block data when update is flushed on it.
+ *
+ * The idea of this is to help catching cases when area is accessing data which
+ * is not yet evaluated, which could happen due to missing relations. The issue
+ * is that usually that data will be kept from previous frame, and it looks to
+ * be plausible.
+ *
+ * This ensures that data does not look plausible, making it much easier to
+ * catch usage of invalid state. */
#undef INVALIDATE_ON_FLUSH
namespace blender::deg {
@@ -144,10 +144,7 @@ inline void flush_handle_component_node(IDNode *id_node,
* special component where we don't want all operations to be tagged.
*
* TODO(sergey): Make this a more generic solution. */
- if (!ELEM(comp_node->type,
- NodeType::PARTICLE_SETTINGS,
- NodeType::PARTICLE_SYSTEM,
- NodeType::BATCH_CACHE)) {
+ if (!ELEM(comp_node->type, NodeType::PARTICLE_SETTINGS, NodeType::PARTICLE_SYSTEM)) {
for (OperationNode *op : comp_node->operations) {
op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
}
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc
index d98486b83a8..c25dc6fc8d5 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc
@@ -98,8 +98,6 @@ const char *operationCodeAsString(OperationCode opcode)
/* Geometry. */
case OperationCode::GEOMETRY_EVAL_INIT:
return "GEOMETRY_EVAL_INIT";
- case OperationCode::GEOMETRY_EVAL_DEFORM:
- return "GEOMETRY_EVAL_DEFORM";
case OperationCode::GEOMETRY_EVAL:
return "GEOMETRY_EVAL";
case OperationCode::GEOMETRY_EVAL_DONE:
@@ -162,12 +160,8 @@ const char *operationCodeAsString(OperationCode opcode)
case OperationCode::FILE_CACHE_UPDATE:
return "FILE_CACHE_UPDATE";
/* Batch cache. */
- case OperationCode::BATCH_UPDATE_SELECT:
- return "BATCH_UPDATE_SELECT";
- case OperationCode::BATCH_UPDATE_DEFORM:
- return "BATCH_UPDATE_DEFORM";
- case OperationCode::BATCH_UPDATE_ALL:
- return "BATCH_UPDATE_ALL";
+ case OperationCode::GEOMETRY_SELECT_UPDATE:
+ return "GEOMETRY_SELECT_UPDATE";
/* Masks. */
case OperationCode::MASK_ANIMATION:
return "MASK_ANIMATION";
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h
index b0130d03c69..a17186da941 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.h
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.h
@@ -100,11 +100,7 @@ enum class OperationCode {
/* Initialize evaluation of the geometry. Is an entry operation of geometry
* component. */
GEOMETRY_EVAL_INIT,
- /* Evaluate the geometry, including modifiers, and update only batches that
- * are affected by deform operations. */
- GEOMETRY_EVAL_DEFORM,
- /* Evaluate the geometry, including modifiers, but don't update the batch
- * cache. */
+ /* Evaluate the whole geometry, including modifiers. */
GEOMETRY_EVAL,
/* Evaluation of geometry is completely done. */
GEOMETRY_EVAL_DONE,
@@ -182,9 +178,7 @@ enum class OperationCode {
WORLD_UPDATE,
/* Batch caches. -------------------------------------------------------- */
- BATCH_UPDATE_SELECT,
- BATCH_UPDATE_DEFORM,
- BATCH_UPDATE_ALL,
+ GEOMETRY_SELECT_UPDATE,
/* Masks. --------------------------------------------------------------- */
MASK_ANIMATION,
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index ad154704d2c..930d82fa225 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -52,8 +52,8 @@ set(INC
set(SRC
intern/draw_cache.c
intern/draw_cache_extract_mesh.cc
- intern/draw_cache_extract_mesh_extractors.c
intern/draw_cache_extract_mesh_render_data.c
+ intern/mesh_extractors/extract_mesh.c
intern/mesh_extractors/extract_mesh_ibo_edituv.cc
intern/mesh_extractors/extract_mesh_ibo_fdots.cc
intern/mesh_extractors/extract_mesh_ibo_lines.cc
@@ -193,7 +193,6 @@ set(SRC
intern/DRW_render.h
intern/draw_cache.h
intern/draw_cache_extract.h
- intern/draw_cache_extract_mesh_private.h
intern/draw_cache_impl.h
intern/draw_cache_inline.h
intern/draw_color_management.h
@@ -207,6 +206,7 @@ set(SRC
intern/draw_manager_text.h
intern/draw_shader.h
intern/draw_view.h
+ intern/mesh_extractors/extract_mesh.h
intern/smaa_textures.h
engines/basic/basic_engine.h
engines/eevee/eevee_engine.h
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 7fe984b4397..76a1b561972 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -158,9 +158,9 @@ void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
const ViewLayer *view_layer = draw_ctx->view_layer;
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
- eGPUTextureFormat format = (num_cryptomatte_layers == 1) ?
- GPU_R32F :
- (num_cryptomatte_layers == 2) ? GPU_RG32F : GPU_RGBA32F;
+ eGPUTextureFormat format = (num_cryptomatte_layers == 1) ? GPU_R32F :
+ (num_cryptomatte_layers == 2) ? GPU_RG32F :
+ GPU_RGBA32F;
const float *viewport_size = DRW_viewport_size_get();
const int buffer_size = viewport_size[0] * viewport_size[1];
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 9d4f7865c32..6a66e8b1a58 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -178,19 +178,19 @@ static void eevee_cache_finish(void *vedata)
}
EEVEE_renderpasses_output_init(sldata, vedata, tot_samples);
- /* Restart taa if a shader has finish compiling. */
- /* HACK We should use notification of some sort from the compilation job instead. */
+ /* Restart TAA if a shader has finish compiling. */
+ /* HACK: We should use notification of some sort from the compilation job instead. */
if (g_data->queued_shaders_count != g_data->queued_shaders_count_prev) {
g_data->queued_shaders_count_prev = g_data->queued_shaders_count;
EEVEE_temporal_sampling_reset(vedata);
}
}
-/* As renders in an HDR offscreen buffer, we need draw everything once
+/* As renders in an HDR off-screen buffer, we need draw everything once
* during the background pass. This way the other drawing callback between
* the background and the scene pass are visible.
* NOTE: we could break it up in two passes using some depth test
- * to reduce the fillrate */
+ * to reduce the fill-rate. */
static void eevee_draw_scene(void *vedata)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index d214409c458..1484a480f80 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -712,15 +712,15 @@ void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *v
CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
- CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c
index b7bcd127859..48c24d138e6 100644
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.c
@@ -192,7 +192,7 @@ void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata,
gpumat, stl->effects->sss_sample_count, &sss_tex_profile);
if (!sss_profile) {
- BLI_assert(0 && "SSS pass requested but no SSS data was found");
+ BLI_assert_msg(0, "SSS pass requested but no SSS data was found");
return;
}
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index 47e8aeeb6e2..ac9abcca16b 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -394,10 +394,9 @@ static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWS
* - Grid exists and texture was loaded -> use texture.
* - Grid exists but has zero size or failed to load -> use zero.
* - Grid does not exist -> use default value. */
- GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture :
- (volume_grid) ?
- e_data.dummy_zero :
- eevee_volume_default_texture(gpu_grid->default_value);
+ GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture :
+ (volume_grid) ? e_data.dummy_zero :
+ eevee_volume_default_texture(gpu_grid->default_value);
DRW_shgroup_uniform_texture(grp, gpu_grid->sampler_name, grid_tex);
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index 8a2aebc95ab..dbe0f27f883 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -283,11 +283,11 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
!BLI_listbase_is_empty(&gpl->mask_layers);
float vert_col_opacity = (override_vertcol) ?
- (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
- pd->is_render ? gpl->vertex_paint_opacity :
- pd->vertex_paint_opacity;
+ (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
+ pd->is_render ? gpl->vertex_paint_opacity :
+ pd->vertex_paint_opacity;
/* Negate thickness sign to tag that strokes are in screen space.
- * Convert to world units (by default, 1 meter = 2000 px). */
+ * Convert to world units (by default, 1 meter = 2000 pixels). */
float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / GPENCIL_PIXEL_FACTOR);
float layer_opacity = gpencil_layer_final_opacity_get(pd, ob, gpl);
float layer_tint[4];
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
index b1368f90846..36a52e05a4a 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
@@ -327,7 +327,7 @@ vec2 safe_normalize_len(vec2 v, out float len)
float stroke_thickness_modulate(float thickness)
{
- /* Modify stroke thickness by object and layer factors.-*/
+ /* Modify stroke thickness by object and layer factors. */
thickness *= thicknessScale;
thickness += thicknessOffset;
thickness = max(1.0, thickness);
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c
index 9d15f0e176d..1da682ff01b 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.c
@@ -1294,10 +1294,9 @@ static void draw_axes(ArmatureDrawContext *ctx,
const bArmature *arm)
{
float final_col[4];
- const float *col = (ctx->const_color) ?
- ctx->const_color :
- (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? G_draw.block.colorTextHi :
- G_draw.block.colorText;
+ const float *col = (ctx->const_color) ? ctx->const_color :
+ (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? G_draw.block.colorTextHi :
+ G_draw.block.colorText;
copy_v4_v4(final_col, col);
/* Mix with axes color. */
final_col[3] = (ctx->const_color) ? 1.0 : (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? 0.1 : 0.65;
diff --git a/source/blender/draw/engines/overlay/overlay_outline.c b/source/blender/draw/engines/overlay/overlay_outline.c
index e3f01d968ae..03d70a37519 100644
--- a/source/blender/draw/engines/overlay/overlay_outline.c
+++ b/source/blender/draw/engines/overlay/overlay_outline.c
@@ -196,7 +196,7 @@ static void gpencil_layer_cache_populate(bGPDlayer *gpl,
float object_scale = mat4_to_scale(iter->ob->obmat);
/* Negate thickness sign to tag that strokes are in screen space.
- * Convert to world units (by default, 1 meter = 2000 px). */
+ * Convert to world units (by default, 1 meter = 2000 pixels). */
float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / 2000.0f);
DRWShadingGroup *grp = iter->stroke_grp = DRW_shgroup_create_sub(iter->stroke_grp);
@@ -340,7 +340,7 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
if (shgroup && geom) {
if (ob->type == OB_POINTCLOUD) {
- /* Draw range to avoid drawcall batching messing up the instance attrib. */
+ /* Draw range to avoid drawcall batching messing up the instance attribute. */
DRW_shgroup_call_instance_range(shgroup, ob, geom, 0, 0);
}
else {
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index e9d6763fbe9..f09c019ef8d 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -128,7 +128,7 @@ static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd,
BLI_INLINE void workbench_object_drawcall(DRWShadingGroup *grp, struct GPUBatch *geom, Object *ob)
{
if (ob->type == OB_POINTCLOUD) {
- /* Draw range to avoid drawcall batching messing up the instance attrib. */
+ /* Draw range to avoid drawcall batching messing up the instance attribute. */
DRW_shgroup_call_instance_range(grp, ob, geom, 0, 0);
}
else {
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index ff3af9b28d1..1292eea4c12 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -91,7 +91,7 @@ typedef struct BoundSphere {
typedef char DRWViewportEmptyList;
#define DRW_VIEWPORT_LIST_SIZE(list) \
- (sizeof(list) == sizeof(DRWViewportEmptyList) ? 0 : ((sizeof(list)) / sizeof(void *)))
+ (sizeof(list) == sizeof(DRWViewportEmptyList) ? 0 : (sizeof(list) / sizeof(void *)))
/* Unused members must be either pass list or 'char *' when not used. */
#define DRW_VIEWPORT_DATA_SIZE(ty) \
@@ -195,15 +195,12 @@ void DRW_texture_free(struct GPUTexture *tex);
/* Shaders */
-#ifndef __GPU_MATERIAL_H__
-/* FIXME: Meh avoid including all GPUMaterial. */
typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat,
int options,
const char **vert_code,
const char **geom_code,
const char **frag_lib,
const char **defines);
-#endif
struct GPUShader *DRW_shader_create_ex(
const char *vert, const char *geom, const char *frag, const char *defines, const char *name);
@@ -327,7 +324,7 @@ typedef enum {
/** Culling test */
DRW_STATE_CULL_BACK = (1 << 7),
DRW_STATE_CULL_FRONT = (1 << 8),
- /** Stencil test . These options are mutually exclusive and packed into 2 bits. */
+ /** Stencil test. These options are mutually exclusive and packed into 2 bits. */
DRW_STATE_STENCIL_ALWAYS = (1 << 9),
DRW_STATE_STENCIL_EQUAL = (2 << 9),
DRW_STATE_STENCIL_NEQUAL = (3 << 9),
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index a2e8dc20907..000ab540813 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -770,9 +770,9 @@ GPUBatch *DRW_cache_normal_arrow_get(void)
}
/* -------------------------------------------------------------------- */
-/** \name Dummy vbos
+/** \name Dummy VBO's
*
- * We need a dummy vbo containing the vertex count to draw instances ranges.
+ * We need a dummy VBO containing the vertex count to draw instances ranges.
*
* \{ */
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index a0694a08f0b..7dc468d1a73 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -81,11 +81,12 @@ typedef enum eMRDataType {
MR_DATA_POLY_NOR = 1 << 1,
MR_DATA_LOOP_NOR = 1 << 2,
MR_DATA_LOOPTRI = 1 << 3,
+ MR_DATA_LOOSE_GEOM = 1 << 4,
/** Force loop normals calculation. */
- MR_DATA_TAN_LOOP_NOR = 1 << 4,
- MR_DATA_MAT_OFFSETS = 1 << 5,
+ MR_DATA_TAN_LOOP_NOR = 1 << 5,
+ MR_DATA_POLYS_SORTED = 1 << 6,
} eMRDataType;
-ENUM_OPERATORS(eMRDataType, MR_DATA_MAT_OFFSETS)
+ENUM_OPERATORS(eMRDataType, MR_DATA_POLYS_SORTED)
#ifdef __cplusplus
extern "C" {
@@ -169,10 +170,10 @@ typedef struct MeshBufferExtractionCache {
} loose_geom;
struct {
- int *tri;
+ int *tri_first_index;
+ int *mat_tri_len;
int visible_tri_len;
- } mat_offsets;
-
+ } poly_sorted;
} MeshBufferExtractionCache;
#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
index 344150014ed..1bc2b8a9def 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -41,9 +41,10 @@
#include "GPU_capabilities.h"
#include "draw_cache_extract.h"
-#include "draw_cache_extract_mesh_private.h"
#include "draw_cache_inline.h"
+#include "mesh_extractors/extract_mesh.h"
+
// #define DEBUG_TIME
#ifdef DEBUG_TIME
@@ -76,27 +77,23 @@ struct ExtractorRunData {
class ExtractorRunDatas : public Vector<ExtractorRunData> {
public:
- void filter_into(ExtractorRunDatas &result, eMRIterType iter_type) const
+ void filter_into(ExtractorRunDatas &result, eMRIterType iter_type, const bool is_mesh) const
{
for (const ExtractorRunData &data : *this) {
const MeshExtract *extractor = data.extractor;
- if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) {
- BLI_assert(extractor->iter_looptri_mesh);
+ if ((iter_type & MR_ITER_LOOPTRI) && *(&extractor->iter_looptri_bm + is_mesh)) {
result.append(data);
continue;
}
- if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) {
- BLI_assert(extractor->iter_poly_mesh);
+ if ((iter_type & MR_ITER_POLY) && *(&extractor->iter_poly_bm + is_mesh)) {
result.append(data);
continue;
}
- if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) {
- BLI_assert(extractor->iter_ledge_mesh);
+ if ((iter_type & MR_ITER_LEDGE) && *(&extractor->iter_ledge_bm + is_mesh)) {
result.append(data);
continue;
}
- if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) {
- BLI_assert(extractor->iter_lvert_mesh);
+ if ((iter_type & MR_ITER_LVERT) && *(&extractor->iter_lvert_bm + is_mesh)) {
result.append(data);
continue;
}
@@ -427,7 +424,7 @@ BLI_INLINE void extract_task_range_run_iter(const MeshRenderData *mr,
return;
}
- extractors->filter_into(range_data.extractors, iter_type);
+ extractors->filter_into(range_data.extractors, iter_type, is_mesh);
BLI_task_parallel_range(0, stop, &range_data, func, settings);
}
@@ -535,7 +532,8 @@ static void mesh_extract_render_data_node_exec(void *__restrict task_data)
mesh_render_data_update_normals(mr, data_flag);
mesh_render_data_update_looptris(mr, iter_type, data_flag);
- mesh_render_data_update_mat_offsets(mr, update_task_data->cache, data_flag);
+ mesh_render_data_update_loose_geom(mr, update_task_data->cache, iter_type, data_flag);
+ mesh_render_data_update_polys_sorted(mr, update_task_data->cache, data_flag);
}
static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
@@ -689,19 +687,8 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
double rdata_start = PIL_check_seconds_timer();
#endif
- eMRIterType iter_type = extractors.iter_types();
- eMRDataType data_flag = extractors.data_types();
-
- MeshRenderData *mr = mesh_render_data_create(me,
- extraction_cache,
- is_editmode,
- is_paint_mode,
- is_mode_active,
- obmat,
- do_final,
- do_uvedit,
- ts,
- iter_type);
+ MeshRenderData *mr = mesh_render_data_create(
+ 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;
@@ -710,6 +697,9 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
double rdata_end = PIL_check_seconds_timer();
#endif
+ eMRIterType iter_type = extractors.iter_types();
+ eMRDataType data_flag = extractors.data_types();
+
struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
task_graph, mr, extraction_cache, iter_type, data_flag);
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 bccf894cc53..27fd6ca9134 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
@@ -25,6 +25,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
#include "BLI_bitmap.h"
#include "BLI_math.h"
#include "BLI_task.h"
@@ -37,7 +38,7 @@
#include "ED_mesh.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "mesh_extractors/extract_mesh.h"
/* ---------------------------------------------------------------------- */
/** \name Update Loose Geometry
@@ -165,119 +166,118 @@ static void mesh_render_data_ledges_bm(const MeshRenderData *mr,
}
}
+void mesh_render_data_update_loose_geom(MeshRenderData *mr,
+ MeshBufferExtractionCache *cache,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag)
+{
+ if ((iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) || (data_flag & MR_DATA_LOOSE_GEOM)) {
+ mesh_render_data_loose_geom_ensure(mr, cache);
+ mesh_render_data_loose_geom_load(mr, cache);
+ }
+}
+
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Material Offsets
+/** \name Polygons sorted per material
*
- * Material offsets contains the offset of a material after sorting tris based on their material.
+ * Contains polygon indices sorted based on their material.
*
* \{ */
-static void mesh_render_data_mat_offset_load(MeshRenderData *mr,
- const MeshBufferExtractionCache *cache);
-static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr,
- MeshBufferExtractionCache *cache);
-static void mesh_render_data_mat_offset_build(MeshRenderData *mr,
- MeshBufferExtractionCache *cache);
-static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr,
+static void mesh_render_data_polys_sorted_load(MeshRenderData *mr,
+ const MeshBufferExtractionCache *cache);
+static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr,
MeshBufferExtractionCache *cache);
-static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr,
- MeshBufferExtractionCache *cache);
-static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr,
- MeshBufferExtractionCache *cache);
-
-void mesh_render_data_update_mat_offsets(MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
- const eMRDataType data_flag)
+static void mesh_render_data_polys_sorted_build(MeshRenderData *mr,
+ MeshBufferExtractionCache *cache);
+static int *mesh_render_data_mat_tri_len_build(MeshRenderData *mr);
+
+void mesh_render_data_update_polys_sorted(MeshRenderData *mr,
+ MeshBufferExtractionCache *cache,
+ const eMRDataType data_flag)
{
- if (data_flag & MR_DATA_MAT_OFFSETS) {
- mesh_render_data_mat_offset_ensure(mr, cache);
- mesh_render_data_mat_offset_load(mr, cache);
+ if (data_flag & MR_DATA_POLYS_SORTED) {
+ mesh_render_data_polys_sorted_ensure(mr, cache);
+ mesh_render_data_polys_sorted_load(mr, cache);
}
}
-static void mesh_render_data_mat_offset_load(MeshRenderData *mr,
- const MeshBufferExtractionCache *cache)
+static void mesh_render_data_polys_sorted_load(MeshRenderData *mr,
+ const MeshBufferExtractionCache *cache)
{
- mr->mat_offsets.tri = cache->mat_offsets.tri;
- mr->mat_offsets.visible_tri_len = cache->mat_offsets.visible_tri_len;
+ mr->poly_sorted.tri_first_index = cache->poly_sorted.tri_first_index;
+ mr->poly_sorted.mat_tri_len = cache->poly_sorted.mat_tri_len;
+ mr->poly_sorted.visible_tri_len = cache->poly_sorted.visible_tri_len;
}
-static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr,
+ MeshBufferExtractionCache *cache)
{
- if (cache->mat_offsets.tri) {
+ if (cache->poly_sorted.tri_first_index) {
return;
}
- mesh_render_data_mat_offset_build(mr, cache);
+ mesh_render_data_polys_sorted_build(mr, cache);
}
-static void mesh_render_data_mat_offset_build(MeshRenderData *mr, MeshBufferExtractionCache *cache)
+static void mesh_render_data_polys_sorted_build(MeshRenderData *mr,
+ MeshBufferExtractionCache *cache)
{
- size_t mat_tri_idx_size = sizeof(int) * mr->mat_len;
- cache->mat_offsets.tri = MEM_callocN(mat_tri_idx_size, __func__);
+ int *tri_first_index = MEM_mallocN(sizeof(*tri_first_index) * mr->poly_len, __func__);
+ int *mat_tri_len = mesh_render_data_mat_tri_len_build(mr);
+
+ /* Apply offset. */
+ int visible_tri_len = 0;
+ int *mat_tri_offs = BLI_array_alloca(mat_tri_offs, mr->mat_len);
+ {
+ for (int i = 0; i < mr->mat_len; i++) {
+ mat_tri_offs[i] = visible_tri_len;
+ visible_tri_len += mat_tri_len[i];
+ }
+ }
- /* Count how many triangles for each material. */
+ /* Sort per material. */
+ int mat_last = mr->mat_len - 1;
if (mr->extract_type == MR_EXTRACT_BMESH) {
- mesh_render_data_mat_offset_build_bm(mr, cache);
+ BMIter iter;
+ BMFace *f;
+ int i;
+ BM_ITER_MESH_INDEX (f, &iter, mr->bm, BM_FACES_OF_MESH, i) {
+ if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ const int mat = min_ii(f->mat_nr, mat_last);
+ tri_first_index[i] = mat_tri_offs[mat];
+ mat_tri_offs[mat] += f->len - 2;
+ }
+ else {
+ tri_first_index[i] = -1;
+ }
+ }
}
else {
- mesh_render_data_mat_offset_build_mesh(mr, cache);
- }
-
- mesh_render_data_mat_offset_apply_offset(mr, cache);
-}
-
-typedef struct MatOffsetUserData {
- MeshRenderData *mr;
- /** This struct is extended during allocation to hold mat_tri_len for each material. */
- int mat_tri_len[0];
-} MatOffsetUserData;
-
-static void mesh_render_data_mat_offset_reduce(const void *__restrict UNUSED(userdata),
- void *__restrict chunk_join,
- void *__restrict chunk)
-{
- MatOffsetUserData *dst = chunk_join;
- MatOffsetUserData *src = chunk;
- int *dst_mat_len = dst->mat_tri_len;
- int *src_mat_len = src->mat_tri_len;
- for (int i = 0; i < dst->mr->mat_len; i++) {
- dst_mat_len[i] += src_mat_len[i];
+ const MPoly *mp = &mr->mpoly[0];
+ for (int i = 0; i < mr->poly_len; i++, mp++) {
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ const int mat = min_ii(mp->mat_nr, mat_last);
+ tri_first_index[i] = mat_tri_offs[mat];
+ mat_tri_offs[mat] += mp->totloop - 2;
+ }
+ else {
+ tri_first_index[i] = -1;
+ }
+ }
}
-}
-static void mesh_render_data_mat_offset_build_threaded(MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
- int face_len,
- TaskParallelRangeFunc range_func)
-{
- /* Extending the #MatOffsetUserData with an int per material slot. */
- size_t userdata_size = sizeof(MatOffsetUserData) +
- (mr->mat_len) * sizeof(*cache->mat_offsets.tri);
- MatOffsetUserData *userdata = MEM_callocN(userdata_size, __func__);
- userdata->mr = mr;
- TaskParallelSettings settings;
- BLI_parallel_range_settings_defaults(&settings);
- settings.userdata_chunk = userdata;
- settings.userdata_chunk_size = userdata_size;
- settings.min_iter_per_thread = MIN_RANGE_LEN;
- settings.func_reduce = mesh_render_data_mat_offset_reduce;
- BLI_task_parallel_range(0, face_len, NULL, range_func, &settings);
-
- memcpy(cache->mat_offsets.tri,
- &userdata->mat_tri_len,
- (mr->mat_len) * sizeof(*cache->mat_offsets.tri));
- MEM_freeN(userdata);
+ cache->poly_sorted.tri_first_index = tri_first_index;
+ cache->poly_sorted.mat_tri_len = mat_tri_len;
+ cache->poly_sorted.visible_tri_len = visible_tri_len;
}
-static void mesh_render_data_mat_offset_bm_range(void *__restrict UNUSED(userdata),
- const int iter,
- const TaskParallelTLS *__restrict tls)
+static void mesh_render_data_mat_tri_len_bm_range_fn(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
{
- MatOffsetUserData *mat_offset_userdata = tls->userdata_chunk;
- MeshRenderData *mr = mat_offset_userdata->mr;
- int *mat_tri_len = mat_offset_userdata->mat_tri_len;
+ MeshRenderData *mr = userdata;
+ int *mat_tri_len = tls->userdata_chunk;
BMesh *bm = mr->bm;
BMFace *efa = BM_face_at_index(bm, iter);
@@ -287,21 +287,12 @@ static void mesh_render_data_mat_offset_bm_range(void *__restrict UNUSED(userdat
}
}
-static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static void mesh_render_data_mat_tri_len_mesh_range_fn(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
{
- BMesh *bm = mr->bm;
- mesh_render_data_mat_offset_build_threaded(
- mr, cache, bm->totface, mesh_render_data_mat_offset_bm_range);
-}
-
-static void mesh_render_data_mat_offset_mesh_range(void *__restrict UNUSED(userdata),
- const int iter,
- const TaskParallelTLS *__restrict tls)
-{
- MatOffsetUserData *mat_offset_userdata = tls->userdata_chunk;
- const MeshRenderData *mr = mat_offset_userdata->mr;
- int *mat_tri_len = mat_offset_userdata->mat_tri_len;
+ MeshRenderData *mr = userdata;
+ int *mat_tri_len = tls->userdata_chunk;
const MPoly *mp = &mr->mpoly[iter];
if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
@@ -310,25 +301,47 @@ static void mesh_render_data_mat_offset_mesh_range(void *__restrict UNUSED(userd
}
}
-static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static void mesh_render_data_mat_tri_len_reduce_fn(const void *__restrict userdata,
+ void *__restrict chunk_join,
+ void *__restrict chunk)
{
- mesh_render_data_mat_offset_build_threaded(
- mr, cache, mr->poly_len, mesh_render_data_mat_offset_mesh_range);
+ const MeshRenderData *mr = userdata;
+ int *dst_mat_len = chunk_join;
+ int *src_mat_len = chunk;
+ for (int i = 0; i < mr->mat_len; i++) {
+ dst_mat_len[i] += src_mat_len[i];
+ }
}
-static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static int *mesh_render_data_mat_tri_len_build_threaded(MeshRenderData *mr,
+ int face_len,
+ TaskParallelRangeFunc range_func)
{
- int *mat_tri_len = cache->mat_offsets.tri;
- int ofs = mat_tri_len[0];
- mat_tri_len[0] = 0;
- for (int i = 1; i < mr->mat_len; i++) {
- int tmp = mat_tri_len[i];
- mat_tri_len[i] = ofs;
- ofs += tmp;
+ /* Extending the #MatOffsetUserData with an int per material slot. */
+ size_t mat_tri_len_size = sizeof(int) * mr->mat_len;
+ int *mat_tri_len = MEM_callocN(mat_tri_len_size, __func__);
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.userdata_chunk = mat_tri_len;
+ settings.userdata_chunk_size = mat_tri_len_size;
+ settings.min_iter_per_thread = MIN_RANGE_LEN;
+ settings.func_reduce = mesh_render_data_mat_tri_len_reduce_fn;
+ BLI_task_parallel_range(0, face_len, mr, range_func, &settings);
+
+ return mat_tri_len;
+}
+
+/* Count how many triangles for each material. */
+static int *mesh_render_data_mat_tri_len_build(MeshRenderData *mr)
+{
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMesh *bm = mr->bm;
+ return mesh_render_data_mat_tri_len_build_threaded(
+ mr, bm->totface, mesh_render_data_mat_tri_len_bm_range_fn);
}
- cache->mat_offsets.visible_tri_len = ofs;
+ return mesh_render_data_mat_tri_len_build_threaded(
+ mr, mr->poly_len, mesh_render_data_mat_tri_len_mesh_range_fn);
}
/** \} */
@@ -447,15 +460,13 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_
* otherwise don't use modifiers as they are not from this object.
*/
MeshRenderData *mesh_render_data_create(Mesh *me,
- MeshBufferExtractionCache *cache,
const bool is_editmode,
const bool is_paint_mode,
const bool is_mode_active,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
- const ToolSettings *ts,
- const eMRIterType iter_type)
+ const ToolSettings *ts)
{
MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__);
mr->toolsettings = ts;
@@ -565,11 +576,6 @@ MeshRenderData *mesh_render_data_create(Mesh *me,
mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
}
- if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
- mesh_render_data_loose_geom_ensure(mr, cache);
- mesh_render_data_loose_geom_load(mr, cache);
- }
-
return mr;
}
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc
index 51bd4c535cd..1efe0c080be 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curve.cc
@@ -663,7 +663,7 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c
for (const int i_spline : splines.index_range()) {
const int eval_size = splines[i_spline]->evaluated_points_size();
- if (splines[i_spline]->is_cyclic()) {
+ if (splines[i_spline]->is_cyclic() && splines[i_spline]->evaluated_edges_size() > 1) {
GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1);
}
for (const int i_point : IndexRange(eval_size)) {
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 0c002ff09f2..359788545e4 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -67,11 +67,12 @@
#include "ED_uvedit.h"
#include "draw_cache_extract.h"
-#include "draw_cache_extract_mesh_private.h"
#include "draw_cache_inline.h"
#include "draw_cache_impl.h" /* own include */
+#include "mesh_extractors/extract_mesh.h"
+
/* ---------------------------------------------------------------------- */
/** \name Dependencies between buffer and batch
* \{ */
@@ -821,17 +822,6 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
mesh_batch_cache_discard_shaded_tri(cache);
mesh_batch_cache_discard_uvedit(cache);
break;
- case BKE_MESH_BATCH_DIRTY_DEFORM:
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.lnor);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_pos);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_nor);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.tris);
- }
- batch_map = MDEPS_CREATE_MAP(vbo.pos_nor, vbo.lnor, vbo.fdots_pos, vbo.fdots_nor, ibo.tris);
- mesh_batch_cache_discard_batch(cache, batch_map);
- break;
case BKE_MESH_BATCH_DIRTY_UVEDIT_ALL:
mesh_batch_cache_discard_uvedit(cache);
break;
@@ -867,7 +857,9 @@ static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extrac
extraction_cache->loose_geom.edge_len = 0;
extraction_cache->loose_geom.vert_len = 0;
- MEM_SAFE_FREE(extraction_cache->mat_offsets.tri);
+ MEM_SAFE_FREE(extraction_cache->poly_sorted.tri_first_index);
+ MEM_SAFE_FREE(extraction_cache->poly_sorted.mat_tri_len);
+ extraction_cache->poly_sorted.visible_tri_len = 0;
}
static void mesh_batch_cache_clear(Mesh *me)
diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c
index a91a1391c31..cba063ed5ef 100644
--- a/source/blender/draw/intern/draw_cache_impl_volume.c
+++ b/source/blender/draw/intern/draw_cache_impl_volume.c
@@ -304,7 +304,7 @@ static DRWVolumeGrid *volume_grid_cache_get(const Volume *volume,
BLI_addtail(&cache->grids, cache_grid);
/* TODO: can we load this earlier, avoid accessing the global and take
- * advantage of dependency graph multithreading? */
+ * advantage of dependency graph multi-threading? */
BKE_volume_load(volume, G.main);
/* Test if we support textures with the number of channels. */
diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h
index b977d0cdda2..5e349d43538 100644
--- a/source/blender/draw/intern/draw_cache_inline.h
+++ b/source/blender/draw/intern/draw_cache_inline.h
@@ -74,8 +74,8 @@ BLI_INLINE void DRW_ibo_request(GPUBatch *batch, GPUIndexBuf **ibo)
BLI_INLINE bool DRW_ibo_requested(GPUIndexBuf *ibo)
{
- /* TODO: do not rely on data uploaded. This prevents multithreading.
- * (need access to a gl context) */
+ /* TODO: do not rely on data uploaded. This prevents multi-threading.
+ * (need access to a OpenGL context). */
return (ibo != NULL && !GPU_indexbuf_is_init(ibo));
}
@@ -85,7 +85,7 @@ BLI_INLINE void DRW_vbo_request(GPUBatch *batch, GPUVertBuf **vbo)
*vbo = GPU_vertbuf_calloc();
}
if (batch != NULL) {
- /* HACK we set vbos that may not yet be valid. */
+ /* HACK we set VBO's that may not yet be valid. */
GPU_batch_vertbuf_add(batch, *vbo);
}
}
diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c
index 449f2cd9606..e055192eb21 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 . */
+ /** Callbuffer 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 6f5e041fa79..35072518b66 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -147,7 +147,7 @@ static bool drw_draw_show_annotation(void)
* the draw manager is only used to draw the background. */
return false;
default:
- BLI_assert("");
+ BLI_assert(0);
return false;
}
}
@@ -2253,7 +2253,7 @@ static void draw_select_framebuffer_depth_only_setup(const int size[2])
/* Must run after all instance datas have been added. */
void DRW_render_instance_buffer_finish(void)
{
- BLI_assert(!DST.buffer_finish_called && "DRW_render_instance_buffer_finish called twice!");
+ BLI_assert_msg(!DST.buffer_finish_called, "DRW_render_instance_buffer_finish called twice!");
DST.buffer_finish_called = true;
DRW_instance_buffer_finish(DST.idatalist);
drw_resource_buffer_finish(DST.vmempool);
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index c4e8d0a980d..512c2775850 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -110,8 +110,8 @@ typedef struct DRWCullingState {
/* Minimum max UBO size is 64KiB. We take the largest
* UBO struct and alloc the max number.
- * ((1 << 16) / sizeof(DRWObjectMatrix)) = 512
- * Keep in sync with common_view_lib.glsl */
+ * `((1 << 16) / sizeof(DRWObjectMatrix)) = 512`
+ * Keep in sync with `common_view_lib.glsl`. */
#define DRW_RESOURCE_CHUNK_LEN 512
/**
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 0a0e1ba9ac3..2126385a352 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -74,7 +74,7 @@ static void draw_call_sort(DRWCommand *array, DRWCommand *array_tmp, int array_l
return;
}
}
- /* Cumulate batch indices */
+ /* Accumulate batch indices */
for (int i = 1; i < ARRAY_SIZE(idx); i++) {
idx[i] += idx[i - 1];
}
@@ -453,7 +453,7 @@ void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
{
int location = GPU_shader_get_ssbo(shgroup->shader, name);
if (location == -1) {
- BLI_assert(false && "Unable to locate binding of shader storage buffer objects.");
+ BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects.");
return;
}
drw_shgroup_uniform_create_ex(
@@ -528,10 +528,10 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob)
drw_call_calc_orco(ob, ob_infos->orcotexfac);
/* Random float value. */
uint random = (DST.dupli_source) ?
- DST.dupli_source->random_id :
- /* TODO(fclem): this is rather costly to do at runtime. Maybe we can
- * put it in ob->runtime and make depsgraph ensure it is up to date. */
- BLI_hash_int_2d(BLI_hash_string(ob->id.name + 2), 0);
+ DST.dupli_source->random_id :
+ /* TODO(fclem): this is rather costly to do at runtime. Maybe we can
+ * put it in ob->runtime and make depsgraph ensure it is up to date. */
+ BLI_hash_int_2d(BLI_hash_string(ob->id.name + 2), 0);
ob_infos->ob_random = random * (1.0f / (float)0xFFFFFFFF);
/* Object State. */
ob_infos->ob_flag = 1.0f; /* Required to have a correct sign */
diff --git a/source/blender/draw/intern/draw_manager_text.c b/source/blender/draw/intern/draw_manager_text.c
index 6c63838201e..265fdba66fd 100644
--- a/source/blender/draw/intern/draw_manager_text.c
+++ b/source/blender/draw/intern/draw_manager_text.c
@@ -266,7 +266,7 @@ void DRW_text_edit_mesh_measure_stats(ARegion *region,
}
const short edge_tex_sep = (short)((edge_tex_count - 1) * 5.0f * U.dpi_fac);
- /* make the precision of the display value proportionate to the gridsize */
+ /* Make the precision of the display value proportionate to the grid-size. */
if (grid <= 0.01f) {
conv_float = "%.6g";
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/mesh_extractors/extract_mesh.c
index e813f006351..53827dcc7d9 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.c
@@ -29,7 +29,8 @@
#include "ED_uvedit.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
+
#include "draw_cache_impl.h"
void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc)
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h
index 5f670bdc5ec..f24ccf1a028 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h
@@ -101,10 +101,12 @@ typedef struct MeshRenderData {
float (*loop_normals)[3];
float (*poly_normals)[3];
int *lverts, *ledges;
+
struct {
- int *tri;
+ int *tri_first_index;
+ int *mat_tri_len;
int visible_tri_len;
- } mat_offsets;
+ } poly_sorted;
} MeshRenderData;
BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
@@ -240,20 +242,22 @@ typedef struct MeshExtract {
/* draw_cache_extract_mesh_render_data.c */
MeshRenderData *mesh_render_data_create(Mesh *me,
- MeshBufferExtractionCache *cache,
const bool is_editmode,
const bool is_paint_mode,
const bool is_mode_active,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
- const ToolSettings *ts,
- const eMRIterType iter_type);
+ const ToolSettings *ts);
void mesh_render_data_free(MeshRenderData *mr);
void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag);
-void mesh_render_data_update_mat_offsets(MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
- const eMRDataType data_flag);
+void mesh_render_data_update_loose_geom(MeshRenderData *mr,
+ MeshBufferExtractionCache *cache,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag);
+void mesh_render_data_update_polys_sorted(MeshRenderData *mr,
+ MeshBufferExtractionCache *cache,
+ const eMRDataType data_flag);
void mesh_render_data_update_looptris(MeshRenderData *mr,
const eMRIterType iter_type,
const eMRDataType data_flag);
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
index 2dff101c71f..5bd5f7adaa8 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
@@ -21,12 +21,12 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
-
#include "BLI_vector.hh"
#include "MEM_guardedalloc.h"
+#include "extract_mesh.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
/** \name Extract Edit UV Triangles Indices
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
index c21725ffa32..0f41702a22c 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
@@ -21,12 +21,12 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
-
#include "BLI_vector.hh"
#include "MEM_guardedalloc.h"
+#include "extract_mesh.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
/** \name Extract Face-dots Indices
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
index 2c2603af1b2..0096da9e56f 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
@@ -21,10 +21,10 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
-
#include "MEM_guardedalloc.h"
+#include "extract_mesh.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
@@ -237,7 +237,7 @@ constexpr MeshExtract create_extractor_lines_loose_only()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_lines_loose_only_init;
- extractor.data_type = MR_DATA_NONE;
+ extractor.data_type = MR_DATA_LOOSE_GEOM;
extractor.data_size = 0;
extractor.use_threading = false;
extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose);
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
index bdb9af1faf3..7a37cf50264 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
@@ -21,13 +21,13 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
-
#include "BLI_edgehash.h"
#include "BLI_vector.hh"
#include "MEM_guardedalloc.h"
+#include "extract_mesh.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
index 277f9d69c2f..5def7edb75a 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
@@ -21,14 +21,14 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
-
#include "BLI_bitmap.h"
#include "BLI_vector.hh"
#include "atomic_ops.h"
#include "MEM_guardedalloc.h"
+#include "extract_mesh.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
/** \name Extract Paint Mask Line Indices
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 cee0c224aab..cc1a19b8d26 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
@@ -21,12 +21,12 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
-
#include "BLI_vector.hh"
#include "MEM_guardedalloc.h"
+#include "extract_mesh.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
index 93f71f920eb..e346177af52 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
@@ -21,65 +21,76 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
-
#include "MEM_guardedalloc.h"
+#include "extract_mesh.h"
+
namespace blender::draw {
+static void extract_tris_mat_task_reduce(void *_userdata_to, void *_userdata_from)
+{
+ GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
+ GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
+ GPU_indexbuf_join(elb_to, elb_from);
+}
+
/* ---------------------------------------------------------------------- */
/** \name Extract Triangles Indices (multi material)
* \{ */
-struct MeshExtract_Tri_Data {
- GPUIndexBufBuilder elb;
- const int *tri_mat_start;
- int *tri_mat_end;
-};
-
static void extract_tris_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *UNUSED(ibo),
void *tls_data)
{
- MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(tls_data);
- data->tri_mat_start = mr->mat_offsets.tri;
- data->tri_mat_end = static_cast<int *>(MEM_dupallocN(data->tri_mat_start));
- GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->mat_offsets.visible_tri_len, mr->loop_len);
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr->poly_sorted.visible_tri_len, mr->loop_len);
}
-static void extract_tris_iter_looptri_bm(const MeshRenderData *mr,
- BMLoop **elt,
- const int UNUSED(elt_index),
- void *_data)
+static void extract_tris_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int f_index,
+ void *_data)
{
- MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
- const int mat_last = mr->mat_len - 1;
+ int tri_first_index = mr->poly_sorted.tri_first_index[f_index];
+ if (tri_first_index == -1) {
+ return;
+ }
- if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
- int *mat_tri_ofs = data->tri_mat_end;
- const int mat = min_ii(elt[0]->f->mat_nr, mat_last);
- GPU_indexbuf_set_tri_verts(&data->elb,
- mat_tri_ofs[mat]++,
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ int tri_first_index_real = poly_to_tri_count(f_index, BM_elem_index_get(f->l_first));
+
+ struct BMLoop *(*looptris)[3] = mr->edit_bmesh->looptris;
+ int tri_len = f->len - 2;
+ for (int offs = 0; offs < tri_len; offs++) {
+ BMLoop **elt = looptris[tri_first_index_real + offs];
+ int tri_index = tri_first_index + offs;
+ GPU_indexbuf_set_tri_verts(elb,
+ tri_index,
BM_elem_index_get(elt[0]),
BM_elem_index_get(elt[1]),
BM_elem_index_get(elt[2]));
}
}
-static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr,
- const MLoopTri *mlt,
- const int UNUSED(elt_index),
- void *_data)
+static void extract_tris_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
{
- MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
- const int mat_last = mr->mat_len - 1;
- const MPoly *mp = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- int *mat_tri_ofs = data->tri_mat_end;
- const int mat = min_ii(mp->mat_nr, mat_last);
- GPU_indexbuf_set_tri_verts(
- &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+ int tri_first_index = mr->poly_sorted.tri_first_index[mp_index];
+ if (tri_first_index == -1) {
+ return;
+ }
+
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ int tri_first_index_real = poly_to_tri_count(mp_index, mp->loopstart);
+
+ int tri_len = mp->totloop - 2;
+ for (int offs = 0; offs < tri_len; offs++) {
+ const MLoopTri *mlt = &mr->mlooptri[tri_first_index_real + offs];
+ int tri_index = tri_first_index + offs;
+ GPU_indexbuf_set_tri_verts(elb, tri_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
}
}
@@ -89,40 +100,41 @@ static void extract_tris_finish(const MeshRenderData *mr,
void *_data)
{
GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
- MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
- GPU_indexbuf_build_in_place(&data->elb, ibo);
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ GPU_indexbuf_build_in_place(elb, ibo);
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
* is created before the surfaces-per-material. */
if (mr->use_final_mesh && cache->final.tris_per_mat) {
MeshBufferCache *mbc_final = &cache->final;
+ int mat_start = 0;
for (int i = 0; i < mr->mat_len; i++) {
/* These IBOs have not been queried yet but we create them just in case they are needed
* later since they are not tracked by mesh_buffer_cache_create_requested(). */
if (mbc_final->tris_per_mat[i] == nullptr) {
mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc();
}
+ const int mat_tri_len = mr->poly_sorted.mat_tri_len[i];
/* Multiply by 3 because these are triangle indices. */
- const int mat_start = data->tri_mat_start[i];
- const int mat_end = data->tri_mat_end[i];
const int start = mat_start * 3;
- const int len = (mat_end - mat_start) * 3;
+ const int len = mat_tri_len * 3;
GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len);
+ mat_start += mat_tri_len;
}
}
- MEM_freeN(data->tri_mat_end);
}
constexpr MeshExtract create_extractor_tris()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_tris_init;
- extractor.iter_looptri_bm = extract_tris_iter_looptri_bm;
- extractor.iter_looptri_mesh = extract_tris_iter_looptri_mesh;
+ extractor.iter_poly_bm = extract_tris_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_tris_iter_poly_mesh;
+ extractor.task_reduce = extract_tris_mat_task_reduce;
extractor.finish = extract_tris_finish;
- extractor.data_type = MR_DATA_MAT_OFFSETS;
- extractor.data_size = sizeof(MeshExtract_Tri_Data);
- extractor.use_threading = false;
+ extractor.data_type = MR_DATA_LOOPTRI | MR_DATA_POLYS_SORTED;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = true;
extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris);
return extractor;
}
@@ -174,13 +186,6 @@ static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr,
}
}
-static void extract_tris_single_mat_task_reduce(void *_userdata_to, void *_userdata_from)
-{
- GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
- GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
- GPU_indexbuf_join(elb_to, elb_from);
-}
-
static void extract_tris_single_mat_finish(const MeshRenderData *mr,
struct MeshBatchCache *cache,
void *buf,
@@ -213,7 +218,7 @@ constexpr MeshExtract create_extractor_tris_single_mat()
extractor.init = extract_tris_single_mat_init;
extractor.iter_looptri_bm = extract_tris_single_mat_iter_looptri_bm;
extractor.iter_looptri_mesh = extract_tris_single_mat_iter_looptri_mesh;
- extractor.task_reduce = extract_tris_single_mat_task_reduce;
+ extractor.task_reduce = extract_tris_mat_task_reduce;
extractor.finish = extract_tris_single_mat_finish;
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(GPUIndexBufBuilder);
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 1bc4c7e330f..302616d4da9 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
@@ -25,7 +25,7 @@
#include "GPU_capabilities.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc
index ff250a30ec4..a7efb9c8a1b 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc
@@ -21,7 +21,8 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
+
#include "draw_cache_impl.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc
index aa58266d56b..0378aadabd0 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc
@@ -21,7 +21,8 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
+
#include "draw_cache_impl.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
index 1d62637d172..a60c0182e89 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
@@ -25,7 +25,7 @@
#include "BKE_mesh.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
index 16814653408..d79ac493c33 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
@@ -25,7 +25,7 @@
#include "BKE_mesh.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc
index 5a988c73a7e..b7182d1b60f 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc
@@ -21,7 +21,8 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
+
#include "draw_cache_impl.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
index fb9d34e7733..5e4ad54f7b6 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
@@ -21,7 +21,7 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc
index c4706c412c6..e765fb8a8bf 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc
@@ -21,7 +21,7 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc
index 0289fd63a30..042a0d2debe 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc
@@ -21,7 +21,7 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
index b942068352b..8344a615cbe 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
@@ -21,7 +21,7 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
index b734061b76a..075d54e268e 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
@@ -30,7 +30,7 @@
#include "BKE_editmesh_bvh.h"
#include "BKE_editmesh_cache.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
index 80b73cac678..269c0343e32 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
@@ -21,7 +21,7 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
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 2ac926dd257..b8e5dcb613f 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
@@ -23,7 +23,7 @@
#include "MEM_guardedalloc.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
index 7b36a009419..dbb1e1f880b 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
@@ -27,7 +27,7 @@
#include "BKE_paint.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
index ac44e97f229..cd8d46901c5 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
@@ -21,7 +21,7 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc
index d7a01ee607f..ffca01d00dd 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc
@@ -21,7 +21,7 @@
* \ingroup draw
*/
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
index f251141c442..8f36bfdf1ef 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
@@ -30,7 +30,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_tangent.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc
index 0f3c2483296..013da7d674a 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc
@@ -23,7 +23,7 @@
#include "BLI_string.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
index 2f1cff08796..d810acfb617 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
@@ -25,7 +25,7 @@
#include "BLI_string.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
index aae266eadce..c547a453aeb 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
@@ -25,7 +25,7 @@
#include "BKE_deform.h"
-#include "draw_cache_extract_mesh_private.h"
+#include "extract_mesh.h"
namespace blender::draw {
diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt
index f50a5ffbb5e..7a53b54b5a4 100644
--- a/source/blender/editors/animation/CMakeLists.txt
+++ b/source/blender/editors/animation/CMakeLists.txt
@@ -47,6 +47,7 @@ set(SRC
keyframes_draw.c
keyframes_edit.c
keyframes_general.c
+ keyframes_keylist.c
keyframing.c
keyingsets.c
time_scrub_ui.c
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 87688ee343c..e5dc9a83ebb 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -966,7 +966,7 @@ static int acf_group_setting_flag(bAnimContext *ac, eAnimChannel_Settings settin
* proved to be a hazard for workflows...
*/
return (ac->spacetype == SPACE_GRAPH) ? AGRP_EXPANDED_G : /* Graph Editor case */
- AGRP_EXPANDED; /* DopeSheet and elsewhere */
+ AGRP_EXPANDED; /* DopeSheet and elsewhere */
}
case ACHANNEL_SETTING_MUTE: /* muted */
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 8f8c1c067d4..afbd9b2c92d 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -2026,7 +2026,7 @@ static const EnumPropertyItem prop_animchannel_settings_types[] = {
* \param mode: eAnimChannels_SetFlag.
* \param onlysel: only selected channels get the flag set.
*
- * TODO: enable a setting which turns flushing on/off?.
+ * TODO: enable a setting which turns flushing on/off?
*/
static void setflag_anim_channels(bAnimContext *ac,
eAnimChannel_Settings setting,
@@ -2683,8 +2683,9 @@ static void box_select_anim_channels(bAnimContext *ac, rcti *rect, short selectm
/* loop over data, doing box select */
for (ale = anim_data.first; ale; ale = ale->next) {
float ymin;
- /* Skip grease pencil datablock. Only use grease pencil layers. */
+
if (ale->type == ANIMTYPE_GPDATABLOCK) {
+ ymax -= ACHANNEL_STEP(ac);
continue;
}
diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c
index 2fcd59a1bbe..baf8adf28d0 100644
--- a/source/blender/editors/animation/anim_draw.c
+++ b/source/blender/editors/animation/anim_draw.c
@@ -48,6 +48,7 @@
#include "ED_anim_api.h"
#include "ED_keyframes_draw.h"
#include "ED_keyframes_edit.h"
+#include "ED_keyframes_keylist.h"
#include "RNA_access.h"
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index 9d998326b4d..cfcea950955 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -123,7 +123,7 @@ ListBase *ED_animcontext_get_markers(const bAnimContext *ac)
* so don't assume anything.
* \param scene: Current scene (for getting current frame)
* \param mode: (TfmMode) transform mode that this transform is for
- * \param value: From the transform code, this is ``t->vec[0]``
+ * \param value: From the transform code, this is `t->vec[0]`
* (which is delta transform for grab/extend, and scale factor for scale)
* \param side: (B/L/R) for 'extend' functionality, which side of current frame to use
*/
@@ -483,7 +483,8 @@ static int marker_get_icon_id(TimeMarker *marker, int flag)
{
if (flag & DRAW_MARKERS_LOCAL) {
return (marker->flag & ACTIVE) ? ICON_PMARKER_ACT :
- (marker->flag & SELECT) ? ICON_PMARKER_SEL : ICON_PMARKER;
+ (marker->flag & SELECT) ? ICON_PMARKER_SEL :
+ ICON_PMARKER;
}
#ifdef DURIAN_CAMERA_SWITCH
if (marker->camera) {
diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c
index 51a897600e1..bddd5dbff55 100644
--- a/source/blender/editors/animation/anim_motion_paths.c
+++ b/source/blender/editors/animation/anim_motion_paths.c
@@ -43,7 +43,7 @@
#include "GPU_vertex_buffer.h"
#include "ED_anim_api.h"
-#include "ED_keyframes_draw.h"
+#include "ED_keyframes_keylist.h"
#include "CLG_log.h"
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 06107b6fee6..4f512c9d7ca 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -24,28 +24,17 @@
/* System includes ----------------------------------------------------- */
#include <float.h>
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "MEM_guardedalloc.h"
#include "BLI_dlrbTree.h"
#include "BLI_listbase.h"
-#include "BLI_math.h"
#include "BLI_rect.h"
-#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
-#include "DNA_brush_types.h"
-#include "DNA_cachefile_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_mask_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
-#include "BKE_fcurve.h"
-
#include "GPU_immediate.h"
#include "GPU_state.h"
@@ -55,498 +44,7 @@
#include "ED_anim_api.h"
#include "ED_keyframes_draw.h"
-
-/* *************************** Keyframe Processing *************************** */
-
-/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */
-
-BLI_INLINE bool is_cfra_eq(float a, float b)
-{
- return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH);
-}
-
-BLI_INLINE bool is_cfra_lt(float a, float b)
-{
- return (b - a) > BEZT_BINARYSEARCH_THRESH;
-}
-
-/* Comparator callback used for ActKeyColumns and cframe float-value pointer */
-/* NOTE: this is exported to other modules that use the ActKeyColumns for finding keyframes */
-short compare_ak_cfraPtr(void *node, void *data)
-{
- ActKeyColumn *ak = (ActKeyColumn *)node;
- const float *cframe = data;
- float val = *cframe;
-
- if (is_cfra_eq(val, ak->cfra)) {
- return 0;
- }
-
- if (val < ak->cfra) {
- return -1;
- }
- return 1;
-}
-
-/* --------------- */
-
-/* Set of references to three logically adjacent keys. */
-typedef struct BezTripleChain {
- /* Current keyframe. */
- BezTriple *cur;
-
- /* Logical neighbors. May be NULL. */
- BezTriple *prev, *next;
-} BezTripleChain;
-
-/* Categorize the interpolation & handle type of the keyframe. */
-static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt)
-{
- if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) {
- return KEYFRAME_HANDLE_AUTO_CLAMP;
- }
- if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) {
- return KEYFRAME_HANDLE_AUTO;
- }
- if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) {
- return KEYFRAME_HANDLE_VECTOR;
- }
- if (ELEM(HD_FREE, bezt->h1, bezt->h2)) {
- return KEYFRAME_HANDLE_FREE;
- }
- return KEYFRAME_HANDLE_ALIGNED;
-}
-
-/* Determine if the keyframe is an extreme by comparing with neighbors.
- * Ends of fixed-value sections and of the whole curve are also marked.
- */
-static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain)
-{
- if (chain->prev == NULL && chain->next == NULL) {
- return KEYFRAME_EXTREME_NONE;
- }
-
- /* Keyframe values for the current one and neighbors. */
- float cur_y = chain->cur->vec[1][1];
- float prev_y = cur_y, next_y = cur_y;
-
- if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) {
- prev_y = chain->prev->vec[1][1];
- }
- if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) {
- next_y = chain->next->vec[1][1];
- }
-
- /* Static hold. */
- if (prev_y == cur_y && next_y == cur_y) {
- return KEYFRAME_EXTREME_FLAT;
- }
-
- /* Middle of an incline. */
- if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) {
- return KEYFRAME_EXTREME_NONE;
- }
-
- /* Bezier handle values for the overshoot check. */
- bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ;
- bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ;
- float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y;
- float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y;
-
- /* Detect extremes. One of the neighbors is allowed to be equal to current. */
- if (prev_y < cur_y || next_y < cur_y) {
- bool is_overshoot = (handle_l > cur_y || handle_r > cur_y);
-
- return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0);
- }
-
- if (prev_y > cur_y || next_y > cur_y) {
- bool is_overshoot = (handle_l < cur_y || handle_r < cur_y);
-
- return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0);
- }
-
- return KEYFRAME_EXTREME_NONE;
-}
-
-/* Comparator callback used for ActKeyColumns and BezTripleChain */
-static short compare_ak_bezt(void *node, void *data)
-{
- BezTripleChain *chain = data;
-
- return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]);
-}
-
-/* New node callback used for building ActKeyColumns from BezTripleChain */
-static DLRBT_Node *nalloc_ak_bezt(void *data)
-{
- ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn");
- BezTripleChain *chain = data;
- BezTriple *bezt = chain->cur;
-
- /* store settings based on state of BezTriple */
- ak->cfra = bezt->vec[1][0];
- ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0;
- ak->key_type = BEZKEYTYPE(bezt);
- ak->handle_type = bezt_handle_type(bezt);
- ak->extreme_type = bezt_extreme_type(chain);
-
- /* count keyframes in this column */
- ak->totkey = 1;
-
- return (DLRBT_Node *)ak;
-}
-
-/* Node updater callback used for building ActKeyColumns from BezTripleChain */
-static void nupdate_ak_bezt(void *node, void *data)
-{
- ActKeyColumn *ak = node;
- BezTripleChain *chain = data;
- BezTriple *bezt = chain->cur;
-
- /* set selection status and 'touched' status */
- if (BEZT_ISSEL_ANY(bezt)) {
- ak->sel = SELECT;
- }
-
- /* count keyframes in this column */
- ak->totkey++;
-
- /* For keyframe type, 'proper' keyframes have priority over breakdowns
- * (and other types for now). */
- if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) {
- ak->key_type = BEZT_KEYTYPE_KEYFRAME;
- }
-
- /* For interpolation type, select the highest value (enum is sorted). */
- ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt));
-
- /* For extremes, detect when combining different states. */
- char new_extreme = bezt_extreme_type(chain);
-
- if (new_extreme != ak->extreme_type) {
- /* Replace the flat status without adding mixed. */
- if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) {
- ak->extreme_type = new_extreme;
- }
- else if (new_extreme != KEYFRAME_EXTREME_FLAT) {
- ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED);
- }
- }
-}
-
-/* ......... */
-
-/* Comparator callback used for ActKeyColumns and GPencil frame */
-static short compare_ak_gpframe(void *node, void *data)
-{
- bGPDframe *gpf = (bGPDframe *)data;
-
- float frame = gpf->framenum;
- return compare_ak_cfraPtr(node, &frame);
-}
-
-/* New node callback used for building ActKeyColumns from GPencil frames */
-static DLRBT_Node *nalloc_ak_gpframe(void *data)
-{
- ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF");
- bGPDframe *gpf = (bGPDframe *)data;
-
- /* store settings based on state of BezTriple */
- ak->cfra = gpf->framenum;
- ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0;
- ak->key_type = gpf->key_type;
-
- /* count keyframes in this column */
- ak->totkey = 1;
- /* Set as visible block. */
- ak->totblock = 1;
- ak->block.sel = ak->sel;
- ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL;
-
- return (DLRBT_Node *)ak;
-}
-
-/* Node updater callback used for building ActKeyColumns from GPencil frames */
-static void nupdate_ak_gpframe(void *node, void *data)
-{
- ActKeyColumn *ak = (ActKeyColumn *)node;
- bGPDframe *gpf = (bGPDframe *)data;
-
- /* set selection status and 'touched' status */
- if (gpf->flag & GP_FRAME_SELECT) {
- ak->sel = SELECT;
- }
-
- /* count keyframes in this column */
- ak->totkey++;
-
- /* for keyframe type, 'proper' keyframes have priority over breakdowns
- * (and other types for now). */
- if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) {
- ak->key_type = BEZT_KEYTYPE_KEYFRAME;
- }
-}
-
-/* ......... */
-
-/* Comparator callback used for ActKeyColumns and GPencil frame */
-static short compare_ak_masklayshape(void *node, void *data)
-{
- MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
-
- float frame = masklay_shape->frame;
- return compare_ak_cfraPtr(node, &frame);
-}
-
-/* New node callback used for building ActKeyColumns from GPencil frames */
-static DLRBT_Node *nalloc_ak_masklayshape(void *data)
-{
- ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF");
- MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
-
- /* store settings based on state of BezTriple */
- ak->cfra = masklay_shape->frame;
- ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0;
-
- /* count keyframes in this column */
- ak->totkey = 1;
-
- return (DLRBT_Node *)ak;
-}
-
-/* Node updater callback used for building ActKeyColumns from GPencil frames */
-static void nupdate_ak_masklayshape(void *node, void *data)
-{
- ActKeyColumn *ak = (ActKeyColumn *)node;
- MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
-
- /* set selection status and 'touched' status */
- if (masklay_shape->flag & MASK_SHAPE_SELECT) {
- ak->sel = SELECT;
- }
-
- /* count keyframes in this column */
- ak->totkey++;
-}
-
-/* --------------- */
-
-/* Add the given BezTriple to the given 'list' of Keyframes */
-static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt)
-{
- if (ELEM(NULL, keys, bezt)) {
- return;
- }
-
- BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt);
-}
-
-/* Add the given GPencil Frame to the given 'list' of Keyframes */
-static void add_gpframe_to_keycolumns_list(DLRBT_Tree *keys, bGPDframe *gpf)
-{
- if (ELEM(NULL, keys, gpf)) {
- return;
- }
-
- BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf);
-}
-
-/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */
-static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *masklay_shape)
-{
- if (ELEM(NULL, keys, masklay_shape)) {
- return;
- }
-
- BLI_dlrbTree_add(keys,
- compare_ak_masklayshape,
- nalloc_ak_masklayshape,
- nupdate_ak_masklayshape,
- masklay_shape);
-}
-
-/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */
-
-static const ActKeyBlockInfo dummy_keyblock = {0};
-
-static void compute_keyblock_data(ActKeyBlockInfo *info, BezTriple *prev, BezTriple *beztn)
-{
- memset(info, 0, sizeof(ActKeyBlockInfo));
-
- if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) {
- /* Animator tagged a "moving hold"
- * - Previous key must also be tagged as a moving hold, otherwise
- * we're just dealing with the first of a pair, and we don't
- * want to be creating any phantom holds...
- */
- if (BEZKEYTYPE(prev) == BEZT_KEYTYPE_MOVEHOLD) {
- info->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD;
- }
- }
-
- /* Check for same values...
- * - Handles must have same central value as each other
- * - Handles which control that section of the curve must be constant
- */
- if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) {
- bool hold;
-
- /* Only check handles in case of actual bezier interpolation. */
- if (prev->ipo == BEZT_IPO_BEZ) {
- hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) &&
- IS_EQF(prev->vec[1][1], prev->vec[2][1]);
- }
- /* This interpolation type induces movement even between identical keys. */
- else {
- hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC);
- }
-
- if (hold) {
- info->flag |= ACTKEYBLOCK_FLAG_STATIC_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD;
- }
- }
-
- /* Remember non-bezier interpolation info. */
- if (prev->ipo != BEZT_IPO_BEZ) {
- info->flag |= ACTKEYBLOCK_FLAG_NON_BEZIER;
- }
-
- info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn);
-}
-
-static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block)
-{
- /* New curve and block. */
- if (col->totcurve <= 1 && col->totblock == 0) {
- memcpy(&col->block, block, sizeof(ActKeyBlockInfo));
- }
- /* Existing curve. */
- else {
- col->block.conflict |= (col->block.flag ^ block->flag);
- col->block.flag |= block->flag;
- col->block.sel |= block->sel;
- }
-
- if (block->flag) {
- col->totblock++;
- }
-}
-
-static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len)
-{
- ActKeyColumn *col = keys->first;
-
- if (bezt && bezt_len >= 2) {
- ActKeyBlockInfo block;
-
- /* Find the first key column while inserting dummy blocks. */
- for (; col != NULL && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) {
- add_keyblock_info(col, &dummy_keyblock);
- }
-
- BLI_assert(col != NULL);
-
- /* Insert real blocks. */
- for (int v = 1; col != NULL && v < bezt_len; v++, bezt++) {
- /* Wrong order of bezier keys: resync position. */
- if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) {
- /* Backtrack to find the right location. */
- if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) {
- ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact(
- keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]);
-
- if (newcol != NULL) {
- col = newcol;
-
- /* The previous keyblock is garbage too. */
- if (col->prev != NULL) {
- add_keyblock_info(col->prev, &dummy_keyblock);
- }
- }
- else {
- BLI_assert(false);
- }
- }
-
- continue;
- }
-
- /* Normal sequence */
- BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0]));
-
- compute_keyblock_data(&block, bezt, bezt + 1);
-
- for (; col != NULL && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) {
- add_keyblock_info(col, &block);
- }
-
- BLI_assert(col != NULL);
- }
- }
-
- /* Insert dummy blocks at the end. */
- for (; col != NULL; col = col->next) {
- add_keyblock_info(col, &dummy_keyblock);
- }
-}
-
-/* Walk through columns and propagate blocks and totcurve.
- *
- * This must be called even by animation sources that don't generate
- * keyblocks to keep the data structure consistent after adding columns.
- */
-static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len)
-{
- /* Recompute the prev/next linked list. */
- BLI_dlrbTree_linkedlist_sync(keys);
-
- /* Find the curve count */
- int max_curve = 0;
-
- LISTBASE_FOREACH (ActKeyColumn *, col, keys) {
- max_curve = MAX2(max_curve, col->totcurve);
- }
-
- /* Propagate blocks to inserted keys */
- ActKeyColumn *prev_ready = NULL;
-
- LISTBASE_FOREACH (ActKeyColumn *, col, keys) {
- /* Pre-existing column. */
- if (col->totcurve > 0) {
- prev_ready = col;
- }
- /* Newly inserted column, so copy block data from previous. */
- else if (prev_ready != NULL) {
- col->totblock = prev_ready->totblock;
- memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo));
- }
-
- col->totcurve = max_curve + 1;
- }
-
- /* Add blocks on top */
- add_bezt_to_keyblocks_list(keys, bezt, bezt_len);
-}
-
-/* --------- */
-
-bool actkeyblock_is_valid(ActKeyColumn *ac)
-{
- return ac != NULL && ac->next != NULL && ac->totblock > 0;
-}
-
-/* Checks if ActKeyBlock should exist... */
-int actkeyblock_get_valid_hold(ActKeyColumn *ac)
-{
- /* check that block is valid */
- if (!actkeyblock_is_valid(ac)) {
- return 0;
- }
-
- const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD);
- return (ac->block.flag & ~ac->block.conflict) & hold_mask;
-}
+#include "ED_keyframes_keylist.h"
/* *************************** Keyframe Drawing *************************** */
@@ -1029,257 +527,3 @@ void draw_masklay_channel(View2D *v2d,
BLI_dlrbTree_free(&keys);
}
-
-/* *************************** Keyframe List Conversions *************************** */
-
-void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag)
-{
- if (ac) {
- ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
- int filter;
-
- /* get F-Curves to take keyframes from */
- filter = ANIMFILTER_DATA_VISIBLE;
- ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
-
- /* loop through each F-Curve, grabbing the keyframes */
- for (ale = anim_data.first; ale; ale = ale->next) {
- /* Why not use all #eAnim_KeyType here?
- * All of the other key types are actually "summaries" themselves,
- * and will just end up duplicating stuff that comes up through
- * standard filtering of just F-Curves. Given the way that these work,
- * there isn't really any benefit at all from including them. - Aligorith */
-
- switch (ale->datatype) {
- case ALE_FCURVE:
- fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
- break;
- case ALE_MASKLAY:
- mask_to_keylist(ac->ads, ale->data, keys);
- break;
- case ALE_GPFRAME:
- gpl_to_keylist(ac->ads, ale->data, keys);
- break;
- default:
- // printf("%s: datatype %d unhandled\n", __func__, ale->datatype);
- break;
- }
- }
-
- ANIM_animdata_freelist(&anim_data);
- }
-}
-
-void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction_flag)
-{
- bAnimContext ac = {NULL};
- ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
- int filter;
-
- bAnimListElem dummychan = {NULL};
-
- if (sce == NULL) {
- return;
- }
-
- /* create a dummy wrapper data to work with */
- dummychan.type = ANIMTYPE_SCENE;
- dummychan.data = sce;
- dummychan.id = &sce->id;
- dummychan.adt = sce->adt;
-
- ac.ads = ads;
- ac.data = &dummychan;
- ac.datatype = ANIMCONT_CHANNEL;
-
- /* get F-Curves to take keyframes from */
- filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
- ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
- /* loop through each F-Curve, grabbing the keyframes */
- for (ale = anim_data.first; ale; ale = ale->next) {
- fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
- }
-
- ANIM_animdata_freelist(&anim_data);
-}
-
-void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_flag)
-{
- bAnimContext ac = {NULL};
- ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
- int filter;
-
- bAnimListElem dummychan = {NULL};
- Base dummybase = {NULL};
-
- if (ob == NULL) {
- return;
- }
-
- /* create a dummy wrapper data to work with */
- dummybase.object = ob;
-
- dummychan.type = ANIMTYPE_OBJECT;
- dummychan.data = &dummybase;
- dummychan.id = &ob->id;
- dummychan.adt = ob->adt;
-
- ac.ads = ads;
- ac.data = &dummychan;
- ac.datatype = ANIMCONT_CHANNEL;
-
- /* get F-Curves to take keyframes from */
- filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
- ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
- /* loop through each F-Curve, grabbing the keyframes */
- for (ale = anim_data.first; ale; ale = ale->next) {
- fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
- }
-
- ANIM_animdata_freelist(&anim_data);
-}
-
-void cachefile_to_keylist(bDopeSheet *ads,
- CacheFile *cache_file,
- DLRBT_Tree *keys,
- int saction_flag)
-{
- if (cache_file == NULL) {
- return;
- }
-
- /* create a dummy wrapper data to work with */
- bAnimListElem dummychan = {NULL};
- dummychan.type = ANIMTYPE_DSCACHEFILE;
- dummychan.data = cache_file;
- dummychan.id = &cache_file->id;
- dummychan.adt = cache_file->adt;
-
- bAnimContext ac = {NULL};
- ac.ads = ads;
- ac.data = &dummychan;
- ac.datatype = ANIMCONT_CHANNEL;
-
- /* get F-Curves to take keyframes from */
- ListBase anim_data = {NULL, NULL};
- int filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
- ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
- /* loop through each F-Curve, grabbing the keyframes */
- LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
- fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
- }
-
- ANIM_animdata_freelist(&anim_data);
-}
-
-void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction_flag)
-{
- if (fcu && fcu->totvert && fcu->bezt) {
- /* apply NLA-mapping (if applicable) */
- if (adt) {
- ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
- }
-
- /* Check if the curve is cyclic. */
- bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2);
- bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0;
-
- /* loop through beztriples, making ActKeysColumns */
- BezTripleChain chain = {0};
-
- for (int v = 0; v < fcu->totvert; v++) {
- chain.cur = &fcu->bezt[v];
-
- /* Neighbor keys, accounting for being cyclic. */
- if (do_extremes) {
- chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : NULL;
- chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL;
- }
-
- add_bezt_to_keycolumns_list(keys, &chain);
- }
-
- /* Update keyblocks. */
- update_keyblocks(keys, fcu->bezt, fcu->totvert);
-
- /* unapply NLA-mapping if applicable */
- if (adt) {
- ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
- }
- }
-}
-
-void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, int saction_flag)
-{
- FCurve *fcu;
-
- if (agrp) {
- /* loop through F-Curves */
- for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) {
- fcurve_to_keylist(adt, fcu, keys, saction_flag);
- }
- }
-}
-
-void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, int saction_flag)
-{
- FCurve *fcu;
-
- if (act) {
- /* loop through F-Curves */
- for (fcu = act->curves.first; fcu; fcu = fcu->next) {
- fcurve_to_keylist(adt, fcu, keys, saction_flag);
- }
- }
-}
-
-void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active)
-{
- bGPDlayer *gpl;
-
- if (gpd && keys) {
- /* for now, just aggregate out all the frames, but only for visible layers */
- for (gpl = gpd->layers.last; gpl; gpl = gpl->prev) {
- if ((gpl->flag & GP_LAYER_HIDE) == 0) {
- if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) {
- gpl_to_keylist(ads, gpl, keys);
- }
- }
- }
- }
-}
-
-void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, DLRBT_Tree *keys)
-{
- bGPDframe *gpf;
-
- if (gpl && keys) {
- /* Although the frames should already be in an ordered list,
- * they are not suitable for displaying yet. */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- add_gpframe_to_keycolumns_list(keys, gpf);
- }
-
- update_keyblocks(keys, NULL, 0);
- }
-}
-
-void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, DLRBT_Tree *keys)
-{
- MaskLayerShape *masklay_shape;
-
- if (masklay && keys) {
- for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
- masklay_shape = masklay_shape->next) {
- add_masklay_to_keycolumns_list(keys, masklay_shape);
- }
-
- update_keyblocks(keys, NULL, 0);
- }
-}
diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.c
new file mode 100644
index 00000000000..47ed2b56300
--- /dev/null
+++ b/source/blender/editors/animation/keyframes_keylist.c
@@ -0,0 +1,793 @@
+/*
+ * 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) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edanimation
+ */
+
+/* System includes ----------------------------------------------------- */
+
+#include <float.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_dlrbTree.h"
+#include "BLI_listbase.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_cachefile_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_mask_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_fcurve.h"
+
+#include "ED_anim_api.h"
+#include "ED_keyframes_keylist.h"
+
+/* *************************** Keyframe Processing *************************** */
+
+/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */
+
+BLI_INLINE bool is_cfra_eq(float a, float b)
+{
+ return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH);
+}
+
+BLI_INLINE bool is_cfra_lt(float a, float b)
+{
+ return (b - a) > BEZT_BINARYSEARCH_THRESH;
+}
+
+/* Comparator callback used for ActKeyColumns and cframe float-value pointer */
+/* NOTE: this is exported to other modules that use the ActKeyColumns for finding keyframes */
+short compare_ak_cfraPtr(void *node, void *data)
+{
+ ActKeyColumn *ak = (ActKeyColumn *)node;
+ const float *cframe = data;
+ float val = *cframe;
+
+ if (is_cfra_eq(val, ak->cfra)) {
+ return 0;
+ }
+
+ if (val < ak->cfra) {
+ return -1;
+ }
+ return 1;
+}
+
+/* --------------- */
+
+/* Set of references to three logically adjacent keys. */
+typedef struct BezTripleChain {
+ /* Current keyframe. */
+ BezTriple *cur;
+
+ /* Logical neighbors. May be NULL. */
+ BezTriple *prev, *next;
+} BezTripleChain;
+
+/* Categorize the interpolation & handle type of the keyframe. */
+static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt)
+{
+ if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) {
+ return KEYFRAME_HANDLE_AUTO_CLAMP;
+ }
+ if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) {
+ return KEYFRAME_HANDLE_AUTO;
+ }
+ if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) {
+ return KEYFRAME_HANDLE_VECTOR;
+ }
+ if (ELEM(HD_FREE, bezt->h1, bezt->h2)) {
+ return KEYFRAME_HANDLE_FREE;
+ }
+ return KEYFRAME_HANDLE_ALIGNED;
+}
+
+/* Determine if the keyframe is an extreme by comparing with neighbors.
+ * Ends of fixed-value sections and of the whole curve are also marked.
+ */
+static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain)
+{
+ if (chain->prev == NULL && chain->next == NULL) {
+ return KEYFRAME_EXTREME_NONE;
+ }
+
+ /* Keyframe values for the current one and neighbors. */
+ float cur_y = chain->cur->vec[1][1];
+ float prev_y = cur_y, next_y = cur_y;
+
+ if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) {
+ prev_y = chain->prev->vec[1][1];
+ }
+ if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) {
+ next_y = chain->next->vec[1][1];
+ }
+
+ /* Static hold. */
+ if (prev_y == cur_y && next_y == cur_y) {
+ return KEYFRAME_EXTREME_FLAT;
+ }
+
+ /* Middle of an incline. */
+ if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) {
+ return KEYFRAME_EXTREME_NONE;
+ }
+
+ /* Bezier handle values for the overshoot check. */
+ bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ;
+ bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ;
+ float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y;
+ float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y;
+
+ /* Detect extremes. One of the neighbors is allowed to be equal to current. */
+ if (prev_y < cur_y || next_y < cur_y) {
+ bool is_overshoot = (handle_l > cur_y || handle_r > cur_y);
+
+ return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0);
+ }
+
+ if (prev_y > cur_y || next_y > cur_y) {
+ bool is_overshoot = (handle_l < cur_y || handle_r < cur_y);
+
+ return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0);
+ }
+
+ return KEYFRAME_EXTREME_NONE;
+}
+
+/* Comparator callback used for ActKeyColumns and BezTripleChain */
+static short compare_ak_bezt(void *node, void *data)
+{
+ BezTripleChain *chain = data;
+
+ return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]);
+}
+
+/* New node callback used for building ActKeyColumns from BezTripleChain */
+static DLRBT_Node *nalloc_ak_bezt(void *data)
+{
+ ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn");
+ BezTripleChain *chain = data;
+ BezTriple *bezt = chain->cur;
+
+ /* store settings based on state of BezTriple */
+ ak->cfra = bezt->vec[1][0];
+ ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0;
+ ak->key_type = BEZKEYTYPE(bezt);
+ ak->handle_type = bezt_handle_type(bezt);
+ ak->extreme_type = bezt_extreme_type(chain);
+
+ /* count keyframes in this column */
+ ak->totkey = 1;
+
+ return (DLRBT_Node *)ak;
+}
+
+/* Node updater callback used for building ActKeyColumns from BezTripleChain */
+static void nupdate_ak_bezt(void *node, void *data)
+{
+ ActKeyColumn *ak = node;
+ BezTripleChain *chain = data;
+ BezTriple *bezt = chain->cur;
+
+ /* set selection status and 'touched' status */
+ if (BEZT_ISSEL_ANY(bezt)) {
+ ak->sel = SELECT;
+ }
+
+ /* count keyframes in this column */
+ ak->totkey++;
+
+ /* For keyframe type, 'proper' keyframes have priority over breakdowns
+ * (and other types for now). */
+ if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) {
+ ak->key_type = BEZT_KEYTYPE_KEYFRAME;
+ }
+
+ /* For interpolation type, select the highest value (enum is sorted). */
+ ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt));
+
+ /* For extremes, detect when combining different states. */
+ char new_extreme = bezt_extreme_type(chain);
+
+ if (new_extreme != ak->extreme_type) {
+ /* Replace the flat status without adding mixed. */
+ if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) {
+ ak->extreme_type = new_extreme;
+ }
+ else if (new_extreme != KEYFRAME_EXTREME_FLAT) {
+ ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED);
+ }
+ }
+}
+
+/* ......... */
+
+/* Comparator callback used for ActKeyColumns and GPencil frame */
+static short compare_ak_gpframe(void *node, void *data)
+{
+ bGPDframe *gpf = (bGPDframe *)data;
+
+ float frame = gpf->framenum;
+ return compare_ak_cfraPtr(node, &frame);
+}
+
+/* New node callback used for building ActKeyColumns from GPencil frames */
+static DLRBT_Node *nalloc_ak_gpframe(void *data)
+{
+ ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF");
+ bGPDframe *gpf = (bGPDframe *)data;
+
+ /* store settings based on state of BezTriple */
+ ak->cfra = gpf->framenum;
+ ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0;
+ ak->key_type = gpf->key_type;
+
+ /* count keyframes in this column */
+ ak->totkey = 1;
+ /* Set as visible block. */
+ ak->totblock = 1;
+ ak->block.sel = ak->sel;
+ ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL;
+
+ return (DLRBT_Node *)ak;
+}
+
+/* Node updater callback used for building ActKeyColumns from GPencil frames */
+static void nupdate_ak_gpframe(void *node, void *data)
+{
+ ActKeyColumn *ak = (ActKeyColumn *)node;
+ bGPDframe *gpf = (bGPDframe *)data;
+
+ /* set selection status and 'touched' status */
+ if (gpf->flag & GP_FRAME_SELECT) {
+ ak->sel = SELECT;
+ }
+
+ /* count keyframes in this column */
+ ak->totkey++;
+
+ /* for keyframe type, 'proper' keyframes have priority over breakdowns
+ * (and other types for now). */
+ if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) {
+ ak->key_type = BEZT_KEYTYPE_KEYFRAME;
+ }
+}
+
+/* ......... */
+
+/* Comparator callback used for ActKeyColumns and GPencil frame */
+static short compare_ak_masklayshape(void *node, void *data)
+{
+ MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
+
+ float frame = masklay_shape->frame;
+ return compare_ak_cfraPtr(node, &frame);
+}
+
+/* New node callback used for building ActKeyColumns from GPencil frames */
+static DLRBT_Node *nalloc_ak_masklayshape(void *data)
+{
+ ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF");
+ MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
+
+ /* store settings based on state of BezTriple */
+ ak->cfra = masklay_shape->frame;
+ ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0;
+
+ /* count keyframes in this column */
+ ak->totkey = 1;
+
+ return (DLRBT_Node *)ak;
+}
+
+/* Node updater callback used for building ActKeyColumns from GPencil frames */
+static void nupdate_ak_masklayshape(void *node, void *data)
+{
+ ActKeyColumn *ak = (ActKeyColumn *)node;
+ MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
+
+ /* set selection status and 'touched' status */
+ if (masklay_shape->flag & MASK_SHAPE_SELECT) {
+ ak->sel = SELECT;
+ }
+
+ /* count keyframes in this column */
+ ak->totkey++;
+}
+
+/* --------------- */
+
+/* Add the given BezTriple to the given 'list' of Keyframes */
+static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt)
+{
+ if (ELEM(NULL, keys, bezt)) {
+ return;
+ }
+
+ BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt);
+}
+
+/* Add the given GPencil Frame to the given 'list' of Keyframes */
+static void add_gpframe_to_keycolumns_list(DLRBT_Tree *keys, bGPDframe *gpf)
+{
+ if (ELEM(NULL, keys, gpf)) {
+ return;
+ }
+
+ BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf);
+}
+
+/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */
+static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *masklay_shape)
+{
+ if (ELEM(NULL, keys, masklay_shape)) {
+ return;
+ }
+
+ BLI_dlrbTree_add(keys,
+ compare_ak_masklayshape,
+ nalloc_ak_masklayshape,
+ nupdate_ak_masklayshape,
+ masklay_shape);
+}
+
+/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */
+
+static const ActKeyBlockInfo dummy_keyblock = {0};
+
+static void compute_keyblock_data(ActKeyBlockInfo *info, BezTriple *prev, BezTriple *beztn)
+{
+ memset(info, 0, sizeof(ActKeyBlockInfo));
+
+ if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) {
+ /* Animator tagged a "moving hold"
+ * - Previous key must also be tagged as a moving hold, otherwise
+ * we're just dealing with the first of a pair, and we don't
+ * want to be creating any phantom holds...
+ */
+ if (BEZKEYTYPE(prev) == BEZT_KEYTYPE_MOVEHOLD) {
+ info->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD;
+ }
+ }
+
+ /* Check for same values...
+ * - Handles must have same central value as each other
+ * - Handles which control that section of the curve must be constant
+ */
+ if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) {
+ bool hold;
+
+ /* Only check handles in case of actual bezier interpolation. */
+ if (prev->ipo == BEZT_IPO_BEZ) {
+ hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) &&
+ IS_EQF(prev->vec[1][1], prev->vec[2][1]);
+ }
+ /* This interpolation type induces movement even between identical keys. */
+ else {
+ hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC);
+ }
+
+ if (hold) {
+ info->flag |= ACTKEYBLOCK_FLAG_STATIC_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD;
+ }
+ }
+
+ /* Remember non-bezier interpolation info. */
+ if (prev->ipo != BEZT_IPO_BEZ) {
+ info->flag |= ACTKEYBLOCK_FLAG_NON_BEZIER;
+ }
+
+ info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn);
+}
+
+static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block)
+{
+ /* New curve and block. */
+ if (col->totcurve <= 1 && col->totblock == 0) {
+ memcpy(&col->block, block, sizeof(ActKeyBlockInfo));
+ }
+ /* Existing curve. */
+ else {
+ col->block.conflict |= (col->block.flag ^ block->flag);
+ col->block.flag |= block->flag;
+ col->block.sel |= block->sel;
+ }
+
+ if (block->flag) {
+ col->totblock++;
+ }
+}
+
+static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len)
+{
+ ActKeyColumn *col = keys->first;
+
+ if (bezt && bezt_len >= 2) {
+ ActKeyBlockInfo block;
+
+ /* Find the first key column while inserting dummy blocks. */
+ for (; col != NULL && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) {
+ add_keyblock_info(col, &dummy_keyblock);
+ }
+
+ BLI_assert(col != NULL);
+
+ /* Insert real blocks. */
+ for (int v = 1; col != NULL && v < bezt_len; v++, bezt++) {
+ /* Wrong order of bezier keys: resync position. */
+ if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) {
+ /* Backtrack to find the right location. */
+ if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) {
+ ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact(
+ keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]);
+
+ if (newcol != NULL) {
+ col = newcol;
+
+ /* The previous keyblock is garbage too. */
+ if (col->prev != NULL) {
+ add_keyblock_info(col->prev, &dummy_keyblock);
+ }
+ }
+ else {
+ BLI_assert(false);
+ }
+ }
+
+ continue;
+ }
+
+ /* Normal sequence */
+ BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0]));
+
+ compute_keyblock_data(&block, bezt, bezt + 1);
+
+ for (; col != NULL && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) {
+ add_keyblock_info(col, &block);
+ }
+
+ BLI_assert(col != NULL);
+ }
+ }
+
+ /* Insert dummy blocks at the end. */
+ for (; col != NULL; col = col->next) {
+ add_keyblock_info(col, &dummy_keyblock);
+ }
+}
+
+/* Walk through columns and propagate blocks and totcurve.
+ *
+ * This must be called even by animation sources that don't generate
+ * keyblocks to keep the data structure consistent after adding columns.
+ */
+static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len)
+{
+ /* Recompute the prev/next linked list. */
+ BLI_dlrbTree_linkedlist_sync(keys);
+
+ /* Find the curve count */
+ int max_curve = 0;
+
+ LISTBASE_FOREACH (ActKeyColumn *, col, keys) {
+ max_curve = MAX2(max_curve, col->totcurve);
+ }
+
+ /* Propagate blocks to inserted keys */
+ ActKeyColumn *prev_ready = NULL;
+
+ LISTBASE_FOREACH (ActKeyColumn *, col, keys) {
+ /* Pre-existing column. */
+ if (col->totcurve > 0) {
+ prev_ready = col;
+ }
+ /* Newly inserted column, so copy block data from previous. */
+ else if (prev_ready != NULL) {
+ col->totblock = prev_ready->totblock;
+ memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo));
+ }
+
+ col->totcurve = max_curve + 1;
+ }
+
+ /* Add blocks on top */
+ add_bezt_to_keyblocks_list(keys, bezt, bezt_len);
+}
+
+/* --------- */
+
+bool actkeyblock_is_valid(ActKeyColumn *ac)
+{
+ return ac != NULL && ac->next != NULL && ac->totblock > 0;
+}
+
+/* Checks if ActKeyBlock should exist... */
+int actkeyblock_get_valid_hold(ActKeyColumn *ac)
+{
+ /* check that block is valid */
+ if (!actkeyblock_is_valid(ac)) {
+ return 0;
+ }
+
+ const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD);
+ return (ac->block.flag & ~ac->block.conflict) & hold_mask;
+}
+
+/* *************************** Keyframe List Conversions *************************** */
+
+void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag)
+{
+ if (ac) {
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ /* get F-Curves to take keyframes from */
+ filter = ANIMFILTER_DATA_VISIBLE;
+ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+
+ /* loop through each F-Curve, grabbing the keyframes */
+ for (ale = anim_data.first; ale; ale = ale->next) {
+ /* Why not use all #eAnim_KeyType here?
+ * All of the other key types are actually "summaries" themselves,
+ * and will just end up duplicating stuff that comes up through
+ * standard filtering of just F-Curves. Given the way that these work,
+ * there isn't really any benefit at all from including them. - Aligorith */
+
+ switch (ale->datatype) {
+ case ALE_FCURVE:
+ fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
+ break;
+ case ALE_MASKLAY:
+ mask_to_keylist(ac->ads, ale->data, keys);
+ break;
+ case ALE_GPFRAME:
+ gpl_to_keylist(ac->ads, ale->data, keys);
+ break;
+ default:
+ // printf("%s: datatype %d unhandled\n", __func__, ale->datatype);
+ break;
+ }
+ }
+
+ ANIM_animdata_freelist(&anim_data);
+ }
+}
+
+void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction_flag)
+{
+ bAnimContext ac = {NULL};
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ bAnimListElem dummychan = {NULL};
+
+ if (sce == NULL) {
+ return;
+ }
+
+ /* create a dummy wrapper data to work with */
+ dummychan.type = ANIMTYPE_SCENE;
+ dummychan.data = sce;
+ dummychan.id = &sce->id;
+ dummychan.adt = sce->adt;
+
+ ac.ads = ads;
+ ac.data = &dummychan;
+ ac.datatype = ANIMCONT_CHANNEL;
+
+ /* get F-Curves to take keyframes from */
+ filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* loop through each F-Curve, grabbing the keyframes */
+ for (ale = anim_data.first; ale; ale = ale->next) {
+ fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
+ }
+
+ ANIM_animdata_freelist(&anim_data);
+}
+
+void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_flag)
+{
+ bAnimContext ac = {NULL};
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ bAnimListElem dummychan = {NULL};
+ Base dummybase = {NULL};
+
+ if (ob == NULL) {
+ return;
+ }
+
+ /* create a dummy wrapper data to work with */
+ dummybase.object = ob;
+
+ dummychan.type = ANIMTYPE_OBJECT;
+ dummychan.data = &dummybase;
+ dummychan.id = &ob->id;
+ dummychan.adt = ob->adt;
+
+ ac.ads = ads;
+ ac.data = &dummychan;
+ ac.datatype = ANIMCONT_CHANNEL;
+
+ /* get F-Curves to take keyframes from */
+ filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* loop through each F-Curve, grabbing the keyframes */
+ for (ale = anim_data.first; ale; ale = ale->next) {
+ fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
+ }
+
+ ANIM_animdata_freelist(&anim_data);
+}
+
+void cachefile_to_keylist(bDopeSheet *ads,
+ CacheFile *cache_file,
+ DLRBT_Tree *keys,
+ int saction_flag)
+{
+ if (cache_file == NULL) {
+ return;
+ }
+
+ /* create a dummy wrapper data to work with */
+ bAnimListElem dummychan = {NULL};
+ dummychan.type = ANIMTYPE_DSCACHEFILE;
+ dummychan.data = cache_file;
+ dummychan.id = &cache_file->id;
+ dummychan.adt = cache_file->adt;
+
+ bAnimContext ac = {NULL};
+ ac.ads = ads;
+ ac.data = &dummychan;
+ ac.datatype = ANIMCONT_CHANNEL;
+
+ /* get F-Curves to take keyframes from */
+ ListBase anim_data = {NULL, NULL};
+ int filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* loop through each F-Curve, grabbing the keyframes */
+ LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
+ fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
+ }
+
+ ANIM_animdata_freelist(&anim_data);
+}
+
+void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction_flag)
+{
+ if (fcu && fcu->totvert && fcu->bezt) {
+ /* apply NLA-mapping (if applicable) */
+ if (adt) {
+ ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
+ }
+
+ /* Check if the curve is cyclic. */
+ bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2);
+ bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0;
+
+ /* loop through beztriples, making ActKeysColumns */
+ BezTripleChain chain = {0};
+
+ for (int v = 0; v < fcu->totvert; v++) {
+ chain.cur = &fcu->bezt[v];
+
+ /* Neighbor keys, accounting for being cyclic. */
+ if (do_extremes) {
+ chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : NULL;
+ chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL;
+ }
+
+ add_bezt_to_keycolumns_list(keys, &chain);
+ }
+
+ /* Update keyblocks. */
+ update_keyblocks(keys, fcu->bezt, fcu->totvert);
+
+ /* unapply NLA-mapping if applicable */
+ if (adt) {
+ ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
+ }
+ }
+}
+
+void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, int saction_flag)
+{
+ FCurve *fcu;
+
+ if (agrp) {
+ /* loop through F-Curves */
+ for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) {
+ fcurve_to_keylist(adt, fcu, keys, saction_flag);
+ }
+ }
+}
+
+void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, int saction_flag)
+{
+ FCurve *fcu;
+
+ if (act) {
+ /* loop through F-Curves */
+ for (fcu = act->curves.first; fcu; fcu = fcu->next) {
+ fcurve_to_keylist(adt, fcu, keys, saction_flag);
+ }
+ }
+}
+
+void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active)
+{
+ bGPDlayer *gpl;
+
+ if (gpd && keys) {
+ /* for now, just aggregate out all the frames, but only for visible layers */
+ for (gpl = gpd->layers.last; gpl; gpl = gpl->prev) {
+ if ((gpl->flag & GP_LAYER_HIDE) == 0) {
+ if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) {
+ gpl_to_keylist(ads, gpl, keys);
+ }
+ }
+ }
+ }
+}
+
+void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, DLRBT_Tree *keys)
+{
+ bGPDframe *gpf;
+
+ if (gpl && keys) {
+ /* Although the frames should already be in an ordered list,
+ * they are not suitable for displaying yet. */
+ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ add_gpframe_to_keycolumns_list(keys, gpf);
+ }
+
+ update_keyblocks(keys, NULL, 0);
+ }
+}
+
+void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, DLRBT_Tree *keys)
+{
+ MaskLayerShape *masklay_shape;
+
+ if (masklay && keys) {
+ for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
+ masklay_shape = masklay_shape->next) {
+ add_masklay_to_keycolumns_list(keys, masklay_shape);
+ }
+
+ update_keyblocks(keys, NULL, 0);
+ }
+}
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index 0030e78002b..aff5803f037 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -20,6 +20,7 @@ set(INC
../../blenfont
../../blenkernel
../../blenlib
+ ../../blenloader
../../blentranslation
../../depsgraph
../../gpu
@@ -43,9 +44,11 @@ set(SRC
armature_utils.c
editarmature_undo.c
meshlaplacian.c
+ pose_backup.cc
pose_edit.c
pose_group.c
pose_lib.c
+ pose_lib_2.c
pose_select.c
pose_slide.c
pose_transform.c
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index ea6c71fd33f..fd5ae6c7099 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -286,8 +286,9 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op)
eCalcRollTypes type = RNA_enum_get(op->ptr, "type");
const bool axis_only = RNA_boolean_get(op->ptr, "axis_only");
/* axis_flip when matching the active bone never makes sense */
- bool axis_flip = ((type >= CALC_ROLL_ACTIVE) ? RNA_boolean_get(op->ptr, "axis_flip") :
- (type >= CALC_ROLL_TAN_NEG_X) ? true : false);
+ bool axis_flip = ((type >= CALC_ROLL_ACTIVE) ? RNA_boolean_get(op->ptr, "axis_flip") :
+ (type >= CALC_ROLL_TAN_NEG_X) ? true :
+ false);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
index d429e51061b..f9950d27e97 100644
--- a/source/blender/editors/armature/armature_intern.h
+++ b/source/blender/editors/armature/armature_intern.h
@@ -203,6 +203,10 @@ void POSELIB_OT_pose_move(struct wmOperatorType *ot);
void POSELIB_OT_browse_interactive(struct wmOperatorType *ot);
void POSELIB_OT_apply_pose(struct wmOperatorType *ot);
+/* pose_lib_2.c */
+void POSELIB_OT_apply_pose_asset(struct wmOperatorType *ot);
+void POSELIB_OT_blend_pose_asset(struct wmOperatorType *ot);
+
/* ******************************************************* */
/* Pose Sliding Tools */
/* pose_slide.c */
diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c
index a0face26bae..fbd89106de5 100644
--- a/source/blender/editors/armature/armature_ops.c
+++ b/source/blender/editors/armature/armature_ops.c
@@ -131,6 +131,8 @@ void ED_operatortypes_armature(void)
/* POSELIB */
WM_operatortype_append(POSELIB_OT_browse_interactive);
WM_operatortype_append(POSELIB_OT_apply_pose);
+ WM_operatortype_append(POSELIB_OT_apply_pose_asset);
+ WM_operatortype_append(POSELIB_OT_blend_pose_asset);
WM_operatortype_append(POSELIB_OT_pose_add);
WM_operatortype_append(POSELIB_OT_pose_remove);
diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c
index 874f1b49451..4fe4422e4e0 100644
--- a/source/blender/editors/armature/armature_utils.c
+++ b/source/blender/editors/armature/armature_utils.c
@@ -107,7 +107,7 @@ int bone_looper(Object *ob, Bone *bone, void *data, int (*bone_func)(Object *, B
{
/* We want to apply the function bone_func to every bone
* in an armature -- feed bone_looper the first bone and
- * a pointer to the bone_func and watch it go!. The int count
+ * a pointer to the bone_func and watch it go! The int count
* can be useful for counting bones with a certain property
* (e.g. skinnable)
*/
diff --git a/source/blender/editors/armature/pose_backup.cc b/source/blender/editors/armature/pose_backup.cc
new file mode 100644
index 00000000000..9c9347309b2
--- /dev/null
+++ b/source/blender/editors/armature/pose_backup.cc
@@ -0,0 +1,143 @@
+/*
+ * 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 edarmature
+ */
+
+#include "ED_armature.h"
+
+#include <cstring>
+
+#include "BLI_listbase.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_action_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_action.h"
+#include "BKE_action.hh"
+#include "BKE_armature.hh"
+#include "BKE_idprop.h"
+
+using namespace blender::bke;
+
+/* simple struct for storing backup info for one pose channel */
+struct PoseChannelBackup {
+ struct PoseChannelBackup *next, *prev;
+
+ struct bPoseChannel *pchan; /* Pose channel this backup is for. */
+
+ struct bPoseChannel olddata; /* Backup of pose channel. */
+ struct IDProperty *oldprops; /* Backup copy (needs freeing) of pose channel's ID properties. */
+};
+
+struct PoseBackup {
+ bool is_bone_selection_relevant;
+ ListBase /* PoseChannelBackup* */ backups;
+};
+
+static PoseBackup *pose_backup_create(const Object *ob,
+ const bAction *action,
+ const BoneNameSet &selected_bone_names)
+{
+ ListBase backups = {nullptr, nullptr};
+ const bool is_bone_selection_relevant = !selected_bone_names.is_empty();
+
+ BoneNameSet backed_up_bone_names;
+ /* Make a backup of the given pose channel. */
+ auto store_animated_pchans = [&](FCurve * /* unused */, const char *bone_name) {
+ if (backed_up_bone_names.contains(bone_name)) {
+ /* Only backup each bone once. */
+ return;
+ }
+
+ bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
+ if (pchan == nullptr) {
+ /* FCurve targets non-existent bone. */
+ return;
+ }
+
+ if (is_bone_selection_relevant && !selected_bone_names.contains(bone_name)) {
+ return;
+ }
+
+ PoseChannelBackup *chan_bak = static_cast<PoseChannelBackup *>(
+ MEM_callocN(sizeof(*chan_bak), "PoseChannelBackup"));
+ chan_bak->pchan = pchan;
+ memcpy(&chan_bak->olddata, chan_bak->pchan, sizeof(chan_bak->olddata));
+
+ if (pchan->prop) {
+ chan_bak->oldprops = IDP_CopyProperty(pchan->prop);
+ }
+
+ BLI_addtail(&backups, chan_bak);
+ backed_up_bone_names.add_new(bone_name);
+ };
+
+ /* Call `store_animated_pchans()` for each FCurve that targets a bone. */
+ BKE_action_find_fcurves_with_bones(action, store_animated_pchans);
+
+ /* PoseBackup is constructed late, so that the above loop can use stack variables. */
+ PoseBackup *pose_backup = static_cast<PoseBackup *>(MEM_callocN(sizeof(*pose_backup), __func__));
+ pose_backup->is_bone_selection_relevant = is_bone_selection_relevant;
+ pose_backup->backups = backups;
+ return pose_backup;
+}
+
+PoseBackup *ED_pose_backup_create_all_bones(const Object *ob, const bAction *action)
+{
+ return pose_backup_create(ob, action, BoneNameSet());
+}
+
+PoseBackup *ED_pose_backup_create_selected_bones(const Object *ob, const bAction *action)
+{
+ const bArmature *armature = static_cast<const bArmature *>(ob->data);
+ const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature);
+ return pose_backup_create(ob, action, selected_bone_names);
+}
+
+bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup)
+{
+ return pose_backup->is_bone_selection_relevant;
+}
+
+void ED_pose_backup_restore(const PoseBackup *pbd)
+{
+ LISTBASE_FOREACH (PoseChannelBackup *, chan_bak, &pbd->backups) {
+ memcpy(chan_bak->pchan, &chan_bak->olddata, sizeof(chan_bak->olddata));
+
+ if (chan_bak->oldprops) {
+ IDP_SyncGroupValues(chan_bak->pchan->prop, chan_bak->oldprops);
+ }
+
+ /* TODO: constraints settings aren't restored yet,
+ * even though these could change (though not that likely) */
+ }
+}
+
+void ED_pose_backup_free(PoseBackup *pbd)
+{
+ LISTBASE_FOREACH_MUTABLE (PoseChannelBackup *, chan_bak, &pbd->backups) {
+ if (chan_bak->oldprops) {
+ IDP_FreeProperty(chan_bak->oldprops);
+ }
+ BLI_freelinkN(&pbd->backups, chan_bak);
+ }
+ MEM_freeN(pbd);
+}
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index df3550d5db6..cb70b2810d1 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -62,8 +62,8 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
-#include "ED_keyframes_draw.h"
#include "ED_keyframes_edit.h"
+#include "ED_keyframes_keylist.h"
#include "ED_keyframing.h"
#include "ED_object.h"
#include "ED_screen.h"
diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c
new file mode 100644
index 00000000000..32440f941ba
--- /dev/null
+++ b/source/blender/editors/armature/pose_lib_2.c
@@ -0,0 +1,638 @@
+/*
+ * 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
+ */
+
+/** \file
+ * \ingroup edarmature
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_string.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_armature_types.h"
+
+#include "BKE_action.h"
+#include "BKE_anim_data.h"
+#include "BKE_animsys.h"
+#include "BKE_armature.h"
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+
+#include "DEG_depsgraph.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+
+#include "ED_armature.h"
+#include "ED_asset.h"
+#include "ED_keyframing.h"
+#include "ED_screen.h"
+
+#include "armature_intern.h"
+
+typedef enum ePoseBlendState {
+ POSE_BLEND_INIT,
+ POSE_BLEND_BLENDING,
+ POSE_BLEND_ORIGINAL,
+ POSE_BLEND_CONFIRM,
+ POSE_BLEND_CANCEL,
+} ePoseBlendState;
+
+typedef struct PoseBlendData {
+ ePoseBlendState state;
+ bool needs_redraw;
+
+ struct {
+ bool use_release_confirm;
+ int drag_start_xy[2];
+ int init_event_type;
+
+ bool cursor_wrap_enabled;
+ } release_confirm_info;
+
+ /* For temp-loading the Action from the pose library. */
+ AssetTempIDConsumer *temp_id_consumer;
+
+ /* Blend factor, interval [0, 1] for interpolating between current and given pose. */
+ float blend_factor;
+ struct PoseBackup *pose_backup;
+
+ Object *ob; /* Object to work on. */
+ bAction *act; /* Pose to blend into the current pose. */
+ bool free_action;
+
+ Scene *scene; /* For auto-keying. */
+ ScrArea *area; /* For drawing status text. */
+
+ /** Info-text to print in header. */
+ char headerstr[UI_MAX_DRAW_STR];
+} PoseBlendData;
+
+/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
+static void poselib_backup_posecopy(PoseBlendData *pbd)
+{
+ pbd->pose_backup = ED_pose_backup_create_selected_bones(pbd->ob, pbd->act);
+
+ if (pbd->state == POSE_BLEND_INIT) {
+ /* Ready for blending now. */
+ pbd->state = POSE_BLEND_BLENDING;
+ }
+}
+
+/* ---------------------------- */
+
+/* Auto-key/tag bones affected by the pose Action. */
+static void poselib_keytag_pose(bContext *C, Scene *scene, PoseBlendData *pbd)
+{
+ if (!autokeyframe_cfra_can_key(scene, &pbd->ob->id)) {
+ return;
+ }
+
+ AnimData *adt = BKE_animdata_from_id(&pbd->ob->id);
+ if (adt != NULL && adt->action != NULL && ID_IS_LINKED(&adt->action->id)) {
+ /* Changes to linked-in Actions are not allowed. */
+ return;
+ }
+
+ bPose *pose = pbd->ob->pose;
+ bAction *act = pbd->act;
+
+ KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID);
+ ListBase dsources = {NULL, NULL};
+
+ /* start tagging/keying */
+ const bArmature *armature = pbd->ob->data;
+ LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
+ /* Only for selected bones unless there aren't any selected, in which case all are included. */
+ bPoseChannel *pchan = BKE_pose_channel_find_name(pose, agrp->name);
+ if (pchan == NULL) {
+ continue;
+ }
+
+ if (ED_pose_backup_is_selection_relevant(pbd->pose_backup) &&
+ !PBONE_SELECTED(armature, pchan->bone)) {
+ continue;
+ }
+
+ /* Add data-source override for the PoseChannel, to be used later. */
+ ANIM_relative_keyingset_add_source(&dsources, &pbd->ob->id, &RNA_PoseBone, pchan);
+ }
+
+ /* Perform actual auto-keying. */
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+ BLI_freelistN(&dsources);
+
+ /* send notifiers for this */
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+}
+
+/* Apply the relevant changes to the pose */
+static void poselib_blend_apply(bContext *C, wmOperator *op)
+{
+ PoseBlendData *pbd = (PoseBlendData *)op->customdata;
+
+ if (pbd->state == POSE_BLEND_BLENDING) {
+ BLI_snprintf(pbd->headerstr,
+ sizeof(pbd->headerstr),
+ TIP_("PoseLib blending: \"%s\" at %3.0f%%"),
+ pbd->act->id.name + 2,
+ pbd->blend_factor * 100);
+ ED_area_status_text(pbd->area, pbd->headerstr);
+
+ ED_workspace_status_text(
+ C, TIP_("Tab: show original pose; Horizontal mouse movement: change blend percentage"));
+ }
+ else {
+ ED_area_status_text(pbd->area, TIP_("PoseLib showing original pose"));
+ ED_workspace_status_text(C, TIP_("Tab: show blended pose"));
+ }
+
+ if (!pbd->needs_redraw) {
+ return;
+ }
+ pbd->needs_redraw = false;
+
+ ED_pose_backup_restore(pbd->pose_backup);
+
+ /* The pose needs updating, whether it's for restoring the original pose or for showing the
+ * result of the blend. */
+ DEG_id_tag_update(&pbd->ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pbd->ob);
+
+ if (pbd->state != POSE_BLEND_BLENDING) {
+ return;
+ }
+
+ /* Perform the actual blending. */
+ struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f);
+ BKE_pose_apply_action_blend(pbd->ob, pbd->act, &anim_eval_context, pbd->blend_factor);
+}
+
+/* ---------------------------- */
+
+static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor)
+{
+ pbd->blend_factor = CLAMPIS(new_factor, 0.0f, 1.0f);
+ pbd->needs_redraw = true;
+}
+
+static void poselib_slide_mouse_update_blendfactor(PoseBlendData *pbd, const wmEvent *event)
+{
+ if (pbd->release_confirm_info.use_release_confirm) {
+ /* Release confirm calculates factor based on where the dragging was started from. */
+ const float range = 300 * U.pixelsize;
+ const float new_factor = (event->x - pbd->release_confirm_info.drag_start_xy[0]) / range;
+ poselib_blend_set_factor(pbd, new_factor);
+ }
+ else {
+ const float new_factor = (event->x - pbd->area->v1->vec.x) / ((float)pbd->area->winx);
+ poselib_blend_set_factor(pbd, new_factor);
+ }
+}
+
+/* Return operator return value. */
+static int poselib_blend_handle_event(bContext *UNUSED(C), wmOperator *op, const wmEvent *event)
+{
+ PoseBlendData *pbd = op->customdata;
+
+ if (event->type == MOUSEMOVE) {
+ poselib_slide_mouse_update_blendfactor(pbd, event);
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ /* Handle the release confirm event directly, it has priority over others. */
+ if (pbd->release_confirm_info.use_release_confirm &&
+ (event->type == pbd->release_confirm_info.init_event_type) && (event->val == KM_RELEASE)) {
+ pbd->state = POSE_BLEND_CONFIRM;
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ /* only accept 'press' event, and ignore 'release', so that we don't get double actions */
+ if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ /* NORMAL EVENT HANDLING... */
+ /* searching takes priority over normal activity */
+ switch (event->type) {
+ /* Exit - cancel. */
+ case EVT_ESCKEY:
+ case RIGHTMOUSE:
+ pbd->state = POSE_BLEND_CANCEL;
+ break;
+
+ /* Exit - confirm. */
+ case LEFTMOUSE:
+ case EVT_RETKEY:
+ case EVT_PADENTER:
+ case EVT_SPACEKEY:
+ pbd->state = POSE_BLEND_CONFIRM;
+ break;
+
+ /* TODO(Sybren): toggle between original pose and poselib pose. */
+ case EVT_TABKEY:
+ pbd->state = pbd->state == POSE_BLEND_BLENDING ? POSE_BLEND_ORIGINAL : POSE_BLEND_BLENDING;
+ pbd->needs_redraw = true;
+ break;
+
+ /* TODO(Sybren): use better UI for slider. */
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void poselib_blend_cursor_update(bContext *C, wmOperator *op)
+{
+ PoseBlendData *pbd = op->customdata;
+
+ /* Ensure cursor-grab (continuous grabbing) is enabled when using release-confirm. */
+ if (pbd->release_confirm_info.use_release_confirm &&
+ !pbd->release_confirm_info.cursor_wrap_enabled) {
+ WM_cursor_grab_enable(CTX_wm_window(C), WM_CURSOR_WRAP_XY, true, NULL);
+ pbd->release_confirm_info.cursor_wrap_enabled = true;
+ }
+}
+
+/* ---------------------------- */
+
+static Object *get_poselib_object(bContext *C)
+{
+ if (C == NULL) {
+ return NULL;
+ }
+ return BKE_object_pose_armature_get(CTX_data_active_object(C));
+}
+
+static void poselib_tempload_exit(PoseBlendData *pbd)
+{
+ ED_asset_temp_id_consumer_free(&pbd->temp_id_consumer);
+}
+
+static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op)
+{
+ bool asset_handle_valid;
+ const AssetLibraryReference *asset_library = CTX_wm_asset_library(C);
+ const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid);
+ /* Poll callback should check. */
+ BLI_assert((asset_library != NULL) && asset_handle_valid);
+
+ PoseBlendData *pbd = op->customdata;
+
+ pbd->temp_id_consumer = ED_asset_temp_id_consumer_create(&asset_handle);
+ return (bAction *)ED_asset_temp_id_consumer_ensure_local_id(
+ pbd->temp_id_consumer, C, asset_library, ID_AC, CTX_data_main(C), op->reports);
+}
+
+static bAction *flip_pose(bContext *C, Object *ob, bAction *action)
+{
+ bAction *action_copy = (bAction *)BKE_id_copy_ex(NULL, &action->id, NULL, LIB_ID_COPY_LOCALIZE);
+
+ /* Lock the window manager while flipping the pose. Flipping requires temporarily modifying the
+ * pose, which can cause unwanted visual glitches. */
+ wmWindowManager *wm = CTX_wm_manager(C);
+ const bool interface_was_locked = CTX_wm_interface_locked(C);
+ WM_set_locked_interface(wm, true);
+
+ BKE_action_flip_with_pose(action_copy, ob);
+
+ WM_set_locked_interface(wm, interface_was_locked);
+ return action_copy;
+}
+
+/* Return true on success, false if the context isn't suitable. */
+static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ op->customdata = NULL;
+
+ /* check if valid poselib */
+ Object *ob = get_poselib_object(C);
+ if (ELEM(NULL, ob, ob->pose, ob->data)) {
+ BKE_report(op->reports, RPT_ERROR, TIP_("Pose lib is only for armatures in pose mode"));
+ return false;
+ }
+
+ /* Set up blend state info. */
+ PoseBlendData *pbd;
+ op->customdata = pbd = MEM_callocN(sizeof(PoseBlendData), "PoseLib Preview Data");
+
+ bAction *action = poselib_blend_init_get_action(C, op);
+ if (action == NULL) {
+ return false;
+ }
+
+ /* Maybe flip the Action. */
+ const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped");
+ if (apply_flipped) {
+ action = flip_pose(C, ob, action);
+ pbd->free_action = true;
+ }
+ pbd->act = action;
+
+ /* Get the basic data. */
+ pbd->ob = ob;
+ pbd->ob->pose = ob->pose;
+
+ pbd->scene = CTX_data_scene(C);
+ pbd->area = CTX_wm_area(C);
+
+ pbd->state = POSE_BLEND_INIT;
+ pbd->needs_redraw = true;
+ pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor");
+ /* Just to avoid a clang-analyzer warning (false positive), it's set properly below. */
+ pbd->release_confirm_info.use_release_confirm = false;
+
+ /* Release confirm data. Only available if there's an event to work with. */
+ if (event != NULL) {
+ PropertyRNA *release_confirm_prop = RNA_struct_find_property(op->ptr, "release_confirm");
+ pbd->release_confirm_info.use_release_confirm = (release_confirm_prop != NULL) &&
+ RNA_property_boolean_get(op->ptr,
+ release_confirm_prop);
+ }
+
+ if (pbd->release_confirm_info.use_release_confirm) {
+ BLI_assert(event != NULL);
+ pbd->release_confirm_info.drag_start_xy[0] = event->x;
+ pbd->release_confirm_info.drag_start_xy[1] = event->y;
+ pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type(
+ event->type);
+ }
+
+ /* Make backups for blending and restoring the pose. */
+ poselib_backup_posecopy(pbd);
+
+ /* Set pose flags to ensure the depsgraph evaluation doesn't overwrite it. */
+ pbd->ob->pose->flag &= ~POSE_DO_UNLOCK;
+ pbd->ob->pose->flag |= POSE_LOCKED;
+
+ return true;
+}
+
+static void poselib_blend_cleanup(bContext *C, wmOperator *op)
+{
+ PoseBlendData *pbd = op->customdata;
+ wmWindow *win = CTX_wm_window(C);
+
+ /* Redraw the header so that it doesn't show any of our stuff anymore. */
+ ED_area_status_text(pbd->area, NULL);
+ ED_workspace_status_text(C, NULL);
+
+ /* This signals the depsgraph to unlock and reevaluate the pose on the next evaluation. */
+ bPose *pose = pbd->ob->pose;
+ pose->flag |= POSE_DO_UNLOCK;
+
+ switch (pbd->state) {
+ case POSE_BLEND_CONFIRM: {
+ Scene *scene = pbd->scene;
+ poselib_keytag_pose(C, scene, pbd);
+
+ /* Ensure the redo panel has the actually-used value, instead of the initial value. */
+ RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor);
+ break;
+ }
+
+ case POSE_BLEND_INIT:
+ case POSE_BLEND_BLENDING:
+ case POSE_BLEND_ORIGINAL:
+ /* Cleanup should not be called directly from these states. */
+ BLI_assert_msg(0, "poselib_blend_cleanup: unexpected pose blend state");
+ BKE_report(op->reports, RPT_ERROR, "Internal pose library error, cancelling operator");
+ ATTR_FALLTHROUGH;
+ case POSE_BLEND_CANCEL:
+ ED_pose_backup_restore(pbd->pose_backup);
+ break;
+ }
+
+ if (pbd->release_confirm_info.cursor_wrap_enabled) {
+ WM_cursor_grab_disable(win, pbd->release_confirm_info.drag_start_xy);
+ pbd->release_confirm_info.cursor_wrap_enabled = false;
+ }
+
+ DEG_id_tag_update(&pbd->ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pbd->ob);
+ /* Update mouse-hover highlights. */
+ WM_event_add_mousemove(win);
+}
+
+static void poselib_blend_free(wmOperator *op)
+{
+ PoseBlendData *pbd = op->customdata;
+ if (pbd == NULL) {
+ return;
+ }
+
+ if (pbd->free_action) {
+ /* Run before #poselib_tempload_exit to avoid any problems from indirectly
+ * referenced ID pointers. */
+ BKE_id_free(NULL, pbd->act);
+ }
+ poselib_tempload_exit(pbd);
+
+ /* Must have been dealt with before! */
+ BLI_assert(pbd->release_confirm_info.cursor_wrap_enabled == false);
+
+ /* Free temp data for operator */
+ ED_pose_backup_free(pbd->pose_backup);
+ pbd->pose_backup = NULL;
+
+ MEM_SAFE_FREE(op->customdata);
+}
+
+static int poselib_blend_exit(bContext *C, wmOperator *op)
+{
+ PoseBlendData *pbd = op->customdata;
+ const ePoseBlendState exit_state = pbd->state;
+
+ poselib_blend_cleanup(C, op);
+ poselib_blend_free(op);
+
+ if (exit_state == POSE_BLEND_CANCEL) {
+ return OPERATOR_CANCELLED;
+ }
+ return OPERATOR_FINISHED;
+}
+
+/* Cancel previewing operation (called when exiting Blender) */
+static void poselib_blend_cancel(bContext *C, wmOperator *op)
+{
+ PoseBlendData *pbd = op->customdata;
+ pbd->state = POSE_BLEND_CANCEL;
+ poselib_blend_exit(C, op);
+}
+
+/* Main modal status check. */
+static int poselib_blend_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const int operator_result = poselib_blend_handle_event(C, op, event);
+
+ poselib_blend_cursor_update(C, op);
+
+ const PoseBlendData *pbd = op->customdata;
+ if (ELEM(pbd->state, POSE_BLEND_CONFIRM, POSE_BLEND_CANCEL)) {
+ return poselib_blend_exit(C, op);
+ }
+
+ if (pbd->needs_redraw) {
+ poselib_blend_apply(C, op);
+ }
+
+ return operator_result;
+}
+
+/* Modal Operator init. */
+static int poselib_blend_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!poselib_blend_init_data(C, op, event)) {
+ poselib_blend_free(op);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Do initial apply to have something to look at. */
+ poselib_blend_apply(C, op);
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Single-shot apply. */
+static int poselib_blend_exec(bContext *C, wmOperator *op)
+{
+ if (!poselib_blend_init_data(C, op, NULL)) {
+ poselib_blend_free(op);
+ return OPERATOR_CANCELLED;
+ }
+
+ poselib_blend_apply(C, op);
+
+ PoseBlendData *pbd = op->customdata;
+ pbd->state = POSE_BLEND_CONFIRM;
+ return poselib_blend_exit(C, op);
+}
+
+static bool poselib_asset_in_context(bContext *C)
+{
+ bool asset_handle_valid;
+ /* Check whether the context provides the asset data needed to add a pose. */
+ const AssetLibraryReference *asset_library = CTX_wm_asset_library(C);
+ AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid);
+
+ return (asset_library != NULL) && asset_handle_valid &&
+ (ED_asset_handle_get_id_type(&asset_handle) == ID_AC);
+}
+
+/* Poll callback for operators that require existing PoseLib data (with poses) to work. */
+static bool poselib_blend_poll(bContext *C)
+{
+ Object *ob = get_poselib_object(C);
+ if (ELEM(NULL, ob, ob->pose, ob->data)) {
+ /* Pose lib is only for armatures in pose mode. */
+ return false;
+ }
+
+ return poselib_asset_in_context(C);
+}
+
+void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
+{
+ /* Identifiers: */
+ ot->name = "Apply Pose Library Pose";
+ ot->idname = "POSELIB_OT_apply_pose_asset";
+ ot->description = "Apply the given Pose Action to the rig";
+
+ /* Callbacks: */
+ ot->exec = poselib_blend_exec;
+ ot->poll = poselib_blend_poll;
+
+ /* Flags: */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* Properties: */
+ RNA_def_float_factor(ot->srna,
+ "blend_factor",
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ "Blend Factor",
+ "Amount that the pose is applied on top of the existing poses",
+ 0.0f,
+ 1.0f);
+ RNA_def_boolean(ot->srna,
+ "flipped",
+ false,
+ "Apply Flipped",
+ "When enabled, applies the pose flipped over the X-axis");
+}
+
+void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* Identifiers: */
+ ot->name = "Blend Pose Library Pose";
+ ot->idname = "POSELIB_OT_blend_pose_asset";
+ ot->description = "Blend the given Pose Action to the rig";
+
+ /* Callbacks: */
+ ot->invoke = poselib_blend_invoke;
+ ot->modal = poselib_blend_modal;
+ ot->cancel = poselib_blend_cancel;
+ ot->exec = poselib_blend_exec;
+ ot->poll = poselib_blend_poll;
+
+ /* Flags: */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+
+ /* Properties: */
+ prop = RNA_def_float_factor(ot->srna,
+ "blend_factor",
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ "Blend Factor",
+ "Amount that the pose is applied on top of the existing poses",
+ 0.0f,
+ 1.0f);
+ /* Blending should always start at 0%, and not at whatever percentage was last used. This RNA
+ * property just exists for symmetry with the Apply operator (and thus simplicity of the rest of
+ * the code, which can assume this property exists). */
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ RNA_def_boolean(ot->srna,
+ "flipped",
+ false,
+ "Apply Flipped",
+ "When enabled, applies the pose flipped over the X-axis");
+ prop = RNA_def_boolean(ot->srna,
+ "release_confirm",
+ false,
+ "Confirm on Release",
+ "Always confirm operation when releasing button");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index 1a1685e4a01..38f562ebf25 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -77,11 +77,12 @@
#include "UI_resources.h"
#include "ED_armature.h"
-#include "ED_keyframes_draw.h"
+#include "ED_keyframes_keylist.h"
#include "ED_markers.h"
#include "ED_numinput.h"
#include "ED_screen.h"
#include "ED_space_api.h"
+#include "ED_util.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
@@ -137,8 +138,8 @@ typedef struct tPoseSlideOp {
Scene *scene;
/** area that we're operating in (needed for modal()) */
ScrArea *area;
- /** Header of the region used for drawing the slider. */
- ARegion *region_header;
+ /** Region we're operating in (needed for modal()). */
+ ARegion *region;
/** len of the PoseSlideObject array. */
uint objects_len;
@@ -168,26 +169,7 @@ typedef struct tPoseSlideOp {
/** Axis-limits for transforms. */
ePoseSlide_AxisLock axislock;
- /** Allow overshoot or clamp between 0% and 100%. */
- bool overshoot;
-
- /** Reduces factor delta from mouse movement. */
- bool precision;
-
- /** Move factor in 10% steps. */
- bool increments;
-
- /** Draw callback handler. */
- void *draw_handle;
-
- /** Accumulative, unclamped and unrounded factor. */
- float raw_factor;
-
- /** 0-1 value for determining the influence of whatever is relevant. */
- float factor;
-
- /** Last cursor position in screen space used for mouse movement delta calculation. */
- int last_cursor_x;
+ struct tSlider *slider;
/** Numeric input. */
NumInput num;
@@ -232,256 +214,6 @@ static const EnumPropertyItem prop_axis_lock_types[] = {
/* ------------------------------------ */
-static void draw_overshoot_triangle(const uint8_t color[4],
- const bool facing_right,
- const float x,
- const float y)
-{
- const uint shdr_pos_2d = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- GPU_blend(GPU_BLEND_ALPHA);
- GPU_polygon_smooth(true);
- immUniformColor3ubvAlpha(color, 225);
- const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize;
- const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize;
-
- immBegin(GPU_PRIM_TRIS, 3);
- immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y);
- immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2);
- immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2);
- immEnd();
-
- GPU_polygon_smooth(false);
- GPU_blend(GPU_BLEND_NONE);
- immUnbindProgram();
-}
-
-static void draw_ticks(const float start_factor,
- const float end_factor,
- const float line_start[2],
- const float base_tick_height,
- const float line_width,
- const uint8_t color_overshoot[4],
- const uint8_t color_line[4])
-{
- /* Use factor represented as 0-100 int to avoid floating point precision problems. */
- const int tick_increment = 10;
-
- /* Round initial_tick_factor up to the next tick_increment. */
- int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment;
-
- while (tick_percentage <= (int)(end_factor * 100)) {
- float tick_height;
- /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit
- * smaller and the rest is the minimum size. */
- if (tick_percentage % 100 == 0) {
- tick_height = base_tick_height;
- }
- else if (tick_percentage % 50 == 0) {
- tick_height = base_tick_height * 0.8;
- }
- else {
- tick_height = base_tick_height * 0.5;
- }
-
- const float x = line_start[0] +
- (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE;
- const rctf tick_rect = {
- .xmin = x - (line_width / 2),
- .xmax = x + (line_width / 2),
- .ymin = line_start[1] - (tick_height / 2),
- .ymax = line_start[1] + (tick_height / 2),
- };
-
- if (tick_percentage < 0 || tick_percentage > 100) {
- UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255);
- }
- else {
- UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255);
- }
- tick_percentage += tick_increment;
- }
-}
-
-static void draw_main_line(const rctf *main_line_rect,
- const float factor,
- const bool overshoot,
- const uint8_t color_overshoot[4],
- const uint8_t color_line[4])
-{
- if (overshoot) {
- /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */
- const float line_zero_percent = main_line_rect->xmin -
- ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) *
- SLIDE_PIXEL_DISTANCE);
-
- const float clamped_line_zero_percent = clamp_f(
- line_zero_percent, main_line_rect->xmin, main_line_rect->xmax);
- const float clamped_line_hundred_percent = clamp_f(
- line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax);
-
- const rctf left_overshoot_line_rect = {
- .xmin = main_line_rect->xmin,
- .xmax = clamped_line_zero_percent,
- .ymin = main_line_rect->ymin,
- .ymax = main_line_rect->ymax,
- };
- const rctf right_overshoot_line_rect = {
- .xmin = clamped_line_hundred_percent,
- .xmax = main_line_rect->xmax,
- .ymin = main_line_rect->ymin,
- .ymax = main_line_rect->ymax,
- };
- UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255);
- UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255);
-
- const rctf non_overshoot_line_rect = {
- .xmin = clamped_line_zero_percent,
- .xmax = clamped_line_hundred_percent,
- .ymin = main_line_rect->ymin,
- .ymax = main_line_rect->ymax,
- };
- UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255);
- }
- else {
- UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255);
- }
-}
-
-static void draw_backdrop(const int fontid,
- const rctf *main_line_rect,
- const float color_bg[4],
- const short region_y_size,
- const float base_tick_height)
-{
- float string_pixel_size[2];
- const char *percentage_string_placeholder = "000%%";
- BLF_width_and_height(fontid,
- percentage_string_placeholder,
- sizeof(percentage_string_placeholder),
- &string_pixel_size[0],
- &string_pixel_size[1]);
- const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize};
- const rctf backdrop_rect = {
- .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0],
- .xmax = main_line_rect->xmax + pad[0],
- .ymin = pad[1],
- .ymax = region_y_size - pad[1],
- };
- UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg);
-}
-
-/**
- * Draw an on screen Slider for a Pose Slide Operator.
- */
-static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg)
-{
- tPoseSlideOp *pso = arg;
-
- /* Only draw in region from which the Operator was started. */
- if (region != pso->region_header) {
- return;
- }
-
- uint8_t color_text[4];
- uint8_t color_line[4];
- uint8_t color_handle[4];
- uint8_t color_overshoot[4];
- float color_bg[4];
-
- /* Get theme colors. */
- UI_GetThemeColor4ubv(TH_TEXT, color_text);
- UI_GetThemeColor4ubv(TH_TEXT, color_line);
- UI_GetThemeColor4ubv(TH_TEXT, color_overshoot);
- UI_GetThemeColor4ubv(TH_ACTIVE, color_handle);
- UI_GetThemeColor3fv(TH_BACK, color_bg);
-
- color_bg[3] = 0.5f;
- color_overshoot[0] = color_overshoot[0] * 0.7;
- color_overshoot[1] = color_overshoot[1] * 0.7;
- color_overshoot[2] = color_overshoot[2] * 0.7;
-
- /* Get the default font. */
- const uiStyle *style = UI_style_get();
- const uiFontStyle *fstyle = &style->widget;
- const int fontid = fstyle->uifont_id;
- BLF_color3ubv(fontid, color_text);
- BLF_rotation(fontid, 0.0f);
-
- const float line_width = 1.5 * U.pixelsize;
- const float base_tick_height = 12.0 * U.pixelsize;
- const float line_y = region->winy / 2;
-
- rctf main_line_rect = {
- .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2),
- .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2),
- .ymin = line_y - line_width / 2,
- .ymax = line_y + line_width / 2,
- };
- float line_start_factor = 0;
- int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->factor;
-
- if (pso->overshoot) {
- main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
- main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
- line_start_factor = pso->factor - 0.5f - OVERSHOOT_RANGE_DELTA;
- handle_pos_x = region->winx / 2;
- }
-
- draw_backdrop(fontid, &main_line_rect, color_bg, pso->region_header->winy, base_tick_height);
-
- draw_main_line(&main_line_rect, pso->factor, pso->overshoot, color_overshoot, color_line);
-
- const float factor_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1;
- const float line_start_position[2] = {main_line_rect.xmin, line_y};
- draw_ticks(line_start_factor,
- line_start_factor + factor_range,
- line_start_position,
- base_tick_height,
- line_width,
- color_overshoot,
- color_line);
-
- /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100%
- * range. */
- if (pso->overshoot) {
- if (pso->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) {
- draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y);
- }
- if (pso->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) {
- draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y);
- }
- }
-
- char percentage_string[256];
-
- /* Draw handle indicating current factor. */
- const rctf handle_rect = {
- .xmin = handle_pos_x - (line_width),
- .xmax = handle_pos_x + (line_width),
- .ymin = line_y - (base_tick_height / 2),
- .ymax = line_y + (base_tick_height / 2),
- };
-
- UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255);
- BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->factor * 100);
-
- /* Draw percentage string. */
- float percentage_string_pixel_size[2];
- BLF_width_and_height(fontid,
- percentage_string,
- sizeof(percentage_string),
- &percentage_string_pixel_size[0],
- &percentage_string_pixel_size[1]);
-
- BLF_position(fontid,
- main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2,
- (region->winy / 2) - percentage_string_pixel_size[1] / 2,
- 0.0f);
- BLF_draw(fontid, percentage_string, sizeof(percentage_string));
-}
-
/** Operator custom-data initialization. */
static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
{
@@ -492,15 +224,13 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
/* Get info from context. */
pso->scene = CTX_data_scene(C);
- pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */
- pso->region_header = CTX_wm_region(C); /* Only really needed when doing modal(). */
+ pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */
+ pso->region = CTX_wm_region(C); /* Only really needed when doing modal(). */
pso->cframe = pso->scene->r.cfra;
pso->mode = mode;
/* Set range info from property values - these may get overridden for the invoke(). */
- pso->factor = RNA_float_get(op->ptr, "factor");
- pso->raw_factor = pso->factor;
pso->prevFrame = RNA_int_get(op->ptr, "prev_frame");
pso->nextFrame = RNA_int_get(op->ptr, "next_frame");
@@ -508,6 +238,9 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
pso->channels = RNA_enum_get(op->ptr, "channels");
pso->axislock = RNA_enum_get(op->ptr, "axis_lock");
+ pso->slider = ED_slider_create(C);
+ ED_slider_factor_set(pso->slider, RNA_float_get(op->ptr, "factor"));
+
/* For each Pose-Channel which gets affected, get the F-Curves for that channel
* and set the relevant transform flags. */
poseAnim_mapping_get(C, &pso->pfLinks);
@@ -552,14 +285,6 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
pso->num.val_flag[0] |= NUM_NO_NEGATIVE;
pso->num.unit_type[0] = B_UNIT_NONE; /* Percentages don't have any units. */
- /* Register UI drawing callback. */
- ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER);
- if (region_header != NULL) {
- pso->region_header = region_header;
- pso->draw_handle = ED_region_draw_cb_activate(
- region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL);
- }
-
/* Return status is whether we've got all the data we were requested to get. */
return 1;
}
@@ -567,17 +292,16 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
/**
* Exiting the operator (free data).
*/
-static void pose_slide_exit(wmOperator *op)
+static void pose_slide_exit(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso = op->customdata;
+ ED_slider_destroy(C, pso->slider);
+
/* Hide Bone Overlay. */
View3D *v3d = pso->area->spacedata.first;
v3d->overlay.flag = pso->overlay_flag;
- /* Remove UI drawing callback. */
- ED_region_draw_cb_exit(pso->region_header->type, pso->draw_handle);
-
/* Free the temp pchan links and their data. */
poseAnim_mapping_free(&pso->pfLinks);
@@ -655,8 +379,8 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo
/* Calculate the relative weights of the endpoints. */
if (pso->mode == POSESLIDE_BREAKDOWN) {
/* Get weights from the factor control. */
- w1 = pso->factor; /* This must come second. */
- w2 = 1.0f - w1; /* This must come first. */
+ w1 = ED_slider_factor_get(pso->slider); /* This must come second. */
+ w2 = 1.0f - w1; /* This must come first. */
}
else {
/* - these weights are derived from the relative distance of these
@@ -682,13 +406,13 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo
case POSESLIDE_PUSH: /* Make the current pose more pronounced. */
{
/* Slide the pose away from the breakdown pose in the timeline */
- (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor;
+ (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * ED_slider_factor_get(pso->slider);
break;
}
case POSESLIDE_RELAX: /* Make the current pose more like its surrounding ones. */
{
/* Slide the pose towards the breakdown pose in the timeline */
- (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor;
+ (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * ED_slider_factor_get(pso->slider);
break;
}
case POSESLIDE_BREAKDOWN: /* Make the current pose slide around between the endpoints. */
@@ -888,7 +612,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
normalize_qt(quat_prev);
normalize_qt(quat_next);
- interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->factor);
+ interp_qt_qtqt(quat_final, quat_prev, quat_next, ED_slider_factor_get(pso->slider));
}
else {
/* POSESLIDE_PUSH and POSESLIDE_RELAX. */
@@ -906,11 +630,12 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
normalize_qt(quat_curr);
if (pso->mode == POSESLIDE_PUSH) {
- interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->factor);
+ interp_qt_qtqt(
+ quat_final, quat_breakdown, quat_curr, 1.0f + ED_slider_factor_get(pso->slider));
}
else {
BLI_assert(pso->mode == POSESLIDE_RELAX);
- interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->factor);
+ interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, ED_slider_factor_get(pso->slider));
}
}
@@ -931,11 +656,11 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo
((lock & PS_LOCK_Z) && (idx == 2))) {
float diff_val = default_value - vec[idx];
if (pso->mode == POSESLIDE_RELAX_REST) {
- vec[idx] += pso->factor * diff_val;
+ vec[idx] += ED_slider_factor_get(pso->slider) * diff_val;
}
else {
/* Push */
- vec[idx] -= pso->factor * diff_val;
+ vec[idx] -= ED_slider_factor_get(pso->slider) * diff_val;
}
}
}
@@ -953,11 +678,11 @@ static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4]
for (int idx = 0; idx < 4; idx++) {
float diff_val = default_values[idx] - vec[idx];
if (pso->mode == POSESLIDE_RELAX_REST) {
- vec[idx] += pso->factor * diff_val;
+ vec[idx] += ED_slider_factor_get(pso->slider) * diff_val;
}
else {
/* Push */
- vec[idx] -= pso->factor * diff_val;
+ vec[idx] -= ED_slider_factor_get(pso->slider) * diff_val;
}
}
}
@@ -1130,9 +855,7 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
char limits_str[UI_MAX_DRAW_STR];
char axis_str[50];
char mode_str[32];
- char overshoot_str[50];
- char precision_str[50];
- char increments_str[50];
+ char slider_str[UI_MAX_DRAW_STR];
char bone_vis_str[50];
switch (pso->mode) {
@@ -1203,29 +926,10 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
break;
}
- if (pso->overshoot) {
- STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot"));
- }
- else {
- STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot"));
- }
-
- if (pso->precision) {
- STRNCPY(precision_str, TIP_("[Shift] - Precision active"));
- }
- else {
- STRNCPY(precision_str, TIP_("Shift - Hold for precision"));
- }
-
- if (pso->increments) {
- STRNCPY(increments_str, TIP_("[Ctrl] - Increments active"));
- }
- else {
- STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments"));
- }
-
STRNCPY(bone_vis_str, TIP_("[H] - Toggle bone visibility"));
+ ED_slider_status_string_get(pso->slider, slider_str, sizeof(slider_str));
+
if (hasNumInput(&pso->num)) {
Scene *scene = pso->scene;
char str_offs[NUM_STR_REP_LEN];
@@ -1237,12 +941,10 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
else {
BLI_snprintf(status_str,
sizeof(status_str),
- "%s: %s | %s | %s | %s | %s",
+ "%s: %s | %s | %s",
mode_str,
limits_str,
- overshoot_str,
- precision_str,
- increments_str,
+ slider_str,
bone_vis_str);
}
@@ -1253,11 +955,15 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
/**
* Common code for invoke() methods.
*/
-static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
+static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent *event)
{
tPChanFCurveLink *pfl;
wmWindow *win = CTX_wm_window(C);
+ tPoseSlideOp *pso = op->customdata;
+
+ ED_slider_init(pso->slider, event);
+
/* For each link, add all its keyframes to the search tree. */
for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
LinkData *ld;
@@ -1315,7 +1021,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p
}
else {
BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between");
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
@@ -1340,38 +1046,14 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p
/* Add a modal handler for this operator. */
WM_event_add_modal_handler(C, op);
- /* Hide Bone Overlay. */
+ /* Save current bone visibility. */
View3D *v3d = pso->area->spacedata.first;
pso->overlay_flag = v3d->overlay.flag;
- v3d->overlay.flag |= V3D_OVERLAY_HIDE_BONES;
return OPERATOR_RUNNING_MODAL;
}
/**
- * Calculate factor based on mouse movement, clamp or round to increments if
- * enabled by the user. Store the new factor value.
- */
-static void pose_slide_mouse_update_factor(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event)
-{
- const float factor_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE));
- /* Reduced factor delta in precision mode (shift held). */
- pso->raw_factor += pso->precision ? (factor_delta / 8) : factor_delta;
- pso->factor = pso->raw_factor;
- pso->last_cursor_x = event->x;
-
- if (!pso->overshoot) {
- pso->factor = clamp_f(pso->factor, 0, 1);
- }
-
- if (pso->increments) {
- pso->factor = round(pso->factor * 10) / 10;
- }
-
- RNA_float_set(op->ptr, "factor", pso->factor);
-}
-
-/**
* Handle an event to toggle channels mode.
*/
static void pose_slide_toggle_channels_mode(wmOperator *op,
@@ -1434,6 +1116,8 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
const bool has_numinput = hasNumInput(&pso->num);
+ do_pose_update = ED_slider_modal(pso->slider, event);
+
switch (event->type) {
case LEFTMOUSE: /* Confirm. */
case EVT_RETKEY:
@@ -1449,7 +1133,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Insert keyframes as required. */
pose_slide_autoKeyframe(C, pso);
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
/* Done! */
return OPERATOR_FINISHED;
@@ -1472,7 +1156,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
pose_slide_refresh(C, pso);
/* Clean up temp data. */
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
/* Canceled! */
return OPERATOR_CANCELLED;
@@ -1485,9 +1169,6 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
/* Only handle mouse-move if not doing numinput. */
if (has_numinput == false) {
- /* Update factor based on position of mouse. */
- pose_slide_mouse_update_factor(pso, op, event);
-
/* Update pose to reflect the new values (see below). */
do_pose_update = true;
}
@@ -1500,12 +1181,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Grab percentage from numeric input, and store this new value for redo
* NOTE: users see ints, while internally we use a 0-1 float
*/
- value = pso->factor * 100.0f;
+ value = ED_slider_factor_get(pso->slider) * 100.0f;
applyNumInput(&pso->num, &value);
- pso->factor = value / 100.0f;
- CLAMP(pso->factor, 0.0f, 1.0f);
- RNA_float_set(op->ptr, "factor", pso->factor);
+ float factor = value / 100;
+ CLAMP(factor, 0.0f, 1.0f);
+ ED_slider_factor_set(pso->slider, factor);
+ RNA_float_set(op->ptr, "factor", ED_slider_factor_get(pso->slider));
/* Update pose to reflect the new values (see below) */
do_pose_update = true;
@@ -1567,58 +1249,17 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
- /* Overshoot. */
- case EVT_EKEY: {
- pso->overshoot = !pso->overshoot;
- do_pose_update = true;
- break;
- }
-
- /* Precision mode. */
- case EVT_LEFTSHIFTKEY:
- case EVT_RIGHTSHIFTKEY: {
- pso->precision = true;
- do_pose_update = true;
- break;
- }
-
- /* Increments mode. */
- case EVT_LEFTCTRLKEY:
- case EVT_RIGHTCTRLKEY: {
- pso->increments = true;
- do_pose_update = true;
- break;
- }
-
/* Toggle Bone visibility. */
case EVT_HKEY: {
View3D *v3d = pso->area->spacedata.first;
v3d->overlay.flag ^= V3D_OVERLAY_HIDE_BONES;
+ ED_region_tag_redraw(pso->region);
}
default: /* Some other unhandled key... */
break;
}
}
- /* Precision and stepping only active while button is held. */
- else if (event->val == KM_RELEASE) {
- switch (event->type) {
- case EVT_LEFTSHIFTKEY:
- case EVT_RIGHTSHIFTKEY: {
- pso->precision = false;
- do_pose_update = true;
- break;
- }
- case EVT_LEFTCTRLKEY:
- case EVT_RIGHTCTRLKEY: {
- pso->increments = false;
- do_pose_update = true;
- break;
- }
- default:
- break;
- }
- }
else {
/* Unhandled event - maybe it was some view manipulation? */
/* Allow to pass through. */
@@ -1652,10 +1293,10 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
/**
* Common code for cancel()
*/
-static void pose_slide_cancel(bContext *UNUSED(C), wmOperator *op)
+static void pose_slide_cancel(bContext *C, wmOperator *op)
{
/* Cleanup and done. */
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
}
/**
@@ -1675,7 +1316,7 @@ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso
pose_slide_autoKeyframe(C, pso);
/* Cleanup and done. */
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_FINISHED;
}
@@ -1743,23 +1384,14 @@ static void pose_slide_opdef_properties(wmOperatorType *ot)
*/
static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- tPoseSlideOp *pso;
-
/* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
- pso = op->customdata;
-
- pso->last_cursor_x = event->x;
-
- /* Initialize factor so that it won't pop on first mouse move. */
- pose_slide_mouse_update_factor(pso, op, event);
-
/* Do common setup work. */
- return pose_slide_invoke_common(C, op, pso);
+ return pose_slide_invoke_common(C, op, event);
}
/**
@@ -1771,7 +1403,7 @@ static int pose_slide_push_exec(bContext *C, wmOperator *op)
/* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
@@ -1809,23 +1441,14 @@ void POSE_OT_push(wmOperatorType *ot)
*/
static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- tPoseSlideOp *pso;
-
/* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
- pso = op->customdata;
-
- pso->last_cursor_x = event->x;
-
- /* Initialize factor so that it won't pop on first mouse move. */
- pose_slide_mouse_update_factor(pso, op, event);
-
/* Do common setup work. */
- return pose_slide_invoke_common(C, op, pso);
+ return pose_slide_invoke_common(C, op, event);
}
/**
@@ -1837,7 +1460,7 @@ static int pose_slide_relax_exec(bContext *C, wmOperator *op)
/* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
@@ -1874,23 +1497,14 @@ void POSE_OT_relax(wmOperatorType *ot)
*/
static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- tPoseSlideOp *pso;
-
/* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
- pso = op->customdata;
-
- pso->last_cursor_x = event->x;
-
- /* Initialize factor so that it won't pop on first mouse move. */
- pose_slide_mouse_update_factor(pso, op, event);
-
/* do common setup work */
- return pose_slide_invoke_common(C, op, pso);
+ return pose_slide_invoke_common(C, op, event);
}
/**
@@ -1902,7 +1516,7 @@ static int pose_slide_push_rest_exec(bContext *C, wmOperator *op)
/* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
@@ -1940,23 +1554,14 @@ void POSE_OT_push_rest(wmOperatorType *ot)
*/
static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- tPoseSlideOp *pso;
-
/* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
- pso = op->customdata;
-
- pso->last_cursor_x = event->x;
-
- /* Initialize factor so that it won't pop on first mouse move. */
- pose_slide_mouse_update_factor(pso, op, event);
-
/* Do common setup work. */
- return pose_slide_invoke_common(C, op, pso);
+ return pose_slide_invoke_common(C, op, event);
}
/**
@@ -1968,7 +1573,7 @@ static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op)
/* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
@@ -2006,23 +1611,14 @@ void POSE_OT_relax_rest(wmOperatorType *ot)
*/
static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- tPoseSlideOp *pso;
-
/* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
- pso = op->customdata;
-
- pso->last_cursor_x = event->x;
-
- /* Initialize factor so that it won't pop on first mouse move. */
- pose_slide_mouse_update_factor(pso, op, event);
-
/* Do common setup work. */
- return pose_slide_invoke_common(C, op, pso);
+ return pose_slide_invoke_common(C, op, event);
}
/**
@@ -2034,7 +1630,7 @@ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op)
/* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
- pose_slide_exit(op);
+ pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt
index 8c5f91561b7..7e578b78809 100644
--- a/source/blender/editors/asset/CMakeLists.txt
+++ b/source/blender/editors/asset/CMakeLists.txt
@@ -16,24 +16,43 @@
# ***** END GPL LICENSE BLOCK *****
set(INC
- ../include
- ../../blenkernel
- ../../blenlib
- ../../makesdna
- ../../makesrna
- ../../windowmanager
- ../../../../intern/guardedalloc
+ .
+ ../include
+ ../../blenkernel
+ ../../blenlib
+ ../../blenloader
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../../../intern/guardedalloc
)
set(INC_SYS
)
set(SRC
- asset_edit.cc
- asset_ops.cc
+ intern/asset_filter.cc
+ intern/asset_handle.cc
+ intern/asset_library_reference.cc
+ intern/asset_library_reference_enum.cc
+ intern/asset_list.cc
+ intern/asset_mark_clear.cc
+ intern/asset_ops.cc
+ intern/asset_temp_id_consumer.cc
+
+ ED_asset_filter.h
+ ED_asset_handle.h
+ ED_asset_library.h
+ ED_asset_list.h
+ ED_asset_list.hh
+ ED_asset_mark_clear.h
+ ED_asset_temp_id_consumer.h
+ intern/asset_library_reference.hh
)
set(LIB
+ bf_blenloader
+ bf_blenkernel
)
blender_add_lib(bf_editor_asset "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/asset/ED_asset_filter.h b/source/blender/editors/asset/ED_asset_filter.h
new file mode 100644
index 00000000000..fb2d3bfb273
--- /dev/null
+++ b/source/blender/editors/asset/ED_asset_filter.h
@@ -0,0 +1,35 @@
+/*
+ * 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 edasset
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AssetHandle;
+struct AssetFilterSettings;
+
+bool ED_asset_filter_matches_asset(const struct AssetFilterSettings *filter,
+ const struct AssetHandle *asset);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h
new file mode 100644
index 00000000000..c51ce422c25
--- /dev/null
+++ b/source/blender/editors/asset/ED_asset_handle.h
@@ -0,0 +1,45 @@
+/*
+ * 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 edasset
+ */
+
+#pragma once
+
+#include "DNA_ID_enums.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AssetHandle;
+struct AssetLibraryReference;
+struct bContext;
+
+const char *ED_asset_handle_get_name(const struct AssetHandle *asset);
+struct AssetMetaData *ED_asset_handle_get_metadata(const struct AssetHandle *asset);
+struct ID *ED_asset_handle_get_local_id(const struct AssetHandle *asset);
+ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset);
+int ED_asset_handle_get_preview_icon_id(const struct AssetHandle *asset);
+void ED_asset_handle_get_full_library_path(const struct bContext *C,
+ const struct AssetLibraryReference *asset_library,
+ const struct AssetHandle *asset,
+ char r_full_lib_path[]);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/asset/ED_asset_library.h b/source/blender/editors/asset/ED_asset_library.h
new file mode 100644
index 00000000000..905d097d223
--- /dev/null
+++ b/source/blender/editors/asset/ED_asset_library.h
@@ -0,0 +1,35 @@
+/*
+ * 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 edasset
+ */
+
+#pragma once
+
+#include "DNA_asset_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library);
+AssetLibraryReference ED_asset_library_reference_from_enum_value(int value);
+const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/asset/ED_asset_list.h b/source/blender/editors/asset/ED_asset_list.h
new file mode 100644
index 00000000000..55a0ac0b500
--- /dev/null
+++ b/source/blender/editors/asset/ED_asset_list.h
@@ -0,0 +1,53 @@
+/*
+ * 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 edasset
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AssetFilterSettings;
+struct AssetHandle;
+struct AssetLibraryReference;
+struct bContext;
+struct ID;
+struct wmNotifier;
+
+void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference,
+ const struct bContext *C);
+void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference,
+ struct bContext *C);
+void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C);
+bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference);
+void ED_assetlist_storage_tag_main_data_dirty(void);
+void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new);
+void ED_assetlist_storage_exit(void);
+
+struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle);
+const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference);
+
+bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference,
+ const struct wmNotifier *notifier);
+int ED_assetlist_size(const struct AssetLibraryReference *library_reference);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/asset/ED_asset_list.hh b/source/blender/editors/asset/ED_asset_list.hh
new file mode 100644
index 00000000000..fee34d929c2
--- /dev/null
+++ b/source/blender/editors/asset/ED_asset_list.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.
+ */
+
+/** \file
+ * \ingroup edasset
+ */
+
+#pragma once
+
+#include <string>
+
+#include "BLI_function_ref.hh"
+
+struct AssetLibraryReference;
+struct AssetHandle;
+struct bContext;
+struct FileDirEntry;
+
+std::string ED_assetlist_asset_filepath_get(const bContext *C,
+ const AssetLibraryReference &library_reference,
+ const AssetHandle &asset_handle);
+
+/* Can return false to stop iterating. */
+using AssetListIterFn = blender::FunctionRef<bool(AssetHandle)>;
+void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn);
diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h
new file mode 100644
index 00000000000..cdd1f0d080b
--- /dev/null
+++ b/source/blender/editors/asset/ED_asset_mark_clear.h
@@ -0,0 +1,37 @@
+/*
+ * 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 edasset
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bContext;
+struct ID;
+
+bool ED_asset_mark_id(const struct bContext *C, struct ID *id);
+bool ED_asset_clear_id(struct ID *id);
+
+bool ED_asset_can_mark_single_from_context(const struct bContext *C);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/asset/ED_asset_temp_id_consumer.h b/source/blender/editors/asset/ED_asset_temp_id_consumer.h
new file mode 100644
index 00000000000..9af08c5c52b
--- /dev/null
+++ b/source/blender/editors/asset/ED_asset_temp_id_consumer.h
@@ -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.
+ */
+
+/** \file
+ * \ingroup edasset
+ */
+
+#pragma once
+
+#include "DNA_ID_enums.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct AssetTempIDConsumer AssetTempIDConsumer;
+
+struct AssetHandle;
+struct AssetLibraryReference;
+struct bContext;
+struct Main;
+struct ReportList;
+
+AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const struct AssetHandle *handle);
+void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer);
+struct ID *ED_asset_temp_id_consumer_ensure_local_id(
+ AssetTempIDConsumer *consumer,
+ const struct bContext *C,
+ const struct AssetLibraryReference *asset_library,
+ ID_Type id_type,
+ struct Main *bmain,
+ struct ReportList *reports);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/asset/intern/asset_filter.cc b/source/blender/editors/asset/intern/asset_filter.cc
new file mode 100644
index 00000000000..329342a30cd
--- /dev/null
+++ b/source/blender/editors/asset/intern/asset_filter.cc
@@ -0,0 +1,64 @@
+/*
+ * 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 edasset
+ */
+
+#include "BKE_idtype.h"
+
+#include "BLI_listbase.h"
+
+#include "DNA_ID.h"
+#include "DNA_asset_types.h"
+
+#include "ED_asset_filter.h"
+#include "ED_asset_handle.h"
+
+/**
+ * Compare \a asset against the settings of \a filter.
+ *
+ * Individual filter parameters are ORed with the asset properties. That means:
+ * * The asset type must be one of the ID types filtered by, and
+ * * The asset must contain at least one of the tags filtered by.
+ * However for an asset to be matching it must have one match in each of the parameters. I.e. one
+ * matching type __and__ at least one matching tag.
+ *
+ * \returns True if the asset should be visible with these filter settings (parameters match).
+ * Otherwise returns false (mismatch).
+ */
+bool ED_asset_filter_matches_asset(const AssetFilterSettings *filter, const AssetHandle *asset)
+{
+ ID_Type asset_type = ED_asset_handle_get_id_type(asset);
+ uint64_t asset_id_filter = BKE_idtype_idcode_to_idfilter(asset_type);
+
+ if ((filter->id_types & asset_id_filter) == 0) {
+ return false;
+ }
+ /* Not very efficient (O(n^2)), could be improved quite a bit. */
+ LISTBASE_FOREACH (const AssetTag *, filter_tag, &filter->tags) {
+ AssetMetaData *asset_data = ED_asset_handle_get_metadata(asset);
+
+ AssetTag *matched_tag = (AssetTag *)BLI_findstring(
+ &asset_data->tags, filter_tag->name, offsetof(AssetTag, name));
+ if (matched_tag == nullptr) {
+ return false;
+ }
+ }
+
+ /* Successfully passed through all filters. */
+ return true;
+}
diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc
new file mode 100644
index 00000000000..aae85e61372
--- /dev/null
+++ b/source/blender/editors/asset/intern/asset_handle.cc
@@ -0,0 +1,75 @@
+/*
+ * 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 edasset
+ *
+ * Asset-handle is a temporary design, not part of the core asset system design.
+ *
+ * Currently asset-list items are just file directory items (#FileDirEntry). So an asset-handle is
+ * just wraps a pointer to this. We try to abstract away the fact that it's just a file entry,
+ * although that doesn't always work (see #rna_def_asset_handle()).
+ */
+
+#include <string>
+
+#include "DNA_asset_types.h"
+#include "DNA_space_types.h"
+
+#include "BLO_readfile.h"
+
+#include "ED_asset_handle.h"
+#include "ED_asset_list.hh"
+
+const char *ED_asset_handle_get_name(const AssetHandle *asset)
+{
+ return asset->file_data->name;
+}
+
+AssetMetaData *ED_asset_handle_get_metadata(const AssetHandle *asset)
+{
+ return asset->file_data->asset_data;
+}
+
+ID *ED_asset_handle_get_local_id(const AssetHandle *asset)
+{
+ return asset->file_data->id;
+}
+
+ID_Type ED_asset_handle_get_id_type(const AssetHandle *asset)
+{
+ return static_cast<ID_Type>(asset->file_data->blentype);
+}
+
+int ED_asset_handle_get_preview_icon_id(const AssetHandle *asset)
+{
+ return asset->file_data->preview_icon_id;
+}
+
+void ED_asset_handle_get_full_library_path(const bContext *C,
+ const AssetLibraryReference *asset_library,
+ const AssetHandle *asset,
+ char r_full_lib_path[FILE_MAX_LIBEXTRA])
+{
+ *r_full_lib_path = '\0';
+
+ std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library, *asset);
+ if (asset_path.empty()) {
+ return;
+ }
+
+ BLO_library_path_explode(asset_path.c_str(), r_full_lib_path, nullptr, nullptr);
+}
diff --git a/source/blender/editors/asset/intern/asset_library_reference.cc b/source/blender/editors/asset/intern/asset_library_reference.cc
new file mode 100644
index 00000000000..df1d58df5f9
--- /dev/null
+++ b/source/blender/editors/asset/intern/asset_library_reference.cc
@@ -0,0 +1,50 @@
+/*
+ * 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 edasset
+ */
+
+#include "BLI_hash.hh"
+
+#include "asset_library_reference.hh"
+
+namespace blender::ed::asset {
+
+AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryReference &reference)
+ : AssetLibraryReference(reference)
+{
+}
+
+bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b)
+{
+ return (a.type == b.type) && (a.type == ASSET_LIBRARY_CUSTOM) ?
+ (a.custom_library_index == b.custom_library_index) :
+ true;
+}
+
+uint64_t AssetLibraryReferenceWrapper::hash() const
+{
+ uint64_t hash1 = DefaultHash<decltype(type)>{}(type);
+ if (type != ASSET_LIBRARY_CUSTOM) {
+ return hash1;
+ }
+
+ uint64_t hash2 = DefaultHash<decltype(custom_library_index)>{}(custom_library_index);
+ return hash1 ^ (hash2 * 33); /* Copied from DefaultHash for std::pair. */
+}
+
+} // namespace blender::ed::asset
diff --git a/source/blender/editors/asset/intern/asset_library_reference.hh b/source/blender/editors/asset/intern/asset_library_reference.hh
new file mode 100644
index 00000000000..7e8cb4a3472
--- /dev/null
+++ b/source/blender/editors/asset/intern/asset_library_reference.hh
@@ -0,0 +1,46 @@
+/*
+ * 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 edasset
+ *
+ * Utility to extend #AssetLibraryReference with C++ functionality (operators, hash function, etc).
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include "DNA_asset_types.h"
+
+namespace blender::ed::asset {
+
+/**
+ * Wrapper to add logic to the AssetLibraryReference DNA struct.
+ */
+class AssetLibraryReferenceWrapper : public AssetLibraryReference {
+ public:
+ /* Intentionally not `explicit`, allow implicit conversion for convenience. Might have to be
+ * NOLINT */
+ AssetLibraryReferenceWrapper(const AssetLibraryReference &reference);
+ ~AssetLibraryReferenceWrapper() = default;
+
+ friend bool operator==(const AssetLibraryReferenceWrapper &a,
+ const AssetLibraryReferenceWrapper &b);
+ uint64_t hash() const;
+};
+
+} // namespace blender::ed::asset
diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc
new file mode 100644
index 00000000000..5a8fed6fea1
--- /dev/null
+++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc
@@ -0,0 +1,156 @@
+/*
+ * 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 edasset
+ *
+ * Helpers to convert asset library references from and to enum values and RNA enums.
+ * In some cases it's simply not possible to reference an asset library with
+ * #AssetLibraryReferences. This API guarantees a safe translation to indices/enum values for as
+ * long as there is no change in the order of registered custom asset libraries.
+ */
+
+#include "BLI_listbase.h"
+
+#include "BKE_preferences.h"
+
+#include "DNA_asset_types.h"
+#include "DNA_userdef_types.h"
+
+#include "UI_resources.h"
+
+#include "RNA_define.h"
+
+#include "ED_asset_library.h"
+
+/**
+ * Return an index that can be used to uniquely identify \a library, assuming
+ * that all relevant indices were created with this function.
+ */
+int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library)
+{
+ /* Simple case: Predefined repository, just set the value. */
+ if (library->type < ASSET_LIBRARY_CUSTOM) {
+ return library->type;
+ }
+
+ /* Note that the path isn't checked for validity here. If an invalid library path is used, the
+ * Asset Browser can give a nice hint on what's wrong. */
+ const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
+ &U, library->custom_library_index);
+ if (user_library) {
+ return ASSET_LIBRARY_CUSTOM + library->custom_library_index;
+ }
+
+ BLI_assert_unreachable();
+ return ASSET_LIBRARY_LOCAL;
+}
+
+/**
+ * Return an asset library reference matching the index returned by
+ * #ED_asset_library_reference_to_enum_value().
+ */
+AssetLibraryReference ED_asset_library_reference_from_enum_value(int value)
+{
+ AssetLibraryReference library;
+
+ /* Simple case: Predefined repository, just set the value. */
+ if (value < ASSET_LIBRARY_CUSTOM) {
+ library.type = value;
+ library.custom_library_index = -1;
+ BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL));
+ return library;
+ }
+
+ const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
+ &U, value - ASSET_LIBRARY_CUSTOM);
+
+ /* Note that there is no check if the path exists here. If an invalid library path is used, the
+ * Asset Browser can give a nice hint on what's wrong. */
+ const bool is_valid = (user_library->name[0] && user_library->path[0]);
+ if (!user_library) {
+ library.type = ASSET_LIBRARY_LOCAL;
+ library.custom_library_index = -1;
+ }
+ else if (user_library && is_valid) {
+ library.custom_library_index = value - ASSET_LIBRARY_CUSTOM;
+ library.type = ASSET_LIBRARY_CUSTOM;
+ }
+ return library;
+}
+
+/**
+ * Translate all available asset libraries to an RNA enum, whereby the enum values match the result
+ * of #ED_asset_library_reference_to_enum_value() for any given library.
+ *
+ * Since this is meant for UI display, skips non-displayable libraries, that is, libraries with an
+ * empty name or path.
+ */
+const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf()
+{
+ const EnumPropertyItem predefined_items[] = {
+ /* For the future. */
+ // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
+ {ASSET_LIBRARY_LOCAL,
+ "LOCAL",
+ ICON_BLENDER,
+ "Current File",
+ "Show the assets currently available in this Blender session"},
+ {0, nullptr, 0, nullptr, nullptr},
+ };
+
+ EnumPropertyItem *item = nullptr;
+ int totitem = 0;
+
+ /* Add separator if needed. */
+ if (!BLI_listbase_is_empty(&U.asset_libraries)) {
+ const EnumPropertyItem sepr = {0, "", 0, "Custom", nullptr};
+ RNA_enum_item_add(&item, &totitem, &sepr);
+ }
+
+ int i = 0;
+ for (bUserAssetLibrary *user_library = (bUserAssetLibrary *)U.asset_libraries.first;
+ user_library;
+ user_library = user_library->next, i++) {
+ /* Note that the path itself isn't checked for validity here. If an invalid library path is
+ * used, the Asset Browser can give a nice hint on what's wrong. */
+ const bool is_valid = (user_library->name[0] && user_library->path[0]);
+ if (!is_valid) {
+ continue;
+ }
+
+ AssetLibraryReference library_reference;
+ library_reference.type = ASSET_LIBRARY_CUSTOM;
+ library_reference.custom_library_index = i;
+
+ const int enum_value = ED_asset_library_reference_to_enum_value(&library_reference);
+ /* Use library path as description, it's a nice hint for users. */
+ EnumPropertyItem tmp = {
+ enum_value, user_library->name, ICON_NONE, user_library->name, user_library->path};
+ RNA_enum_item_add(&item, &totitem, &tmp);
+ }
+
+ if (totitem) {
+ const EnumPropertyItem sepr = {0, "", 0, "Built-in", nullptr};
+ RNA_enum_item_add(&item, &totitem, &sepr);
+ }
+
+ /* Add predefined items. */
+ RNA_enum_items_add(&item, &totitem, predefined_items);
+
+ RNA_enum_item_end(&item, &totitem);
+ return item;
+}
diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc
new file mode 100644
index 00000000000..57c25e1614b
--- /dev/null
+++ b/source/blender/editors/asset/intern/asset_list.cc
@@ -0,0 +1,586 @@
+/*
+ * 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 edasset
+ *
+ * Abstractions to manage runtime asset lists with a global cache for multiple UI elements to
+ * access.
+ * Internally this uses the #FileList API and structures from `filelist.c`. This is just because it
+ * contains most necessary logic already and there's not much time for a more long-term solution.
+ */
+
+#include <optional>
+#include <string>
+
+#include "BKE_context.h"
+
+#include "BLI_map.hh"
+#include "BLI_path_util.h"
+#include "BLI_utility_mixins.hh"
+
+#include "DNA_asset_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_preferences.h"
+
+#include "ED_fileselect.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/* XXX uses private header of file-space. */
+#include "../space_file/filelist.h"
+
+#include "ED_asset_handle.h"
+#include "ED_asset_list.h"
+#include "ED_asset_list.hh"
+#include "asset_library_reference.hh"
+
+namespace blender::ed::asset {
+
+/* -------------------------------------------------------------------- */
+/** \name Asset list API
+ *
+ * Internally re-uses #FileList from the File Browser. It does all the heavy lifting already.
+ * \{ */
+
+/**
+ * RAII wrapper for `FileList`
+ */
+class FileListWrapper {
+ static void filelist_free_fn(FileList *list)
+ {
+ filelist_free(list);
+ MEM_freeN(list);
+ }
+
+ std::unique_ptr<FileList, decltype(&filelist_free_fn)> file_list_;
+
+ public:
+ explicit FileListWrapper(eFileSelectType filesel_type)
+ : file_list_(filelist_new(filesel_type), filelist_free_fn)
+ {
+ }
+ FileListWrapper(FileListWrapper &&other) = default;
+ FileListWrapper &operator=(FileListWrapper &&other) = default;
+ ~FileListWrapper()
+ {
+ /* Destructs the owned pointer. */
+ file_list_ = nullptr;
+ }
+
+ operator FileList *() const
+ {
+ return file_list_.get();
+ }
+};
+
+class PreviewTimer {
+ /* Non-owning! The Window-Manager registers and owns this. */
+ wmTimer *timer_ = nullptr;
+
+ public:
+ void ensureRunning(const bContext *C)
+ {
+ if (!timer_) {
+ timer_ = WM_event_add_timer_notifier(
+ CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_LIST_PREVIEW, 0.01);
+ }
+ }
+
+ void stop(const bContext *C)
+ {
+ if (timer_) {
+ WM_event_remove_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C), timer_);
+ timer_ = nullptr;
+ }
+ }
+};
+
+class AssetList : NonCopyable {
+ FileListWrapper filelist_;
+ AssetLibraryReference library_ref_;
+ PreviewTimer previews_timer_;
+
+ public:
+ AssetList() = delete;
+ AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref);
+ AssetList(AssetList &&other) = default;
+ ~AssetList() = default;
+
+ void setup();
+ void fetch(const bContext &C);
+ void ensurePreviewsJob(bContext *C);
+ void clear(bContext *C);
+
+ bool needsRefetch() const;
+ void iterate(AssetListIterFn fn) const;
+ bool listen(const wmNotifier &notifier) const;
+ int size() const;
+ void tagMainDataDirty() const;
+ void remapID(ID *id_old, ID *id_new) const;
+ StringRef filepath() const;
+};
+
+AssetList::AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref)
+ : filelist_(filesel_type), library_ref_(asset_library_ref)
+{
+}
+
+void AssetList::setup()
+{
+ FileList *files = filelist_;
+
+ bUserAssetLibrary *user_library = nullptr;
+
+ /* Ensure valid repository, or fall-back to local one. */
+ if (library_ref_.type == ASSET_LIBRARY_CUSTOM) {
+ BLI_assert(library_ref_.custom_library_index >= 0);
+
+ user_library = BKE_preferences_asset_library_find_from_index(
+ &U, library_ref_.custom_library_index);
+ }
+
+ /* Relevant bits from file_refresh(). */
+ /* TODO pass options properly. */
+ filelist_setrecursion(files, 1);
+ filelist_setsorting(files, FILE_SORT_ALPHA, false);
+ filelist_setlibrary(files, &library_ref_);
+ filelist_setfilter_options(
+ files,
+ false,
+ true,
+ true, /* Just always hide parent, prefer to not add an extra user option for this. */
+ FILE_TYPE_BLENDERLIB,
+ FILTER_ID_ALL,
+ true,
+ "",
+ "");
+
+ char path[FILE_MAXDIR] = "";
+ if (user_library) {
+ BLI_strncpy(path, user_library->path, sizeof(path));
+ filelist_setdir(files, path);
+ }
+ else {
+ filelist_setdir(files, path);
+ }
+}
+
+void AssetList::fetch(const bContext &C)
+{
+ FileList *files = filelist_;
+
+ if (filelist_needs_force_reset(files)) {
+ filelist_readjob_stop(files, CTX_wm_manager(&C));
+ filelist_clear(files);
+ }
+
+ if (filelist_needs_reading(files)) {
+ if (!filelist_pending(files)) {
+ filelist_readjob_start(files, NC_ASSET | ND_ASSET_LIST_READING, &C);
+ }
+ }
+ filelist_sort(files);
+ filelist_filter(files);
+}
+
+bool AssetList::needsRefetch() const
+{
+ return filelist_needs_force_reset(filelist_) || filelist_needs_reading(filelist_);
+}
+
+void AssetList::iterate(AssetListIterFn fn) const
+{
+ FileList *files = filelist_;
+ int numfiles = filelist_files_ensure(files);
+
+ for (int i = 0; i < numfiles; i++) {
+ FileDirEntry *file = filelist_file(files, i);
+ if ((file->typeflag & FILE_TYPE_ASSET) == 0) {
+ continue;
+ }
+
+ AssetHandle asset_handle = {file};
+ if (!fn(asset_handle)) {
+ /* If the callback returns false, we stop iterating. */
+ break;
+ }
+ }
+}
+
+void AssetList::ensurePreviewsJob(bContext *C)
+{
+ FileList *files = filelist_;
+ int numfiles = filelist_files_ensure(files);
+
+ filelist_cache_previews_set(files, true);
+ filelist_file_cache_slidingwindow_set(files, 256);
+ /* TODO fetch all previews for now. */
+ filelist_file_cache_block(files, numfiles / 2);
+ filelist_cache_previews_update(files);
+
+ {
+ const bool previews_running = filelist_cache_previews_running(files) &&
+ !filelist_cache_previews_done(files);
+ if (previews_running) {
+ previews_timer_.ensureRunning(C);
+ }
+ else {
+ /* Preview is not running, no need to keep generating update events! */
+ previews_timer_.stop(C);
+ }
+ }
+}
+
+void AssetList::clear(bContext *C)
+{
+ /* Based on #ED_fileselect_clear() */
+
+ FileList *files = filelist_;
+ filelist_readjob_stop(files, CTX_wm_manager(C));
+ filelist_freelib(files);
+ filelist_clear(files);
+
+ WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr);
+}
+
+/**
+ * \return True if the asset-list needs a UI redraw.
+ */
+bool AssetList::listen(const wmNotifier &notifier) const
+{
+ switch (notifier.category) {
+ case NC_ID: {
+ if (ELEM(notifier.action, NA_RENAME)) {
+ return true;
+ }
+ break;
+ }
+ case NC_ASSET:
+ if (ELEM(notifier.data, ND_ASSET_LIST, ND_ASSET_LIST_READING, ND_ASSET_LIST_PREVIEW)) {
+ return true;
+ }
+ if (ELEM(notifier.action, NA_ADDED, NA_REMOVED, NA_EDITED)) {
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * \return The number of assets in the list.
+ */
+int AssetList::size() const
+{
+ return filelist_files_ensure(filelist_);
+}
+
+void AssetList::tagMainDataDirty() const
+{
+ if (filelist_needs_reset_on_main_changes(filelist_)) {
+ /* Full refresh of the file list if local asset data was changed. Refreshing this view
+ * is cheap and users expect this to be updated immediately. */
+ filelist_tag_force_reset(filelist_);
+ }
+}
+
+void AssetList::remapID(ID * /*id_old*/, ID * /*id_new*/) const
+{
+ /* Trigger full re-fetch of the file list if main data was changed, don't even attempt remap
+ * pointers. We could give file list types a id-remap callback, but it's probably not worth it.
+ * Refreshing local file lists is relatively cheap. */
+ tagMainDataDirty();
+}
+
+StringRef AssetList::filepath() const
+{
+ return filelist_dir(filelist_);
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Runtime asset list cache
+ * \{ */
+
+/**
+ * Class managing a global asset list map, each entry being a list for a specific asset library.
+ */
+class AssetListStorage {
+ using AssetListMap = Map<AssetLibraryReferenceWrapper, AssetList>;
+
+ public:
+ /* Purely static class, can't instantiate this. */
+ AssetListStorage() = delete;
+
+ static void fetch_library(const AssetLibraryReference &library_reference, const bContext &C);
+ static void destruct();
+ static AssetList *lookup_list(const AssetLibraryReference &library_ref);
+ static void tagMainDataDirty();
+ static void remapID(ID *id_new, ID *id_old);
+
+ private:
+ static std::optional<eFileSelectType> asset_library_reference_to_fileselect_type(
+ const AssetLibraryReference &library_reference);
+
+ using is_new_t = bool;
+ static std::tuple<AssetList &, is_new_t> ensure_list_storage(
+ const AssetLibraryReference &library_reference, eFileSelectType filesel_type);
+
+ static AssetListMap &global_storage();
+};
+
+void AssetListStorage::fetch_library(const AssetLibraryReference &library_reference,
+ const bContext &C)
+{
+ std::optional filesel_type = asset_library_reference_to_fileselect_type(library_reference);
+ if (!filesel_type) {
+ return;
+ }
+
+ auto [list, is_new] = ensure_list_storage(library_reference, *filesel_type);
+ if (is_new || list.needsRefetch()) {
+ list.setup();
+ list.fetch(C);
+ }
+}
+
+void AssetListStorage::destruct()
+{
+ global_storage().~AssetListMap();
+}
+
+AssetList *AssetListStorage::lookup_list(const AssetLibraryReference &library_ref)
+{
+ return global_storage().lookup_ptr(library_ref);
+}
+
+void AssetListStorage::tagMainDataDirty()
+{
+ for (AssetList &list : global_storage().values()) {
+ list.tagMainDataDirty();
+ }
+}
+
+void AssetListStorage::remapID(ID *id_new, ID *id_old)
+{
+ for (AssetList &list : global_storage().values()) {
+ list.remapID(id_new, id_old);
+ }
+}
+
+std::optional<eFileSelectType> AssetListStorage::asset_library_reference_to_fileselect_type(
+ const AssetLibraryReference &library_reference)
+{
+ switch (library_reference.type) {
+ case ASSET_LIBRARY_CUSTOM:
+ return FILE_LOADLIB;
+ case ASSET_LIBRARY_LOCAL:
+ return FILE_MAIN_ASSET;
+ }
+
+ return std::nullopt;
+}
+
+std::tuple<AssetList &, AssetListStorage::is_new_t> AssetListStorage::ensure_list_storage(
+ const AssetLibraryReference &library_reference, eFileSelectType filesel_type)
+{
+ AssetListMap &storage = global_storage();
+
+ if (AssetList *list = storage.lookup_ptr(library_reference)) {
+ return {*list, false};
+ }
+ storage.add(library_reference, AssetList(filesel_type, library_reference));
+ return {storage.lookup(library_reference), true};
+}
+
+/**
+ * Wrapper for Construct on First Use idiom, to avoid the Static Initialization Fiasco.
+ */
+AssetListStorage::AssetListMap &AssetListStorage::global_storage()
+{
+ static AssetListMap global_storage_;
+ return global_storage_;
+}
+
+/** \} */
+
+} // namespace blender::ed::asset
+
+/* -------------------------------------------------------------------- */
+/** \name C-API
+ * \{ */
+
+using namespace blender::ed::asset;
+
+/**
+ * Invoke asset list reading, potentially in a parallel job. Won't wait until the job is done,
+ * and may return earlier.
+ */
+void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference, const bContext *C)
+{
+ AssetListStorage::fetch_library(*library_reference, *C);
+}
+
+void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, bContext *C)
+{
+
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ list->ensurePreviewsJob(C);
+ }
+}
+
+void ED_assetlist_clear(const AssetLibraryReference *library_reference, bContext *C)
+{
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ list->clear(C);
+ }
+}
+
+bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference)
+{
+ return AssetListStorage::lookup_list(*library_reference) != nullptr;
+}
+
+void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn)
+{
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ list->iterate(fn);
+ }
+}
+
+/* TODO hack to use the File Browser path, so we can keep all the import logic handled by the asset
+ * API. Get rid of this once the File Browser is integrated better with the asset list. */
+static const char *assetlist_library_path_from_sfile_get_hack(const bContext *C)
+{
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ if (!sfile || !ED_fileselect_is_asset_browser(sfile)) {
+ return nullptr;
+ }
+
+ FileAssetSelectParams *asset_select_params = ED_fileselect_get_asset_params(sfile);
+ if (!asset_select_params) {
+ return nullptr;
+ }
+
+ return filelist_dir(sfile->files);
+}
+
+std::string ED_assetlist_asset_filepath_get(const bContext *C,
+ const AssetLibraryReference &library_reference,
+ const AssetHandle &asset_handle)
+{
+ if (ED_asset_handle_get_local_id(&asset_handle) ||
+ !ED_asset_handle_get_metadata(&asset_handle)) {
+ return {};
+ }
+ const char *library_path = ED_assetlist_library_path(&library_reference);
+ if (!library_path && C) {
+ library_path = assetlist_library_path_from_sfile_get_hack(C);
+ }
+ if (!library_path) {
+ return {};
+ }
+ const char *asset_relpath = asset_handle.file_data->relpath;
+
+ char path[FILE_MAX_LIBEXTRA];
+ BLI_join_dirfile(path, sizeof(path), library_path, asset_relpath);
+
+ return path;
+}
+
+ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle)
+{
+ ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data);
+ if (imbuf) {
+ return imbuf;
+ }
+
+ return filelist_geticon_image_ex(asset_handle->file_data);
+}
+
+const char *ED_assetlist_library_path(const AssetLibraryReference *library_reference)
+{
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ return list->filepath().data();
+ }
+ return nullptr;
+}
+
+/**
+ * \return True if the region needs a UI redraw.
+ */
+bool ED_assetlist_listen(const AssetLibraryReference *library_reference,
+ const wmNotifier *notifier)
+{
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ return list->listen(*notifier);
+ }
+ return false;
+}
+
+/**
+ * \return The number of assets stored in the asset list for \a library_reference, or -1 if there
+ * is no list fetched for it.
+ */
+int ED_assetlist_size(const AssetLibraryReference *library_reference)
+{
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ return list->size();
+ }
+ return -1;
+}
+
+/**
+ * Tag all asset lists in the storage that show main data as needing an update (re-fetch).
+ *
+ * This only tags the data. If the asset list is visible on screen, the space is still responsible
+ * for ensuring the necessary redraw. It can use #ED_assetlist_listen() to check if the asset-list
+ * needs a redraw for a given notifier.
+ */
+void ED_assetlist_storage_tag_main_data_dirty()
+{
+ AssetListStorage::tagMainDataDirty();
+}
+
+/**
+ * Remapping of ID pointers within the asset lists. Typically called when an ID is deleted to clear
+ * all references to it (\a id_new is null then).
+ */
+void ED_assetlist_storage_id_remap(ID *id_old, ID *id_new)
+{
+ AssetListStorage::remapID(id_old, id_new);
+}
+
+/**
+ * Can't wait for static deallocation to run. There's nested data allocated with our guarded
+ * allocator, it will complain about unfreed memory on exit.
+ */
+void ED_assetlist_storage_exit()
+{
+ AssetListStorage::destruct();
+}
+
+/** \} */
diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc
index d20de4141cb..ba348e38823 100644
--- a/source/blender/editors/asset/asset_edit.cc
+++ b/source/blender/editors/asset/intern/asset_mark_clear.cc
@@ -16,19 +16,29 @@
/** \file
* \ingroup edasset
+ *
+ * Functions for marking and clearing assets.
*/
+#include <memory>
+#include <string>
+
#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BLO_readfile.h"
+
#include "DNA_ID.h"
+#include "DNA_asset_types.h"
+#include "DNA_space_types.h"
#include "UI_interface_icons.h"
#include "RNA_access.h"
-#include "ED_asset.h"
+#include "ED_asset_list.h"
+#include "ED_asset_mark_clear.h"
bool ED_asset_mark_id(const bContext *C, ID *id)
{
@@ -45,6 +55,9 @@ bool ED_asset_mark_id(const bContext *C, ID *id)
UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true);
+ /* Important for asset storage to update properly! */
+ ED_assetlist_storage_tag_main_data_dirty();
+
return true;
}
@@ -57,10 +70,13 @@ bool ED_asset_clear_id(ID *id)
/* Don't clear fake user here, there's no guarantee that it was actually set by
* #ED_asset_mark_id(), it might have been something/someone else. */
+ /* Important for asset storage to update properly! */
+ ED_assetlist_storage_tag_main_data_dirty();
+
return true;
}
-bool ED_asset_can_make_single_from_context(const bContext *C)
+bool ED_asset_can_mark_single_from_context(const bContext *C)
{
/* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */
return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr;
diff --git a/source/blender/editors/asset/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index 8ca1b488a1d..f18cf9712db 100644
--- a/source/blender/editors/asset/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -151,7 +151,7 @@ static int asset_mark_exec(bContext *C, wmOperator *op)
static void ASSET_OT_mark(wmOperatorType *ot)
{
- ot->name = "Mark Asset";
+ ot->name = "Mark as Asset";
ot->description =
"Enable easier reuse of selected data-blocks through the Asset Browser, with the help of "
"customizable metadata (like previews, descriptions and tags)";
@@ -169,7 +169,7 @@ class AssetClearHelper {
public:
void operator()(PointerRNAVec &ids);
- void reportResults(ReportList &reports) const;
+ void reportResults(const bContext *C, ReportList &reports) const;
bool wasSuccessful() const;
private:
@@ -198,10 +198,22 @@ void AssetClearHelper::operator()(PointerRNAVec &ids)
}
}
-void AssetClearHelper::reportResults(ReportList &reports) const
+void AssetClearHelper::reportResults(const bContext *C, ReportList &reports) const
{
if (!wasSuccessful()) {
- BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused");
+ bool is_valid;
+ /* Dedicated error message for when there is an active asset detected, but it's not an ID local
+ * to this file. Helps users better understanding what's going on. */
+ if (AssetHandle active_asset = CTX_wm_asset_handle(C, &is_valid);
+ is_valid && !ED_asset_handle_get_local_id(&active_asset)) {
+ BKE_report(&reports,
+ RPT_ERROR,
+ "No asset data-blocks from the current file selected (assets must be stored in "
+ "the current file to be able to edit or clear them)");
+ }
+ else {
+ BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused");
+ }
}
else if (stats.tot_cleared == 1) {
/* If only one data-block: Give more useful message by printing asset name. */
@@ -224,7 +236,7 @@ static int asset_clear_exec(bContext *C, wmOperator *op)
AssetClearHelper clear_helper;
clear_helper(ids);
- clear_helper.reportResults(*op->reports);
+ clear_helper.reportResults(C, *op->reports);
if (!clear_helper.wasSuccessful()) {
return OPERATOR_CANCELLED;
@@ -252,8 +264,41 @@ static void ASSET_OT_clear(wmOperatorType *ot)
/* -------------------------------------------------------------------- */
+static bool asset_list_refresh_poll(bContext *C)
+{
+ const AssetLibraryReference *library = CTX_wm_asset_library(C);
+ if (!library) {
+ return false;
+ }
+
+ return ED_assetlist_storage_has_list_for_library(library);
+}
+
+static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
+{
+ const AssetLibraryReference *library = CTX_wm_asset_library(C);
+ ED_assetlist_clear(library, C);
+ return OPERATOR_FINISHED;
+}
+
+static void ASSET_OT_list_refresh(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Refresh Asset List";
+ ot->description = "Trigger a reread of the assets";
+ ot->idname = "ASSET_OT_list_refresh";
+
+ /* api callbacks */
+ ot->exec = asset_list_refresh_exec;
+ ot->poll = asset_list_refresh_poll;
+}
+
+/* -------------------------------------------------------------------- */
+
void ED_operatortypes_asset(void)
{
WM_operatortype_append(ASSET_OT_mark);
WM_operatortype_append(ASSET_OT_clear);
+
+ WM_operatortype_append(ASSET_OT_list_refresh);
}
diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc
new file mode 100644
index 00000000000..bed35fdeeb5
--- /dev/null
+++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc
@@ -0,0 +1,116 @@
+/*
+ * 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 edasset
+ *
+ * API for temporary loading of asset IDs.
+ * Uses the `BLO_library_temp_xxx()` API internally.
+ */
+
+#include <new>
+
+#include "DNA_asset_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_report.h"
+
+#include "BLI_utility_mixins.hh"
+
+#include "BLO_readfile.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "ED_asset_handle.h"
+#include "ED_asset_temp_id_consumer.h"
+
+using namespace blender;
+
+class AssetTemporaryIDConsumer : NonCopyable, NonMovable {
+ const AssetHandle &handle_;
+ TempLibraryContext *temp_lib_context_ = nullptr;
+
+ public:
+ AssetTemporaryIDConsumer(const AssetHandle &handle) : handle_(handle)
+ {
+ }
+ ~AssetTemporaryIDConsumer()
+ {
+ if (temp_lib_context_) {
+ BLO_library_temp_free(temp_lib_context_);
+ }
+ }
+
+ ID *get_local_id()
+ {
+ return ED_asset_handle_get_local_id(&handle_);
+ }
+
+ ID *import_id(const bContext *C,
+ const AssetLibraryReference &asset_library,
+ ID_Type id_type,
+ Main &bmain,
+ ReportList &reports)
+ {
+ const char *asset_name = ED_asset_handle_get_name(&handle_);
+ char blend_file_path[FILE_MAX_LIBEXTRA];
+ ED_asset_handle_get_full_library_path(C, &asset_library, &handle_, blend_file_path);
+
+ temp_lib_context_ = BLO_library_temp_load_id(
+ &bmain, blend_file_path, id_type, asset_name, &reports);
+
+ if (temp_lib_context_ == nullptr || temp_lib_context_->temp_id == nullptr) {
+ BKE_reportf(&reports, RPT_ERROR, "Unable to load %s from %s", asset_name, blend_file_path);
+ return nullptr;
+ }
+
+ BLI_assert(GS(temp_lib_context_->temp_id->name) == id_type);
+ return temp_lib_context_->temp_id;
+ }
+};
+
+AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle)
+{
+ if (!handle) {
+ return nullptr;
+ }
+ BLI_assert(handle->file_data->asset_data != nullptr);
+ return reinterpret_cast<AssetTempIDConsumer *>(
+ OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle));
+}
+
+void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer)
+{
+ OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer);
+}
+
+ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_,
+ const bContext *C,
+ const AssetLibraryReference *asset_library,
+ ID_Type id_type,
+ Main *bmain,
+ ReportList *reports)
+{
+ if (!(consumer_ && asset_library && bmain && reports)) {
+ return nullptr;
+ }
+ AssetTemporaryIDConsumer *consumer = reinterpret_cast<AssetTemporaryIDConsumer *>(consumer_);
+
+ if (ID *local_id = consumer->get_local_id()) {
+ return local_id;
+ }
+ return consumer->import_id(C, *asset_library, id_type, *bmain, *reports);
+}
diff --git a/source/blender/editors/geometry/geometry_attributes.c b/source/blender/editors/geometry/geometry_attributes.c
index 9b034d82a51..5cb491f116a 100644
--- a/source/blender/editors/geometry/geometry_attributes.c
+++ b/source/blender/editors/geometry/geometry_attributes.c
@@ -47,6 +47,21 @@ static bool geometry_attributes_poll(bContext *C)
BKE_id_attributes_supported(data);
}
+static bool geometry_attributes_remove_poll(bContext *C)
+{
+ if (!geometry_attributes_poll(C)) {
+ return false;
+ }
+
+ Object *ob = ED_object_context(C);
+ ID *data = (ob) ? ob->data : NULL;
+ if (BKE_id_attributes_active_get(data) != NULL) {
+ return true;
+ }
+
+ return false;
+}
+
static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C,
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
@@ -160,7 +175,7 @@ void GEOMETRY_OT_attribute_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = geometry_attribute_remove_exec;
- ot->poll = geometry_attributes_poll;
+ ot->poll = geometry_attributes_remove_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
index 31ab5eca974..ae2cc05c01b 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
@@ -17,8 +17,8 @@
* All rights reserved.
*/
-/** \file snap3d_gizmo.c
- * \ingroup edgizmolib
+/** \file
+ * \ingroup edgizmolib
*
* \name Snap Gizmo
*
@@ -374,7 +374,7 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
eSnapEditType edit_mode_type = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL) ?
SNAP_GEOM_FINAL :
- (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ?
+ (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ?
SNAP_GEOM_CAGE :
SNAP_GEOM_EDIT;
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c
index 196fb88ea55..7795eed7c21 100644
--- a/source/blender/editors/gpencil/annotate_draw.c
+++ b/source/blender/editors/gpencil/annotate_draw.c
@@ -918,7 +918,7 @@ void ED_annotation_draw_view3d(
return;
}
- /* when rendering to the offscreen buffer we don't want to
+ /* When rendering to the off-screen buffer we don't want to
* deal with the camera border, otherwise map the coords to the camera border. */
if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_FLAG_RENDER_VIEWPORT)) {
rctf rectf;
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 4b0c5ccd285..9bf44370c80 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -1320,7 +1320,7 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p)
p->area = curarea;
p->region = region;
p->v2d = &region->v2d;
- p->align_flag = &ts->gpencil_seq_align;
+ p->align_flag = &ts->gpencil_v2d_align;
/* check that gpencil data is allowed to be drawn */
if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
@@ -1339,7 +1339,7 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p)
p->area = curarea;
p->region = region;
p->v2d = &region->v2d;
- p->align_flag = &ts->gpencil_ima_align;
+ p->align_flag = &ts->gpencil_v2d_align;
break;
}
case SPACE_CLIP: {
diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c
index c800851bb08..86ec6c53fe6 100644
--- a/source/blender/editors/gpencil/gpencil_armature.c
+++ b/source/blender/editors/gpencil/gpencil_armature.c
@@ -80,7 +80,7 @@ static int gpencil_bone_looper(Object *ob,
{
/* We want to apply the function bone_func to every bone
* in an armature -- feed bone_looper the first bone and
- * a pointer to the bone_func and watch it go!. The int count
+ * a pointer to the bone_func and watch it go! The int count
* can be useful for counting bones with a certain property
* (e.g. skinnable)
*/
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index b1e57079d28..8baac26bed3 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -1355,6 +1355,14 @@ static int gpencil_merge_layer_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
/* Try to find frame in destination layer hash table. */
bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum));
+ /* Apply layer transformation. */
+ LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
+ for (int p = 0; p < gps_src->totpoints; p++) {
+ bGPDspoint *pt = &gps_src->points[p];
+ mul_v3_m4v3(&pt->x, gpl_src->layer_mat, &pt->x);
+ }
+ }
+
/* Add to tail all strokes. */
if (gpf_dst) {
BLI_movelisttolist(&gpf_dst->strokes, &gpf_src->strokes);
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 0062e363cdf..8640ffa67cf 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -278,7 +278,7 @@ static void gpencil_stroke_pair_table(bContext *C,
tGPDinterpolate_layer *tgpil)
{
bGPdata *gpd = tgpi->gpd;
- const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) &&
+ const bool only_selected = (GPENCIL_EDIT_MODE(gpd) &&
((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) != 0));
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
@@ -291,8 +291,7 @@ static void gpencil_stroke_pair_table(bContext *C,
LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) {
bGPDstroke *gps_to = NULL;
/* only selected */
- if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) &&
- ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
+ if (GPENCIL_EDIT_MODE(gpd) && (only_selected) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
continue;
}
/* skip strokes that are invalid for current view */
@@ -712,7 +711,7 @@ static bool gpencil_interpolate_set_init_values(bContext *C, wmOperator *op, tGP
tgpi->flag, (RNA_enum_get(op->ptr, "layers") == 1), GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS);
SET_FLAG_FROM_TEST(
tgpi->flag,
- ((GPENCIL_EDIT_MODE(tgpi->gpd)) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))),
+ (GPENCIL_EDIT_MODE(tgpi->gpd) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))),
GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED);
tgpi->flipmode = RNA_enum_get(op->ptr, "flip");
@@ -1249,7 +1248,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
const int step = RNA_int_get(op->ptr, "step");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const bool all_layers = (bool)(RNA_enum_get(op->ptr, "layers") == 1);
- const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) &&
+ const bool only_selected = (GPENCIL_EDIT_MODE(gpd) &&
(RNA_boolean_get(op->ptr, "interpolate_selected_only") != 0));
eGP_InterpolateFlipMode flipmode = RNA_enum_get(op->ptr, "flip");
@@ -1309,7 +1308,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) {
bGPDstroke *gps_to = NULL;
/* Only selected. */
- if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) &&
+ if (GPENCIL_EDIT_MODE(gpd) && (only_selected) &&
((gps_from->flag & GP_STROKE_SELECT) == 0)) {
continue;
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 78eaab01b1a..cf49aefe2ea 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1316,8 +1316,7 @@ static void gpencil_primitive_interaction_end(bContext *C,
BrushGpencilSettings *brush_settings = brush->gpencil_settings;
const int def_nr = tgpi->gpd->vertex_group_active_index - 1;
- const ListBase *defbase = BKE_object_defgroup_list(tgpi->ob);
- const bool have_weight = (bool)BLI_findlink(defbase, def_nr);
+ const bool have_weight = BLI_findlink(&tgpi->gpd->vertex_group_names, def_nr) != NULL;
/* return to normal cursor and header status */
ED_workspace_status_text(C, NULL);
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 462462cf341..14caf0c08a7 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -1352,7 +1352,7 @@ static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso
* - This is useful when animating as it saves that "uh-oh" moment when you realize you've
* spent too much time editing the wrong frame.
*/
- if ((IS_AUTOKEY_ON(scene)) && (gpf->framenum != cfra)) {
+ if (IS_AUTOKEY_ON(scene) && (gpf->framenum != cfra)) {
BKE_gpencil_frame_addcopy(gpl, cfra);
/* Need tag to recalculate evaluated data to avoid crashes. */
DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@@ -1377,7 +1377,7 @@ static float gpencil_sculpt_rotation_eval_get(tGP_BrushEditData *gso,
int idx_eval)
{
/* If multiframe or no modifiers, return 0. */
- if ((GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd)) || (!gso->is_transformed)) {
+ if (GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd) || (!gso->is_transformed)) {
return 0.0f;
}
@@ -1513,8 +1513,7 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
}
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
/* If masked and the point is not selected, skip it. */
- if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) &&
- ((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
+ if (GPENCIL_ANY_SCULPT_MASK(gso->mask) && ((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
continue;
}
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index 16605b6c634..633e371cbd1 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -919,7 +919,7 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
pt_active = pt->runtime.pt_orig;
if (pt_active != NULL) {
/* If masked and the point is not selected, skip it. */
- if ((GPENCIL_ANY_VERTEX_MASK(gso->mask)) &&
+ if (GPENCIL_ANY_VERTEX_MASK(gso->mask) &&
((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
continue;
}
diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h
index 1d688b2ad68..0a1cf643f37 100644
--- a/source/blender/editors/include/BIF_glutil.h
+++ b/source/blender/editors/include/BIF_glutil.h
@@ -49,17 +49,17 @@ typedef struct IMMDrawPixelsTexState {
IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin);
/**
- * immDrawPixelsTex - Functions like a limited glDrawPixels, but actually draws the
+ * #immDrawPixelsTex - Functions like a limited #glDrawPixels, but actually draws the
* image using textures, which can be tremendously faster on low-end
* cards, and also avoids problems with the raster position being
- * clipped when offscreen. Pixel unpacking parameters and
- * the glPixelZoom values are _not_ respected.
+ * clipped when off-screen. Pixel unpacking parameters and
+ * the #glPixelZoom values are _not_ respected.
*
- * \attention Use immDrawPixelsTexSetup before calling this function.
+ * \attention Use #immDrawPixelsTexSetup before calling this function.
*
- * \attention This routine makes many assumptions: the rect data
+ * \attention This routine makes many assumptions: the `rect` data
* is expected to be in RGBA byte or float format, and the
- * modelview and projection matrices are assumed to define a
+ * model-view and projection matrices are assumed to define a
* 1-to-1 mapping to screen space.
*/
void immDrawPixelsTex(IMMDrawPixelsTexState *state,
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 5cf2a9c9dd0..75c02082bd3 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -34,9 +34,9 @@ struct ListBase;
struct ARegion;
struct ARegionType;
+struct FModifier;
struct Main;
struct NlaStrip;
-struct FModifier;
struct PanelType;
struct ReportList;
struct ScrArea;
@@ -290,7 +290,7 @@ typedef enum eAnimFilter_Flags {
* (i.e. scene visibility criteria).
*
* XXX: it's hard to think of any examples where this *ISN'T* the case...
- * perhaps becomes implicit?.
+ * perhaps becomes implicit?
*/
ANIMFILTER_DATA_VISIBLE = (1 << 0),
/** channel is visible within the channel-list hierarchy
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 85563b76f38..868235c36e5 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -23,6 +23,10 @@
#pragma once
+#include <stdbool.h>
+
+#include "BLI_listbase.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -41,6 +45,7 @@ struct Scene;
struct UndoType;
struct View3D;
struct ViewLayer;
+struct bAction;
struct bArmature;
struct bContext;
struct bPoseChannel;
@@ -242,6 +247,17 @@ void ED_mesh_deform_bind_callback(struct MeshDeformModifierData *mmd,
int totvert,
float cagemat[4][4]);
+/* Pose backups, pose_backup.c */
+struct PoseBackup;
+/* Create a backup of those bones that are animated in the given action. */
+struct PoseBackup *ED_pose_backup_create_selected_bones(
+ const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT;
+struct PoseBackup *ED_pose_backup_create_all_bones(
+ const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT;
+bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup);
+void ED_pose_backup_restore(const struct PoseBackup *pbd);
+void ED_pose_backup_free(struct PoseBackup *pbd);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h
index dd505167fe5..8f19c97e671 100644
--- a/source/blender/editors/include/ED_asset.h
+++ b/source/blender/editors/include/ED_asset.h
@@ -16,6 +16,9 @@
/** \file
* \ingroup editors
+ *
+ * The public API for assets is defined in dedicated headers. This is a utility file that just
+ * includes all of these.
*/
#pragma once
@@ -24,13 +27,23 @@
extern "C" {
#endif
-bool ED_asset_mark_id(const struct bContext *C, struct ID *id);
-bool ED_asset_clear_id(struct ID *id);
-
-bool ED_asset_can_make_single_from_context(const struct bContext *C);
+/* Barely anything here. Just general editor level functions. Actual asset level code is in
+ * dedicated headers. */
void ED_operatortypes_asset(void);
#ifdef __cplusplus
}
#endif
+
+#include "../asset/ED_asset_filter.h"
+#include "../asset/ED_asset_handle.h"
+#include "../asset/ED_asset_library.h"
+#include "../asset/ED_asset_list.h"
+#include "../asset/ED_asset_mark_clear.h"
+#include "../asset/ED_asset_temp_id_consumer.h"
+
+/* C++ only headers. */
+#ifdef __cplusplus
+# include "../asset/ED_asset_list.hh"
+#endif
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index e57e2316d93..82057c726a5 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -140,6 +140,7 @@ void ED_fileselect_clear(struct wmWindowManager *wm, struct SpaceFile *sfile);
void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile);
+bool ED_fileselect_is_file_browser(const struct SpaceFile *sfile);
bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile);
struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile);
diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h
index 2f8faf1b2bd..d2d22dd38dc 100644
--- a/source/blender/editors/include/ED_keyframes_draw.h
+++ b/source/blender/editors/include/ED_keyframes_draw.h
@@ -28,10 +28,7 @@ extern "C" {
#endif
struct AnimData;
-struct CacheFile;
-struct DLRBT_Tree;
struct FCurve;
-struct ListBase;
struct MaskLayer;
struct Object;
struct Scene;
@@ -42,99 +39,6 @@ struct bAnimContext;
struct bDopeSheet;
struct bGPDlayer;
-/* ****************************** Base Structs ****************************** */
-
-/* Information about the stretch of time from current to the next column */
-typedef struct ActKeyBlockInfo {
- /* Combination of flags from all curves. */
- short flag;
- /* Mask of flags that differ between curves. */
- short conflict;
-
- /* Selection flag. */
- char sel;
-} ActKeyBlockInfo;
-
-/* Keyframe Column Struct */
-typedef struct ActKeyColumn {
- /* ListBase linkage */
- struct ActKeyColumn *next, *prev;
-
- /* sorting-tree linkage */
- /** 'children' of this node, less than and greater than it (respectively) */
- struct ActKeyColumn *left, *right;
- /** parent of this node in the tree */
- struct ActKeyColumn *parent;
- /** DLRB_BLACK or DLRB_RED */
- char tree_col;
-
- /* keyframe info */
- /** eBezTripe_KeyframeType */
- char key_type;
- /** eKeyframeHandleDrawOpts */
- char handle_type;
- /** eKeyframeExtremeDrawOpts */
- char extreme_type;
- short sel;
- float cfra;
-
- /* key-block info */
- ActKeyBlockInfo block;
-
- /* number of curves and keys in this column */
- short totcurve, totkey, totblock;
-} ActKeyColumn;
-
-/* ActKeyBlockInfo - Flag */
-typedef enum eActKeyBlock_Hold {
- /* Key block represents a moving hold */
- ACTKEYBLOCK_FLAG_MOVING_HOLD = (1 << 0),
- /* Key block represents a static hold */
- ACTKEYBLOCK_FLAG_STATIC_HOLD = (1 << 1),
- /* Key block represents any kind of hold */
- ACTKEYBLOCK_FLAG_ANY_HOLD = (1 << 2),
- /* The curve segment uses non-bezier interpolation */
- ACTKEYBLOCK_FLAG_NON_BEZIER = (1 << 3),
- /* The block is grease pencil */
- ACTKEYBLOCK_FLAG_GPENCIL = (1 << 4),
-} eActKeyBlock_Flag;
-
-/* *********************** Keyframe Drawing ****************************** */
-
-/* options for keyframe shape drawing */
-typedef enum eKeyframeShapeDrawOpts {
- /* only the border */
- KEYFRAME_SHAPE_FRAME = 0,
- /* only the inside filling */
- KEYFRAME_SHAPE_INSIDE,
- /* the whole thing */
- KEYFRAME_SHAPE_BOTH,
-} eKeyframeShapeDrawOpts;
-
-/* Handle type. */
-typedef enum eKeyframeHandleDrawOpts {
- /* Don't draw */
- KEYFRAME_HANDLE_NONE = 0,
- /* Various marks in order of increasing display priority. */
- KEYFRAME_HANDLE_AUTO_CLAMP,
- KEYFRAME_HANDLE_AUTO,
- KEYFRAME_HANDLE_VECTOR,
- KEYFRAME_HANDLE_ALIGNED,
- KEYFRAME_HANDLE_FREE,
-} eKeyframeHandleDrawOpts;
-
-/* Extreme type. */
-typedef enum eKeyframeExtremeDrawOpts {
- KEYFRAME_EXTREME_NONE = 0,
- /* Minimum/maximum present. */
- KEYFRAME_EXTREME_MIN = (1 << 0),
- KEYFRAME_EXTREME_MAX = (1 << 1),
- /* Grouped keys have different states. */
- KEYFRAME_EXTREME_MIXED = (1 << 2),
- /* Both neighbors are equal to this key. */
- KEYFRAME_EXTREME_FLAT = (1 << 3),
-} eKeyframeExtremeDrawOpts;
-
/* draw simple diamond-shape keyframe */
/* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND,
* immBegin(GPU_PRIM_POINTS, n), then call this n times */
@@ -216,59 +120,6 @@ void draw_masklay_channel(struct View2D *v2d,
float yscale_fac,
int saction_flag);
-/* Keydata Generation --------------- */
-/* F-Curve */
-void fcurve_to_keylist(struct AnimData *adt,
- struct FCurve *fcu,
- struct DLRBT_Tree *keys,
- int saction_flag);
-/* Action Group */
-void agroup_to_keylist(struct AnimData *adt,
- struct bActionGroup *agrp,
- struct DLRBT_Tree *keys,
- int saction_flag);
-/* Action */
-void action_to_keylist(struct AnimData *adt,
- struct bAction *act,
- struct DLRBT_Tree *keys,
- int saction_flag);
-/* Object */
-void ob_to_keylist(struct bDopeSheet *ads,
- struct Object *ob,
- struct DLRBT_Tree *keys,
- int saction_flag);
-/* Cache File */
-void cachefile_to_keylist(struct bDopeSheet *ads,
- struct CacheFile *cache_file,
- struct DLRBT_Tree *keys,
- int saction_flag);
-/* Scene */
-void scene_to_keylist(struct bDopeSheet *ads,
- struct Scene *sce,
- struct DLRBT_Tree *keys,
- int saction_flag);
-/* DopeSheet Summary */
-void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, int saction_flag);
-/* Grease Pencil datablock summary */
-void gpencil_to_keylist(struct bDopeSheet *ads,
- struct bGPdata *gpd,
- struct DLRBT_Tree *keys,
- const bool active);
-/* Grease Pencil Layer */
-void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys);
-/* Mask */
-void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct DLRBT_Tree *keys);
-
-/* ActKeyColumn API ---------------- */
-/* Comparator callback used for ActKeyColumns and cframe float-value pointer */
-short compare_ak_cfraPtr(void *node, void *data);
-
-/* Checks if ActKeyColumn has any block data */
-bool actkeyblock_is_valid(ActKeyColumn *ac);
-
-/* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */
-int actkeyblock_get_valid_hold(ActKeyColumn *ac);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h
new file mode 100644
index 00000000000..be3eac66771
--- /dev/null
+++ b/source/blender/editors/include/ED_keyframes_keylist.h
@@ -0,0 +1,192 @@
+/*
+ * 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) (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AnimData;
+struct CacheFile;
+struct DLRBT_Tree;
+struct FCurve;
+struct MaskLayer;
+struct Object;
+struct Scene;
+struct bAnimContext;
+struct bDopeSheet;
+struct bGPDlayer;
+
+/* ****************************** Base Structs ****************************** */
+
+/* Information about the stretch of time from current to the next column */
+typedef struct ActKeyBlockInfo {
+ /* Combination of flags from all curves. */
+ short flag;
+ /* Mask of flags that differ between curves. */
+ short conflict;
+
+ /* Selection flag. */
+ char sel;
+} ActKeyBlockInfo;
+
+/* Keyframe Column Struct */
+typedef struct ActKeyColumn {
+ /* ListBase linkage */
+ struct ActKeyColumn *next, *prev;
+
+ /* sorting-tree linkage */
+ /** 'children' of this node, less than and greater than it (respectively) */
+ struct ActKeyColumn *left, *right;
+ /** parent of this node in the tree */
+ struct ActKeyColumn *parent;
+ /** DLRB_BLACK or DLRB_RED */
+ char tree_col;
+
+ /* keyframe info */
+ /** eBezTripe_KeyframeType */
+ char key_type;
+ /** eKeyframeHandleDrawOpts */
+ char handle_type;
+ /** eKeyframeExtremeDrawOpts */
+ char extreme_type;
+ short sel;
+ float cfra;
+
+ /* key-block info */
+ ActKeyBlockInfo block;
+
+ /* number of curves and keys in this column */
+ short totcurve, totkey, totblock;
+} ActKeyColumn;
+
+/* ActKeyBlockInfo - Flag */
+typedef enum eActKeyBlock_Hold {
+ /* Key block represents a moving hold */
+ ACTKEYBLOCK_FLAG_MOVING_HOLD = (1 << 0),
+ /* Key block represents a static hold */
+ ACTKEYBLOCK_FLAG_STATIC_HOLD = (1 << 1),
+ /* Key block represents any kind of hold */
+ ACTKEYBLOCK_FLAG_ANY_HOLD = (1 << 2),
+ /* The curve segment uses non-bezier interpolation */
+ ACTKEYBLOCK_FLAG_NON_BEZIER = (1 << 3),
+ /* The block is grease pencil */
+ ACTKEYBLOCK_FLAG_GPENCIL = (1 << 4),
+} eActKeyBlock_Flag;
+
+/* *********************** Keyframe Drawing ****************************** */
+
+/* options for keyframe shape drawing */
+typedef enum eKeyframeShapeDrawOpts {
+ /* only the border */
+ KEYFRAME_SHAPE_FRAME = 0,
+ /* only the inside filling */
+ KEYFRAME_SHAPE_INSIDE,
+ /* the whole thing */
+ KEYFRAME_SHAPE_BOTH,
+} eKeyframeShapeDrawOpts;
+
+/* Handle type. */
+typedef enum eKeyframeHandleDrawOpts {
+ /* Don't draw */
+ KEYFRAME_HANDLE_NONE = 0,
+ /* Various marks in order of increasing display priority. */
+ KEYFRAME_HANDLE_AUTO_CLAMP,
+ KEYFRAME_HANDLE_AUTO,
+ KEYFRAME_HANDLE_VECTOR,
+ KEYFRAME_HANDLE_ALIGNED,
+ KEYFRAME_HANDLE_FREE,
+} eKeyframeHandleDrawOpts;
+
+/* Extreme type. */
+typedef enum eKeyframeExtremeDrawOpts {
+ KEYFRAME_EXTREME_NONE = 0,
+ /* Minimum/maximum present. */
+ KEYFRAME_EXTREME_MIN = (1 << 0),
+ KEYFRAME_EXTREME_MAX = (1 << 1),
+ /* Grouped keys have different states. */
+ KEYFRAME_EXTREME_MIXED = (1 << 2),
+ /* Both neighbors are equal to this key. */
+ KEYFRAME_EXTREME_FLAT = (1 << 3),
+} eKeyframeExtremeDrawOpts;
+
+/* ******************************* Methods ****************************** */
+
+/* Key-data Generation --------------- */
+
+/* F-Curve */
+void fcurve_to_keylist(struct AnimData *adt,
+ struct FCurve *fcu,
+ struct DLRBT_Tree *keys,
+ int saction_flag);
+/* Action Group */
+void agroup_to_keylist(struct AnimData *adt,
+ struct bActionGroup *agrp,
+ struct DLRBT_Tree *keys,
+ int saction_flag);
+/* Action */
+void action_to_keylist(struct AnimData *adt,
+ struct bAction *act,
+ struct DLRBT_Tree *keys,
+ int saction_flag);
+/* Object */
+void ob_to_keylist(struct bDopeSheet *ads,
+ struct Object *ob,
+ struct DLRBT_Tree *keys,
+ int saction_flag);
+/* Cache File */
+void cachefile_to_keylist(struct bDopeSheet *ads,
+ struct CacheFile *cache_file,
+ struct DLRBT_Tree *keys,
+ int saction_flag);
+/* Scene */
+void scene_to_keylist(struct bDopeSheet *ads,
+ struct Scene *sce,
+ struct DLRBT_Tree *keys,
+ int saction_flag);
+/* DopeSheet Summary */
+void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, int saction_flag);
+/* Grease Pencil datablock summary */
+void gpencil_to_keylist(struct bDopeSheet *ads,
+ struct bGPdata *gpd,
+ struct DLRBT_Tree *keys,
+ const bool active);
+/* Grease Pencil Layer */
+void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys);
+/* Mask */
+void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct DLRBT_Tree *keys);
+
+/* ActKeyColumn API ---------------- */
+/* Comparator callback used for ActKeyColumns and cframe float-value pointer */
+short compare_ak_cfraPtr(void *node, void *data);
+
+/* Checks if ActKeyColumn has any block data */
+bool actkeyblock_is_valid(ActKeyColumn *ac);
+
+/* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */
+int actkeyblock_get_valid_hold(ActKeyColumn *ac);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h
index 058d4fa91a3..6e4002fcc0a 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -31,6 +31,7 @@ struct ID;
struct Main;
struct Scene;
struct ScrArea;
+struct SpaceNode;
struct Tex;
struct View2D;
struct bContext;
@@ -40,7 +41,6 @@ struct bNodeSocketType;
struct bNodeTree;
struct bNodeTreeType;
struct bNodeType;
-struct SpaceNode;
typedef enum {
NODE_TOP = 1,
diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h
index 6d0172e724a..5318c653b6d 100644
--- a/source/blender/editors/include/ED_particle.h
+++ b/source/blender/editors/include/ED_particle.h
@@ -34,9 +34,9 @@ struct ParticleSystem;
struct Scene;
struct UndoType;
struct ViewLayer;
-struct wmGenericUserData;
struct bContext;
struct rcti;
+struct wmGenericUserData;
/* particle edit mode */
void PE_free_ptcache_edit(struct PTCacheEdit *edit);
diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h
index 0fb06639dbf..5cdcbbab42a 100644
--- a/source/blender/editors/include/ED_render.h
+++ b/source/blender/editors/include/ED_render.h
@@ -72,13 +72,12 @@ struct Scene *ED_render_job_get_current_scene(const struct bContext *C);
* - PR_NODE_RENDER: preview is rendered for node editor
* - PR_ICON_DEFERRED: No render, we just ensure deferred icon data gets generated.
*/
-
-enum {
+typedef enum ePreviewRenderMethod {
PR_BUTS_RENDER = 0,
PR_ICON_RENDER = 1,
PR_NODE_RENDER = 2,
PR_ICON_DEFERRED = 3,
-};
+} ePreviewRenderMethod;
void ED_preview_ensure_dbase(void);
void ED_preview_free_dbase(void);
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 823050b46f7..60ef3e740c6 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -317,6 +317,8 @@ bool ED_operator_animview_active(struct bContext *C);
bool ED_operator_outliner_active(struct bContext *C);
bool ED_operator_outliner_active_no_editobject(struct bContext *C);
bool ED_operator_file_active(struct bContext *C);
+bool ED_operator_file_browsing_active(struct bContext *C);
+bool ED_operator_asset_browsing_active(struct bContext *C);
bool ED_operator_spreadsheet_active(struct bContext *C);
bool ED_operator_action_active(struct bContext *C);
bool ED_operator_buttons_active(struct bContext *C);
diff --git a/source/blender/editors/include/ED_spreadsheet.h b/source/blender/editors/include/ED_spreadsheet.h
index ff77135a51c..dfa8aa7bfbc 100644
--- a/source/blender/editors/include/ED_spreadsheet.h
+++ b/source/blender/editors/include/ED_spreadsheet.h
@@ -16,14 +16,14 @@
#pragma once
-struct SpreadsheetContext;
-struct SpaceSpreadsheet;
-struct SpaceNode;
struct ID;
-struct bNode;
struct Main;
-struct bContext;
struct Object;
+struct SpaceNode;
+struct SpaceSpreadsheet;
+struct SpreadsheetContext;
+struct bContext;
+struct bNode;
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index 953f26aa45f..0105af843bb 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -24,6 +24,7 @@
#pragma once
#include "BLI_compiler_attrs.h"
+#include "WM_types.h"
#ifdef __cplusplus
extern "C" {
@@ -61,6 +62,24 @@ void ED_region_draw_mouse_line_cb(const struct bContext *C,
void ED_region_image_metadata_draw(
int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy);
+/* Slider */
+struct tSlider;
+
+struct tSlider *ED_slider_create(struct bContext *C);
+void ED_slider_init(struct tSlider *slider, const struct wmEvent *event);
+bool ED_slider_modal(struct tSlider *slider, const struct wmEvent *event);
+void ED_slider_destroy(struct bContext *C, struct tSlider *slider);
+
+void ED_slider_status_string_get(const struct tSlider *slider,
+ char *status_string,
+ const size_t size_of_status_string);
+
+float ED_slider_factor_get(struct tSlider *slider);
+void ED_slider_factor_set(struct tSlider *slider, float factor);
+
+bool ED_slider_allow_overshoot_get(struct tSlider *slider);
+void ED_slider_allow_overshoot_set(struct tSlider *slider, const bool value);
+
/* ************** XXX OLD CRUFT WARNING ************* */
void apply_keyb_grid(
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 802c175492f..a6e465d04e8 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -25,6 +25,7 @@
#include "BLI_compiler_attrs.h"
#include "BLI_sys_types.h" /* size_t */
+#include "BLI_utildefines.h"
#include "UI_interface_icons.h"
#ifdef __cplusplus
@@ -34,8 +35,11 @@ extern "C" {
/* Struct Declarations */
struct ARegion;
+struct AssetHandle;
+struct AssetFilterSettings;
struct AutoComplete;
struct EnumPropertyItem;
+struct FileDirEntry;
struct FileSelectParams;
struct ID;
struct IDProperty;
@@ -366,6 +370,9 @@ typedef enum {
/** Buttons with value >= #UI_BTYPE_SEARCH_MENU don't get undo pushes. */
UI_BTYPE_SEARCH_MENU = 41 << 9,
UI_BTYPE_EXTRA = 42 << 9,
+ /** A preview image (#PreviewImage), with text under it. Typically bigger than normal buttons and
+ * laid out in a grid, e.g. like the File Browser in thumbnail display mode. */
+ UI_BTYPE_PREVIEW_TILE = 43 << 9,
UI_BTYPE_HOTKEY_EVENT = 46 << 9,
/** Non-interactive image, used for splash screen */
UI_BTYPE_IMAGE = 47 << 9,
@@ -587,6 +594,8 @@ bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title);
bool UI_block_is_empty(const uiBlock *block);
bool UI_block_can_add_separator(const uiBlock *block);
+struct uiList *UI_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event);
+
/* interface_region_menu_popup.c */
/**
* Popup Menus
@@ -704,6 +713,7 @@ void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2],
void UI_block_end(const struct bContext *C, uiBlock *block);
void UI_block_draw(const struct bContext *C, struct uiBlock *block);
void UI_blocklist_update_window_matrix(const struct bContext *C, const struct ListBase *lb);
+void UI_blocklist_update_view_for_buttons(const struct bContext *C, const struct ListBase *lb);
void UI_blocklist_draw(const struct bContext *C, const struct ListBase *lb);
void UI_block_update_from_old(const struct bContext *C, struct uiBlock *block);
@@ -761,9 +771,8 @@ int UI_but_return_value_get(uiBut *but);
void UI_but_drag_set_id(uiBut *but, struct ID *id);
void UI_but_drag_set_asset(uiBut *but,
- const char *name,
+ const struct AssetHandle *asset,
const char *path,
- int id_type,
int import_type, /* eFileAssetImportType */
int icon,
struct ImBuf *imb,
@@ -2194,6 +2203,17 @@ void uiTemplateCacheFile(uiLayout *layout,
/* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */
#define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list"
+enum uiTemplateListFlags {
+ UI_TEMPLATE_LIST_FLAG_NONE = 0,
+ UI_TEMPLATE_LIST_SORT_REVERSE = (1 << 0),
+ UI_TEMPLATE_LIST_SORT_LOCK = (1 << 1),
+ /* Don't allow resizing the list, i.e. don't add the grip button. */
+ UI_TEMPLATE_LIST_NO_GRIP = (1 << 2),
+
+ UI_TEMPLATE_LIST_FLAGS_LAST
+};
+ENUM_OPERATORS(enum uiTemplateListFlags, UI_TEMPLATE_LIST_FLAGS_LAST);
+
void uiTemplateList(uiLayout *layout,
struct bContext *C,
const char *listtype_name,
@@ -2207,8 +2227,23 @@ void uiTemplateList(uiLayout *layout,
int maxrows,
int layout_type,
int columns,
- bool sort_reverse,
- bool sort_lock);
+ enum uiTemplateListFlags flags);
+struct uiList *uiTemplateList_ex(uiLayout *layout,
+ struct bContext *C,
+ const char *listtype_name,
+ const char *list_id,
+ struct PointerRNA *dataptr,
+ const char *propname,
+ struct PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ int rows,
+ int maxrows,
+ int layout_type,
+ int columns,
+ enum uiTemplateListFlags flags,
+ void *customdata);
+
void uiTemplateNodeLink(uiLayout *layout,
struct bContext *C,
struct bNodeTree *ntree,
@@ -2254,6 +2289,27 @@ int uiTemplateRecentFiles(struct uiLayout *layout, int rows);
void uiTemplateFileSelectPath(uiLayout *layout,
struct bContext *C,
struct FileSelectParams *params);
+void uiTemplateAssetView(struct uiLayout *layout,
+ struct bContext *C,
+ const char *list_id,
+ struct PointerRNA *asset_library_dataptr,
+ const char *asset_library_propname,
+ struct PointerRNA *assets_dataptr,
+ const char *assets_propname,
+ struct PointerRNA *active_dataptr,
+ const char *active_propname,
+ const struct AssetFilterSettings *filter_settings,
+ const char *activate_opname,
+ struct PointerRNA *r_activate_op_properties,
+ const char *drag_opname,
+ struct PointerRNA *r_drag_op_properties);
+
+struct PointerRNA *UI_list_custom_activate_operator_set(struct uiList *ui_list,
+ const char *opname,
+ bool create_properties);
+struct PointerRNA *UI_list_custom_drag_operator_set(struct uiList *ui_list,
+ const char *opname,
+ bool create_properties);
/* items */
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname);
@@ -2518,6 +2574,7 @@ typedef struct uiDragColorHandle {
void ED_operatortypes_ui(void);
void ED_keymap_ui(struct wmKeyConfig *keyconf);
+void ED_uilisttypes_ui(void);
void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop);
bool UI_drop_color_poll(struct bContext *C,
@@ -2618,6 +2675,8 @@ bool UI_editsource_enable_check(void);
void UI_editsource_active_but_test(uiBut *but);
void UI_editsource_but_replace(const uiBut *old_but, uiBut *new_but);
+void UI_but_ensure_in_view(const struct bContext *C, struct ARegion *region, const uiBut *but);
+
/* UI_butstore_ helpers */
typedef struct uiButStore uiButStore;
typedef struct uiButStoreElem uiButStoreElem;
diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h
index 266a538b6c3..37cf7229ffb 100644
--- a/source/blender/editors/include/UI_interface_icons.h
+++ b/source/blender/editors/include/UI_interface_icons.h
@@ -105,7 +105,10 @@ int UI_iconfile_get_index(const char *filename);
struct PreviewImage *UI_icon_to_preview(int icon_id);
-int UI_icon_from_rnaptr(struct bContext *C, struct PointerRNA *ptr, int rnaicon, const bool big);
+int UI_icon_from_rnaptr(const struct bContext *C,
+ struct PointerRNA *ptr,
+ int rnaicon,
+ const bool big);
int UI_icon_from_idcode(const int idcode);
int UI_icon_from_library(const struct ID *id);
int UI_icon_from_object_mode(const int mode);
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index 8191a9a9062..4ee7df89487 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -143,17 +143,19 @@ void UI_view2d_view_orthoSpecial(struct ARegion *region, struct View2D *v2d, con
void UI_view2d_view_restore(const struct bContext *C);
/* grid drawing */
-void UI_view2d_constant_grid_draw(const struct View2D *v2d, float step);
void UI_view2d_multi_grid_draw(
const struct View2D *v2d, int colorid, float step, int level_size, int totlevels);
void UI_view2d_draw_lines_y__values(const struct View2D *v2d);
void UI_view2d_draw_lines_x__values(const struct View2D *v2d);
-void UI_view2d_draw_lines_x__discrete_values(const struct View2D *v2d);
-void UI_view2d_draw_lines_x__discrete_time(const struct View2D *v2d, const struct Scene *scene);
+void UI_view2d_draw_lines_x__discrete_values(const struct View2D *v2d, bool display_minor_lines);
+void UI_view2d_draw_lines_x__discrete_time(const struct View2D *v2d,
+ const struct Scene *scene,
+ bool display_minor_lines);
void UI_view2d_draw_lines_x__discrete_frames_or_seconds(const struct View2D *v2d,
const struct Scene *scene,
- bool display_seconds);
+ bool display_seconds,
+ bool display_minor_lines);
void UI_view2d_draw_lines_x__frames_or_seconds(const struct View2D *v2d,
const struct Scene *scene,
bool display_seconds);
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 2cc3830042c..39dd6143eb9 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -66,6 +66,8 @@ set(SRC
interface_region_tooltip.c
interface_regions.c
interface_style.c
+ interface_template_asset_view.cc
+ interface_template_list.cc
interface_template_search_menu.c
interface_template_search_operator.c
interface_templates.c
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 32b86119753..a2b25aed582 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -57,6 +57,8 @@
#include "BKE_screen.h"
#include "BKE_unit.h"
+#include "ED_asset.h"
+
#include "GPU_matrix.h"
#include "GPU_state.h"
@@ -131,12 +133,10 @@ static bool ui_but_is_unit_radians(const uiBut *but)
/* ************* window matrix ************** */
-void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y)
+void ui_block_to_region_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y)
{
const int getsizex = BLI_rcti_size_x(&region->winrct) + 1;
const int getsizey = BLI_rcti_size_y(&region->winrct) + 1;
- const int sx = region->winrct.xmin;
- const int sy = region->winrct.ymin;
float gx = *r_x;
float gy = *r_y;
@@ -146,14 +146,19 @@ void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, fl
gy += block->panel->ofsy;
}
- *r_x = ((float)sx) +
- ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] +
+ *r_x = ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] +
block->winmat[3][0]));
- *r_y = ((float)sy) +
- ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] +
+ *r_y = ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] +
block->winmat[3][1]));
}
+void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y)
+{
+ ui_block_to_region_fl(region, block, r_x, r_y);
+ *r_x += region->winrct.xmin;
+ *r_y += region->winrct.ymin;
+}
+
void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_y)
{
float fx = *r_x;
@@ -165,6 +170,16 @@ void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_
*r_y = (int)(fy + 0.5f);
}
+void ui_block_to_region_rctf(const ARegion *region,
+ uiBlock *block,
+ rctf *rct_dst,
+ const rctf *rct_src)
+{
+ *rct_dst = *rct_src;
+ ui_block_to_region_fl(region, block, &rct_dst->xmin, &rct_dst->ymin);
+ ui_block_to_region_fl(region, block, &rct_dst->xmax, &rct_dst->ymax);
+}
+
void ui_block_to_window_rctf(const ARegion *region,
uiBlock *block,
rctf *rct_dst,
@@ -249,6 +264,14 @@ void ui_window_to_region_rcti(const ARegion *region, rcti *rect_dst, const rcti
rect_dst->ymax = rct_src->ymax - region->winrct.ymin;
}
+void ui_window_to_region_rctf(const ARegion *region, rctf *rect_dst, const rctf *rct_src)
+{
+ rect_dst->xmin = rct_src->xmin - region->winrct.xmin;
+ rect_dst->xmax = rct_src->xmax - region->winrct.xmin;
+ rect_dst->ymin = rct_src->ymin - region->winrct.ymin;
+ rect_dst->ymax = rct_src->ymax - region->winrct.ymin;
+}
+
void ui_region_to_window(const ARegion *region, int *r_x, int *r_y)
{
*r_x += region->winrct.xmin;
@@ -2432,7 +2455,7 @@ bool ui_but_is_rna_valid(uiBut *but)
*/
bool ui_but_supports_cycling(const uiBut *but)
{
- return ((ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_LISTBOX)) ||
+ return (ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_LISTBOX) ||
(but->type == UI_BTYPE_MENU && ui_but_menu_step_poll(but)) ||
(but->type == UI_BTYPE_COLOR && ((uiButColor *)but)->is_pallete_color) ||
(but->menu_step_func != NULL));
@@ -2629,7 +2652,7 @@ static double ui_get_but_scale_unit(uiBut *but, double value)
const int unit_type = UI_but_unit_type_get(but);
/* Time unit is a bit special, not handled by BKE_scene_unit_scale() for now. */
- if (unit_type == PROP_UNIT_TIME) { /* WARNING - using evil_C :| */
+ if (unit_type == PROP_UNIT_TIME) { /* WARNING: using evil_C :| */
Scene *scene = CTX_data_scene(but->block->evil_C);
return FRA2TIME(value);
}
@@ -3440,6 +3463,15 @@ void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb)
}
}
+void UI_blocklist_update_view_for_buttons(const bContext *C, const ListBase *lb)
+{
+ LISTBASE_FOREACH (uiBlock *, block, lb) {
+ if (block->active) {
+ ui_but_update_view_for_active(C, block);
+ }
+ }
+}
+
void UI_blocklist_draw(const bContext *C, const ListBase *lb)
{
LISTBASE_FOREACH (uiBlock *, block, lb) {
@@ -6146,10 +6178,12 @@ void UI_but_drag_set_id(uiBut *but, ID *id)
but->dragpoin = (void *)id;
}
+/**
+ * \param asset: May be passed from a temporary variable, drag data only stores a copy of this.
+ */
void UI_but_drag_set_asset(uiBut *but,
- const char *name,
+ const AssetHandle *asset,
const char *path,
- int id_type,
int import_type,
int icon,
struct ImBuf *imb,
@@ -6157,9 +6191,9 @@ void UI_but_drag_set_asset(uiBut *but,
{
wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset");
- BLI_strncpy(asset_drag->name, name, sizeof(asset_drag->name));
+ BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name));
asset_drag->path = path;
- asset_drag->id_type = id_type;
+ asset_drag->id_type = ED_asset_handle_get_id_type(asset);
asset_drag->import_type = import_type;
but->dragtype = WM_DRAG_ASSET;
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 2a7611eabb1..d917534895d 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -494,7 +494,7 @@ static void ui_but_menu_add_path_operators(uiLayout *layout, PointerRNA *ptr, Pr
RNA_string_set(&props_ptr, "filepath", dir);
}
-bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
+bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *event)
{
/* ui_but_is_interactive() may let some buttons through that should not get a context menu - it
* doesn't make sense for them. */
@@ -952,7 +952,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
}
/* If the button represents an id, it can set the "id" context pointer. */
- if (U.experimental.use_asset_browser && ED_asset_can_make_single_from_context(C)) {
+ if (U.experimental.use_asset_browser && ED_asset_can_mark_single_from_context(C)) {
ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data;
/* Gray out items depending on if data-block is an asset. Preferably this could be done via
@@ -1226,6 +1226,20 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
}
}
+ /* UI List item context menu. Scripts can add items to it, by default there's nothing shown. */
+ ARegion *region = CTX_wm_region(C);
+ const bool is_inside_listbox = ui_list_find_mouse_over(region, event) != NULL;
+ const bool is_inside_listrow = is_inside_listbox ?
+ ui_list_row_find_mouse_over(region, event->x, event->y) !=
+ NULL :
+ false;
+ if (is_inside_listrow) {
+ MenuType *mt = WM_menutype_find("UI_MT_list_item_context_menu", true);
+ if (mt) {
+ UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
+ }
+ }
+
MenuType *mt = WM_menutype_find("WM_MT_button_context", true);
if (mt) {
UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 655fdda3069..65104885d98 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -106,14 +106,30 @@ void UI_draw_roundbox_4fv_ex(const rctf *rect,
.color_inner1[1] = inner1 ? inner1[1] : 0.0f,
.color_inner1[2] = inner1 ? inner1[2] : 0.0f,
.color_inner1[3] = inner1 ? inner1[3] : 0.0f,
- .color_inner2[0] = inner2 ? inner2[0] : inner1 ? inner1[0] : 0.0f,
- .color_inner2[1] = inner2 ? inner2[1] : inner1 ? inner1[1] : 0.0f,
- .color_inner2[2] = inner2 ? inner2[2] : inner1 ? inner1[2] : 0.0f,
- .color_inner2[3] = inner2 ? inner2[3] : inner1 ? inner1[3] : 0.0f,
- .color_outline[0] = outline ? outline[0] : inner1 ? inner1[0] : 0.0f,
- .color_outline[1] = outline ? outline[1] : inner1 ? inner1[1] : 0.0f,
- .color_outline[2] = outline ? outline[2] : inner1 ? inner1[2] : 0.0f,
- .color_outline[3] = outline ? outline[3] : inner1 ? inner1[3] : 0.0f,
+ .color_inner2[0] = inner2 ? inner2[0] :
+ inner1 ? inner1[0] :
+ 0.0f,
+ .color_inner2[1] = inner2 ? inner2[1] :
+ inner1 ? inner1[1] :
+ 0.0f,
+ .color_inner2[2] = inner2 ? inner2[2] :
+ inner1 ? inner1[2] :
+ 0.0f,
+ .color_inner2[3] = inner2 ? inner2[3] :
+ inner1 ? inner1[3] :
+ 0.0f,
+ .color_outline[0] = outline ? outline[0] :
+ inner1 ? inner1[0] :
+ 0.0f,
+ .color_outline[1] = outline ? outline[1] :
+ inner1 ? inner1[1] :
+ 0.0f,
+ .color_outline[2] = outline ? outline[2] :
+ inner1 ? inner1[2] :
+ 0.0f,
+ .color_outline[3] = outline ? outline[3] :
+ inner1 ? inner1[3] :
+ 0.0f,
.shade_dir = shade_dir,
.alpha_discard = 1.0f,
};
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 3136ca64e0f..bfc03a95949 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -767,23 +767,34 @@ static uiAfterFunc *ui_afterfunc_new(void)
* For executing operators after the button is pressed.
* (some non operator buttons need to trigger operators), see: T37795.
*
+ * \param context_but: A button from which to get the context from (`uiBut.context`) for the
+ * operator execution.
+ *
+ * \note Ownership over \a properties is moved here. The #uiAfterFunc owns it now.
* \note Can only call while handling buttons.
*/
-PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props)
+static void ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot,
+ PointerRNA **properties,
+ int opcontext,
+ const uiBut *context_but)
{
- PointerRNA *ptr = NULL;
uiAfterFunc *after = ui_afterfunc_new();
after->optype = ot;
after->opcontext = opcontext;
+ if (properties) {
+ after->opptr = *properties;
+ *properties = NULL;
+ }
- if (create_props) {
- ptr = MEM_callocN(sizeof(PointerRNA), __func__);
- WM_operator_properties_create_ptr(ptr, ot);
- after->opptr = ptr;
+ if (context_but && context_but->context) {
+ after->context = CTX_store_copy(context_but->context);
}
+}
- return ptr;
+void ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext)
+{
+ ui_handle_afterfunc_add_operator_ex(ot, NULL, opcontext, NULL);
}
static void popup_check(bContext *C, wmOperator *op)
@@ -1145,6 +1156,42 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
data->applied = true;
}
+/**
+ * \note Ownership of \a properties is moved here. The #uiAfterFunc owns it now.
+ *
+ * \param context_but: The button to use context from when calling or polling the operator.
+ *
+ * \returns true if the operator was executed, otherwise false.
+ */
+static bool ui_list_invoke_item_operator(bContext *C,
+ const uiBut *context_but,
+ wmOperatorType *ot,
+ PointerRNA **properties)
+{
+ if (!ui_but_context_poll_operator(C, ot, context_but)) {
+ return false;
+ }
+
+ /* Allow the context to be set from the hovered button, so the list item draw callback can set
+ * context for the operators. */
+ ui_handle_afterfunc_add_operator_ex(ot, properties, WM_OP_INVOKE_DEFAULT, context_but);
+ return true;
+}
+
+static void ui_apply_but_LISTROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+ uiBut *listbox = ui_list_find_from_row(data->region, but);
+ if (listbox) {
+ uiList *list = listbox->custom_data;
+ if (list && list->dyn_data->custom_activate_optype) {
+ ui_list_invoke_item_operator(
+ C, but, list->dyn_data->custom_activate_optype, &list->dyn_data->custom_activate_opptr);
+ }
+ }
+
+ ui_apply_but_ROW(C, block, but, data);
+}
+
static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
{
if (!data->str) {
@@ -1617,7 +1664,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const
*/
if (drag_info->is_xy_lock_init == false) {
/* first store the buttons original coords */
- uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true);
+ uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true, NULL, NULL);
if (but) {
if (but->flag & UI_BUT_DRAG_LOCK) {
@@ -1688,7 +1735,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void
wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
uiBut *but = ui_but_find_mouse_over_ex(
- region, drag_info->xy_init[0], drag_info->xy_init[1], true);
+ region, drag_info->xy_init[0], drag_info->xy_init[1], true, NULL, NULL);
if (but) {
ui_apply_but_undo(but);
@@ -2250,9 +2297,11 @@ static void ui_apply_but(
ui_apply_but_TOG(C, but, data);
break;
case UI_BTYPE_ROW:
- case UI_BTYPE_LISTROW:
ui_apply_but_ROW(C, block, but, data);
break;
+ case UI_BTYPE_LISTROW:
+ ui_apply_but_LISTROW(C, block, but, data);
+ break;
case UI_BTYPE_DATASETROW:
ui_apply_but_ROW(C, block, but, data);
break;
@@ -3017,7 +3066,7 @@ static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str),
/**
* \param x: Screen space cursor location - #wmEvent.x
*
- * \note ``but->block->aspect`` is used here, so drawing button style is getting scaled too.
+ * \note `but->block->aspect` is used here, so drawing button style is getting scaled too.
*/
static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, const float x)
{
@@ -3470,6 +3519,11 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
ui_but_update(but);
+ /* Popup blocks don't support moving after creation, so don't change the view for them. */
+ if (!data->searchbox) {
+ UI_but_ensure_in_view(C, data->region, but);
+ }
+
WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT);
#ifdef WITH_INPUT_IME
@@ -3875,7 +3929,7 @@ static void ui_do_but_textedit(
/* exception that's useful for number buttons, some keyboard
* numpads have a comma instead of a period */
- if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* could use data->min*/
+ if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */
if (event->type == EVT_PADPERIOD && ascii == ',') {
ascii = '.';
utf8_buf = NULL; /* force ascii fallback */
@@ -4284,7 +4338,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C,
uiButtonActivateType activate_type)
{
ARegion *region = CTX_wm_region(C);
- uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true);
+ uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true, NULL, NULL);
if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) {
/* exit listrow */
@@ -4778,6 +4832,15 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con
if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) {
ret = WM_UI_HANDLER_CONTINUE;
}
+ /* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event
+ * will be sent for the list to work with. */
+ const uiBut *listbox = ui_list_find_mouse_over(data->region, event);
+ if (listbox) {
+ const uiList *ui_list = listbox->custom_data;
+ if (ui_list && ui_list->dyn_data->custom_drag_optype) {
+ ret = WM_UI_HANDLER_CONTINUE;
+ }
+ }
button_activate_state(C, but, BUTTON_STATE_EXIT);
return ret;
}
@@ -7851,7 +7914,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) &&
(event->val == KM_PRESS)) {
/* RMB has two options now */
- if (ui_popup_context_menu_for_button(C, but)) {
+ if (ui_popup_context_menu_for_button(C, but, event)) {
return WM_UI_HANDLER_BREAK;
}
}
@@ -7931,6 +7994,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_IMAGE:
case UI_BTYPE_PROGRESS_BAR:
case UI_BTYPE_NODE_SOCKET:
+ case UI_BTYPE_PREVIEW_TILE:
retval = ui_do_but_EXIT(C, but, data, event);
break;
case UI_BTYPE_HISTOGRAM:
@@ -8825,6 +8889,26 @@ void UI_context_update_anim_flag(const bContext *C)
}
}
+/**
+ * In some cases we may want to update the view (#View2D) in-between layout definition and drawing.
+ * E.g. to make sure a button is visible while editing.
+ */
+void ui_but_update_view_for_active(const bContext *C, const uiBlock *block)
+{
+ uiBut *active_but = ui_block_active_but_get(block);
+ if (!active_but || !active_but->active || !active_but->changed || active_but->block != block) {
+ return;
+ }
+ /* If there is a search popup attached to the button, don't change the view. The popups don't
+ * support updating the position to the button position nicely. */
+ uiHandleButtonData *data = active_but->active;
+ if (data->searchbox) {
+ return;
+ }
+
+ UI_but_ensure_in_view(C, active_but->active->region, active_but);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -9289,6 +9373,149 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
return retval;
}
+/**
+ * Activate the underlying list-row button, so the row is highlighted.
+ * Early exits if \a activate_dragging is true, but the custom drag operator fails to execute.
+ * Gives the wanted behavior where the item is activated on a tweak event when the custom drag
+ * operator is executed.
+ */
+static int ui_list_activate_hovered_row(bContext *C,
+ ARegion *region,
+ const uiList *ui_list,
+ const wmEvent *event,
+ bool activate_dragging)
+{
+ const bool do_drag = activate_dragging && ui_list->dyn_data->custom_drag_optype;
+
+ if (do_drag) {
+ const uiBut *hovered_but = ui_but_find_mouse_over(region, event);
+ if (!ui_list_invoke_item_operator(C,
+ hovered_but,
+ ui_list->dyn_data->custom_drag_optype,
+ &ui_list->dyn_data->custom_drag_opptr)) {
+ return WM_UI_HANDLER_CONTINUE;
+ }
+ }
+
+ const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x;
+ uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy[0], mouse_xy[1]);
+ if (listrow) {
+ wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype;
+
+ /* Hacky: Ensure the custom activate operator is not called when the custom drag operator
+ * was. Only one should run! */
+ if (activate_dragging && do_drag) {
+ ((uiList *)ui_list)->dyn_data->custom_activate_optype = NULL;
+ }
+
+ /* Simulate click on listrow button itself (which may be overlapped by another button). Also
+ * calls the custom activate operator (ui_list->custom_activate_opname). */
+ UI_but_execute(C, region, listrow);
+
+ ((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype;
+ }
+
+ return WM_UI_HANDLER_BREAK;
+}
+
+static bool ui_list_is_hovering_draggable_but(bContext *C,
+ const uiList *list,
+ const ARegion *region,
+ const wmEvent *event)
+{
+ /* On a tweak event, uses the coordinates from where tweaking was started. */
+ const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x;
+ const uiBut *hovered_but = ui_but_find_mouse_over_ex(
+ region, mouse_xy[0], mouse_xy[1], false, NULL, NULL);
+
+ if (list->dyn_data->custom_drag_optype) {
+ if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) {
+ return true;
+ }
+ }
+
+ return (hovered_but && hovered_but->dragpoin);
+}
+
+static int ui_list_handle_click_drag(bContext *C,
+ const uiList *ui_list,
+ ARegion *region,
+ const wmEvent *event)
+{
+ if (!ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) {
+ return WM_HANDLER_CONTINUE;
+ }
+
+ int retval = WM_HANDLER_CONTINUE;
+
+ const bool is_draggable = ui_list_is_hovering_draggable_but(C, ui_list, region, event);
+ bool activate = false;
+ bool activate_dragging = false;
+
+ if (event->type == EVT_TWEAK_L) {
+ if (is_draggable) {
+ activate_dragging = true;
+ activate = true;
+ }
+ }
+ /* #KM_CLICK is only sent after an uncaught release event, so the foreground button gets all
+ * regular events (including mouse presses to start dragging) and this part only kicks in if it
+ * hasn't handled the release event. Note that if there's no overlaid button, the row selects
+ * on the press event already via regular #UI_BTYPE_LISTROW handling. */
+ else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) {
+ activate = true;
+ }
+
+ if (activate) {
+ retval = ui_list_activate_hovered_row(C, region, ui_list, event, activate_dragging);
+ }
+
+ return retval;
+}
+
+static void ui_list_activate_row_from_index(
+ bContext *C, ARegion *region, uiBut *listbox, uiList *ui_list, int index)
+{
+ uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox);
+ if (new_active_row) {
+ /* Preferred way to update the active item, also calls the custom activate operator
+ * (#uiList.custom_activate_opname). */
+ UI_but_execute(C, region, new_active_row);
+ }
+ else {
+ /* A bit ugly, set the active index in RNA directly. That's because a button that's
+ * scrolled away in the list box isn't created at all.
+ * The custom activate operator (#uiList.custom_activate_opname) is not called in this case
+ * (which may need the row button context). */
+ RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index);
+ RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop);
+ ui_apply_but_undo(listbox);
+ }
+
+ ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+}
+
+static int ui_list_get_increment(const uiList *ui_list, const int type, const int columns)
+{
+ int increment = 0;
+
+ /* Handle column offsets for grid layouts. */
+ if (ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) &&
+ ELEM(ui_list->layout_type, UILST_LAYOUT_GRID, UILST_LAYOUT_BIG_PREVIEW_GRID)) {
+ increment = (type == EVT_UPARROWKEY) ? -columns : columns;
+ }
+ else {
+ /* Left or right in grid layouts or any direction in single column layouts increments by 1. */
+ increment = ELEM(type, EVT_UPARROWKEY, EVT_LEFTARROWKEY, WHEELUPMOUSE) ? -1 : 1;
+ }
+
+ if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) {
+ increment *= -1;
+ }
+
+ return increment;
+}
+
static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox)
{
int retval = WM_UI_HANDLER_CONTINUE;
@@ -9322,22 +9549,19 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
}
}
- if (val == KM_PRESS) {
- if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) &&
+ if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) {
+ retval = ui_list_handle_click_drag(C, ui_list, region, event);
+ }
+ else if (val == KM_PRESS) {
+ if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY, EVT_LEFTARROWKEY, EVT_RIGHTARROWKEY) &&
!IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) ||
((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl &&
!IS_EVENT_MOD(event, shift, alt, oskey)))) {
const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop);
- int value, min, max, inc;
+ int value, min, max;
- /* activate up/down the list */
value = value_orig;
- if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) {
- inc = ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE) ? 1 : -1;
- }
- else {
- inc = ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE) ? -1 : 1;
- }
+ const int inc = ui_list_get_increment(ui_list, type, dyn_data->columns);
if (dyn_data->items_filter_neworder || dyn_data->items_filter_flags) {
/* If we have a display order different from
@@ -9384,12 +9608,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
CLAMP(value, min, max);
if (value != value_orig) {
- RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, value);
- RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop);
-
- ui_apply_but_undo(listbox);
-
- ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+ ui_list_activate_row_from_index(C, region, listbox, ui_list, value);
redraw = true;
}
retval = WM_UI_HANDLER_BREAK;
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 2a505abe257..0ffc5659191 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -66,6 +66,7 @@
#include "ED_datafiles.h"
#include "ED_keyframes_draw.h"
+#include "ED_keyframes_keylist.h"
#include "ED_render.h"
#include "UI_interface.h"
@@ -285,18 +286,15 @@ static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float
static void vicon_keytype_draw_wrapper(
int x, int y, int w, int h, float alpha, short key_type, short handle_type)
{
- /* init dummy theme state for Action Editor - where these colors are defined
- * (since we're doing this offscreen, free from any particular space_id)
- */
+ /* Initialize dummy theme state for Action Editor - where these colors are defined
+ * (since we're doing this off-screen, free from any particular space_id). */
struct bThemeState theme_state;
UI_Theme_Store(&theme_state);
UI_SetTheme(SPACE_ACTION, RGN_TYPE_WINDOW);
- /* the "x" and "y" given are the bottom-left coordinates of the icon,
- * while the draw_keyframe_shape() function needs the midpoint for
- * the keyframe
- */
+ /* The "x" and "y" given are the bottom-left coordinates of the icon,
+ * while the #draw_keyframe_shape() function needs the midpoint for the keyframe. */
const float xco = x + w / 2 + 0.5f;
const float yco = y + h / 2 + 0.5f;
@@ -1346,8 +1344,8 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi
case ICON_TYPE_PREVIEW: {
ID *id = (icon->id_type != 0) ? icon->obj : NULL;
PreviewImage *prv = id ? BKE_previewimg_id_ensure(id) : icon->obj;
- /* Using jobs for screen previews crashes due to offscreen rendering.
- * XXX would be nicer if PreviewImage could store if it supports jobs */
+ /* Using jobs for screen previews crashes due to off-screen rendering.
+ * XXX: would be nicer if #PreviewImage could store if it supports jobs. */
const bool use_jobs = !id || (GS(id->name) != ID_SCR);
if (prv) {
@@ -2143,7 +2141,7 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
static int ui_id_screen_get_icon(const bContext *C, ID *id)
{
BKE_icon_id_ensure(id);
- /* Don't use jobs here, offscreen rendering doesn't like this and crashes. */
+ /* Don't use jobs here, off-screen rendering doesn't like this and crashes. */
ui_id_icon_render(C, id, false);
return id->icon_id;
@@ -2201,7 +2199,7 @@ int UI_icon_from_library(const ID *id)
return ICON_NONE;
}
-int UI_icon_from_rnaptr(bContext *C, PointerRNA *ptr, int rnaicon, const bool big)
+int UI_icon_from_rnaptr(const bContext *C, PointerRNA *ptr, int rnaicon, const bool big)
{
ID *id = NULL;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 40a9c67c630..6b0b8e8df8f 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -597,11 +597,19 @@ typedef struct uiSafetyRct {
void ui_fontscale(short *points, float aspect);
+extern void ui_block_to_region_fl(const struct ARegion *region,
+ uiBlock *block,
+ float *r_x,
+ float *r_y);
extern void ui_block_to_window_fl(const struct ARegion *region,
uiBlock *block,
float *x,
float *y);
extern void ui_block_to_window(const struct ARegion *region, uiBlock *block, int *x, int *y);
+extern void ui_block_to_region_rctf(const struct ARegion *region,
+ uiBlock *block,
+ rctf *rct_dst,
+ const rctf *rct_src);
extern void ui_block_to_window_rctf(const struct ARegion *region,
uiBlock *block,
rctf *rct_dst,
@@ -620,6 +628,9 @@ extern void ui_window_to_region(const struct ARegion *region, int *x, int *y);
extern void ui_window_to_region_rcti(const struct ARegion *region,
rcti *rect_dst,
const rcti *rct_src);
+extern void ui_window_to_region_rctf(const struct ARegion *region,
+ rctf *rect_dst,
+ const rctf *rct_src);
extern void ui_region_to_window(const struct ARegion *region, int *x, int *y);
extern void ui_region_winrct_get_no_margin(const struct ARegion *region, struct rcti *r_rect);
@@ -931,9 +942,7 @@ const char *ui_textedit_undo(struct uiUndoStack_Text *undo_stack,
int *r_cursor_index);
/* interface_handlers.c */
-PointerRNA *ui_handle_afterfunc_add_operator(struct wmOperatorType *ot,
- int opcontext,
- bool create_props);
+extern void ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, int opcontext);
extern void ui_pan_to_scroll(const struct wmEvent *event, int *type, int *val);
extern void ui_but_activate_event(struct bContext *C, struct ARegion *region, uiBut *but);
extern void ui_but_activate_over(struct bContext *C, struct ARegion *region, uiBut *but);
@@ -946,6 +955,7 @@ extern void ui_but_execute_end(struct bContext *C,
uiBut *but,
void *active_back);
extern void ui_but_active_free(const struct bContext *C, uiBut *but);
+extern void ui_but_update_view_for_active(const struct bContext *C, const uiBlock *block);
extern int ui_but_menu_direction(uiBut *but);
extern void ui_but_text_password_hide(char password_str[128], uiBut *but, const bool restore);
extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction);
@@ -1045,13 +1055,23 @@ void ui_draw_menu_item(const struct uiFontStyle *fstyle,
int state,
uiMenuItemSeparatorType separator_type,
int *r_xmax);
-void ui_draw_preview_item(
- const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state);
+void ui_draw_preview_item(const struct uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ int state,
+ eFontStyle_Align text_align);
+void ui_draw_preview_item_stateless(const struct uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ const uchar text_col[4],
+ eFontStyle_Align text_align);
#define UI_TEXT_MARGIN_X 0.4f
#define UI_POPUP_MARGIN (UI_DPI_FAC * 12)
/* Margin at top of screen for popups. Note this value must be sufficient
- to draw a popover arrow to avoid cropping it. */
+ * to draw a popover arrow to avoid cropping it. */
#define UI_POPUP_MENU_TOP (int)(10 * UI_DPI_FAC)
#define UI_PIXEL_AA_JITTER 8
@@ -1128,19 +1148,32 @@ bool ui_but_contains_point_px_icon(const uiBut *but,
bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, int x, int y)
ATTR_WARN_UNUSED_RESULT;
-uiBut *ui_list_find_mouse_over(struct ARegion *region,
+uiBut *ui_list_find_mouse_over(const struct ARegion *region,
const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT;
-
+uiBut *ui_list_find_from_row(const struct ARegion *region,
+ const uiBut *row_but) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_list_row_find_mouse_over(const struct ARegion *region,
+ int x,
+ int y) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_list_row_find_from_index(const struct ARegion *region,
+ const int index,
+ uiBut *listbox) ATTR_WARN_UNUSED_RESULT;
+
+typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata);
uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region,
const int x,
const int y,
- const bool labeledit) ATTR_WARN_UNUSED_RESULT;
+ const bool labeledit,
+ const uiButFindPollFn find_poll,
+ const void *find_custom_data) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_find_mouse_over(const struct ARegion *region,
const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_find_rect_over(const struct ARegion *region,
const rcti *rect_px) ATTR_WARN_UNUSED_RESULT;
-uiBut *ui_list_find_mouse_over_ex(struct ARegion *region, int x, int y) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region,
+ int x,
+ int y) ATTR_WARN_UNUSED_RESULT;
bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
@@ -1152,6 +1185,7 @@ uiBut *ui_but_next(uiBut *but) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_first(uiBlock *block) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_last(uiBlock *block) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_block_active_but_get(const uiBlock *block);
bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT;
bool ui_block_is_popover(const uiBlock *block) ATTR_WARN_UNUSED_RESULT;
bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT;
@@ -1179,7 +1213,7 @@ struct ARegion *ui_screen_region_find_mouse_over(struct bScreen *screen,
const struct wmEvent *event);
/* interface_context_menu.c */
-bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but);
+bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but, const struct wmEvent *event);
void ui_popup_context_menu_for_panel(struct bContext *C,
struct ARegion *region,
struct Panel *panel);
@@ -1207,6 +1241,9 @@ void UI_OT_eyedropper_driver(struct wmOperatorType *ot);
/* interface_eyedropper_gpencil_color.c */
void UI_OT_eyedropper_gpencil_color(struct wmOperatorType *ot);
+/* interface_template_asset_view.cc */
+struct uiListType *UI_UL_asset_view(void);
+
/**
* For use with #ui_rna_collection_search_update_fn.
*/
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 8b9539f1d33..03c67e9b046 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2911,6 +2911,12 @@ static uiBut *ui_item_menu(uiLayout *layout,
void uiItemM_ptr(uiLayout *layout, MenuType *mt, const char *name, int icon)
{
+ uiBlock *block = layout->root->block;
+ bContext *C = block->evil_C;
+ if (WM_menutype_poll(C, mt) == false) {
+ return;
+ }
+
if (!name) {
name = CTX_IFACE_(mt->translation_context, mt->label);
}
@@ -2949,6 +2955,9 @@ void uiItemMContents(uiLayout *layout, const char *menuname)
uiBlock *block = layout->root->block;
bContext *C = block->evil_C;
+ if (WM_menutype_poll(C, mt) == false) {
+ return;
+ }
UI_menutype_draw(C, mt, layout);
}
@@ -5956,8 +5965,8 @@ uiLayout *uiItemsAlertBox(uiBlock *block, const int size, const eAlertIcon icon)
const int text_points_max = MAX2(style->widget.points, style->widgetlabel.points);
const int dialog_width = icon_size + (text_points_max * size * U.dpi_fac);
/* By default, the space between icon and text/buttons will be equal to the 'columnspace',
- this extra padding will add some space by increasing the left column width,
- making the icon placement more symmetrical, between the block edge and the text. */
+ * this extra padding will add some space by increasing the left column width,
+ * making the icon placement more symmetrical, between the block edge and the text. */
const float icon_padding = 5.0f * U.dpi_fac;
/* Calculate the factor of the fixed icon column depending on the block width. */
const float split_factor = ((float)icon_size + icon_padding) /
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 4afe232e33e..3ab49b8773b 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -75,6 +75,32 @@
#include "ED_text.h"
/* -------------------------------------------------------------------- */
+/** \name Immediate redraw helper
+ *
+ * Generally handlers shouldn't do any redrawing, that includes the layout/button definitions. That
+ * violates the Model-View-Controller pattern.
+ *
+ * But there are some operators which really need to re-run the layout definitions for various
+ * reasons. For example, "Edit Source" does it to find out which exact Python code added a button.
+ * Other operators may need to access buttons that aren't currently visible. In Blender's UI code
+ * design that typically means just not adding the button in the first place, for a particular
+ * redraw. So the operator needs to change context and re-create the layout, so the button becomes
+ * available to act on.
+ *
+ * \{ */
+
+static void ui_region_redraw_immediately(bContext *C, ARegion *region)
+{
+ ED_region_do_layout(C, region);
+ WM_draw_region_viewport_bind(region);
+ ED_region_do_draw(C, region);
+ WM_draw_region_viewport_unbind(region);
+ region->do_draw = false;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Copy Data Path Operator
* \{ */
@@ -649,7 +675,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
PropertyRNA *src_prop;
RNA_id_pointer_create(id->override_library->reference, &id_refptr);
if (!RNA_path_resolve_property(&id_refptr, oprop->rna_path, &src, &src_prop)) {
- BLI_assert(0 && "Failed to create matching source (linked data) RNA pointer");
+ BLI_assert_msg(0, "Failed to create matching source (linked data) RNA pointer");
}
}
@@ -1379,11 +1405,7 @@ static int editsource_exec(bContext *C, wmOperator *op)
ui_editsource_active_but_set(but);
/* redraw and get active button python info */
- ED_region_do_layout(C, region);
- WM_draw_region_viewport_bind(region);
- ED_region_do_draw(C, region);
- WM_draw_region_viewport_unbind(region);
- region->do_draw = false;
+ ui_region_redraw_immediately(C, region);
for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash);
BLI_ghashIterator_done(&ghi) == false;
@@ -1836,6 +1858,64 @@ static void UI_OT_drop_color(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name UI List Search Operator
+ * \{ */
+
+static bool ui_list_focused_poll(bContext *C)
+{
+ const ARegion *region = CTX_wm_region(C);
+ const wmWindow *win = CTX_wm_window(C);
+ const uiList *list = UI_list_find_mouse_over(region, win->eventstate);
+
+ return list != NULL;
+}
+
+/**
+ * Ensure the filter options are set to be visible in the UI list.
+ * \return if the visibility changed, requiring a redraw.
+ */
+static bool ui_list_unhide_filter_options(uiList *list)
+{
+ if (list->filter_flag & UILST_FLT_SHOW) {
+ /* Nothing to be done. */
+ return false;
+ }
+
+ list->filter_flag |= UILST_FLT_SHOW;
+ return true;
+}
+
+static int ui_list_start_filter_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+ uiList *list = UI_list_find_mouse_over(region, event);
+ /* Poll should check. */
+ BLI_assert(list != NULL);
+
+ if (ui_list_unhide_filter_options(list)) {
+ ui_region_redraw_immediately(C, region);
+ }
+
+ if (!UI_textbutton_activate_rna(C, region, list, "filter_name")) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void UI_OT_list_start_filter(wmOperatorType *ot)
+{
+ ot->name = "List Filter";
+ ot->idname = "UI_OT_list_start_filter";
+ ot->description = "Start entering filter text for the list in focus";
+
+ ot->invoke = ui_list_start_filter_invoke;
+ ot->poll = ui_list_focused_poll;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Operator & Keymap Registration
* \{ */
@@ -1860,6 +1940,8 @@ void ED_operatortypes_ui(void)
WM_operatortype_append(UI_OT_button_execute);
WM_operatortype_append(UI_OT_button_string_clear);
+ WM_operatortype_append(UI_OT_list_start_filter);
+
/* external */
WM_operatortype_append(UI_OT_eyedropper_color);
WM_operatortype_append(UI_OT_eyedropper_colorramp);
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 38dc91fb57f..97d01ac3763 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -2653,7 +2653,7 @@ static void panel_activate_state(const bContext *C, Panel *panel, const uiHandle
/* Initiate edge panning during drags for scrolling beyond the initial region view. */
wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
- ui_handle_afterfunc_add_operator(ot, WM_OP_INVOKE_DEFAULT, true);
+ ui_handle_afterfunc_add_operator(ot, WM_OP_INVOKE_DEFAULT);
}
else if (state == PANEL_STATE_ANIMATION) {
panel_set_flag_recursive(panel, PNL_SELECT, false);
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 025c242d0fc..8534c95b6fd 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -266,11 +266,29 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv
return BLI_rcti_isect_pt(&rect, x, y);
}
+static uiBut *ui_but_find(const ARegion *region,
+ const uiButFindPollFn find_poll,
+ const void *find_custom_data)
+{
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
+ if (find_poll && find_poll(but, find_custom_data) == false) {
+ continue;
+ }
+ return but;
+ }
+ }
+
+ return NULL;
+}
+
/* x and y are only used in case event is NULL... */
uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
const int x,
const int y,
- const bool labeledit)
+ const bool labeledit,
+ const uiButFindPollFn find_poll,
+ const void *find_custom_data)
{
uiBut *butover = NULL;
@@ -282,6 +300,9 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
ui_window_to_block_fl(region, block, &mx, &my);
LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
+ if (find_poll && find_poll(but, find_custom_data) == false) {
+ continue;
+ }
if (ui_but_is_interactive(but, labeledit)) {
if (but->pie_dir != UI_RADIAL_NONE) {
if (ui_but_isect_pie_seg(block, but)) {
@@ -310,7 +331,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event)
{
- return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0);
+ return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0, NULL, NULL);
}
uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
@@ -351,7 +372,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
return butover;
}
-uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y)
+uiBut *ui_list_find_mouse_over_ex(const ARegion *region, int x, int y)
{
if (!ui_region_contains_point_px(region, x, y)) {
return NULL;
@@ -369,11 +390,77 @@ uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y)
return NULL;
}
-uiBut *ui_list_find_mouse_over(ARegion *region, const wmEvent *event)
+uiBut *ui_list_find_mouse_over(const ARegion *region, const wmEvent *event)
{
+ if (event == NULL) {
+ /* If there is no info about the mouse, just act as if there is nothing underneath it. */
+ return NULL;
+ }
return ui_list_find_mouse_over_ex(region, event->x, event->y);
}
+uiList *UI_list_find_mouse_over(const ARegion *region, const wmEvent *event)
+{
+ uiBut *list_but = ui_list_find_mouse_over(region, event);
+ if (!list_but) {
+ return NULL;
+ }
+
+ return list_but->custom_data;
+}
+
+static bool ui_list_contains_row(const uiBut *listbox_but, const uiBut *listrow_but)
+{
+ BLI_assert(listbox_but->type == UI_BTYPE_LISTBOX);
+ BLI_assert(listrow_but->type == UI_BTYPE_LISTROW);
+ /* The list box and its rows have the same RNA data (active data pointer/prop). */
+ return ui_but_rna_equals(listbox_but, listrow_but);
+}
+
+static bool ui_but_is_listbox_with_row(const uiBut *but, const void *customdata)
+{
+ const uiBut *row_but = customdata;
+ return (but->type == UI_BTYPE_LISTBOX) && ui_list_contains_row(but, row_but);
+}
+
+uiBut *ui_list_find_from_row(const ARegion *region, const uiBut *row_but)
+{
+ return ui_but_find(region, ui_but_is_listbox_with_row, row_but);
+}
+
+static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata))
+{
+ return but->type == UI_BTYPE_LISTROW;
+}
+
+uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int x, const int y)
+{
+ return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_listrow, NULL);
+}
+
+struct ListRowFindIndexData {
+ int index;
+ uiBut *listbox;
+};
+
+static bool ui_but_is_listrow_at_index(const uiBut *but, const void *customdata)
+{
+ const struct ListRowFindIndexData *find_data = customdata;
+
+ return ui_but_is_listrow(but, NULL) && ui_list_contains_row(find_data->listbox, but) &&
+ (but->hardmax == find_data->index);
+}
+
+uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut *listbox)
+{
+ BLI_assert(listbox->type == UI_BTYPE_LISTBOX);
+ struct ListRowFindIndexData data = {
+ .index = index,
+ .listbox = listbox,
+ };
+ return ui_but_find(region, ui_but_is_listrow_at_index, &data);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -485,6 +572,17 @@ size_t ui_but_tip_len_only_first_line(const uiBut *but)
/** \name Block (#uiBlock) State
* \{ */
+uiBut *ui_block_active_but_get(const uiBlock *block)
+{
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
+ if (but->active) {
+ return but;
+ }
+ }
+
+ return NULL;
+}
+
bool ui_block_is_menu(const uiBlock *block)
{
return (((block->flag & UI_BLOCK_LOOP) != 0) &&
@@ -588,10 +686,9 @@ uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, b
uiBut *ui_region_find_active_but(ARegion *region)
{
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
- LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
- if (but->active) {
- return but;
- }
+ uiBut *but = ui_block_active_but_get(block);
+ if (but) {
+ return but;
}
}
diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c
index b8c4d8ddb09..f8f19c2e43d 100644
--- a/source/blender/editors/interface/interface_region_popover.c
+++ b/source/blender/editors/interface/interface_region_popover.c
@@ -188,7 +188,7 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v
}
}
- /* Estimated a maximum size so we don't go offscreen for low height
+ /* Estimated a maximum size so we don't go off-screen for low height
* areas near the bottom of the window on refreshes. */
handle->max_size_y = UI_UNIT_Y * 16.0f;
}
diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c
index c35dbc5d7a6..c863b1f8bdf 100644
--- a/source/blender/editors/interface/interface_region_search.c
+++ b/source/blender/editors/interface/interface_region_search.c
@@ -599,8 +599,12 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
ui_searchbox_butrect(&rect, data, a);
/* widget itself */
- ui_draw_preview_item(
- &data->fstyle, &rect, data->items.names[a], data->items.icons[a], state);
+ ui_draw_preview_item(&data->fstyle,
+ &rect,
+ data->items.names[a],
+ data->items.icons[a],
+ state,
+ UI_STYLE_TEXT_LEFT);
}
/* indicate more */
@@ -684,13 +688,13 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
if (data->items.more) {
ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
GPU_blend(GPU_BLEND_ALPHA);
- UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
+ UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
GPU_blend(GPU_BLEND_NONE);
}
if (data->items.offset) {
ui_searchbox_butrect(&rect, data, 0);
GPU_blend(GPU_BLEND_ALPHA);
- UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP);
+ UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymax - 7, ICON_TRIA_UP);
GPU_blend(GPU_BLEND_NONE);
}
}
@@ -986,13 +990,13 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe
if (data->items.more) {
ui_searchbox_butrect(&rect, data, data->items.maxitem - 1);
GPU_blend(GPU_BLEND_ALPHA);
- UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
+ UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymin - 9, ICON_TRIA_DOWN);
GPU_blend(GPU_BLEND_NONE);
}
if (data->items.offset) {
ui_searchbox_butrect(&rect, data, 0);
GPU_blend(GPU_BLEND_ALPHA);
- UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP);
+ UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymax - 7, ICON_TRIA_UP);
GPU_blend(GPU_BLEND_NONE);
}
}
diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc
new file mode 100644
index 00000000000..b4ba6a7feab
--- /dev/null
+++ b/source/blender/editors/interface/interface_template_asset_view.cc
@@ -0,0 +1,277 @@
+/*
+ * 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 edinterface
+ */
+
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_screen.h"
+
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_string_ref.hh"
+
+#include "BLO_readfile.h"
+
+#include "ED_asset.h"
+#include "ED_screen.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "interface_intern.h"
+
+struct AssetViewListData {
+ AssetLibraryReference asset_library;
+ bScreen *screen;
+};
+
+static void asset_view_item_but_drag_set(uiBut *but,
+ AssetViewListData *list_data,
+ AssetHandle *asset_handle)
+{
+ ID *id = ED_asset_handle_get_local_id(asset_handle);
+ if (id != nullptr) {
+ UI_but_drag_set_id(but, id);
+ return;
+ }
+
+ char blend_path[FILE_MAX_LIBEXTRA];
+ /* Context can be null here, it's only needed for a File Browser specific hack that should go
+ * away before too long. */
+ ED_asset_handle_get_full_library_path(
+ nullptr, &list_data->asset_library, asset_handle, blend_path);
+
+ if (blend_path[0]) {
+ ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle);
+ UI_but_drag_set_asset(but,
+ asset_handle,
+ BLI_strdup(blend_path),
+ FILE_ASSET_IMPORT_APPEND,
+ ED_asset_handle_get_preview_icon_id(asset_handle),
+ imbuf,
+ 1.0f);
+ }
+}
+
+static void asset_view_draw_item(uiList *ui_list,
+ bContext *UNUSED(C),
+ uiLayout *layout,
+ PointerRNA *UNUSED(dataptr),
+ PointerRNA *itemptr,
+ int UNUSED(icon),
+ PointerRNA *UNUSED(active_dataptr),
+ const char *UNUSED(active_propname),
+ int UNUSED(index),
+ int UNUSED(flt_flag))
+{
+ AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata;
+
+ BLI_assert(RNA_struct_is_a(itemptr->type, &RNA_AssetHandle));
+ AssetHandle *asset_handle = (AssetHandle *)itemptr->data;
+
+ uiLayoutSetContextPointer(layout, "asset_handle", itemptr);
+
+ uiBlock *block = uiLayoutGetBlock(layout);
+ /* TODO ED_fileselect_init_layout(). Share somehow? */
+ const float size_x = (96.0f / 20.0f) * UI_UNIT_X;
+ const float size_y = (96.0f / 20.0f) * UI_UNIT_Y;
+ uiBut *but = uiDefIconTextBut(block,
+ UI_BTYPE_PREVIEW_TILE,
+ 0,
+ ED_asset_handle_get_preview_icon_id(asset_handle),
+ ED_asset_handle_get_name(asset_handle),
+ 0,
+ 0,
+ size_x,
+ size_y,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ ui_def_but_icon(but,
+ ED_asset_handle_get_preview_icon_id(asset_handle),
+ /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */
+ UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
+ if (!ui_list->dyn_data->custom_drag_optype) {
+ asset_view_item_but_drag_set(but, list_data, asset_handle);
+ }
+}
+
+static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params)
+{
+ AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata;
+ const wmNotifier *notifier = params->notifier;
+
+ switch (notifier->category) {
+ case NC_ID: {
+ if (ELEM(notifier->action, NA_RENAME)) {
+ ED_assetlist_storage_tag_main_data_dirty();
+ }
+ break;
+ }
+ }
+
+ if (ED_assetlist_listen(&list_data->asset_library, params->notifier)) {
+ ED_region_tag_redraw(params->region);
+ }
+}
+
+uiListType *UI_UL_asset_view()
+{
+ uiListType *list_type = (uiListType *)MEM_callocN(sizeof(*list_type), __func__);
+
+ BLI_strncpy(list_type->idname, "UI_UL_asset_view", sizeof(list_type->idname));
+ list_type->draw_item = asset_view_draw_item;
+ list_type->listener = asset_view_listener;
+
+ return list_type;
+}
+
+static void asset_view_template_refresh_asset_collection(
+ const AssetLibraryReference &asset_library,
+ const AssetFilterSettings &filter_settings,
+ PointerRNA &assets_dataptr,
+ const char *assets_propname)
+{
+ PropertyRNA *assets_prop = RNA_struct_find_property(&assets_dataptr, assets_propname);
+ if (!assets_prop) {
+ RNA_warning("Asset collection not found");
+ return;
+ }
+ if (RNA_property_type(assets_prop) != PROP_COLLECTION) {
+ RNA_warning("Expected a collection property");
+ return;
+ }
+ if (!RNA_struct_is_a(RNA_property_pointer_type(&assets_dataptr, assets_prop),
+ &RNA_AssetHandle)) {
+ RNA_warning("Expected a collection property for AssetHandle items");
+ return;
+ }
+
+ RNA_property_collection_clear(&assets_dataptr, assets_prop);
+
+ ED_assetlist_iterate(&asset_library, [&](AssetHandle asset) {
+ if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) {
+ /* Don't do anything else, but return true to continue iterating. */
+ return true;
+ }
+
+ PointerRNA itemptr, fileptr;
+ RNA_property_collection_add(&assets_dataptr, assets_prop, &itemptr);
+
+ RNA_pointer_create(
+ nullptr, &RNA_FileSelectEntry, const_cast<FileDirEntry *>(asset.file_data), &fileptr);
+ RNA_pointer_set(&itemptr, "file_data", fileptr);
+
+ return true;
+ });
+}
+
+void uiTemplateAssetView(uiLayout *layout,
+ bContext *C,
+ const char *list_id,
+ PointerRNA *asset_library_dataptr,
+ const char *asset_library_propname,
+ PointerRNA *assets_dataptr,
+ const char *assets_propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ const AssetFilterSettings *filter_settings,
+ const char *activate_opname,
+ PointerRNA *r_activate_op_properties,
+ const char *drag_opname,
+ PointerRNA *r_drag_op_properties)
+{
+ if (!list_id || !list_id[0]) {
+ RNA_warning("Asset view needs a valid identifier");
+ return;
+ }
+
+ uiLayout *col = uiLayoutColumn(layout, false);
+
+ PropertyRNA *asset_library_prop = RNA_struct_find_property(asset_library_dataptr,
+ asset_library_propname);
+ AssetLibraryReference asset_library = ED_asset_library_reference_from_enum_value(
+ RNA_property_enum_get(asset_library_dataptr, asset_library_prop));
+
+ uiLayout *row = uiLayoutRow(col, true);
+ uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0);
+ if (asset_library.type != ASSET_LIBRARY_LOCAL) {
+ uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh");
+ }
+
+ ED_assetlist_storage_fetch(&asset_library, C);
+ ED_assetlist_ensure_previews_job(&asset_library, C);
+ const int tot_items = ED_assetlist_size(&asset_library);
+
+ asset_view_template_refresh_asset_collection(
+ asset_library, *filter_settings, *assets_dataptr, assets_propname);
+
+ AssetViewListData *list_data = (AssetViewListData *)MEM_mallocN(sizeof(*list_data),
+ "AssetViewListData");
+ list_data->asset_library = asset_library;
+ list_data->screen = CTX_wm_screen(C);
+
+ /* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading
+ * (of previews) of the items? */
+ uiList *list = uiTemplateList_ex(col,
+ C,
+ "UI_UL_asset_view",
+ list_id,
+ assets_dataptr,
+ assets_propname,
+ active_dataptr,
+ active_propname,
+ nullptr,
+ tot_items,
+ 0,
+ UILST_LAYOUT_BIG_PREVIEW_GRID,
+ 0,
+ UI_TEMPLATE_LIST_NO_GRIP,
+ list_data);
+ if (!list) {
+ /* List creation failed. */
+ MEM_freeN(list_data);
+ return;
+ }
+
+ if (activate_opname) {
+ PointerRNA *ptr = UI_list_custom_activate_operator_set(
+ list, activate_opname, r_activate_op_properties != nullptr);
+ if (r_activate_op_properties && ptr) {
+ *r_activate_op_properties = *ptr;
+ }
+ }
+ if (drag_opname) {
+ PointerRNA *ptr = UI_list_custom_drag_operator_set(
+ list, drag_opname, r_drag_op_properties != nullptr);
+ if (r_drag_op_properties && ptr) {
+ *r_drag_op_properties = *ptr;
+ }
+ }
+}
diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc
new file mode 100644
index 00000000000..0ab45ea0f81
--- /dev/null
+++ b/source/blender/editors/interface/interface_template_list.cc
@@ -0,0 +1,1314 @@
+/*
+ * 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 edinterface
+ */
+
+#include <cstdlib>
+#include <cstring>
+
+#include "BLI_fnmatch.h"
+#include "BLI_listbase.h"
+#include "BLI_math_base.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_screen.h"
+
+#include "BLT_translation.h"
+
+#include "ED_screen.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_view2d.h"
+
+#include "WM_api.h"
+
+#include "interface_intern.h"
+
+/**
+ * The validated data that was passed to #uiTemplateList (typically through Python).
+ * Populated through #ui_template_list_data_retrieve().
+ */
+struct TemplateListInputData {
+ PointerRNA dataptr;
+ PropertyRNA *prop;
+ PointerRNA active_dataptr;
+ PropertyRNA *activeprop;
+ const char *item_dyntip_propname;
+
+ /* Index as stored in the input property. I.e. the index before sorting. */
+ int active_item_idx;
+};
+
+/**
+ * Internal wrapper for a single item in the list (well, actually stored as a vector).
+ */
+struct _uilist_item {
+ PointerRNA item;
+ int org_idx;
+ int flt_flag;
+};
+
+/**
+ * Container for the item vector and additional info.
+ */
+struct TemplateListItems {
+ _uilist_item *item_vec;
+ /* Index of the active item following visual order. I.e. unlike
+ * TemplateListInputData.active_item_idx, this is the index after sorting. */
+ int active_item_idx;
+ int tot_items;
+};
+
+struct TemplateListLayoutDrawData {
+ uiListDrawItemFunc draw_item;
+ uiListDrawFilterFunc draw_filter;
+
+ int rows;
+ int maxrows;
+ int columns;
+};
+
+struct TemplateListVisualInfo {
+ int visual_items; /* Visual number of items (i.e. number of items we have room to display). */
+ int start_idx; /* Index of first item to display. */
+ int end_idx; /* Index of last item to display + 1. */
+};
+
+static void uilist_draw_item_default(struct uiList *ui_list,
+ struct bContext *UNUSED(C),
+ struct uiLayout *layout,
+ struct PointerRNA *UNUSED(dataptr),
+ struct PointerRNA *itemptr,
+ int icon,
+ struct PointerRNA *UNUSED(active_dataptr),
+ const char *UNUSED(active_propname),
+ int UNUSED(index),
+ int UNUSED(flt_flag))
+{
+ PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type);
+
+ /* Simplest one! */
+ switch (ui_list->layout_type) {
+ case UILST_LAYOUT_GRID:
+ uiItemL(layout, "", icon);
+ break;
+ case UILST_LAYOUT_DEFAULT:
+ case UILST_LAYOUT_COMPACT:
+ default:
+ if (nameprop) {
+ uiItemFullR(layout, itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon);
+ }
+ else {
+ uiItemL(layout, "", icon);
+ }
+ break;
+ }
+}
+
+static void uilist_draw_filter_default(struct uiList *ui_list,
+ struct bContext *UNUSED(C),
+ struct uiLayout *layout)
+{
+ PointerRNA listptr;
+ RNA_pointer_create(nullptr, &RNA_UIList, ui_list, &listptr);
+
+ uiLayout *row = uiLayoutRow(layout, false);
+
+ uiLayout *subrow = uiLayoutRow(row, true);
+ uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE);
+ uiItemR(subrow,
+ &listptr,
+ "use_filter_invert",
+ UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
+ "",
+ ICON_ARROW_LEFTRIGHT);
+
+ if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) {
+ subrow = uiLayoutRow(row, true);
+ uiItemR(subrow,
+ &listptr,
+ "use_filter_sort_alpha",
+ UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
+ "",
+ ICON_NONE);
+ uiItemR(subrow,
+ &listptr,
+ "use_filter_sort_reverse",
+ UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
+ "",
+ (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC : ICON_SORT_ASC);
+ }
+}
+
+struct StringCmp {
+ char name[MAX_IDPROP_NAME];
+ int org_idx;
+};
+
+static int cmpstringp(const void *p1, const void *p2)
+{
+ /* Case-insensitive comparison. */
+ return BLI_strcasecmp(((StringCmp *)p1)->name, ((StringCmp *)p2)->name);
+}
+
+static void uilist_filter_items_default(struct uiList *ui_list,
+ struct bContext *UNUSED(C),
+ struct PointerRNA *dataptr,
+ const char *propname)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ PropertyRNA *prop = RNA_struct_find_property(dataptr, propname);
+
+ const char *filter_raw = ui_list->filter_byname;
+ char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = nullptr;
+ const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0;
+ const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) ==
+ UILST_FLT_SORT_ALPHA;
+ const int len = RNA_property_collection_length(dataptr, prop);
+
+ dyn_data->items_shown = dyn_data->items_len = len;
+
+ if (len && (order_by_name || filter_raw[0])) {
+ StringCmp *names = nullptr;
+ int order_idx = 0, i = 0;
+
+ if (order_by_name) {
+ names = static_cast<StringCmp *>(MEM_callocN(sizeof(StringCmp) * len, "StringCmp"));
+ }
+ if (filter_raw[0]) {
+ const size_t slen = strlen(filter_raw);
+
+ dyn_data->items_filter_flags = static_cast<int *>(
+ MEM_callocN(sizeof(int) * len, "items_filter_flags"));
+ dyn_data->items_shown = 0;
+
+ /* Implicitly add heading/trailing wildcards if needed. */
+ if (slen + 3 <= sizeof(filter_buff)) {
+ filter = filter_buff;
+ }
+ else {
+ filter = filter_dyn = static_cast<char *>(
+ MEM_mallocN((slen + 3) * sizeof(char), "filter_dyn"));
+ }
+ BLI_strncpy_ensure_pad(filter, filter_raw, '*', slen + 3);
+ }
+
+ RNA_PROP_BEGIN (dataptr, itemptr, prop) {
+ bool do_order = false;
+
+ char *namebuf = RNA_struct_name_get_alloc(&itemptr, nullptr, 0, nullptr);
+ const char *name = namebuf ? namebuf : "";
+
+ if (filter[0]) {
+ /* Case-insensitive! */
+ if (fnmatch(filter, name, FNM_CASEFOLD) == 0) {
+ dyn_data->items_filter_flags[i] = UILST_FLT_ITEM;
+ if (!filter_exclude) {
+ dyn_data->items_shown++;
+ do_order = order_by_name;
+ }
+ // printf("%s: '%s' matches '%s'\n", __func__, name, filter);
+ }
+ else if (filter_exclude) {
+ dyn_data->items_shown++;
+ do_order = order_by_name;
+ }
+ }
+ else {
+ do_order = order_by_name;
+ }
+
+ if (do_order) {
+ names[order_idx].org_idx = order_idx;
+ BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME);
+ }
+
+ /* free name */
+ if (namebuf) {
+ MEM_freeN(namebuf);
+ }
+ i++;
+ }
+ RNA_PROP_END;
+
+ if (order_by_name) {
+ int new_idx;
+ /* NOTE: order_idx equals either to ui_list->items_len if no filtering done,
+ * or to ui_list->items_shown if filter is enabled,
+ * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded.
+ * This way, we only sort items we actually intend to draw!
+ */
+ qsort(names, order_idx, sizeof(StringCmp), cmpstringp);
+
+ dyn_data->items_filter_neworder = static_cast<int *>(
+ MEM_mallocN(sizeof(int) * order_idx, "items_filter_neworder"));
+ for (new_idx = 0; new_idx < order_idx; new_idx++) {
+ dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx;
+ }
+ }
+
+ if (filter_dyn) {
+ MEM_freeN(filter_dyn);
+ }
+ if (names) {
+ MEM_freeN(names);
+ }
+ }
+}
+
+static void uilist_free_dyn_data(uiList *ui_list)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ if (!dyn_data) {
+ return;
+ }
+
+ if (dyn_data->custom_activate_opptr) {
+ WM_operator_properties_free(dyn_data->custom_activate_opptr);
+ MEM_freeN(dyn_data->custom_activate_opptr);
+ }
+ if (dyn_data->custom_drag_opptr) {
+ WM_operator_properties_free(dyn_data->custom_drag_opptr);
+ MEM_freeN(dyn_data->custom_drag_opptr);
+ }
+
+ MEM_SAFE_FREE(dyn_data->items_filter_flags);
+ MEM_SAFE_FREE(dyn_data->items_filter_neworder);
+ MEM_SAFE_FREE(dyn_data->customdata);
+}
+
+/**
+ * Validate input parameters and initialize \a r_data from that. Plus find the list-type and return
+ * it in \a r_list_type.
+ *
+ * \return false if the input data isn't valid. Will also raise an RNA warning in that case.
+ */
+static bool ui_template_list_data_retrieve(const char *listtype_name,
+ const char *list_id,
+ PointerRNA *dataptr,
+ const char *propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ TemplateListInputData *r_input_data,
+ uiListType **r_list_type)
+{
+ memset(r_input_data, 0, sizeof(*r_input_data));
+
+ /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */
+ if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) {
+ RNA_warning("template_list using default '%s' UIList class must provide a custom list_id",
+ UI_UL_DEFAULT_CLASS_NAME);
+ return false;
+ }
+
+ if (!active_dataptr->data) {
+ RNA_warning("No active data");
+ return false;
+ }
+
+ r_input_data->dataptr = *dataptr;
+ if (dataptr->data) {
+ r_input_data->prop = RNA_struct_find_property(dataptr, propname);
+ if (!r_input_data->prop) {
+ RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname);
+ return false;
+ }
+ }
+
+ r_input_data->active_dataptr = *active_dataptr;
+ r_input_data->activeprop = RNA_struct_find_property(active_dataptr, active_propname);
+ if (!r_input_data->activeprop) {
+ RNA_warning(
+ "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname);
+ return false;
+ }
+
+ if (r_input_data->prop) {
+ const PropertyType type = RNA_property_type(r_input_data->prop);
+ if (type != PROP_COLLECTION) {
+ RNA_warning("Expected a collection data property");
+ return false;
+ }
+ }
+
+ const PropertyType activetype = RNA_property_type(r_input_data->activeprop);
+ if (activetype != PROP_INT) {
+ RNA_warning("Expected an integer active data property");
+ return false;
+ }
+
+ /* Find the uiList type. */
+ if (!(*r_list_type = WM_uilisttype_find(listtype_name, false))) {
+ RNA_warning("List type %s not found", listtype_name);
+ return false;
+ }
+
+ r_input_data->active_item_idx = RNA_property_int_get(&r_input_data->active_dataptr,
+ r_input_data->activeprop);
+ r_input_data->item_dyntip_propname = item_dyntip_propname;
+
+ return true;
+}
+
+static void ui_template_list_collect_items(PointerRNA *list_ptr,
+ PropertyRNA *list_prop,
+ uiListDyn *dyn_data,
+ int filter_exclude,
+ bool order_reverse,
+ int activei,
+ TemplateListItems *r_items)
+{
+ int i = 0;
+ int reorder_i = 0;
+ bool activei_mapping_pending = true;
+
+ RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) {
+ if (!dyn_data->items_filter_flags ||
+ ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) {
+ int new_order_idx;
+ if (dyn_data->items_filter_neworder) {
+ new_order_idx = dyn_data->items_filter_neworder[reorder_i++];
+ new_order_idx = order_reverse ? dyn_data->items_shown - new_order_idx - 1 : new_order_idx;
+ }
+ else {
+ new_order_idx = order_reverse ? dyn_data->items_shown - ++reorder_i : reorder_i++;
+ }
+ // printf("%s: ii: %d\n", __func__, ii);
+ r_items->item_vec[new_order_idx].item = itemptr;
+ r_items->item_vec[new_order_idx].org_idx = i;
+ r_items->item_vec[new_order_idx].flt_flag = dyn_data->items_filter_flags ?
+ dyn_data->items_filter_flags[i] :
+ 0;
+
+ if (activei_mapping_pending && activei == i) {
+ activei = new_order_idx;
+ /* So that we do not map again activei! */
+ activei_mapping_pending = false;
+ }
+#if 0 /* For now, do not alter active element, even if it will be hidden... */
+ else if (activei < i) {
+ /* We do not want an active but invisible item!
+ * Only exception is when all items are filtered out...
+ */
+ if (prev_order_idx >= 0) {
+ activei = prev_order_idx;
+ RNA_property_int_set(active_dataptr, activeprop, prev_i);
+ }
+ else {
+ activei = new_order_idx;
+ RNA_property_int_set(active_dataptr, activeprop, i);
+ }
+ }
+ prev_i = i;
+ prev_ii = new_order_idx;
+#endif
+ }
+ i++;
+ }
+ RNA_PROP_END;
+
+ /* If mapping is still pending, no active item was found. Mark as invalid (-1) */
+ r_items->active_item_idx = activei_mapping_pending ? -1 : activei;
+}
+
+/**
+ * Create the UI-list representation of the list items, sorted and filtered if needed.
+ */
+static void ui_template_list_collect_display_items(bContext *C,
+ uiList *ui_list,
+ TemplateListInputData *input_data,
+ const uiListFilterItemsFunc filter_items_fn,
+ TemplateListItems *r_items)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ memset(r_items, 0, sizeof(*r_items));
+
+ /* Filter list items! (not for compact layout, though) */
+ if (input_data->dataptr.data && input_data->prop) {
+ const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
+ const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0;
+ int items_shown;
+#if 0
+ int prev_ii = -1, prev_i;
+#endif
+
+ if (ui_list->layout_type == UILST_LAYOUT_COMPACT) {
+ dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(
+ &input_data->dataptr, input_data->prop);
+ }
+ else {
+ // printf("%s: filtering...\n", __func__);
+ filter_items_fn(ui_list, C, &input_data->dataptr, RNA_property_identifier(input_data->prop));
+ // printf("%s: filtering done.\n", __func__);
+ }
+
+ items_shown = dyn_data->items_shown;
+ if (items_shown >= 0) {
+ r_items->item_vec = static_cast<_uilist_item *>(
+ MEM_mallocN(sizeof(*r_items->item_vec) * items_shown, __func__));
+ // printf("%s: items shown: %d.\n", __func__, items_shown);
+
+ ui_template_list_collect_items(&input_data->dataptr,
+ input_data->prop,
+ dyn_data,
+ filter_exclude,
+ order_reverse,
+ input_data->active_item_idx,
+ r_items);
+ }
+ if (dyn_data->items_shown >= 0) {
+ r_items->tot_items = dyn_data->items_shown;
+ }
+ else {
+ r_items->tot_items = dyn_data->items_len;
+ }
+ }
+}
+
+static void ui_template_list_free_items(TemplateListItems *items)
+{
+ if (items->item_vec) {
+ MEM_freeN(items->item_vec);
+ }
+}
+
+static void uilist_prepare(uiList *ui_list,
+ const TemplateListItems *items,
+ const TemplateListLayoutDrawData *layout_data,
+ TemplateListVisualInfo *r_visual_info)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ const bool use_auto_size = (ui_list->list_grip <
+ (layout_data->rows - UI_LIST_AUTO_SIZE_THRESHOLD));
+
+ int actual_rows = layout_data->rows;
+ int actual_maxrows = layout_data->maxrows;
+ int columns = layout_data->columns;
+
+ /* default rows */
+ if (actual_rows <= 0) {
+ actual_rows = 5;
+ }
+ dyn_data->visual_height_min = actual_rows;
+ if (actual_maxrows < actual_rows) {
+ actual_maxrows = max_ii(actual_rows, 5);
+ }
+ if (columns <= 0) {
+ columns = 9;
+ }
+
+ int activei_row;
+ if (columns > 1) {
+ dyn_data->height = (int)ceil((double)items->tot_items / (double)columns);
+ activei_row = (int)floor((double)items->active_item_idx / (double)columns);
+ }
+ else {
+ dyn_data->height = items->tot_items;
+ activei_row = items->active_item_idx;
+ }
+
+ dyn_data->columns = columns;
+
+ if (!use_auto_size) {
+ /* No auto-size, yet we clamp at min size! */
+ actual_rows = max_ii(ui_list->list_grip, actual_rows);
+ }
+ else if ((actual_rows != actual_maxrows) && (dyn_data->height > actual_rows)) {
+ /* Expand size if needed and possible. */
+ actual_rows = min_ii(dyn_data->height, actual_maxrows);
+ }
+
+ /* If list length changes or list is tagged to check this,
+ * and active is out of view, scroll to it. */
+ if ((ui_list->list_last_len != items->tot_items) ||
+ (ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM)) {
+ if (activei_row < ui_list->list_scroll) {
+ ui_list->list_scroll = activei_row;
+ }
+ else if (activei_row >= ui_list->list_scroll + actual_rows) {
+ ui_list->list_scroll = activei_row - actual_rows + 1;
+ }
+ ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM;
+ }
+
+ const int max_scroll = max_ii(0, dyn_data->height - actual_rows);
+ CLAMP(ui_list->list_scroll, 0, max_scroll);
+ ui_list->list_last_len = items->tot_items;
+ dyn_data->visual_height = actual_rows;
+ r_visual_info->visual_items = actual_rows * columns;
+ r_visual_info->start_idx = ui_list->list_scroll * columns;
+ r_visual_info->end_idx = min_ii(r_visual_info->start_idx + actual_rows * columns,
+ items->tot_items);
+}
+
+static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2))
+{
+ uiList *ui_list = static_cast<uiList *>(arg1);
+ uiListDyn *dyn_data = ui_list->dyn_data;
+
+ /* This way we get diff in number of additional items to show (positive) or hide (negative). */
+ const int diff = round_fl_to_int((float)(dyn_data->resize - dyn_data->resize_prev) /
+ (float)UI_UNIT_Y);
+
+ if (diff != 0) {
+ ui_list->list_grip += diff;
+ dyn_data->resize_prev += diff * UI_UNIT_Y;
+ ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+ }
+
+ /* In case uilist is in popup, we need special refreshing */
+ ED_region_tag_refresh_ui(CTX_wm_menu(C));
+}
+
+static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname)
+{
+ if (propname && propname[0] && itemptr && itemptr->data) {
+ PropertyRNA *prop = RNA_struct_find_property(itemptr, propname);
+
+ if (prop && (RNA_property_type(prop) == PROP_STRING)) {
+ return RNA_property_string_get_alloc(itemptr, prop, nullptr, 0, nullptr);
+ }
+ }
+ return nullptr;
+}
+
+static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip)
+{
+ char *dyn_tooltip = static_cast<char *>(argN);
+ return BLI_sprintfN("%s - %s", tip, dyn_tooltip);
+}
+
+/**
+ * \note Note that \a layout_type may be null.
+ */
+static uiList *ui_list_ensure(bContext *C,
+ uiListType *ui_list_type,
+ const char *list_id,
+ int layout_type,
+ bool sort_reverse,
+ bool sort_lock)
+{
+ /* Allows to work in popups. */
+ ARegion *region = CTX_wm_menu(C);
+ if (region == nullptr) {
+ region = CTX_wm_region(C);
+ }
+
+ /* Find or add the uiList to the current Region. */
+
+ char full_list_id[UI_MAX_NAME_STR];
+ WM_uilisttype_to_full_list_id(ui_list_type, list_id, full_list_id);
+
+ uiList *ui_list = static_cast<uiList *>(
+ BLI_findstring(&region->ui_lists, full_list_id, offsetof(uiList, list_id)));
+
+ if (!ui_list) {
+ ui_list = static_cast<uiList *>(MEM_callocN(sizeof(uiList), "uiList"));
+ BLI_strncpy(ui_list->list_id, full_list_id, sizeof(ui_list->list_id));
+ BLI_addtail(&region->ui_lists, ui_list);
+ ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */
+ if (sort_reverse) {
+ ui_list->filter_sort_flag |= UILST_FLT_SORT_REVERSE;
+ }
+ if (sort_lock) {
+ ui_list->filter_sort_flag |= UILST_FLT_SORT_LOCK;
+ }
+ }
+
+ if (!ui_list->dyn_data) {
+ ui_list->dyn_data = static_cast<uiListDyn *>(
+ MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data"));
+ }
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ /* Note that this isn't a `uiListType` callback, it's stored in the runtime list data. Otherwise
+ * the runtime data could leak when the type is unregistered (e.g. on "Reload Scripts"). */
+ dyn_data->free_runtime_data_fn = uilist_free_dyn_data;
+
+ /* Because we can't actually pass type across save&load... */
+ ui_list->type = ui_list_type;
+ ui_list->layout_type = layout_type;
+
+ /* Reset filtering data. */
+ MEM_SAFE_FREE(dyn_data->items_filter_flags);
+ MEM_SAFE_FREE(dyn_data->items_filter_neworder);
+ dyn_data->items_len = dyn_data->items_shown = -1;
+
+ return ui_list;
+}
+
+static void ui_template_list_layout_draw(bContext *C,
+ uiList *ui_list,
+ uiLayout *layout,
+ TemplateListInputData *input_data,
+ TemplateListItems *items,
+ const TemplateListLayoutDrawData *layout_data,
+ const enum uiTemplateListFlags flags)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ const char *active_propname = RNA_property_identifier(input_data->activeprop);
+
+ uiLayout *glob = nullptr, *box, *row, *col, *subrow, *sub, *overlap;
+ char numstr[32];
+ int rnaicon = ICON_NONE, icon = ICON_NONE;
+ uiBut *but;
+
+ uiBlock *block = uiLayoutGetBlock(layout);
+
+ /* get icon */
+ if (input_data->dataptr.data && input_data->prop) {
+ StructRNA *ptype = RNA_property_pointer_type(&input_data->dataptr, input_data->prop);
+ rnaicon = RNA_struct_ui_icon(ptype);
+ }
+
+ TemplateListVisualInfo visual_info;
+ switch (ui_list->layout_type) {
+ case UILST_LAYOUT_DEFAULT: {
+ /* layout */
+ box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop);
+ glob = uiLayoutColumn(box, true);
+ row = uiLayoutRow(glob, false);
+ col = uiLayoutColumn(row, true);
+
+ TemplateListLayoutDrawData adjusted_layout_data = *layout_data;
+ adjusted_layout_data.columns = 1;
+ /* init numbers */
+ uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info);
+
+ int i = 0;
+ if (input_data->dataptr.data && input_data->prop) {
+ /* create list items */
+ for (i = visual_info.start_idx; i < visual_info.end_idx; i++) {
+ PointerRNA *itemptr = &items->item_vec[i].item;
+ void *dyntip_data;
+ const int org_i = items->item_vec[i].org_idx;
+ const int flt_flag = items->item_vec[i].flt_flag;
+ uiBlock *subblock = uiLayoutGetBlock(col);
+
+ overlap = uiLayoutOverlap(col);
+
+ UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
+
+ /* list item behind label & other buttons */
+ uiLayoutRow(overlap, false);
+
+ but = uiDefButR_prop(subblock,
+ UI_BTYPE_LISTROW,
+ 0,
+ "",
+ 0,
+ 0,
+ UI_UNIT_X * 10,
+ UI_UNIT_Y,
+ &input_data->active_dataptr,
+ input_data->activeprop,
+ 0,
+ 0,
+ org_i,
+ 0,
+ 0,
+ TIP_("Double click to rename"));
+ if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr,
+ input_data->item_dyntip_propname))) {
+ UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data, MEM_freeN);
+ }
+
+ sub = uiLayoutRow(overlap, false);
+
+ icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
+ if (icon == ICON_DOT) {
+ icon = ICON_NONE;
+ }
+ layout_data->draw_item(ui_list,
+ C,
+ sub,
+ &input_data->dataptr,
+ itemptr,
+ icon,
+ &input_data->active_dataptr,
+ active_propname,
+ org_i,
+ flt_flag);
+
+ /* Items should be able to set context pointers for the layout. But the list-row button
+ * swallows events, so it needs the context storage too for handlers to see it. */
+ but->context = uiLayoutGetContextStore(sub);
+
+ /* If we are "drawing" active item, set all labels as active. */
+ if (i == items->active_item_idx) {
+ ui_layout_list_set_labels_active(sub);
+ }
+
+ UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
+ }
+ }
+
+ /* add dummy buttons to fill space */
+ for (; i < visual_info.start_idx + visual_info.visual_items; i++) {
+ uiItemL(col, "", ICON_NONE);
+ }
+
+ /* add scrollbar */
+ if (items->tot_items > visual_info.visual_items) {
+ uiLayoutColumn(row, false);
+ uiDefButI(block,
+ UI_BTYPE_SCROLL,
+ 0,
+ "",
+ 0,
+ 0,
+ V2D_SCROLL_WIDTH,
+ UI_UNIT_Y * dyn_data->visual_height,
+ &ui_list->list_scroll,
+ 0,
+ dyn_data->height - dyn_data->visual_height,
+ dyn_data->visual_height,
+ 0,
+ "");
+ }
+ } break;
+ case UILST_LAYOUT_COMPACT:
+ row = uiLayoutRow(layout, true);
+
+ if ((input_data->dataptr.data && input_data->prop) && (dyn_data->items_shown > 0) &&
+ (items->active_item_idx >= 0) && (items->active_item_idx < dyn_data->items_shown)) {
+ PointerRNA *itemptr = &items->item_vec[items->active_item_idx].item;
+ const int org_i = items->item_vec[items->active_item_idx].org_idx;
+
+ icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
+ if (icon == ICON_DOT) {
+ icon = ICON_NONE;
+ }
+ layout_data->draw_item(ui_list,
+ C,
+ row,
+ &input_data->dataptr,
+ itemptr,
+ icon,
+ &input_data->active_dataptr,
+ active_propname,
+ org_i,
+ 0);
+ }
+ /* if list is empty, add in dummy button */
+ else {
+ uiItemL(row, "", ICON_NONE);
+ }
+
+ /* next/prev button */
+ BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown);
+ but = uiDefIconTextButR_prop(block,
+ UI_BTYPE_NUM,
+ 0,
+ 0,
+ numstr,
+ 0,
+ 0,
+ UI_UNIT_X * 5,
+ UI_UNIT_Y,
+ &input_data->active_dataptr,
+ input_data->activeprop,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ if (dyn_data->items_shown == 0) {
+ UI_but_flag_enable(but, UI_BUT_DISABLED);
+ }
+ break;
+ case UILST_LAYOUT_GRID: {
+ box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop);
+ glob = uiLayoutColumn(box, true);
+ row = uiLayoutRow(glob, false);
+ col = uiLayoutColumn(row, true);
+ subrow = nullptr; /* Quite gcc warning! */
+
+ uilist_prepare(ui_list, items, layout_data, &visual_info);
+
+ int i = 0;
+ if (input_data->dataptr.data && input_data->prop) {
+ /* create list items */
+ for (i = visual_info.start_idx; i < visual_info.end_idx; i++) {
+ PointerRNA *itemptr = &items->item_vec[i].item;
+ const int org_i = items->item_vec[i].org_idx;
+ const int flt_flag = items->item_vec[i].flt_flag;
+
+ /* create button */
+ if (!(i % layout_data->columns)) {
+ subrow = uiLayoutRow(col, false);
+ }
+
+ uiBlock *subblock = uiLayoutGetBlock(subrow);
+ overlap = uiLayoutOverlap(subrow);
+
+ UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
+
+ /* list item behind label & other buttons */
+ uiLayoutRow(overlap, false);
+
+ but = uiDefButR_prop(subblock,
+ UI_BTYPE_LISTROW,
+ 0,
+ "",
+ 0,
+ 0,
+ UI_UNIT_X * 10,
+ UI_UNIT_Y,
+ &input_data->active_dataptr,
+ input_data->activeprop,
+ 0,
+ 0,
+ org_i,
+ 0,
+ 0,
+ nullptr);
+ UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP);
+
+ sub = uiLayoutRow(overlap, false);
+
+ icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
+ layout_data->draw_item(ui_list,
+ C,
+ sub,
+ &input_data->dataptr,
+ itemptr,
+ icon,
+ &input_data->active_dataptr,
+ active_propname,
+ org_i,
+ flt_flag);
+
+ /* If we are "drawing" active item, set all labels as active. */
+ if (i == items->active_item_idx) {
+ ui_layout_list_set_labels_active(sub);
+ }
+
+ UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
+ }
+ }
+
+ /* add dummy buttons to fill space */
+ for (; i < visual_info.start_idx + visual_info.visual_items; i++) {
+ if (!(i % layout_data->columns)) {
+ subrow = uiLayoutRow(col, false);
+ }
+ uiItemL(subrow, "", ICON_NONE);
+ }
+
+ /* add scrollbar */
+ if (items->tot_items > visual_info.visual_items) {
+ /* col = */ uiLayoutColumn(row, false);
+ uiDefButI(block,
+ UI_BTYPE_SCROLL,
+ 0,
+ "",
+ 0,
+ 0,
+ V2D_SCROLL_WIDTH,
+ UI_UNIT_Y * dyn_data->visual_height,
+ &ui_list->list_scroll,
+ 0,
+ dyn_data->height - dyn_data->visual_height,
+ dyn_data->visual_height,
+ 0,
+ "");
+ }
+ break;
+ }
+ case UILST_LAYOUT_BIG_PREVIEW_GRID:
+ box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop);
+ /* For grip button. */
+ glob = uiLayoutColumn(box, true);
+ /* For scrollbar. */
+ row = uiLayoutRow(glob, false);
+
+ /* TODO ED_fileselect_init_layout(). Share somehow? */
+ float size_x = (96.0f / 20.0f) * UI_UNIT_X;
+ float size_y = (96.0f / 20.0f) * UI_UNIT_Y;
+
+ const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1);
+ uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true);
+
+ TemplateListLayoutDrawData adjusted_layout_data = *layout_data;
+ adjusted_layout_data.columns = cols_per_row;
+ uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info);
+
+ if (input_data->dataptr.data && input_data->prop) {
+ /* create list items */
+ for (int i = visual_info.start_idx; i < visual_info.end_idx; i++) {
+ PointerRNA *itemptr = &items->item_vec[i].item;
+ const int org_i = items->item_vec[i].org_idx;
+ const int flt_flag = items->item_vec[i].flt_flag;
+
+ overlap = uiLayoutOverlap(grid);
+ col = uiLayoutColumn(overlap, false);
+
+ uiBlock *subblock = uiLayoutGetBlock(col);
+ UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
+
+ but = uiDefButR_prop(subblock,
+ UI_BTYPE_LISTROW,
+ 0,
+ "",
+ 0,
+ 0,
+ size_x,
+ size_y,
+ &input_data->active_dataptr,
+ input_data->activeprop,
+ 0,
+ 0,
+ org_i,
+ 0,
+ 0,
+ nullptr);
+ UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP);
+
+ col = uiLayoutColumn(overlap, false);
+
+ icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
+ layout_data->draw_item(ui_list,
+ C,
+ col,
+ &input_data->dataptr,
+ itemptr,
+ icon,
+ &input_data->active_dataptr,
+ active_propname,
+ org_i,
+ flt_flag);
+
+ /* Items should be able to set context pointers for the layout. But the list-row button
+ * swallows events, so it needs the context storage too for handlers to see it. */
+ but->context = uiLayoutGetContextStore(col);
+
+ /* If we are "drawing" active item, set all labels as active. */
+ if (i == items->active_item_idx) {
+ ui_layout_list_set_labels_active(col);
+ }
+
+ UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
+ }
+ }
+
+ if (items->tot_items > visual_info.visual_items) {
+ /* col = */ uiLayoutColumn(row, false);
+ uiDefButI(block,
+ UI_BTYPE_SCROLL,
+ 0,
+ "",
+ 0,
+ 0,
+ V2D_SCROLL_WIDTH,
+ size_y * dyn_data->visual_height,
+ &ui_list->list_scroll,
+ 0,
+ dyn_data->height - dyn_data->visual_height,
+ dyn_data->visual_height,
+ 0,
+ "");
+ }
+ break;
+ }
+
+ if (glob) {
+ const bool add_grip_but = (flags & UI_TEMPLATE_LIST_NO_GRIP) == 0;
+
+ /* About #UI_BTYPE_GRIP drag-resize:
+ * We can't directly use results from a grip button, since we have a
+ * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature).
+ * Since we *never* know whether we are grip-resizing or not
+ * (because there is no callback for when a button enters/leaves its "edit mode"),
+ * we use the fact that grip-controlled value (dyn_data->resize) is completely handled
+ * by the grip during the grab resize, so settings its value here has no effect at all.
+ *
+ * It is only meaningful when we are not resizing,
+ * in which case this gives us the correct "init drag" value.
+ * Note we cannot affect `dyn_data->resize_prev here`,
+ * since this value is not controlled by the grip!
+ */
+ dyn_data->resize = dyn_data->resize_prev +
+ (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y;
+
+ row = uiLayoutRow(glob, true);
+ uiBlock *subblock = uiLayoutGetBlock(row);
+ UI_block_emboss_set(subblock, UI_EMBOSS_NONE);
+
+ if (ui_list->filter_flag & UILST_FLT_SHOW) {
+ but = uiDefIconButBitI(subblock,
+ UI_BTYPE_TOGGLE,
+ UILST_FLT_SHOW,
+ 0,
+ ICON_DISCLOSURE_TRI_DOWN,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y * 0.5f,
+ &(ui_list->filter_flag),
+ 0,
+ 0,
+ 0,
+ 0,
+ TIP_("Hide filtering options"));
+ UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */
+
+ if (add_grip_but) {
+ but = uiDefIconButI(subblock,
+ UI_BTYPE_GRIP,
+ 0,
+ ICON_GRIP,
+ 0,
+ 0,
+ UI_UNIT_X * 10.0f,
+ UI_UNIT_Y * 0.5f,
+ &dyn_data->resize,
+ 0.0,
+ 0.0,
+ 0,
+ 0,
+ "");
+ UI_but_func_set(but, uilist_resize_update_cb, ui_list, nullptr);
+ }
+
+ UI_block_emboss_set(subblock, UI_EMBOSS);
+
+ col = uiLayoutColumn(glob, false);
+ subblock = uiLayoutGetBlock(col);
+ uiDefBut(subblock,
+ UI_BTYPE_SEPR,
+ 0,
+ "",
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y * 0.05f,
+ nullptr,
+ 0.0,
+ 0.0,
+ 0,
+ 0,
+ "");
+
+ layout_data->draw_filter(ui_list, C, col);
+ }
+ else {
+ but = uiDefIconButBitI(subblock,
+ UI_BTYPE_TOGGLE,
+ UILST_FLT_SHOW,
+ 0,
+ ICON_DISCLOSURE_TRI_RIGHT,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y * 0.5f,
+ &(ui_list->filter_flag),
+ 0,
+ 0,
+ 0,
+ 0,
+ TIP_("Show filtering options"));
+ UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */
+
+ if (add_grip_but) {
+ but = uiDefIconButI(subblock,
+ UI_BTYPE_GRIP,
+ 0,
+ ICON_GRIP,
+ 0,
+ 0,
+ UI_UNIT_X * 10.0f,
+ UI_UNIT_Y * 0.5f,
+ &dyn_data->resize,
+ 0.0,
+ 0.0,
+ 0,
+ 0,
+ "");
+ UI_but_func_set(but, uilist_resize_update_cb, ui_list, nullptr);
+ }
+
+ UI_block_emboss_set(subblock, UI_EMBOSS);
+ }
+ }
+}
+
+uiList *uiTemplateList_ex(uiLayout *layout,
+ bContext *C,
+ const char *listtype_name,
+ const char *list_id,
+ PointerRNA *dataptr,
+ const char *propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ int rows,
+ int maxrows,
+ int layout_type,
+ int columns,
+ enum uiTemplateListFlags flags,
+ void *customdata)
+{
+ TemplateListInputData input_data = {{nullptr}};
+ uiListType *ui_list_type;
+ if (!ui_template_list_data_retrieve(listtype_name,
+ list_id,
+ dataptr,
+ propname,
+ active_dataptr,
+ active_propname,
+ item_dyntip_propname,
+ &input_data,
+ &ui_list_type)) {
+ return nullptr;
+ }
+
+ uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item :
+ uilist_draw_item_default;
+ uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter :
+ uilist_draw_filter_default;
+ uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items :
+ uilist_filter_items_default;
+
+ uiList *ui_list = ui_list_ensure(C,
+ ui_list_type,
+ list_id,
+ layout_type,
+ flags & UI_TEMPLATE_LIST_SORT_REVERSE,
+ flags & UI_TEMPLATE_LIST_SORT_LOCK);
+ uiListDyn *dyn_data = ui_list->dyn_data;
+
+ MEM_SAFE_FREE(dyn_data->customdata);
+ dyn_data->customdata = customdata;
+
+ /* When active item changed since last draw, scroll to it. */
+ if (input_data.active_item_idx != ui_list->list_last_activei) {
+ ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+ ui_list->list_last_activei = input_data.active_item_idx;
+ }
+
+ TemplateListItems items;
+ ui_template_list_collect_display_items(C, ui_list, &input_data, filter_items, &items);
+
+ TemplateListLayoutDrawData layout_data;
+ layout_data.draw_item = draw_item;
+ layout_data.draw_filter = draw_filter;
+ layout_data.rows = rows;
+ layout_data.maxrows = maxrows;
+ layout_data.columns = columns;
+
+ ui_template_list_layout_draw(C, ui_list, layout, &input_data, &items, &layout_data, flags);
+
+ ui_template_list_free_items(&items);
+
+ return ui_list;
+}
+
+void uiTemplateList(uiLayout *layout,
+ bContext *C,
+ const char *listtype_name,
+ const char *list_id,
+ PointerRNA *dataptr,
+ const char *propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ int rows,
+ int maxrows,
+ int layout_type,
+ int columns,
+ enum uiTemplateListFlags flags)
+{
+ uiTemplateList_ex(layout,
+ C,
+ listtype_name,
+ list_id,
+ dataptr,
+ propname,
+ active_dataptr,
+ active_propname,
+ item_dyntip_propname,
+ rows,
+ maxrows,
+ layout_type,
+ columns,
+ flags,
+ nullptr);
+}
+
+/**
+ * \return: A RNA pointer for the operator properties.
+ */
+PointerRNA *UI_list_custom_activate_operator_set(uiList *ui_list,
+ const char *opname,
+ bool create_properties)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ dyn_data->custom_activate_optype = WM_operatortype_find(opname, false);
+ if (!dyn_data->custom_activate_optype) {
+ return nullptr;
+ }
+
+ if (create_properties) {
+ PointerRNA *opptr = dyn_data->custom_activate_opptr;
+ WM_operator_properties_alloc(
+ &dyn_data->custom_activate_opptr, opptr ? (IDProperty **)&opptr->data : nullptr, opname);
+ }
+
+ return dyn_data->custom_activate_opptr;
+}
+
+/**
+ * \return: A RNA pointer for the operator properties.
+ */
+PointerRNA *UI_list_custom_drag_operator_set(uiList *ui_list,
+ const char *opname,
+ bool create_properties)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ dyn_data->custom_drag_optype = WM_operatortype_find(opname, false);
+ if (!dyn_data->custom_drag_optype) {
+ return nullptr;
+ }
+
+ if (create_properties) {
+ PointerRNA *opptr = dyn_data->custom_drag_opptr;
+ WM_operator_properties_alloc(
+ &dyn_data->custom_drag_opptr, opptr ? (IDProperty **)&opptr->data : nullptr, opname);
+ }
+
+ return dyn_data->custom_drag_opptr;
+}
+
+/* -------------------------------------------------------------------- */
+
+/** \name List-types Registration
+ * \{ */
+
+void ED_uilisttypes_ui(void)
+{
+ WM_uilisttype_add(UI_UL_asset_view());
+}
+
+/** \} */
diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c
index f01dca7712c..3105891142f 100644
--- a/source/blender/editors/interface/interface_template_search_menu.c
+++ b/source/blender/editors/interface/interface_template_search_menu.c
@@ -1037,7 +1037,7 @@ static void menu_search_update_fn(const bContext *UNUSED(C),
static bool ui_search_menu_create_context_menu(struct bContext *C,
void *arg,
void *active,
- const struct wmEvent *UNUSED(event))
+ const struct wmEvent *event)
{
struct MenuSearch_Data *data = arg;
struct MenuSearch_Item *item = active;
@@ -1058,7 +1058,7 @@ static bool ui_search_menu_create_context_menu(struct bContext *C,
CTX_wm_region_set(C, item->wm_context->region);
}
- if (ui_popup_context_menu_for_button(C, but)) {
+ if (ui_popup_context_menu_for_button(C, but, event)) {
has_menu = true;
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 9c17486aea4..766840909cc 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -5645,887 +5645,6 @@ void uiTemplateLayers(uiLayout *layout,
/** \} */
/* -------------------------------------------------------------------- */
-/** \name List Template
- * \{ */
-
-static void uilist_draw_item_default(struct uiList *ui_list,
- struct bContext *UNUSED(C),
- struct uiLayout *layout,
- struct PointerRNA *UNUSED(dataptr),
- struct PointerRNA *itemptr,
- int icon,
- struct PointerRNA *UNUSED(active_dataptr),
- const char *UNUSED(active_propname),
- int UNUSED(index),
- int UNUSED(flt_flag))
-{
- PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type);
-
- /* Simplest one! */
- switch (ui_list->layout_type) {
- case UILST_LAYOUT_GRID:
- uiItemL(layout, "", icon);
- break;
- case UILST_LAYOUT_DEFAULT:
- case UILST_LAYOUT_COMPACT:
- default:
- if (nameprop) {
- uiItemFullR(layout, itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon);
- }
- else {
- uiItemL(layout, "", icon);
- }
- break;
- }
-}
-
-static void uilist_draw_filter_default(struct uiList *ui_list,
- struct bContext *UNUSED(C),
- struct uiLayout *layout)
-{
- PointerRNA listptr;
- RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr);
-
- uiLayout *row = uiLayoutRow(layout, false);
-
- uiLayout *subrow = uiLayoutRow(row, true);
- uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE);
- uiItemR(subrow,
- &listptr,
- "use_filter_invert",
- UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
- "",
- ICON_ARROW_LEFTRIGHT);
-
- if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) {
- subrow = uiLayoutRow(row, true);
- uiItemR(subrow,
- &listptr,
- "use_filter_sort_alpha",
- UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
- "",
- ICON_NONE);
- uiItemR(subrow,
- &listptr,
- "use_filter_sort_reverse",
- UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
- "",
- (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC : ICON_SORT_ASC);
- }
-}
-
-typedef struct {
- char name[MAX_IDPROP_NAME];
- int org_idx;
-} StringCmp;
-
-static int cmpstringp(const void *p1, const void *p2)
-{
- /* Case-insensitive comparison. */
- return BLI_strcasecmp(((StringCmp *)p1)->name, ((StringCmp *)p2)->name);
-}
-
-static void uilist_filter_items_default(struct uiList *ui_list,
- struct bContext *UNUSED(C),
- struct PointerRNA *dataptr,
- const char *propname)
-{
- uiListDyn *dyn_data = ui_list->dyn_data;
- PropertyRNA *prop = RNA_struct_find_property(dataptr, propname);
-
- const char *filter_raw = ui_list->filter_byname;
- char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL;
- const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0;
- const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) ==
- UILST_FLT_SORT_ALPHA;
- const int len = RNA_property_collection_length(dataptr, prop);
-
- dyn_data->items_shown = dyn_data->items_len = len;
-
- if (len && (order_by_name || filter_raw[0])) {
- StringCmp *names = NULL;
- int order_idx = 0, i = 0;
-
- if (order_by_name) {
- names = MEM_callocN(sizeof(StringCmp) * len, "StringCmp");
- }
- if (filter_raw[0]) {
- const size_t slen = strlen(filter_raw);
-
- dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, "items_filter_flags");
- dyn_data->items_shown = 0;
-
- /* Implicitly add heading/trailing wildcards if needed. */
- if (slen + 3 <= sizeof(filter_buff)) {
- filter = filter_buff;
- }
- else {
- filter = filter_dyn = MEM_mallocN((slen + 3) * sizeof(char), "filter_dyn");
- }
- BLI_strncpy_ensure_pad(filter, filter_raw, '*', slen + 3);
- }
-
- RNA_PROP_BEGIN (dataptr, itemptr, prop) {
- bool do_order = false;
-
- char *namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL);
- const char *name = namebuf ? namebuf : "";
-
- if (filter[0]) {
- /* Case-insensitive! */
- if (fnmatch(filter, name, FNM_CASEFOLD) == 0) {
- dyn_data->items_filter_flags[i] = UILST_FLT_ITEM;
- if (!filter_exclude) {
- dyn_data->items_shown++;
- do_order = order_by_name;
- }
- // printf("%s: '%s' matches '%s'\n", __func__, name, filter);
- }
- else if (filter_exclude) {
- dyn_data->items_shown++;
- do_order = order_by_name;
- }
- }
- else {
- do_order = order_by_name;
- }
-
- if (do_order) {
- names[order_idx].org_idx = order_idx;
- BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME);
- }
-
- /* free name */
- if (namebuf) {
- MEM_freeN(namebuf);
- }
- i++;
- }
- RNA_PROP_END;
-
- if (order_by_name) {
- int new_idx;
- /* NOTE: order_idx equals either to ui_list->items_len if no filtering done,
- * or to ui_list->items_shown if filter is enabled,
- * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded.
- * This way, we only sort items we actually intend to draw!
- */
- qsort(names, order_idx, sizeof(StringCmp), cmpstringp);
-
- dyn_data->items_filter_neworder = MEM_mallocN(sizeof(int) * order_idx,
- "items_filter_neworder");
- for (new_idx = 0; new_idx < order_idx; new_idx++) {
- dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx;
- }
- }
-
- if (filter_dyn) {
- MEM_freeN(filter_dyn);
- }
- if (names) {
- MEM_freeN(names);
- }
- }
-}
-
-typedef struct {
- PointerRNA item;
- int org_idx;
- int flt_flag;
-} _uilist_item;
-
-typedef struct {
- int visual_items; /* Visual number of items (i.e. number of items we have room to display). */
- int start_idx; /* Index of first item to display. */
- int end_idx; /* Index of last item to display + 1. */
-} uiListLayoutdata;
-
-static void uilist_prepare(uiList *ui_list,
- int len,
- int activei,
- int rows,
- int maxrows,
- int columns,
- uiListLayoutdata *layoutdata)
-{
- uiListDyn *dyn_data = ui_list->dyn_data;
- const bool use_auto_size = (ui_list->list_grip < (rows - UI_LIST_AUTO_SIZE_THRESHOLD));
-
- /* default rows */
- if (rows <= 0) {
- rows = 5;
- }
- dyn_data->visual_height_min = rows;
- if (maxrows < rows) {
- maxrows = max_ii(rows, 5);
- }
- if (columns <= 0) {
- columns = 9;
- }
-
- int activei_row;
- if (columns > 1) {
- dyn_data->height = (int)ceil((double)len / (double)columns);
- activei_row = (int)floor((double)activei / (double)columns);
- }
- else {
- dyn_data->height = len;
- activei_row = activei;
- }
-
- if (!use_auto_size) {
- /* No auto-size, yet we clamp at min size! */
- maxrows = rows = max_ii(ui_list->list_grip, rows);
- }
- else if ((rows != maxrows) && (dyn_data->height > rows)) {
- /* Expand size if needed and possible. */
- rows = min_ii(dyn_data->height, maxrows);
- }
-
- /* If list length changes or list is tagged to check this,
- * and active is out of view, scroll to it. */
- if (ui_list->list_last_len != len || ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM) {
- if (activei_row < ui_list->list_scroll) {
- ui_list->list_scroll = activei_row;
- }
- else if (activei_row >= ui_list->list_scroll + rows) {
- ui_list->list_scroll = activei_row - rows + 1;
- }
- ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM;
- }
-
- const int max_scroll = max_ii(0, dyn_data->height - rows);
- CLAMP(ui_list->list_scroll, 0, max_scroll);
- ui_list->list_last_len = len;
- dyn_data->visual_height = rows;
- layoutdata->visual_items = rows * columns;
- layoutdata->start_idx = ui_list->list_scroll * columns;
- layoutdata->end_idx = min_ii(layoutdata->start_idx + rows * columns, len);
-}
-
-static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2))
-{
- uiList *ui_list = arg1;
- uiListDyn *dyn_data = ui_list->dyn_data;
-
- /* This way we get diff in number of additional items to show (positive) or hide (negative). */
- const int diff = round_fl_to_int((float)(dyn_data->resize - dyn_data->resize_prev) /
- (float)UI_UNIT_Y);
-
- if (diff != 0) {
- ui_list->list_grip += diff;
- dyn_data->resize_prev += diff * UI_UNIT_Y;
- ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
- }
-
- /* In case uilist is in popup, we need special refreshing */
- ED_region_tag_refresh_ui(CTX_wm_menu(C));
-}
-
-static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname)
-{
- if (propname && propname[0] && itemptr && itemptr->data) {
- PropertyRNA *prop = RNA_struct_find_property(itemptr, propname);
-
- if (prop && (RNA_property_type(prop) == PROP_STRING)) {
- return RNA_property_string_get_alloc(itemptr, prop, NULL, 0, NULL);
- }
- }
- return NULL;
-}
-
-static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip)
-{
- char *dyn_tooltip = argN;
- return BLI_sprintfN("%s - %s", tip, dyn_tooltip);
-}
-
-void uiTemplateList(uiLayout *layout,
- bContext *C,
- const char *listtype_name,
- const char *list_id,
- PointerRNA *dataptr,
- const char *propname,
- PointerRNA *active_dataptr,
- const char *active_propname,
- const char *item_dyntip_propname,
- int rows,
- int maxrows,
- int layout_type,
- int columns,
- bool sort_reverse,
- bool sort_lock)
-{
- PropertyRNA *prop = NULL, *activeprop;
- _uilist_item *items_ptr = NULL;
- uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap;
- uiBut *but;
-
- uiListLayoutdata layoutdata;
- char ui_list_id[UI_MAX_NAME_STR];
- char numstr[32];
- int rnaicon = ICON_NONE, icon = ICON_NONE;
- int i = 0, activei = 0;
- int len = 0;
-
- /* validate arguments */
- /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */
- if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) {
- RNA_warning("template_list using default '%s' UIList class must provide a custom list_id",
- UI_UL_DEFAULT_CLASS_NAME);
- return;
- }
-
- uiBlock *block = uiLayoutGetBlock(layout);
-
- if (!active_dataptr->data) {
- RNA_warning("No active data");
- return;
- }
-
- if (dataptr->data) {
- prop = RNA_struct_find_property(dataptr, propname);
- if (!prop) {
- RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname);
- return;
- }
- }
-
- activeprop = RNA_struct_find_property(active_dataptr, active_propname);
- if (!activeprop) {
- RNA_warning(
- "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname);
- return;
- }
-
- if (prop) {
- const PropertyType type = RNA_property_type(prop);
- if (type != PROP_COLLECTION) {
- RNA_warning("Expected a collection data property");
- return;
- }
- }
-
- const PropertyType activetype = RNA_property_type(activeprop);
- if (activetype != PROP_INT) {
- RNA_warning("Expected an integer active data property");
- return;
- }
-
- /* get icon */
- if (dataptr->data && prop) {
- StructRNA *ptype = RNA_property_pointer_type(dataptr, prop);
- rnaicon = RNA_struct_ui_icon(ptype);
- }
-
- /* get active data */
- activei = RNA_property_int_get(active_dataptr, activeprop);
-
- /* Find the uiList type. */
- uiListType *ui_list_type = WM_uilisttype_find(listtype_name, false);
-
- if (ui_list_type == NULL) {
- RNA_warning("List type %s not found", listtype_name);
- return;
- }
-
- uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item :
- uilist_draw_item_default;
- uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter :
- uilist_draw_filter_default;
- uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items :
- uilist_filter_items_default;
-
- /* Find or add the uiList to the current Region. */
- /* We tag the list id with the list type... */
- BLI_snprintf(
- ui_list_id, sizeof(ui_list_id), "%s_%s", ui_list_type->idname, list_id ? list_id : "");
-
- /* Allows to work in popups. */
- ARegion *region = CTX_wm_menu(C);
- if (region == NULL) {
- region = CTX_wm_region(C);
- }
- uiList *ui_list = BLI_findstring(&region->ui_lists, ui_list_id, offsetof(uiList, list_id));
-
- if (!ui_list) {
- ui_list = MEM_callocN(sizeof(uiList), "uiList");
- BLI_strncpy(ui_list->list_id, ui_list_id, sizeof(ui_list->list_id));
- BLI_addtail(&region->ui_lists, ui_list);
- ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */
- if (sort_reverse) {
- ui_list->filter_sort_flag |= UILST_FLT_SORT_REVERSE;
- }
- if (sort_lock) {
- ui_list->filter_sort_flag |= UILST_FLT_SORT_LOCK;
- }
- }
-
- if (!ui_list->dyn_data) {
- ui_list->dyn_data = MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data");
- }
- uiListDyn *dyn_data = ui_list->dyn_data;
-
- /* Because we can't actually pass type across save&load... */
- ui_list->type = ui_list_type;
- ui_list->layout_type = layout_type;
-
- /* Reset filtering data. */
- MEM_SAFE_FREE(dyn_data->items_filter_flags);
- MEM_SAFE_FREE(dyn_data->items_filter_neworder);
- dyn_data->items_len = dyn_data->items_shown = -1;
-
- /* When active item changed since last draw, scroll to it. */
- if (activei != ui_list->list_last_activei) {
- ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
- ui_list->list_last_activei = activei;
- }
-
- /* Filter list items! (not for compact layout, though) */
- if (dataptr->data && prop) {
- const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
- const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0;
- int items_shown, idx = 0;
-#if 0
- int prev_ii = -1, prev_i;
-#endif
-
- if (layout_type == UILST_LAYOUT_COMPACT) {
- dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(dataptr, prop);
- }
- else {
- // printf("%s: filtering...\n", __func__);
- filter_items(ui_list, C, dataptr, propname);
- // printf("%s: filtering done.\n", __func__);
- }
-
- items_shown = dyn_data->items_shown;
- if (items_shown >= 0) {
- bool activei_mapping_pending = true;
- items_ptr = MEM_mallocN(sizeof(_uilist_item) * items_shown, __func__);
- // printf("%s: items shown: %d.\n", __func__, items_shown);
- RNA_PROP_BEGIN (dataptr, itemptr, prop) {
- if (!dyn_data->items_filter_flags ||
- ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) {
- int ii;
- if (dyn_data->items_filter_neworder) {
- ii = dyn_data->items_filter_neworder[idx++];
- ii = order_reverse ? items_shown - ii - 1 : ii;
- }
- else {
- ii = order_reverse ? items_shown - ++idx : idx++;
- }
- // printf("%s: ii: %d\n", __func__, ii);
- items_ptr[ii].item = itemptr;
- items_ptr[ii].org_idx = i;
- items_ptr[ii].flt_flag = dyn_data->items_filter_flags ? dyn_data->items_filter_flags[i] :
- 0;
-
- if (activei_mapping_pending && activei == i) {
- activei = ii;
- /* So that we do not map again activei! */
- activei_mapping_pending = false;
- }
-#if 0 /* For now, do not alter active element, even if it will be hidden... */
- else if (activei < i) {
- /* We do not want an active but invisible item!
- * Only exception is when all items are filtered out...
- */
- if (prev_ii >= 0) {
- activei = prev_ii;
- RNA_property_int_set(active_dataptr, activeprop, prev_i);
- }
- else {
- activei = ii;
- RNA_property_int_set(active_dataptr, activeprop, i);
- }
- }
- prev_i = i;
- prev_ii = ii;
-#endif
- }
- i++;
- }
- RNA_PROP_END;
-
- if (activei_mapping_pending) {
- /* No active item found, set to 'invalid' -1 value... */
- activei = -1;
- }
- }
- if (dyn_data->items_shown >= 0) {
- len = dyn_data->items_shown;
- }
- else {
- len = dyn_data->items_len;
- }
- }
-
- switch (layout_type) {
- case UILST_LAYOUT_DEFAULT:
- /* layout */
- box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
- glob = uiLayoutColumn(box, true);
- row = uiLayoutRow(glob, false);
- col = uiLayoutColumn(row, true);
-
- /* init numbers */
- uilist_prepare(ui_list, len, activei, rows, maxrows, 1, &layoutdata);
-
- if (dataptr->data && prop) {
- /* create list items */
- for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
- PointerRNA *itemptr = &items_ptr[i].item;
- void *dyntip_data;
- const int org_i = items_ptr[i].org_idx;
- const int flt_flag = items_ptr[i].flt_flag;
- uiBlock *subblock = uiLayoutGetBlock(col);
-
- overlap = uiLayoutOverlap(col);
-
- UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
-
- /* list item behind label & other buttons */
- sub = uiLayoutRow(overlap, false);
-
- but = uiDefButR_prop(subblock,
- UI_BTYPE_LISTROW,
- 0,
- "",
- 0,
- 0,
- UI_UNIT_X * 10,
- UI_UNIT_Y,
- active_dataptr,
- activeprop,
- 0,
- 0,
- org_i,
- 0,
- 0,
- TIP_("Double click to rename"));
- if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, item_dyntip_propname))) {
- UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data, MEM_freeN);
- }
-
- sub = uiLayoutRow(overlap, false);
-
- icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
- if (icon == ICON_DOT) {
- icon = ICON_NONE;
- }
- draw_item(ui_list,
- C,
- sub,
- dataptr,
- itemptr,
- icon,
- active_dataptr,
- active_propname,
- org_i,
- flt_flag);
-
- /* Items should be able to set context pointers for the layout. But the list-row button
- * swallows events, so it needs the context storage too for handlers to see it. */
- but->context = uiLayoutGetContextStore(sub);
-
- /* If we are "drawing" active item, set all labels as active. */
- if (i == activei) {
- ui_layout_list_set_labels_active(sub);
- }
-
- UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
- }
- }
-
- /* add dummy buttons to fill space */
- for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
- uiItemL(col, "", ICON_NONE);
- }
-
- /* add scrollbar */
- if (len > layoutdata.visual_items) {
- col = uiLayoutColumn(row, false);
- uiDefButI(block,
- UI_BTYPE_SCROLL,
- 0,
- "",
- 0,
- 0,
- V2D_SCROLL_WIDTH,
- UI_UNIT_Y * dyn_data->visual_height,
- &ui_list->list_scroll,
- 0,
- dyn_data->height - dyn_data->visual_height,
- dyn_data->visual_height,
- 0,
- "");
- }
- break;
- case UILST_LAYOUT_COMPACT:
- row = uiLayoutRow(layout, true);
-
- if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) &&
- (activei < dyn_data->items_shown)) {
- PointerRNA *itemptr = &items_ptr[activei].item;
- const int org_i = items_ptr[activei].org_idx;
-
- icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
- if (icon == ICON_DOT) {
- icon = ICON_NONE;
- }
- draw_item(
- ui_list, C, row, dataptr, itemptr, icon, active_dataptr, active_propname, org_i, 0);
- }
- /* if list is empty, add in dummy button */
- else {
- uiItemL(row, "", ICON_NONE);
- }
-
- /* next/prev button */
- BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown);
- but = uiDefIconTextButR_prop(block,
- UI_BTYPE_NUM,
- 0,
- 0,
- numstr,
- 0,
- 0,
- UI_UNIT_X * 5,
- UI_UNIT_Y,
- active_dataptr,
- activeprop,
- 0,
- 0,
- 0,
- 0,
- 0,
- "");
- if (dyn_data->items_shown == 0) {
- UI_but_flag_enable(but, UI_BUT_DISABLED);
- }
- break;
- case UILST_LAYOUT_GRID:
- box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
- glob = uiLayoutColumn(box, true);
- row = uiLayoutRow(glob, false);
- col = uiLayoutColumn(row, true);
- subrow = NULL; /* Quite gcc warning! */
-
- uilist_prepare(ui_list, len, activei, rows, maxrows, columns, &layoutdata);
-
- if (dataptr->data && prop) {
- /* create list items */
- for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
- PointerRNA *itemptr = &items_ptr[i].item;
- const int org_i = items_ptr[i].org_idx;
- const int flt_flag = items_ptr[i].flt_flag;
-
- /* create button */
- if (!(i % columns)) {
- subrow = uiLayoutRow(col, false);
- }
-
- uiBlock *subblock = uiLayoutGetBlock(subrow);
- overlap = uiLayoutOverlap(subrow);
-
- UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
-
- /* list item behind label & other buttons */
- sub = uiLayoutRow(overlap, false);
-
- but = uiDefButR_prop(subblock,
- UI_BTYPE_LISTROW,
- 0,
- "",
- 0,
- 0,
- UI_UNIT_X * 10,
- UI_UNIT_Y,
- active_dataptr,
- activeprop,
- 0,
- 0,
- org_i,
- 0,
- 0,
- NULL);
- UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP);
-
- sub = uiLayoutRow(overlap, false);
-
- icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
- draw_item(ui_list,
- C,
- sub,
- dataptr,
- itemptr,
- icon,
- active_dataptr,
- active_propname,
- org_i,
- flt_flag);
-
- /* If we are "drawing" active item, set all labels as active. */
- if (i == activei) {
- ui_layout_list_set_labels_active(sub);
- }
-
- UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
- }
- }
-
- /* add dummy buttons to fill space */
- for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
- if (!(i % columns)) {
- subrow = uiLayoutRow(col, false);
- }
- uiItemL(subrow, "", ICON_NONE);
- }
-
- /* add scrollbar */
- if (len > layoutdata.visual_items) {
- /* col = */ uiLayoutColumn(row, false);
- uiDefButI(block,
- UI_BTYPE_SCROLL,
- 0,
- "",
- 0,
- 0,
- V2D_SCROLL_WIDTH,
- UI_UNIT_Y * dyn_data->visual_height,
- &ui_list->list_scroll,
- 0,
- dyn_data->height - dyn_data->visual_height,
- dyn_data->visual_height,
- 0,
- "");
- }
- break;
- }
-
- if (glob) {
- /* About #UI_BTYPE_GRIP drag-resize:
- * We can't directly use results from a grip button, since we have a
- * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature).
- * Since we *never* know whether we are grip-resizing or not
- * (because there is no callback for when a button enters/leaves its "edit mode"),
- * we use the fact that grip-controlled value (dyn_data->resize) is completely handled
- * by the grip during the grab resize, so settings its value here has no effect at all.
- *
- * It is only meaningful when we are not resizing,
- * in which case this gives us the correct "init drag" value.
- * Note we cannot affect `dyn_data->resize_prev here`,
- * since this value is not controlled by the grip!
- */
- dyn_data->resize = dyn_data->resize_prev +
- (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y;
-
- row = uiLayoutRow(glob, true);
- uiBlock *subblock = uiLayoutGetBlock(row);
- UI_block_emboss_set(subblock, UI_EMBOSS_NONE);
-
- if (ui_list->filter_flag & UILST_FLT_SHOW) {
- but = uiDefIconButBitI(subblock,
- UI_BTYPE_TOGGLE,
- UILST_FLT_SHOW,
- 0,
- ICON_DISCLOSURE_TRI_DOWN,
- 0,
- 0,
- UI_UNIT_X,
- UI_UNIT_Y * 0.5f,
- &(ui_list->filter_flag),
- 0,
- 0,
- 0,
- 0,
- TIP_("Hide filtering options"));
- UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */
-
- but = uiDefIconButI(subblock,
- UI_BTYPE_GRIP,
- 0,
- ICON_GRIP,
- 0,
- 0,
- UI_UNIT_X * 10.0f,
- UI_UNIT_Y * 0.5f,
- &dyn_data->resize,
- 0.0,
- 0.0,
- 0,
- 0,
- "");
- UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL);
-
- UI_block_emboss_set(subblock, UI_EMBOSS);
-
- col = uiLayoutColumn(glob, false);
- subblock = uiLayoutGetBlock(col);
- uiDefBut(subblock,
- UI_BTYPE_SEPR,
- 0,
- "",
- 0,
- 0,
- UI_UNIT_X,
- UI_UNIT_Y * 0.05f,
- NULL,
- 0.0,
- 0.0,
- 0,
- 0,
- "");
-
- draw_filter(ui_list, C, col);
- }
- else {
- but = uiDefIconButBitI(subblock,
- UI_BTYPE_TOGGLE,
- UILST_FLT_SHOW,
- 0,
- ICON_DISCLOSURE_TRI_RIGHT,
- 0,
- 0,
- UI_UNIT_X,
- UI_UNIT_Y * 0.5f,
- &(ui_list->filter_flag),
- 0,
- 0,
- 0,
- 0,
- TIP_("Show filtering options"));
- UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */
-
- but = uiDefIconButI(subblock,
- UI_BTYPE_GRIP,
- 0,
- ICON_GRIP,
- 0,
- 0,
- UI_UNIT_X * 10.0f,
- UI_UNIT_Y * 0.5f,
- &dyn_data->resize,
- 0.0,
- 0.0,
- 0,
- 0,
- "");
- UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL);
-
- UI_block_emboss_set(subblock, UI_EMBOSS);
- }
- }
-
- if (items_ptr) {
- MEM_freeN(items_ptr);
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Running Jobs Template
* \{ */
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 7ea02226f02..93a790b53d0 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -29,6 +29,8 @@
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
+#include "ED_screen.h"
+
#include "BLI_alloca.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -38,6 +40,7 @@
#include "BLT_translation.h"
+#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_report.h"
@@ -48,6 +51,7 @@
#include "UI_interface.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
+#include "UI_view2d.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -774,6 +778,98 @@ bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str,
}
/* -------------------------------------------------------------------- */
+
+static rctf ui_but_rect_to_view(const uiBut *but, const ARegion *region, const View2D *v2d)
+{
+ rctf region_rect;
+ ui_block_to_region_rctf(region, but->block, &region_rect, &but->rect);
+
+ rctf view_rect;
+ UI_view2d_region_to_view_rctf(v2d, &region_rect, &view_rect);
+
+ return view_rect;
+}
+
+/**
+ * To get a margin (typically wanted), add the margin to \a rect directly.
+ *
+ * Based on #file_ensure_inside_viewbounds(), could probably share code.
+ *
+ * \return true if anything changed.
+ */
+static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect)
+{
+ const float rect_width = BLI_rctf_size_x(rect);
+ const float rect_height = BLI_rctf_size_y(rect);
+
+ rctf *cur = &v2d->cur;
+ const float cur_width = BLI_rctf_size_x(cur);
+ const float cur_height = BLI_rctf_size_y(cur);
+
+ bool changed = false;
+
+ /* Snap to bottom edge. Also use if rect is higher than view bounds (could be a parameter). */
+ if ((cur->ymin > rect->ymin) || (rect_height > cur_height)) {
+ cur->ymin = rect->ymin;
+ cur->ymax = cur->ymin + cur_height;
+ changed = true;
+ }
+ /* Snap to upper edge. */
+ else if (cur->ymax < rect->ymax) {
+ cur->ymax = rect->ymax;
+ cur->ymin = cur->ymax - cur_height;
+ changed = true;
+ }
+ /* Snap to left edge. Also use if rect is wider than view bounds. */
+ else if ((cur->xmin > rect->xmin) || (rect_width > cur_width)) {
+ cur->xmin = rect->xmin;
+ cur->xmax = cur->xmin + cur_width;
+ changed = true;
+ }
+ /* Snap to right edge. */
+ else if (cur->xmax < rect->xmax) {
+ cur->xmax = rect->xmax;
+ cur->xmin = cur->xmax - cur_width;
+ changed = true;
+ }
+ else {
+ BLI_assert(BLI_rctf_inside_rctf(cur, rect));
+ }
+
+ return changed;
+}
+
+/**
+ * Adjust the view so the rectangle of \a but is in view, with some extra margin.
+ *
+ * It's important that this is only executed after buttons received their final #uiBut.rect. E.g.
+ * #UI_panels_end() modifies them, so if that is executed, this function must not be called before
+ * it.
+ *
+ * \param region: The region the button is placed in. Make sure this is actually the one the button
+ * is placed in, not just the context region.
+ */
+void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but)
+{
+ View2D *v2d = &region->v2d;
+ /* Uninitialized view or region that doesn't use View2D. */
+ if ((v2d->flag & V2D_IS_INIT) == 0) {
+ return;
+ }
+
+ rctf rect = ui_but_rect_to_view(but, region, v2d);
+
+ const int margin = UI_UNIT_X * 0.5f;
+ BLI_rctf_pad(&rect, margin, margin);
+
+ const bool changed = ui_view2d_cur_ensure_rect_in_view(v2d, &rect);
+ if (changed) {
+ UI_view2d_curRect_changed(C, v2d);
+ ED_region_tag_redraw_no_rebuild(region);
+ }
+}
+
+/* -------------------------------------------------------------------- */
/** \name Button Store
*
* Modal Button Store API.
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 8abff4ad0d8..d3481c449ac 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -105,6 +105,7 @@ typedef enum {
/* specials */
UI_WTYPE_ICON,
UI_WTYPE_ICON_LABEL,
+ UI_WTYPE_PREVIEW_TILE,
UI_WTYPE_SWATCH,
UI_WTYPE_RGB_PICKER,
UI_WTYPE_UNITVEC,
@@ -2291,7 +2292,7 @@ static void widget_draw_extra_icons(const uiWidgetColors *wcol,
const float icon_size = ICON_SIZE_FROM_BUTRECT(rect);
/* Offset of icons from the right edge. Keep in sync
- with 'ui_but_extra_operator_icon_mouse_over_get'. */
+ * with 'ui_but_extra_operator_icon_mouse_over_get'. */
if (!BLI_listbase_is_empty(&but->extra_op_icons)) {
/* Eyeballed. */
rect->xmax -= 0.2 * icon_size;
@@ -2480,8 +2481,8 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
}
else {
/* In case a separate text label and some other button are placed under each other,
- and the outline of the button does not contrast with the background.
- Add an offset (thickness of the outline) so that the text does not stick out visually. */
+ * and the outline of the button does not contrast with the background.
+ * Add an offset (thickness of the outline) so that the text does not stick out visually. */
if (but->drawflag & UI_BUT_TEXT_LEFT) {
rect->xmin += U.pixelsize;
}
@@ -4018,6 +4019,14 @@ static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roun
widgetbase_draw(&wtb, wcol);
}
+static void widget_preview_tile(
+ uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
+{
+ const uiStyle *style = UI_style_get();
+ ui_draw_preview_item_stateless(
+ &style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER);
+}
+
static void widget_menuiconbut(uiWidgetColors *wcol,
rcti *rect,
int UNUSED(state),
@@ -4483,6 +4492,13 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
wt.custom = widget_icon_has_anim;
break;
+ case UI_WTYPE_PREVIEW_TILE:
+ wt.draw = NULL;
+ /* Drawn via the `custom` callback. */
+ wt.text = NULL;
+ wt.custom = widget_preview_tile;
+ break;
+
case UI_WTYPE_SWATCH:
wt.custom = widget_swatch;
break;
@@ -4778,6 +4794,10 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
wt = widget_type(UI_WTYPE_BOX);
break;
+ case UI_BTYPE_PREVIEW_TILE:
+ wt = widget_type(UI_WTYPE_PREVIEW_TILE);
+ break;
+
case UI_BTYPE_EXTRA:
widget_draw_extra_mask(C, but, widget_type(UI_WTYPE_BOX), rect);
break;
@@ -4931,13 +4951,15 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
wt->draw(&wt->wcol, rect, state, roundboxalign);
}
- if (use_alpha_blend) {
- GPU_blend(GPU_BLEND_ALPHA);
- }
+ if (wt->text) {
+ if (use_alpha_blend) {
+ GPU_blend(GPU_BLEND_ALPHA);
+ }
- wt->text(fstyle, &wt->wcol, but, rect);
- if (use_alpha_blend) {
- GPU_blend(GPU_BLEND_NONE);
+ wt->text(fstyle, &wt->wcol, but, rect);
+ if (use_alpha_blend) {
+ GPU_blend(GPU_BLEND_NONE);
+ }
}
}
@@ -5459,17 +5481,20 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
}
}
-void ui_draw_preview_item(
- const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state)
+/**
+ * Version of #ui_draw_preview_item() that does not draw the menu background and item text based on
+ * state. It just draws the preview and text directly.
+ */
+void ui_draw_preview_item_stateless(const uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ const uchar text_col[4],
+ eFontStyle_Align text_align)
{
rcti trect = *rect;
const float text_size = UI_UNIT_Y;
float font_dims[2] = {0.0f, 0.0f};
- uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
-
- /* drawing button background */
- wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED);
- wt->draw(&wt->wcol, rect, 0, 0);
/* draw icon in rect above the space reserved for the label */
rect->ymin += text_size;
@@ -5481,8 +5506,6 @@ void ui_draw_preview_item(
fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]);
/* text rect */
- trect.xmin += 0;
- trect.xmax = trect.xmin + font_dims[0] + U.widget_unit / 2;
trect.ymin += U.widget_unit / 2;
trect.ymax = trect.ymin + font_dims[1];
if (trect.xmax > rect->xmax - PREVIEW_PAD) {
@@ -5501,11 +5524,27 @@ void ui_draw_preview_item(
UI_fontstyle_draw(fstyle,
&trect,
drawstr,
- wt->wcol.text,
+ text_col,
&(struct uiFontStyleDraw_Params){
- .align = UI_STYLE_TEXT_CENTER,
+ .align = text_align,
});
}
}
+void ui_draw_preview_item(const uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ int state,
+ eFontStyle_Align text_align)
+{
+ uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
+
+ /* drawing button background */
+ wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED);
+ wt->draw(&wt->wcol, rect, 0, 0);
+
+ ui_draw_preview_item_stateless(fstyle, rect, name, iconid, wt->wcol.text, text_align);
+}
+
/** \} */
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index afac254f542..c9bfd883332 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -1089,7 +1089,7 @@ bTheme *UI_GetTheme(void)
}
/**
- * for the rare case we need to temp swap in a different theme (offscreen render)
+ * For the rare case we need to temp swap in a different theme (off-screen render).
*/
void UI_Theme_Store(struct bThemeState *theme_state)
{
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index e9804840801..23c8a0d35bf 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -1194,78 +1194,6 @@ void UI_view2d_view_restore(const bContext *C)
/** \name Grid-Line Drawing
* \{ */
-/* Draw a constant grid in given 2d-region */
-void UI_view2d_constant_grid_draw(const View2D *v2d, float step)
-{
- float start_x, start_y;
- int count_x, count_y;
-
- start_x = v2d->cur.xmin;
- if (start_x < 0.0) {
- start_x += -(float)fmod(v2d->cur.xmin, step);
- }
- else {
- start_x += (step - (float)fmod(v2d->cur.xmin, step));
- }
-
- if (start_x > v2d->cur.xmax) {
- count_x = 0;
- }
- else {
- count_x = (v2d->cur.xmax - start_x) / step + 1;
- }
-
- start_y = v2d->cur.ymin;
- if (start_y < 0.0) {
- start_y += -(float)fmod(v2d->cur.ymin, step);
- }
- else {
- start_y += (step - (float)fabs(fmod(v2d->cur.ymin, step)));
- }
-
- if (start_y > v2d->cur.ymax) {
- count_y = 0;
- }
- else {
- count_y = (v2d->cur.ymax - start_y) / step + 1;
- }
-
- if (count_x > 0 || count_y > 0) {
- GPUVertFormat *format = immVertexFormat();
- const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- float theme_color[3];
-
- UI_GetThemeColorShade3fv(TH_BACK, -10, theme_color);
-
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
- immBegin(GPU_PRIM_LINES, count_x * 2 + count_y * 2 + 4);
-
- immAttr3fv(color, theme_color);
- for (int i = 0; i < count_x; start_x += step, i++) {
- immVertex2f(pos, start_x, v2d->cur.ymin);
- immVertex2f(pos, start_x, v2d->cur.ymax);
- }
-
- for (int i = 0; i < count_y; start_y += step, i++) {
- immVertex2f(pos, v2d->cur.xmin, start_y);
- immVertex2f(pos, v2d->cur.xmax, start_y);
- }
-
- /* X and Y axis */
- UI_GetThemeColorShade3fv(TH_BACK, -18, theme_color);
-
- immAttr3fv(color, theme_color);
- immVertex2f(pos, 0.0f, v2d->cur.ymin);
- immVertex2f(pos, 0.0f, v2d->cur.ymax);
- immVertex2f(pos, v2d->cur.xmin, 0.0f);
- immVertex2f(pos, v2d->cur.xmax, 0.0f);
-
- immEnd();
- immUnbindProgram();
- }
-}
-
/* Draw a multi-level grid in given 2d-region */
void UI_view2d_multi_grid_draw(
const View2D *v2d, int colorid, float step, int level_size, int totlevels)
@@ -1914,7 +1842,7 @@ float UI_view2d_scale_get_y(const View2D *v2d)
return BLI_rcti_size_y(&v2d->mask) / BLI_rctf_size_y(&v2d->cur);
}
/**
- * Same as ``UI_view2d_scale_get() - 1.0f / x, y``
+ * Same as `UI_view2d_scale_get() - 1.0f / x, y`.
*/
void UI_view2d_scale_get_inverse(const View2D *v2d, float *r_x, float *r_y)
{
diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c
index f7ef8c06389..95427e49495 100644
--- a/source/blender/editors/interface/view2d_draw.c
+++ b/source/blender/editors/interface/view2d_draw.c
@@ -326,20 +326,35 @@ static void draw_horizontal_scale_indicators(const ARegion *region,
const float xmin = rect->xmin;
const float xmax = rect->xmax;
- for (uint i = 0; i < steps; i++) {
- const float xpos_view = start + i * distance;
- const float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view);
- char text[32];
- to_string(to_string_data, xpos_view, distance, sizeof(text), text);
- const float text_width = BLF_width(font_id, text, strlen(text));
+ char text[32];
- if (xpos_region - text_width / 2.0f >= xmin && xpos_region + text_width / 2.0f <= xmax) {
- BLF_draw_default_ascii(xpos_region - text_width / 2.0f, ypos, 0.0f, text, sizeof(text));
+ /* Calculate max_label_count and draw_frequency based on largest visible label. */
+ int draw_frequency;
+ {
+ to_string(to_string_data, start, 0, sizeof(text), text);
+ const float left_text_width = BLF_width(font_id, text, strlen(text));
+ to_string(to_string_data, start + steps * distance, 0, sizeof(text), text);
+ const float right_text_width = BLF_width(font_id, text, strlen(text));
+ const float max_text_width = max_ff(left_text_width, right_text_width);
+ const float max_label_count = BLI_rcti_size_x(&v2d->mask) / (max_text_width + 10.0f);
+ draw_frequency = ceil((float)steps / max_label_count);
+ }
+
+ if (draw_frequency != 0) {
+ const int start_index = abs((int)(start / distance)) % draw_frequency;
+ for (uint i = start_index; i < steps; i += draw_frequency) {
+ const float xpos_view = start + i * distance;
+ const float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view);
+ to_string(to_string_data, xpos_view, distance, sizeof(text), text);
+ const float text_width = BLF_width(font_id, text, strlen(text));
+
+ if (xpos_region - text_width / 2.0f >= xmin && xpos_region + text_width / 2.0f <= xmax) {
+ BLF_draw_default_ascii(xpos_region - text_width / 2.0f, ypos, 0.0f, text, sizeof(text));
+ }
}
}
BLF_batch_draw_end();
-
GPU_matrix_pop_projection();
}
@@ -413,11 +428,15 @@ static void view_to_string__frame_number(
}
static void view_to_string__time(
- void *user_data, float v2d_pos, float UNUSED(v2d_step), uint max_len, char *r_str)
+ void *user_data, float v2d_pos, float v2d_step, uint max_len, char *r_str)
{
const Scene *scene = (const Scene *)user_data;
- const int brevity_level = 0;
+ int brevity_level = 0;
+ if (U.timecode_style == USER_TIMECODE_MINIMAL && v2d_step >= FPS) {
+ brevity_level = 1;
+ }
+
BLI_timecode_string_from_time(
r_str, max_len, brevity_level, v2d_pos / (float)FPS, FPS, U.timecode_style);
}
@@ -460,10 +479,11 @@ float UI_view2d_grid_resolution_y__values(const struct View2D *v2d)
/* Line Drawing API
**************************************************/
-void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d)
+void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d, bool display_minor_lines)
{
const uint major_line_distance = view2d_major_step_x__discrete(v2d);
- view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v');
+ view2d_draw_lines(
+ v2d, major_line_distance, display_minor_lines && (major_line_distance > 1), 'v');
}
void UI_view2d_draw_lines_x__values(const View2D *v2d)
@@ -478,21 +498,25 @@ void UI_view2d_draw_lines_y__values(const View2D *v2d)
view2d_draw_lines(v2d, major_line_distance, true, 'h');
}
-void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d, const Scene *scene)
+void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d,
+ const Scene *scene,
+ bool display_minor_lines)
{
const float major_line_distance = view2d_major_step_x__time(v2d, scene);
- view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v');
+ view2d_draw_lines(
+ v2d, major_line_distance, display_minor_lines && (major_line_distance > 1), 'v');
}
void UI_view2d_draw_lines_x__discrete_frames_or_seconds(const View2D *v2d,
const Scene *scene,
- bool display_seconds)
+ bool display_seconds,
+ bool display_minor_lines)
{
if (display_seconds) {
- UI_view2d_draw_lines_x__discrete_time(v2d, scene);
+ UI_view2d_draw_lines_x__discrete_time(v2d, scene, display_minor_lines);
}
else {
- UI_view2d_draw_lines_x__discrete_values(v2d);
+ UI_view2d_draw_lines_x__discrete_values(v2d, display_minor_lines);
}
}
@@ -501,7 +525,7 @@ void UI_view2d_draw_lines_x__frames_or_seconds(const View2D *v2d,
bool display_seconds)
{
if (display_seconds) {
- UI_view2d_draw_lines_x__discrete_time(v2d, scene);
+ UI_view2d_draw_lines_x__discrete_time(v2d, scene, true);
}
else {
UI_view2d_draw_lines_x__values(v2d);
diff --git a/source/blender/editors/io/io_gpencil.h b/source/blender/editors/io/io_gpencil.h
index b347be00412..2359ab4a333 100644
--- a/source/blender/editors/io/io_gpencil.h
+++ b/source/blender/editors/io/io_gpencil.h
@@ -17,16 +17,15 @@
* All rights reserved.
*/
-#ifndef __IO_GPENCIL_H__
-#define __IO_GPENCIL_H__
-
/** \file
* \ingroup editor/io
*/
+#pragma once
+
struct ARegion;
-struct bContext;
struct View3D;
+struct bContext;
struct wmOperatorType;
void WM_OT_gpencil_import_svg(struct wmOperatorType *ot);
@@ -40,5 +39,3 @@ void WM_OT_gpencil_export_pdf(struct wmOperatorType *ot);
struct ARegion *get_invoke_region(struct bContext *C);
struct View3D *get_invoke_view3d(struct bContext *C);
-
-#endif /* __IO_GPENCIL_H__ */
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index 880d27e1615..ad71f4d9da7 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -361,8 +361,10 @@ static bool add_vertex_extrude(const bContext *C,
}
}
- // print_v2("", tangent_point);
- // printf("%d\n", point_index);
+#if 0
+ print_v2("", tangent_point);
+ printf("%d\n", point_index);
+#endif
mask_spline_add_point_at_index(spline, point_index);
diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c
index 0a4fecde6ea..8b78b091fd2 100644
--- a/source/blender/editors/mesh/editmesh_loopcut.c
+++ b/source/blender/editors/mesh/editmesh_loopcut.c
@@ -187,7 +187,8 @@ static void ringsel_finish(bContext *C, wmOperator *op)
const bool is_edge_wire = BM_edge_is_wire(lcd->eed);
const bool is_single = is_edge_wire || !BM_edge_is_any_face_len_test(lcd->eed, 4);
const int seltype = is_edge_wire ? SUBDIV_SELECT_INNER :
- is_single ? SUBDIV_SELECT_NONE : SUBDIV_SELECT_LOOPCUT;
+ is_single ? SUBDIV_SELECT_NONE :
+ SUBDIV_SELECT_LOOPCUT;
/* Enable grid-fill, so that intersecting loop-cut works as one would expect.
* Note though that it will break edge-slide in this specific case.
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index 73b3fb9724e..b2379610f65 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -482,8 +482,34 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name)
return false;
}
+/*********************** General poll ************************/
+
+static bool layers_poll(bContext *C)
+{
+ Object *ob = ED_object_context(C);
+ ID *data = (ob) ? ob->data : NULL;
+ return (ob && !ID_IS_LINKED(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED(data));
+}
+
/*********************** Sculpt Vertex colors operators ************************/
+static bool sculpt_vertex_color_remove_poll(bContext *C)
+{
+ if (!layers_poll(C)) {
+ return false;
+ }
+
+ Object *ob = ED_object_context(C);
+ Mesh *me = ob->data;
+ CustomData *vdata = GET_CD_DATA(me, vdata);
+ const int active = CustomData_get_active_layer(vdata, CD_PROP_COLOR);
+ if (active != -1) {
+ return true;
+ }
+
+ return false;
+}
+
/* NOTE: keep in sync with #ED_mesh_uv_texture_add. */
int ED_mesh_sculpt_color_add(Mesh *me, const char *name, const bool active_set, const bool do_init)
{
@@ -591,11 +617,21 @@ bool ED_mesh_sculpt_color_remove_named(Mesh *me, const char *name)
/*********************** UV texture operators ************************/
-static bool layers_poll(bContext *C)
+static bool uv_texture_remove_poll(bContext *C)
{
+ if (!layers_poll(C)) {
+ return false;
+ }
+
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- return (ob && !ID_IS_LINKED(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED(data));
+ Mesh *me = ob->data;
+ CustomData *ldata = GET_CD_DATA(me, ldata);
+ const int active = CustomData_get_active_layer(ldata, CD_MLOOPUV);
+ if (active != -1) {
+ return true;
+ }
+
+ return false;
}
static int mesh_uv_texture_add_exec(bContext *C, wmOperator *UNUSED(op))
@@ -657,7 +693,7 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot)
ot->idname = "MESH_OT_uv_texture_remove";
/* api callbacks */
- ot->poll = layers_poll;
+ ot->poll = uv_texture_remove_poll;
ot->exec = mesh_uv_texture_remove_exec;
/* flags */
@@ -666,6 +702,23 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot)
/*********************** vertex color operators ************************/
+static bool vertex_color_remove_poll(bContext *C)
+{
+ if (!layers_poll(C)) {
+ return false;
+ }
+
+ Object *ob = ED_object_context(C);
+ Mesh *me = ob->data;
+ CustomData *ldata = GET_CD_DATA(me, ldata);
+ const int active = CustomData_get_active_layer(ldata, CD_MLOOPCOL);
+ if (active != -1) {
+ return true;
+ }
+
+ return false;
+}
+
static int mesh_vertex_color_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -714,7 +767,7 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = mesh_vertex_color_remove_exec;
- ot->poll = layers_poll;
+ ot->poll = vertex_color_remove_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -770,7 +823,7 @@ void MESH_OT_sculpt_vertex_color_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = mesh_sculpt_vertex_color_remove_exec;
- ot->poll = layers_poll;
+ ot->poll = sculpt_vertex_color_remove_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index 34e9a3f45a5..94823b92c44 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -336,7 +336,7 @@ void ED_operatormacros_mesh(void)
ot = WM_operatortype_append_macro("MESH_OT_polybuild_face_at_cursor_move",
"Face at Cursor Move",
- "",
+ NULL,
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "MESH_OT_polybuild_face_at_cursor");
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
@@ -345,7 +345,7 @@ void ED_operatormacros_mesh(void)
ot = WM_operatortype_append_macro("MESH_OT_polybuild_split_at_cursor_move",
"Split at Cursor Move",
- "",
+ NULL,
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "MESH_OT_polybuild_split_at_cursor");
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
@@ -354,7 +354,7 @@ void ED_operatormacros_mesh(void)
ot = WM_operatortype_append_macro("MESH_OT_polybuild_transform_at_cursor_move",
"Transform at Cursor Move",
- "",
+ NULL,
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor");
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
@@ -363,7 +363,7 @@ void ED_operatormacros_mesh(void)
ot = WM_operatortype_append_macro("MESH_OT_polybuild_extrude_at_cursor_move",
"Extrude at Cursor Move",
- "",
+ NULL,
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor");
otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_edges_indiv");
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 4338b043fc9..18f2b58eb65 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -56,7 +56,7 @@ set(SRC
object_ops.c
object_random.c
object_relations.c
- object_remesh.c
+ object_remesh.cc
object_select.c
object_shader_fx.c
object_shapekey.c
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 233f2a65dac..43358f51396 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -416,7 +416,6 @@ static bool is_noncolor_pass(eScenePassType pass_type)
SCE_PASS_VECTOR,
SCE_PASS_INDEXOB,
SCE_PASS_UV,
- SCE_PASS_RAYHITS,
SCE_PASS_INDEXMA);
}
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 2109fe2a822..6251fb799c5 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -123,9 +123,14 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C,
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_ALL_SRC);
- if (data_type == DT_TYPE_MDEFORMVERT) {
- Object *ob_src = CTX_data_active_object(C);
+ Object *ob_src = CTX_data_active_object(C);
+ if (ob_src == NULL) {
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+ return item;
+ }
+ if (data_type == DT_TYPE_MDEFORMVERT && BKE_object_supports_vertex_groups(ob_src)) {
if (BKE_object_pose_armature_get(ob_src)) {
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_SELECT);
@@ -133,67 +138,57 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C,
&item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_DEFORM);
}
- if (ob_src) {
- const bDeformGroup *dg;
- int i;
+ const bDeformGroup *dg;
+ int i;
- RNA_enum_item_add_separator(&item, &totitem);
+ RNA_enum_item_add_separator(&item, &totitem);
- const ListBase *defbase = BKE_object_defgroup_list(ob_src);
- for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) {
- tmp_item.value = i;
- tmp_item.identifier = tmp_item.name = dg->name;
- RNA_enum_item_add(&item, &totitem, &tmp_item);
- }
+ const ListBase *defbase = BKE_object_defgroup_list(ob_src);
+ for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) {
+ tmp_item.value = i;
+ tmp_item.identifier = tmp_item.name = dg->name;
+ RNA_enum_item_add(&item, &totitem, &tmp_item);
}
}
else if (data_type == DT_TYPE_SHAPEKEY) {
/* TODO */
}
else if (data_type == DT_TYPE_UV) {
- Object *ob_src = CTX_data_active_object(C);
-
- if (ob_src) {
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
- Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+ Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src);
- CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH;
- cddata_masks.lmask |= CD_MASK_MLOOPUV;
- Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks);
- int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPUV);
+ CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH;
+ cddata_masks.lmask |= CD_MASK_MLOOPUV;
+ Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks);
+ int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPUV);
- RNA_enum_item_add_separator(&item, &totitem);
+ RNA_enum_item_add_separator(&item, &totitem);
- for (int i = 0; i < num_data; i++) {
- tmp_item.value = i;
- tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(
- &me_eval->ldata, CD_MLOOPUV, i);
- RNA_enum_item_add(&item, &totitem, &tmp_item);
- }
+ for (int i = 0; i < num_data; i++) {
+ tmp_item.value = i;
+ tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(
+ &me_eval->ldata, CD_MLOOPUV, i);
+ RNA_enum_item_add(&item, &totitem, &tmp_item);
}
}
else if (data_type == DT_TYPE_VCOL) {
- Object *ob_src = CTX_data_active_object(C);
-
- if (ob_src) {
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
- Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+ Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src);
- CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH;
- cddata_masks.lmask |= CD_MASK_MLOOPCOL;
- Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks);
- int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPCOL);
+ CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH;
+ cddata_masks.lmask |= CD_MASK_MLOOPCOL;
+ Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks);
+ int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPCOL);
- RNA_enum_item_add_separator(&item, &totitem);
+ RNA_enum_item_add_separator(&item, &totitem);
- for (int i = 0; i < num_data; i++) {
- tmp_item.value = i;
- tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(
- &me_eval->ldata, CD_MLOOPCOL, i);
- RNA_enum_item_add(&item, &totitem, &tmp_item);
- }
+ for (int i = 0; i < num_data; i++) {
+ tmp_item.value = i;
+ tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(
+ &me_eval->ldata, CD_MLOOPCOL, i);
+ RNA_enum_item_add(&item, &totitem, &tmp_item);
}
}
diff --git a/source/blender/editors/object/object_facemap_ops.c b/source/blender/editors/object/object_facemap_ops.c
index 3d4d3c8f622..92f3d28878c 100644
--- a/source/blender/editors/object/object_facemap_ops.c
+++ b/source/blender/editors/object/object_facemap_ops.c
@@ -176,6 +176,21 @@ static bool face_map_supported_edit_mode_poll(bContext *C)
return false;
}
+static bool face_map_supported_remove_poll(bContext *C)
+{
+ if (!face_map_supported_poll(C)) {
+ return false;
+ }
+
+ Object *ob = ED_object_context(C);
+ bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
+ if (fmap) {
+ return true;
+ }
+
+ return false;
+}
+
static int face_map_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -225,7 +240,7 @@ void OBJECT_OT_face_map_remove(struct wmOperatorType *ot)
ot->description = "Remove a face map from the active object";
/* api callbacks */
- ot->poll = face_map_supported_poll;
+ ot->poll = face_map_supported_remove_poll;
ot->exec = face_map_remove_exec;
/* flags */
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 5a3a28b5a3f..6299fdcc7f7 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -303,7 +303,7 @@ void OBJECT_OT_bake(wmOperatorType *ot);
/* object_random.c */
void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot);
-/* object_remesh.c */
+/* object_remesh.cc */
void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot);
void OBJECT_OT_voxel_size_edit(struct wmOperatorType *ot);
void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 7bbca7ea9e6..e9142742d15 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -1923,8 +1923,8 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)(
- RNA_enum_get(op->ptr, "mode"));
+ const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)(RNA_enum_get(
+ op->ptr, "mode"));
multiresModifier_subdivide(object, mmd, subdivide_mode);
ED_object_iter_other(
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.cc
index 05f9980e0ab..82dbc9aaf38 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.cc
@@ -21,22 +21,22 @@
* \ingroup edobj
*/
-#include <ctype.h>
-#include <float.h>
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cctype>
+#include <cfloat>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
-#include "BLI_blenlib.h"
#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
#include "BLT_translation.h"
@@ -99,7 +99,7 @@ static bool object_remesh_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (ob == NULL || ob->data == NULL) {
+ if (ob == nullptr || ob->data == nullptr) {
return false;
}
@@ -131,8 +131,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
- Mesh *mesh = ob->data;
- Mesh *new_mesh;
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
if (mesh->remesh_voxel_size <= 0.0f) {
BKE_report(op->reports, RPT_ERROR, "Voxel remesher cannot run with a voxel size of 0.0");
@@ -151,7 +150,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
isovalue = mesh->remesh_voxel_size * 0.3f;
}
- new_mesh = BKE_mesh_remesh_voxel_to_mesh_nomain(
+ Mesh *new_mesh = BKE_mesh_remesh_voxel(
mesh, mesh->remesh_voxel_size, mesh->remesh_voxel_adaptivity, isovalue);
if (!new_mesh) {
@@ -164,7 +163,9 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
}
if (mesh->flag & ME_REMESH_FIX_POLES && mesh->remesh_voxel_adaptivity <= 0.0f) {
- new_mesh = BKE_mesh_remesh_voxel_fix_poles(new_mesh);
+ Mesh *mesh_fixed_poles = BKE_mesh_remesh_voxel_fix_poles(new_mesh);
+ BKE_id_free(nullptr, new_mesh);
+ new_mesh = mesh_fixed_poles;
BKE_mesh_calc_normals(new_mesh);
}
@@ -193,7 +194,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
if (smooth_normals) {
- BKE_mesh_smooth_flag_set(ob->data, true);
+ BKE_mesh_smooth_flag_set(static_cast<Mesh *>(ob->data), true);
}
if (ob->mode == OB_MODE_SCULPT) {
@@ -201,7 +202,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
ED_sculpt_undo_geometry_end(ob);
}
- BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
+ BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(ob->data), BKE_MESH_BATCH_DIRTY_ALL);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
@@ -233,7 +234,7 @@ void OBJECT_OT_voxel_remesh(wmOperatorType *ot)
#define VOXEL_SIZE_EDIT_MAX_GRIDS_LINES 500
#define VOXEL_SIZE_EDIT_MAX_STR_LEN 20
-typedef struct VoxelSizeEditCustomData {
+struct VoxelSizeEditCustomData {
void *draw_handle;
Object *active_object;
@@ -249,7 +250,7 @@ typedef struct VoxelSizeEditCustomData {
float preview_plane[4][3];
float text_mat[4][4];
-} VoxelSizeEditCustomData;
+};
static void voxel_size_parallel_lines_draw(uint pos3d,
const float initial_co[3],
@@ -300,7 +301,7 @@ static void voxel_size_parallel_lines_draw(uint pos3d,
static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
{
- VoxelSizeEditCustomData *cd = arg;
+ VoxelSizeEditCustomData *cd = static_cast<VoxelSizeEditCustomData *>(arg);
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
@@ -378,19 +379,19 @@ static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar),
static void voxel_size_edit_cancel(bContext *C, wmOperator *op)
{
ARegion *region = CTX_wm_region(C);
- VoxelSizeEditCustomData *cd = op->customdata;
+ VoxelSizeEditCustomData *cd = static_cast<VoxelSizeEditCustomData *>(op->customdata);
ED_region_draw_cb_exit(region->type, cd->draw_handle);
MEM_freeN(op->customdata);
- ED_workspace_status_text(C, NULL);
+ ED_workspace_status_text(C, nullptr);
}
static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
- VoxelSizeEditCustomData *cd = op->customdata;
+ VoxelSizeEditCustomData *cd = static_cast<VoxelSizeEditCustomData *>(op->customdata);
Object *active_object = cd->active_object;
Mesh *mesh = (Mesh *)active_object->data;
@@ -410,11 +411,11 @@ static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *eve
mesh->remesh_voxel_size = cd->voxel_size;
MEM_freeN(op->customdata);
ED_region_tag_redraw(region);
- ED_workspace_status_text(C, NULL);
+ ED_workspace_status_text(C, nullptr);
return OPERATOR_FINISHED;
}
- const float mval[2] = {event->mval[0], event->mval[1]};
+ const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
float d = cd->init_mval[0] - mval[0];
@@ -461,8 +462,8 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
Object *active_object = CTX_data_active_object(C);
Mesh *mesh = (Mesh *)active_object->data;
- VoxelSizeEditCustomData *cd = MEM_callocN(sizeof(VoxelSizeEditCustomData),
- "Voxel Size Edit OP Custom Data");
+ VoxelSizeEditCustomData *cd = (VoxelSizeEditCustomData *)MEM_callocN(
+ sizeof(VoxelSizeEditCustomData), "Voxel Size Edit OP Custom Data");
/* Initial operator Custom Data setup. */
cd->draw_handle = ED_region_draw_cb_activate(
@@ -641,13 +642,13 @@ enum {
QUADRIFLOW_REMESH_FACES,
};
-typedef enum eSymmetryAxes {
+enum eSymmetryAxes {
SYMMETRY_AXES_X = (1 << 0),
SYMMETRY_AXES_Y = (1 << 1),
SYMMETRY_AXES_Z = (1 << 2),
-} eSymmetryAxes;
+};
-typedef struct QuadriFlowJob {
+struct QuadriFlowJob {
/* from wmJob */
struct Object *owner;
short *stop, *do_update;
@@ -668,7 +669,7 @@ typedef struct QuadriFlowJob {
int success;
bool is_nonblocking_job;
-} QuadriFlowJob;
+};
static bool mesh_is_manifold_consistent(Mesh *mesh)
{
@@ -723,7 +724,7 @@ static bool mesh_is_manifold_consistent(Mesh *mesh)
static void quadriflow_free_job(void *customdata)
{
- QuadriFlowJob *qj = customdata;
+ QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
MEM_freeN(qj);
}
@@ -748,7 +749,7 @@ static int quadriflow_break_job(void *customdata)
/** Called by ocean-bake, #wmJob sends notifier. */
static void quadriflow_update_job(void *customdata, float progress, int *cancel)
{
- QuadriFlowJob *qj = customdata;
+ QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
if (quadriflow_break_job(qj)) {
*cancel = 1;
@@ -763,7 +764,7 @@ static void quadriflow_update_job(void *customdata, float progress, int *cancel)
static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes)
{
- MirrorModifierData mmd = {{0}};
+ MirrorModifierData mmd = {{nullptr}};
mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE;
Mesh *mesh_bisect, *mesh_bisect_temp;
@@ -785,19 +786,19 @@ static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes)
mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(
&mmd, mesh_bisect, axis, plane_co, plane_no);
if (mesh_bisect_temp != mesh_bisect) {
- BKE_id_free(NULL, mesh_bisect_temp);
+ BKE_id_free(nullptr, mesh_bisect_temp);
}
}
}
- BKE_id_free(NULL, mesh);
+ BKE_id_free(nullptr, mesh);
return mesh_bisect;
}
static Mesh *remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes)
{
- MirrorModifierData mmd = {{0}};
+ MirrorModifierData mmd = {{nullptr}};
mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE;
Mesh *mesh_mirror, *mesh_mirror_temp;
@@ -814,7 +815,7 @@ static Mesh *remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmet
mesh_mirror_temp = mesh_mirror;
mesh_mirror = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(&mmd, ob, mesh_mirror, axis);
if (mesh_mirror_temp != mesh_mirror) {
- BKE_id_free(NULL, mesh_mirror_temp);
+ BKE_id_free(nullptr, mesh_mirror_temp);
}
}
}
@@ -824,7 +825,7 @@ static Mesh *remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmet
static void quadriflow_start_job(void *customdata, short *stop, short *do_update, float *progress)
{
- QuadriFlowJob *qj = customdata;
+ QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
qj->stop = stop;
qj->do_update = do_update;
@@ -836,7 +837,7 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
}
Object *ob = qj->owner;
- Mesh *mesh = ob->data;
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
Mesh *new_mesh;
Mesh *bisect_mesh;
@@ -853,23 +854,22 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
/* Bisect the input mesh using the paint symmetry settings */
bisect_mesh = remesh_symmetry_bisect(bisect_mesh, qj->symmetry_axes);
- new_mesh = BKE_mesh_remesh_quadriflow_to_mesh_nomain(
- bisect_mesh,
- qj->target_faces,
- qj->seed,
- qj->use_preserve_sharp,
- (qj->use_preserve_boundary || qj->use_mesh_symmetry),
+ new_mesh = BKE_mesh_remesh_quadriflow(bisect_mesh,
+ qj->target_faces,
+ qj->seed,
+ qj->use_preserve_sharp,
+ (qj->use_preserve_boundary || qj->use_mesh_symmetry),
#ifdef USE_MESH_CURVATURE
- qj->use_mesh_curvature,
+ qj->use_mesh_curvature,
#else
- false,
+ false,
#endif
- quadriflow_update_job,
- (void *)qj);
+ quadriflow_update_job,
+ (void *)qj);
- BKE_id_free(NULL, bisect_mesh);
+ BKE_id_free(nullptr, bisect_mesh);
- if (new_mesh == NULL) {
+ if (new_mesh == nullptr) {
*do_update = true;
*stop = 0;
if (qj->success == 1) {
@@ -895,9 +895,9 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
if (qj->smooth_normals) {
if (qj->use_mesh_symmetry) {
- BKE_mesh_calc_normals(ob->data);
+ BKE_mesh_calc_normals(static_cast<Mesh *>(ob->data));
}
- BKE_mesh_smooth_flag_set(ob->data, true);
+ BKE_mesh_smooth_flag_set(static_cast<Mesh *>(ob->data), true);
}
if (ob->mode == OB_MODE_SCULPT) {
@@ -905,7 +905,7 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
ED_sculpt_undo_geometry_end(ob);
}
- BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
+ BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(ob->data), BKE_MESH_BATCH_DIRTY_ALL);
*do_update = true;
*stop = 0;
@@ -913,12 +913,12 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
static void quadriflow_end_job(void *customdata)
{
- QuadriFlowJob *qj = customdata;
+ QuadriFlowJob *qj = (QuadriFlowJob *)customdata;
Object *ob = qj->owner;
if (qj->is_nonblocking_job) {
- WM_set_locked_interface(G_MAIN->wm.first, false);
+ WM_set_locked_interface(static_cast<wmWindowManager *>(G_MAIN->wm.first), false);
}
switch (qj->success) {
@@ -942,7 +942,7 @@ static void quadriflow_end_job(void *customdata)
static int quadriflow_remesh_exec(bContext *C, wmOperator *op)
{
- QuadriFlowJob *job = MEM_mallocN(sizeof(QuadriFlowJob), "QuadriFlowJob");
+ QuadriFlowJob *job = (QuadriFlowJob *)MEM_mallocN(sizeof(QuadriFlowJob), "QuadriFlowJob");
job->owner = CTX_data_active_object(C);
job->scene = CTX_data_scene(C);
@@ -976,7 +976,7 @@ static int quadriflow_remesh_exec(bContext *C, wmOperator *op)
}
else {
job->use_mesh_symmetry = false;
- job->symmetry_axes = 0;
+ job->symmetry_axes = (eSymmetryAxes)0;
}
if (op->flag == 0) {
@@ -1001,7 +1001,7 @@ static int quadriflow_remesh_exec(bContext *C, wmOperator *op)
WM_jobs_customdata_set(wm_job, job, quadriflow_free_job);
WM_jobs_timer(wm_job, 0.1, NC_GEOM | ND_DATA, NC_GEOM | ND_DATA);
- WM_jobs_callbacks(wm_job, quadriflow_start_job, NULL, NULL, quadriflow_end_job);
+ WM_jobs_callbacks(wm_job, quadriflow_start_job, nullptr, nullptr, quadriflow_end_job);
WM_set_locked_interface(CTX_wm_manager(C), true);
@@ -1018,7 +1018,7 @@ static bool quadriflow_check(bContext *C, wmOperator *op)
float area = RNA_float_get(op->ptr, "mesh_area");
if (area < 0.0f) {
Object *ob = CTX_data_active_object(C);
- area = BKE_mesh_calc_area(ob->data);
+ area = BKE_mesh_calc_area(static_cast<const Mesh *>(ob->data));
RNA_float_set(op->ptr, "mesh_area", area);
}
int num_faces;
@@ -1029,7 +1029,7 @@ static bool quadriflow_check(bContext *C, wmOperator *op)
}
else if (mode == QUADRIFLOW_REMESH_RATIO) {
Object *ob = CTX_data_active_object(C);
- Mesh *mesh = ob->data;
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
int num_faces;
float ratio = RNA_float_get(op->ptr, "target_ratio");
@@ -1091,7 +1091,7 @@ static const EnumPropertyItem mode_type_items[] = {
"Edge Length",
"Input target edge length in the new mesh"},
{QUADRIFLOW_REMESH_FACES, "FACES", 0, "Faces", "Input target number of faces in the new mesh"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot)
@@ -1198,7 +1198,7 @@ void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot)
"This property is only used to cache the object area for later calculations",
0.0f,
FLT_MAX);
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, static_cast<PropertyFlag>(PROP_HIDDEN | PROP_SKIP_SAVE));
RNA_def_int(ot->srna,
"seed",
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 4ea599fd30e..f64f95c5322 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -3892,7 +3892,7 @@ static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op)
int fail = 0;
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
- if (obact != ob) {
+ if (obact != ob && BKE_object_supports_vertex_groups(ob)) {
if (ED_vgroup_array_copy(ob, obact)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(CTX_data_main(C));
@@ -3909,8 +3909,8 @@ static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op)
if ((changed_tot == 0 && fail == 0) || fail) {
BKE_reportf(op->reports,
RPT_ERROR,
- "Copy vertex groups to selected: %d done, %d failed (object data must have "
- "matching indices)",
+ "Copy vertex groups to selected: %d done, %d failed (object data must support "
+ "vertex groups and have matching indices)",
changed_tot,
fail);
}
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index f2bbd6d5084..5a629058c81 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -368,7 +368,7 @@ static PTCacheEdit *pe_get_current(Depsgraph *depsgraph, Scene *scene, Object *o
else if (pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) {
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
pset->flag |= PE_FADE_TIME;
- // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
+ /* Nice to have but doesn't work: `pset->brushtype = PE_BRUSH_COMB;`. */
PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL);
}
edit = pid->cache->edit;
@@ -377,7 +377,7 @@ static PTCacheEdit *pe_get_current(Depsgraph *depsgraph, Scene *scene, Object *o
else if (pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) {
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
pset->flag |= PE_FADE_TIME;
- // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
+ /* Nice to have but doesn't work: `pset->brushtype = PE_BRUSH_COMB;`. */
PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL);
}
edit = pid->cache->edit;
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 387d10d538b..2668846284d 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -1331,7 +1331,12 @@ static int duplicate_particle_systems_exec(bContext *C, wmOperator *op)
const bool duplicate_settings = RNA_boolean_get(op->ptr, "use_duplicate_settings");
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
+ /* Context pointer is only valid in the Properties Editor. */
ParticleSystem *psys = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data;
+ if (psys == NULL) {
+ psys = psys_get_current(ob);
+ }
+
copy_particle_systems_to_object(
C, scene, ob, psys, ob, PAR_COPY_SPACE_OBJECT, duplicate_settings);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 17aaa5aa79d..d3307ebf274 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -767,7 +767,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
sizey = (scene->r.size * scene->r.ysch) / 100;
/* corrects render size with actual size, not every card supports non-power-of-two dimensions */
- DRW_opengl_context_enable(); /* Offscreen creation needs to be done in DRW context. */
+ DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */
ofs = GPU_offscreen_create(sizex, sizey, true, true, err_out);
DRW_opengl_context_disable();
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 7f7ee45a803..fe1e850dcba 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -54,7 +54,9 @@
#include "DNA_space_types.h"
#include "DNA_world_types.h"
+#include "BKE_animsys.h"
#include "BKE_appdir.h"
+#include "BKE_armature.h"
#include "BKE_brush.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
@@ -93,6 +95,7 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "ED_armature.h"
#include "ED_datafiles.h"
#include "ED_render.h"
#include "ED_screen.h"
@@ -151,6 +154,10 @@ typedef struct IconPreview {
void *owner;
ID *id, *id_copy; /* May be NULL! (see ICON_TYPE_PREVIEW case in #ui_icon_ensure_deferred()) */
ListBase sizes;
+
+ /* May be NULL, is used for rendering IDs that require some other object for it to be applied on
+ * before the ID can be represented as an image, for example when rendering an Action. */
+ struct Object *active_object;
} IconPreview;
/** \} */
@@ -227,7 +234,7 @@ static Scene *preview_get_scene(Main *pr_main)
return pr_main->scenes.first;
}
-static const char *preview_collection_name(const char pr_type)
+static const char *preview_collection_name(const ePreviewType pr_type)
{
switch (pr_type) {
case MA_FLAT:
@@ -258,10 +265,7 @@ static const char *preview_collection_name(const char pr_type)
}
}
-static void set_preview_visibility(Scene *scene,
- ViewLayer *view_layer,
- char pr_type,
- int pr_method)
+static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePreviewType pr_type)
{
/* Set appropriate layer as visible. */
LayerCollection *lc = view_layer->layer_collections.first;
@@ -275,7 +279,11 @@ static void set_preview_visibility(Scene *scene,
lc->collection->flag |= COLLECTION_RESTRICT_RENDER;
}
}
+}
+static void switch_preview_floor_visibility(ViewLayer *view_layer,
+ const ePreviewRenderMethod pr_method)
+{
/* Hide floor for icon renders. */
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (STREQ(base->object->id.name + 2, "Floor")) {
@@ -287,7 +295,15 @@ static void set_preview_visibility(Scene *scene,
}
}
}
+}
+static void set_preview_visibility(Scene *scene,
+ ViewLayer *view_layer,
+ const ePreviewType pr_type,
+ const ePreviewRenderMethod pr_method)
+{
+ switch_preview_collection_visibilty(view_layer, pr_type);
+ switch_preview_floor_visibility(view_layer, pr_method);
BKE_layer_collection_sync(scene, view_layer);
}
@@ -341,6 +357,38 @@ static ID *duplicate_ids(ID *id, const bool allow_failure)
}
}
+static World *preview_get_world(Main *pr_main)
+{
+ World *result = NULL;
+ const char *world_name = "World";
+ result = BLI_findstring(&pr_main->worlds, world_name, offsetof(ID, name) + 2);
+
+ /* No world found return first world. */
+ if (result == NULL) {
+ result = pr_main->worlds.first;
+ }
+
+ BLI_assert_msg(result, "Preview file has no world.");
+ return result;
+}
+
+static void preview_sync_exposure(World *dst, const World *src)
+{
+ BLI_assert(dst);
+ BLI_assert(src);
+ dst->exp = src->exp;
+ dst->range = src->range;
+}
+
+static World *preview_prepare_world(Main *pr_main, const World *world)
+{
+ World *result = preview_get_world(pr_main);
+ if (world) {
+ preview_sync_exposure(result, world);
+ }
+ return result;
+}
+
/* call this with a pointer to initialize preview scene */
/* call this with NULL to restore assigned ID pointers in preview scene */
static Scene *preview_prepare_scene(
@@ -361,13 +409,7 @@ static Scene *preview_prepare_scene(
/* this flag tells render to not execute depsgraph or ipos etc */
sce->r.scemode |= R_BUTS_PREVIEW;
- /* set world always back, is used now */
- sce->world = pr_main->worlds.first;
- /* now: exposure copy */
- if (scene->world) {
- sce->world->exp = scene->world->exp;
- sce->world->range = scene->world->range;
- }
+ BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine));
sce->r.color_mgt_flag = scene->r.color_mgt_flag;
BKE_color_managed_display_settings_copy(&sce->display_settings, &scene->display_settings);
@@ -393,13 +435,13 @@ static Scene *preview_prepare_scene(
sce->r.cfra = scene->r.cfra;
+ /* Setup the world. */
+ sce->world = preview_prepare_world(pr_main, scene->world);
+
if (id_type == ID_TE) {
/* Texture is not actually rendered with engine, just set dummy value. */
BLI_strncpy(sce->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(sce->r.engine));
}
- else {
- BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine));
- }
if (id_type == ID_MA) {
Material *mat = NULL, *origmat = (Material *)id;
@@ -425,14 +467,12 @@ static Scene *preview_prepare_scene(
sce->world->horb = 0.05f;
}
- if (sp->pr_method == PR_ICON_RENDER && sp->pr_main == G_pr_main_grease_pencil) {
- /* For grease pencil, always use sphere for icon renders. */
- set_preview_visibility(sce, view_layer, MA_SPHERE_A, sp->pr_method);
- }
- else {
- /* Use specified preview shape for both preview panel and icon previews. */
- set_preview_visibility(sce, view_layer, mat->pr_type, sp->pr_method);
- }
+ /* For grease pencil, always use sphere for icon renders. */
+ const ePreviewType preview_type = (sp->pr_method == PR_ICON_RENDER &&
+ sp->pr_main == G_pr_main_grease_pencil) ?
+ MA_SPHERE_A :
+ mat->pr_type;
+ set_preview_visibility(sce, view_layer, preview_type, sp->pr_method);
if (sp->pr_method != PR_ICON_RENDER) {
if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) {
@@ -684,9 +724,11 @@ void ED_preview_draw(const bContext *C, void *idp, void *parentp, void *slotp, r
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). */
+ /* Copy of the object to create the preview for. The copy is for thread safety (and to insert
+ * it into an own main). */
Object *object;
+ /* Current frame. */
+ int cfra;
int sizex;
int sizey;
};
@@ -720,6 +762,10 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe
Depsgraph **r_depsgraph)
{
Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene");
+ /* Preview need to be in the current frame to get a thumbnail similar of what
+ * viewport displays. */
+ CFRA = preview_data->cfra;
+
ViewLayer *view_layer = scene->view_layers.first;
Depsgraph *depsgraph = DEG_graph_new(
preview_data->pr_main, scene, view_layer, DAG_EVAL_VIEWPORT);
@@ -764,6 +810,7 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
.pr_main = preview_main,
/* Act on a copy. */
.object = (Object *)preview->id_copy,
+ .cfra = preview->scene->r.cfra,
.sizex = preview_sized->sizex,
.sizey = preview_sized->sizey,
};
@@ -813,9 +860,50 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
/** \name Action Preview
* \{ */
-/* Render a pose. It is assumed that the pose has already been applied and that the scene camera is
- * capturing the pose. In other words, this function just renders from the scene camera without
- * evaluating the Action stored in preview->id. */
+static struct PoseBackup *action_preview_render_prepare(IconPreview *preview)
+{
+ Object *object = preview->active_object;
+ if (object == NULL) {
+ WM_report(RPT_WARNING, "No active object, unable to apply the Action before rendering");
+ return NULL;
+ }
+ if (object->pose == NULL) {
+ WM_reportf(RPT_WARNING,
+ "Object %s has no pose, unable to apply the Action before rendering",
+ object->id.name + 2);
+ return NULL;
+ }
+
+ /* Create a backup of the current pose. */
+ struct bAction *action = (struct bAction *)preview->id;
+ struct PoseBackup *pose_backup = ED_pose_backup_create_all_bones(object, action);
+
+ /* Apply the Action as pose, so that it can be rendered. This assumes the Action represents a
+ * single pose, and that thus the evaluation time doesn't matter. */
+ AnimationEvalContext anim_eval_context = {preview->depsgraph, 0.0f};
+ BKE_pose_apply_action_all_bones(object, action, &anim_eval_context);
+
+ /* Force evaluation of the new pose, before the preview is rendered. */
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+ DEG_evaluate_on_refresh(preview->depsgraph);
+
+ return pose_backup;
+}
+
+static void action_preview_render_cleanup(IconPreview *preview, struct PoseBackup *pose_backup)
+{
+ if (pose_backup == NULL) {
+ return;
+ }
+ ED_pose_backup_restore(pose_backup);
+ ED_pose_backup_free(pose_backup);
+
+ DEG_id_tag_update(&preview->active_object->id, ID_RECALC_GEOMETRY);
+}
+
+/* Render a pose from the scene camera. It is assumed that the scene camera is
+ * capturing the pose. The pose is applied temporarily to the current object
+ * before rendering. */
static void action_preview_render(IconPreview *preview, IconPreviewSize *preview_sized)
{
char err_out[256] = "";
@@ -827,6 +915,9 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview
BLI_assert(depsgraph != NULL);
BLI_assert(preview->scene == DEG_get_input_scene(depsgraph));
+ /* Apply the pose before getting the evaluated scene, so that the new pose is evaluated. */
+ struct PoseBackup *pose_backup = action_preview_render_prepare(preview);
+
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *camera_eval = scene_eval->camera;
if (camera_eval == NULL) {
@@ -850,6 +941,8 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview
NULL,
err_out);
+ action_preview_render_cleanup(preview, pose_backup);
+
if (err_out[0] != '\0') {
printf("Error rendering Action %s preview: %s\n", preview->id->name + 2, err_out);
}
@@ -1255,8 +1348,9 @@ static void icon_copy_rect(ImBuf *ibuf, uint w, uint h, uint *rect)
scaledy = (float)h;
}
- ex = (short)scaledx;
- ey = (short)scaledy;
+ /* Scaling down must never assign zero width/height, see: T89868. */
+ ex = MAX2(1, (short)scaledx);
+ ey = MAX2(1, (short)scaledy);
dx = (w - ex) / 2;
dy = (h - ey) / 2;
@@ -1497,8 +1591,8 @@ static void icon_preview_startjob_all_sizes(void *customdata,
/* check_engine_supports_preview() checks whether the engine supports "preview mode" (think:
* Material Preview). This check is only relevant when the render function called below is
- * going to use such a mode. Object and Action render functions use Solid mode, though, so they
- * can skip this test. */
+ * going to use such a mode. Object and Action render functions use Solid mode, though, so
+ * they can skip this test. */
/* TODO: Decouple the ID-type-specific render functions from this function, so that it's not
* necessary to know here what happens inside lower-level functions. */
const bool use_solid_render_mode = (ip->id != NULL) && ELEM(GS(ip->id->name), ID_OB, ID_AC);
@@ -1625,6 +1719,7 @@ void ED_preview_icon_render(
/* Control isn't given back to the caller until the preview is done. So we don't need to copy
* the ID to avoid thread races. */
ip.id_copy = duplicate_ids(id, true);
+ ip.active_object = CTX_data_active_object(C);
icon_preview_add_size(&ip, rect, sizex, sizey);
@@ -1666,6 +1761,7 @@ void ED_preview_icon_job(
ip->bmain = CTX_data_main(C);
ip->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ip->scene = DEG_get_input_scene(ip->depsgraph);
+ ip->active_object = CTX_data_active_object(C);
ip->owner = owner;
ip->id = id;
ip->id_copy = duplicate_ids(id, false);
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index d2b1ebdad78..8a3d8f9636b 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -56,6 +56,7 @@
#include "BKE_linestyle.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -1043,6 +1044,10 @@ static int view_layer_add_aov_exec(bContext *C, wmOperator *UNUSED(op))
engine = NULL;
}
+ if (scene->nodetree) {
+ ntreeCompositUpdateRLayers(scene->nodetree);
+ }
+
DEG_id_tag_update(&scene->id, 0);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
@@ -1091,6 +1096,10 @@ static int view_layer_remove_aov_exec(bContext *C, wmOperator *UNUSED(op))
engine = NULL;
}
+ if (scene->nodetree) {
+ ntreeCompositUpdateRLayers(scene->nodetree);
+ }
+
DEG_id_tag_update(&scene->id, 0);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 80b5623b9c3..fb9d11feb63 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -65,7 +65,7 @@
/***************************** Render Engines ********************************/
-/* Update 3D viewport render or draw engine on changes to the scene or view settings . */
+/* Update 3D viewport render or draw engine on changes to the scene or view settings. */
void ED_render_view3d_update(Depsgraph *depsgraph,
wmWindow *window,
ScrArea *area,
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index f4109981aae..c351ade9954 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -161,6 +161,12 @@ void ED_region_do_listen(wmRegionListenerParams *params)
if (region->type && region->type->listener) {
region->type->listener(params);
}
+
+ LISTBASE_FOREACH (uiList *, list, &region->ui_lists) {
+ if (list->type && list->type->listener) {
+ list->type->listener(list, params);
+ }
+ }
}
/* only exported for WM */
@@ -3018,6 +3024,8 @@ void ED_region_panels_layout_ex(const bContext *C,
y = -y;
}
+ UI_blocklist_update_view_for_buttons(C, &region->uiblocks);
+
if (update_tot_size) {
/* this also changes the 'cur' */
UI_view2d_totRect_set(v2d, x, y);
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index d50962a56a9..8123d8bb104 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -36,6 +36,7 @@
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_workspace_types.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
@@ -111,6 +112,8 @@ const char *screen_context_dir[] = {
"selected_editable_fcurves",
"active_editable_fcurve",
"selected_editable_keyframes",
+ "ui_list",
+ "asset_library",
NULL,
};
@@ -1024,6 +1027,23 @@ static eContextResult screen_ctx_selected_editable_keyframes(const bContext *C,
return CTX_RESULT_NO_DATA;
}
+static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataResult *result)
+{
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ CTX_data_pointer_set(
+ result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library);
+ return CTX_RESULT_OK;
+}
+
+static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult *result)
+{
+ wmWindow *win = CTX_wm_window(C);
+ ARegion *region = CTX_wm_region(C);
+ uiList *list = UI_list_find_mouse_over(region, win->eventstate);
+ CTX_data_pointer_set(result, NULL, &RNA_UIList, list);
+ return CTX_RESULT_OK;
+}
+
/* Registry of context callback functions. */
typedef eContextResult (*context_callback)(const bContext *C, bContextDataResult *result);
@@ -1098,6 +1118,8 @@ static void ensure_ed_screen_context_functions(void)
register_context_function("selected_visible_fcurves", screen_ctx_selected_visible_fcurves);
register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve);
register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes);
+ register_context_function("asset_library", screen_ctx_asset_library);
+ register_context_function("ui_list", screen_ctx_ui_list);
}
/* Entry point for the screen context. */
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index ffeaf514642..8d7d742e44b 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -70,8 +70,9 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
#include "ED_clip.h"
+#include "ED_fileselect.h"
#include "ED_image.h"
-#include "ED_keyframes_draw.h"
+#include "ED_keyframes_keylist.h"
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_screen.h"
@@ -274,11 +275,37 @@ bool ED_operator_outliner_active_no_editobject(bContext *C)
return false;
}
+/**
+ * \note Will return true for file spaces in either file or asset browsing mode! See
+ * #ED_operator_file_browsing_active() (file browsing only) and
+ * #ED_operator_asset_browsing_active() (asset browsing only).
+ */
bool ED_operator_file_active(bContext *C)
{
return ed_spacetype_test(C, SPACE_FILE);
}
+/**
+ * \note Will only return true if the file space is in file browsing mode, not asset browsing! See
+ * #ED_operator_file_active() (file or asset browsing) and
+ * #ED_operator_asset_browsing_active() (asset browsing only).
+ */
+bool ED_operator_file_browsing_active(bContext *C)
+{
+ if (ed_spacetype_test(C, SPACE_FILE)) {
+ return ED_fileselect_is_file_browser(CTX_wm_space_file(C));
+ }
+ return false;
+}
+
+bool ED_operator_asset_browsing_active(bContext *C)
+{
+ if (ed_spacetype_test(C, SPACE_FILE)) {
+ return ED_fileselect_is_asset_browser(CTX_wm_space_file(C));
+ }
+ return false;
+}
+
bool ED_operator_spreadsheet_active(bContext *C)
{
return ed_spacetype_test(C, SPACE_SPREADSHEET);
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index ffa6f6ac962..23b90171a1d 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -784,11 +784,10 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
bool do_random = false;
bool do_partial_update = false;
- bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) &&
- ((ELEM(brush->gradient_stroke_mode,
- BRUSH_GRADIENT_SPACING_REPEAT,
- BRUSH_GRADIENT_SPACING_CLAMP)) ||
- (cache->last_pressure != pressure)));
+ bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) && (ELEM(brush->gradient_stroke_mode,
+ BRUSH_GRADIENT_SPACING_REPEAT,
+ BRUSH_GRADIENT_SPACING_CLAMP) ||
+ (cache->last_pressure != pressure)));
float tex_rotation = -brush->mtex.rot;
float mask_rotation = -brush->mask_mtex.rot;
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index bd05d309421..a8ad6ab1b74 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -270,7 +270,7 @@ typedef struct ProjPaintState {
float stencil_value;
/* projection painting only */
- /** for multithreading, the first item is sometimes used for non threaded cases too. */
+ /** For multi-threading, the first item is sometimes used for non threaded cases too. */
MemArena *arena_mt[BLENDER_MAX_THREADS];
/** screen sized 2D array, each pixel has a linked list of ProjPixel's */
LinkNode **bucketRect;
@@ -375,7 +375,7 @@ typedef struct ProjPaintState {
/* -------------------------------------------------------------------- */
/* Vars shared between multiple views (keep last) */
/**
- * This data is owned by ``ProjStrokeHandle.ps_views[0]``,
+ * This data is owned by `ProjStrokeHandle.ps_views[0]`,
* all other views re-use the data.
*/
@@ -4449,7 +4449,7 @@ static void project_paint_begin(const bContext *C,
ProjPaintFaceLookup face_lookup;
const MLoopUV *mloopuv_base = NULL;
- /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */
+ /* At the moment this is just ps->arena_mt[0], but use this to show were not multi-threading. */
MemArena *arena;
const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush);
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index de9511bab6f..4b49bf2cefb 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -10,7 +10,7 @@
* 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,
+ * 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 Blender Foundation.
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
index c3977b28178..936ebb7e8f7 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -10,7 +10,7 @@
* 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,
+ * 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 Blender Foundation.
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index 0d5b197ae93..59d2063ea84 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -51,8 +51,8 @@
#include "ED_anim_api.h"
#include "ED_gpencil.h"
-#include "ED_keyframes_draw.h"
#include "ED_keyframes_edit.h"
+#include "ED_keyframes_keylist.h"
#include "ED_markers.h"
#include "ED_mask.h"
#include "ED_screen.h"
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 28482faf6e3..5e5143723a6 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -196,7 +196,8 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_ortho(v2d);
/* time grid */
- UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, saction->flag & SACTION_DRAWTIME);
+ UI_view2d_draw_lines_x__discrete_frames_or_seconds(
+ v2d, scene, saction->flag & SACTION_DRAWTIME, true);
ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW);
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index adb824b8934..b3b3eafb6e7 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -129,6 +129,8 @@ void ED_spacetypes_init(void)
ED_screen_user_menu_register();
+ ED_uilisttypes_ui();
+
/* Gizmo types. */
ED_gizmotypes_button_2d();
ED_gizmotypes_dial_3d();
diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h
index 7564fa4b930..9cb363ff0c9 100644
--- a/source/blender/editors/space_buttons/buttons_intern.h
+++ b/source/blender/editors/space_buttons/buttons_intern.h
@@ -34,8 +34,8 @@ struct Tex;
struct bContext;
struct bContextDataResult;
struct bNode;
-struct bNodeTree;
struct bNodeSocket;
+struct bNodeTree;
struct wmOperatorType;
struct SpaceProperties_Runtime {
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index 326c221a2e3..aef3385f2dc 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -1112,7 +1112,7 @@ static void dopesheet_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_ortho(v2d);
/* time grid */
- UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, sc->flag & SC_SHOW_SECONDS);
+ UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, sc->flag & SC_SHOW_SECONDS, true);
/* data... */
clip_draw_dopesheet_main(sc, region, scene);
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index faa4b3cc9cc..37a56816677 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -185,9 +185,8 @@ static void file_draw_icon(const SpaceFile *sfile,
BLI_assert(asset_params != NULL);
UI_but_drag_set_asset(but,
- file->name,
+ &(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
- file->blentype,
asset_params->import_type,
icon,
preview_image,
@@ -500,9 +499,8 @@ static void file_draw_preview(const SpaceFile *sfile,
BLI_assert(asset_params != NULL);
UI_but_drag_set_asset(but,
- file->name,
+ &(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
- file->blentype,
asset_params->import_type,
icon,
imb,
@@ -1105,7 +1103,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
return false;
}
/* Check if the library exists. */
- if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) ||
+ if ((asset_params->asset_library.type == ASSET_LIBRARY_LOCAL) ||
filelist_is_dir(sfile->files, asset_params->base_params.dir)) {
return false;
}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index 0bbed65671c..17749d36418 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -62,7 +62,6 @@ void FILE_OT_bookmark_cleanup(struct wmOperatorType *ot);
void FILE_OT_bookmark_move(struct wmOperatorType *ot);
void FILE_OT_reset_recent(wmOperatorType *ot);
void FILE_OT_hidedot(struct wmOperatorType *ot);
-void FILE_OT_associate_blend(struct wmOperatorType *ot);
void FILE_OT_execute(struct wmOperatorType *ot);
void FILE_OT_mouse_execute(struct wmOperatorType *ot);
void FILE_OT_cancel(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 995383d9d0e..616e7fe51db 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -513,6 +513,7 @@ void FILE_OT_select_box(wmOperatorType *ot)
ot->invoke = WM_gesture_box_invoke;
ot->exec = file_box_select_exec;
ot->modal = file_box_select_modal;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
ot->cancel = WM_gesture_box_cancel;
@@ -606,6 +607,7 @@ void FILE_OT_select(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_select_invoke;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
/* properties */
@@ -864,6 +866,7 @@ void FILE_OT_select_walk(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_walk_select_invoke;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
/* properties */
@@ -951,6 +954,7 @@ void FILE_OT_select_all(wmOperatorType *ot)
/* api callbacks */
ot->exec = file_select_all_exec;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
/* properties */
@@ -1003,6 +1007,7 @@ void FILE_OT_view_selected(wmOperatorType *ot)
/* api callbacks */
ot->exec = file_view_selected_exec;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
}
@@ -1014,7 +1019,6 @@ void FILE_OT_view_selected(wmOperatorType *ot)
/* Note we could get rid of this one, but it's used by some addon so...
* Does not hurt keeping it around for now. */
-/* TODO: disallow bookmark editing in assets mode? */
static int bookmark_select_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1047,7 +1051,8 @@ void FILE_OT_select_bookmark(wmOperatorType *ot)
/* api callbacks */
ot->exec = bookmark_select_exec;
- ot->poll = ED_operator_file_active;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
/* properties */
prop = RNA_def_string(ot->srna, "dir", NULL, FILE_MAXDIR, "Directory", "");
@@ -1093,7 +1098,8 @@ void FILE_OT_bookmark_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = bookmark_add_exec;
- ot->poll = ED_operator_file_active;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
}
/** \} */
@@ -1147,7 +1153,8 @@ void FILE_OT_bookmark_delete(wmOperatorType *ot)
/* api callbacks */
ot->exec = bookmark_delete_exec;
- ot->poll = ED_operator_file_active;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
/* properties */
prop = RNA_def_int(ot->srna, "index", -1, -1, 20000, "Index", "", -1, 20000);
@@ -1205,7 +1212,8 @@ void FILE_OT_bookmark_cleanup(wmOperatorType *ot)
/* api callbacks */
ot->exec = bookmark_cleanup_exec;
- ot->poll = ED_operator_file_active;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
/* properties */
}
@@ -1293,8 +1301,9 @@ void FILE_OT_bookmark_move(wmOperatorType *ot)
ot->description = "Move the active bookmark up/down in the list";
/* api callbacks */
- ot->poll = ED_operator_file_active;
ot->exec = bookmark_move_exec;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
/* flags */
ot->flag = OPTYPE_REGISTER; /* No undo! */
@@ -1341,7 +1350,8 @@ void FILE_OT_reset_recent(wmOperatorType *ot)
/* api callbacks */
ot->exec = reset_recent_exec;
- ot->poll = ED_operator_file_active;
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
}
/** \} */
@@ -1414,6 +1424,7 @@ void FILE_OT_highlight(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_highlight_invoke;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
}
@@ -1465,6 +1476,7 @@ void FILE_OT_sort_column_ui_context(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_column_sort_ui_context_invoke;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
ot->flag = OPTYPE_INTERNAL;
@@ -1478,7 +1490,7 @@ void FILE_OT_sort_column_ui_context(wmOperatorType *ot)
static bool file_operator_poll(bContext *C)
{
- bool poll = ED_operator_file_active(C);
+ bool poll = ED_operator_file_browsing_active(C);
SpaceFile *sfile = CTX_wm_space_file(C);
if (!sfile || !sfile->op) {
@@ -1801,7 +1813,7 @@ void FILE_OT_execute(struct wmOperatorType *ot)
*
* Avoid using #file_operator_poll since this is also used for entering directories
* which is used even when the file manager doesn't have an operator. */
- ot->poll = ED_operator_file_active;
+ ot->poll = ED_operator_file_browsing_active;
}
/**
@@ -1856,7 +1868,7 @@ void FILE_OT_mouse_execute(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_execute_mouse_invoke;
- ot->poll = ED_operator_file_active;
+ ot->poll = ED_operator_file_browsing_active;
ot->flag = OPTYPE_INTERNAL;
}
@@ -1895,6 +1907,7 @@ void FILE_OT_refresh(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_refresh_exec;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
}
@@ -1935,7 +1948,8 @@ void FILE_OT_parent(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_parent_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
}
/** \} */
@@ -1970,7 +1984,8 @@ void FILE_OT_previous(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_previous_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
}
/** \} */
@@ -2006,7 +2021,8 @@ void FILE_OT_next(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_next_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
}
/** \} */
@@ -2197,7 +2213,7 @@ void FILE_OT_smoothscroll(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_smoothscroll_invoke;
-
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
}
@@ -2241,7 +2257,8 @@ void FILE_OT_filepath_drop(wmOperatorType *ot)
ot->idname = "FILE_OT_filepath_drop";
ot->exec = filepath_drop_exec;
- ot->poll = WM_operator_winactive;
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
RNA_def_string_file_path(ot->srna, "filepath", "Path", FILE_MAX, "", "");
}
@@ -2340,7 +2357,9 @@ static int file_directory_new_exec(bContext *C, wmOperator *op)
/* If we don't enter the directory directly, remember file to jump into editing. */
if (do_diropen == false) {
- BLI_assert(params->rename_id == NULL || !"File rename handling should immediately clear rename_id when done, because otherwise it will keep taking precedence over renamefile.");
+ BLI_assert_msg(params->rename_id == NULL,
+ "File rename handling should immediately clear rename_id when done, "
+ "because otherwise it will keep taking precedence over renamefile.");
BLI_strncpy(params->renamefile, name, FILE_MAXFILE);
rename_flag = FILE_PARAMS_RENAME_PENDING;
}
@@ -2373,7 +2392,8 @@ void FILE_OT_directory_new(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_operator_confirm_or_exec;
ot->exec = file_directory_new_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
prop = RNA_def_string_dir_path(
ot->srna, "directory", NULL, FILE_MAX, "Directory", "Name of new directory");
@@ -2616,44 +2636,8 @@ void FILE_OT_hidedot(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_hidedot_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Associate File Type Operator (Windows only)
- * \{ */
-
-static int associate_blend_exec(bContext *UNUSED(C), wmOperator *op)
-{
-#ifdef WIN32
- WM_cursor_wait(true);
- if (BLI_windows_register_blend_extension(true)) {
- BKE_report(op->reports, RPT_INFO, "File association registered");
- WM_cursor_wait(false);
- return OPERATOR_FINISHED;
- }
- else {
- BKE_report(op->reports, RPT_ERROR, "Unable to register file association");
- WM_cursor_wait(false);
- return OPERATOR_CANCELLED;
- }
-#else
- BKE_report(op->reports, RPT_WARNING, "Operator Not supported");
- return OPERATOR_CANCELLED;
-#endif
-}
-
-void FILE_OT_associate_blend(struct wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Register File Association";
- ot->description = "Use this installation for .blend files and to display thumbnails";
- ot->idname = "FILE_OT_associate_blend";
-
- /* api callbacks */
- ot->exec = associate_blend_exec;
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
}
/** \} */
@@ -2666,7 +2650,8 @@ static bool file_filenum_poll(bContext *C)
{
SpaceFile *sfile = CTX_wm_space_file(C);
- if (!ED_operator_file_active(C)) {
+ /* File browsing only operator (not asset browsing). */
+ if (!ED_operator_file_browsing_active(C)) {
return false;
}
@@ -2793,11 +2778,6 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
-static bool file_rename_poll(bContext *C)
-{
- return ED_operator_file_active(C) && !ED_fileselect_is_asset_browser(CTX_wm_space_file(C));
-}
-
void FILE_OT_rename(struct wmOperatorType *ot)
{
/* identifiers */
@@ -2808,7 +2788,8 @@ void FILE_OT_rename(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_rename_invoke;
ot->exec = file_rename_exec;
- ot->poll = file_rename_poll;
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
}
/** \} */
@@ -2819,33 +2800,30 @@ void FILE_OT_rename(struct wmOperatorType *ot)
static bool file_delete_poll(bContext *C)
{
- bool poll = ED_operator_file_active(C);
+ if (!ED_operator_file_browsing_active(C)) {
+ return false;
+ }
+
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ if (!sfile || !params) {
+ return false;
+ }
- if (sfile && params) {
- char dir[FILE_MAX_LIBEXTRA];
- int numfiles = filelist_files_ensure(sfile->files);
- int i;
- int num_selected = 0;
+ char dir[FILE_MAX_LIBEXTRA];
+ if (filelist_islibrary(sfile->files, dir, NULL)) {
+ return false;
+ }
- if (filelist_islibrary(sfile->files, dir, NULL)) {
- poll = 0;
- }
- for (i = 0; i < numfiles; i++) {
- if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
- num_selected++;
- }
- }
- if (num_selected <= 0) {
- poll = 0;
+ int numfiles = filelist_files_ensure(sfile->files);
+ for (int i = 0; i < numfiles; i++) {
+ if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
+ /* Has a selected file -> the operator can run. */
+ return true;
}
}
- else {
- poll = 0;
- }
- return poll;
+ return false;
}
static bool file_delete_single(const FileSelectParams *params,
@@ -2958,6 +2936,7 @@ void FILE_OT_start_filter(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_start_filter_exec;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 0e15538e03b..630c9aed157 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -37,6 +37,8 @@
#endif
#include "MEM_guardedalloc.h"
+#include "BLF_api.h"
+
#include "BLI_blenlib.h"
#include "BLI_fileops.h"
#include "BLI_fileops_types.h"
@@ -334,6 +336,7 @@ typedef struct FileListEntryCache {
/* Previews handling. */
TaskPool *previews_pool;
ThreadQueue *previews_done;
+ size_t previews_todo_count;
} FileListEntryCache;
/* FileListCache.flags */
@@ -381,7 +384,7 @@ typedef struct FileList {
eFileSelectType type;
/* The library this list was created for. Stored here so we know when to re-read. */
- FileSelectAssetLibraryUID *asset_library;
+ AssetLibraryReference *asset_library;
short flags;
@@ -1044,13 +1047,13 @@ void filelist_setfilter_options(FileList *filelist,
* Checks two libraries for equality.
* \return True if the libraries match.
*/
-static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *library_a,
- const FileSelectAssetLibraryUID *library_b)
+static bool filelist_compare_asset_libraries(const AssetLibraryReference *library_a,
+ const AssetLibraryReference *library_b)
{
if (library_a->type != library_b->type) {
return false;
}
- if (library_a->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ if (library_a->type == ASSET_LIBRARY_CUSTOM) {
/* Don't only check the index, also check that it's valid. */
bUserAssetLibrary *library_ptr_a = BKE_preferences_asset_library_find_from_index(
&U, library_a->custom_library_index);
@@ -1064,7 +1067,7 @@ static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *li
/**
* \param asset_library: May be NULL to unset the library.
*/
-void filelist_setlibrary(FileList *filelist, const FileSelectAssetLibraryUID *asset_library)
+void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library)
{
/* Unset if needed. */
if (!asset_library) {
@@ -1153,7 +1156,7 @@ ImBuf *filelist_file_getimage(const FileDirEntry *file)
return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL;
}
-static ImBuf *filelist_geticon_image_ex(FileDirEntry *file)
+ImBuf *filelist_geticon_image_ex(const FileDirEntry *file)
{
ImBuf *ibuf = NULL;
@@ -1369,9 +1372,6 @@ static void filelist_entry_clear(FileDirEntry *entry)
if (entry->name && ((entry->flags & FILE_ENTRY_NAME_FREE) != 0)) {
MEM_freeN(entry->name);
}
- if (entry->description) {
- MEM_freeN(entry->description);
- }
if (entry->relpath) {
MEM_freeN(entry->relpath);
}
@@ -1494,6 +1494,7 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
/* That way task freeing function won't free th preview, since it does not own it anymore. */
atomic_cas_ptr((void **)&preview_taskdata->preview, 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);
@@ -1520,6 +1521,7 @@ static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
if (!cache->previews_pool) {
cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW);
cache->previews_done = BLI_thread_queue_init();
+ cache->previews_todo_count = 0;
IMB_thumb_locks_acquire();
}
@@ -1553,6 +1555,7 @@ static void filelist_cache_previews_free(FileListEntryCache *cache)
BLI_task_pool_free(cache->previews_pool);
cache->previews_pool = NULL;
cache->previews_done = NULL;
+ cache->previews_todo_count = 0;
IMB_thumb_locks_release();
}
@@ -1633,6 +1636,8 @@ static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
cache->size = cache_size;
cache->flags = FLC_IS_INIT;
+ cache->previews_todo_count = 0;
+
/* We cannot translate from non-main thread, so init translated strings once from here. */
IMB_thumb_ensure_translations();
}
@@ -1806,13 +1811,23 @@ BlendHandle *filelist_lib(struct FileList *filelist)
return filelist->libfiledata;
}
-static const char *fileentry_uiname(const char *root,
- const char *relpath,
- const eFileSel_File_Types typeflag,
- char *buff)
+static char *fileentry_uiname(const char *root,
+ const char *relpath,
+ const eFileSel_File_Types typeflag,
+ char *buff)
{
char *name = NULL;
+ if (typeflag & FILE_TYPE_FTFONT && !(typeflag & FILE_TYPE_BLENDERLIB)) {
+ char abspath[FILE_MAX_LIBEXTRA];
+ BLI_join_dirfile(abspath, sizeof(abspath), root, relpath);
+ name = BLF_display_name_from_file(abspath);
+ if (name) {
+ /* Allocated string, so no need to BLI_strdup.*/
+ return name;
+ }
+ }
+
if (typeflag & FILE_TYPE_BLENDERLIB) {
char abspath[FILE_MAX_LIBEXTRA];
char *group;
@@ -1834,7 +1849,7 @@ static const char *fileentry_uiname(const char *root,
}
BLI_assert(name);
- return name;
+ return BLI_strdup(name);
}
const char *filelist_dir(struct FileList *filelist)
@@ -1934,7 +1949,6 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
else {
ret->name = entry->name;
}
- ret->description = BLI_strdupcat(filelist->filelist.root, entry->relpath);
ret->uid = entry->uid;
ret->blentype = entry->blentype;
ret->typeflag = entry->typeflag;
@@ -2384,7 +2398,8 @@ void filelist_cache_previews_set(FileList *filelist, const bool use_previews)
if (use_previews && (filelist->flags & FL_IS_READY)) {
cache->flags |= FLC_PREVIEWS_ACTIVE;
- BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL));
+ BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL) &&
+ (cache->previews_todo_count == 0));
// printf("%s: Init Previews...\n", __func__);
@@ -2457,6 +2472,18 @@ bool filelist_cache_previews_running(FileList *filelist)
return (cache->previews_pool != NULL);
}
+bool filelist_cache_previews_done(FileList *filelist)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+ if ((cache->flags & FLC_PREVIEWS_ACTIVE) == 0) {
+ /* There are no previews. */
+ return false;
+ }
+
+ return (cache->previews_pool == NULL) || (cache->previews_done == NULL) ||
+ (cache->previews_todo_count == (size_t)BLI_thread_queue_len(cache->previews_done));
+}
+
/* would recognize .blend as well */
static bool file_is_blend_backup(const char *str)
{
@@ -3184,7 +3211,7 @@ static void filelist_readjob_do(const bool do_lib,
MEM_freeN(entry->relpath);
entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//'
* added by BLI_path_rel to rel_subdir. */
- entry->name = BLI_strdup(fileentry_uiname(root, entry->relpath, entry->typeflag, dir));
+ entry->name = fileentry_uiname(root, entry->relpath, entry->typeflag, dir);
entry->free_name = true;
/* Here we decide whether current filedirentry is to be listed too, or not. */
@@ -3432,7 +3459,7 @@ static void filelist_readjob_free(void *flrjv)
MEM_freeN(flrj);
}
-void filelist_readjob_start(FileList *filelist, const bContext *C)
+void filelist_readjob_start(FileList *filelist, const int space_notifier, const bContext *C)
{
Main *bmain = CTX_data_main(C);
wmJob *wm_job;
@@ -3464,7 +3491,7 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
filelist_readjob_endjob(flrj);
filelist_readjob_free(flrj);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED, NULL);
+ WM_event_add_notifier(C, space_notifier | NA_JOB_FINISHED, NULL);
return;
}
@@ -3476,10 +3503,7 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
WM_JOB_PROGRESS,
WM_JOB_TYPE_FILESEL_READDIR);
WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free);
- WM_jobs_timer(wm_job,
- 0.01,
- NC_SPACE | ND_SPACE_FILE_LIST,
- NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED);
+ WM_jobs_timer(wm_job, 0.01, space_notifier, space_notifier | NA_JOB_FINISHED);
WM_jobs_callbacks(
wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob);
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 0aace74e621..6915e853681 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -29,7 +29,7 @@ extern "C" {
struct BlendHandle;
struct FileList;
-struct FileSelectAssetLibraryUID;
+struct AssetLibraryReference;
struct FileSelection;
struct wmWindowManager;
@@ -73,12 +73,13 @@ void filelist_setfilter_options(struct FileList *filelist,
const char *filter_search);
void filelist_filter(struct FileList *filelist);
void filelist_setlibrary(struct FileList *filelist,
- const struct FileSelectAssetLibraryUID *asset_library);
+ const struct AssetLibraryReference *asset_library);
void filelist_init_icons(void);
void filelist_free_icons(void);
struct ImBuf *filelist_getimage(struct FileList *filelist, const int index);
struct ImBuf *filelist_file_getimage(const FileDirEntry *file);
+struct ImBuf *filelist_geticon_image_ex(const FileDirEntry *file);
struct ImBuf *filelist_geticon_image(struct FileList *filelist, const int index);
int filelist_geticon(struct FileList *filelist, const int index, const bool is_main);
@@ -144,13 +145,16 @@ struct BlendHandle *filelist_lib(struct FileList *filelist);
bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group);
void filelist_freelib(struct FileList *filelist);
-void filelist_readjob_start(struct FileList *filelist, const struct bContext *C);
+void filelist_readjob_start(struct FileList *filelist,
+ int space_notifier,
+ const struct bContext *C);
void filelist_readjob_stop(struct FileList *filelist, struct wmWindowManager *wm);
int filelist_readjob_running(struct FileList *filelist, struct wmWindowManager *wm);
bool filelist_cache_previews_update(struct FileList *filelist);
void filelist_cache_previews_set(struct FileList *filelist, const bool use_previews);
bool filelist_cache_previews_running(struct FileList *filelist);
+bool filelist_cache_previews_done(struct FileList *filelist);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 7ab93df81d2..89142b6669b 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -118,7 +118,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params),
"FileAssetSelectParams");
asset_params->base_params.details_flags = U_default.file_space_data.details_flags;
- asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL;
+ asset_params->asset_library.type = ASSET_LIBRARY_LOCAL;
asset_params->asset_library.custom_library_index = -1;
asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
}
@@ -415,31 +415,31 @@ FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile)
static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
{
- FileSelectAssetLibraryUID *library = &asset_params->asset_library;
+ AssetLibraryReference *library = &asset_params->asset_library;
FileSelectParams *base_params = &asset_params->base_params;
bUserAssetLibrary *user_library = NULL;
/* Ensure valid repository, or fall-back to local one. */
- if (library->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ if (library->type == ASSET_LIBRARY_CUSTOM) {
BLI_assert(library->custom_library_index >= 0);
user_library = BKE_preferences_asset_library_find_from_index(&U,
library->custom_library_index);
if (!user_library) {
- library->type = FILE_ASSET_LIBRARY_LOCAL;
+ library->type = ASSET_LIBRARY_LOCAL;
}
}
switch (library->type) {
- case FILE_ASSET_LIBRARY_LOCAL:
+ case ASSET_LIBRARY_LOCAL:
base_params->dir[0] = '\0';
break;
- case FILE_ASSET_LIBRARY_CUSTOM:
+ case ASSET_LIBRARY_CUSTOM:
BLI_assert(user_library);
BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
break;
}
- base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
+ base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
}
void fileselect_refresh_params(SpaceFile *sfile)
@@ -450,6 +450,11 @@ void fileselect_refresh_params(SpaceFile *sfile)
}
}
+bool ED_fileselect_is_file_browser(const SpaceFile *sfile)
+{
+ return (sfile->browse_mode == FILE_BROWSE_MODE_FILES);
+}
+
bool ED_fileselect_is_asset_browser(const SpaceFile *sfile)
{
return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 05d484d8e2e..46cc96ba0d4 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -367,7 +367,7 @@ static void file_refresh(const bContext *C, ScrArea *area)
if (filelist_needs_reading(sfile->files)) {
if (!filelist_pending(sfile->files)) {
- filelist_readjob_start(sfile->files, C);
+ filelist_readjob_start(sfile->files, NC_SPACE | ND_SPACE_FILE_LIST, C);
}
}
@@ -685,7 +685,6 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_bookmark_move);
WM_operatortype_append(FILE_OT_reset_recent);
WM_operatortype_append(FILE_OT_hidedot);
- WM_operatortype_append(FILE_OT_associate_blend);
WM_operatortype_append(FILE_OT_filenum);
WM_operatortype_append(FILE_OT_directory_new);
WM_operatortype_append(FILE_OT_delete);
@@ -868,7 +867,12 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C),
}
}
-static const char *file_context_dir[] = {"active_file", "id", NULL};
+static const char *file_context_dir[] = {
+ "active_file",
+ "asset_library",
+ "id",
+ NULL,
+};
static int /*eContextResult*/ file_context(const bContext *C,
const char *member,
@@ -899,6 +903,16 @@ static int /*eContextResult*/ file_context(const bContext *C,
CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file);
return CTX_RESULT_OK;
}
+ if (CTX_data_equals(member, "asset_library")) {
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ if (!asset_params) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ CTX_data_pointer_set(
+ result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library);
+ return CTX_RESULT_OK;
+ }
if (CTX_data_equals(member, "id")) {
const FileDirEntry *file = filelist_file(sfile->files, params->active_file);
if (file == NULL) {
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index 56592cbbd1b..af88bbced9c 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -1259,14 +1259,15 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu)
immUniformColor3f(0.9f, 0.9f, 0.9f);
immUniform1f("dash_width", 10.0f);
immUniform1f("dash_factor", 0.5f);
+ GPU_line_width(1.0f);
- immBegin(GPU_PRIM_LINES, (y >= v2d->cur.ymin) ? 4 : 2);
+ immBegin(GPU_PRIM_LINES, (y <= v2d->cur.ymax) ? 4 : 2);
/* x-axis lookup */
co[0] = x;
- if (y >= v2d->cur.ymin) {
- co[1] = v2d->cur.ymin - 1.0f;
+ if (y <= v2d->cur.ymax) {
+ co[1] = v2d->cur.ymax + 1.0f;
immVertex2fv(shdr_pos, co);
co[1] = y;
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 6f1b0bb0d7d..8bfafc6bef8 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -261,7 +261,7 @@ static int graphkeys_insertkey_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* Which channels to affect?. */
+ /* Which channels to affect? */
mode = RNA_enum_get(op->ptr, "type");
/* Insert keyframes. */
@@ -2447,7 +2447,7 @@ static void mirror_graph_keys(bAnimContext *ac, short mode)
float unit_scale = ANIM_unit_mapping_get_factor(
ac->scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS, &offset);
- ked.f1 = (cursor_value + offset) * unit_scale;
+ ked.f1 = (cursor_value - offset) / unit_scale;
}
/* Perform actual mirroring. */
@@ -2814,7 +2814,7 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
}
ANIM_animdata_freelist(&anim_data);
- /* Successful or not?. */
+ /* Successful or not? */
if (ok) {
/* Set notifier that keyframes have changed. */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
@@ -2873,7 +2873,7 @@ static int graph_driver_vars_copy_exec(bContext *C, wmOperator *op)
ok = ANIM_driver_vars_copy(op->reports, fcu);
}
- /* Successful or not?. */
+ /* Successful or not? */
if (ok) {
return OPERATOR_FINISHED;
}
@@ -2915,7 +2915,7 @@ static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op)
ok = ANIM_driver_vars_paste(op->reports, fcu, replace);
}
- /* Successful or not?. */
+ /* Successful or not? */
if (ok) {
/* Rebuild depsgraph, now that there are extra deps here. */
DEG_relations_tag_update(CTX_data_main(C));
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index 1421be41124..a853efb1ace 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -859,7 +859,7 @@ static int graphkeys_box_select_exec(bContext *C, wmOperator *op)
* as frame-range one is often used for tweaking timing when "blocking",
* while channels is not that useful.
*/
- if ((BLI_rcti_size_x(&rect)) >= (BLI_rcti_size_y(&rect))) {
+ if (BLI_rcti_size_x(&rect) >= BLI_rcti_size_y(&rect)) {
mode = BEZT_OK_FRAMERANGE;
}
else {
diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c
index 31c53cde62c..036fd354c18 100644
--- a/source/blender/editors/space_graph/graph_view.c
+++ b/source/blender/editors/space_graph/graph_view.c
@@ -398,7 +398,7 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end)
ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- /* Loop through filtered data and add keys between selected keyframes on every frame . */
+ /* Loop through filtered data and add keys between selected keyframes on every frame. */
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
FCurve *gcu = BKE_fcurve_create();
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 50b0ea75052..86349a64681 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -1013,14 +1013,14 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma
uiLayoutRow(col, true), imfptr, "color_mode", UI_ITEM_R_EXPAND, IFACE_("Color"), ICON_NONE);
/* only display depth setting if multiple depths can be used */
- if ((ELEM(depth_ok,
- R_IMF_CHAN_DEPTH_1,
- R_IMF_CHAN_DEPTH_8,
- R_IMF_CHAN_DEPTH_10,
- R_IMF_CHAN_DEPTH_12,
- R_IMF_CHAN_DEPTH_16,
- R_IMF_CHAN_DEPTH_24,
- R_IMF_CHAN_DEPTH_32)) == 0) {
+ if (ELEM(depth_ok,
+ R_IMF_CHAN_DEPTH_1,
+ R_IMF_CHAN_DEPTH_8,
+ R_IMF_CHAN_DEPTH_10,
+ R_IMF_CHAN_DEPTH_12,
+ R_IMF_CHAN_DEPTH_16,
+ R_IMF_CHAN_DEPTH_24,
+ R_IMF_CHAN_DEPTH_32) == 0) {
uiItemR(uiLayoutRow(col, true), imfptr, "color_depth", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
}
@@ -1189,7 +1189,7 @@ void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser
const int menus_width = 160 * dpi_fac;
const bool is_render_result = (ima->type == IMA_TYPE_R_RESULT);
- /* use BKE_image_acquire_renderresult so we get the correct slot in the menu */
+ /* Use BKE_image_acquire_renderresult so we get the correct slot in the menu. */
rr = BKE_image_acquire_renderresult(scene, ima);
uiblock_layer_pass_buttons(
layout, ima, rr, iuser, menus_width, is_render_result ? &ima->render_slot : NULL);
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index dc693b25107..92ceb00d5c0 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -460,258 +460,6 @@ void ED_image_draw_info(Scene *scene,
BLF_draw_ascii(blf_mono_font, str, sizeof(str));
}
}
-
-/* image drawing */
-static void sima_draw_zbuf_pixels(
- float x1, float y1, int rectx, int recty, const int *rect, float zoomx, float zoomy)
-{
- const float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
-
- /* Very slow! */
- float *rectf = MEM_mallocN(rectx * recty * sizeof(float), "temp");
- for (int a = rectx * recty - 1; a >= 0; a--) {
- /* zbuffer values are signed, so we need to shift color range */
- rectf[a] = rect[a] * 0.5f + 0.5f;
- }
-
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
- GPU_shader_uniform_vector(
- state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
-
- immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL);
-
- MEM_freeN(rectf);
-}
-
-static void sima_draw_zbuffloat_pixels(Scene *scene,
- float x1,
- float y1,
- int rectx,
- int recty,
- const float *rect_float,
- float zoomx,
- float zoomy)
-{
- float bias, scale, *rectf, clip_end;
- int a;
- const float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
-
- if (scene->camera && scene->camera->type == OB_CAMERA) {
- bias = ((Camera *)scene->camera->data)->clip_start;
- clip_end = ((Camera *)scene->camera->data)->clip_end;
- scale = 1.0f / (clip_end - bias);
- }
- else {
- bias = 0.1f;
- scale = 0.01f;
- clip_end = 100.0f;
- }
-
- rectf = MEM_mallocN(rectx * recty * sizeof(float), "temp");
- for (a = rectx * recty - 1; a >= 0; a--) {
- if (rect_float[a] > clip_end) {
- rectf[a] = 0.0f;
- }
- else if (rect_float[a] < bias) {
- rectf[a] = 1.0f;
- }
- else {
- rectf[a] = 1.0f - (rect_float[a] - bias) * scale;
- rectf[a] *= rectf[a];
- }
- }
-
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
- GPU_shader_uniform_vector(
- state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
-
- immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL);
-
- MEM_freeN(rectf);
-}
-
-static void draw_udim_label(ARegion *region, float fx, float fy, const char *label)
-{
- if (label == NULL || !label[0]) {
- return;
- }
-
- /* find window pixel coordinates of origin */
- int x, y;
- UI_view2d_view_to_region(&region->v2d, fx, fy, &x, &y);
-
- GPU_blend(GPU_BLEND_ALPHA);
-
- int textwidth = BLF_width(blf_mono_font, label, strlen(label)) + 10;
- float stepx = BLI_rcti_size_x(&region->v2d.mask) / BLI_rctf_size_x(&region->v2d.cur);
- float opacity;
- if (textwidth < 0.5f * (stepx - 10)) {
- opacity = 1.0f;
- }
- else if (textwidth < (stepx - 10)) {
- opacity = 2.0f - 2.0f * (textwidth / (stepx - 10));
- }
- else {
- opacity = 0.0f;
- }
- BLF_color4ub(blf_mono_font, 220, 220, 220, 150 * opacity);
- BLF_position(blf_mono_font, (int)(x + 10), (int)(y + 10), 0);
- BLF_draw_ascii(blf_mono_font, label, strlen(label));
-
- GPU_blend(GPU_BLEND_NONE);
-}
-
-static void draw_image_buffer(const bContext *C,
- SpaceImage *sima,
- ARegion *region,
- Scene *scene,
- ImBuf *ibuf,
- float fx,
- float fy,
- float zoomx,
- float zoomy)
-{
- /* Image are still drawn in display space. */
- GPUFrameBuffer *fb = GPU_framebuffer_active_get();
- GPU_framebuffer_bind_no_srgb(fb);
-
- int x, y;
- int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(ibuf);
-
- /* find window pixel coordinates of origin */
- UI_view2d_view_to_region(&region->v2d, fx, fy, &x, &y);
-
- /* this part is generic image display */
- if (sima_flag & SI_SHOW_ZBUF && (ibuf->zbuf || ibuf->zbuf_float || (ibuf->channels == 1))) {
- if (ibuf->zbuf) {
- sima_draw_zbuf_pixels(x, y, ibuf->x, ibuf->y, ibuf->zbuf, zoomx, zoomy);
- }
- else if (ibuf->zbuf_float) {
- sima_draw_zbuffloat_pixels(scene, x, y, ibuf->x, ibuf->y, ibuf->zbuf_float, zoomx, zoomy);
- }
- else if (ibuf->channels == 1) {
- sima_draw_zbuffloat_pixels(scene, x, y, ibuf->x, ibuf->y, ibuf->rect_float, zoomx, zoomy);
- }
- }
- else {
- int clip_max_x, clip_max_y;
- UI_view2d_view_to_region(
- &region->v2d, region->v2d.cur.xmax, region->v2d.cur.ymax, &clip_max_x, &clip_max_y);
-
- if (sima_flag & SI_USE_ALPHA) {
- imm_draw_box_checker_2d(x, y, x + ibuf->x * zoomx, y + ibuf->y * zoomy);
-
- GPU_blend(GPU_BLEND_ALPHA);
- }
-
- /* If RGBA display with color management */
- if ((sima_flag & (SI_SHOW_R | SI_SHOW_G | SI_SHOW_B | SI_SHOW_ALPHA)) == 0) {
-
- ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, false, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy);
- }
- else {
- float shuffle[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- uchar *display_buffer;
- void *cache_handle;
- ColorManagedViewSettings *view_settings;
- ColorManagedDisplaySettings *display_settings;
-
- if (sima_flag & SI_SHOW_R) {
- shuffle[0] = 1.0f;
- }
- else if (sima_flag & SI_SHOW_G) {
- shuffle[1] = 1.0f;
- }
- else if (sima_flag & SI_SHOW_B) {
- shuffle[2] = 1.0f;
- }
- else if (sima_flag & SI_SHOW_ALPHA) {
- shuffle[3] = 1.0f;
- }
-
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
- GPU_shader_uniform_vector(
- state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, shuffle);
-
- IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
- display_buffer = IMB_display_buffer_acquire(
- ibuf, view_settings, display_settings, &cache_handle);
-
- if (display_buffer) {
- immDrawPixelsTex_clipping(&state,
- x,
- y,
- ibuf->x,
- ibuf->y,
- GPU_RGBA8,
- false,
- display_buffer,
- 0,
- 0,
- clip_max_x,
- clip_max_y,
- zoomx,
- zoomy,
- NULL);
- }
-
- IMB_display_buffer_release(cache_handle);
- }
-
- if (sima_flag & SI_USE_ALPHA) {
- GPU_blend(GPU_BLEND_NONE);
- }
- }
-
- GPU_framebuffer_bind(fb);
-}
-
-static void draw_image_buffer_repeated(const bContext *C,
- SpaceImage *sima,
- ARegion *region,
- Scene *scene,
- ImBuf *ibuf,
- float zoomx,
- float zoomy)
-{
- const double time_current = PIL_check_seconds_timer();
-
- const int xmax = ceil(region->v2d.cur.xmax);
- const int ymax = ceil(region->v2d.cur.ymax);
- const int xmin = floor(region->v2d.cur.xmin);
- const int ymin = floor(region->v2d.cur.ymin);
-
- for (int x = xmin; x < xmax; x++) {
- for (int y = ymin; y < ymax; y++) {
- draw_image_buffer(C, sima, region, scene, ibuf, x, y, zoomx, zoomy);
-
- /* only draw until running out of time */
- if ((PIL_check_seconds_timer() - time_current) > 0.25) {
- return;
- }
- }
- }
-}
-
-/* draw uv edit */
-
-/* draw grease pencil */
-void draw_image_grease_pencil(bContext *C, bool onlyv2d)
-{
- /* draw in View2D space? */
- if (onlyv2d) {
- /* draw grease-pencil ('image' strokes) */
- ED_annotation_draw_2dimage(C);
- }
- else {
- /* assume that UI_view2d_restore(C) has been called... */
- // SpaceImage *sima = (SpaceImage *)CTX_wm_space_data(C);
-
- /* draw grease-pencil ('screen' strokes) */
- ED_annotation_draw_view2d(C, 0);
- }
-}
-
void draw_image_sample_line(SpaceImage *sima)
{
if (sima->sample_line_hist.flag & HISTO_FLAG_SAMPLELINE) {
@@ -742,229 +490,6 @@ void draw_image_sample_line(SpaceImage *sima)
}
}
-static void draw_udim_tile_grid(uint pos_attr,
- uint color_attr,
- ARegion *region,
- int x,
- int y,
- float stepx,
- float stepy,
- const float color[3])
-{
- float x1, y1;
- UI_view2d_view_to_region_fl(&region->v2d, x, y, &x1, &y1);
- const int gridpos[5][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
- for (int i = 0; i < 4; i++) {
- immAttr3fv(color_attr, color);
- immVertex2f(pos_attr, x1 + gridpos[i][0] * stepx, y1 + gridpos[i][1] * stepy);
- immAttr3fv(color_attr, color);
- immVertex2f(pos_attr, x1 + gridpos[i + 1][0] * stepx, y1 + gridpos[i + 1][1] * stepy);
- }
-}
-
-static void draw_udim_tile_grids(ARegion *region, SpaceImage *sima, Image *ima)
-{
- int num_tiles;
- if (ima != NULL) {
- num_tiles = BLI_listbase_count(&ima->tiles);
-
- if (ima->source != IMA_SRC_TILED) {
- return;
- }
- }
- else {
- num_tiles = sima->tile_grid_shape[0] * sima->tile_grid_shape[1];
- }
-
- float stepx = BLI_rcti_size_x(&region->v2d.mask) / BLI_rctf_size_x(&region->v2d.cur);
- float stepy = BLI_rcti_size_y(&region->v2d.mask) / BLI_rctf_size_y(&region->v2d.cur);
-
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
- immBegin(GPU_PRIM_LINES, 8 * num_tiles);
-
- float theme_color[3], selected_color[3];
- UI_GetThemeColorShade3fv(TH_GRID, 60.0f, theme_color);
- UI_GetThemeColor3fv(TH_FACE_SELECT, selected_color);
-
- if (ima != NULL) {
- ImageTile *cur_tile = BLI_findlink(&ima->tiles, ima->active_tile_index);
-
- LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
- if (tile != cur_tile) {
- int x = (tile->tile_number - 1001) % 10;
- int y = (tile->tile_number - 1001) / 10;
- draw_udim_tile_grid(pos, color, region, x, y, stepx, stepy, theme_color);
- }
- }
-
- if (cur_tile != NULL) {
- int cur_x = (cur_tile->tile_number - 1001) % 10;
- int cur_y = (cur_tile->tile_number - 1001) / 10;
- draw_udim_tile_grid(pos, color, region, cur_x, cur_y, stepx, stepy, selected_color);
- }
- }
- else {
- for (int y = 0; y < sima->tile_grid_shape[1]; y++) {
- for (int x = 0; x < sima->tile_grid_shape[0]; x++) {
- draw_udim_tile_grid(pos, color, region, x, y, stepx, stepy, theme_color);
- }
- }
- }
-
- immEnd();
- immUnbindProgram();
-}
-
-/* draw main image region */
-
-void draw_image_main(const bContext *C, ARegion *region)
-{
- SpaceImage *sima = CTX_wm_space_image(C);
- Scene *scene = CTX_data_scene(C);
- Image *ima;
- ImBuf *ibuf;
- float zoomx, zoomy;
- bool show_viewer, show_stereo3d, show_multilayer;
- void *lock;
-
- /* XXX can we do this in refresh? */
-#if 0
- what_image(sima);
-
- if (sima->image) {
- ED_image_get_aspect(sima->image, &xuser_asp, &yuser_asp);
-
- /* UGLY hack? until now iusers worked fine... but for flipbook viewer we need this */
- if (sima->image->type == IMA_TYPE_COMPOSITE) {
- ImageUser *iuser = ntree_get_active_iuser(scene->nodetree);
- if (iuser) {
- BKE_image_user_calc_imanr(iuser, scene->r.cfra, 0);
- sima->iuser = *iuser;
- }
- }
- /* and we check for spare */
- ibuf = ED_space_image_buffer(sima);
- }
-#endif
-
- /* retrieve the image and information about it */
- ima = ED_space_image(sima);
- ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
-
- /* Tag image as in active use for garbage collector. */
- if (ima) {
- BKE_image_tag_time(ima);
- }
-
- show_viewer = (ima && ima->source == IMA_SRC_VIEWER) != 0;
- show_stereo3d = (ima && BKE_image_is_stereo(ima) && (sima->iuser.flag & IMA_SHOW_STEREO));
- show_multilayer = ima && BKE_image_is_multilayer(ima);
-
- if (show_viewer) {
- /* use locked draw for drawing viewer image buffer since the compositor
- * is running in separated thread and compositor could free this buffers.
- * other images are not modifying in such a way so they does not require
- * lock (sergey)
- */
- BLI_thread_lock(LOCK_DRAW_IMAGE);
- }
-
- if (show_stereo3d) {
- if (show_multilayer) {
- /* Update multi-index and pass for the current eye. */
- BKE_image_multilayer_index(ima->rr, &sima->iuser);
- }
- else {
- BKE_image_multiview_index(ima, &sima->iuser);
- }
- }
-
- ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
-
- int main_w = 0;
- int main_h = 0;
-
- /* draw the image or grid */
- if (ibuf == NULL) {
- if (ima != NULL) {
- LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
- int x = (tile->tile_number - 1001) % 10;
- int y = (tile->tile_number - 1001) / 10;
- ED_region_grid_draw(region, zoomx, zoomy, x, y);
- }
- }
- else {
- for (int y = 0; y < sima->tile_grid_shape[1]; y++) {
- for (int x = 0; x < sima->tile_grid_shape[0]; x++) {
- ED_region_grid_draw(region, zoomx, zoomy, x, y);
- }
- }
- }
- }
- else {
- if (sima->flag & SI_DRAW_TILE) {
- draw_image_buffer_repeated(C, sima, region, scene, ibuf, zoomx, zoomy);
- }
- else {
- main_w = ibuf->x;
- main_h = ibuf->y;
-
- draw_image_buffer(C, sima, region, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy);
- if (ima->source == IMA_SRC_TILED) {
- ImageTile *tile = BKE_image_get_tile(ima, 0);
- char label[sizeof(tile->label)];
- BKE_image_get_tile_label(ima, tile, label, sizeof(label));
- draw_udim_label(region, 0.0f, 0.0f, label);
- }
- }
-
- if (sima->flag & SI_DRAW_METADATA) {
- int x, y;
- rctf frame;
-
- BLI_rctf_init(&frame, 0.0f, ibuf->x, 0.0f, ibuf->y);
- UI_view2d_view_to_region(&region->v2d, 0.0f, 0.0f, &x, &y);
-
- ED_region_image_metadata_draw(x, y, ibuf, &frame, zoomx, zoomy);
- }
- }
-
- ED_space_image_release_buffer(sima, ibuf, lock);
-
- if (ima != NULL && ima->source == IMA_SRC_TILED) {
- LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
- if (tile->tile_number == 1001) {
- continue;
- }
-
- ibuf = ED_space_image_acquire_buffer(sima, &lock, tile->tile_number);
- if (ibuf != NULL) {
- int x_pos = (tile->tile_number - 1001) % 10;
- int y_pos = (tile->tile_number - 1001) / 10;
- char label[sizeof(tile->label)];
- BKE_image_get_tile_label(ima, tile, label, sizeof(label));
-
- float tile_zoomx = (zoomx * main_w) / ibuf->x;
- float tile_zoomy = (zoomy * main_h) / ibuf->y;
- draw_image_buffer(C, sima, region, scene, ibuf, x_pos, y_pos, tile_zoomx, tile_zoomy);
- draw_udim_label(region, x_pos, y_pos, label);
- }
- ED_space_image_release_buffer(sima, ibuf, lock);
- }
- }
-
- draw_udim_tile_grids(region, sima, ima);
- draw_image_main_helpers(C, region);
-
- if (show_viewer) {
- BLI_thread_unlock(LOCK_DRAW_IMAGE);
- }
-}
-
void draw_image_main_helpers(const bContext *C, ARegion *region)
{
SpaceImage *sima = CTX_wm_space_image(C);
diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h
index d302f099772..6af0f3a416b 100644
--- a/source/blender/editors/space_image/image_intern.h
+++ b/source/blender/editors/space_image/image_intern.h
@@ -36,10 +36,8 @@ struct wmOperatorType;
extern const char *image_context_dir[]; /* doc access */
/* image_draw.c */
-void draw_image_main(const struct bContext *C, struct ARegion *region);
void draw_image_main_helpers(const struct bContext *C, struct ARegion *region);
void draw_image_cache(const struct bContext *C, struct ARegion *region);
-void draw_image_grease_pencil(struct bContext *C, bool onlyv2d);
void draw_image_sample_line(struct SpaceImage *sima);
/* image_ops.c */
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 6b9821745c7..dad354ba8ee 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1995,7 +1995,7 @@ static bool image_save_as_draw_check_prop(PointerRNA *ptr,
return !(STREQ(prop_id, "filepath") || STREQ(prop_id, "directory") ||
STREQ(prop_id, "filename") ||
/* when saving a copy, relative path has no effect */
- ((STREQ(prop_id, "relative_path")) && RNA_boolean_get(ptr, "copy")));
+ (STREQ(prop_id, "relative_path") && RNA_boolean_get(ptr, "copy")));
}
static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index f9fb386095d..c96047da0c8 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -44,6 +44,7 @@
#include "ED_anim_api.h"
#include "ED_keyframes_draw.h"
+#include "ED_keyframes_keylist.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index 011cd7e2651..987d06cfe5c 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -239,7 +239,7 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_ortho(v2d);
/* time grid */
- UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, snla->flag & SNLA_DRAWTIME);
+ UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, snla->flag & SNLA_DRAWTIME, true);
ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW);
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 243652da608..95eb1ccc025 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -165,7 +165,7 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA
}
#define SAMPLE_FLT_ISNONE FLT_MAX
-/* bad bad, 2.5 will do better?... no it won't... */
+/* Bad bad, 2.5 will do better? ... no it won't! */
static float _sample_col[4] = {SAMPLE_FLT_ISNONE};
void ED_node_sample_set(const float col[4])
{
@@ -1953,8 +1953,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
0,
0,
0,
- false,
- false);
+ UI_TEMPLATE_LIST_FLAG_NONE);
RNA_property_collection_lookup_int(
ptr, RNA_struct_find_property(ptr, "layer_slots"), active_index, &active_input_ptr);
}
@@ -1972,8 +1971,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
0,
0,
0,
- false,
- false);
+ UI_TEMPLATE_LIST_FLAG_NONE);
RNA_property_collection_lookup_int(
ptr, RNA_struct_find_property(ptr, "file_slots"), active_index, &active_input_ptr);
}
@@ -3911,8 +3909,10 @@ static struct {
GPUVertBuf *inst_vbo;
uint p0_id, p1_id, p2_id, p3_id;
uint colid_id, muted_id;
+ uint dim_factor_id;
GPUVertBufRaw p0_step, p1_step, p2_step, p3_step;
GPUVertBufRaw colid_step, muted_step;
+ GPUVertBufRaw dim_factor_step;
uint count;
bool enabled;
} g_batch_link;
@@ -3927,6 +3927,8 @@ static void nodelink_batch_reset()
g_batch_link.inst_vbo, g_batch_link.colid_id, &g_batch_link.colid_step);
GPU_vertbuf_attr_get_raw_data(
g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step);
+ GPU_vertbuf_attr_get_raw_data(
+ g_batch_link.inst_vbo, g_batch_link.dim_factor_id, &g_batch_link.dim_factor_step);
g_batch_link.count = 0;
}
@@ -4044,6 +4046,8 @@ static void nodelink_batch_init()
&format_inst, "colid_doarrow", GPU_COMP_U8, 4, GPU_FETCH_INT);
g_batch_link.muted_id = GPU_vertformat_attr_add(
&format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT);
+ g_batch_link.dim_factor_id = GPU_vertformat_attr_add(
+ &format_inst, "dim_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM);
/* Alloc max count but only draw the range we need. */
GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE);
@@ -4119,7 +4123,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode,
int th_col2,
int th_col3,
bool drawarrow,
- bool drawmuted)
+ bool drawmuted,
+ float dim_factor)
{
/* Only allow these colors. If more is needed, you need to modify the shader accordingly. */
BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
@@ -4138,6 +4143,7 @@ static void nodelink_batch_add_link(const SpaceNode *snode,
colid[3] = drawarrow;
char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step);
muted[0] = drawmuted;
+ *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor;
if (g_batch_link.count == NODELINK_GROUP_SIZE) {
nodelink_batch_draw(snode);
@@ -4152,6 +4158,8 @@ void node_draw_link_bezier(const View2D *v2d,
int th_col2,
int th_col3)
{
+ const float dim_factor = node_link_dim_factor(v2d, link);
+
float vec[4][2];
const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT;
if (node_link_bezier_handles(v2d, snode, link, vec)) {
@@ -4164,8 +4172,17 @@ void node_draw_link_bezier(const View2D *v2d,
if (g_batch_link.enabled && !highlighted) {
/* Add link to batch. */
- nodelink_batch_add_link(
- snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow, drawmuted);
+ nodelink_batch_add_link(snode,
+ vec[0],
+ vec[1],
+ vec[2],
+ vec[3],
+ th_col1,
+ th_col2,
+ th_col3,
+ drawarrow,
+ drawmuted,
+ dim_factor);
}
else {
/* Draw single link. */
@@ -4190,6 +4207,7 @@ void node_draw_link_bezier(const View2D *v2d,
GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE);
GPU_batch_uniform_1i(batch, "doArrow", drawarrow);
GPU_batch_uniform_1i(batch, "doMuted", drawmuted);
+ GPU_batch_uniform_1f(batch, "dim_factor", dim_factor);
GPU_batch_draw(batch);
}
}
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index c167744de01..9264c9d3572 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -260,7 +260,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
BLI_listbase_clear(&input_links);
for (link = (bNodeLink *)ntree->links.first; link; link = link->next) {
- if (nodeLinkIsHidden(link)) {
+ if (node_link_is_hidden_or_dimmed(&region->v2d, link)) {
continue;
}
if (add_reroute_intersect_check(link, mcoords, i, insert_point)) {
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 7eca5c0c933..6ec6315a238 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -2125,7 +2125,7 @@ void node_draw_space(const bContext *C, ARegion *region)
SpaceNode *snode = CTX_wm_space_node(C);
View2D *v2d = &region->v2d;
- /* Setup offscreen buffers. */
+ /* Setup off-screen buffers. */
GPUViewport *viewport = WM_draw_region_get_viewport(region);
GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport);
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index 5dd935bdd76..cbf03f553f6 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -21,6 +21,8 @@
* \ingroup spnode
*/
+#include <algorithm>
+
#include "MEM_guardedalloc.h"
#include "DNA_light_types.h"
@@ -611,9 +613,8 @@ void snode_set_context(const bContext *C)
/* check the tree type */
if (!treetype || (treetype->poll && !treetype->poll(C, treetype))) {
/* invalid tree type, skip
- * NB: not resetting the node path here, invalid bNodeTreeType
- * may still be registered at a later point.
- */
+ * NOTE: not resetting the node path here, invalid #bNodeTreeType
+ * may still be registered at a later point. */
return;
}
@@ -1226,6 +1227,32 @@ int node_find_indicated_socket(
return 0;
}
+/* ****************** Link Dimming *********************** */
+
+float node_link_dim_factor(const View2D *v2d, const bNodeLink *link)
+{
+ if (link->fromsock == nullptr || link->tosock == nullptr) {
+ return 1.0f;
+ }
+
+ const float min_endpoint_distance = std::min(
+ std::max(BLI_rctf_length_x(&v2d->cur, link->fromsock->locx),
+ BLI_rctf_length_y(&v2d->cur, link->fromsock->locy)),
+ std::max(BLI_rctf_length_x(&v2d->cur, link->tosock->locx),
+ BLI_rctf_length_y(&v2d->cur, link->tosock->locy)));
+
+ if (min_endpoint_distance == 0.0f) {
+ return 1.0f;
+ }
+ const float viewport_width = BLI_rctf_size_x(&v2d->cur);
+ return std::clamp(1.0f - min_endpoint_distance / viewport_width * 10.0f, 0.05f, 1.0f);
+}
+
+bool node_link_is_hidden_or_dimmed(const View2D *v2d, const bNodeLink *link)
+{
+ return nodeLinkIsHidden(link) || node_link_dim_factor(v2d, link) < 0.5f;
+}
+
/* ****************** Duplicate *********************** */
static void node_duplicate_reparent_recursive(bNode *node)
@@ -1275,9 +1302,8 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
}
- /* copy links between selected nodes
- * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy!
- */
+ /* Copy links between selected nodes.
+ * NOTE: this depends on correct node->new_node and sock->new_sock pointers from above copy! */
bNodeLink *lastlink = (bNodeLink *)ntree->links.last;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes.
@@ -2135,9 +2161,9 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- /* copy links between selected nodes
- * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy!
- */
+ /* Copy links between selected nodes.
+ * NOTE: this depends on correct node->new_node and sock->new_sock pointers from above copy! */
+
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes. */
if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode &&
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 09e5a110a45..df20420e472 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -265,6 +265,8 @@ int node_find_indicated_socket(struct SpaceNode *snode,
struct bNodeSocket **sockp,
const float cursor[2],
int in_out);
+float node_link_dim_factor(const struct View2D *v2d, const struct bNodeLink *link);
+bool node_link_is_hidden_or_dimmed(const struct View2D *v2d, const struct bNodeLink *link);
void NODE_OT_duplicate(struct wmOperatorType *ot);
void NODE_OT_delete(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index aadf93961e9..c6c3ca27d6e 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -36,6 +36,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_screen.h"
#include "ED_node.h" /* own include */
#include "ED_render.h"
@@ -298,7 +299,7 @@ static void pick_input_link_by_link_intersect(const bContext *C,
/* If no linked was picked in this call, try using the one picked in the previous call.
* Not essential for the basic behavior, but can make interaction feel a bit better if
- * the mouse moves to the right and loses the "selection." */
+ * the mouse moves to the right and loses the "selection." */
if (!link_to_pick) {
bNodeLink *last_picked_link = nldrag->last_picked_multi_input_socket_link;
if (last_picked_link) {
@@ -663,9 +664,19 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
nodeRemLink(snode->edittree, link);
/* find a socket after the previously connected socket */
- for (sock = sock->next; sock; sock = sock->next) {
- if (!nodeSocketIsHidden(sock)) {
- break;
+ if (ED_node_is_geometry(snode)) {
+ /* Geometry nodes viewer only supports geometry sockets for now. */
+ for (sock = sock->next; sock; sock = sock->next) {
+ if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) {
+ break;
+ }
+ }
+ }
+ else {
+ for (sock = sock->next; sock; sock = sock->next) {
+ if (!nodeSocketIsHidden(sock)) {
+ break;
+ }
}
}
}
@@ -673,19 +684,40 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
if (tonode) {
/* Find a selected socket that overrides the socket to connect to */
- LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
- if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
- sock = sock2;
- break;
+ if (ED_node_is_geometry(snode)) {
+ /* Geometry nodes viewer only supports geometry sockets for now. */
+ LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
+ if (sock2->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
+ sock = sock2;
+ break;
+ }
+ }
+ }
+ else {
+ LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
+ if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
+ sock = sock2;
+ break;
+ }
}
}
}
/* find a socket starting from the first socket */
if (!sock) {
- for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
- if (!nodeSocketIsHidden(sock)) {
- break;
+ if (ED_node_is_geometry(snode)) {
+ /* Geometry nodes viewer only supports geometry sockets for now. */
+ for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
+ if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) {
+ break;
+ }
+ }
+ }
+ else {
+ for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
+ if (!nodeSocketIsHidden(sock)) {
+ break;
+ }
}
}
}
@@ -1332,7 +1364,7 @@ static int cut_links_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) {
- if (nodeLinkIsHidden(link)) {
+ if (node_link_is_hidden_or_dimmed(&region->v2d, link)) {
continue;
}
@@ -1429,7 +1461,7 @@ static int mute_links_exec(bContext *C, wmOperator *op)
/* Count intersected links and clear test flag. */
int tot = 0;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
- if (nodeLinkIsHidden(link)) {
+ if (node_link_is_hidden_or_dimmed(&region->v2d, link)) {
continue;
}
link->flag &= ~NODE_LINK_TEST;
@@ -1443,7 +1475,7 @@ static int mute_links_exec(bContext *C, wmOperator *op)
/* Mute links. */
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
- if (nodeLinkIsHidden(link) || (link->flag & NODE_LINK_TEST)) {
+ if (node_link_is_hidden_or_dimmed(&region->v2d, link) || (link->flag & NODE_LINK_TEST)) {
continue;
}
@@ -1458,7 +1490,7 @@ static int mute_links_exec(bContext *C, wmOperator *op)
/* Clear remaining test flags. */
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
- if (nodeLinkIsHidden(link)) {
+ if (node_link_is_hidden_or_dimmed(&region->v2d, link)) {
continue;
}
link->flag &= ~NODE_LINK_TEST;
@@ -1894,9 +1926,11 @@ static bool ed_node_link_conditions(ScrArea *area,
return false;
}
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
+
/* test node for links */
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
- if (nodeLinkIsHidden(link)) {
+ if (node_link_is_hidden_or_dimmed(&region->v2d, link)) {
continue;
}
@@ -1927,13 +1961,15 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
return;
}
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
+
/* find link to select/highlight */
bNodeLink *selink = nullptr;
float dist_best = FLT_MAX;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
float coord_array[NODE_LINK_RESOL + 1][2];
- if (nodeLinkIsHidden(link)) {
+ if (node_link_is_hidden_or_dimmed(&region->v2d, link)) {
continue;
}
diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc
index a081cc83481..6c07c41f451 100644
--- a/source/blender/editors/space_node/node_select.cc
+++ b/source/blender/editors/space_node/node_select.cc
@@ -1196,7 +1196,7 @@ static void node_find_create_label(const bNode *node, char *str, int maxlen)
}
}
-/* generic search invoke */
+/* Generic search invoke. */
static void node_find_update_fn(const struct bContext *C,
void *UNUSED(arg),
const char *str,
diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc
index cbe33fab64e..648ede7abd5 100644
--- a/source/blender/editors/space_node/node_templates.cc
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -848,7 +848,10 @@ static void ui_node_draw_input(
break;
case SOCK_STRING: {
const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
- if (node_tree->type == NTREE_GEOMETRY) {
+ SpaceNode *snode = CTX_wm_space_node(C);
+ if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) {
+ /* Only add the attribute search in the node editor, in other places there is not
+ * enough context. */
node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, row);
}
else {
diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c
index 062d2e2b5d1..6538f5709b7 100644
--- a/source/blender/editors/space_outliner/outliner_collections.c
+++ b/source/blender/editors/space_outliner/outliner_collections.c
@@ -912,10 +912,9 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op)
.space_outliner = space_outliner,
};
bool clear = strstr(op->idname, "clear") != NULL;
- int flag = strstr(op->idname, "holdout") ?
- LAYER_COLLECTION_HOLDOUT :
- strstr(op->idname, "indirect_only") ? LAYER_COLLECTION_INDIRECT_ONLY :
- LAYER_COLLECTION_EXCLUDE;
+ int flag = strstr(op->idname, "holdout") ? LAYER_COLLECTION_HOLDOUT :
+ strstr(op->idname, "indirect_only") ? LAYER_COLLECTION_INDIRECT_ONLY :
+ LAYER_COLLECTION_EXCLUDE;
data.collections_to_edit = BLI_gset_ptr_new(__func__);
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index 35015356f0b..aaa52f6b649 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -697,7 +697,9 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED
{
Editing *ed = SEQ_editing_get(scene, false);
- /* XXX select_single_seq(seq, 1); */
+#if 0
+ select_single_seq(seq, 1);
+#endif
Sequence *p = ed->seqbasep->first;
while (p) {
if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) {
@@ -705,8 +707,11 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED
continue;
}
- /* XXX: if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) select_single_seq(p,
- * 0); */
+#if 0
+ if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) {
+ select_single_seq(p, 0);
+ }
+#endif
p = p->next;
}
}
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 8a3ba9a24c2..3edb12c5503 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -1747,8 +1747,6 @@ typedef enum eOutlinerIdOpTypes {
OUTLINER_IDOP_INVALID = 0,
OUTLINER_IDOP_UNLINK,
- OUTLINER_IDOP_MARK_ASSET,
- OUTLINER_IDOP_CLEAR_ASSET,
OUTLINER_IDOP_LOCAL,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
@@ -1775,8 +1773,6 @@ typedef enum eOutlinerIdOpTypes {
/* TODO: implement support for changing the ID-block used. */
static const EnumPropertyItem prop_id_op_types[] = {
{OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
- {OUTLINER_IDOP_MARK_ASSET, "MARK_ASSET", 0, "Mark Asset", ""},
- {OUTLINER_IDOP_CLEAR_ASSET, "CLEAR_ASSET", 0, "Clear Asset", ""},
{OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
{OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
{OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
@@ -1859,9 +1855,6 @@ static bool outliner_id_operation_item_poll(bContext *C,
}
switch (enum_value) {
- case OUTLINER_IDOP_MARK_ASSET:
- case OUTLINER_IDOP_CLEAR_ASSET:
- return U.experimental.use_asset_browser;
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE:
if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) {
return true;
@@ -2016,14 +2009,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
break;
}
- case OUTLINER_IDOP_MARK_ASSET: {
- WM_operator_name_call(C, "ASSET_OT_mark", WM_OP_EXEC_DEFAULT, NULL);
- break;
- }
- case OUTLINER_IDOP_CLEAR_ASSET: {
- WM_operator_name_call(C, "ASSET_OT_clear", WM_OP_EXEC_DEFAULT, NULL);
- break;
- }
case OUTLINER_IDOP_LOCAL: {
/* make local */
outliner_do_libdata_operation(
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 1239286d4da..b6b3d1841d2 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -1210,6 +1210,57 @@ static int sequencer_add_effect_strip_invoke(bContext *C,
return sequencer_add_effect_strip_exec(C, op);
}
+static char *sequencer_add_effect_strip_desc(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(op),
+ PointerRNA *ptr)
+{
+ const int type = RNA_enum_get(ptr, "type");
+
+ switch (type) {
+ case SEQ_TYPE_CROSS:
+ return BLI_strdup(TIP_("Add a crossfade transition to the sequencer"));
+ case SEQ_TYPE_ADD:
+ return BLI_strdup(TIP_("Add an add effect strip to the sequencer"));
+ case SEQ_TYPE_SUB:
+ return BLI_strdup(TIP_("Add a subtract effect strip to the sequencer"));
+ case SEQ_TYPE_ALPHAOVER:
+ return BLI_strdup(TIP_("Add an alpha over effect strip to the sequencer"));
+ case SEQ_TYPE_ALPHAUNDER:
+ return BLI_strdup(TIP_("Add an alpha under effect strip to the sequencer"));
+ case SEQ_TYPE_GAMCROSS:
+ return BLI_strdup(TIP_("Add a gamma cross transition to the sequencer"));
+ case SEQ_TYPE_MUL:
+ return BLI_strdup(TIP_("Add a multiply effect strip to the sequencer"));
+ case SEQ_TYPE_OVERDROP:
+ return BLI_strdup(TIP_("Add an alpha over drop effect strip to the sequencer"));
+ case SEQ_TYPE_WIPE:
+ return BLI_strdup(TIP_("Add a wipe transition to the sequencer"));
+ case SEQ_TYPE_GLOW:
+ return BLI_strdup(TIP_("Add a glow effect strip to the sequencer"));
+ case SEQ_TYPE_TRANSFORM:
+ return BLI_strdup(TIP_("Add a transform effect strip to the sequencer"));
+ case SEQ_TYPE_COLOR:
+ return BLI_strdup(TIP_("Add a color strip to the sequencer"));
+ case SEQ_TYPE_SPEED:
+ return BLI_strdup(TIP_("Add a speed effect strip to the sequencer"));
+ case SEQ_TYPE_MULTICAM:
+ return BLI_strdup(TIP_("Add a multicam selector effect strip to the sequencer"));
+ case SEQ_TYPE_ADJUSTMENT:
+ return BLI_strdup(TIP_("Add an adjustment layer effect strip to the sequencer"));
+ case SEQ_TYPE_GAUSSIAN_BLUR:
+ return BLI_strdup(TIP_("Add a gaussian blur effect strip to the sequencer"));
+ case SEQ_TYPE_TEXT:
+ return BLI_strdup(TIP_("Add a text strip to the sequencer"));
+ case SEQ_TYPE_COLORMIX:
+ return BLI_strdup(TIP_("Add a color mix effect strip to the sequencer"));
+ default:
+ break;
+ }
+
+ /* Use default description. */
+ return NULL;
+}
+
void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -1224,6 +1275,7 @@ void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot)
ot->exec = sequencer_add_effect_strip_exec;
ot->poll = ED_operator_sequencer_active_editable;
ot->poll_property = seq_effect_add_properties_poll;
+ ot->get_description = sequencer_add_effect_strip_desc;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index cdbe5bc63ce..3f8dea8b533 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -100,11 +100,7 @@
#define SEQ_HANDLE_SIZE 8.0f
#define SEQ_SCROLLER_TEXT_OFFSET 8
#define MUTE_ALPHA 120
-
-/* NOTE: Don't use SEQ_ALL_BEGIN/SEQ_ALL_END while drawing!
- * it messes up transform. */
-#undef SEQ_ALL_BEGIN
-#undef SEQ_ALL_END
+#define OVERLAP_ALPHA 180
static Sequence *special_seq_update = NULL;
@@ -802,11 +798,17 @@ static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y,
uchar col[4];
SolidColorVars *colvars = (SolidColorVars *)seq->effectdata;
+ GPU_blend(GPU_BLEND_ALPHA);
rgb_float_to_uchar(col, colvars->col);
+
+ /* Draw muted strips semi-transparent. */
if (seq->flag & SEQ_MUTE) {
- GPU_blend(GPU_BLEND_ALPHA);
col[3] = MUTE_ALPHA;
}
+ /* Draw background semi-transparent when overlapping strips. */
+ else if (seq->flag & SEQ_OVERLAP) {
+ col[3] = OVERLAP_ALPHA;
+ }
else {
col[3] = 255;
}
@@ -824,9 +826,7 @@ static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y,
immVertex2f(pos, seq->enddisp, text_margin_y);
immEnd();
- if (seq->flag & SEQ_MUTE) {
- GPU_blend(GPU_BLEND_NONE);
- }
+ GPU_blend(GPU_BLEND_NONE);
}
static void draw_seq_background(Scene *scene,
@@ -839,6 +839,7 @@ static void draw_seq_background(Scene *scene,
bool is_single_image)
{
uchar col[4];
+ GPU_blend(GPU_BLEND_ALPHA);
/* Get the correct color per strip type, transitions use their inputs ones. */
if (ELEM(seq->type, SEQ_TYPE_CROSS, SEQ_TYPE_GAMCROSS, SEQ_TYPE_WIPE)) {
@@ -855,14 +856,18 @@ static void draw_seq_background(Scene *scene,
color3ubv_from_seq(scene, seq, col);
}
+ /* Draw muted strips semi-transparent. */
if (seq->flag & SEQ_MUTE) {
- GPU_blend(GPU_BLEND_ALPHA);
-
col[3] = MUTE_ALPHA;
}
+ /* Draw background semi-transparent when overlapping strips. */
+ else if (seq->flag & SEQ_OVERLAP) {
+ col[3] = OVERLAP_ALPHA;
+ }
else {
col[3] = 255;
}
+
immUniformColor4ubv(col);
/* Draw the main strip body. */
@@ -922,9 +927,7 @@ static void draw_seq_background(Scene *scene,
immEnd();
}
- if (seq->flag & SEQ_MUTE) {
- GPU_blend(GPU_BLEND_NONE);
- }
+ GPU_blend(GPU_BLEND_NONE);
}
static void draw_seq_locked(float x1, float y1, float x2, float y2)
@@ -1978,7 +1981,7 @@ static void draw_seq_backdrop(View2D *v2d)
/* Lines separating the horizontal bands. */
i = max_ii(1, ((int)v2d->cur.ymin) - 1);
int line_len = (int)v2d->cur.ymax - i + 1;
- immUniformThemeColor(TH_GRID);
+ immUniformThemeColorShade(TH_GRID, 10);
immBegin(GPU_PRIM_LINES, line_len * 2);
while (line_len--) {
immVertex2f(pos, v2d->cur.xmax, i);
@@ -2416,7 +2419,12 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
/* Get timeline bound-box, needed for the scroll-bars. */
SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot);
draw_seq_backdrop(v2d);
- UI_view2d_constant_grid_draw(v2d, FPS);
+ if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_GRID)) {
+ U.v2d_min_gridsize *= 3;
+ UI_view2d_draw_lines_x__discrete_frames_or_seconds(
+ v2d, scene, (sseq->flag & SEQ_DRAWFRAMES) == 0, false);
+ U.v2d_min_gridsize /= 3;
+ }
/* Only draw backdrop in timeline view. */
if (sseq->view == SEQ_VIEW_SEQUENCE && sseq->draw_flag & SEQ_DRAW_BACKDROP) {
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 75cf8542f67..4b26469aad3 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -2934,6 +2934,23 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot)
/** \name Export Subtitles Operator
* \{ */
+/** Comparison function suitable to be used with BLI_listbase_sort(). */
+static int seq_cmp_time_startdisp_channel(const void *a, const void *b)
+{
+ Sequence *seq_a = (Sequence *)a;
+ Sequence *seq_b = (Sequence *)b;
+
+ int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a);
+ int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b);
+
+ /** If strips have the same start frame favor the one with a higher channel. **/
+ if (seq_a_start == seq_b_start) {
+ return seq_a->machine > seq_b->machine;
+ }
+
+ return (seq_a_start > seq_b_start);
+}
+
static int sequencer_export_subtitles_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
@@ -3003,7 +3020,7 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BLI_listbase_sort(&text_seq, SEQ_time_cmp_time_startdisp);
+ BLI_listbase_sort(&text_seq, seq_cmp_time_startdisp_channel);
/* Open and write file. */
file = BLI_fopen(filepath, "w");
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index cf26d1e3243..6de95f0995a 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -100,7 +100,7 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce
sseq->mainb = SEQ_DRAW_IMG_IMBUF;
sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES |
SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME |
- SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION;
+ SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION | SEQ_SHOW_GRID;
/* Tool header. */
region = MEM_callocN(sizeof(ARegion), "tool header for sequencer");
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
index c9b73aabf96..680da9b6794 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
@@ -22,8 +22,8 @@
#include "BLI_float2.hh"
#include "BLI_float3.hh"
-struct Object;
struct Collection;
+struct Object;
namespace blender::ed::spreadsheet {
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
index 1ac2075e281..c38e765caee 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
@@ -489,7 +489,7 @@ bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet *
break;
}
SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)node_context_path[i];
- if (!STREQ(node_context->node_name, tree_path[i]->node_name)) {
+ if (!STREQ(node_context->node_name, tree_path[i + 1]->node_name)) {
break;
}
valid_count++;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh
index d9e6d882c2a..19906d73e7f 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh
@@ -23,9 +23,9 @@
#include "spreadsheet_dataset_layout.hh"
struct ARegion;
-struct uiBlock;
struct View2D;
struct bContext;
+struct uiBlock;
namespace blender::ed::spreadsheet {
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
index 647587ec8b0..9accd1d3d09 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
@@ -18,9 +18,9 @@
#include "BLI_vector.hh"
-struct uiBlock;
-struct bContext;
struct ARegion;
+struct bContext;
+struct uiBlock;
namespace blender::ed::spreadsheet {
diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c
index 16eb66624ce..0cd2d9baa0b 100644
--- a/source/blender/editors/space_text/text_format_lua.c
+++ b/source/blender/editors/space_text/text_format_lua.c
@@ -165,9 +165,9 @@ static char txtfmt_lua_format_identifier(const char *str)
/* Keep aligned args for readability. */
/* clang-format off */
- if ((txtfmt_lua_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL;
- } else if ((txtfmt_lua_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD;
- } else { fmt = FMT_TYPE_DEFAULT;
+ if (txtfmt_lua_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL;
+ } else if (txtfmt_lua_find_keyword(str) != -1) { fmt = FMT_TYPE_KEYWORD;
+ } else { fmt = FMT_TYPE_DEFAULT;
}
/* clang-format on */
diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c
index 1a024779a83..97d9ec546ca 100644
--- a/source/blender/editors/space_text/text_format_osl.c
+++ b/source/blender/editors/space_text/text_format_osl.c
@@ -189,11 +189,11 @@ static char txtfmt_osl_format_identifier(const char *str)
/* Keep aligned args for readability. */
/* clang-format off */
- if ((txtfmt_osl_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL;
- } else if ((txtfmt_osl_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD;
- } else if ((txtfmt_osl_find_reserved(str)) != -1) { fmt = FMT_TYPE_RESERVED;
- } else if ((txtfmt_osl_find_preprocessor(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE;
- } else { fmt = FMT_TYPE_DEFAULT;
+ if (txtfmt_osl_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL;
+ } else if (txtfmt_osl_find_builtinfunc(str) != -1) { fmt = FMT_TYPE_KEYWORD;
+ } else if (txtfmt_osl_find_reserved(str) != -1) { fmt = FMT_TYPE_RESERVED;
+ } else if (txtfmt_osl_find_preprocessor(str) != -1) { fmt = FMT_TYPE_DIRECTIVE;
+ } else { fmt = FMT_TYPE_DEFAULT;
}
/* clang-format on */
diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c
index 1200dda7533..ea3d0ec1478 100644
--- a/source/blender/editors/space_text/text_format_pov.c
+++ b/source/blender/editors/space_text/text_format_pov.c
@@ -762,11 +762,11 @@ static char txtfmt_pov_format_identifier(const char *str)
/* Keep aligned args for readability. */
/* clang-format off */
- if ((txtfmt_pov_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL;
- } else if ((txtfmt_pov_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD;
- } else if ((txtfmt_pov_find_reserved_keywords(str)) != -1) { fmt = FMT_TYPE_RESERVED;
- } else if ((txtfmt_pov_find_reserved_builtins(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE;
- } else { fmt = FMT_TYPE_DEFAULT;
+ if (txtfmt_pov_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL;
+ } else if (txtfmt_pov_find_keyword(str) != -1) { fmt = FMT_TYPE_KEYWORD;
+ } else if (txtfmt_pov_find_reserved_keywords(str) != -1) { fmt = FMT_TYPE_RESERVED;
+ } else if (txtfmt_pov_find_reserved_builtins(str) != -1) { fmt = FMT_TYPE_DIRECTIVE;
+ } else { fmt = FMT_TYPE_DEFAULT;
}
/* clang-format on */
diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c
index 1c6a93d2d7a..259ad02a6b7 100644
--- a/source/blender/editors/space_text/text_format_pov_ini.c
+++ b/source/blender/editors/space_text/text_format_pov_ini.c
@@ -347,10 +347,10 @@ static int txtfmt_ini_find_bool(const char *string)
static char txtfmt_pov_ini_format_identifier(const char *str)
{
char fmt;
- if ((txtfmt_ini_find_keyword(str)) != -1) {
+ if (txtfmt_ini_find_keyword(str) != -1) {
fmt = FMT_TYPE_KEYWORD;
}
- else if ((txtfmt_ini_find_reserved(str)) != -1) {
+ else if (txtfmt_ini_find_reserved(str) != -1) {
fmt = FMT_TYPE_RESERVED;
}
else {
diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c
index 2717c0bf5b0..e2a01a8d85d 100644
--- a/source/blender/editors/space_text/text_format_py.c
+++ b/source/blender/editors/space_text/text_format_py.c
@@ -315,10 +315,10 @@ static char txtfmt_py_format_identifier(const char *str)
/* Keep aligned args for readability. */
/* clang-format off */
- if ((txtfmt_py_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL;
- } else if ((txtfmt_py_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD;
- } else if ((txtfmt_py_find_decorator(str)) != -1) { fmt = FMT_TYPE_RESERVED;
- } else { fmt = FMT_TYPE_DEFAULT;
+ if (txtfmt_py_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL;
+ } else if (txtfmt_py_find_builtinfunc(str) != -1) { fmt = FMT_TYPE_KEYWORD;
+ } else if (txtfmt_py_find_decorator(str) != -1) { fmt = FMT_TYPE_RESERVED;
+ } else { fmt = FMT_TYPE_DEFAULT;
}
/* clang-format on */
diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c
index 7c799f0d97b..63506678b70 100644
--- a/source/blender/editors/space_userpref/userpref_ops.c
+++ b/source/blender/editors/space_userpref/userpref_ops.c
@@ -26,11 +26,16 @@
#include "DNA_screen_types.h"
#include "BLI_listbase.h"
+#ifdef WIN32
+# include "BLI_winstuff.h"
+#endif
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_preferences.h"
+#include "BKE_report.h"
+
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_types.h"
@@ -188,6 +193,56 @@ static void PREFERENCES_OT_asset_library_remove(wmOperatorType *ot)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Associate File Type Operator (Windows only)
+ * \{ */
+
+static bool associate_blend_poll(bContext *C)
+{
+#ifdef WIN32
+ UNUSED_VARS(C);
+ return true;
+#else
+ CTX_wm_operator_poll_msg_set(C, "Windows-only operator");
+ return false;
+#endif
+}
+
+static int associate_blend_exec(bContext *UNUSED(C), wmOperator *op)
+{
+#ifdef WIN32
+ WM_cursor_wait(true);
+ if (BLI_windows_register_blend_extension(true)) {
+ BKE_report(op->reports, RPT_INFO, "File association registered");
+ WM_cursor_wait(false);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Unable to register file association");
+ WM_cursor_wait(false);
+ return OPERATOR_CANCELLED;
+ }
+#else
+ UNUSED_VARS(op);
+ BLI_assert_unreachable();
+ return OPERATOR_CANCELLED;
+#endif
+}
+
+static void PREFERENCES_OT_associate_blend(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Register File Association";
+ ot->description = "Use this installation for .blend files and to display thumbnails";
+ ot->idname = "PREFERENCES_OT_associate_blend";
+
+ /* api callbacks */
+ ot->exec = associate_blend_exec;
+ ot->poll = associate_blend_poll;
+}
+
+/** \} */
+
void ED_operatortypes_userpref(void)
{
WM_operatortype_append(PREFERENCES_OT_reset_default_theme);
@@ -197,4 +252,6 @@ void ED_operatortypes_userpref(void)
WM_operatortype_append(PREFERENCES_OT_asset_library_add);
WM_operatortype_append(PREFERENCES_OT_asset_library_remove);
+
+ WM_operatortype_append(PREFERENCES_OT_associate_blend);
}
diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c
index 638c8a49ffd..8380c87b999 100644
--- a/source/blender/editors/space_view3d/view3d_camera_control.c
+++ b/source/blender/editors/space_view3d/view3d_camera_control.c
@@ -24,7 +24,7 @@
* Typical view-control usage:
*
* - Acquire a view-control (#ED_view3d_cameracontrol_acquire).
- * - Modify ``rv3d->ofs``, ``rv3d->viewquat``.
+ * - Modify `rv3d->ofs`, `rv3d->viewquat`.
* - Update the view data (#ED_view3d_cameracontrol_acquire) -
* within a loop which draws the viewport.
* - Finish and release the view-control (#ED_view3d_cameracontrol_release),
@@ -32,8 +32,8 @@
*
* Notes:
*
- * - when acquiring ``rv3d->dist`` is set to zero
- * (so ``rv3d->ofs`` is always the view-point)
+ * - when acquiring `rv3d->dist` is set to zero
+ * (so `rv3d->ofs` is always the view-point)
* - updating can optionally keyframe the camera object.
*/
@@ -244,7 +244,7 @@ static bool object_apply_mat4_with_protect(Object *ob,
}
/**
- * Updates cameras from the ``rv3d`` values, optionally auto-keyframing.
+ * Updates cameras from the `rv3d` values, optionally auto-keyframing.
*/
void ED_view3d_cameracontrol_update(View3DCameraControl *vctrl,
/* args for keyframing */
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index c97ba7ba7e9..d87c14b9844 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1602,7 +1602,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *region)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Offscreen Drawing
+/** \name Off-screen Drawing
* \{ */
static void view3d_stereo3d_setup_offscreen(Depsgraph *depsgraph,
diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c
index c7a4030c402..20e00356152 100644
--- a/source/blender/editors/space_view3d/view3d_iterators.c
+++ b/source/blender/editors/space_view3d/view3d_iterators.c
@@ -439,7 +439,7 @@ void mesh_foreachScreenEdge(ViewContext *vc,
}
BM_mesh_elem_table_ensure(vc->em->bm, BM_EDGE);
- BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge__mapFunc, &data);
+ BKE_mesh_foreach_mapped_edge(me, vc->em->bm->totedge, mesh_foreachScreenEdge__mapFunc, &data);
}
/** \} */
@@ -529,10 +529,11 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc,
if ((clip_flag & V3D_PROJ_TEST_CLIP_BB) && (vc->rv3d->clipbb != NULL)) {
ED_view3d_clipping_local(vc->rv3d, vc->obedit->obmat); /* for local clipping lookups. */
- BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge_clip_bb_segment__mapFunc, &data);
+ BKE_mesh_foreach_mapped_edge(
+ me, vc->em->bm->totedge, mesh_foreachScreenEdge_clip_bb_segment__mapFunc, &data);
}
else {
- BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge__mapFunc, &data);
+ BKE_mesh_foreach_mapped_edge(me, vc->em->bm->totedge, mesh_foreachScreenEdge__mapFunc, &data);
}
}
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 8bcc05c1e55..e09453b9957 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -506,8 +506,8 @@ void ED_view3d_lock_clear(View3D *v3d)
/**
* For viewport operators that exit camera perspective.
*
- * \note This differs from simply setting ``rv3d->persp = persp`` because it
- * sets the ``ofs`` and ``dist`` values of the viewport so it matches the camera,
+ * \note This differs from simply setting `rv3d->persp = persp` because it
+ * sets the `ofs` and `dist` values of the viewport so it matches the camera,
* otherwise switching out of camera view may jump to a different part of the scene.
*/
void ED_view3d_persp_switch_from_camera(const Depsgraph *depsgraph,
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index d34cc6f424f..efcf7d587e1 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -78,7 +78,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]);
bool transdata_check_local_islands(TransInfo *t, short around)
{
- return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH, OB_GPENCIL))));
+ return ((around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->obedit_type, OB_MESH, OB_GPENCIL)));
}
/* ************************** SPACE DEPENDENT CODE **************************** */
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 383f9870714..7377e47da3d 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -1981,7 +1981,7 @@ static void tc_mesh_partial_update(TransInfo *t,
/* Promote the partial update types based on the previous state
* so the values that no longer modified are reset before being left as-is.
- * Needed for translation which can toggle snap-to-normal during transform. */
+ * Needed for translation which can toggle snap-to-normal during transform. */
const enum ePartialType partial_for_looptri = MAX2(partial_state->for_looptri,
partial_state_prev->for_looptri);
const enum ePartialType partial_for_normals = MAX2(partial_state->for_normals,
@@ -2067,27 +2067,6 @@ static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc)
}
}
-static bool tc_mesh_is_deform_only_update(TransInfo *t, TransDataContainer *tc)
-{
- if (tc->custom.type.data &&
- ((struct TransCustomDataMesh *)tc->custom.type.data)->cd_layer_correct) {
- return false;
- }
-
- Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(t->depsgraph, (ID *)tc->obedit->data);
- Mesh *mesh_eval_cage = me_eval->edit_mesh->mesh_eval_cage;
- Mesh *mesh_eval_final = me_eval->edit_mesh->mesh_eval_final;
- if (mesh_eval_cage && !mesh_eval_cage->runtime.is_original) {
- return false;
- }
- if (mesh_eval_final && mesh_eval_final != mesh_eval_cage &&
- !mesh_eval_final->runtime.is_original) {
- return false;
- }
-
- return me_eval->runtime.deformed_only;
-}
-
void recalcData_mesh(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
@@ -2115,10 +2094,7 @@ void recalcData_mesh(TransInfo *t)
tc_mesh_partial_types_calc(t, &partial_state);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- const bool is_deform_only = tc_mesh_is_deform_only_update(t, tc);
-
- DEG_id_tag_update(tc->obedit->data,
- is_deform_only ? ID_RECALC_GEOMETRY_DEFORM : ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
tc_mesh_partial_update(t, tc, &partial_state);
}
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index a6f5aba5a1d..17512c79d03 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -262,8 +262,16 @@ static void free_transform_custom_data(TransCustomData *custom_data)
/* Canceled, need to update the strips display. */
static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips)
{
+ ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false));
+
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, transformed_strips) {
+ /* Handle pre-existing overlapping strips even when operator is canceled.
+ * This is necessary for SEQUENCER_OT_duplicate_move macro for example. */
+ if (SEQ_transform_test_overlap(seqbase, seq)) {
+ SEQ_transform_seqbase_shuffle(seqbase, seq, t->scene);
+ }
+
SEQ_time_update_sequence_bounds(t->scene, seq);
}
}
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index aaac8e21cb9..e89ab6729d2 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -343,9 +343,9 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else {
if (region) {
- /* XXX for now, get View2D from the active region */
+ /* XXX: For now, get View2D from the active region. */
t->view = &region->v2d;
- /* XXX for now, the center point is the midpoint of the data */
+ /* XXX: For now, the center point is the midpoint of the data. */
}
else {
t->view = NULL;
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 080a19cce1f..279dca9731d 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -354,10 +354,9 @@ static void gizmo_get_axis_color(const int axis_idx,
if (is_plane) {
idot_axis = 1.0f - idot_axis;
}
- alpha_fac = ((idot_axis > idot_max) ?
- 1.0f :
- (idot_axis < idot_min) ? 0.0f :
- ((idot_axis - idot_min) / (idot_max - idot_min)));
+ alpha_fac = ((idot_axis > idot_max) ? 1.0f :
+ (idot_axis < idot_min) ? 0.0f :
+ ((idot_axis - idot_min) / (idot_max - idot_min)));
}
else {
alpha_fac = 1.0f;
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 2cbf52b6100..75744f26c15 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -266,7 +266,8 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
if (t->flag & T_AUTOIK) {
short chainlen = t->settings->autoik_chainlen;
if (chainlen) {
- ofs += BLI_snprintf_rlen(str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("AutoIK-Len: %d"), chainlen);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Auto IK Length: %d"), chainlen);
ofs += BLI_strncpy_rlen(str + ofs, " ", UI_MAX_DRAW_STR - ofs);
}
}
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index 155250261de..33f4b06eb0e 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -537,10 +537,10 @@ short ED_transform_calc_orientation_from_type_ex(const bContext *C,
case V3D_ORIENT_LOCAL: {
if (ob) {
if (ob->mode & OB_MODE_POSE) {
- /* each bone moves on its own local axis, but to avoid confusion,
+ /* Each bone moves on its own local axis, but to avoid confusion,
* use the active pones axis for display T33575, this works as expected on a single
* bone and users who select many bones will understand what's going on and what local
- * means when they start transforming */
+ * means when they start transforming. */
ED_getTransformOrientationMatrix(C, ob, obedit, pivot_point, r_mat);
}
else {
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 2d98d756dba..bb04f557074 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -1142,7 +1142,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
* \param r_loc: Hit location.
* \param r_no: Hit normal (optional).
* \param r_index: Hit index or -1 when no valid index is found.
- * (currently only set to the polygon index when using ``snap_to == SCE_SNAP_MODE_FACE``).
+ * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`).
* \param r_ob: Hit object.
* \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances).
* \param r_hit_list: List of #SnapObjectHitDepth (caller must free).
@@ -1213,49 +1213,56 @@ static bool snap_bound_box_check_dist(const float min[3],
return true;
}
-static void cb_mvert_co_get(const int index, const float **co, const BVHTreeFromMesh *data)
+static void cb_mvert_co_get(const int index, const void *user_data, const float **r_co)
{
- *co = data->vert[index].co;
+ const BVHTreeFromMesh *data = user_data;
+ *r_co = data->vert[index].co;
}
-static void cb_bvert_co_get(const int index, const float **co, const BMEditMesh *data)
+static void cb_bvert_co_get(const int index, const void *user_data, const float **r_co)
{
+ const BMEditMesh *data = user_data;
BMVert *eve = BM_vert_at_index(data->bm, index);
- *co = eve->co;
+ *r_co = eve->co;
}
-static void cb_mvert_no_copy(const int index, float r_no[3], const BVHTreeFromMesh *data)
+static void cb_mvert_no_copy(const int index, const void *user_data, float r_no[3])
{
+ const BVHTreeFromMesh *data = user_data;
const MVert *vert = data->vert + index;
normal_short_to_float_v3(r_no, vert->no);
}
-static void cb_bvert_no_copy(const int index, float r_no[3], const BMEditMesh *data)
+static void cb_bvert_no_copy(const int index, const void *user_data, float r_no[3])
{
+ const BMEditMesh *data = user_data;
BMVert *eve = BM_vert_at_index(data->bm, index);
copy_v3_v3(r_no, eve->no);
}
-static void cb_medge_verts_get(const int index, int v_index[2], const BVHTreeFromMesh *data)
+static void cb_medge_verts_get(const int index, const void *user_data, int r_v_index[2])
{
+ const BVHTreeFromMesh *data = user_data;
const MEdge *edge = &data->edge[index];
- v_index[0] = edge->v1;
- v_index[1] = edge->v2;
+ r_v_index[0] = edge->v1;
+ r_v_index[1] = edge->v2;
}
-static void cb_bedge_verts_get(const int index, int v_index[2], const BMEditMesh *data)
+static void cb_bedge_verts_get(const int index, const void *user_data, int r_v_index[2])
{
+ const BMEditMesh *data = user_data;
BMEdge *eed = BM_edge_at_index(data->bm, index);
- v_index[0] = BM_elem_index_get(eed->v1);
- v_index[1] = BM_elem_index_get(eed->v2);
+ r_v_index[0] = BM_elem_index_get(eed->v1);
+ r_v_index[1] = BM_elem_index_get(eed->v2);
}
-static void cb_mlooptri_edges_get(const int index, int v_index[3], const BVHTreeFromMesh *data)
+static void cb_mlooptri_edges_get(const int index, const void *user_data, int r_v_index[3])
{
+ const BVHTreeFromMesh *data = user_data;
const MEdge *medge = data->edge;
const MLoop *mloop = data->loop;
const MLoopTri *lt = &data->looptri[index];
@@ -1264,22 +1271,23 @@ static void cb_mlooptri_edges_get(const int index, int v_index[3], const BVHTree
const uint tri_edge[2] = {mloop[lt->tri[j]].v, mloop[lt->tri[j_next]].v};
if (ELEM(ed->v1, tri_edge[0], tri_edge[1]) && ELEM(ed->v2, tri_edge[0], tri_edge[1])) {
// printf("real edge found\n");
- v_index[j] = mloop[lt->tri[j]].e;
+ r_v_index[j] = mloop[lt->tri[j]].e;
}
else {
- v_index[j] = -1;
+ r_v_index[j] = -1;
}
}
}
-static void cb_mlooptri_verts_get(const int index, int v_index[3], const BVHTreeFromMesh *data)
+static void cb_mlooptri_verts_get(const int index, const void *user_data, int r_v_index[3])
{
+ const BVHTreeFromMesh *data = user_data;
const MLoop *loop = data->loop;
const MLoopTri *looptri = &data->looptri[index];
- v_index[0] = loop[looptri->tri[0]].v;
- v_index[1] = loop[looptri->tri[1]].v;
- v_index[2] = loop[looptri->tri[2]].v;
+ r_v_index[0] = loop[looptri->tri[0]].v;
+ r_v_index[1] = loop[looptri->tri[1]].v;
+ r_v_index[2] = loop[looptri->tri[2]].v;
}
static bool test_projected_vert_dist(const struct DistProjectedAABBPrecalc *precalc,
@@ -1348,12 +1356,20 @@ static bool test_projected_edge_dist(const struct DistProjectedAABBPrecalc *prec
/** \name Walk DFS
* \{ */
-typedef void (*Nearest2DGetVertCoCallback)(const int index, const float **co, void *data);
-typedef void (*Nearest2DGetEdgeVertsCallback)(const int index, const int v_index[2], void *data);
-typedef void (*Nearest2DGetTriVertsCallback)(const int index, const int v_index[3], void *data);
+typedef void (*Nearest2DGetVertCoCallback)(const int index,
+ const void *user_data,
+ const float **r_co);
+typedef void (*Nearest2DGetEdgeVertsCallback)(const int index,
+ const void *user_data,
+ int r_v_index[2]);
+typedef void (*Nearest2DGetTriVertsCallback)(const int index,
+ const void *user_data,
+ int r_v_index[3]);
/* Equal the previous one */
-typedef void (*Nearest2DGetTriEdgesCallback)(const int index, const int e_index[3], void *data);
-typedef void (*Nearest2DCopyVertNoCallback)(const int index, const float r_no[3], void *data);
+typedef void (*Nearest2DGetTriEdgesCallback)(const int index,
+ const void *user_data,
+ int r_e_index[3]);
+typedef void (*Nearest2DCopyVertNoCallback)(const int index, const void *user_data, float r_no[3]);
typedef struct Nearest2dUserData {
void *userdata;
@@ -1374,18 +1390,18 @@ static void nearest2d_data_init(SnapObjectData *sod,
{
if (sod->type == SNAP_MESH) {
r_nearest2d->userdata = &sod->treedata_mesh;
- r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
- r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
- r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
- r_nearest2d->get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get;
- r_nearest2d->get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get;
+ r_nearest2d->get_vert_co = cb_mvert_co_get;
+ r_nearest2d->get_edge_verts_index = cb_medge_verts_get;
+ r_nearest2d->copy_vert_no = cb_mvert_no_copy;
+ r_nearest2d->get_tri_verts_index = cb_mlooptri_verts_get;
+ r_nearest2d->get_tri_edges_index = cb_mlooptri_edges_get;
}
else {
BLI_assert(sod->type == SNAP_EDIT_MESH);
r_nearest2d->userdata = sod->treedata_editmesh.em;
- r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
- r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
- r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
+ r_nearest2d->get_vert_co = cb_bvert_co_get;
+ r_nearest2d->get_edge_verts_index = cb_bedge_verts_get;
+ r_nearest2d->copy_vert_no = cb_bvert_no_copy;
r_nearest2d->get_tri_verts_index = NULL;
r_nearest2d->get_tri_edges_index = NULL;
}
@@ -1404,7 +1420,7 @@ static void cb_snap_vert(void *userdata,
struct Nearest2dUserData *data = userdata;
const float *co;
- data->get_vert_co(index, &co, data->userdata);
+ data->get_vert_co(index, data->userdata, &co);
if (test_projected_vert_dist(precalc,
clip_plane,
@@ -1413,7 +1429,7 @@ static void cb_snap_vert(void *userdata,
co,
&nearest->dist_sq,
nearest->co)) {
- data->copy_vert_no(index, nearest->no, data->userdata);
+ data->copy_vert_no(index, data->userdata, nearest->no);
nearest->index = index;
}
}
@@ -1428,11 +1444,11 @@ static void cb_snap_edge(void *userdata,
struct Nearest2dUserData *data = userdata;
int vindex[2];
- data->get_edge_verts_index(index, vindex, data->userdata);
+ data->get_edge_verts_index(index, data->userdata, vindex);
const float *v_pair[2];
- data->get_vert_co(vindex[0], &v_pair[0], data->userdata);
- data->get_vert_co(vindex[1], &v_pair[1], data->userdata);
+ data->get_vert_co(vindex[0], data->userdata, &v_pair[0]);
+ data->get_vert_co(vindex[1], data->userdata, &v_pair[1]);
if (test_projected_edge_dist(precalc,
clip_plane,
@@ -1457,7 +1473,7 @@ static void cb_snap_edge_verts(void *userdata,
struct Nearest2dUserData *data = userdata;
int vindex[2];
- data->get_edge_verts_index(index, vindex, data->userdata);
+ data->get_edge_verts_index(index, data->userdata, vindex);
for (int i = 2; i--;) {
if (vindex[i] == nearest->index) {
@@ -1478,12 +1494,12 @@ static void cb_snap_tri_edges(void *userdata,
if (data->use_backface_culling) {
int vindex[3];
- data->get_tri_verts_index(index, vindex, data->userdata);
+ data->get_tri_verts_index(index, data->userdata, vindex);
const float *t0, *t1, *t2;
- data->get_vert_co(vindex[0], &t0, data->userdata);
- data->get_vert_co(vindex[1], &t1, data->userdata);
- data->get_vert_co(vindex[2], &t2, data->userdata);
+ data->get_vert_co(vindex[0], data->userdata, &t0);
+ data->get_vert_co(vindex[1], data->userdata, &t1);
+ data->get_vert_co(vindex[2], data->userdata, &t2);
float dummy[3];
if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) {
return;
@@ -1491,7 +1507,7 @@ static void cb_snap_tri_edges(void *userdata,
}
int eindex[3];
- data->get_tri_edges_index(index, eindex, data->userdata);
+ data->get_tri_edges_index(index, data->userdata, eindex);
for (int i = 3; i--;) {
if (eindex[i] != -1) {
if (eindex[i] == nearest->index) {
@@ -1512,13 +1528,13 @@ static void cb_snap_tri_verts(void *userdata,
struct Nearest2dUserData *data = userdata;
int vindex[3];
- data->get_tri_verts_index(index, vindex, data->userdata);
+ data->get_tri_verts_index(index, data->userdata, vindex);
if (data->use_backface_culling) {
const float *t0, *t1, *t2;
- data->get_vert_co(vindex[0], &t0, data->userdata);
- data->get_vert_co(vindex[1], &t1, data->userdata);
- data->get_vert_co(vindex[2], &t2, data->userdata);
+ data->get_vert_co(vindex[0], data->userdata, &t0);
+ data->get_vert_co(vindex[1], data->userdata, &t1);
+ data->get_vert_co(vindex[2], data->userdata, &t2);
float dummy[3];
if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) {
return;
@@ -1693,11 +1709,11 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
int vindex[2];
- nearest2d.get_edge_verts_index(*r_index, vindex, nearest2d.userdata);
+ nearest2d.get_edge_verts_index(*r_index, nearest2d.userdata, vindex);
const float *v_pair[2];
- nearest2d.get_vert_co(vindex[0], &v_pair[0], nearest2d.userdata);
- nearest2d.get_vert_co(vindex[1], &v_pair[1], nearest2d.userdata);
+ nearest2d.get_vert_co(vindex[0], nearest2d.userdata, &v_pair[0]);
+ nearest2d.get_vert_co(vindex[1], nearest2d.userdata, &v_pair[1]);
struct DistProjectedAABBPrecalc neasrest_precalc;
{
@@ -1744,7 +1760,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
if (r_no) {
float imat[4][4];
invert_m4_m4(imat, obmat);
- nearest2d.copy_vert_no(vindex[v_id], r_no, nearest2d.userdata);
+ nearest2d.copy_vert_no(vindex[v_id], nearest2d.userdata, r_no);
mul_transposed_mat3_m4_v3(imat, r_no);
normalize_v3(r_no);
}
@@ -2777,7 +2793,7 @@ static void snap_obj_fn(SnapObjectContext *sctx,
* \param r_loc: Hit location.
* \param r_no: Hit normal (optional).
* \param r_index: Hit index or -1 when no valid index is found.
- * (currently only set to the polygon index when using ``snap_to == SCE_SNAP_MODE_FACE``).
+ * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`).
* \param r_ob: Hit object.
* \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances).
*/
diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c
index d0b730383d5..a1f396eb503 100644
--- a/source/blender/editors/transform/transform_snap_sequencer.c
+++ b/source/blender/editors/transform/transform_snap_sequencer.c
@@ -34,6 +34,7 @@
#include "UI_view2d.h"
+#include "SEQ_effects.h"
#include "SEQ_iterator.h"
#include "SEQ_sequencer.h"
@@ -104,11 +105,41 @@ static void seq_snap_source_points_build(const TransInfo *UNUSED(t),
/** \name Snap targets
* \{ */
-static SeqCollection *query_snap_targets(const TransInfo *t)
+/* Add effect strips directly or indirectly connected to `seq_reference` to `collection`. */
+static void query_strip_effects_fn(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection)
{
- const ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false));
+ if (!SEQ_collection_append_strip(seq_reference, collection)) {
+ return; /* Strip is already in set, so all effects connected to it are as well. */
+ }
+
+ /* Find all strips connected to `seq_reference`. */
+ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
+ if (seq_test->seq1 == seq_reference || seq_test->seq2 == seq_reference ||
+ seq_test->seq3 == seq_reference) {
+ query_strip_effects_fn(seq_test, seqbase, collection);
+ }
+ }
+}
+
+static SeqCollection *seq_collection_extract_effects(SeqCollection *collection)
+{
+ SeqCollection *effects = SEQ_collection_create(__func__);
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (SEQ_effect_get_num_inputs(seq->type) > 0) {
+ SEQ_collection_append_strip(seq, effects);
+ }
+ }
+ return effects;
+}
+
+static SeqCollection *query_snap_targets(const TransInfo *t, SeqCollection *snap_sources)
+{
+ ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false));
const short snap_flag = SEQ_tool_settings_snap_flag_get(t->scene);
- SeqCollection *collection = SEQ_collection_create(__func__);
+ SeqCollection *snap_targets = SEQ_collection_create(__func__);
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if ((seq->flag & SELECT)) {
continue; /* Selected are being transformed. */
@@ -119,9 +150,18 @@ static SeqCollection *query_snap_targets(const TransInfo *t)
if (seq->type == SEQ_TYPE_SOUND_RAM && (snap_flag & SEQ_SNAP_IGNORE_SOUND)) {
continue;
}
- SEQ_collection_append_strip(seq, collection);
+ SEQ_collection_append_strip(seq, snap_targets);
}
- return collection;
+
+ /* Effects will always change position with strip to which they are connected and they don't have
+ * to be selected. Remove such strips from `snap_targets` collection. */
+ SeqCollection *snap_sources_temp = SEQ_collection_duplicate(snap_sources);
+ SEQ_collection_expand(seqbase, snap_sources_temp, query_strip_effects_fn);
+ SeqCollection *snap_sources_effects = seq_collection_extract_effects(snap_sources_temp);
+ SEQ_collection_exclude(snap_targets, snap_sources_effects);
+ SEQ_collection_free(snap_sources_temp);
+
+ return snap_targets;
}
static int seq_get_snap_target_points_count(const TransInfo *t,
@@ -178,11 +218,14 @@ static void seq_snap_target_points_build(const TransInfo *t,
if (snap_mode & SEQ_SNAP_TO_STRIP_HOLD) {
int content_start = min_ii(seq->enddisp, seq->start);
int content_end = max_ii(seq->startdisp, seq->start + seq->len);
- if (seq->anim_startofs == 0) {
- content_start = seq->startdisp;
- }
- if (seq->anim_endofs == 0) {
- content_end = seq->enddisp;
+ /* Effects and single image strips produce incorrect content length. Skip these strips. */
+ if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->len == 1) {
+ if (seq->anim_startofs == 0 && seq->startstill == 0) {
+ content_start = seq->startdisp;
+ }
+ if (seq->anim_endofs == 0 && seq->endstill == 0) {
+ content_end = seq->enddisp;
+ }
}
snap_data->target_snap_points[i] = content_start;
snap_data->target_snap_points[i + 1] = content_end;
@@ -214,16 +257,25 @@ TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t)
TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__);
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false));
- /* Build arrays of snap points. */
SeqCollection *snap_sources = SEQ_query_selected_strips(seqbase);
+ SeqCollection *snap_targets = query_snap_targets(t, snap_sources);
+
+ if (SEQ_collection_len(snap_sources) == 0 || SEQ_collection_len(snap_targets) == 0) {
+ SEQ_collection_free(snap_targets);
+ SEQ_collection_free(snap_sources);
+ MEM_freeN(snap_data);
+ return NULL;
+ }
+
+ /* Build arrays of snap points. */
seq_snap_source_points_alloc(snap_data, snap_sources);
seq_snap_source_points_build(t, snap_data, snap_sources);
SEQ_collection_free(snap_sources);
- SeqCollection *snap_targets = query_snap_targets(t);
seq_snap_target_points_alloc(t, snap_data, snap_targets);
seq_snap_target_points_build(t, snap_data, snap_targets);
SEQ_collection_free(snap_targets);
+
return snap_data;
}
@@ -236,12 +288,16 @@ void transform_snap_sequencer_data_free(TransSeqSnapData *data)
bool transform_snap_sequencer_calc(TransInfo *t)
{
+ const TransSeqSnapData *snap_data = t->tsnap.seq_context;
+ if (snap_data == NULL) {
+ return false;
+ }
+
/* Prevent snapping when constrained to Y axis. */
if (t->con.mode & CON_APPLY && t->con.mode & CON_AXIS1) {
return false;
}
- const TransSeqSnapData *snap_data = t->tsnap.seq_context;
int best_dist = MAXFRAME, best_target_frame = 0, best_source_frame = 0;
for (int i = 0; i < snap_data->source_snap_point_count; i++) {
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index 23161028e03..3e0029156c1 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -50,6 +50,7 @@
#include "BLO_blend_validate.h"
+#include "ED_asset.h"
#include "ED_gpencil.h"
#include "ED_object.h"
#include "ED_outliner.h"
@@ -268,6 +269,8 @@ static void ed_undo_step_post(bContext *C,
WM_toolsystem_refresh_active(C);
WM_toolsystem_refresh_screen_all(bmain);
+ ED_assetlist_storage_tag_main_data_dirty();
+
if (CLOG_CHECK(&LOG, 1)) {
BKE_undosys_print(wm->undo_stack);
}
@@ -692,7 +695,7 @@ int ED_undo_operator_repeat(bContext *C, wmOperator *op)
CTX_wm_region_set(C, region_win);
}
- if ((WM_operator_repeat_check(C, op)) && (WM_operator_poll(C, op->type)) &&
+ if (WM_operator_repeat_check(C, op) && WM_operator_poll(C, op->type) &&
/* NOTE: undo/redo can't run if there are jobs active,
* check for screen jobs only so jobs like material/texture/world preview
* (which copy their data), won't stop redo, see T29579],
diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c
index 3b543cdf6ce..fc351ab728c 100644
--- a/source/blender/editors/util/ed_draw.c
+++ b/source/blender/editors/util/ed_draw.c
@@ -33,6 +33,8 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_image.h"
@@ -56,6 +58,473 @@
#include "WM_api.h"
#include "WM_types.h"
+/* -------------------------------------------------------------------- */
+/** \name Generic Slider
+ *
+ * The generic slider is supposed to be called during modal operations. It calculates a factor
+ * value based on mouse position and draws a visual representation. In order to use it, you need to
+ * store a reference to a #tSlider in your operator which you get by calling #ED_slider_create.
+ * Then you need to update it during modal operations by calling #ED_slider_modal", which will
+ * update #tSlider.factor for you to use. To remove drawing and free the memory, call
+ * #ED_slider_destroy.
+ * \{ */
+
+#define SLIDE_PIXEL_DISTANCE (300.0f * U.dpi_fac)
+#define OVERSHOOT_RANGE_DELTA 0.2f
+
+typedef struct tSlider {
+ struct Scene *scene;
+ struct ScrArea *area;
+
+ /** Header of the region used for drawing the slider. */
+ struct ARegion *region_header;
+
+ /** Draw callback handler. */
+ void *draw_handle;
+
+ /** Accumulative factor (not clamped or rounded). */
+ float raw_factor;
+
+ /** 0-1 value for determining the influence of whatever is relevant. */
+ float factor;
+
+ /** Last mouse cursor position used for mouse movement delta calculation. */
+ float last_cursor[2];
+
+ /** Enable range beyond 0-100%. */
+ bool allow_overshoot;
+
+ /** Allow overshoot or clamp between 0% and 100%. */
+ bool overshoot;
+
+ /** Move factor in 10% steps. */
+ bool increments;
+
+ /** Reduces factor delta from mouse movement. */
+ bool precision;
+} tSlider;
+
+static void draw_overshoot_triangle(const uint8_t color[4],
+ const bool facing_right,
+ const float x,
+ const float y)
+{
+ const uint shdr_pos_2d = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_polygon_smooth(true);
+ immUniformColor3ubvAlpha(color, 225);
+ const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize;
+ const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize;
+
+ immBegin(GPU_PRIM_TRIS, 3);
+ immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y);
+ immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2);
+ immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2);
+ immEnd();
+
+ GPU_polygon_smooth(false);
+ GPU_blend(GPU_BLEND_NONE);
+ immUnbindProgram();
+}
+
+static void draw_ticks(const float start_factor,
+ const float end_factor,
+ const float line_start[2],
+ const float base_tick_height,
+ const float line_width,
+ const uint8_t color_overshoot[4],
+ const uint8_t color_line[4])
+{
+ /* Use factor represented as 0-100 int to avoid floating point precision problems. */
+ const int tick_increment = 10;
+
+ /* Round initial_tick_factor up to the next tick_increment. */
+ int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment;
+
+ while (tick_percentage <= (int)(end_factor * 100)) {
+ float tick_height;
+ /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit
+ * smaller and the rest is the minimum size. */
+ if (tick_percentage % 100 == 0) {
+ tick_height = base_tick_height;
+ }
+ else if (tick_percentage % 50 == 0) {
+ tick_height = base_tick_height * 0.8;
+ }
+ else {
+ tick_height = base_tick_height * 0.5;
+ }
+
+ const float x = line_start[0] +
+ (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE;
+ const rctf tick_rect = {
+ .xmin = x - (line_width / 2),
+ .xmax = x + (line_width / 2),
+ .ymin = line_start[1] - (tick_height / 2),
+ .ymax = line_start[1] + (tick_height / 2),
+ };
+
+ if (tick_percentage < 0 || tick_percentage > 100) {
+ UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255);
+ }
+ else {
+ UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255);
+ }
+ tick_percentage += tick_increment;
+ }
+}
+
+static void draw_main_line(const rctf *main_line_rect,
+ const float factor,
+ const bool overshoot,
+ const uint8_t color_overshoot[4],
+ const uint8_t color_line[4])
+{
+ if (overshoot) {
+ /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */
+ const float line_zero_percent = main_line_rect->xmin -
+ ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) *
+ SLIDE_PIXEL_DISTANCE);
+
+ const float clamped_line_zero_percent = clamp_f(
+ line_zero_percent, main_line_rect->xmin, main_line_rect->xmax);
+ const float clamped_line_hundred_percent = clamp_f(
+ line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax);
+
+ const rctf left_overshoot_line_rect = {
+ .xmin = main_line_rect->xmin,
+ .xmax = clamped_line_zero_percent,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ const rctf right_overshoot_line_rect = {
+ .xmin = clamped_line_hundred_percent,
+ .xmax = main_line_rect->xmax,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255);
+ UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255);
+
+ const rctf non_overshoot_line_rect = {
+ .xmin = clamped_line_zero_percent,
+ .xmax = clamped_line_hundred_percent,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255);
+ }
+ else {
+ UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255);
+ }
+}
+
+static void draw_backdrop(const int fontid,
+ const rctf *main_line_rect,
+ const float color_bg[4],
+ const short region_y_size,
+ const float base_tick_height)
+{
+ float string_pixel_size[2];
+ const char *percentage_string_placeholder = "000%%";
+ BLF_width_and_height(fontid,
+ percentage_string_placeholder,
+ sizeof(percentage_string_placeholder),
+ &string_pixel_size[0],
+ &string_pixel_size[1]);
+ const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize};
+ const rctf backdrop_rect = {
+ .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0],
+ .xmax = main_line_rect->xmax + pad[0],
+ .ymin = pad[1],
+ .ymax = region_y_size - pad[1],
+ };
+ UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg);
+}
+
+/**
+ * Draw an on screen Slider for a Pose Slide Operator.
+ */
+static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void *arg)
+{
+ tSlider *slider = arg;
+
+ /* Only draw in region from which the Operator was started. */
+ if (region != slider->region_header) {
+ return;
+ }
+
+ uint8_t color_text[4];
+ uint8_t color_line[4];
+ uint8_t color_handle[4];
+ uint8_t color_overshoot[4];
+ float color_bg[4];
+
+ /* Get theme colors. */
+ UI_GetThemeColor4ubv(TH_TEXT, color_text);
+ UI_GetThemeColor4ubv(TH_TEXT, color_line);
+ UI_GetThemeColor4ubv(TH_TEXT, color_overshoot);
+ UI_GetThemeColor4ubv(TH_ACTIVE, color_handle);
+ UI_GetThemeColor3fv(TH_BACK, color_bg);
+
+ color_bg[3] = 0.5f;
+ color_overshoot[0] = color_overshoot[0] * 0.7;
+ color_overshoot[1] = color_overshoot[1] * 0.7;
+ color_overshoot[2] = color_overshoot[2] * 0.7;
+
+ /* Get the default font. */
+ const uiStyle *style = UI_style_get();
+ const uiFontStyle *fstyle = &style->widget;
+ const int fontid = fstyle->uifont_id;
+ BLF_color3ubv(fontid, color_text);
+ BLF_rotation(fontid, 0.0f);
+
+ const float line_width = 1.5 * U.pixelsize;
+ const float base_tick_height = 12.0 * U.pixelsize;
+ const float line_y = region->winy / 2;
+
+ rctf main_line_rect = {
+ .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2),
+ .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2),
+ .ymin = line_y - line_width / 2,
+ .ymax = line_y + line_width / 2,
+ };
+ float line_start_factor = 0;
+ int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * slider->factor;
+
+ if (slider->overshoot) {
+ main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
+ main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
+ line_start_factor = slider->factor - 0.5f - OVERSHOOT_RANGE_DELTA;
+ handle_pos_x = region->winx / 2;
+ }
+
+ draw_backdrop(fontid, &main_line_rect, color_bg, slider->region_header->winy, base_tick_height);
+
+ draw_main_line(&main_line_rect, slider->factor, slider->overshoot, color_overshoot, color_line);
+
+ const float factor_range = slider->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1;
+ const float line_start_position[2] = {main_line_rect.xmin, line_y};
+ draw_ticks(line_start_factor,
+ line_start_factor + factor_range,
+ line_start_position,
+ base_tick_height,
+ line_width,
+ color_overshoot,
+ color_line);
+
+ /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100%
+ * range. */
+ if (slider->overshoot) {
+ if (slider->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) {
+ draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y);
+ }
+ if (slider->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) {
+ draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y);
+ }
+ }
+
+ char percentage_string[256];
+
+ /* Draw handle indicating current factor. */
+ const rctf handle_rect = {
+ .xmin = handle_pos_x - (line_width),
+ .xmax = handle_pos_x + (line_width),
+ .ymin = line_y - (base_tick_height / 2),
+ .ymax = line_y + (base_tick_height / 2),
+ };
+
+ UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255);
+ BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", slider->factor * 100);
+
+ /* Draw percentage string. */
+ float percentage_string_pixel_size[2];
+ BLF_width_and_height(fontid,
+ percentage_string,
+ sizeof(percentage_string),
+ &percentage_string_pixel_size[0],
+ &percentage_string_pixel_size[1]);
+
+ BLF_position(fontid,
+ main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2,
+ (region->winy / 2) - percentage_string_pixel_size[1] / 2,
+ 0.0f);
+ BLF_draw(fontid, percentage_string, sizeof(percentage_string));
+}
+
+static void slider_update_factor(tSlider *slider, const wmEvent *event)
+{
+ const float factor_delta = (event->x - slider->last_cursor[0]) / SLIDE_PIXEL_DISTANCE;
+ /* Reduced factor delta in precision mode (shift held). */
+ slider->raw_factor += slider->precision ? (factor_delta / 8) : factor_delta;
+ slider->factor = slider->raw_factor;
+ slider->last_cursor[0] = event->x;
+ slider->last_cursor[1] = event->y;
+
+ if (!slider->overshoot) {
+ slider->factor = clamp_f(slider->factor, 0, 1);
+ }
+
+ if (slider->increments) {
+ slider->factor = round(slider->factor * 10) / 10;
+ }
+}
+
+tSlider *ED_slider_create(struct bContext *C)
+{
+ tSlider *slider = MEM_callocN(sizeof(tSlider), "tSlider");
+ slider->scene = CTX_data_scene(C);
+ slider->area = CTX_wm_area(C);
+ slider->region_header = CTX_wm_region(C);
+
+ /* Default is true, caller needs to manually set to false. */
+ slider->allow_overshoot = true;
+
+ /* Set initial factor. */
+ slider->raw_factor = 0.5f;
+ slider->factor = 0.5;
+
+ /* Add draw callback. Always in header. */
+ LISTBASE_FOREACH (ARegion *, region, &slider->area->regionbase) {
+ if (region->regiontype == RGN_TYPE_HEADER) {
+ slider->region_header = region;
+ slider->draw_handle = ED_region_draw_cb_activate(
+ region->type, slider_draw, slider, REGION_DRAW_POST_PIXEL);
+ }
+ }
+
+ return slider;
+}
+
+/**
+ * For modal operations so the percentage doesn't pop on the first mouse movement.
+ */
+void ED_slider_init(struct tSlider *slider, const wmEvent *event)
+{
+ slider->last_cursor[0] = event->x;
+ slider->last_cursor[1] = event->y;
+}
+
+/**
+ * Calculate slider factor based on mouse position.
+ */
+bool ED_slider_modal(tSlider *slider, const wmEvent *event)
+{
+ bool event_handled = true;
+ /* Handle key presses. */
+ switch (event->type) {
+ case EVT_EKEY:
+ if (slider->allow_overshoot) {
+ slider->overshoot = event->val == KM_PRESS ? !slider->overshoot : slider->overshoot;
+ slider_update_factor(slider, event);
+ }
+ break;
+ case EVT_LEFTSHIFTKEY:
+ case EVT_RIGHTSHIFTKEY:
+ slider->precision = event->val == KM_PRESS;
+ break;
+ case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY:
+ slider->increments = event->val == KM_PRESS;
+ break;
+ case MOUSEMOVE:;
+ /* Update factor. */
+ slider_update_factor(slider, event);
+ break;
+ default:
+ event_handled = false;
+ break;
+ }
+
+ ED_region_tag_redraw(slider->region_header);
+
+ return event_handled;
+}
+
+/**
+ * Return string based on the current state of the slider.
+ */
+void ED_slider_status_string_get(const struct tSlider *slider,
+ char *status_string,
+ const size_t size_of_status_string)
+{
+ /* 50 characters is enough to fit the individual setting strings. Extend if message is longer. */
+ char overshoot_str[50];
+ char precision_str[50];
+ char increments_str[50];
+
+ if (slider->allow_overshoot) {
+ if (slider->overshoot) {
+ STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot"));
+ }
+ else {
+ STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot"));
+ }
+ }
+ else {
+ STRNCPY(overshoot_str, TIP_("Overshoot disabled"));
+ }
+
+ if (slider->precision) {
+ STRNCPY(precision_str, TIP_("[Shift] - Precision active"));
+ }
+ else {
+ STRNCPY(precision_str, TIP_("Shift - Hold for precision"));
+ }
+
+ if (slider->increments) {
+ STRNCPY(increments_str, TIP_("[Ctrl] - Increments active"));
+ }
+ else {
+ STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments"));
+ }
+
+ BLI_snprintf(status_string,
+ size_of_status_string,
+ "%s | %s | %s",
+ overshoot_str,
+ precision_str,
+ increments_str);
+}
+
+void ED_slider_destroy(struct bContext *C, tSlider *slider)
+{
+ /* Remove draw callback. */
+ ED_region_draw_cb_exit(slider->region_header->type, slider->draw_handle);
+ ED_area_status_text(slider->area, NULL);
+ ED_workspace_status_text(C, NULL);
+ MEM_freeN(slider);
+}
+
+/* Setters & Getters */
+
+float ED_slider_factor_get(struct tSlider *slider)
+{
+ return slider->factor;
+}
+
+void ED_slider_factor_set(struct tSlider *slider, const float factor)
+{
+ slider->factor = factor;
+ if (!slider->overshoot) {
+ slider->factor = clamp_f(slider->factor, 0, 1);
+ }
+}
+
+bool ED_slider_allow_overshoot_get(struct tSlider *slider)
+{
+ return slider->allow_overshoot;
+}
+
+void ED_slider_allow_overshoot_set(struct tSlider *slider, const bool value)
+{
+ slider->allow_overshoot = value;
+}
+
+/** \} */
+
/**
* Callback that draws a line between the mouse and a position given as the initial argument.
*/
@@ -309,7 +778,9 @@ static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top)
return 0;
}
-/* Should be kept in sync with BKE_image_stamp_buf */
+/**
+ * \note Keep in sync with #BKE_image_stamp_buf.
+ */
void ED_region_image_metadata_draw(
int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy)
{
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 7bbdc58474f..73f328f85d7 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -46,6 +46,7 @@
#include "DEG_depsgraph.h"
#include "ED_armature.h"
+#include "ED_asset.h"
#include "ED_image.h"
#include "ED_mesh.h"
#include "ED_object.h"
@@ -169,6 +170,8 @@ void ED_editors_init(bContext *C)
ED_space_image_paint_update(bmain, wm, scene);
}
+ ED_assetlist_storage_tag_main_data_dirty();
+
SWAP(int, reports->flag, reports_flag_prev);
wm->op_undo_depth--;
}
diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc
index 462f7768f81..7d32d252718 100644
--- a/source/blender/editors/util/ed_util_ops.cc
+++ b/source/blender/editors/util/ed_util_ops.cc
@@ -36,6 +36,7 @@
#include "BLT_translation.h"
+#include "ED_asset.h"
#include "ED_render.h"
#include "ED_undo.h"
#include "ED_util.h"
@@ -131,9 +132,11 @@ static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op))
if (preview) {
BKE_previewimg_clear(preview);
}
+
UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true);
WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr);
+ ED_assetlist_storage_tag_main_data_dirty();
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 7709b76290f..20aadb84b7b 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -1126,7 +1126,7 @@ static void uv_select_edgeloop_single_side_tag(const Scene *scene,
while (l_step != NULL) {
if (!uvedit_face_visible_test(scene, l_step->f) ||
- /* Check the boundary is still a boundary. */
+ /* Check the boundary is still a boundary. */
(uvedit_loop_find_other_radial_loop_with_visible_face(
scene, l_step, cd_loop_uv_offset) != NULL)) {
break;
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 28853bcdedf..535a0e00347 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -1928,6 +1928,11 @@ static StitchState *stitch_init(bContext *C,
state->obedit = obedit;
state->em = em;
+ /* Workaround for sync-select & face-select mode which implies all selected faces are detached,
+ * for stitch this isn't useful behavior, see T86924. */
+ const int selectmode_orig = scene->toolsettings->selectmode;
+ scene->toolsettings->selectmode = SCE_SELECT_VERTEX;
+
/* in uv synch selection, all uv's are visible */
if (ts->uv_flag & UV_SYNC_SELECTION) {
state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true);
@@ -1935,6 +1940,9 @@ static StitchState *stitch_init(bContext *C,
else {
state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true);
}
+
+ scene->toolsettings->selectmode = selectmode_orig;
+
if (!state->element_map) {
state_delete(state);
return NULL;
@@ -1989,7 +1997,7 @@ static StitchState *stitch_init(bContext *C,
/* Now, on to generate our uv connectivity data */
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!(ts->uv_flag & UV_SYNC_SELECTION) &&
- ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) {
+ (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) {
continue;
}
@@ -2172,8 +2180,8 @@ static StitchState *stitch_init(bContext *C,
"uv_stitch_selection_stack");
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!(ts->uv_flag & UV_SYNC_SELECTION) && ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) ||
- !BM_elem_flag_test(efa, BM_ELEM_SELECT))) {
+ if (!(ts->uv_flag & UV_SYNC_SELECTION) &&
+ (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) {
continue;
}
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 0e9669d0f60..3d5dabda23d 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -315,7 +315,7 @@ static ParamHandle *construct_param_handle(const Scene *scene,
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
- if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) ||
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ||
(options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
continue;
}
@@ -404,7 +404,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
- if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) ||
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ||
(options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
continue;
}
diff --git a/source/blender/freestyle/intern/application/Controller.cpp b/source/blender/freestyle/intern/application/Controller.cpp
index 9c675c93a2e..0d243a812fa 100644
--- a/source/blender/freestyle/intern/application/Controller.cpp
+++ b/source/blender/freestyle/intern/application/Controller.cpp
@@ -707,7 +707,7 @@ void Controller::ComputeSteerableViewMap()
for (unsigned int x = 0; x < img[i]->width(); ++x) {
//img[i]->setPixel(x, y, (float)qGray(qimg.pixel(x, y)) / 255.0f);
img[i]->setPixel(x, y, (float)qGray(qimg.pixel(x, y)));
- //float c = qGray(qimg.pixel(x, y));
+ // float c = qGray(qimg.pixel(x, y));
//img[i]->setPixel(x, y, qGray(qimg.pixel(x, y)));
}
}
diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
index e290b8a87a3..937a10f26b1 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
+++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
@@ -138,7 +138,7 @@ BlenderStrokeRenderer::BlenderStrokeRenderer(Render *re, int render_count)
// Scene layer.
ViewLayer *view_layer = (ViewLayer *)freestyle_scene->view_layers.first;
- view_layer->layflag = SCE_LAY_SOLID | SCE_LAY_ZTRA;
+ view_layer->layflag = SCE_LAY_SOLID;
// Camera
Object *object_camera = BKE_object_add(freestyle_bmain, view_layer, OB_CAMERA, nullptr);
diff --git a/source/blender/freestyle/intern/geometry/FastGrid.cpp b/source/blender/freestyle/intern/geometry/FastGrid.cpp
index 1e0ae06da19..0f1edd647c7 100644
--- a/source/blender/freestyle/intern/geometry/FastGrid.cpp
+++ b/source/blender/freestyle/intern/geometry/FastGrid.cpp
@@ -62,7 +62,7 @@ Cell *FastGrid::getCell(const Vec3u &p)
<< " " << _cells_size << endl;
}
#endif
- BLI_assert(_cells || ("_cells is a null pointer"));
+ BLI_assert_msg(_cells, "_cells is a null pointer");
BLI_assert((_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]) < _cells_size);
BLI_assert(p[0] < _cells_nb[0]);
BLI_assert(p[1] < _cells_nb[1]);
@@ -72,7 +72,7 @@ Cell *FastGrid::getCell(const Vec3u &p)
void FastGrid::fillCell(const Vec3u &p, Cell &cell)
{
- BLI_assert(_cells || ("_cells is a null pointer"));
+ BLI_assert_msg(_cells, "_cells is a null pointer");
BLI_assert((_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]) < _cells_size);
BLI_assert(p[0] < _cells_nb[0]);
BLI_assert(p[1] < _cells_nb[1]);
diff --git a/source/blender/freestyle/intern/view_map/Interface0D.h b/source/blender/freestyle/intern/view_map/Interface0D.h
index 065319578e5..d7b213706b5 100644
--- a/source/blender/freestyle/intern/view_map/Interface0D.h
+++ b/source/blender/freestyle/intern/view_map/Interface0D.h
@@ -311,13 +311,13 @@ class Interface0DIterator : public Iterator {
return result;
}
- /** operator == . */
+ /** operator `==`. */
bool operator==(const Interface0DIterator &it) const
{
return _iterator->operator==(*(it._iterator));
}
- /** operator != . */
+ /** operator `!=`. */
bool operator!=(const Interface0DIterator &it) const
{
return !(*this == it);
diff --git a/source/blender/freestyle/intern/view_map/Silhouette.h b/source/blender/freestyle/intern/view_map/Silhouette.h
index 3709b0ae11a..479dbaeedc1 100644
--- a/source/blender/freestyle/intern/view_map/Silhouette.h
+++ b/source/blender/freestyle/intern/view_map/Silhouette.h
@@ -745,7 +745,7 @@ class FEdge : public Interface1D {
_VertexB = vB;
}
- /** Sets the FEdge Id . */
+ /** Sets the FEdge Id. */
inline void setId(const Id &id)
{
_Id = id;
@@ -1153,7 +1153,7 @@ class FEdgeSharp : public FEdge {
bool _bFaceMark;
public:
- /** Returns the string "FEdgeSharp" . */
+ /** Returns the string "FEdgeSharp". */
virtual string getExactTypeName() const
{
return "FEdgeSharp";
@@ -1305,7 +1305,7 @@ class FEdgeSmooth : public FEdge {
bool _FaceMark;
public:
- /** Returns the string "FEdgeSmooth" . */
+ /** Returns the string "FEdgeSmooth". */
virtual string getExactTypeName() const
{
return "FEdgeSmooth";
diff --git a/source/blender/freestyle/intern/view_map/ViewMap.h b/source/blender/freestyle/intern/view_map/ViewMap.h
index bd7edad7642..e7d57c9dfb4 100644
--- a/source/blender/freestyle/intern/view_map/ViewMap.h
+++ b/source/blender/freestyle/intern/view_map/ViewMap.h
@@ -477,10 +477,14 @@ class TVertex : public ViewVertex {
directedViewEdge _FrontEdgeB;
directedViewEdge _BackEdgeA;
directedViewEdge _BackEdgeB;
- Id _Id; // id to identify t vertices . these id will be negative in order not to be mixed with
- // NonTVertex ids.
- edge_pointers_container
- _sortedEdges; // the list of the four ViewEdges, ordered in CCW order (in the image plan)
+
+ /**
+ * ID to identify t vertices.
+ * these id will be negative in order not to be mixed with NonTVertex ids.
+ */
+ Id _Id;
+ /** The list of the four ViewEdges, ordered in CCW order (in the image plan). */
+ edge_pointers_container _sortedEdges;
public:
/** Default constructor. */
diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
index cd0059f3c21..afb23690a84 100644
--- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
@@ -2285,7 +2285,7 @@ struct less_SVertex2D {
Vec3r A = x->point2D();
Vec3r B = y->point2D();
for (unsigned int i = 0; i < 3; i++) {
- if ((fabs(A[i] - B[i])) < epsilon) {
+ if (fabs(A[i] - B[i]) < epsilon) {
continue;
}
if (A[i] < B[i]) {
diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.cpp b/source/blender/freestyle/intern/winged_edge/Curvature.cpp
index 478e48de66c..411685bd921 100644
--- a/source/blender/freestyle/intern/winged_edge/Curvature.cpp
+++ b/source/blender/freestyle/intern/winged_edge/Curvature.cpp
@@ -371,29 +371,29 @@ void gts_vertex_principal_directions(WVertex *v, Vec3r Kh, real Kg, Vec3r &e1, V
}
e = *itE;
- /* since this vertex passed the tests in gts_vertex_mean_curvature_normal(), this should be
- * true. */
+ /* Since this vertex passed the tests in gts_vertex_mean_curvature_normal(),
+ * this should be true. */
// g_assert(gts_edge_face_number (e, s) == 2);
- /* identify the two triangles bordering e in s */
+ /* Identify the two triangles bordering e in s. */
f1 = e->GetaFace();
f2 = e->GetbFace();
/* We are solving for the values of the curvature tensor
- * B = [ a b ; b c ].
- * The computations here are from section 5 of [Meyer et al 2002].
+ * `B = [ a b ; b c ]`.
+ * The computations here are from section 5 of [Meyer et al 2002].
*
- * The first step is to calculate the linear equations governing the values of (a,b,c). These
+ * The first step is to calculate the linear equations governing the values of (a,b,c). These
* can be computed by setting the derivatives of the error E to zero (section 5.3).
*
- * Since a + c = norm(Kh), we only compute the linear equations for dE/da and dE/db. (NB:
- * [Meyer et al 2002] has the equation a + b = norm(Kh), but I'm almost positive this is
- * incorrect).
+ * Since a + c = norm(Kh), we only compute the linear equations for `dE/da` and `dE/db`.
+ * (NOTE: [Meyer et al 2002] has the equation `a + b = norm(Kh)`,
+ * but I'm almost positive this is incorrect).
*
- * Note that the w_ij (defined in section 5.2) are all scaled by (1/8*A_mixed). We drop this
+ * Note that the w_ij (defined in section 5.2) are all scaled by `(1/8*A_mixed)`. We drop this
* uniform scale factor because the solution of the linear equations doesn't rely on it.
*
- * The terms of the linear equations are xterm_dy with x in {a,b,c} and y in {a,b}. There are
+ * The terms of the linear equations are xterm_dy with x in {a,b,c} and y in {a,b}. There are
* also const_dy terms that are the constant factors in the equations.
*/
diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh
index 421e5f0018d..bc3f398c8e9 100644
--- a/source/blender/functions/FN_cpp_type.hh
+++ b/source/blender/functions/FN_cpp_type.hh
@@ -163,7 +163,7 @@ class CPPType : NonCopyable, NonMovable {
* Required memory in bytes for an instance of this type.
*
* C++ equivalent:
- * sizeof(T);
+ * `sizeof(T);`
*/
int64_t size() const
{
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index 40dc585b39b..c9398ceb547 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -688,7 +688,7 @@ class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> {
public:
template<typename... Args>
- GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args)
+ GVArray_For_EmbeddedVArray(const int64_t size, Args &&...args)
: GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
{
this->varray_ = &embedded_varray_;
@@ -703,7 +703,7 @@ class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMuta
public:
template<typename... Args>
- GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args)
+ GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&...args)
: GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
{
this->varray_ = &embedded_varray_;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
index a156fca5b7b..e750c22f0e8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
@@ -9,7 +9,7 @@
* 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,
+ * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
@@ -346,7 +346,7 @@ static void gpencil_modifier_panel_header(const bContext *UNUSED(C), Panel *pane
uiItemMenuF(row, "", ICON_DOWNARROW_HLT, gpencil_modifier_ops_extra_draw, md);
/* Remove button. */
- sub = uiLayoutRow(row, true);
+ sub = uiLayoutRow(row, false);
uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
uiItemO(sub, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove");
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index fcc44aab583..73ca4b9c529 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -261,10 +261,12 @@ static void updateDepsgraph(GpencilModifierData *md,
else {
add_this_collection(ctx->scene->master_collection, ctx, mode);
}
- DEG_add_object_relation(
- ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
- DEG_add_object_relation(
- ctx->node, ctx->scene->camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier");
+ if (ctx->scene->camera) {
+ DEG_add_object_relation(
+ ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
+ DEG_add_object_relation(
+ ctx->node, ctx->scene->camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier");
+ }
}
static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
@@ -320,13 +322,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
}
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetRedAlert(row, !material_valid);
- uiItemPointerR(row,
- ptr,
- "target_material",
- &obj_data_ptr,
- "materials",
- NULL,
- material_valid ? ICON_SHADING_TEXTURE : ICON_ERROR);
+ uiItemPointerR(
+ row, ptr, "target_material", &obj_data_ptr, "materials", NULL, ICON_SHADING_TEXTURE);
gpencil_modifier_panel_end(layout, ptr);
}
@@ -385,6 +382,8 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
return;
}
+ uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE);
+
uiLayout *col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "use_remove_doubles", 0, NULL, ICON_NONE);
@@ -432,6 +431,17 @@ static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel)
}
}
+static bool anything_showing_through(PointerRNA *ptr)
+{
+ const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
+ const int level_start = RNA_int_get(ptr, "level_start");
+ const int level_end = RNA_int_get(ptr, "level_end");
+ if (use_multiple_levels) {
+ return (MAX2(level_start, level_end) > 0);
+ }
+ return (level_start > 0);
+}
+
static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -439,6 +449,7 @@ static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *pa
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
uiLayoutSetEnabled(layout, !is_baked);
+ uiLayoutSetActive(layout, anything_showing_through(ptr));
uiItemR(layout, ptr, "use_material_mask", 0, IFACE_("Material Mask"), ICON_NONE);
}
@@ -450,24 +461,24 @@ static void material_mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
uiLayoutSetEnabled(layout, !is_baked);
+ uiLayoutSetActive(layout, anything_showing_through(ptr));
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, RNA_boolean_get(ptr, "use_material_mask"));
- uiLayout *row = uiLayoutRow(layout, true);
- uiLayoutSetPropDecorate(row, false);
- uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks"));
- char text[2] = "0";
+ uiLayout *col = uiLayoutColumn(layout, true);
+ uiLayout *sub = uiLayoutRowWithHeading(col, true, IFACE_("Masks"));
PropertyRNA *prop = RNA_struct_find_property(ptr, "use_material_mask_bits");
- for (int i = 0; i < 8; i++, text[0]++) {
- uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, text, ICON_NONE);
+ for (int i = 0; i < 8; i++) {
+ uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, " ", ICON_NONE);
+ if (i == 3) {
+ sub = uiLayoutRow(col, true);
+ }
}
- uiItemL(row, "", ICON_BLANK1); /* Space for decorator. */
- uiLayout *col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_material_mask_match", 0, IFACE_("Match All Masks"), ICON_NONE);
+ uiItemR(layout, ptr, "use_material_mask_match", 0, IFACE_("Exact Match"), ICON_NONE);
}
static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -482,19 +493,18 @@ static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_intersection"));
- uiLayout *row = uiLayoutRow(layout, true);
- uiLayoutSetPropDecorate(row, false);
- uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks"));
- char text[2] = "0";
+ uiLayout *col = uiLayoutColumn(layout, true);
+ uiLayout *sub = uiLayoutRowWithHeading(col, true, IFACE_("Collection Masks"));
PropertyRNA *prop = RNA_struct_find_property(ptr, "use_intersection_mask");
- for (int i = 0; i < 8; i++, text[0]++) {
- uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, text, ICON_NONE);
+ for (int i = 0; i < 8; i++) {
+ uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, " ", ICON_NONE);
+ if (i == 3) {
+ sub = uiLayoutRow(col, true);
+ }
}
- uiItemL(row, "", ICON_BLANK1); /* Space for decorator. */
- uiLayout *col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_intersection_match", 0, IFACE_("Match All Masks"), ICON_NONE);
+ uiItemR(layout, ptr, "use_intersection_match", 0, IFACE_("Exact Match"), ICON_NONE);
}
static void face_mark_panel_draw_header(const bContext *UNUSED(C), Panel *panel)
{
@@ -566,7 +576,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_geometry_space_chain", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_geometry_space_chain", 0, IFACE_("Geometry Space"), ICON_NONE);
uiItemR(layout,
ptr,
@@ -613,7 +623,7 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel)
}
}
-static void baking_panel_draw(const bContext *UNUSED(C), Panel *panel)
+static void bake_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
@@ -669,7 +679,7 @@ static void panelRegister(ARegionType *region_type)
gpencil_modifier_subpanel_register(
region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
- region_type, "baking", "Baking", NULL, baking_panel_draw, panel_type);
+ region_type, "bake", "Bake", NULL, bake_panel_draw, panel_type);
}
GpencilModifierTypeInfo modifierType_Gpencil_Lineart = {
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c
index c7fe20edef7..686023a36d4 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c
@@ -246,7 +246,7 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
{
WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md;
- return !(mmd->target_vgname && mmd->target_vgname[0] != '\0');
+ return (mmd->target_vgname[0] == '\0');
}
static void distance_panel_draw(const bContext *UNUSED(C), Panel *panel)
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 247b0b3f57b..9ac07b9632d 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -228,6 +228,8 @@ typedef struct LineartRenderBuffer {
double view_projection[4][4];
double view[4][4];
+ float overscan;
+
struct LineartBoundingArea *initial_bounding_areas;
unsigned int bounding_area_count;
@@ -473,7 +475,8 @@ typedef struct LineartBoundingArea {
BLI_INLINE int lineart_LineIntersectTest2d(
const double *a1, const double *a2, const double *b1, const double *b2, double *aRatio)
{
-#define USE_VECTOR_LINE_INTERSECTION
+/* Legacy intersection math aligns better with occlusion function quirks. */
+/* #define USE_VECTOR_LINE_INTERSECTION */
#ifdef USE_VECTOR_LINE_INTERSECTION
/* from isect_line_line_v2_point() */
@@ -549,7 +552,7 @@ BLI_INLINE int lineart_LineIntersectTest2d(
k1 = (a2[1] - a1[1]) / x_diff;
k2 = (b2[1] - b1[1]) / x_diff2;
- if ((k1 == k2))
+ if (k1 == k2)
return 0;
x = (a1[1] - b1[1] - k1 * a1[0] + k2 * b1[0]) / (k2 - k1);
@@ -575,9 +578,9 @@ BLI_INLINE int lineart_LineIntersectTest2d(
}
struct Depsgraph;
-struct Scene;
-struct LineartRenderBuffer;
struct LineartGpencilModifierData;
+struct LineartRenderBuffer;
+struct Scene;
void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd);
@@ -602,8 +605,8 @@ LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *r
LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y);
-struct bGPDlayer;
struct bGPDframe;
+struct bGPDlayer;
void MOD_lineart_gpencil_generate(LineartCache *cache,
struct Depsgraph *depsgraph,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 9899d889c56..8762ca1f384 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -380,7 +380,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *
/* Ignore this triangle if an intersection line directly comes from it, */
lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)tri) ||
/* Or if this triangle isn't effectively occluding anything nor it's providing a
- material flag. */
+ * material flag. */
((!tri->base.mat_occlusion) && (!tri->base.material_mask_bits))) {
continue;
}
@@ -1803,10 +1803,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu
tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ?
mat->lineart.material_mask_bits :
0);
- tri->mat_occlusion |= ((mat &&
- (mat->lineart.flags & LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS)) ?
- mat->lineart.mat_occlusion :
- 1);
+ tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
tri->intersection_mask = obi->override_intersection_mask;
@@ -1956,7 +1953,7 @@ static uchar lineart_intersection_mask_check(Collection *c, Object *ob)
* See if this object in such collection is used for generating line art,
* Disabling a collection for line art will doable all objects inside.
*/
-static int lineart_usage_check(Collection *c, Object *ob)
+static int lineart_usage_check(Collection *c, Object *ob, bool is_render)
{
if (!c) {
@@ -1969,8 +1966,12 @@ static int lineart_usage_check(Collection *c, Object *ob)
return ob->lineart.usage;
}
- if (c->children.first == NULL) {
+ if (c->gobject.first) {
if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
+ if ((is_render && (c->flag & COLLECTION_RESTRICT_RENDER)) ||
+ ((!is_render) && (c->flag & COLLECTION_RESTRICT_VIEWPORT))) {
+ return OBJECT_LRT_EXCLUDE;
+ }
if (ob->lineart.usage == OBJECT_LRT_INHERIT) {
switch (c->lineart_usage) {
case COLLECTION_LRT_OCCLUSION_ONLY:
@@ -1989,7 +1990,7 @@ static int lineart_usage_check(Collection *c, Object *ob)
}
LISTBASE_FOREACH (CollectionChild *, cc, &c->children) {
- int result = lineart_usage_check(cc->collection, ob);
+ int result = lineart_usage_check(cc->collection, ob, is_render);
if (result > OBJECT_LRT_INHERIT) {
return result;
}
@@ -2016,7 +2017,10 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_
use_olti->pending = obi;
}
-static bool lineart_geometry_check_visible(double (*model_view_proj)[4], Object *use_ob)
+static bool lineart_geometry_check_visible(double (*model_view_proj)[4],
+ double shift_x,
+ double shift_y,
+ Object *use_ob)
{
BoundBox *bb = BKE_object_boundbox_get(use_ob);
if (!bb) {
@@ -2030,6 +2034,8 @@ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], Object
copy_v3db_v3fl(co[i], bb->vec[i]);
copy_v3_v3_db(tmp, co[i]);
mul_v4_m4v3_db(co[i], model_view_proj, tmp);
+ co[i][0] -= shift_x * 2 * co[i][3];
+ co[i][1] -= shift_y * 2 * co[i][3];
}
bool cond[6] = {true, true, true, true, true, true};
@@ -2065,11 +2071,6 @@ static void lineart_main_load_geometries(
int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h);
double asp = ((double)rb->w / (double)rb->h);
- double t_start;
-
- if (G.debug_value == 4000) {
- t_start = PIL_check_seconds_timer();
- }
int bound_box_discard_count = 0;
if (cam->type == CAM_PERSP) {
@@ -2079,14 +2080,20 @@ static void lineart_main_load_geometries(
if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
sensor /= asp;
}
- double fov = focallength_to_fov(cam->lens, sensor);
+ const double fov = focallength_to_fov(cam->lens / (1 + rb->overscan), sensor);
lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
}
else if (cam->type == CAM_ORTHO) {
- double w = cam->ortho_scale / 2;
+ const double w = cam->ortho_scale / 2;
lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
}
+ double t_start;
+
+ if (G.debug_value == 4000) {
+ t_start = PIL_check_seconds_timer();
+ }
+
invert_m4_m4(inv, rb->cam_obmat);
mul_m4db_m4db_m4fl_uniq(result, proj, inv);
copy_m4_m4_db(proj, result);
@@ -2112,9 +2119,11 @@ static void lineart_main_load_geometries(
LineartObjectLoadTaskInfo *olti = lineart_mem_acquire(
&rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count);
+ bool is_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
+
DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) {
LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo));
- obi->usage = lineart_usage_check(scene->master_collection, ob);
+ obi->usage = lineart_usage_check(scene->master_collection, ob, is_render);
obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection,
ob);
Mesh *use_mesh;
@@ -2125,7 +2134,7 @@ static void lineart_main_load_geometries(
Object *use_ob = DEG_get_evaluated_object(depsgraph, ob);
/* Prepare the matrix used for transforming this specific object (instance). This has to be
- * done before mesh boundbox check because the function needs that. */
+ * done before mesh boundbox check because the function needs that. */
mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat);
mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat);
@@ -2133,7 +2142,7 @@ static void lineart_main_load_geometries(
continue;
}
- if (!lineart_geometry_check_visible(obi->model_view_proj, use_ob)) {
+ if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_ob)) {
if (G.debug_value == 4000) {
bound_box_discard_count++;
}
@@ -2156,7 +2165,7 @@ static void lineart_main_load_geometries(
obi->free_use_mesh = true;
}
- /* Make normal matrix. */
+ /* Make normal matrix. */
float imat[4][4];
invert_m4_m4(imat, ob->obmat);
transpose_m4(imat);
@@ -2478,8 +2487,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
}
}
else if (st_r == 0) {
- INTERSECT_JUST_GREATER(is, order, 0, LCross);
- if (LRT_ABC(LCross) && is[LCross] > 0) {
+ INTERSECT_JUST_GREATER(is, order, DBL_TRIANGLE_LIM, LCross);
+ if (LRT_ABC(LCross) && is[LCross] > DBL_TRIANGLE_LIM) {
INTERSECT_JUST_GREATER(is, order, is[LCross], RCross);
}
else {
@@ -3026,6 +3035,11 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp;
rb->shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp;
+ rb->overscan = lmd->overscan;
+
+ rb->shift_x /= (1 + rb->overscan);
+ rb->shift_y /= (1 + rb->overscan);
+
rb->crease_threshold = cos(M_PI - lmd->crease_threshold);
rb->angle_splitting_threshold = lmd->angle_splitting_threshold;
rb->chaining_image_threshold = lmd->chaining_image_threshold;
@@ -3442,9 +3456,9 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb,
return true;
}
- if ((lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba)) ||
- (lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba)) ||
- (lineart_bounding_area_edge_intersect(fb, FBC3, FBC1, ba))) {
+ if (lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba) ||
+ lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba) ||
+ lineart_bounding_area_edge_intersect(fb, FBC3, FBC1, ba)) {
return true;
}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 9d109320f09..70ff4a373dd 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -33,10 +33,10 @@
#include <math.h>
#include <string.h>
-struct LineartStaticMemPool;
-struct LineartStaticMemPoolNode;
struct LineartEdge;
struct LineartRenderBuffer;
+struct LineartStaticMemPool;
+struct LineartStaticMemPoolNode;
void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data);
void *lineart_list_append_pointer_pool_sized(ListBase *h,
diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h
index e3a566e5f21..0d5388c6b82 100644
--- a/source/blender/gpu/GPU_vertex_format.h
+++ b/source/blender/gpu/GPU_vertex_format.h
@@ -127,7 +127,7 @@ BLI_INLINE const char *GPU_vertformat_attr_name_get(const GPUVertFormat *format,
}
/* WARNING: Can only rename using a string with same character count.
- * WARNING: This removes all other aliases of this attrib */
+ * WARNING: This removes all other aliases of this attribute. */
void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr, const char *new_name);
void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint max_len);
diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc
index 9dc24c59e22..0e5c7900da9 100644
--- a/source/blender/gpu/intern/gpu_batch.cc
+++ b/source/blender/gpu/intern/gpu_batch.cc
@@ -184,7 +184,7 @@ int GPU_batch_instbuf_add_ex(GPUBatch *batch, GPUVertBuf *insts, bool own_vbo)
}
}
/* we only make it this far if there is no room for another GPUVertBuf */
- BLI_assert(0 && "Not enough Instance VBO slot in batch");
+ BLI_assert_msg(0, "Not enough Instance VBO slot in batch");
return -1;
}
@@ -207,7 +207,7 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo)
}
}
/* we only make it this far if there is no room for another GPUVertBuf */
- BLI_assert(0 && "Not enough VBO slot in batch");
+ BLI_assert_msg(0, "Not enough VBO slot in batch");
return -1;
}
@@ -268,7 +268,7 @@ void GPU_batch_draw_advanced(
}
if (i_count == 0) {
i_count = (batch->inst[0]) ? batch->inst_(0)->vertex_len : 1;
- /* Meh. This is to be able to use different numbers of verts in instance vbos. */
+ /* Meh. This is to be able to use different numbers of verts in instance VBO's. */
if (batch->inst[1] != nullptr) {
i_count = min_ii(i_count, batch->inst_(1)->vertex_len);
}
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index d12cecd129e..bb1ebc0e85d 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -657,7 +657,7 @@ static const char *attr_prefix_get(CustomDataType type)
case CD_AUTO_FROM_NAME:
return "a";
default:
- BLI_assert(false && "GPUVertAttr Prefix type not found : This should not happen!");
+ BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!");
return "";
}
}
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index 1293cc0953d..a6f7d43e563 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -495,7 +495,7 @@ void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Frame-Buffer Stack
+/** \name Frame-Buffer Stack
*
* Keeps track of frame-buffer binding operation to restore previously bound frame-buffers.
* \{ */
@@ -609,7 +609,13 @@ GPUOffScreen *GPU_offscreen_create(
}
if ((depth && !ofs->depth) || !ofs->color) {
- BLI_snprintf(err_out, 256, "GPUTexture: Texture allocation failed.");
+ const char error[] = "GPUTexture: Texture allocation failed.";
+ if (err_out) {
+ BLI_snprintf(err_out, 256, error);
+ }
+ else {
+ fprintf(stderr, error);
+ }
GPU_offscreen_free(ofs);
return nullptr;
}
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 675036b31c3..37089785e0e 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -452,8 +452,8 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd,
/* Distance from surface. */
float d = kd->max_radius * ((float)i + 0.00001f) / ((float)resolution);
- /* For each distance d we compute the radiance incoming from an hypothetic parallel plane. */
- /* Compute radius of the footprint on the hypothetic plane */
+ /* For each distance d we compute the radiance incoming from an hypothetical parallel plane. */
+ /* Compute radius of the footprint on the hypothetical plane. */
float r_fp = sqrtf(kd->max_radius * kd->max_radius - d * d);
float r_step = r_fp / INTEGRAL_RESOLUTION;
float area_accum = 0.0f;
diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c
index 7646cce2a3e..585cb296bac 100644
--- a/source/blender/gpu/intern/gpu_node_graph.c
+++ b/source/blender/gpu/intern/gpu_node_graph.c
@@ -88,7 +88,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
name = outnode->name;
input = outnode->inputs.first;
- if ((STR_ELEM(name, "set_value", "set_rgb", "set_rgba")) && (input->type == type)) {
+ if (STR_ELEM(name, "set_value", "set_rgb", "set_rgba") && (input->type == type)) {
input = MEM_dupallocN(outnode->inputs.first);
if (input->link) {
input->link->users++;
diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc
index fc755568687..7b9b3020639 100644
--- a/source/blender/gpu/intern/gpu_select_sample_query.cc
+++ b/source/blender/gpu/intern/gpu_select_sample_query.cc
@@ -56,7 +56,7 @@ struct GPUSelectQueryState {
Vector<uint> *ids;
/* Cache on initialization. */
uint (*buffer)[4];
- /* Buffer size (stores number of integers, for actual size multiply by sizeof integer). */
+ /* Buffer size (stores number of integers, for actual size multiply by `sizeof(int)`). */
uint bufsize;
/* Mode of operation. */
char mode;
diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
index 2b8a5a5cc12..26be6f57312 100644
--- a/source/blender/gpu/intern/gpu_texture_private.hh
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -316,7 +316,7 @@ inline size_t to_bytesize(eGPUTextureFormat format)
case GPU_RGBA8_DXT5:
return 1; /* Incorrect but actual size is fractional. */
default:
- BLI_assert(!"Texture format incorrect or unsupported\n");
+ BLI_assert_msg(0, "Texture format incorrect or unsupported");
return 0;
}
}
@@ -333,7 +333,7 @@ inline size_t to_block_size(eGPUTextureFormat data_type)
case GPU_RGBA8_DXT5:
return 16;
default:
- BLI_assert(!"Texture format is not a compressed format\n");
+ BLI_assert_msg(0, "Texture format is not a compressed format");
return 0;
}
}
@@ -407,7 +407,7 @@ inline size_t to_bytesize(eGPUDataFormat data_format)
case GPU_DATA_2_10_10_10_REV:
return 4;
default:
- BLI_assert(!"Data format incorrect or unsupported\n");
+ BLI_assert_msg(0, "Data format incorrect or unsupported");
return 0;
}
}
@@ -503,7 +503,7 @@ inline eGPUFrameBufferBits to_framebuffer_bits(eGPUTextureFormat tex_format)
static inline eGPUTextureFormat to_texture_format(const GPUVertFormat *format)
{
if (format->attr_len > 1 || format->attr_len == 0) {
- BLI_assert(!"Incorrect vertex format for buffer texture");
+ BLI_assert_msg(0, "Incorrect vertex format for buffer texture");
return GPU_DEPTH_COMPONENT24;
}
switch (format->attrs[0].comp_len) {
@@ -584,7 +584,7 @@ static inline eGPUTextureFormat to_texture_format(const GPUVertFormat *format)
default:
break;
}
- BLI_assert(!"Unsupported vertex format for buffer texture");
+ BLI_assert_msg(0, "Unsupported vertex format for buffer texture");
return GPU_DEPTH_COMPONENT24;
}
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index f90c37e5c50..42b85da1f93 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -90,7 +90,7 @@ void GLBackend::platform_init()
device |= GPU_DEVICE_INTEL_UHD;
}
}
- else if ((strstr(renderer, "Mesa DRI R")) ||
+ else if (strstr(renderer, "Mesa DRI R") ||
(strstr(renderer, "Radeon") && strstr(vendor, "X.Org")) ||
(strstr(renderer, "AMD") && strstr(vendor, "X.Org")) ||
(strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) ||
diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc
index 23654cb96f3..09bc0aa6e58 100644
--- a/source/blender/gpu/opengl/gl_context.cc
+++ b/source/blender/gpu/opengl/gl_context.cc
@@ -93,7 +93,7 @@ GLContext::GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list
}
}
else {
- /* For offscreen contexts. Default framebuffer is NULL. */
+ /* For off-screen contexts. Default frame-buffer is NULL. */
back_left = new GLFrameBuffer("back_left", this, GL_NONE, 0, 0, 0);
}
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
index 9cf072b2e8a..9900a4e0766 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.cc
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -170,7 +170,7 @@ GLShaderInterface::GLShaderInterface(GLuint program)
program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_len);
}
- BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t");
+ BLI_assert_msg(ubo_len <= 16, "enabled_ubo_mask_ is uint16_t");
/* Work around driver bug with Intel HD 4600 on Windows 7/8, where
* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */
diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh
index dc048ea05c0..2a480e71017 100644
--- a/source/blender/gpu/opengl/gl_texture.hh
+++ b/source/blender/gpu/opengl/gl_texture.hh
@@ -192,7 +192,7 @@ inline GLenum to_gl_internal_format(eGPUTextureFormat format)
case GPU_DEPTH_COMPONENT16:
return GL_DEPTH_COMPONENT16;
default:
- BLI_assert(!"Texture format incorrect or unsupported\n");
+ BLI_assert_msg(0, "Texture format incorrect or unsupported");
return 0;
}
}
@@ -287,7 +287,7 @@ inline GLenum to_gl(eGPUDataFormat format)
case GPU_DATA_10_11_11_REV:
return GL_UNSIGNED_INT_10F_11F_11F_REV;
default:
- BLI_assert(!"Unhandled data format");
+ BLI_assert_msg(0, "Unhandled data format");
return GL_FLOAT;
}
}
@@ -359,7 +359,7 @@ inline GLenum to_gl_data_format(eGPUTextureFormat format)
case GPU_RGBA8_DXT5:
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
default:
- BLI_assert(!"Texture format incorrect or unsupported\n");
+ BLI_assert_msg(0, "Texture format incorrect or unsupported\n");
return 0;
}
}
@@ -377,7 +377,7 @@ inline GLenum channel_len_to_gl(int channel_len)
case 4:
return GL_RGBA;
default:
- BLI_assert(!"Wrong number of texture channels");
+ BLI_assert_msg(0, "Wrong number of texture channels");
return GL_RED;
}
}
diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc
index ea2770f099d..e324916b934 100644
--- a/source/blender/gpu/opengl/gl_vertex_array.cc
+++ b/source/blender/gpu/opengl/gl_vertex_array.cc
@@ -108,7 +108,7 @@ static uint16_t vbo_bind(const ShaderInterface *interface,
return enabled_attrib;
}
-/* Update the Attrib Binding of the currently bound VAO. */
+/* Update the Attribute Binding of the currently bound VAO. */
void GLVertArray::update_bindings(const GLuint vao,
const GPUBatch *batch_, /* Should be GLBatch. */
const ShaderInterface *interface,
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 9ce2a1be015..aae7f641af8 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
@@ -18,6 +18,7 @@ in vec2 P2;
in vec2 P3;
in ivec4 colid_doarrow;
in ivec2 domuted;
+in float dim_factor;
uniform vec4 colors[6];
@@ -39,6 +40,7 @@ uniform vec2 bezierPts[4];
uniform vec4 colors[3];
uniform bool doArrow;
uniform bool doMuted;
+uniform float dim_factor;
# define colShadow colors[0]
# define colStart colors[1]
@@ -98,6 +100,8 @@ void main(void)
}
}
+ finalColor[3] *= dim_factor;
+
/* Expand into a line */
gl_Position.xy += exp_axis * expandSize * expand_dist;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
index 40f76b9cacc..0f69a4b9aa0 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
@@ -1,11 +1,17 @@
/*
+ * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez.
+ *
* Smooth Voronoi:
*
* - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
*
- * Distance To Edge:
+ * Distance To Edge based on:
+ *
+ * - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
+ * - https://www.shadertoy.com/view/ldl3W8
*
- * - https://www.shadertoy.com/view/llG3zy
+ * With optimization to change -2..2 scan window to -1..1 for better performance,
+ * as explained in https://www.shadertoy.com/view/llG3zy.
*
*/
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 2cfce7b1ba0..d527aca184c 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -69,8 +69,8 @@ extern "C" {
* \attention defined in ???
*/
struct ImBuf;
-struct rcti;
struct rctf;
+struct rcti;
/**
*
diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c
index e62473fb8c7..59a17fcc600 100644
--- a/source/blender/imbuf/intern/iris.c
+++ b/source/blender/imbuf/intern/iris.c
@@ -39,7 +39,7 @@
#define IMAGIC 0732
typedef struct {
- ushort imagic; /* stuff saved on disk . . */
+ ushort imagic; /* Stuff saved on disk. */
ushort type;
ushort dim;
ushort xsize;
diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.cpp b/source/blender/imbuf/intern/oiio/openimageio_api.cpp
index 0941338160d..3ad902a241d 100644
--- a/source/blender/imbuf/intern/oiio/openimageio_api.cpp
+++ b/source/blender/imbuf/intern/oiio/openimageio_api.cpp
@@ -28,7 +28,7 @@
# define _USE_MATH_DEFINES
#endif
-// NOTE: Keep first, BLI_path_util conflicts with OIIO's format.
+/* NOTE: Keep first, #BLI_path_util conflicts with OIIO's format. */
#include "openimageio_api.h"
#include <OpenImageIO/imageio.h>
#include <memory>
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index ab578e25b94..d1fa26e1a3e 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -1705,7 +1705,7 @@ static void exr_print_filecontents(MultiPartInputFile &file)
}
}
-/* for non-multilayer, map R G B A channel names to something that's in this file */
+/* For non-multi-layer, map R G B A channel names to something that's in this file. */
static const char *exr_rgba_channelname(MultiPartInputFile &file, const char *chan)
{
const ChannelList &channels = file.header(0).channels();
diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c
index 561a833803d..399fd487065 100644
--- a/source/blender/imbuf/intern/png.c
+++ b/source/blender/imbuf/intern/png.c
@@ -749,8 +749,9 @@ ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colors
/* set the individual row-pointers to point at the correct offsets */
for (int i = 0; i < ibuf->y; i++) {
- row_pointers[ibuf->y - 1 - i] = (png_bytep)(
- (unsigned char *)pixels + (((size_t)i) * ibuf->x) * channels * sizeof(unsigned char));
+ row_pointers[ibuf->y - 1 - i] = (png_bytep)((unsigned char *)pixels +
+ (((size_t)i) * ibuf->x) * channels *
+ sizeof(unsigned char));
}
png_read_image(png_ptr, row_pointers);
diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c
index 4a964c64917..1c4b7af6ef1 100644
--- a/source/blender/imbuf/intern/scaling.c
+++ b/source/blender/imbuf/intern/scaling.c
@@ -867,7 +867,7 @@ static void q_scale_float(
*
* only handles common cases when we either
*
- * scale both, x and y or
+ * scale both, x and y or
* shrink both, x and y
*
* but that is pretty fast:
@@ -1195,22 +1195,9 @@ static ImBuf *scaleupx(struct ImBuf *ibuf, int newx)
{
uchar *rect, *_newrect = NULL, *newrect;
float *rectf, *_newrectf = NULL, *newrectf;
- float sample, add;
- float val_a, nval_a, diff_a;
- float val_b, nval_b, diff_b;
- float val_g, nval_g, diff_g;
- float val_r, nval_r, diff_r;
- float val_af, nval_af, diff_af;
- float val_bf, nval_bf, diff_bf;
- float val_gf, nval_gf, diff_gf;
- float val_rf, nval_rf, diff_rf;
int x, y;
bool do_rect = false, do_float = false;
- val_a = nval_a = diff_a = val_b = nval_b = diff_b = 0;
- val_g = nval_g = diff_g = val_r = nval_r = diff_r = 0;
- val_af = nval_af = diff_af = val_bf = nval_bf = diff_bf = 0;
- val_gf = nval_gf = diff_gf = val_rf = nval_rf = diff_rf = 0;
if (ibuf == NULL) {
return NULL;
}
@@ -1236,119 +1223,158 @@ static ImBuf *scaleupx(struct ImBuf *ibuf, int newx)
}
}
- add = (ibuf->x - 1.001) / (newx - 1.0);
-
rect = (uchar *)ibuf->rect;
rectf = (float *)ibuf->rect_float;
newrect = _newrect;
newrectf = _newrectf;
- for (y = ibuf->y; y > 0; y--) {
-
- sample = 0;
-
+ /* Special case, copy all columns, needed since the scaling logic assumes there is at least
+ * two rows to interpolate between causing out of bounds read for 1px images, see T70356. */
+ if (UNLIKELY(ibuf->x == 1)) {
if (do_rect) {
- val_a = rect[0];
- nval_a = rect[4];
- diff_a = nval_a - val_a;
- val_a += 0.5f;
-
- val_b = rect[1];
- nval_b = rect[5];
- diff_b = nval_b - val_b;
- val_b += 0.5f;
-
- val_g = rect[2];
- nval_g = rect[6];
- diff_g = nval_g - val_g;
- val_g += 0.5f;
-
- val_r = rect[3];
- nval_r = rect[7];
- diff_r = nval_r - val_r;
- val_r += 0.5f;
-
- rect += 8;
+ for (y = ibuf->y; y > 0; y--) {
+ for (x = newx; x > 0; x--) {
+ memcpy(newrect, rect, sizeof(char[4]));
+ newrect += 4;
+ }
+ rect += 4;
+ }
}
if (do_float) {
- val_af = rectf[0];
- nval_af = rectf[4];
- diff_af = nval_af - val_af;
+ for (y = ibuf->y; y > 0; y--) {
+ for (x = newx; x > 0; x--) {
+ memcpy(newrectf, rectf, sizeof(float[4]));
+ newrectf += 4;
+ }
+ rectf += 4;
+ }
+ }
+ }
+ else {
+ const float add = (ibuf->x - 1.001) / (newx - 1.0);
+ float sample;
- val_bf = rectf[1];
- nval_bf = rectf[5];
- diff_bf = nval_bf - val_bf;
+ float val_a, nval_a, diff_a;
+ float val_b, nval_b, diff_b;
+ float val_g, nval_g, diff_g;
+ float val_r, nval_r, diff_r;
+ float val_af, nval_af, diff_af;
+ float val_bf, nval_bf, diff_bf;
+ float val_gf, nval_gf, diff_gf;
+ float val_rf, nval_rf, diff_rf;
- val_gf = rectf[2];
- nval_gf = rectf[6];
- diff_gf = nval_gf - val_gf;
+ val_a = nval_a = diff_a = val_b = nval_b = diff_b = 0;
+ val_g = nval_g = diff_g = val_r = nval_r = diff_r = 0;
+ val_af = nval_af = diff_af = val_bf = nval_bf = diff_bf = 0;
+ val_gf = nval_gf = diff_gf = val_rf = nval_rf = diff_rf = 0;
- val_rf = rectf[3];
- nval_rf = rectf[7];
- diff_rf = nval_rf - val_rf;
+ for (y = ibuf->y; y > 0; y--) {
- rectf += 8;
- }
- for (x = newx; x > 0; x--) {
- if (sample >= 1.0f) {
- sample -= 1.0f;
+ sample = 0;
- if (do_rect) {
- val_a = nval_a;
- nval_a = rect[0];
- diff_a = nval_a - val_a;
- val_a += 0.5f;
-
- val_b = nval_b;
- nval_b = rect[1];
- diff_b = nval_b - val_b;
- val_b += 0.5f;
-
- val_g = nval_g;
- nval_g = rect[2];
- diff_g = nval_g - val_g;
- val_g += 0.5f;
-
- val_r = nval_r;
- nval_r = rect[3];
- diff_r = nval_r - val_r;
- val_r += 0.5f;
- rect += 4;
- }
- if (do_float) {
- val_af = nval_af;
- nval_af = rectf[0];
- diff_af = nval_af - val_af;
+ if (do_rect) {
+ val_a = rect[0];
+ nval_a = rect[4];
+ diff_a = nval_a - val_a;
+ val_a += 0.5f;
+
+ val_b = rect[1];
+ nval_b = rect[5];
+ diff_b = nval_b - val_b;
+ val_b += 0.5f;
+
+ val_g = rect[2];
+ nval_g = rect[6];
+ diff_g = nval_g - val_g;
+ val_g += 0.5f;
+
+ val_r = rect[3];
+ nval_r = rect[7];
+ diff_r = nval_r - val_r;
+ val_r += 0.5f;
+
+ rect += 8;
+ }
+ if (do_float) {
+ val_af = rectf[0];
+ nval_af = rectf[4];
+ diff_af = nval_af - val_af;
- val_bf = nval_bf;
- nval_bf = rectf[1];
- diff_bf = nval_bf - val_bf;
+ val_bf = rectf[1];
+ nval_bf = rectf[5];
+ diff_bf = nval_bf - val_bf;
- val_gf = nval_gf;
- nval_gf = rectf[2];
- diff_gf = nval_gf - val_gf;
+ val_gf = rectf[2];
+ nval_gf = rectf[6];
+ diff_gf = nval_gf - val_gf;
- val_rf = nval_rf;
- nval_rf = rectf[3];
- diff_rf = nval_rf - val_rf;
- rectf += 4;
- }
- }
- if (do_rect) {
- newrect[0] = val_a + sample * diff_a;
- newrect[1] = val_b + sample * diff_b;
- newrect[2] = val_g + sample * diff_g;
- newrect[3] = val_r + sample * diff_r;
- newrect += 4;
+ val_rf = rectf[3];
+ nval_rf = rectf[7];
+ diff_rf = nval_rf - val_rf;
+
+ rectf += 8;
}
- if (do_float) {
- newrectf[0] = val_af + sample * diff_af;
- newrectf[1] = val_bf + sample * diff_bf;
- newrectf[2] = val_gf + sample * diff_gf;
- newrectf[3] = val_rf + sample * diff_rf;
- newrectf += 4;
+ for (x = newx; x > 0; x--) {
+ if (sample >= 1.0f) {
+ sample -= 1.0f;
+
+ if (do_rect) {
+ val_a = nval_a;
+ nval_a = rect[0];
+ diff_a = nval_a - val_a;
+ val_a += 0.5f;
+
+ val_b = nval_b;
+ nval_b = rect[1];
+ diff_b = nval_b - val_b;
+ val_b += 0.5f;
+
+ val_g = nval_g;
+ nval_g = rect[2];
+ diff_g = nval_g - val_g;
+ val_g += 0.5f;
+
+ val_r = nval_r;
+ nval_r = rect[3];
+ diff_r = nval_r - val_r;
+ val_r += 0.5f;
+ rect += 4;
+ }
+ if (do_float) {
+ val_af = nval_af;
+ nval_af = rectf[0];
+ diff_af = nval_af - val_af;
+
+ val_bf = nval_bf;
+ nval_bf = rectf[1];
+ diff_bf = nval_bf - val_bf;
+
+ val_gf = nval_gf;
+ nval_gf = rectf[2];
+ diff_gf = nval_gf - val_gf;
+
+ val_rf = nval_rf;
+ nval_rf = rectf[3];
+ diff_rf = nval_rf - val_rf;
+ rectf += 4;
+ }
+ }
+ if (do_rect) {
+ newrect[0] = val_a + sample * diff_a;
+ newrect[1] = val_b + sample * diff_b;
+ newrect[2] = val_g + sample * diff_g;
+ newrect[3] = val_r + sample * diff_r;
+ newrect += 4;
+ }
+ if (do_float) {
+ newrectf[0] = val_af + sample * diff_af;
+ newrectf[1] = val_bf + sample * diff_bf;
+ newrectf[2] = val_gf + sample * diff_gf;
+ newrectf[3] = val_rf + sample * diff_rf;
+ newrectf += 4;
+ }
+ sample += add;
}
- sample += add;
}
}
@@ -1371,22 +1397,9 @@ static ImBuf *scaleupy(struct ImBuf *ibuf, int newy)
{
uchar *rect, *_newrect = NULL, *newrect;
float *rectf, *_newrectf = NULL, *newrectf;
- float sample, add;
- float val_a, nval_a, diff_a;
- float val_b, nval_b, diff_b;
- float val_g, nval_g, diff_g;
- float val_r, nval_r, diff_r;
- float val_af, nval_af, diff_af;
- float val_bf, nval_bf, diff_bf;
- float val_gf, nval_gf, diff_gf;
- float val_rf, nval_rf, diff_rf;
int x, y, skipx;
bool do_rect = false, do_float = false;
- val_a = nval_a = diff_a = val_b = nval_b = diff_b = 0;
- val_g = nval_g = diff_g = val_r = nval_r = diff_r = 0;
- val_af = nval_af = diff_af = val_bf = nval_bf = diff_bf = 0;
- val_gf = nval_gf = diff_gf = val_rf = nval_rf = diff_rf = 0;
if (ibuf == NULL) {
return NULL;
}
@@ -1412,126 +1425,159 @@ static ImBuf *scaleupy(struct ImBuf *ibuf, int newy)
}
}
- add = (ibuf->y - 1.001) / (newy - 1.0);
- skipx = 4 * ibuf->x;
-
rect = (uchar *)ibuf->rect;
rectf = (float *)ibuf->rect_float;
newrect = _newrect;
newrectf = _newrectf;
- for (x = ibuf->x; x > 0; x--) {
+ skipx = 4 * ibuf->x;
- sample = 0;
+ /* Special case, copy all rows, needed since the scaling logic assumes there is at least
+ * two rows to interpolate between causing out of bounds read for 1px images, see T70356. */
+ if (UNLIKELY(ibuf->y == 1)) {
if (do_rect) {
- rect = ((uchar *)ibuf->rect) + 4 * (x - 1);
- newrect = _newrect + 4 * (x - 1);
-
- val_a = rect[0];
- nval_a = rect[skipx];
- diff_a = nval_a - val_a;
- val_a += 0.5f;
-
- val_b = rect[1];
- nval_b = rect[skipx + 1];
- diff_b = nval_b - val_b;
- val_b += 0.5f;
-
- val_g = rect[2];
- nval_g = rect[skipx + 2];
- diff_g = nval_g - val_g;
- val_g += 0.5f;
-
- val_r = rect[3];
- nval_r = rect[skipx + 3];
- diff_r = nval_r - val_r;
- val_r += 0.5f;
-
- rect += 2 * skipx;
+ for (y = newy; y > 0; y--) {
+ memcpy(newrect, rect, sizeof(char) * skipx);
+ newrect += skipx;
+ }
}
if (do_float) {
- rectf = ibuf->rect_float + 4 * (x - 1);
- newrectf = _newrectf + 4 * (x - 1);
-
- val_af = rectf[0];
- nval_af = rectf[skipx];
- diff_af = nval_af - val_af;
+ for (y = newy; y > 0; y--) {
+ memcpy(newrectf, rectf, sizeof(float) * skipx);
+ newrectf += skipx;
+ }
+ }
+ }
+ else {
+ const float add = (ibuf->y - 1.001) / (newy - 1.0);
+ float sample;
+
+ float val_a, nval_a, diff_a;
+ float val_b, nval_b, diff_b;
+ float val_g, nval_g, diff_g;
+ float val_r, nval_r, diff_r;
+ float val_af, nval_af, diff_af;
+ float val_bf, nval_bf, diff_bf;
+ float val_gf, nval_gf, diff_gf;
+ float val_rf, nval_rf, diff_rf;
+
+ val_a = nval_a = diff_a = val_b = nval_b = diff_b = 0;
+ val_g = nval_g = diff_g = val_r = nval_r = diff_r = 0;
+ val_af = nval_af = diff_af = val_bf = nval_bf = diff_bf = 0;
+ val_gf = nval_gf = diff_gf = val_rf = nval_rf = diff_rf = 0;
+
+ for (x = ibuf->x; x > 0; x--) {
+ sample = 0;
+ if (do_rect) {
+ rect = ((uchar *)ibuf->rect) + 4 * (x - 1);
+ newrect = _newrect + 4 * (x - 1);
+
+ val_a = rect[0];
+ nval_a = rect[skipx];
+ diff_a = nval_a - val_a;
+ val_a += 0.5f;
+
+ val_b = rect[1];
+ nval_b = rect[skipx + 1];
+ diff_b = nval_b - val_b;
+ val_b += 0.5f;
+
+ val_g = rect[2];
+ nval_g = rect[skipx + 2];
+ diff_g = nval_g - val_g;
+ val_g += 0.5f;
+
+ val_r = rect[3];
+ nval_r = rect[skipx + 3];
+ diff_r = nval_r - val_r;
+ val_r += 0.5f;
+
+ rect += 2 * skipx;
+ }
+ if (do_float) {
+ rectf = ibuf->rect_float + 4 * (x - 1);
+ newrectf = _newrectf + 4 * (x - 1);
- val_bf = rectf[1];
- nval_bf = rectf[skipx + 1];
- diff_bf = nval_bf - val_bf;
+ val_af = rectf[0];
+ nval_af = rectf[skipx];
+ diff_af = nval_af - val_af;
- val_gf = rectf[2];
- nval_gf = rectf[skipx + 2];
- diff_gf = nval_gf - val_gf;
+ val_bf = rectf[1];
+ nval_bf = rectf[skipx + 1];
+ diff_bf = nval_bf - val_bf;
- val_rf = rectf[3];
- nval_rf = rectf[skipx + 3];
- diff_rf = nval_rf - val_rf;
+ val_gf = rectf[2];
+ nval_gf = rectf[skipx + 2];
+ diff_gf = nval_gf - val_gf;
- rectf += 2 * skipx;
- }
+ val_rf = rectf[3];
+ nval_rf = rectf[skipx + 3];
+ diff_rf = nval_rf - val_rf;
- for (y = newy; y > 0; y--) {
- if (sample >= 1.0f) {
- sample -= 1.0f;
+ rectf += 2 * skipx;
+ }
+ for (y = newy; y > 0; y--) {
+ if (sample >= 1.0f) {
+ sample -= 1.0f;
+
+ if (do_rect) {
+ val_a = nval_a;
+ nval_a = rect[0];
+ diff_a = nval_a - val_a;
+ val_a += 0.5f;
+
+ val_b = nval_b;
+ nval_b = rect[1];
+ diff_b = nval_b - val_b;
+ val_b += 0.5f;
+
+ val_g = nval_g;
+ nval_g = rect[2];
+ diff_g = nval_g - val_g;
+ val_g += 0.5f;
+
+ val_r = nval_r;
+ nval_r = rect[3];
+ diff_r = nval_r - val_r;
+ val_r += 0.5f;
+ rect += skipx;
+ }
+ if (do_float) {
+ val_af = nval_af;
+ nval_af = rectf[0];
+ diff_af = nval_af - val_af;
+
+ val_bf = nval_bf;
+ nval_bf = rectf[1];
+ diff_bf = nval_bf - val_bf;
+
+ val_gf = nval_gf;
+ nval_gf = rectf[2];
+ diff_gf = nval_gf - val_gf;
+
+ val_rf = nval_rf;
+ nval_rf = rectf[3];
+ diff_rf = nval_rf - val_rf;
+ rectf += skipx;
+ }
+ }
if (do_rect) {
- val_a = nval_a;
- nval_a = rect[0];
- diff_a = nval_a - val_a;
- val_a += 0.5f;
-
- val_b = nval_b;
- nval_b = rect[1];
- diff_b = nval_b - val_b;
- val_b += 0.5f;
-
- val_g = nval_g;
- nval_g = rect[2];
- diff_g = nval_g - val_g;
- val_g += 0.5f;
-
- val_r = nval_r;
- nval_r = rect[3];
- diff_r = nval_r - val_r;
- val_r += 0.5f;
- rect += skipx;
+ newrect[0] = val_a + sample * diff_a;
+ newrect[1] = val_b + sample * diff_b;
+ newrect[2] = val_g + sample * diff_g;
+ newrect[3] = val_r + sample * diff_r;
+ newrect += skipx;
}
if (do_float) {
- val_af = nval_af;
- nval_af = rectf[0];
- diff_af = nval_af - val_af;
-
- val_bf = nval_bf;
- nval_bf = rectf[1];
- diff_bf = nval_bf - val_bf;
-
- val_gf = nval_gf;
- nval_gf = rectf[2];
- diff_gf = nval_gf - val_gf;
-
- val_rf = nval_rf;
- nval_rf = rectf[3];
- diff_rf = nval_rf - val_rf;
- rectf += skipx;
+ newrectf[0] = val_af + sample * diff_af;
+ newrectf[1] = val_bf + sample * diff_bf;
+ newrectf[2] = val_gf + sample * diff_gf;
+ newrectf[3] = val_rf + sample * diff_rf;
+ newrectf += skipx;
}
+ sample += add;
}
- if (do_rect) {
- newrect[0] = val_a + sample * diff_a;
- newrect[1] = val_b + sample * diff_b;
- newrect[2] = val_g + sample * diff_g;
- newrect[3] = val_r + sample * diff_r;
- newrect += skipx;
- }
- if (do_float) {
- newrectf[0] = val_af + sample * diff_af;
- newrectf[1] = val_bf + sample * diff_bf;
- newrectf[2] = val_gf + sample * diff_gf;
- newrectf[3] = val_rf + sample * diff_rf;
- newrectf += skipx;
- }
- sample += add;
}
}
@@ -1620,6 +1666,8 @@ static void scalefast_Z_ImBuf(ImBuf *ibuf, int newx, int newy)
*/
bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy)
{
+ BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
+
if (ibuf == NULL) {
return false;
}
@@ -1666,6 +1714,8 @@ struct imbufRGBA {
*/
bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy)
{
+ BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
+
unsigned int *rect, *_newrect, *newrect;
struct imbufRGBA *rectf, *_newrectf, *newrectf;
int x, y;
@@ -1838,6 +1888,8 @@ static void *do_scale_thread(void *data_v)
void IMB_scaleImBuf_threaded(ImBuf *ibuf, unsigned int newx, unsigned int newy)
{
+ BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
+
ScaleTreadInitData init_data = {NULL};
/* prepare initialization data */
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index 7ffb61e1d1b..131b60b90fb 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -538,7 +538,7 @@ static void get_loop_normals(struct Mesh *mesh,
BKE_mesh_calc_normals_split(mesh);
const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
- BLI_assert(lnors != nullptr || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL");
+ BLI_assert_msg(lnors != nullptr, "BKE_mesh_calc_normals_split() should have computed CD_NORMAL");
normals.resize(mesh->totloop);
diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp
index 626e4258239..e52bdca0d87 100644
--- a/source/blender/io/collada/AnimationImporter.cpp
+++ b/source/blender/io/collada/AnimationImporter.cpp
@@ -1090,7 +1090,7 @@ void AnimationImporter::translate_Animations(
apply_matrix_curves(ob, animcurves, root, node, transform);
}
else {
- /* calculate rnapaths and array index of fcurves according to transformation and
+ /* Calculate RNA-paths and array index of F-curves according to transformation and
* animation class */
Assign_transform_animations(transform, &bindings[j], &animcurves, is_joint, joint_path);
diff --git a/source/blender/io/collada/BCAnimationSampler.cpp b/source/blender/io/collada/BCAnimationSampler.cpp
index 02a4e1f3167..d2bde193c0a 100644
--- a/source/blender/io/collada/BCAnimationSampler.cpp
+++ b/source/blender/io/collada/BCAnimationSampler.cpp
@@ -161,10 +161,12 @@ void BCAnimationSampler::update_animation_curves(BCAnimation &animation,
BCSample &BCAnimationSampler::sample_object(Object *ob, int frame_index, bool for_opensim)
{
BCSample &ob_sample = sample_data.add(ob, frame_index);
- // if (export_settings.get_apply_global_orientation()) {
- // const BCMatrix &global_transform = export_settings.get_global_transform();
- // ob_sample.get_matrix(global_transform);
- //}
+#if 0
+ if (export_settings.get_apply_global_orientation()) {
+ const BCMatrix &global_transform = export_settings.get_global_transform();
+ ob_sample.get_matrix(global_transform);
+ }
+#endif
if (ob->type == OB_ARMATURE) {
bPoseChannel *pchan;
diff --git a/source/blender/io/collada/ControllerExporter.cpp b/source/blender/io/collada/ControllerExporter.cpp
index e61ed47adee..d768d2e44e8 100644
--- a/source/blender/io/collada/ControllerExporter.cpp
+++ b/source/blender/io/collada/ControllerExporter.cpp
@@ -231,7 +231,7 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm)
uint idx = vert->dw[j].def_nr;
if (idx >= joint_index_by_def_index.size()) {
/* XXX: Maybe better find out where and
- * why the Out Of Bound indexes get created ? */
+ * why the Out Of Bound indexes get created? */
oob_counter += 1;
}
else {
diff --git a/source/blender/io/collada/DocumentExporter.cpp b/source/blender/io/collada/DocumentExporter.cpp
index 46dfdda4ede..3c64591366c 100644
--- a/source/blender/io/collada/DocumentExporter.cpp
+++ b/source/blender/io/collada/DocumentExporter.cpp
@@ -180,7 +180,7 @@ int DocumentExporter::exportCurrentScene()
bContext *C = blender_context.get_context();
PointerRNA sceneptr, unit_settings;
- PropertyRNA *system; /* unused , *scale; */
+ PropertyRNA *system; /* unused, *scale; */
clear_global_id_map();
diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp
index ac4c65464c8..81f0cc608d2 100644
--- a/source/blender/io/collada/Materials.cpp
+++ b/source/blender/io/collada/Materials.cpp
@@ -411,10 +411,9 @@ void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
if (!has_specularity) {
/* If specularity is black or not defined reset the Specular value to 0
- TODO: This is a solution only for a corner case. We must find a better
- way to handle specularity in general. Also note that currently we
- do not export specularity values, see EffectExporter::operator()
- */
+ * TODO: This is a solution only for a corner case. We must find a better
+ * way to handle specularity in general. Also note that currently we
+ * do not export specularity values, see EffectExporter::operator() */
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Specular");
((bNodeSocketValueFloat *)socket->default_value)->value = 0.0f;
}
diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp
index 5aa57159328..ef486375ce3 100644
--- a/source/blender/io/collada/MeshImporter.cpp
+++ b/source/blender/io/collada/MeshImporter.cpp
@@ -708,7 +708,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
prim.totpoly++;
}
- /* Moving cursor to the next triangle fan. */
+ /* Moving cursor to the next triangle fan. */
if (mp_has_normals) {
normal_indices += 2;
}
diff --git a/source/blender/io/collada/TransformReader.cpp b/source/blender/io/collada/TransformReader.cpp
index 81b0ffc340a..d23f8da2570 100644
--- a/source/blender/io/collada/TransformReader.cpp
+++ b/source/blender/io/collada/TransformReader.cpp
@@ -103,9 +103,11 @@ void TransformReader::dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[
COLLADABU::Math::Vector3 &axis = ro->getRotationAxis();
const float angle = (float)DEG2RAD(ro->getRotationAngle());
const float ax[] = {(float)axis[0], (float)axis[1], (float)axis[2]};
- // float quat[4];
- // axis_angle_to_quat(quat, axis, angle);
- // quat_to_mat4(m, quat);
+#if 0
+ float quat[4];
+ axis_angle_to_quat(quat, axis, angle);
+ quat_to_mat4(m, quat);
+#endif
axis_angle_to_mat4(m, ax, angle);
}
diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp
index 9967a526971..60c4a9bad13 100644
--- a/source/blender/io/collada/collada_utils.cpp
+++ b/source/blender/io/collada/collada_utils.cpp
@@ -1216,8 +1216,10 @@ static bNodeSocket *bc_group_add_input_socket(bNodeTree *ntree,
{
bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index);
- //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket);
- //return socket;
+# if 0
+ bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket);
+ return socket;
+# endif
bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket);
bNode *inputGroup = ntreeFindType(ntree, NODE_GROUP_INPUT);
@@ -1235,8 +1237,10 @@ static bNodeSocket *bc_group_add_output_socket(bNodeTree *ntree,
{
bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index);
- //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket);
- //return socket;
+# if 0
+ bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket);
+ return socket;
+# endif
bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, from_node, from_socket);
bNode *outputGroup = ntreeFindType(ntree, NODE_GROUP_OUTPUT);
diff --git a/source/blender/io/gpencil/gpencil_io.h b/source/blender/io/gpencil/gpencil_io.h
index 24b13479359..fab867b38b3 100644
--- a/source/blender/io/gpencil/gpencil_io.h
+++ b/source/blender/io/gpencil/gpencil_io.h
@@ -27,9 +27,9 @@ extern "C" {
#endif
struct ARegion;
-struct bContext;
struct Object;
struct View3D;
+struct bContext;
typedef struct GpencilIOParams {
bContext *C;
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh
index c8d85d08f7b..02758883f19 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.hh
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh
@@ -37,9 +37,9 @@ struct Object;
struct RegionView3D;
struct Scene;
-struct bGPdata;
struct bGPDlayer;
struct bGPDstroke;
+struct bGPdata;
using blender::Vector;
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
index 3b20ac9f110..82f8444d43e 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
@@ -91,19 +91,20 @@ bool GpencilExporterPDF::write()
{
/* Support unicode character paths on Windows. */
HPDF_STATUS res = 0;
- /* TODO: It looks libharu does not support unicode. */
- //#ifdef WIN32
- // char filename_cstr[FILE_MAX];
- // BLI_strncpy(filename_cstr, filename_, FILE_MAX);
- //
- // UTF16_ENCODE(filename_cstr);
- // std::wstring wstr(filename_cstr_16);
- // res = HPDF_SaveToFile(pdf_, wstr.c_str());
- //
- // UTF16_UN_ENCODE(filename_cstr);
- //#else
+
+ /* TODO: It looks `libharu` does not support unicode. */
+#if 0 /* `ifdef WIN32` */
+ char filename_cstr[FILE_MAX];
+ BLI_strncpy(filename_cstr, filename_, FILE_MAX);
+
+ UTF16_ENCODE(filename_cstr);
+ std::wstring wstr(filename_cstr_16);
+ res = HPDF_SaveToFile(pdf_, wstr.c_str());
+
+ UTF16_UN_ENCODE(filename_cstr);
+#else
res = HPDF_SaveToFile(pdf_, filename_);
- //#endif
+#endif
return (res == 0) ? true : false;
}
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
index 52fcc017ffb..db6bbc7768e 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
@@ -148,6 +148,8 @@ bool GpencilImporterSVG::read()
for (bGPDspoint &pt : MutableSpan(gps->points, gps->totpoints)) {
sub_v3_v3(&pt.x, gp_center);
}
+ /* Calc stroke bounding box. */
+ BKE_gpencil_stroke_boundingbox_calc(gps);
}
}
}
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh
index 0e9271dd2c6..99e8b1ed4fd 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh
@@ -24,10 +24,10 @@
#include "gpencil_io_import_base.hh"
struct GpencilIOParams;
-struct NSVGshape;
struct NSVGpath;
-struct bGPdata;
+struct NSVGshape;
struct bGPDframe;
+struct bGPdata;
#define SVG_IMPORTER_NAME "SVG Import for Grease Pencil"
#define SVG_IMPORTER_VERSION "v1.0"
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 43969bf0768..c9d652ad03d 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -621,9 +621,9 @@ typedef enum IDRecalcFlag {
* When a collection gets tagged with this flag, all objects depending on the geometry and
* transforms on any of the objects in the collection are updated. */
ID_RECALC_GEOMETRY = (1 << 1),
- /* Same as #ID_RECALC_GEOMETRY, but instead of tagging the batch cache as `dirty_all`, just tags
- what matches the deform cache. */
- ID_RECALC_GEOMETRY_DEFORM = (1 << 2),
+
+ /* ** Animation or time changed and animation is to be re-evaluated. ** */
+ ID_RECALC_ANIMATION = (1 << 2),
/* ** Particle system changed. ** */
/* Only do pathcache etc. */
@@ -683,9 +683,6 @@ typedef enum IDRecalcFlag {
* have to be copied on every update. */
ID_RECALC_PARAMETERS = (1 << 21),
- /* ** Animation or time changed and animation is to be re-evaluated. ** */
- ID_RECALC_ANIMATION = (1 << 22),
-
/* Input has changed and datablock is to be reload from disk.
* Applies to movie clips to inform that copy-on-written version is to be refreshed for the new
* input file or for color space changes. */
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index aba6ccfd3ba..baed2aa2866 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -830,7 +830,7 @@ typedef struct SpaceAction {
/** The currently active context (when not showing action). */
bDopeSheet ads;
- /** For Time-Slide transform mode drawing - current frame?. */
+ /** For Time-Slide transform mode drawing - current frame? */
float timeslide;
short flag;
@@ -838,7 +838,7 @@ typedef struct SpaceAction {
char mode;
/* Storage for sub-space types. */
char mode_prev;
- /** Automatic keyframe snapping mode . */
+ /** Automatic keyframe snapping mode. */
char autosnap;
/** (eTimeline_Cache_Flag). */
char cache_display;
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index c02035be4ac..a49a76c3f26 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -51,7 +51,7 @@ typedef struct FModifier {
/** Pointer to modifier data. */
void *data;
- /** User-defined description for the modifier - MAX_ID_NAME-2. */
+ /** User-defined description for the modifier - `MAX_ID_NAME - 2`. */
char name[64];
/** Type of f-curve modifier. */
short type;
@@ -185,7 +185,7 @@ typedef enum eFMod_Generator_Functions {
/* envelope modifier - envelope data */
typedef struct FCM_EnvelopeData {
- /** Min/max values for envelope at this point (absolute values) . */
+ /** Min/max values for envelope at this point (absolute values). */
float min, max;
/** Time for that this sample-point occurs. */
float time;
@@ -198,7 +198,7 @@ typedef struct FCM_EnvelopeData {
/* envelope-like adjustment to values (for fade in/out) */
typedef struct FMod_Envelope {
- /** Data-points defining envelope to apply (array) . */
+ /** Data-points defining envelope to apply (array). */
FCM_EnvelopeData *data;
/** Number of envelope points. */
int totvert;
@@ -210,7 +210,7 @@ typedef struct FMod_Envelope {
} FMod_Envelope;
/* cycling/repetition modifier data */
-// TODO: we can only do complete cycles...
+/* TODO: we can only do complete cycles. */
typedef struct FMod_Cycles {
/** Extrapolation mode to use before first keyframe. */
short before_mode;
@@ -321,7 +321,7 @@ typedef struct DriverTarget {
/**
* Name of the posebone to use
- * (for vars where DTAR_FLAG_STRUCT_REF is used) - MAX_ID_NAME-2.
+ * (for vars where DTAR_FLAG_STRUCT_REF is used) - `MAX_ID_NAME - 2`.
*/
char pchan_name[64];
/** Transform channel index (for #DVAR_TYPE_TRANSFORM_CHAN). */
@@ -418,7 +418,7 @@ typedef struct DriverVar {
/**
* Name of the variable to use in py-expression
- * (must be valid python identifier) - MAX_ID_NAME-2.
+ * (must be valid python identifier) - `MAX_ID_NAME - 2`.
*/
char name[64];
@@ -734,7 +734,7 @@ typedef struct NlaStrip {
/** F-Curve modifiers to be applied to the entire strip's referenced F-Curves. */
ListBase modifiers;
- /** User-Visible Identifier for Strip - MAX_ID_NAME-2. */
+ /** User-Visible Identifier for Strip - `MAX_ID_NAME - 2`. */
char name[64];
/** Influence of strip. */
@@ -873,7 +873,7 @@ typedef struct NlaTrack {
* \note not really useful, but we need a '_pad' var anyways! */
int index;
- /** Short user-description of this track - MAX_ID_NAME-2. */
+ /** Short user-description of this track - `MAX_ID_NAME - 2`. */
char name[64];
} NlaTrack;
@@ -917,7 +917,7 @@ typedef struct KS_Path {
/** ID block that keyframes are for. */
ID *id;
- /** Name of the group to add to - MAX_ID_NAME-2. */
+ /** Name of the group to add to - `MAX_ID_NAME - 2`. */
char group[64];
/** ID-type that path can be used on. */
@@ -977,13 +977,13 @@ typedef struct KeyingSet {
/** (KS_Path) paths to keyframe to. */
ListBase paths;
- /** Unique name (for search, etc.) - MAX_ID_NAME-2 . */
+ /** Unique name (for search, etc.) - `MAX_ID_NAME - 2`. */
char idname[64];
- /** User-viewable name for KeyingSet (for menus, etc.) - MAX_ID_NAME-2. */
+ /** User-viewable name for KeyingSet (for menus, etc.) - `MAX_ID_NAME - 2`. */
char name[64];
/** (RNA_DYN_DESCR_MAX) short help text. */
char description[240];
- /** Name of the typeinfo data used for the relative paths - MAX_ID_NAME-2. */
+ /** Name of the typeinfo data used for the relative paths - `MAX_ID_NAME - 2`. */
char typeinfo[64];
/** Index of the active path. */
diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h
index f397b7f27b4..454d843112a 100644
--- a/source/blender/makesdna/DNA_armature_types.h
+++ b/source/blender/makesdna/DNA_armature_types.h
@@ -76,7 +76,12 @@ typedef struct Bone {
/** dist, weight: for non-deformgroup deforms. */
float dist, weight;
- /** width: for block bones. keep in this order, transform!. */
+ /**
+ * The width for block bones.
+ *
+ * \note keep in this order for transform code which stores a pointer to `xwidth`,
+ * accessing length and `zwidth` as offsets.
+ */
float xwidth, length, zwidth;
/**
* Radius for head/tail sphere, defining deform as well,
diff --git a/source/blender/makesdna/DNA_asset_defaults.h b/source/blender/makesdna/DNA_asset_defaults.h
index ff00ba79cf0..ce01563f619 100644
--- a/source/blender/makesdna/DNA_asset_defaults.h
+++ b/source/blender/makesdna/DNA_asset_defaults.h
@@ -32,6 +32,13 @@
0 \
}
+#define _DNA_DEFAULT_AssetLibraryReference \
+ { \
+ .type = ASSET_LIBRARY_LOCAL, \
+ /* Not needed really (should be ignored for #ASSET_LIBRARY_LOCAL), but helps debugging. */ \
+ .custom_library_index = -1, \
+ }
+
/** \} */
/* clang-format on */
diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h
index 697d25653f8..ca16e6728dd 100644
--- a/source/blender/makesdna/DNA_asset_types.h
+++ b/source/blender/makesdna/DNA_asset_types.h
@@ -20,6 +20,7 @@
#pragma once
+#include "DNA_defs.h"
#include "DNA_listBase.h"
#ifdef __cplusplus
@@ -36,6 +37,15 @@ typedef struct AssetTag {
char name[64]; /* MAX_NAME */
} AssetTag;
+#
+#
+typedef struct AssetFilterSettings {
+ /** Tags to match against. These are newly allocated, and compared against the
+ * #AssetMetaData.tags. */
+ ListBase tags; /* AssetTag */
+ uint64_t id_types; /* rna_enum_id_type_filter_items */
+} AssetFilterSettings;
+
/**
* \brief The meta-data of an asset.
* By creating and giving this for a data-block (#ID.asset_data), the data-block becomes an asset.
@@ -62,6 +72,51 @@ typedef struct AssetMetaData {
char _pad[4];
} AssetMetaData;
+typedef enum eAssetLibraryType {
+ /* For the future. Display assets bundled with Blender by default. */
+ // ASSET_LIBRARY_BUNDLED = 0,
+ /** Display assets from the current session (current "Main"). */
+ ASSET_LIBRARY_LOCAL = 1,
+ /* For the future. Display assets for the current project. */
+ // ASSET_LIBRARY_PROJECT = 2,
+
+ /** Display assets from custom asset libraries, as defined in the preferences
+ * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname
+ * then.
+ * In RNA, we add the index of the custom library to this to identify it by index. So keep
+ * this last! */
+ ASSET_LIBRARY_CUSTOM = 100,
+} eAssetLibraryType;
+
+/**
+ * Information to identify a asset library. May be either one of the predefined types (current
+ * 'Main', builtin library, project library), or a custom type as defined in the Preferences.
+ *
+ * If the type is set to #ASSET_LIBRARY_CUSTOM, `custom_library_index` must be set to identify the
+ * custom library. Otherwise it is not used.
+ */
+typedef struct AssetLibraryReference {
+ short type; /* eAssetLibraryType */
+ char _pad1[2];
+ /**
+ * If showing a custom asset library (#ASSET_LIBRARY_CUSTOM), this is the index of the
+ * #bUserAssetLibrary within #UserDef.asset_libraries.
+ * Should be ignored otherwise (but better set to -1 then, for sanity and debugging).
+ */
+ int custom_library_index;
+} AssetLibraryReference;
+
+/**
+ * Not part of the core design, we should try to get rid of it. Only needed to wrap FileDirEntry
+ * into a type with PropertyGroup as base, so we can have an RNA collection of #AssetHandle's to
+ * pass to the UI.
+ */
+#
+#
+typedef struct AssetHandle {
+ const struct FileDirEntry *file_data;
+} AssetHandle;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 34f50b23c77..a77fbc9e45e 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -425,7 +425,7 @@ typedef struct bRigidBodyJointConstraint {
typedef struct bClampToConstraint {
/** 'target' must be a curve. */
struct Object *tar;
- /** Which axis/plane to compare owner's location on . */
+ /** Which axis/plane to compare owner's location on. */
int flag;
/** For legacy reasons, this is flag2. used for any extra settings. */
int flag2;
@@ -474,7 +474,7 @@ typedef struct bTransformConstraint {
float from_min[3];
/** To map on to to_min/max range. */
float from_max[3];
- /** Range of motion on owner caused by target . */
+ /** Range of motion on owner caused by target. */
float to_min[3];
float to_max[3];
@@ -482,7 +482,7 @@ typedef struct bTransformConstraint {
float from_min_rot[3];
/** To map on to to_min/max range. */
float from_max_rot[3];
- /** Range of motion on owner caused by target . */
+ /** Range of motion on owner caused by target. */
float to_min_rot[3];
float to_max_rot[3];
@@ -490,7 +490,7 @@ typedef struct bTransformConstraint {
float from_min_scale[3];
/** To map on to to_min/max range. */
float from_max_scale[3];
- /** Range of motion on owner caused by target . */
+ /** Range of motion on owner caused by target. */
float to_min_scale[3];
float to_max_scale[3];
} bTransformConstraint;
@@ -1102,7 +1102,7 @@ typedef enum eRotLimit_Flags {
/* distance limit constraint */
/* bDistLimitConstraint->flag */
typedef enum eDistLimit_Flag {
- /* "soft" cushion effect when reaching the limit sphere */ // NOT IMPLEMENTED!
+ /* "soft" cushion effect when reaching the limit sphere */ /* NOT IMPLEMENTED! */
LIMITDIST_USESOFT = (1 << 0),
/* as for all Limit constraints - allow to be used during transform? */
LIMITDIST_TRANSFORM = (1 << 1),
diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h
index 3732de6c0ec..520fc6c1b00 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -35,6 +35,7 @@ extern "C" {
#define MAXTEXTBOX 256 /* used in readfile.c and editfont.c */
struct AnimData;
+struct CurveEval;
struct CurveProfile;
struct EditFont;
struct GHash;
@@ -43,7 +44,6 @@ struct Key;
struct Material;
struct Object;
struct VFont;
-struct CurveEval;
/* These two Lines with # tell makesdna this struct can be excluded. */
#
diff --git a/source/blender/makesdna/DNA_documentation.h b/source/blender/makesdna/DNA_documentation.h
index 0251625292c..85190d8a56d 100644
--- a/source/blender/makesdna/DNA_documentation.h
+++ b/source/blender/makesdna/DNA_documentation.h
@@ -56,7 +56,7 @@
*
* Ignored structs can only be referred to from non-ignored structs
* when referred to as a pointer (where they're usually allocated
- * and cleared in ``readfile.c``).
+ * and cleared in `readfile.c`).
*
* - %Path to the header files
*
diff --git a/source/blender/makesdna/DNA_fluid_defaults.h b/source/blender/makesdna/DNA_fluid_defaults.h
index 9454342654c..95f5b8b66b0 100644
--- a/source/blender/makesdna/DNA_fluid_defaults.h
+++ b/source/blender/makesdna/DNA_fluid_defaults.h
@@ -101,7 +101,6 @@
.noise_time_anim = 0.1f, \
.res_noise = {0, 0, 0}, \
.noise_scale = 2, \
- .noise_type = FLUID_NOISE_TYPE_WAVELET, \
.particle_randomness = 0.1f, \
.particle_number = 2, \
.particle_minimum = 8, \
diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h
index cec6eb0d044..835af3c6ff8 100644
--- a/source/blender/makesdna/DNA_fluid_types.h
+++ b/source/blender/makesdna/DNA_fluid_types.h
@@ -198,11 +198,6 @@ enum {
FLUID_DOMAIN_TYPE_LIQUID = 1,
};
-/* Smoke noise types. */
-enum {
- FLUID_NOISE_TYPE_WAVELET = (1 << 0),
-};
-
/* Mesh levelset generator types. */
enum {
FLUID_DOMAIN_MESH_IMPROVED = 0,
@@ -580,8 +575,7 @@ typedef struct FluidDomainSettings {
float noise_time_anim;
int res_noise[3];
int noise_scale;
- short noise_type; /* Noise type: wave, curl, anisotropic. */
- char _pad3[2]; /* Unused. */
+ char _pad3[4]; /* Unused. */
/* Liquid domain options. */
float particle_randomness;
diff --git a/source/blender/makesdna/DNA_freestyle_types.h b/source/blender/makesdna/DNA_freestyle_types.h
index 884e11f3a8e..4d4fbaed29a 100644
--- a/source/blender/makesdna/DNA_freestyle_types.h
+++ b/source/blender/makesdna/DNA_freestyle_types.h
@@ -145,7 +145,7 @@ typedef struct FreestyleConfig {
int flags;
float sphere_radius;
float dkr_epsilon;
- /** In radians!. */
+ /** In radians. */
float crease_angle;
ListBase linesets;
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index b9697beeea9..81e9abc4916 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -307,6 +307,7 @@
.calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES, \
.angle_splitting_threshold = DEG2RAD(60.0f), \
.chaining_image_threshold = 0.001f, \
+ .overscan = 0.1f,\
}
#define _DNA_DEFAULT_LengthGpencilModifierData \
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index fac5bd3d4f4..c91afa58cb1 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -942,9 +942,11 @@ struct LineartCache;
typedef struct LineartGpencilModifierData {
GpencilModifierData modifier;
- short edge_types; /* line type enable flags, bits in eLineartEdgeFlag */
+ /** Line type enable flags, bits in #eLineartEdgeFlag. */
+ short edge_types;
- char source_type; /* Object or Collection, from eLineartGpencilModifierSource */
+ /** Object or Collection, from #eLineartGpencilModifierSource. */
+ char source_type;
char use_multiple_levels;
short level_start;
@@ -963,14 +965,22 @@ typedef struct LineartGpencilModifierData {
char source_vertex_group[64];
char vgname[64];
+ /**
+ * Camera focal length is divided by `1 + overscan`, before calculation, which give a wider FOV,
+ * this doesn't change coordinates range internally (-1, 1), but makes the calculated frame
+ * bigger than actual output. This is for the easier shifting calculation. A value of 0.5 means
+ * the "internal" focal length become 2/3 of the actual camera.
+ */
+ float overscan;
+
float opacity;
short thickness;
- unsigned char mask_switches; /* eLineartGpencilMaskSwitches */
+ unsigned char mask_switches; /* #eLineartGpencilMaskSwitches */
unsigned char material_mask_bits;
unsigned char intersection_mask;
- char _pad[7];
+ char _pad[3];
/** `0..1` range for cosine angle */
float crease_threshold;
@@ -984,7 +994,7 @@ typedef struct LineartGpencilModifierData {
/* Ported from SceneLineArt flags. */
int calculation_flags;
- /* eLineArtGPencilModifierFlags, modifier internal state. */
+ /* #eLineArtGPencilModifierFlags, modifier internal state. */
int flags;
/* Runtime data. */
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 0952c45e81f..380d8ad1249 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -33,8 +33,8 @@ extern "C" {
struct AnimData;
struct Curve;
-struct MDeformVert;
struct Curve;
+struct MDeformVert;
#define GP_DEFAULT_PIX_FACTOR 1.0f
#define GP_DEFAULT_GRID_LINES 4
diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h
index 0552c449819..e2fdd19232c 100644
--- a/source/blender/makesdna/DNA_ipo_types.h
+++ b/source/blender/makesdna/DNA_ipo_types.h
@@ -63,9 +63,9 @@ typedef struct IpoDriver {
typedef struct IpoCurve {
struct IpoCurve *next, *prev;
- /** Array of BPoints (sizeof(BPoint) * totvert) - i.e. baked/imported data. */
+ /** Array of #BPoints `(sizeof(BPoint) * totvert)` - i.e. baked/imported data. */
struct BPoint *bp;
- /** Array of BezTriples (sizeof(BezTriple) * totvert) - i.e. user-editable keyframes . */
+ /** Array of #BezTriples `(sizeof(BezTriple) * totvert)` - i.e. user-editable keyframes. */
struct BezTriple *bezt;
/** Bounding boxes. */
@@ -75,14 +75,14 @@ typedef struct IpoCurve {
short blocktype, adrcode, vartype;
/** Total number of BezTriples (i.e. keyframes) on curve. */
short totvert;
- /** Interpolation and extrapolation modes . */
+ /** Interpolation and extrapolation modes. */
short ipo, extrap;
/** Flag= settings. */
short flag;
char _pad0[2];
/** Minimum/maximum y-extents for curve. */
float ymin, ymax;
- /** ???. */
+ /** Unused since the first available revision. */
unsigned int bitmask;
/** Minimum/maximum values for sliders (in action editor). */
@@ -102,7 +102,7 @@ typedef struct Ipo {
/** A list of IpoCurve structs in a linked list. */
ListBase curve;
- /** Rect defining extents of keyframes?. */
+ /** Rect defining extents of keyframes? */
rctf cur;
/** Blocktype: self-explanatory; showkey: either 0 or 1
diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h
index 82ff3c95834..5ec82a68a2d 100644
--- a/source/blender/makesdna/DNA_light_types.h
+++ b/source/blender/makesdna/DNA_light_types.h
@@ -134,7 +134,7 @@ typedef struct Light {
/* #define LA_NO_DIFF (1 << 11) */ /* not used anywhere */
/* #define LA_NO_SPEC (1 << 12) */ /* not used anywhere */
/* #define LA_SHAD_RAY (1 << 13) */ /* not used anywhere - cleaned */
-/* yafray: light shadowbuffer flag, softlight */
+/* yafray: light shadowbuffer flag, softlight */
/* Since it is used with LOCAL light, can't use LA_SHAD */
/* #define LA_YF_SOFT (1 << 14) */ /* not used anymore */
/* #define LA_LAYER_SHADOW (1 << 15) */ /* not used anymore */
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
index d7e62d04af2..e93cf050e18 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -25,11 +25,10 @@
* ***** END GPL LICENSE BLOCK *****
*/
-#ifndef __DNA_LRT_TYPES_H__
-#define __DNA_LRT_TYPES_H__
+#pragma once
-/** \file DNA_lineart_types.h
- * \ingroup DNA
+/** \file
+ * \ingroup DNA
*/
#include "DNA_ID.h"
@@ -71,5 +70,3 @@ typedef enum eLineartEdgeFlag {
} eLineartEdgeFlag;
#define LRT_EDGE_FLAG_ALL_TYPE 0x3f
-
-#endif
diff --git a/source/blender/makesdna/DNA_linestyle_types.h b/source/blender/makesdna/DNA_linestyle_types.h
index b154de4507e..d83e24c5117 100644
--- a/source/blender/makesdna/DNA_linestyle_types.h
+++ b/source/blender/makesdna/DNA_linestyle_types.h
@@ -382,7 +382,7 @@ typedef struct LineStyleGeometryModifier_PerlinNoise1D {
struct LineStyleModifier modifier;
float frequency, amplitude;
- /** In radians!. */
+ /** In radians. */
float angle;
unsigned int octaves;
int seed;
@@ -393,7 +393,7 @@ typedef struct LineStyleGeometryModifier_PerlinNoise2D {
struct LineStyleModifier modifier;
float frequency, amplitude;
- /** In radians!. */
+ /** In radians. */
float angle;
unsigned int octaves;
int seed;
@@ -463,7 +463,7 @@ typedef struct LineStyleGeometryModifier_2DTransform {
int pivot;
float scale_x, scale_y;
- /** In radians!. */
+ /** In radians. */
float angle;
float pivot_u;
float pivot_x, pivot_y;
@@ -483,7 +483,7 @@ typedef struct LineStyleThicknessModifier_Calligraphy {
struct LineStyleModifier modifier;
float min_thickness, max_thickness;
- /** In radians!. */
+ /** In radians. */
float orientation;
char _pad[4];
} LineStyleThicknessModifier_Calligraphy;
diff --git a/source/blender/makesdna/DNA_mask_types.h b/source/blender/makesdna/DNA_mask_types.h
index 8868ac1ea12..e6a7c004078 100644
--- a/source/blender/makesdna/DNA_mask_types.h
+++ b/source/blender/makesdna/DNA_mask_types.h
@@ -93,7 +93,7 @@ typedef struct MaskSplinePointUW {
} MaskSplinePointUW;
typedef struct MaskSplinePoint {
- /** Actual point coordinates and its handles . */
+ /** Actual point coordinates and its handles. */
BezTriple bezt;
char _pad[4];
/** Number of uv feather values. */
@@ -173,7 +173,7 @@ typedef struct MaskLayer {
/** For animation. */
char flag;
- /** Matching 'Object' flag of the same name - eventually use in the outliner . */
+ /** Matching 'Object' flag of the same name - eventually use in the outliner. */
char restrictflag;
} MaskLayer;
diff --git a/source/blender/makesdna/DNA_material_defaults.h b/source/blender/makesdna/DNA_material_defaults.h
index 3f4496ce735..3fa87800b2e 100644
--- a/source/blender/makesdna/DNA_material_defaults.h
+++ b/source/blender/makesdna/DNA_material_defaults.h
@@ -45,6 +45,8 @@
.alpha_threshold = 0.5f, \
\
.blend_shadow = MA_BS_SOLID, \
+ \
+ .lineart.mat_occlusion = 1, \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index b7354aaa724..67cd68afb8a 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -160,6 +160,8 @@ typedef struct MaterialLineArt {
typedef enum eMaterialLineArtFlags {
LRT_MATERIAL_MASK_ENABLED = (1 << 0),
+
+ /* Deprecated, kept for versioning code. */
LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS = (1 << 1),
} eMaterialLineArtFlags;
@@ -295,37 +297,21 @@ typedef struct Material {
#define MAP_COL (1 << 0)
#define MAP_ALPHA (1 << 7)
-/* pmapto */
-/* init */
-#define MAP_PA_INIT ((1 << 5) - 1)
-#define MAP_PA_TIME (1 << 0)
-#define MAP_PA_LIFE (1 << 1)
-#define MAP_PA_DENS (1 << 2)
-#define MAP_PA_SIZE (1 << 3)
-#define MAP_PA_LENGTH (1 << 4)
-/* reset */
-#define MAP_PA_IVEL (1 << 5)
-/* physics */
-#define MAP_PA_PVEL (1 << 6)
-/* path cache */
-#define MAP_PA_CLUMP (1 << 7)
-#define MAP_PA_KINK (1 << 8)
-#define MAP_PA_ROUGH (1 << 9)
-#define MAP_PA_FREQ (1 << 10)
-
/* pr_type */
-#define MA_FLAT 0
-#define MA_SPHERE 1
-#define MA_CUBE 2
-#define MA_SHADERBALL 3
-#define MA_SPHERE_A 4 /* Used for icon renders only. */
-#define MA_TEXTURE 5
-#define MA_LAMP 6
-#define MA_SKY 7
-#define MA_HAIR 10
-#define MA_ATMOS 11
-#define MA_CLOTH 12
-#define MA_FLUID 13
+typedef enum ePreviewType {
+ MA_FLAT = 0,
+ MA_SPHERE = 1,
+ MA_CUBE = 2,
+ MA_SHADERBALL = 3,
+ MA_SPHERE_A = 4, /* Used for icon renders only. */
+ MA_TEXTURE = 5,
+ MA_LAMP = 6,
+ MA_SKY = 7,
+ MA_HAIR = 10,
+ MA_ATMOS = 11,
+ MA_CLOTH = 12,
+ MA_FLUID = 13,
+} ePreviewType;
/* pr_flag */
#define MA_PREVIEW_WORLD (1 << 0)
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index c54c086affd..932f4715298 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -175,7 +175,7 @@ typedef struct Mesh {
struct Mesh *texcomesh;
/* When the object is available, the preferred access method is: BKE_editmesh_from_object(ob) */
- /** Not saved in file!. */
+ /** Not saved in file. */
struct BMEditMesh *edit_mesh;
struct CustomData vdata, edata, fdata;
@@ -281,9 +281,9 @@ enum {
/* We can't have both flags enabled at once,
* flags defined in DNA_scene_types.h */
#define ME_EDIT_PAINT_SEL_MODE(_me) \
- (((_me)->editflag & ME_EDIT_PAINT_FACE_SEL) ? \
- SCE_SELECT_FACE : \
- ((_me)->editflag & ME_EDIT_PAINT_VERT_SEL) ? SCE_SELECT_VERTEX : 0)
+ (((_me)->editflag & ME_EDIT_PAINT_FACE_SEL) ? SCE_SELECT_FACE : \
+ ((_me)->editflag & ME_EDIT_PAINT_VERT_SEL) ? SCE_SELECT_VERTEX : \
+ 0)
/* me->flag */
enum {
diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h
index f6dac88051b..1b3dbd148df 100644
--- a/source/blender/makesdna/DNA_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_modifier_defaults.h
@@ -647,7 +647,8 @@
.target = NULL, \
.verts = NULL, \
.falloff = 4.0f, \
- .numverts = 0, \
+ .num_mesh_verts = 0, \
+ .num_bind_verts = 0, \
.numpoly = 0, \
.flags = 0, \
.mat = _DNA_DEFAULT_UNIT_M4, \
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 1c765d19ce2..f66de378c35 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -952,7 +952,7 @@ typedef struct MeshDeformModifierData {
MDefCell *dyngrid;
/** Dynamic binding vertex influences. */
MDefInfluence *dyninfluences;
- /** Is this vertex bound or not?. */
+ /** Is this vertex bound or not? */
int *dynverts;
/** Size of the dynamic bind grid. */
int dyngridsize;
@@ -1469,7 +1469,7 @@ typedef struct WeightVGEditModifierData {
float default_weight;
/* Mapping stuff. */
- /** The custom mapping curve!. */
+ /** The custom mapping curve. */
struct CurveMapping *cmap_curve;
/* The add/remove vertices weight thresholds. */
@@ -1543,7 +1543,7 @@ typedef struct WeightVGMixModifierData {
struct Object *mask_tex_map_obj;
/** Name of the map bone. */
char mask_tex_map_bone[64];
- /** How to map the texture!. */
+ /** How to map the texture. */
int mask_tex_mapping;
/** Name of the UV map. MAX_CUSTOMDATA_LAYER_NAME. */
char mask_tex_uvlayer_name[64];
@@ -1601,7 +1601,7 @@ typedef struct WeightVGProximityModifierData {
char defgrp_name[64];
/* Mapping stuff. */
- /** The custom mapping curve!. */
+ /** The custom mapping curve. */
struct CurveMapping *cmap_curve;
/* Proximity modes. */
@@ -1626,7 +1626,7 @@ typedef struct WeightVGProximityModifierData {
struct Object *mask_tex_map_obj;
/** Name of the map bone. */
char mask_tex_map_bone[64];
- /** How to map the texture!. */
+ /** How to map the texture. */
int mask_tex_mapping;
/** Name of the UV Map. MAX_CUSTOMDATA_LAYER_NAME. */
char mask_tex_uvlayer_name[64];
@@ -2023,6 +2023,7 @@ typedef struct WeldModifierData {
/* WeldModifierData->flag */
enum {
MOD_WELD_INVERT_VGROUP = (1 << 0),
+ MOD_WELD_LOOSE_EDGES = (1 << 1),
};
/* #WeldModifierData.mode */
@@ -2180,7 +2181,7 @@ typedef struct SDefBind {
typedef struct SDefVert {
SDefBind *binds;
unsigned int numbinds;
- char _pad[4];
+ unsigned int vertex_idx;
} SDefVert;
typedef struct SurfaceDeformModifierData {
@@ -2192,11 +2193,10 @@ typedef struct SurfaceDeformModifierData {
/** Vertex bind data. */
SDefVert *verts;
float falloff;
- unsigned int numverts, numpoly;
+ unsigned int num_mesh_verts, num_bind_verts, numpoly;
int flags;
float mat[4][4];
float strength;
- char _pad[4];
char defgrp_name[64];
} SurfaceDeformModifierData;
@@ -2204,10 +2204,9 @@ typedef struct SurfaceDeformModifierData {
enum {
/* This indicates "do bind on next modifier evaluation" as well as "is bound". */
MOD_SDEF_BIND = (1 << 0),
- MOD_SDEF_INVERT_VGROUP = (1 << 1)
-
- /* MOD_SDEF_USES_LOOPTRI = (1 << 1), */ /* UNUSED */
- /* MOD_SDEF_HAS_CONCAVE = (1 << 2), */ /* UNUSED */
+ MOD_SDEF_INVERT_VGROUP = (1 << 1),
+ /* Only store bind data for nonzero vgroup weights at the time of bind. */
+ MOD_SDEF_SPARSE_BIND = (1 << 2),
};
/* Surface Deform vertex bind modes */
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 5e4692481ba..5152098f57a 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -37,6 +37,8 @@ struct Collection;
struct ID;
struct Image;
struct ListBase;
+struct Material;
+struct Tex;
struct bGPdata;
struct bNodeInstanceHash;
struct bNodeLink;
@@ -44,8 +46,6 @@ struct bNodePreview;
struct bNodeTreeExec;
struct bNodeType;
struct uiBlock;
-struct Tex;
-struct Material;
#define NODE_MAXSTR 64
@@ -1355,6 +1355,13 @@ typedef struct NodeSwitch {
uint8_t input_type;
} NodeSwitch;
+typedef struct NodeGeometryCurveSetHandles {
+ /* GeometryNodeCurveHandleType. */
+ uint8_t handle_type;
+ /* GeometryNodeCurveHandleMode. */
+ uint8_t mode;
+} NodeGeometryCurveSetHandles;
+
typedef struct NodeGeometryCurvePrimitiveLine {
/* GeometryNodeCurvePrimitiveLineMode. */
uint8_t mode;
@@ -1385,6 +1392,11 @@ typedef struct NodeGeometryCurveSubdivide {
uint8_t cuts_type;
} NodeGeometryCurveSubdivide;
+typedef struct NodeGeometryCurveTrim {
+ /* GeometryNodeCurveInterpolateMode. */
+ uint8_t mode;
+} NodeGeometryCurveTrim;
+
typedef struct NodeGeometryCurveToPoints {
/* GeometryNodeCurveSampleMode. */
uint8_t mode;
@@ -1821,6 +1833,18 @@ typedef enum GeometryNodeCurvePrimitiveCircleMode {
GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS = 1
} GeometryNodeCurvePrimitiveCircleMode;
+typedef enum GeometryNodeCurveHandleType {
+ GEO_NODE_CURVE_HANDLE_FREE = 0,
+ GEO_NODE_CURVE_HANDLE_AUTO = 1,
+ GEO_NODE_CURVE_HANDLE_VECTOR = 2,
+ GEO_NODE_CURVE_HANDLE_ALIGN = 3
+} GeometryNodeCurveHandleType;
+
+typedef enum GeometryNodeCurveHandleMode {
+ GEO_NODE_CURVE_HANDLE_LEFT = (1 << 0),
+ GEO_NODE_CURVE_HANDLE_RIGHT = (1 << 1)
+} GeometryNodeCurveHandleMode;
+
typedef enum GeometryNodeTriangulateNGons {
GEO_NODE_TRIANGULATE_NGON_BEAUTY = 0,
GEO_NODE_TRIANGULATE_NGON_EARCLIP = 1,
@@ -1944,6 +1968,11 @@ typedef enum GeometryNodeCurveSampleMode {
GEO_NODE_CURVE_SAMPLE_EVALUATED = 2,
} GeometryNodeCurveSampleMode;
+typedef enum GeometryNodeCurveInterpolateMode {
+ GEO_NODE_CURVE_INTERPOLATE_FACTOR = 0,
+ GEO_NODE_CURVE_INTERPOLATE_LENGTH = 1,
+} GeometryNodeCurveInterpolateMode;
+
typedef enum GeometryNodeAttributeTransferMapMode {
GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0,
GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1,
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index dd31e85647d..60a34fef899 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -336,7 +336,7 @@ typedef struct Object {
/** Deprecated, use 'matbits'. */
short colbits DNA_DEPRECATED;
- /** Transformation settings and transform locks . */
+ /** Transformation settings and transform locks. */
short transflag, protectflag;
short trackflag, upflag;
/** Used for DopeSheet filtering settings (expanded/collapsed). */
diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h
index 1d5e5eeed31..aa11e74e89d 100644
--- a/source/blender/makesdna/DNA_rigidbody_types.h
+++ b/source/blender/makesdna/DNA_rigidbody_types.h
@@ -125,7 +125,7 @@ typedef struct RigidBodyOb_Shared {
*/
typedef struct RigidBodyOb {
/* General Settings for this RigidBodyOb */
- /** (eRigidBodyOb_Type) role of RigidBody in sim . */
+ /** (eRigidBodyOb_Type) role of RigidBody in sim. */
short type;
/** (eRigidBody_Shape) collision shape to use. */
short shape;
@@ -243,7 +243,7 @@ typedef struct RigidBodyCon {
struct Object *ob2;
/* General Settings for this RigidBodyCon */
- /** (eRigidBodyCon_Type) role of RigidBody in sim . */
+ /** (eRigidBodyCon_Type) role of RigidBody in sim. */
short type;
/** Number of constraint solver iterations made per simulation step. */
short num_solver_iterations;
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 1a2a8892e64..61707964191 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -374,8 +374,6 @@
/* GP Stroke Placement */ \
.gpencil_v3d_align = GP_PROJECT_VIEWSPACE, \
.gpencil_v2d_align = GP_PROJECT_VIEWSPACE, \
- .gpencil_seq_align = GP_PROJECT_VIEWSPACE, \
- .gpencil_ima_align = GP_PROJECT_VIEWSPACE, \
}
/* clang-format off */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index cd752b220a3..3cd5a068e86 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -233,22 +233,24 @@ typedef struct SceneRenderLayer {
/** #SceneRenderLayer.layflag */
#define SCE_LAY_SOLID (1 << 0)
-#define SCE_LAY_ZTRA (1 << 1)
-#define SCE_LAY_HALO (1 << 2)
-#define SCE_LAY_EDGE (1 << 3)
+#define SCE_LAY_UNUSED_1 (1 << 1)
+#define SCE_LAY_UNUSED_2 (1 << 2)
+#define SCE_LAY_UNUSED_3 (1 << 3)
#define SCE_LAY_SKY (1 << 4)
#define SCE_LAY_STRAND (1 << 5)
#define SCE_LAY_FRS (1 << 6)
#define SCE_LAY_AO (1 << 7)
#define SCE_LAY_VOLUMES (1 << 8)
#define SCE_LAY_MOTION_BLUR (1 << 9)
-/* flags between (1 << 9) and (1 << 15) are set to 1 already, for future options */
-#define SCE_LAY_ALL_Z (1 << 15)
-/* #define SCE_LAY_XOR (1 << 16) */ /* UNUSED */
+/* Flags between (1 << 9) and (1 << 15) are set to 1 already, for future options. */
+#define SCE_LAY_FLAG_DEFAULT ((1 << 15) - 1)
+
+#define SCE_LAY_UNUSED_4 (1 << 15)
+#define SCE_LAY_UNUSED_5 (1 << 16)
#define SCE_LAY_DISABLE (1 << 17)
-#define SCE_LAY_ZMASK (1 << 18)
-#define SCE_LAY_NEG_ZMASK (1 << 19)
+#define SCE_LAY_UNUSED_6 (1 << 18)
+#define SCE_LAY_UNUSED_7 (1 << 19)
/** #SceneRenderLayer.passflag */
typedef enum eScenePassType {
@@ -267,7 +269,7 @@ typedef enum eScenePassType {
SCE_PASS_UV = (1 << 12),
SCE_PASS_UNUSED_6 = (1 << 13), /* INDIRECT */
SCE_PASS_MIST = (1 << 14),
- SCE_PASS_RAYHITS = (1 << 15),
+ SCE_PASS_UNUSED_7 = (1 << 15), /* RAYHITS */
SCE_PASS_EMIT = (1 << 16),
SCE_PASS_ENVIRONMENT = (1 << 17),
SCE_PASS_INDEXMA = (1 << 18),
@@ -302,7 +304,6 @@ typedef enum eScenePassType {
#define RE_PASSNAME_INDEXMA "IndexMA"
#define RE_PASSNAME_MIST "Mist"
-#define RE_PASSNAME_RAYHITS "RayHits"
#define RE_PASSNAME_DIFFUSE_DIRECT "DiffDir"
#define RE_PASSNAME_DIFFUSE_INDIRECT "DiffInd"
#define RE_PASSNAME_DIFFUSE_COLOR "DiffCol"
@@ -734,7 +735,7 @@ typedef struct RenderData {
/* sequencer options */
char seq_prev_type;
- /** UNUSED!. */
+ /** UNUSED. */
char seq_rend_type;
/** Flag use for sequence render/draw. */
char seq_flag;
@@ -1409,10 +1410,7 @@ typedef struct ToolSettings {
char gpencil_v3d_align;
/** General 2D Editor. */
char gpencil_v2d_align;
- /** Sequencer Preview. */
- char gpencil_seq_align;
- /** Image Editor. */
- char gpencil_ima_align;
+ char _pad0[2];
/* Annotations */
/** Stroke placement settings - 3D View. */
@@ -1538,7 +1536,7 @@ typedef struct ToolSettings {
typedef struct UnitSettings {
/* Display/Editing unit options for each scene */
- /** Maybe have other unit conversions?. */
+ /** Maybe have other unit conversions? */
float scale_length;
/** Imperial, metric etc. */
char system;
@@ -1750,7 +1748,7 @@ typedef struct Scene {
/** (runtime) info/cache used for presenting playback framerate info to the user. */
void *fps_info;
- /* none of the dependency graph vars is mean to be saved */
+ /* None of the dependency graph vars is mean to be saved. */
struct GHash *depsgraph_hash;
char _pad7[4];
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index 670e84e0c7a..d5b7458ae7b 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -43,6 +43,7 @@ struct SpaceLink;
struct SpaceType;
struct uiBlock;
struct uiLayout;
+struct uiList;
struct wmDrawBuffer;
struct wmTimer;
struct wmTooltipState;
@@ -246,11 +247,16 @@ typedef struct PanelCategoryStack {
char idname[64];
} PanelCategoryStack;
+typedef void (*uiListFreeRuntimeDataFunc)(struct uiList *ui_list);
+
/* uiList dynamic data... */
/* These two Lines with # tell makesdna this struct can be excluded. */
#
#
typedef struct uiListDyn {
+ /** Callback to free UI data when freeing UI-Lists in BKE. */
+ uiListFreeRuntimeDataFunc free_runtime_data_fn;
+
/** Number of rows needed to draw all elements. */
int height;
/** Actual visual height of the list (in rows). */
@@ -258,6 +264,9 @@ typedef struct uiListDyn {
/** Minimal visual height of the list (in rows). */
int visual_height_min;
+ /** Number of columns drawn for grid layouts. */
+ int columns;
+
/** Number of items in collection. */
int items_len;
/** Number of items actually visible after filtering. */
@@ -270,11 +279,19 @@ typedef struct uiListDyn {
int resize;
int resize_prev;
+ /** Allocated custom data. Freed together with the #uiList (and when re-assigning). */
+ void *customdata;
+
/* Filtering data. */
/** Items_len length. */
int *items_filter_flags;
/** Org_idx -> new_idx, items_len length. */
int *items_filter_neworder;
+
+ struct wmOperatorType *custom_drag_optype;
+ struct PointerRNA *custom_drag_opptr;
+ struct wmOperatorType *custom_activate_optype;
+ struct PointerRNA *custom_activate_opptr;
} uiListDyn;
typedef struct uiList { /* some list UI data need to be saved in file */
@@ -301,6 +318,12 @@ typedef struct uiList { /* some list UI data need to be saved in file */
int filter_flag;
int filter_sort_flag;
+ /** Operator executed when activating an item. */
+ const char *custom_activate_opname;
+ /** Operator executed when dragging an item (item gets activated too, without running
+ * custom_activate_opname above). */
+ const char *custom_drag_opname;
+
/* Custom sub-classes properties. */
IDProperty *properties;
@@ -499,7 +522,7 @@ typedef struct ARegion {
/** Use this string to draw info. */
char *headerstr;
- /** XXX 2.50, need spacedata equivalent?. */
+ /** XXX 2.50, need spacedata equivalent? */
void *regiondata;
ARegion_Runtime runtime;
@@ -584,6 +607,7 @@ enum {
UILST_LAYOUT_DEFAULT = 0,
UILST_LAYOUT_COMPACT = 1,
UILST_LAYOUT_GRID = 2,
+ UILST_LAYOUT_BIG_PREVIEW_GRID = 3,
};
/** #uiList.flag */
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 1bd4c9233e3..af524ff4866 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -43,9 +43,9 @@ extern "C" {
struct Ipo;
struct MovieClip;
struct Scene;
+struct SequenceLookup;
struct VFont;
struct bSound;
-struct SequenceLookup;
/* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */
@@ -201,6 +201,7 @@ typedef struct Sequence {
ListBase anims;
float effect_fader;
+ /* DEPRECATED, only used for versioning. */
float speed_fader;
/* pointers for effects: */
@@ -335,12 +336,28 @@ typedef struct SolidColorVars {
typedef struct SpeedControlVars {
float *frameMap;
+ /* DEPRECATED, only used for versioning. */
float globalSpeed;
+ /* DEPRECATED, only used for versioning. */
int flags;
+
int length;
int lastValidFrame;
+ int speed_control_type;
+
+ float speed_fader;
+ float speed_fader_length;
+ float speed_fader_frame_number;
} SpeedControlVars;
+/* SpeedControlVars.speed_control_type */
+enum {
+ SEQ_SPEED_STRETCH = 0,
+ SEQ_SPEED_MULTIPLY = 1,
+ SEQ_SPEED_LENGTH = 2,
+ SEQ_SPEED_FRAME_NUMBER = 3,
+};
+
typedef struct GaussianBlurVars {
float size_x;
float size_y;
@@ -485,9 +502,9 @@ typedef struct SequencerScopes {
#define SEQ_EDIT_PROXY_DIR_STORAGE 1
/* SpeedControlVars->flags */
-#define SEQ_SPEED_INTEGRATE (1 << 0)
+#define SEQ_SPEED_UNUSED_2 (1 << 0) /* cleared */
#define SEQ_SPEED_UNUSED_1 (1 << 1) /* cleared */
-#define SEQ_SPEED_COMPRESS_IPO_Y (1 << 2)
+#define SEQ_SPEED_UNUSED_3 (1 << 2) /* cleared */
#define SEQ_SPEED_USE_INTERPOLATION (1 << 3)
/* ***************** SEQUENCE ****************** */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 73a44ec16bb..7290647dbc6 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -24,6 +24,7 @@
#pragma once
+#include "DNA_asset_types.h"
#include "DNA_color_types.h" /* for Histogram */
#include "DNA_defs.h"
#include "DNA_image_types.h" /* ImageUser */
@@ -653,6 +654,7 @@ typedef enum eSpaceSeq_Flag {
SEQ_SHOW_STRIP_SOURCE = (1 << 15),
SEQ_SHOW_STRIP_DURATION = (1 << 16),
SEQ_USE_PROXIES = (1 << 17),
+ SEQ_SHOW_GRID = (1 << 18),
} eSpaceSeq_Flag;
/* SpaceSeq.view */
@@ -696,24 +698,6 @@ typedef enum eSpaceSeq_OverlayType {
/** \name File Selector
* \{ */
-/**
- * Information to identify a asset library. May be either one of the predefined types (current
- * 'Main', builtin library, project library), or a custom type as defined in the Preferences.
- *
- * If the type is set to #FILE_ASSET_LIBRARY_CUSTOM, idname must have the name to identify the
- * custom library. Otherwise idname is not used.
- */
-typedef struct FileSelectAssetLibraryUID {
- short type; /* eFileAssetLibrary_Type */
- char _pad[2];
- /**
- * If showing a custom asset library (#FILE_ASSET_LIBRARY_CUSTOM), this is the index of the
- * #bUserAssetLibrary within #UserDef.asset_libraries.
- * Should be ignored otherwise (but better set to -1 then, for sanity and debugging).
- */
- int custom_library_index;
-} FileSelectAssetLibraryUID;
-
/* Config and Input for File Selector */
typedef struct FileSelectParams {
/** Title, also used for the text of the execute button. */
@@ -785,7 +769,7 @@ typedef struct FileSelectParams {
typedef struct FileAssetSelectParams {
FileSelectParams base_params;
- FileSelectAssetLibraryUID asset_library;
+ AssetLibraryReference asset_library;
short import_type; /* eFileAssetImportType */
char _pad[6];
@@ -885,22 +869,6 @@ typedef enum eFileBrowse_Mode {
FILE_BROWSE_MODE_ASSETS = 1,
} eFileBrowse_Mode;
-typedef enum eFileAssetLibrary_Type {
- /* For the future. Display assets bundled with Blender by default. */
- // FILE_ASSET_LIBRARY_BUNDLED = 0,
- /** Display assets from the current session (current "Main"). */
- FILE_ASSET_LIBRARY_LOCAL = 1,
- /* For the future. Display assets for the current project. */
- // FILE_ASSET_LIBRARY_PROJECT = 2,
-
- /** Display assets from custom asset libraries, as defined in the preferences
- * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname
- * then.
- * In RNA, we add the index of the custom library to this to identify it by index. So keep
- * this last! */
- FILE_ASSET_LIBRARY_CUSTOM = 100,
-} eFileAssetLibrary_Type;
-
/* FileSelectParams.display */
enum eFileDisplayType {
/** Internal (not exposed to users): Keep whatever display type was used during the last File
@@ -1057,7 +1025,6 @@ typedef struct FileDirEntry {
/* Name needs freeing if FILE_ENTRY_NAME_FREE is set. Otherwise this is a direct pointer to a
* name buffer. */
char *name;
- char *description;
uint64_t size;
int64_t time;
diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h
index 2308f04c4c7..ee33e8666ec 100644
--- a/source/blender/makesdna/DNA_texture_types.h
+++ b/source/blender/makesdna/DNA_texture_types.h
@@ -54,11 +54,10 @@ typedef struct MTex {
float ofs[3], size[3], rot, random_angle;
char _pad0[2];
- short colormodel, pmapto, pmaptoneg;
+ short colormodel;
short normapspace, which_output;
float r, g, b, k;
float def_var;
- char _pad1[4];
/* common */
float colfac, varfac;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 629aa191890..5f8a8c6230a 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -513,7 +513,7 @@ typedef struct bTheme {
typedef struct bAddon {
struct bAddon *next, *prev;
char module[64];
- /** User-Defined Properties on this Addon (for storing preferences). */
+ /** User-Defined Properties on this add-on (for storing preferences). */
IDProperty *prop;
} bAddon;
@@ -800,7 +800,7 @@ typedef struct UserDef {
short rvisize;
/** Rotating view icon brightness. */
short rvibright;
- /** Maximum number of recently used files to remember . */
+ /** Maximum number of recently used files to remember. */
short recent_files;
/** Milliseconds to spend spinning the view. */
short smooth_viewtx;
@@ -1291,7 +1291,7 @@ typedef enum eTimecodeStyles {
USER_TIMECODE_SECONDS_ONLY = 4,
/**
* Private (not exposed as generic choices) options.
- * milliseconds for sub-frames , SubRip format- HH:MM:SS,sss.
+ * milliseconds for sub-frames, SubRip format- HH:MM:SS,sss.
*/
USER_TIMECODE_SUBRIP = 100,
} eTimecodeStyles;
diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h
index 131772d49b4..c385ac04bd3 100644
--- a/source/blender/makesdna/DNA_view2d_types.h
+++ b/source/blender/makesdna/DNA_view2d_types.h
@@ -50,7 +50,7 @@ typedef struct View2D {
/** Scroll_ui - temp settings used for UI drawing of scrollers. */
short scroll_ui;
- /** Keeptot - 'cur' rect cannot move outside the 'tot' rect?. */
+ /** Keeptot - 'cur' rect cannot move outside the 'tot' rect? */
short keeptot;
/** Keepzoom - axes that zooming cannot occur on, and also clamp within zoom-limits. */
short keepzoom;
diff --git a/source/blender/makesdna/DNA_view3d_enums.h b/source/blender/makesdna/DNA_view3d_enums.h
index 3de430e1177..aec52da1bf9 100644
--- a/source/blender/makesdna/DNA_view3d_enums.h
+++ b/source/blender/makesdna/DNA_view3d_enums.h
@@ -24,7 +24,7 @@
extern "C" {
#endif
-/** Settings for offscreen rendering */
+/** Settings for off-screen rendering. */
typedef enum eV3DOffscreenDrawFlag {
V3D_OFSDRAW_NONE = (0),
V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0),
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 94e89944f08..a9016dd4edd 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -94,7 +94,7 @@ typedef struct Report {
/** ReportType. */
short type;
short flag;
- /** `strlen(message)`, saves some time calculating the word wrap . */
+ /** `strlen(message)`, saves some time calculating the word wrap. */
int len;
const char *typestr;
const char *message;
diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h
index 0fce331fadf..2bac040ea90 100644
--- a/source/blender/makesdna/DNA_workspace_types.h
+++ b/source/blender/makesdna/DNA_workspace_types.h
@@ -23,6 +23,7 @@
#pragma once
#include "DNA_ID.h"
+#include "DNA_asset_types.h"
#ifdef __cplusplus
extern "C" {
@@ -135,6 +136,10 @@ typedef struct WorkSpace {
/** Info text from modal operators (runtime). */
char *status_text;
+
+ /** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The
+ * Asset Browser has its own and doesn't use this. */
+ AssetLibraryReference asset_library;
} WorkSpace;
/**
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
index 8e63760fef7..fc00d5eb839 100644
--- a/source/blender/makesdna/DNA_xr_types.h
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -50,6 +50,7 @@ typedef struct XrSessionSettings {
typedef enum eXrSessionFlag {
XR_SESSION_USE_POSITION_TRACKING = (1 << 0),
+ XR_SESSION_USE_ABSOLUTE_TRACKING = (1 << 1),
} eXrSessionFlag;
typedef enum eXRSessionBasePoseType {
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 03f7dbf6489..a573e2f9e8c 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -152,6 +152,7 @@
/* DNA_asset_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(AssetMetaData);
+SDNA_DEFAULT_DECL_STRUCT(AssetLibraryReference);
/* DNA_armature_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(bArmature);
@@ -348,6 +349,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
/* DNA_asset_defaults.h */
SDNA_DEFAULT_DECL(AssetMetaData),
+ SDNA_DEFAULT_DECL(AssetLibraryReference),
/* DNA_armature_defaults.h */
SDNA_DEFAULT_DECL(bArmature),
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index d23b9441822..24d0d39292e 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -84,8 +84,8 @@
* **Remember to read/write integer and short aligned!**
*
* While writing a file, the names of a struct is indicated with a type number,
- * to be found with: ``type = DNA_struct_find_nr(SDNA *, const char *)``
- * The value of ``type`` corresponds with the index within the structs array
+ * to be found with: `type = DNA_struct_find_nr(SDNA *, const char *)`
+ * The value of `type` corresponds with the index within the structs array
*
* For the moment: the complete DNA file is included in a .blend file. For
* the future we can think of smarter methods, like only included the used
@@ -101,7 +101,7 @@
* - Change of a pointer type: when the name doesn't change the contents is copied.
*
* NOT YET:
- * - array (``vec[3]``) to float struct (``vec3f``).
+ * - array (`vec[3]`) to float struct (`vec3f`).
*
* DONE:
* - Endian compatibility.
diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h
index 735be0c10bf..d363e40e4f0 100644
--- a/source/blender/makesdna/intern/dna_rename_defs.h
+++ b/source/blender/makesdna/intern/dna_rename_defs.h
@@ -136,4 +136,5 @@ DNA_STRUCT_RENAME_ELEM(wmWindow, global_area_map, global_areas)
DNA_STRUCT_RENAME_ELEM(LineartGpencilModifierData, line_types, edge_types)
DNA_STRUCT_RENAME_ELEM(LineartGpencilModifierData, transparency_flags, mask_switches)
DNA_STRUCT_RENAME_ELEM(LineartGpencilModifierData, transparency_mask, material_mask_bits)
+DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numverts, num_bind_verts)
DNA_STRUCT_RENAME_ELEM(MaterialLineArt, transparency_mask, material_mask_bits)
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 299f96a4c46..97615016016 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -71,6 +71,8 @@ extern StructRNA RNA_ArrayGpencilModifier;
extern StructRNA RNA_ArrayModifier;
extern StructRNA RNA_Attribute;
extern StructRNA RNA_AttributeGroup;
+extern StructRNA RNA_AssetHandle;
+extern StructRNA RNA_AssetLibraryReference;
extern StructRNA RNA_AssetMetaData;
extern StructRNA RNA_AssetTag;
extern StructRNA RNA_BackgroundImage;
diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h
index a31182b2f5a..01e26cbb41c 100644
--- a/source/blender/makesrna/RNA_define.h
+++ b/source/blender/makesrna/RNA_define.h
@@ -506,6 +506,11 @@ void RNA_def_property_duplicate_pointers(StructOrFunctionRNA *cont_, PropertyRNA
void RNA_def_property_free_pointers(PropertyRNA *prop);
int RNA_def_property_free_identifier(StructOrFunctionRNA *cont_, const char *identifier);
+int RNA_def_property_free_identifier_deferred_prepare(StructOrFunctionRNA *cont_,
+ const char *identifier,
+ void **handle);
+void RNA_def_property_free_identifier_deferred_finish(StructOrFunctionRNA *cont_, void *handle);
+
void RNA_def_property_free_pointers_set_py_data_callback(
void (*py_data_clear_fn)(PropertyRNA *prop));
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index c8010a0e1ae..d544083a749 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -245,6 +245,22 @@ extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bo
extern const EnumPropertyItem rna_enum_collection_color_items[];
+/**
+ * For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64
+ * bit integers. So can't use the regular #EnumPropertyItem. Would be nice if RNA supported this
+ * itself.
+ *
+ * Meant to be used with #RNA_def_property_boolean_sdna() which supports 64 bit flags as well.
+ */
+struct IDFilterEnumPropertyItem {
+ const uint64_t flag;
+ const char *identifier;
+ const int icon;
+ const char *name;
+ const char *description;
+};
+extern const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[];
+
/* API calls */
int rna_node_tree_type_to_enum(struct bNodeTreeType *typeinfo);
int rna_node_tree_idname_to_enum(const char *idname);
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index c2335b82fca..d880c892aa9 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -226,7 +226,7 @@ typedef enum PropertyFlag {
PROP_ICONS_CONSECUTIVE = (1 << 12),
PROP_ICONS_REVERSE = (1 << 8),
- /** Hidden in the user interface. */
+ /** Hidden in the user interface. */
PROP_HIDDEN = (1 << 19),
/** Do not write in presets. */
PROP_SKIP_SAVE = (1 << 28),
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 38b00afdc8f..6df03d19538 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -126,6 +126,97 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items
{0, NULL, 0, NULL, NULL},
};
+/**
+ * \note Uses #IDFilterEnumPropertyItem, not EnumPropertyItem, to support 64 bit items.
+ */
+const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = {
+ /* Datablocks */
+ {FILTER_ID_AC, "filter_action", ICON_ANIM_DATA, "Actions", "Show Action data-blocks"},
+ {FILTER_ID_AR,
+ "filter_armature",
+ ICON_ARMATURE_DATA,
+ "Armatures",
+ "Show Armature data-blocks"},
+ {FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"},
+ {FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"},
+ {FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"},
+ {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"},
+ {FILTER_ID_GD,
+ "filter_grease_pencil",
+ ICON_GREASEPENCIL,
+ "Grease Pencil",
+ "Show Grease pencil data-blocks"},
+ {FILTER_ID_GR,
+ "filter_group",
+ ICON_OUTLINER_COLLECTION,
+ "Collections",
+ "Show Collection data-blocks"},
+ {FILTER_ID_HA, "filter_hair", ICON_HAIR_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,
+ "filter_light_probe",
+ ICON_OUTLINER_DATA_LIGHTPROBE,
+ "Light Probes",
+ "Show Light Probe data-blocks"},
+ {FILTER_ID_LS,
+ "filter_linestyle",
+ ICON_LINE_DATA,
+ "Freestyle Linestyles",
+ "Show Freestyle's Line Style data-blocks"},
+ {FILTER_ID_LT, "filter_lattice", ICON_LATTICE_DATA, "Lattices", "Show Lattice data-blocks"},
+ {FILTER_ID_MA,
+ "filter_material",
+ ICON_MATERIAL_DATA,
+ "Materials",
+ "Show Material data-blocks"},
+ {FILTER_ID_MB, "filter_metaball", ICON_META_DATA, "Metaballs", "Show Metaball data-blocks"},
+ {FILTER_ID_MC,
+ "filter_movie_clip",
+ ICON_TRACKER_DATA,
+ "Movie Clips",
+ "Show Movie Clip data-blocks"},
+ {FILTER_ID_ME, "filter_mesh", ICON_MESH_DATA, "Meshes", "Show Mesh data-blocks"},
+ {FILTER_ID_MSK, "filter_mask", ICON_MOD_MASK, "Masks", "Show Mask data-blocks"},
+ {FILTER_ID_NT, "filter_node_tree", ICON_NODETREE, "Node Trees", "Show Node Tree data-blocks"},
+ {FILTER_ID_OB, "filter_object", ICON_OBJECT_DATA, "Objects", "Show Object data-blocks"},
+ {FILTER_ID_PA,
+ "filter_particle_settings",
+ ICON_PARTICLE_DATA,
+ "Particles Settings",
+ "Show Particle Settings data-blocks"},
+ {FILTER_ID_PAL, "filter_palette", ICON_COLOR, "Palettes", "Show Palette data-blocks"},
+ {FILTER_ID_PC,
+ "filter_paint_curve",
+ ICON_CURVE_BEZCURVE,
+ "Paint Curves",
+ "Show Paint Curve data-blocks"},
+ {FILTER_ID_PT,
+ "filter_pointcloud",
+ ICON_POINTCLOUD_DATA,
+ "Point Clouds",
+ "Show/hide Point Cloud data-blocks"},
+ {FILTER_ID_SCE, "filter_scene", ICON_SCENE_DATA, "Scenes", "Show Scene data-blocks"},
+ {FILTER_ID_SIM,
+ "filter_simulation",
+ ICON_PHYSICS,
+ "Simulations",
+ "Show Simulation data-blocks"}, /* TODO: Use correct icon. */
+ {FILTER_ID_SPK, "filter_speaker", ICON_SPEAKER, "Speakers", "Show Speaker data-blocks"},
+ {FILTER_ID_SO, "filter_sound", ICON_SOUND, "Sounds", "Show Sound data-blocks"},
+ {FILTER_ID_TE, "filter_texture", ICON_TEXTURE_DATA, "Textures", "Show Texture data-blocks"},
+ {FILTER_ID_TXT, "filter_text", ICON_TEXT, "Texts", "Show Text data-blocks"},
+ {FILTER_ID_VF, "filter_font", ICON_FONT_DATA, "Fonts", "Show Font data-blocks"},
+ {FILTER_ID_VO, "filter_volume", ICON_VOLUME_DATA, "Volumes", "Show/hide Volume data-blocks"},
+ {FILTER_ID_WO, "filter_world", ICON_WORLD_DATA, "Worlds", "Show World data-blocks"},
+ {FILTER_ID_WS,
+ "filter_work_space",
+ ICON_WORKSPACE,
+ "Workspaces",
+ "Show workspace data-blocks"},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
# include "DNA_anim_types.h"
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 0285ef44e17..feacd47c98c 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -5031,7 +5031,7 @@ static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int
}
}
else {
- /* get data until . or [ */
+ /* Get data until `.` or `[`. */
p = *path;
while (*p && *p != '.' && *p != '[') {
@@ -5998,7 +5998,7 @@ static void rna_path_array_multi_string_from_flat_index(PointerRNA *ptr,
/**
* \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc.
- * \param index: The *flattened* index to use when \a ``index_dim > 0``,
+ * \param index: The *flattened* index to use when \a `index_dim > 0`,
* this is expanded when used with multi-dimensional arrays.
*/
char *RNA_path_from_ID_to_property_index(PointerRNA *ptr,
diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c
index 55f680c736d..80b2594d0c9 100644
--- a/source/blender/makesrna/intern/rna_asset.c
+++ b/source/blender/makesrna/intern/rna_asset.c
@@ -25,6 +25,7 @@
#include "DNA_asset_types.h"
#include "DNA_defs.h"
+#include "DNA_space_types.h"
#include "rna_internal.h"
@@ -35,6 +36,8 @@
# include "BLI_listbase.h"
+# include "ED_asset.h"
+
# include "RNA_access.h"
static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data,
@@ -123,6 +126,54 @@ static void rna_AssetMetaData_active_tag_range(
*max = *softmax = MAX2(asset_data->tot_tags - 1, 0);
}
+static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr)
+{
+ AssetHandle *asset_handle = ptr->data;
+ /* Have to cast away const, but the file entry API doesn't allow modifications anyway. */
+ return rna_pointer_inherit_refine(
+ ptr, &RNA_FileSelectEntry, (FileDirEntry *)asset_handle->file_data);
+}
+
+static void rna_AssetHandle_file_data_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *UNUSED(reports))
+{
+ AssetHandle *asset_handle = ptr->data;
+ asset_handle->file_data = value.data;
+}
+
+static void rna_AssetHandle_get_full_library_path(
+ // AssetHandle *asset,
+ bContext *C,
+ FileDirEntry *asset_file,
+ AssetLibraryReference *library,
+ char r_result[/*FILE_MAX_LIBEXTRA*/])
+{
+ AssetHandle asset = {.file_data = asset_file};
+ ED_asset_handle_get_full_library_path(C, library, &asset, r_result);
+}
+
+static PointerRNA rna_AssetHandle_local_id_get(PointerRNA *ptr)
+{
+ const AssetHandle *asset = ptr->data;
+ ID *id = ED_asset_handle_get_local_id(asset);
+ return rna_pointer_inherit_refine(ptr, &RNA_ID, id);
+}
+
+const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf();
+ if (!items) {
+ *r_free = false;
+ }
+
+ *r_free = true;
+ return items;
+}
+
#else
static void rna_def_asset_tag(BlenderRNA *brna)
@@ -182,7 +233,7 @@ static void rna_def_asset_data(BlenderRNA *brna)
srna = RNA_def_struct(brna, "AssetMetaData", NULL);
RNA_def_struct_ui_text(srna, "Asset Data", "Additional data stored for an asset data-block");
- // RNA_def_struct_ui_icon(srna, ICON_ASSET); /* TODO: Icon doesn't exist!. */
+ // RNA_def_struct_ui_icon(srna, ICON_ASSET); /* TODO: Icon doesn't exist! */
/* The struct has custom properties, but no pointer properties to other IDs! */
RNA_def_struct_idprops_func(srna, "rna_AssetMetaData_idprops");
RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */
@@ -209,12 +260,90 @@ static void rna_def_asset_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing");
}
+static void rna_def_asset_handle_api(StructRNA *srna)
+{
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ func = RNA_def_function(srna, "get_full_library_path", "rna_AssetHandle_get_full_library_path");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ /* TODO temporarily static function, for until .py can receive the asset handle from context
+ * properly. `asset_file_handle` should go away too then. */
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "asset_file_handle", "FileSelectEntry", "", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "asset_library",
+ "AssetLibraryReference",
+ "",
+ "The asset library containing the given asset, only valid if the asset "
+ "library is external (i.e. not the \"Current File\" one");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_string(func, "result", NULL, FILE_MAX_LIBEXTRA, "result", "");
+ RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_function_output(func, parm);
+}
+
+static void rna_def_asset_handle(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "AssetHandle", "PropertyGroup");
+ RNA_def_struct_ui_text(srna, "Asset Handle", "Reference to some asset");
+
+ /* TODO It is super ugly to expose the file data here. We have to do it though so the asset view
+ * template can populate a RNA collection with asset-handles, which are just file entries
+ * currently. A proper design is being worked on. */
+ prop = RNA_def_property(srna, "file_data", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_struct_type(prop, "FileSelectEntry");
+ RNA_def_property_pointer_funcs(
+ prop, "rna_AssetHandle_file_data_get", "rna_AssetHandle_file_data_set", NULL, NULL);
+ RNA_def_property_ui_text(
+ prop, "File Entry", "TEMPORARY, DO NOT USE - File data used to refer to the asset");
+
+ prop = RNA_def_property(srna, "local_id", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ID");
+ RNA_def_property_pointer_funcs(prop, "rna_AssetHandle_local_id_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop,
+ "",
+ "The local data-block this asset represents; only valid if that is a "
+ "data-block in this file");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ rna_def_asset_handle_api(srna);
+}
+
+static void rna_def_asset_library_reference(BlenderRNA *brna)
+{
+ StructRNA *srna = RNA_def_struct(brna, "AssetLibraryReference", NULL);
+ RNA_def_struct_ui_text(
+ srna, "Asset Library Reference", "Identifier to refere to the asset library");
+}
+
+/**
+ * \note the UI text and updating has to be set by the caller.
+ */
+PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna,
+ const char *get,
+ const char *set)
+{
+ PropertyRNA *prop = RNA_def_property(srna, "asset_library", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, DummyRNA_NULL_items);
+ RNA_def_property_enum_funcs(prop, get, set, "rna_asset_library_reference_itemf");
+
+ return prop;
+}
+
void RNA_def_asset(BlenderRNA *brna)
{
RNA_define_animate_sdna(false);
rna_def_asset_tag(brna);
rna_def_asset_data(brna);
+ rna_def_asset_library_reference(brna);
+ rna_def_asset_handle(brna);
RNA_define_animate_sdna(true);
}
diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c
index 9e57368f8f9..2bc00dd5af5 100644
--- a/source/blender/makesrna/intern/rna_cloth.c
+++ b/source/blender/makesrna/intern/rna_cloth.c
@@ -652,7 +652,6 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_UNIT_MASS);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_text(prop, "Vertex Mass", "The mass of each vertex on the cloth material");
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "vertex_group_mass", PROP_STRING, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c
index 752c9495e50..608a8e51b09 100644
--- a/source/blender/makesrna/intern/rna_collection.c
+++ b/source/blender/makesrna/intern/rna_collection.c
@@ -170,7 +170,7 @@ static bool rna_Collection_objects_override_apply(Main *bmain,
Collection *coll_dst = (Collection *)ptr_dst->owner_id;
if (ptr_item_dst->type == NULL || ptr_item_src->type == NULL) {
- // BLI_assert(0 && "invalid source or destination object.");
+ // BLI_assert_msg(0, "invalid source or destination object.");
return false;
}
@@ -185,7 +185,7 @@ static bool rna_Collection_objects_override_apply(Main *bmain,
&coll_dst->gobject, ob_dst, offsetof(CollectionObject, ob));
if (cob_dst == NULL) {
- BLI_assert(0 && "Could not find destination object in destination collection!");
+ BLI_assert_msg(0, "Could not find destination object in destination collection!");
return false;
}
@@ -288,7 +288,7 @@ static bool rna_Collection_children_override_apply(Main *bmain,
&coll_dst->children, subcoll_dst, offsetof(CollectionChild, collection));
if (collchild_dst == NULL) {
- BLI_assert(0 && "Could not find destination sub-collection in destination collection!");
+ BLI_assert_msg(0, "Could not find destination sub-collection in destination collection!");
return false;
}
diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c
index 4079406e64b..70fb10c54b0 100644
--- a/source/blender/makesrna/intern/rna_context.c
+++ b/source/blender/makesrna/intern/rna_context.c
@@ -58,6 +58,8 @@ const EnumPropertyItem rna_enum_context_mode_items[] = {
#ifdef RNA_RUNTIME
+# include "DNA_asset_types.h"
+
# ifdef WITH_PYTHON
# include "BPY_extern.h"
# endif
@@ -134,6 +136,22 @@ static PointerRNA rna_Context_gizmo_group_get(PointerRNA *ptr)
return newptr;
}
+static PointerRNA rna_Context_asset_file_handle_get(PointerRNA *ptr)
+{
+ bContext *C = (bContext *)ptr->data;
+ bool is_handle_valid;
+ AssetHandle asset_handle = CTX_wm_asset_handle(C, &is_handle_valid);
+ if (!is_handle_valid) {
+ return PointerRNA_NULL;
+ }
+
+ PointerRNA newptr;
+ /* Have to cast away const, but the file entry API doesn't allow modifications anyway. */
+ RNA_pointer_create(
+ NULL, &RNA_FileSelectEntry, (struct FileDirEntry *)asset_handle.file_data, &newptr);
+ return newptr;
+}
+
static PointerRNA rna_Context_main_get(PointerRNA *ptr)
{
bContext *C = (bContext *)ptr->data;
@@ -281,6 +299,17 @@ void RNA_def_context(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "GizmoGroup");
RNA_def_property_pointer_funcs(prop, "rna_Context_gizmo_group_get", NULL, NULL, NULL);
+ /* TODO can't expose AssetHandle, since there is no permanent storage to it (so we can't
+ * return a pointer). Instead provide the FileDirEntry pointer it wraps. */
+ prop = RNA_def_property(srna, "asset_file_handle", PROP_POINTER, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_struct_type(prop, "FileSelectEntry");
+ RNA_def_property_pointer_funcs(prop, "rna_Context_asset_file_handle_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop,
+ "",
+ "The file of an active asset. Avoid using this, it will be replaced by "
+ "a proper AssetHandle design");
+
/* Data */
prop = RNA_def_property(srna, "blend_data", PROP_POINTER, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index fadce9e3c89..bd5ade36eaa 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -4759,25 +4759,60 @@ static void rna_def_property_free(StructOrFunctionRNA *cont_, PropertyRNA *prop)
}
}
+static PropertyRNA *rna_def_property_find_py_id(ContainerRNA *cont, const char *identifier)
+{
+ for (PropertyRNA *prop = cont->properties.first; prop; prop = prop->next) {
+ if (STREQ(prop->identifier, identifier)) {
+ return prop;
+ }
+ }
+ return NULL;
+}
+
/* NOTE: only intended for removing dynamic props. */
int RNA_def_property_free_identifier(StructOrFunctionRNA *cont_, const char *identifier)
{
ContainerRNA *cont = cont_;
- PropertyRNA *prop;
+ PropertyRNA *prop = rna_def_property_find_py_id(cont, identifier);
+ if (prop != NULL) {
+ if (prop->flag_internal & PROP_INTERN_RUNTIME) {
+ rna_def_property_free(cont, prop);
+ return 1;
+ }
+ else {
+ return -1;
+ }
+ }
+ return 0;
+}
- for (prop = cont->properties.first; prop; prop = prop->next) {
- if (STREQ(prop->identifier, identifier)) {
- if (prop->flag_internal & PROP_INTERN_RUNTIME) {
- rna_def_property_free(cont_, prop);
- return 1;
- }
- else {
- return -1;
- }
+int RNA_def_property_free_identifier_deferred_prepare(StructOrFunctionRNA *cont_,
+ const char *identifier,
+ void **r_handle)
+{
+ ContainerRNA *cont = cont_;
+ PropertyRNA *prop = rna_def_property_find_py_id(cont, identifier);
+ if (prop != NULL) {
+ if (prop->flag_internal & PROP_INTERN_RUNTIME) {
+ *r_handle = prop;
+ return 1;
+ }
+ else {
+ return -1;
}
}
return 0;
}
+
+void RNA_def_property_free_identifier_deferred_finish(StructOrFunctionRNA *cont_, void *handle)
+{
+ ContainerRNA *cont = cont_;
+ PropertyRNA *prop = handle;
+ BLI_assert(BLI_findindex(&cont->properties, prop) != -1);
+ BLI_assert(prop->flag_internal & PROP_INTERN_RUNTIME);
+ rna_def_property_free(cont, prop);
+}
+
#endif /* RNA_RUNTIME */
const char *RNA_property_typename(PropertyType type)
diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c
index 5e015af8e20..10e899b7ee3 100644
--- a/source/blender/makesrna/intern/rna_fluid.c
+++ b/source/blender/makesrna/intern/rna_fluid.c
@@ -1268,11 +1268,6 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
{FLUID_DOMAIN_TYPE_LIQUID, "LIQUID", 0, "Liquid", "Create domain for liquids"},
{0, NULL, 0, NULL, NULL}};
- static const EnumPropertyItem prop_noise_type_items[] = {
- {FLUID_NOISE_TYPE_WAVELET, "NOISEWAVE", 0, "Wavelet", ""},
- {0, NULL, 0, NULL, NULL},
- };
-
static const EnumPropertyItem prop_compression_items[] = {
{VDB_COMPRESSION_ZIP, "ZIP", 0, "Zip", "Effective but slow compression"},
# ifdef WITH_OPENVDB_BLOSC
@@ -1813,14 +1808,6 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_noise_reset");
- prop = RNA_def_property(srna, "noise_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_sdna(prop, NULL, "noise_type");
- RNA_def_property_enum_items(prop, prop_noise_type_items);
- RNA_def_property_ui_text(
- prop, "Noise Method", "Noise method which is used during the high-res simulation");
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_noise_reset");
-
prop = RNA_def_property(srna, "use_noise", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_NOISE);
RNA_def_property_ui_text(prop, "Use Noise", "Enable fluid noise (using amplification)");
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index f1c05079d9c..4e95174e42b 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -656,6 +656,24 @@ static void rna_TextureGpencilModifier_material_set(PointerRNA *ptr,
rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
}
+static void rna_Lineart_start_level_set(PointerRNA *ptr, int value)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)ptr->data;
+
+ CLAMP(value, 0, 128);
+ lmd->level_start = value;
+ lmd->level_end = MAX2(value, lmd->level_end);
+}
+
+static void rna_Lineart_end_level_set(PointerRNA *ptr, int value)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)ptr->data;
+
+ CLAMP(value, 0, 128);
+ lmd->level_end = value;
+ lmd->level_start = MIN2(value, lmd->level_start);
+}
+
#else
static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
@@ -3015,15 +3033,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop = RNA_def_property(srna, "source_object", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Object");
- RNA_def_property_ui_text(
- prop, "Source Object", "Source object that this modifier uses data from");
+ RNA_def_property_ui_text(prop, "Object", "Generate strokes from this object");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "source_collection", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Collection");
RNA_def_property_ui_text(
- prop, "Source Collection", "Source collection that this modifier uses data from");
+ prop, "Collection", "Generate strokes from the objects in this collection");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
/* types */
@@ -3068,12 +3085,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Level Start", "Minimum number of occlusions for the generated strokes");
RNA_def_property_range(prop, 0, 128);
+ RNA_def_property_int_funcs(prop, NULL, "rna_Lineart_start_level_set", NULL);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "level_end", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(
prop, "Level End", "Maximum number of occlusions for the generated strokes");
RNA_def_property_range(prop, 0, 128);
+ RNA_def_property_int_funcs(prop, NULL, "rna_Lineart_end_level_set", NULL);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "target_material", PROP_POINTER, PROP_NONE);
@@ -3085,12 +3104,11 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
NULL,
"rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(
- prop, "Target Material", "Grease Pencil material assigned to the generated strokes");
+ prop, "Material", "Grease Pencil material assigned to the generated strokes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "target_layer", PROP_STRING, PROP_NONE);
- RNA_def_property_ui_text(
- prop, "Target Layer", "Grease Pencil layer assigned to the generated strokes");
+ RNA_def_property_ui_text(prop, "Layer", "Grease Pencil layer assigned to the generated strokes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "source_vertex_group", PROP_STRING, PROP_NONE);
@@ -3119,6 +3137,15 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
"Certain settings will be unavailable");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "overscan", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop,
+ "Overscan",
+ "A margin to prevent strokes from ending abruptly at the edge of the image");
+ RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.01f, 3);
+ RNA_def_property_range(prop, 0.0f, 0.5f);
+ RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(prop, "Thickness", "The thickness for the generated strokes");
RNA_def_property_ui_range(prop, 1, 100, 1, 1);
@@ -3255,7 +3282,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
/* data */
srna = RNA_def_struct(brna, "GpencilModifier", NULL);
- RNA_def_struct_ui_text(srna, "GpencilModifier", "Modifier affecting the grease pencil object");
+ RNA_def_struct_ui_text(srna, "GpencilModifier", "Modifier affecting the Grease Pencil object");
RNA_def_struct_refine_func(srna, "rna_GpencilModifier_refine");
RNA_def_struct_path_func(srna, "rna_GpencilModifier_path");
RNA_def_struct_sdna(srna, "GpencilModifierData");
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index f15368bcc77..0bb76fd933a 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -30,6 +30,7 @@
#define RNA_MAGIC ((int)~0)
+struct AssetLibraryReference;
struct FreestyleSettings;
struct ID;
struct IDOverrideLibrary;
@@ -266,6 +267,14 @@ void rna_def_mtex_common(struct BlenderRNA *brna,
void rna_def_texpaint_slots(struct BlenderRNA *brna, struct StructRNA *srna);
void rna_def_view_layer_common(struct BlenderRNA *brna, struct StructRNA *srna, const bool scene);
+PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna,
+ const char *get,
+ const char *set);
+const EnumPropertyItem *rna_asset_library_reference_itemf(struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ bool *r_free);
+
void rna_def_actionbone_group_common(struct StructRNA *srna,
int update_flag,
const char *update_cb);
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index b662f54ed4c..d91c0bfaf29 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -702,14 +702,6 @@ static void rna_def_material_lineart(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Mask", "");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
- prop = RNA_def_property(srna, "use_mat_occlusion", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_default(prop, 0);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS);
- RNA_def_property_ui_text(prop,
- "Custom Occlusion Effectiveness",
- "Use custom occlusion effectiveness for this material");
- RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
-
prop = RNA_def_property(srna, "mat_occlusion", PROP_INT, PROP_NONE);
RNA_def_property_int_default(prop, 1);
RNA_def_property_ui_range(prop, 0.0f, 5.0f, 1.0f, 1);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index e64eaf8c363..b424a575094 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -1874,7 +1874,7 @@ static void rna_def_modifier_warp(BlenderRNA *brna)
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
RNA_def_property_update(prop, 0, "rna_Modifier_update");
- prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_UNSIGNED | PROP_DISTANCE);
+ prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_ui_text(prop, "Radius", "Radius to apply");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -4327,14 +4327,14 @@ static void rna_def_modifier_shrinkwrap(BlenderRNA *brna)
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_ShrinkwrapModifier_vgroup_name_set");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
- prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_UNIT_LENGTH);
+ prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "keepDist");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_range(prop, -100, 100, 1, 2);
RNA_def_property_ui_text(prop, "Offset", "Distance to keep from the target");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
- prop = RNA_def_property(srna, "project_limit", PROP_FLOAT, PROP_UNIT_LENGTH);
+ prop = RNA_def_property(srna, "project_limit", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "projLimit");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0, 100, 1, 2);
@@ -6279,6 +6279,12 @@ static void rna_def_modifier_weld(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "loose_edges", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_WELD_LOOSE_EDGES);
+ RNA_def_property_ui_text(
+ prop, "Only Loose Edges", "Collapse edges without faces, cloth sewing edges");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
RNA_define_lib_overridable(false);
}
@@ -6884,6 +6890,15 @@ static void rna_def_modifier_surfacedeform(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "use_sparse_bind", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SDEF_SPARSE_BIND);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(
+ prop,
+ "Sparse Bind",
+ "Only record binding data for vertices matching the vertex group at the time of bind");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, -100, 100);
RNA_def_property_ui_range(prop, -100, 100, 10, 2);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 4712f4a0a0b..3d4256db335 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -6622,10 +6622,9 @@ static void def_cmp_image(StructRNA *srna)
"Put node output buffer to straight alpha instead of premultiplied");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
- /* NB: image user properties used in the UI are redefined in def_node_image_user,
+ /* NOTE: Image user properties used in the UI are redefined in def_node_image_user,
* to trigger correct updates of the node editor. RNA design problem that prevents
- * updates from nested structs ...
- */
+ * updates from nested structs. */
RNA_def_struct_sdna_from(srna, "ImageUser", "storage");
def_node_image_user(srna);
}
@@ -6722,9 +6721,8 @@ static void rna_def_cmp_output_file_slots_api(BlenderRNA *brna,
parm = RNA_def_pointer(func, "socket", "NodeSocket", "", "New socket");
RNA_def_function_return(func, parm);
- /* NB: methods below can use the standard node socket API functions,
- * included here for completeness.
- */
+ /* NOTE: methods below can use the standard node socket API functions,
+ * included here for completeness. */
func = RNA_def_function(srna, "remove", "rna_Node_socket_remove");
RNA_def_function_ui_description(func, "Remove a file slot from this node");
@@ -9443,6 +9441,52 @@ static void def_geo_attribute_vector_rotate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_curve_set_handles(StructRNA *srna)
+{
+ static const EnumPropertyItem type_items[] = {
+ {GEO_NODE_CURVE_HANDLE_FREE,
+ "FREE",
+ ICON_HANDLE_FREE,
+ "Free",
+ "The handle can be moved anywhere, and doesn't influence the point's other handle"},
+ {GEO_NODE_CURVE_HANDLE_AUTO,
+ "AUTO",
+ ICON_HANDLE_AUTO,
+ "Auto",
+ "The location is automatically calculated to be smooth"},
+ {GEO_NODE_CURVE_HANDLE_VECTOR,
+ "VECTOR",
+ ICON_HANDLE_VECTOR,
+ "Vector",
+ "The location is calculated to point to the next/previous control point"},
+ {GEO_NODE_CURVE_HANDLE_ALIGN,
+ "ALIGN",
+ ICON_HANDLE_ALIGNED,
+ "Align",
+ "The location is constrained to point in the opposite direction as the other handle"},
+ {0, NULL, 0, NULL, NULL}};
+
+ static const EnumPropertyItem mode_items[] = {
+ {GEO_NODE_CURVE_HANDLE_LEFT, "LEFT", ICON_NONE, "Left", "Update the left handles"},
+ {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Update the right handles"},
+ {0, NULL, 0, NULL, NULL}};
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage");
+
+ prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "handle_type");
+ RNA_def_property_enum_items(prop, type_items);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ 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", "Whether to update left and right handles");
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_curve_primitive_circle(StructRNA *srna)
{
static const EnumPropertyItem mode_items[] = {
@@ -10039,6 +10083,32 @@ static void def_geo_curve_to_points(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_curve_trim(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static EnumPropertyItem mode_items[] = {
+ {GEO_NODE_CURVE_INTERPOLATE_FACTOR,
+ "FACTOR",
+ 0,
+ "Factor",
+ "Find the endpoint positions using a factor of each spline's length"},
+ {GEO_NODE_CURVE_INTERPOLATE_LENGTH,
+ "LENGTH",
+ 0,
+ "Length",
+ "Find the endpoint positions using a length from the start of each spline"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurveTrim", "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", "How to find endpoint positions for the trimmed spline");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_attribute_transfer(StructRNA *srna)
{
static EnumPropertyItem mapping_items[] = {
@@ -10275,11 +10345,11 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "Node", "Node owning this socket");
- /* NB: the type property is used by standard sockets.
+ /* NOTE: The type property is used by standard sockets.
* Ideally should be defined only for the registered subclass,
* but to use the existing DNA is added in the base type here.
- * Future socket types can ignore or override this if needed.
- */
+ * Future socket types can ignore or override this if needed. */
+
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, node_socket_type_items);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index e110459eeea..ed681291e29 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -1997,16 +1997,25 @@ static void rna_Object_boundbox_get(PointerRNA *ptr, float *values)
}
}
+static bool check_object_vgroup_support_and_warn(const Object *ob,
+ const char *op_name,
+ ReportList *reports)
+{
+ if (!BKE_object_supports_vertex_groups(ob)) {
+ const char *ob_type_name = "Unknown";
+ RNA_enum_name_from_value(rna_enum_object_type_items, ob->type, &ob_type_name);
+ BKE_reportf(reports, RPT_ERROR, "%s is not supported for '%s' objects", op_name, ob_type_name);
+ return false;
+ }
+ return true;
+}
+
static bDeformGroup *rna_Object_vgroup_new(Object *ob,
Main *bmain,
ReportList *reports,
const char *name)
{
- if (!OB_TYPE_SUPPORT_VGROUP(ob->type)) {
- const char *ob_type_name = "Unknown";
- RNA_enum_name_from_value(rna_enum_object_type_items, ob->type, &ob_type_name);
- BKE_reportf(
- reports, RPT_ERROR, "VertexGroups.new(): is not supported for '%s' objects", ob_type_name);
+ if (!check_object_vgroup_support_and_warn(ob, "VertexGroups.new()", reports)) {
return NULL;
}
@@ -2023,6 +2032,10 @@ static void rna_Object_vgroup_remove(Object *ob,
ReportList *reports,
PointerRNA *defgroup_ptr)
{
+ if (!check_object_vgroup_support_and_warn(ob, "VertexGroups.remove()", reports)) {
+ return;
+ }
+
bDeformGroup *defgroup = defgroup_ptr->data;
ListBase *defbase = BKE_object_defgroup_list_mutable(ob);
@@ -2042,8 +2055,12 @@ static void rna_Object_vgroup_remove(Object *ob,
WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
}
-static void rna_Object_vgroup_clear(Object *ob, Main *bmain)
+static void rna_Object_vgroup_clear(Object *ob, Main *bmain, ReportList *reports)
{
+ if (!check_object_vgroup_support_and_warn(ob, "VertexGroups.clear()", reports)) {
+ return;
+ }
+
BKE_object_defgroup_remove_all(ob);
DEG_relations_tag_update(bmain);
@@ -2777,7 +2794,7 @@ static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
func = RNA_def_function(srna, "clear", "rna_Object_vgroup_clear");
- RNA_def_function_flag(func, FUNC_USE_MAIN);
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Delete all vertex groups from object");
}
diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c
index 0d35365c2d8..1e1667f0ae8 100644
--- a/source/blender/makesrna/intern/rna_pose_api.c
+++ b/source/blender/makesrna/intern/rna_pose_api.c
@@ -117,7 +117,7 @@ static void rna_Pose_apply_pose_from_action(ID *pose_owner,
Object *pose_owner_ob = (Object *)pose_owner;
AnimationEvalContext anim_eval_context = {CTX_data_depsgraph_pointer(C), evaluation_time};
- BKE_pose_apply_action(pose_owner_ob, action, &anim_eval_context);
+ BKE_pose_apply_action_selected_bones(pose_owner_ob, action, &anim_eval_context);
/* Do NOT tag with ID_RECALC_ANIMATION, as that would overwrite the just-applied pose. */
DEG_id_tag_update(pose_owner, ID_RECALC_GEOMETRY);
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 4fe61df9387..73924c45d52 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -72,9 +72,6 @@ const EnumPropertyItem rna_enum_render_pass_type_items[] = {
{SCE_PASS_SUBSURFACE_DIRECT, "SUBSURFACE_DIRECT", 0, "Subsurface Direct", ""},
{SCE_PASS_SUBSURFACE_INDIRECT, "SUBSURFACE_INDIRECT", 0, "Subsurface Indirect", ""},
{SCE_PASS_SUBSURFACE_COLOR, "SUBSURFACE_COLOR", 0, "Subsurface Color", ""},
-#ifdef WITH_CYCLES_DEBUG
- {SCE_PASS_DEBUG, "DEBUG", 0, "Pass used for render engine debugging", ""},
-#endif
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index 6008ef40b60..f714987fc05 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -458,29 +458,6 @@ int rna_builtin_properties_lookup_string(PointerRNA *ptr, const char *key, Point
}
}
} while ((srna = srna->base));
-
- /* this was used pre 2.5beta0, now ID property access uses python's
- * getitem style access
- * - ob["foo"] rather than ob.foo */
-# if 0
- if (ptr->data) {
- IDProperty *group, *idp;
-
- group = RNA_struct_idprops(ptr, 0);
-
- if (group) {
- for (idp = group->data.group.first; idp; idp = idp->next) {
- if (STREQ(idp->name, key)) {
- propptr.type = &RNA_Property;
- propptr.data = idp;
-
- *r_ptr = propptr;
- return true;
- }
- }
- }
- }
-# endif
return false;
}
@@ -2089,7 +2066,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain),
switch (RNA_property_type(prop_local)) {
case PROP_BOOLEAN:
/* TODO: support boolean ops? Really doubt this would ever be useful though. */
- BLI_assert(0 && "Boolean properties support no override diff operation");
+ BLI_assert_msg(0, "Boolean properties support no override diff operation");
break;
case PROP_INT: {
int prop_min, prop_max;
@@ -2123,7 +2100,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain),
for (int j = len_local; j--;) {
array_b[j] = j >= i ? -array_b[j] : fac * (array_a[j] - array_b[j]);
if (array_b[j] < prop_min || array_b[j] > prop_max) {
- /* We failed to find a suitable diff op,
+ /* We failed to find a suitable diff op,
* fall back to plain REPLACE one. */
opop->operation = IDOVERRIDE_LIBRARY_OP_REPLACE;
do_set = false;
@@ -2143,7 +2120,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain),
break;
}
default:
- BLI_assert(0 && "Unsupported RNA override diff operation on integer");
+ BLI_assert_msg(0, "Unsupported RNA override diff operation on integer");
break;
}
@@ -2175,7 +2152,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain),
break;
}
default:
- BLI_assert(0 && "Unsupported RNA override diff operation on integer");
+ BLI_assert_msg(0, "Unsupported RNA override diff operation on integer");
break;
}
}
@@ -2213,7 +2190,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain),
for (int j = len_local; j--;) {
array_b[j] = j >= i ? -array_b[j] : fac * (array_a[j] - array_b[j]);
if (array_b[j] < prop_min || array_b[j] > prop_max) {
- /* We failed to find a suitable diff op,
+ /* We failed to find a suitable diff op,
* fall back to plain REPLACE one. */
opop->operation = IDOVERRIDE_LIBRARY_OP_REPLACE;
do_set = false;
@@ -2256,7 +2233,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain),
break;
}
default:
- BLI_assert(0 && "Unsupported RNA override diff operation on float");
+ BLI_assert_msg(0, "Unsupported RNA override diff operation on float");
break;
}
@@ -2299,7 +2276,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain),
break;
}
default:
- BLI_assert(0 && "Unsupported RNA override diff operation on float");
+ BLI_assert_msg(0, "Unsupported RNA override diff operation on float");
break;
}
}
@@ -2307,17 +2284,17 @@ bool rna_property_override_store_default(Main *UNUSED(bmain),
}
case PROP_ENUM:
/* TODO: support add/sub, for bitflags? */
- BLI_assert(0 && "Enum properties support no override diff operation");
+ BLI_assert_msg(0, "Enum properties support no override diff operation");
break;
case PROP_POINTER:
- BLI_assert(0 && "Pointer properties support no override diff operation");
+ BLI_assert_msg(0, "Pointer properties support no override diff operation");
break;
case PROP_STRING:
- BLI_assert(0 && "String properties support no override diff operation");
+ BLI_assert_msg(0, "String properties support no override diff operation");
break;
case PROP_COLLECTION:
/* XXX TODO: support this of course... */
- BLI_assert(0 && "Collection properties support no override diff operation");
+ BLI_assert_msg(0, "Collection properties support no override diff operation");
break;
default:
break;
@@ -2364,7 +2341,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
RNA_property_boolean_set_array(ptr_dst, prop_dst, array_a);
break;
default:
- BLI_assert(0 && "Unsupported RNA override operation on boolean");
+ BLI_assert_msg(0, "Unsupported RNA override operation on boolean");
return false;
}
@@ -2380,7 +2357,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
RNA_PROPERTY_SET_SINGLE(boolean, ptr_dst, prop_dst, index, value);
break;
default:
- BLI_assert(0 && "Unsupported RNA override operation on boolean");
+ BLI_assert_msg(0, "Unsupported RNA override operation on boolean");
return false;
}
}
@@ -2421,7 +2398,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
}
break;
default:
- BLI_assert(0 && "Unsupported RNA override operation on integer");
+ BLI_assert_msg(0, "Unsupported RNA override operation on integer");
return false;
}
@@ -2459,7 +2436,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
storage_value);
break;
default:
- BLI_assert(0 && "Unsupported RNA override operation on integer");
+ BLI_assert_msg(0, "Unsupported RNA override operation on integer");
return false;
}
}
@@ -2506,7 +2483,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
}
break;
default:
- BLI_assert(0 && "Unsupported RNA override operation on float");
+ BLI_assert_msg(0, "Unsupported RNA override operation on float");
return false;
}
@@ -2552,7 +2529,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
storage_value);
break;
default:
- BLI_assert(0 && "Unsupported RNA override operation on float");
+ BLI_assert_msg(0, "Unsupported RNA override operation on float");
return false;
}
}
@@ -2566,7 +2543,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
break;
/* TODO: support add/sub, for bitflags? */
default:
- BLI_assert(0 && "Unsupported RNA override operation on enum");
+ BLI_assert_msg(0, "Unsupported RNA override operation on enum");
return false;
}
return true;
@@ -2579,7 +2556,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
RNA_property_pointer_set(ptr_dst, prop_dst, value, NULL);
break;
default:
- BLI_assert(0 && "Unsupported RNA override operation on pointer");
+ BLI_assert_msg(0, "Unsupported RNA override operation on pointer");
return false;
}
return true;
@@ -2593,7 +2570,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
RNA_property_string_set(ptr_dst, prop_dst, value);
break;
default:
- BLI_assert(0 && "Unsupported RNA override operation on string");
+ BLI_assert_msg(0, "Unsupported RNA override operation on string");
return false;
}
@@ -2609,7 +2586,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
const bool is_dst_idprop = (prop_dst->magic != RNA_MAGIC) ||
(prop_dst->flag & PROP_IDPROPERTY) != 0;
if (!(is_src_idprop && is_dst_idprop)) {
- BLI_assert(0 && "You need to define a specific override apply callback for collections");
+ BLI_assert_msg(0, "You need to define a specific override apply callback for collections");
return false;
}
@@ -2668,7 +2645,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain),
return RNA_property_collection_move(ptr_dst, prop_dst, item_index_added, item_index_dst);
}
default:
- BLI_assert(0 && "Unsupported RNA override operation on collection");
+ BLI_assert_msg(0, "Unsupported RNA override operation on collection");
return false;
}
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 0a91d5f01bc..7d7eec6f256 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -2834,7 +2834,18 @@ static void rna_def_tool_settings(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
- static const EnumPropertyItem annotation_stroke_placement_items[] = {
+ static const EnumPropertyItem annotation_stroke_placement_view2d_items[] = {
+ {GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR,
+ "IMAGE",
+ ICON_IMAGE_DATA,
+ "Image",
+ "Strick stroke to the image"},
+ /* Weird, GP_PROJECT_VIEWALIGN is inverted. */
+ {0, "VIEW", ICON_RESTRICT_VIEW_ON, "View", "Stick stroke to the view"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem annotation_stroke_placement_view3d_items[] = {
{GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR,
"CURSOR",
ICON_PIVOT_CURSOR,
@@ -3116,6 +3127,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Use Snapping", "Snap to strip edges or current frame");
RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1);
RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* Publish message-bus. */
prop = RNA_def_property(srna, "snap_elements", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_mode");
@@ -3337,30 +3349,15 @@ static void rna_def_tool_settings(BlenderRNA *brna)
/* Annotations - 2D Views Stroke Placement */
prop = RNA_def_property(srna, "annotation_stroke_placement_view2d", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v2d_align");
- RNA_def_property_enum_items(prop, annotation_stroke_placement_items);
+ RNA_def_property_enum_items(prop, annotation_stroke_placement_view2d_items);
RNA_def_property_ui_text(prop, "Stroke Placement (2D View)", "");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
- /* Annotations - Sequencer Preview Stroke Placement */
- prop = RNA_def_property(
- srna, "annotation_stroke_placement_sequencer_preview", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_seq_align");
- RNA_def_property_enum_items(prop, annotation_stroke_placement_items);
- RNA_def_property_ui_text(prop, "Stroke Placement (Sequencer Preview)", "");
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
-
- /* Annotations - Image Editor Stroke Placement */
- prop = RNA_def_property(srna, "annotation_stroke_placement_image_editor", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_ima_align");
- RNA_def_property_enum_items(prop, annotation_stroke_placement_items);
- RNA_def_property_ui_text(prop, "Stroke Placement (Image Editor)", "");
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
-
/* Annotations - 3D View Stroke Placement */
/* XXX: Do we need to decouple the stroke_endpoints setting too? */
prop = RNA_def_property(srna, "annotation_stroke_placement_view3d", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "annotate_v3d_align");
- RNA_def_property_enum_items(prop, annotation_stroke_placement_items);
+ RNA_def_property_enum_items(prop, annotation_stroke_placement_view3d_items);
RNA_def_property_enum_default(prop, GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR);
RNA_def_property_ui_text(prop,
"Annotation Stroke Placement (3D View)",
@@ -4123,41 +4120,6 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
}
- /* layer options */
- prop = RNA_def_property(srna, "use_zmask", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_ZMASK);
- RNA_def_property_ui_text(prop, "Zmask", "Only render what's in front of the solid z values");
- if (scene) {
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
- }
- else {
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- }
-
- prop = RNA_def_property(srna, "invert_zmask", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_NEG_ZMASK);
- RNA_def_property_ui_text(
- prop,
- "Zmask Negate",
- "For Zmask, only render what is behind solid z values instead of in front");
- if (scene) {
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
- }
- else {
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- }
-
- prop = RNA_def_property(srna, "use_all_z", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_ALL_Z);
- RNA_def_property_ui_text(
- prop, "All Z", "Fill in Z values for solid faces in invisible layers, for masking");
- if (scene) {
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
- }
- else {
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- }
-
prop = RNA_def_property(srna, "use_solid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_SOLID);
RNA_def_property_ui_text(prop, "Solid", "Render Solid faces in this Layer");
@@ -4167,29 +4129,6 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce
else {
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
-
- prop = RNA_def_property(srna, "use_halo", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_HALO);
- RNA_def_property_ui_text(prop, "Halo", "Render Halos in this Layer (on top of Solid)");
- if (scene) {
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
- }
- else {
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- }
-
- prop = RNA_def_property(srna, "use_ztransp", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_ZTRA);
- RNA_def_property_ui_text(prop,
- "Z-Transparent",
- "Render Z-transparent faces in this layer (on top of Solid and Halos)");
- if (scene) {
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
- }
- else {
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- }
-
prop = RNA_def_property(srna, "use_sky", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_SKY);
RNA_def_property_ui_text(prop, "Sky", "Render Sky in this Layer");
@@ -4210,17 +4149,6 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
- prop = RNA_def_property(srna, "use_edge_enhance", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_EDGE);
- RNA_def_property_ui_text(
- prop, "Edge", "Render edge-enhance in this layer (only works for solid faces)");
- if (scene) {
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
- }
- else {
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- }
-
prop = RNA_def_property(srna, "use_strand", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_STRAND);
RNA_def_property_ui_text(prop, "Strand", "Render Strands in this Layer");
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index b1f0b0d760f..74fe2a26505 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -306,6 +306,10 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq)
SEQ_transform_seqbase_shuffle(seqbase, seq, scene); /* XXX: BROKEN!, uses context seqbasep. */
}
SEQ_sort(seqbase);
+
+ if (seq->type == SEQ_TYPE_SOUND_RAM) {
+ DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
+ }
}
/* A simple wrapper around above func, directly usable as prop update func.
@@ -324,7 +328,6 @@ static void rna_Sequence_start_frame_set(PointerRNA *ptr, int value)
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
- SEQ_relations_invalidate_cache_composite(scene, seq);
SEQ_transform_translate_sequence(scene, seq, value - seq->start);
do_sequence_frame_change_update(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
@@ -335,7 +338,6 @@ static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, int value)
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
- SEQ_relations_invalidate_cache_composite(scene, seq);
SEQ_transform_set_left_handle_frame(seq, value);
SEQ_transform_fix_single_image_seq_offsets(seq);
do_sequence_frame_change_update(scene, seq);
@@ -347,7 +349,6 @@ static void rna_Sequence_end_frame_final_set(PointerRNA *ptr, int value)
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
- SEQ_relations_invalidate_cache_composite(scene, seq);
SEQ_transform_set_right_handle_frame(seq, value);
SEQ_transform_fix_single_image_seq_offsets(seq);
do_sequence_frame_change_update(scene, seq);
@@ -451,7 +452,6 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value)
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
- SEQ_relations_invalidate_cache_composite(scene, seq);
SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) + value);
do_sequence_frame_change_update(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
@@ -477,7 +477,6 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value)
Editing *ed = SEQ_editing_get(scene, false);
ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq);
- SEQ_relations_invalidate_cache_composite(scene, seq);
/* check channel increment or decrement */
const int channel_delta = (value >= seq->machine) ? 1 : -1;
seq->machine = value;
@@ -1921,16 +1920,6 @@ static void rna_def_sequence(BlenderRNA *brna)
RNA_def_property_update(
prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
- prop = RNA_def_property(srna, "speed_factor", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "speed_fader");
- RNA_def_property_ui_text(
- prop,
- "Speed Factor",
- "Multiply the current speed of the sequence with this number or remap current frame "
- "to this frame");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
-
/* modifiers */
prop = RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "SequenceModifier");
@@ -2787,24 +2776,48 @@ static void rna_def_speed_control(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "SpeedControlVars", "effectdata");
- prop = RNA_def_property(srna, "multiply_speed", PROP_FLOAT, PROP_UNSIGNED);
- RNA_def_property_float_sdna(prop, NULL, "globalSpeed");
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* seq->facf0 is used to animate this */
- RNA_def_property_ui_text(
- prop, "Multiply Speed", "Multiply the resulting speed after the speed factor");
- RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, -1);
+ static const EnumPropertyItem speed_control_items[] = {
+ {SEQ_SPEED_STRETCH,
+ "STRETCH",
+ 0,
+ "Stretch",
+ "Adjust input playback speed, so its duration fits strip length"},
+ {SEQ_SPEED_MULTIPLY, "MULTIPLY", 0, "Multiply", "Multiply with the speed factor"},
+ {SEQ_SPEED_FRAME_NUMBER,
+ "FRAME_NUMBER",
+ 0,
+ "Frame Number",
+ "Frame number of the input strip"},
+ {SEQ_SPEED_LENGTH, "LENGTH", 0, "Length", "Percentage of the input strip length"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "speed_control", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "speed_control_type");
+ RNA_def_property_enum_items(prop, speed_control_items);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Speed Control", "Speed control method");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
- prop = RNA_def_property(srna, "use_as_speed", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SEQ_SPEED_INTEGRATE);
+ prop = RNA_def_property(srna, "speed_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "speed_fader");
RNA_def_property_ui_text(
- prop, "Use as Speed", "Interpret the value as speed instead of a frame number");
+ prop,
+ "Multiply Factor",
+ "Multiply the current speed of the sequence with this number or remap current frame "
+ "to this frame");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
- prop = RNA_def_property(srna, "use_scale_to_length", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SEQ_SPEED_COMPRESS_IPO_Y);
- RNA_def_property_ui_text(
- prop, "Scale to Length", "Scale values from 0.0 to 1.0 to target sequence length");
+ prop = RNA_def_property(srna, "speed_frame_number", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "speed_fader_frame_number");
+ RNA_def_property_ui_text(prop, "Frame Number", "Frame number of input strip");
+ RNA_def_property_ui_range(prop, 0.0, MAXFRAME, 1.0, -1);
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
+
+ prop = RNA_def_property(srna, "speed_length", PROP_FLOAT, PROP_PERCENTAGE);
+ RNA_def_property_float_sdna(prop, NULL, "speed_fader_length");
+ RNA_def_property_ui_text(prop, "Length", "Percentage of input strip length");
+ RNA_def_property_ui_range(prop, 0.0, 100.0, 1, -1);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_frame_interpolate", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index 8aab0c079a3..4895ab11618 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -30,6 +30,8 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "SEQ_edit.h"
+
#include "rna_internal.h"
#ifdef RNA_RUNTIME
@@ -99,6 +101,24 @@ static void rna_Sequences_move_strip_to_meta(
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene);
}
+static Sequence *rna_Sequence_split(
+ ID *id, Sequence *seq, Main *bmain, int frame, int split_method)
+{
+ Scene *scene = (Scene *)id;
+ Editing *ed = SEQ_editing_get(scene, false);
+ ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq);
+
+ Sequence *r_seq = SEQ_edit_strip_split(bmain, scene, seqbase, seq, frame, split_method);
+
+ /* Update depsgraph. */
+ DEG_relations_tag_update(bmain);
+ DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
+
+ WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene);
+
+ return r_seq;
+}
+
static Sequence *rna_Sequences_new_clip(ID *id,
ListBase *seqbase,
Main *bmain,
@@ -635,6 +655,12 @@ void RNA_api_sequence_strip(StructRNA *srna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem seq_split_method_items[] = {
+ {SEQ_SPLIT_SOFT, "SOFT", 0, "Soft", ""},
+ {SEQ_SPLIT_HARD, "HARD", 0, "Hard", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
func = RNA_def_function(srna, "update", "rna_Sequence_update_rnafunc");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Update the strip dimensions");
@@ -676,6 +702,18 @@ void RNA_api_sequence_strip(StructRNA *srna)
"Invalidate cached images for strip and all dependent strips");
parm = RNA_def_enum(func, "type", seq_cahce_type_items, 0, "Type", "Cache Type");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+
+ func = RNA_def_function(srna, "split", "rna_Sequence_split");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
+ RNA_def_function_ui_description(func, "Split Sequence");
+ parm = RNA_def_int(
+ func, "frame", 0, INT_MIN, INT_MAX, "", "Frame where to split the strip", INT_MIN, INT_MAX);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_enum(func, "split_method", seq_split_method_items, 0, "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ /* Return type. */
+ parm = RNA_def_pointer(func, "sequence", "Sequence", "", "Right side Sequence");
+ RNA_def_function_return(func, parm);
}
void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 1d00c379b06..1d4318602c2 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -359,7 +359,7 @@ static const EnumPropertyItem display_channels_items[] = {
"Color and Alpha",
"Display image with RGB colors and alpha transparency"},
{0, "COLOR", ICON_IMAGE_RGB, "Color", "Display image with RGB colors"},
- {SI_SHOW_ALPHA, "ALPHA", ICON_IMAGE_ALPHA, "Alpha", "Display alpha transparency channel"},
+ {SI_SHOW_ALPHA, "ALPHA", ICON_IMAGE_ALPHA, "Alpha", "Display alpha transparency channel"},
{SI_SHOW_ZBUF,
"Z_BUFFER",
ICON_IMAGE_ZDEPTH,
@@ -509,6 +509,7 @@ static const EnumPropertyItem rna_enum_curve_display_handle_items[] = {
#ifdef RNA_RUNTIME
# include "DNA_anim_types.h"
+# include "DNA_asset_types.h"
# include "DNA_scene_types.h"
# include "DNA_screen_types.h"
# include "DNA_userdef_types.h"
@@ -535,6 +536,7 @@ static const EnumPropertyItem rna_enum_curve_display_handle_items[] = {
# include "DEG_depsgraph_build.h"
# include "ED_anim_api.h"
+# include "ED_asset.h"
# include "ED_buttons.h"
# include "ED_clip.h"
# include "ED_fileselect.h"
@@ -2567,104 +2569,13 @@ static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr)
/* Just an extra sanity check to ensure this isn't somehow called for RNA_FileSelectParams. */
BLI_assert(ptr->type == &RNA_FileAssetSelectParams);
- /* Simple case: Predefined repo, just set the value. */
- if (params->asset_library.type < FILE_ASSET_LIBRARY_CUSTOM) {
- return params->asset_library.type;
- }
-
- /* Note that the path isn't checked for validity here. If an invalid library path is used, the
- * Asset Browser can give a nice hint on what's wrong. */
- const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
- &U, params->asset_library.custom_library_index);
- if (user_library) {
- return FILE_ASSET_LIBRARY_CUSTOM + params->asset_library.custom_library_index;
- }
-
- BLI_assert(0);
- return FILE_ASSET_LIBRARY_LOCAL;
+ return ED_asset_library_reference_to_enum_value(&params->asset_library);
}
static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int value)
{
FileAssetSelectParams *params = ptr->data;
-
- /* Simple case: Predefined repo, just set the value. */
- if (value < FILE_ASSET_LIBRARY_CUSTOM) {
- params->asset_library.type = value;
- params->asset_library.custom_library_index = -1;
- BLI_assert(ELEM(value, FILE_ASSET_LIBRARY_LOCAL));
- return;
- }
-
- const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
- &U, value - FILE_ASSET_LIBRARY_CUSTOM);
-
- /* Note that the path isn't checked for validity here. If an invalid library path is used, the
- * Asset Browser can give a nice hint on what's wrong. */
- const bool is_valid = (user_library->name[0] && user_library->path[0]);
- if (!user_library) {
- params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL;
- params->asset_library.custom_library_index = -1;
- }
- else if (user_library && is_valid) {
- params->asset_library.custom_library_index = value - FILE_ASSET_LIBRARY_CUSTOM;
- params->asset_library.type = FILE_ASSET_LIBRARY_CUSTOM;
- }
-}
-
-static const EnumPropertyItem *rna_FileAssetSelectParams_asset_library_itemf(
- bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
-{
- const EnumPropertyItem predefined_items[] = {
- /* For the future. */
- // {FILE_ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
- {FILE_ASSET_LIBRARY_LOCAL,
- "LOCAL",
- ICON_BLENDER,
- "Current File",
- "Show the assets currently available in this Blender session"},
- {0, NULL, 0, NULL, NULL},
- };
-
- EnumPropertyItem *item = NULL;
- int totitem = 0;
-
- /* Add separator if needed. */
- if (!BLI_listbase_is_empty(&U.asset_libraries)) {
- const EnumPropertyItem sepr = {0, "", 0, "Custom", NULL};
- RNA_enum_item_add(&item, &totitem, &sepr);
- }
-
- int i = 0;
- for (bUserAssetLibrary *user_library = U.asset_libraries.first; user_library;
- user_library = user_library->next, i++) {
- /* Note that the path itself isn't checked for validity here. If an invalid library path is
- * used, the Asset Browser can give a nice hint on what's wrong. */
- const bool is_valid = (user_library->name[0] && user_library->path[0]);
- if (!is_valid) {
- continue;
- }
-
- /* Use library path as description, it's a nice hint for users. */
- EnumPropertyItem tmp = {FILE_ASSET_LIBRARY_CUSTOM + i,
- user_library->name,
- ICON_NONE,
- user_library->name,
- user_library->path};
- RNA_enum_item_add(&item, &totitem, &tmp);
- }
-
- if (totitem) {
- const EnumPropertyItem sepr = {0, "", 0, "Built-in", NULL};
- RNA_enum_item_add(&item, &totitem, &sepr);
- }
-
- /* Add predefined items. */
- RNA_enum_items_add(&item, &totitem, predefined_items);
-
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
- return item;
+ params->asset_library = ED_asset_library_reference_from_enum_value(value);
}
static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value)
@@ -2691,6 +2602,32 @@ static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr)
return (int)strlen(entry->name);
}
+static const EnumPropertyItem *rna_FileBrowser_FileSelectEntry_id_type_itemf(
+ bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
+{
+ const FileDirEntry *entry = ptr->data;
+ if (entry->blentype == 0) {
+ static const EnumPropertyItem none_items[] = {
+ {0, "NONE", 0, "None", ""},
+ };
+ return none_items;
+ }
+
+ return rna_enum_id_type_items;
+}
+
+static int rna_FileBrowser_FileSelectEntry_id_type_get(PointerRNA *ptr)
+{
+ const FileDirEntry *entry = ptr->data;
+ return entry->blentype;
+}
+
+static PointerRNA rna_FileBrowser_FileSelectEntry_local_id_get(PointerRNA *ptr)
+{
+ const FileDirEntry *entry = ptr->data;
+ return rna_pointer_inherit_refine(ptr, &RNA_ID, entry->id);
+}
+
static int rna_FileBrowser_FileSelectEntry_preview_icon_id_get(PointerRNA *ptr)
{
const FileDirEntry *entry = ptr->data;
@@ -3188,6 +3125,45 @@ static const EnumPropertyItem dt_uv_items[] = {
{0, NULL, 0, NULL, NULL},
};
+static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[] = {
+ /* Categories */
+ {FILTER_ID_SCE, "category_scene", ICON_SCENE_DATA, "Scenes", "Show scenes"},
+ {FILTER_ID_AC, "category_animation", ICON_ANIM_DATA, "Animations", "Show animation data"},
+ {FILTER_ID_OB | FILTER_ID_GR,
+ "category_object",
+ 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_PT | FILTER_ID_VO,
+ "category_geometry",
+ ICON_NODETREE,
+ "Geometry",
+ "Show meshes, curves, lattice, armatures and metaballs data"},
+ {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
+ "category_shading",
+ ICON_MATERIAL_DATA,
+ "Shading",
+ "Show materials, nodetrees, textures and Freestyle's linestyles"},
+ {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO,
+ "category_image",
+ ICON_IMAGE_DATA,
+ "Images & Sounds",
+ "Show images, movie clips, sounds and masks"},
+ {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO,
+ "category_environment",
+ ICON_WORLD_DATA,
+ "Environment",
+ "Show worlds, lights, cameras and speakers"},
+ {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT |
+ FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS,
+ "category_misc",
+ ICON_GREASEPENCIL,
+ "Miscellaneous",
+ "Show other data types"},
+ {0, NULL, 0, NULL, NULL},
+};
+
static void rna_def_space_generic_show_region_toggles(StructRNA *srna, int region_type_mask)
{
PropertyRNA *prop;
@@ -5543,6 +5519,11 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_TRANSFORM_PREVIEW);
RNA_def_property_ui_text(prop, "Transform Preview", "Show preview of the transformed frames");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ prop = RNA_def_property(srna, "show_grid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_GRID);
+ RNA_def_property_ui_text(prop, "Show Grid", "Show vertical grid lines");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
}
static void rna_def_space_text(BlenderRNA *brna)
@@ -6135,142 +6116,6 @@ static void rna_def_space_console(BlenderRNA *brna)
/* Filter for datablock types in link/append. */
static void rna_def_fileselect_idfilter(BlenderRNA *brna)
{
- struct IDFilterBoolean {
- /* 64 bit, so we can't use bitflag enum. */
- const uint64_t flag;
- const char *identifier;
- const int icon;
- const char *name;
- const char *description;
- };
-
- static const struct IDFilterBoolean booleans[] = {
- /* Datablocks */
- {FILTER_ID_AC, "filter_action", ICON_ANIM_DATA, "Actions", "Show Action data-blocks"},
- {FILTER_ID_AR,
- "filter_armature",
- ICON_ARMATURE_DATA,
- "Armatures",
- "Show Armature data-blocks"},
- {FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"},
- {FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"},
- {FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"},
- {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"},
- {FILTER_ID_GD,
- "filter_grease_pencil",
- ICON_GREASEPENCIL,
- "Grease Pencil",
- "Show Grease pencil data-blocks"},
- {FILTER_ID_GR,
- "filter_group",
- ICON_OUTLINER_COLLECTION,
- "Collections",
- "Show Collection data-blocks"},
- {FILTER_ID_HA, "filter_hair", ICON_HAIR_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,
- "filter_light_probe",
- ICON_OUTLINER_DATA_LIGHTPROBE,
- "Light Probes",
- "Show Light Probe data-blocks"},
- {FILTER_ID_LS,
- "filter_linestyle",
- ICON_LINE_DATA,
- "Freestyle Linestyles",
- "Show Freestyle's Line Style data-blocks"},
- {FILTER_ID_LT, "filter_lattice", ICON_LATTICE_DATA, "Lattices", "Show Lattice data-blocks"},
- {FILTER_ID_MA,
- "filter_material",
- ICON_MATERIAL_DATA,
- "Materials",
- "Show Material data-blocks"},
- {FILTER_ID_MB, "filter_metaball", ICON_META_DATA, "Metaballs", "Show Metaball data-blocks"},
- {FILTER_ID_MC,
- "filter_movie_clip",
- ICON_TRACKER_DATA,
- "Movie Clips",
- "Show Movie Clip data-blocks"},
- {FILTER_ID_ME, "filter_mesh", ICON_MESH_DATA, "Meshes", "Show Mesh data-blocks"},
- {FILTER_ID_MSK, "filter_mask", ICON_MOD_MASK, "Masks", "Show Mask data-blocks"},
- {FILTER_ID_NT,
- "filter_node_tree",
- ICON_NODETREE,
- "Node Trees",
- "Show Node Tree data-blocks"},
- {FILTER_ID_OB, "filter_object", ICON_OBJECT_DATA, "Objects", "Show Object data-blocks"},
- {FILTER_ID_PA,
- "filter_particle_settings",
- ICON_PARTICLE_DATA,
- "Particles Settings",
- "Show Particle Settings data-blocks"},
- {FILTER_ID_PAL, "filter_palette", ICON_COLOR, "Palettes", "Show Palette data-blocks"},
- {FILTER_ID_PC,
- "filter_paint_curve",
- ICON_CURVE_BEZCURVE,
- "Paint Curves",
- "Show Paint Curve data-blocks"},
- {FILTER_ID_PT,
- "filter_pointcloud",
- ICON_POINTCLOUD_DATA,
- "Point Clouds",
- "Show/hide Point Cloud data-blocks"},
- {FILTER_ID_SCE, "filter_scene", ICON_SCENE_DATA, "Scenes", "Show Scene data-blocks"},
- {FILTER_ID_SIM,
- "filter_simulation",
- ICON_PHYSICS,
- "Simulations",
- "Show Simulation data-blocks"}, /* TODO: Use correct icon. */
- {FILTER_ID_SPK, "filter_speaker", ICON_SPEAKER, "Speakers", "Show Speaker data-blocks"},
- {FILTER_ID_SO, "filter_sound", ICON_SOUND, "Sounds", "Show Sound data-blocks"},
- {FILTER_ID_TE, "filter_texture", ICON_TEXTURE_DATA, "Textures", "Show Texture data-blocks"},
- {FILTER_ID_TXT, "filter_text", ICON_TEXT, "Texts", "Show Text data-blocks"},
- {FILTER_ID_VF, "filter_font", ICON_FONT_DATA, "Fonts", "Show Font data-blocks"},
- {FILTER_ID_VO, "filter_volume", ICON_VOLUME_DATA, "Volumes", "Show/hide Volume data-blocks"},
- {FILTER_ID_WO, "filter_world", ICON_WORLD_DATA, "Worlds", "Show World data-blocks"},
- {FILTER_ID_WS,
- "filter_work_space",
- ICON_WORKSPACE,
- "Workspaces",
- "Show workspace data-blocks"},
-
- /* Categories */
- {FILTER_ID_SCE, "category_scene", ICON_SCENE_DATA, "Scenes", "Show scenes"},
- {FILTER_ID_AC, "category_animation", ICON_ANIM_DATA, "Animations", "Show animation data"},
- {FILTER_ID_OB | FILTER_ID_GR,
- "category_object",
- 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_PT | FILTER_ID_VO,
- "category_geometry",
- ICON_NODETREE,
- "Geometry",
- "Show meshes, curves, lattice, armatures and metaballs data"},
- {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
- "category_shading",
- ICON_MATERIAL_DATA,
- "Shading",
- "Show materials, nodetrees, textures and Freestyle's linestyles"},
- {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO,
- "category_image",
- ICON_IMAGE_DATA,
- "Images & Sounds",
- "Show images, movie clips, sounds and masks"},
- {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO,
- "category_environment",
- ICON_WORLD_DATA,
- "Environment",
- "Show worlds, lights, cameras and speakers"},
- {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT |
- FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS,
- "category_misc",
- ICON_GREASEPENCIL,
- "Miscellaneous",
- "Show other data types"},
-
- {0, NULL, 0, NULL, NULL}};
StructRNA *srna = RNA_def_struct(brna, "FileSelectIDFilter", NULL);
RNA_def_struct_sdna(srna, "FileSelectParams");
@@ -6278,12 +6123,23 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna, "File Select ID Filter", "Which ID types to show/hide, when browsing a library");
- for (int i = 0; booleans[i].identifier; i++) {
- PropertyRNA *prop = RNA_def_property(srna, booleans[i].identifier, PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "filter_id", booleans[i].flag);
- RNA_def_property_ui_text(prop, booleans[i].name, booleans[i].description);
- RNA_def_property_ui_icon(prop, booleans[i].icon, 0);
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ const struct IDFilterEnumPropertyItem *individual_ids_and_categories[] = {
+ rna_enum_id_type_filter_items,
+ rna_enum_space_file_id_filter_categories,
+ NULL,
+ };
+ for (uint i = 0; individual_ids_and_categories[i]; i++) {
+ for (int j = 0; individual_ids_and_categories[i][j].identifier; j++) {
+ PropertyRNA *prop = RNA_def_property(
+ srna, individual_ids_and_categories[i][j].identifier, PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(
+ prop, NULL, "filter_id", individual_ids_and_categories[i][j].flag);
+ RNA_def_property_ui_text(prop,
+ individual_ids_and_categories[i][j].name,
+ individual_ids_and_categories[i][j].description);
+ RNA_def_property_ui_icon(prop, individual_ids_and_categories[i][j].icon, 0);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ }
}
}
@@ -6303,6 +6159,28 @@ static void rna_def_fileselect_entry(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_struct_name_property(srna, prop);
+ prop = RNA_def_property(srna, "id_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_id_type_items);
+ RNA_def_property_enum_funcs(prop,
+ "rna_FileBrowser_FileSelectEntry_id_type_get",
+ NULL,
+ "rna_FileBrowser_FileSelectEntry_id_type_itemf");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop,
+ "Data-block Type",
+ "The type of the data-block, if the file represents one ('NONE' otherwise)");
+
+ prop = RNA_def_property(srna, "local_id", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ID");
+ RNA_def_property_pointer_funcs(
+ prop, "rna_FileBrowser_FileSelectEntry_local_id_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop,
+ "",
+ "The local data-block this file represents; only valid if that is a "
+ "data-block in this file");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
prop = RNA_def_int(
srna,
"preview_icon_id",
@@ -6539,7 +6417,7 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
- /* XXX copied from rna_def_fileselect_idfilter. */
+ /* XXX copied from rna_enum_id_type_filter_items. */
static const EnumPropertyItem asset_category_items[] = {
{FILTER_ID_SCE, "SCENES", ICON_SCENE_DATA, "Scenes", "Show scenes"},
{FILTER_ID_AC, "ANIMATIONS", ICON_ANIM_DATA, "Animations", "Show animation data"},
@@ -6594,12 +6472,9 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna, "Asset Select Parameters", "Settings for the file selection in Asset Browser mode");
- prop = RNA_def_property(srna, "asset_library", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, DummyRNA_NULL_items);
- RNA_def_property_enum_funcs(prop,
- "rna_FileAssetSelectParams_asset_library_get",
- "rna_FileAssetSelectParams_asset_library_set",
- "rna_FileAssetSelectParams_asset_library_itemf");
+ prop = rna_def_asset_library_reference_common(srna,
+ "rna_FileAssetSelectParams_asset_library_get",
+ "rna_FileAssetSelectParams_asset_library_set");
RNA_def_property_ui_text(prop, "Asset Library", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index 849bbca16f4..a88b100435a 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -458,6 +458,27 @@ static IDProperty **rna_UIList_idprops(PointerRNA *ptr)
return &ui_list->properties;
}
+static void rna_UIList_list_id_get(PointerRNA *ptr, char *value)
+{
+ uiList *ui_list = (uiList *)ptr->data;
+ if (!ui_list->type) {
+ value[0] = '\0';
+ return;
+ }
+
+ strcpy(value, WM_uilisttype_list_id_get(ui_list->type, ui_list));
+}
+
+static int rna_UIList_list_id_length(PointerRNA *ptr)
+{
+ uiList *ui_list = (uiList *)ptr->data;
+ if (!ui_list->type) {
+ return 0;
+ }
+
+ return strlen(WM_uilisttype_list_id_get(ui_list->type, ui_list));
+}
+
static void uilist_draw_item(uiList *ui_list,
bContext *C,
uiLayout *layout,
@@ -633,7 +654,7 @@ static void uilist_filter_items(uiList *ui_list,
RNA_parameter_list_free(&list);
}
-static void rna_UIList_unregister(Main *UNUSED(bmain), StructRNA *type)
+static void rna_UIList_unregister(Main *bmain, StructRNA *type)
{
uiListType *ult = RNA_struct_blender_type_get(type);
@@ -644,7 +665,7 @@ static void rna_UIList_unregister(Main *UNUSED(bmain), StructRNA *type)
RNA_struct_free_extension(type, &ult->rna_ext);
RNA_struct_free(&BLENDER_RNA, type);
- WM_uilisttype_freelink(ult);
+ WM_uilisttype_remove_ptr(bmain, ult);
/* update while blender is running */
WM_main_add_notifier(NC_WINDOW, NULL);
@@ -1535,6 +1556,16 @@ static void rna_def_uilist(BlenderRNA *brna)
"script, then bl_idname = \"OBJECT_UL_vgroups\")");
/* Data */
+ /* Note that this is the "non-full" list-ID as obtained through #WM_uilisttype_list_id_get(),
+ * which differs from the (internal) `uiList.list_id`. */
+ prop = RNA_def_property(srna, "list_id", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(prop, "rna_UIList_list_id_get", "rna_UIList_list_id_length", NULL);
+ RNA_def_property_ui_text(prop,
+ "List Name",
+ "Identifier of the list, if any was passed to the \"list_id\" "
+ "parameter of \"template_list()\"");
+
prop = RNA_def_property(srna, "layout_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_uilist_layout_type_items);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index aa235b599b7..e06cc39a88b 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -50,6 +50,8 @@ const EnumPropertyItem rna_enum_icon_items[] = {
#ifdef RNA_RUNTIME
+# include "DNA_asset_types.h"
+
const char *rna_translate_ui_text(
const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, bool translate)
{
@@ -525,6 +527,46 @@ static void rna_uiTemplateAnyID(uiLayout *layout,
uiTemplateAnyID(layout, ptr, propname, proptypename, name);
}
+void rna_uiTemplateList(uiLayout *layout,
+ struct bContext *C,
+ const char *listtype_name,
+ const char *list_id,
+ struct PointerRNA *dataptr,
+ const char *propname,
+ struct PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ const int rows,
+ const int maxrows,
+ const int layout_type,
+ const int columns,
+ const bool sort_reverse,
+ const bool sort_lock)
+{
+ int flags = UI_TEMPLATE_LIST_FLAG_NONE;
+ if (sort_reverse) {
+ flags |= UI_TEMPLATE_LIST_SORT_REVERSE;
+ }
+ if (sort_lock) {
+ flags |= UI_TEMPLATE_LIST_SORT_LOCK;
+ }
+
+ uiTemplateList(layout,
+ C,
+ listtype_name,
+ list_id,
+ dataptr,
+ propname,
+ active_dataptr,
+ active_propname,
+ item_dyntip_propname,
+ rows,
+ maxrows,
+ layout_type,
+ columns,
+ flags);
+}
+
static void rna_uiTemplateCacheFile(uiLayout *layout,
bContext *C,
PointerRNA *ptr,
@@ -570,6 +612,69 @@ static void rna_uiTemplateEventFromKeymapItem(
uiTemplateEventFromKeymapItem(layout, name, kmi, true);
}
+static void rna_uiTemplateAssetView(uiLayout *layout,
+ bContext *C,
+ const char *list_id,
+ PointerRNA *asset_library_dataptr,
+ const char *asset_library_propname,
+ PointerRNA *assets_dataptr,
+ const char *assets_propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ int filter_id_types,
+ const char *activate_opname,
+ PointerRNA *r_activate_op_properties,
+ const char *drag_opname,
+ PointerRNA *r_drag_op_properties)
+{
+ AssetFilterSettings filter_settings = {
+ .id_types = filter_id_types ? filter_id_types : FILTER_ID_ALL,
+ };
+ uiTemplateAssetView(layout,
+ C,
+ list_id,
+ asset_library_dataptr,
+ asset_library_propname,
+ assets_dataptr,
+ assets_propname,
+ active_dataptr,
+ active_propname,
+ &filter_settings,
+ activate_opname,
+ r_activate_op_properties,
+ drag_opname,
+ r_drag_op_properties);
+}
+
+/**
+ * XXX Remove filter items that require more than 32 bits for storage. RNA enums don't support
+ * that currently.
+ */
+static const EnumPropertyItem *rna_uiTemplateAssetView_filter_id_types_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ EnumPropertyItem *items = NULL;
+ int totitem = 0;
+
+ for (int i = 0; rna_enum_id_type_filter_items[i].identifier; i++) {
+ if (rna_enum_id_type_filter_items[i].flag > (1ULL << 31)) {
+ continue;
+ }
+
+ EnumPropertyItem tmp = {0, "", 0, "", ""};
+ tmp.value = rna_enum_id_type_filter_items[i].flag;
+ tmp.identifier = rna_enum_id_type_filter_items[i].identifier;
+ tmp.icon = rna_enum_id_type_filter_items[i].icon;
+ tmp.name = rna_enum_id_type_filter_items[i].name;
+ tmp.description = rna_enum_id_type_filter_items[i].description;
+ RNA_enum_item_add(&items, &totitem, &tmp);
+ }
+ RNA_enum_item_end(&items, &totitem);
+
+ *r_free = true;
+ return items;
+}
+
static uiLayout *rna_uiLayoutRowWithHeading(
uiLayout *layout, bool align, const char *heading, const char *heading_ctxt, bool translate)
{
@@ -1508,7 +1613,7 @@ void RNA_api_ui_layout(StructRNA *srna)
parm = RNA_def_pointer(func, "clip_user", "MovieClipUser", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
- func = RNA_def_function(srna, "template_list", "uiTemplateList");
+ func = RNA_def_function(srna, "template_list", "rna_uiTemplateList");
RNA_def_function_ui_description(func, "Item. A list widget to display data, e.g. vertexgroups.");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_string(func, "listtype_name", NULL, 0, "", "Identifier of the list type to use");
@@ -1689,6 +1794,81 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_property_ui_text(parm, "Item", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
api_ui_item_common_text(func);
+
+ func = RNA_def_function(srna, "template_asset_view", "rna_uiTemplateAssetView");
+ RNA_def_function_ui_description(func, "Item. A scrollable list of assets in a grid view");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ parm = RNA_def_string(func,
+ "list_id",
+ NULL,
+ 0,
+ "",
+ "Identifier of this asset view. Necessary to tell apart different asset "
+ "views and to idenify an asset view read from a .blend");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "asset_library_dataptr",
+ "AnyType",
+ "",
+ "Data from which to take the active asset library property");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ parm = RNA_def_string(
+ func, "asset_library_propname", NULL, 0, "", "Identifier of the asset library property");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(
+ func, "assets_dataptr", "AnyType", "", "Data from which to take the asset list property");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ parm = RNA_def_string(
+ func, "assets_propname", NULL, 0, "", "Identifier of the asset list property");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "active_dataptr",
+ "AnyType",
+ "",
+ "Data from which to take the integer property, index of the active item");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ parm = RNA_def_string(
+ func,
+ "active_propname",
+ NULL,
+ 0,
+ "",
+ "Identifier of the integer property in active_data, index of the active item");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_property(func, "filter_id_types", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(parm, DummyRNA_NULL_items);
+ RNA_def_property_enum_funcs(parm, NULL, NULL, "rna_uiTemplateAssetView_filter_id_types_itemf");
+ RNA_def_property_flag(parm, PROP_ENUM_FLAG);
+ RNA_def_string(func,
+ "activate_operator",
+ NULL,
+ 0,
+ "",
+ "Name of a custom operator to invoke when activating an item");
+ parm = RNA_def_pointer(
+ func,
+ "activate_operator_properties",
+ "OperatorProperties",
+ "",
+ "Operator properties to fill in for the custom activate operator passed to the template");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
+ RNA_def_function_output(func, parm);
+ RNA_def_string(func,
+ "drag_operator",
+ NULL,
+ 0,
+ "",
+ "Name of a custom operator to invoke when starting to drag an item. Never "
+ "invoked together with the `active_operator` (if set), it's either the drag or "
+ "the activate one");
+ parm = RNA_def_pointer(
+ func,
+ "drag_operator_properties",
+ "OperatorProperties",
+ "",
+ "Operator properties to fill in for the custom drag operator passed to the template");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
+ RNA_def_function_output(func, parm);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index fd1c0b7951a..d29a90a1886 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -1100,7 +1100,7 @@ int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char
static size_t max_memory_in_megabytes(void)
{
/* Maximum addressable bytes on this platform. */
- const size_t limit_bytes = (((size_t)1) << ((sizeof(size_t[8])) - 1));
+ const size_t limit_bytes = (((size_t)1) << (sizeof(size_t[8]) - 1));
/* Convert it to megabytes and return. */
return (limit_bytes >> 20);
}
diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c
index 76db6f3e325..8ed53c9f70f 100644
--- a/source/blender/makesrna/intern/rna_volume.c
+++ b/source/blender/makesrna/intern/rna_volume.c
@@ -495,7 +495,7 @@ static void rna_def_volume_render(BlenderRNA *brna)
prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, space_items);
RNA_def_property_ui_text(
- prop, "Space", "Specify volume density and step size in object or world space");
+ prop, "Space", "Specify volume density and step size in object or world space");
RNA_def_property_update(prop, 0, "rna_Volume_update_display");
prop = RNA_def_property(srna, "step_size", PROP_FLOAT, PROP_DISTANCE);
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 2a4abac04f8..667f3822935 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -1825,7 +1825,7 @@ static void rna_KeyMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi
#else /* RNA_RUNTIME */
/**
- * expose ``Operator.options`` as its own type so we can control each flags use
+ * expose `Operator.options` as its own type so we can control each flags use
* (some are read-only).
*/
static void rna_def_operator_options_runtime(BlenderRNA *brna)
@@ -1912,7 +1912,7 @@ static void rna_def_operator(BlenderRNA *brna)
/* Registration */
prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->idname");
- /* else it uses the pointer size!. -3 because '.' -> '_OT_' */
+ /* Without setting the length the pointer size would be used. -3 because `.` -> `_OT_`. */
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME - 3);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c
index 6b52a38c2da..95f62d7de16 100644
--- a/source/blender/makesrna/intern/rna_workspace.c
+++ b/source/blender/makesrna/intern/rna_workspace.c
@@ -45,6 +45,8 @@
# include "DNA_screen_types.h"
# include "DNA_space_types.h"
+# include "ED_asset.h"
+
# include "RNA_access.h"
# include "WM_toolsystem.h"
@@ -107,6 +109,18 @@ static void rna_WorkSpace_owner_ids_clear(WorkSpace *workspace)
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, workspace);
}
+static int rna_WorkSpace_asset_library_get(PointerRNA *ptr)
+{
+ const WorkSpace *workspace = ptr->data;
+ return ED_asset_library_reference_to_enum_value(&workspace->asset_library);
+}
+
+static void rna_WorkSpace_asset_library_set(PointerRNA *ptr, int value)
+{
+ WorkSpace *workspace = ptr->data;
+ workspace->asset_library = ED_asset_library_reference_from_enum_value(value);
+}
+
static bToolRef *rna_WorkSpace_tools_from_tkey(WorkSpace *workspace,
const bToolKey *tkey,
bool create)
@@ -407,6 +421,14 @@ static void rna_def_workspace(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Use UI Tags", "Filter the UI by tags");
RNA_def_property_update(prop, 0, "rna_window_update_all");
+ prop = rna_def_asset_library_reference_common(
+ srna, "rna_WorkSpace_asset_library_get", "rna_WorkSpace_asset_library_set");
+ RNA_def_property_ui_text(prop,
+ "Asset Library",
+ "Active asset library to show in the UI, not used by the Asset Browser "
+ "(which has its own active asset library)");
+ RNA_def_property_update(prop, NC_ASSET | ND_ASSET_LIST_READING, NULL);
+
RNA_api_workspace(srna);
}
diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c
index 04a8500d136..56e8418972c 100644
--- a/source/blender/makesrna/intern/rna_xr.c
+++ b/source/blender/makesrna/intern/rna_xr.c
@@ -34,6 +34,63 @@
# include "WM_api.h"
+# ifdef WITH_XR_OPENXR
+static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr)
+{
+ /* Callers could also get XrSessionState pointer through ptr->data, but prefer if we just
+ * consistently pass wmXrData pointers to the WM_xr_xxx() API. */
+
+ BLI_assert((ptr->type == &RNA_XrSessionSettings) || (ptr->type == &RNA_XrSessionState));
+
+ wmWindowManager *wm = (wmWindowManager *)ptr->owner_id;
+ BLI_assert(wm && (GS(wm->id.name) == ID_WM));
+
+ return &wm->xr;
+}
+# endif
+
+static bool rna_XrSessionSettings_use_positional_tracking_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ return (xr->session_settings.flag & XR_SESSION_USE_POSITION_TRACKING) != 0;
+# else
+ UNUSED_VARS(ptr);
+ return false;
+# endif
+}
+
+static void rna_XrSessionSettings_use_positional_tracking_set(PointerRNA *ptr, bool value)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_USE_POSITION_TRACKING);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static bool rna_XrSessionSettings_use_absolute_tracking_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ return (xr->session_settings.flag & XR_SESSION_USE_ABSOLUTE_TRACKING) != 0;
+# else
+ UNUSED_VARS(ptr);
+ return false;
+# endif
+}
+
+static void rna_XrSessionSettings_use_absolute_tracking_set(PointerRNA *ptr, bool value)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_USE_ABSOLUTE_TRACKING);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
static bool rna_XrSessionState_is_running(bContext *C)
{
# ifdef WITH_XR_OPENXR
@@ -55,25 +112,10 @@ static void rna_XrSessionState_reset_to_base_pose(bContext *C)
# endif
}
-# ifdef WITH_XR_OPENXR
-static wmXrData *rna_XrSessionState_wm_xr_data_get(PointerRNA *ptr)
-{
- /* Callers could also get XrSessionState pointer through ptr->data, but prefer if we just
- * consistently pass wmXrData pointers to the WM_xr_xxx() API. */
-
- BLI_assert(ptr->type == &RNA_XrSessionState);
-
- wmWindowManager *wm = (wmWindowManager *)ptr->owner_id;
- BLI_assert(wm && (GS(wm->id.name) == ID_WM));
-
- return &wm->xr;
-}
-# endif
-
static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values)
{
# ifdef WITH_XR_OPENXR
- const wmXrData *xr = rna_XrSessionState_wm_xr_data_get(ptr);
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
WM_xr_session_state_viewer_pose_location_get(xr, r_values);
# else
UNUSED_VARS(ptr);
@@ -84,7 +126,7 @@ static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *
static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float *r_values)
{
# ifdef WITH_XR_OPENXR
- const wmXrData *xr = rna_XrSessionState_wm_xr_data_get(ptr);
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
WM_xr_session_state_viewer_pose_rotation_get(xr, r_values);
# else
UNUSED_VARS(ptr);
@@ -181,12 +223,22 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "use_positional_tracking", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", XR_SESSION_USE_POSITION_TRACKING);
+ RNA_def_property_boolean_funcs(prop,
+ "rna_XrSessionSettings_use_positional_tracking_get",
+ "rna_XrSessionSettings_use_positional_tracking_set");
RNA_def_property_ui_text(
prop,
"Positional Tracking",
"Allow VR headsets to affect the location in virtual space, in addition to the rotation");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ prop = RNA_def_property(srna, "use_absolute_tracking", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop,
+ "rna_XrSessionSettings_use_absolute_tracking_get",
+ "rna_XrSessionSettings_use_absolute_tracking_set");
+ RNA_def_property_ui_text(
+ prop, "Absolute Tracking", "Use unadjusted location/rotation as defined by the XR runtime");
+ RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
}
static void rna_def_xr_session_state(BlenderRNA *brna)
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index 722614c4831..6a9c9715994 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -194,8 +194,8 @@ static void dm_mvert_map_doubles(int *doubles_map,
i_target_low_bound = 0;
target_scan_completed = false;
- /* Scan source vertices, in SortVertsElem sorted array, */
- /* all the while maintaining the lower bound of possible doubles in target vertices */
+ /* Scan source vertices, in #SortVertsElem sorted array,
+ * all the while maintaining the lower bound of possible doubles in target vertices. */
for (i_source = 0, sve_source = sorted_verts_source; i_source < source_num_verts;
i_source++, sve_source++) {
int best_target_vertex = -1;
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 87fce26c45e..5fa11ffdd10 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -10,7 +10,7 @@
* 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,
+ * 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 by the Blender Foundation.
@@ -1133,6 +1133,18 @@ static void panel_draw(const bContext *C, Panel *panel)
}
}
+ /* Draw node warnings. */
+ if (nmd->runtime_eval_log != nullptr) {
+ const geo_log::ModifierLog &log = *static_cast<geo_log::ModifierLog *>(nmd->runtime_eval_log);
+ log.foreach_node_log([layout](const geo_log::NodeLog &node_log) {
+ for (const geo_log::NodeWarning &warning : node_log.warnings()) {
+ if (warning.type != geo_log::NodeWarningType::Info) {
+ uiItemL(layout, warning.message.c_str(), ICON_ERROR);
+ }
+ }
+ });
+ }
+
modifier_panel_end(layout, ptr);
}
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index 18cc1ce6c86..e652eb8353d 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -1177,7 +1177,7 @@ class GeometryNodesEvaluator {
to_sockets.append(to_socket);
}
};
- auto handle_skipped_socket_fn = [&, this](const DSocket socket) {
+ auto handle_skipped_socket_fn = [&](const DSocket socket) {
sockets_to_log_to.append(socket);
};
from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn);
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index 54bb68dc21a..e7750f0a0d1 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -257,10 +257,10 @@ static void normalEditModifier_do_radial(NormalEditModifierData *enmd,
generate_vert_coordinates(mesh, ob, ob_target, enmd->offset, num_verts, cos, size);
/**
- * size gives us our spheroid coefficients ``(A, B, C)``.
+ * size gives us our spheroid coefficients `(A, B, C)`.
* Then, we want to find out for each vert its (a, b, c) triple (proportional to (A, B, C) one).
*
- * Ellipsoid basic equation: ``(x^2/a^2) + (y^2/b^2) + (z^2/c^2) = 1.``
+ * Ellipsoid basic equation: `(x^2/a^2) + (y^2/b^2) + (z^2/c^2) = 1`.
* Since we want to find (a, b, c) matching this equation and proportional to (A, B, C),
* we can do:
* <pre>
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index ef70f3fe6f4..71fc7f3e424 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -282,13 +282,13 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB)) {
uiItemO(layout,
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"),
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Instances Real"),
ICON_NONE,
"OBJECT_OT_duplicates_make_real");
}
else if (psys->part->ren_as == PART_DRAW_PATH) {
uiItemO(layout,
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"),
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert to Mesh"),
ICON_NONE,
"OBJECT_OT_modifier_convert");
}
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index 4677a9bc253..df3db894f4e 100644
--- a/source/blender/modifiers/intern/MOD_remesh.c
+++ b/source/blender/modifiers/intern/MOD_remesh.c
@@ -159,7 +159,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
if (rmd->voxel_size == 0.0f) {
return NULL;
}
- result = BKE_mesh_remesh_voxel_to_mesh_nomain(mesh, rmd->voxel_size, rmd->adaptivity, 0.0f);
+ result = BKE_mesh_remesh_voxel(mesh, rmd->voxel_size, rmd->adaptivity, 0.0f);
if (result == NULL) {
return NULL;
}
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index dd011a293ee..ec6de8f8387 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -94,6 +94,11 @@ typedef struct SDefBindCalcData {
float imat[4][4];
const float falloff;
int success;
+ /** Vertex group lookup data. */
+ const MDeformVert *const dvert;
+ int const defgrp_index;
+ bool const invert_vgroup;
+ bool const sparse_bind;
} SDefBindCalcData;
/**
@@ -218,7 +223,7 @@ static void freeData(ModifierData *md)
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
if (smd->verts) {
- for (int i = 0; i < smd->numverts; i++) {
+ for (int i = 0; i < smd->num_bind_verts; i++) {
if (smd->verts[i].binds) {
for (int j = 0; j < smd->verts[i].numbinds; j++) {
MEM_SAFE_FREE(smd->verts[i].binds[j].vert_inds);
@@ -243,7 +248,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
if (smd->verts) {
tsmd->verts = MEM_dupallocN(smd->verts);
- for (int i = 0; i < smd->numverts; i++) {
+ for (int i = 0; i < smd->num_bind_verts; i++) {
if (smd->verts[i].binds) {
tsmd->verts[i].binds = MEM_dupallocN(smd->verts[i].binds);
@@ -963,12 +968,32 @@ static void bindVert(void *__restrict userdata,
SDefBindPoly *bpoly;
SDefBind *sdbind;
+ sdvert->vertex_idx = index;
+
if (data->success != MOD_SDEF_BIND_RESULT_SUCCESS) {
sdvert->binds = NULL;
sdvert->numbinds = 0;
return;
}
+ if (data->sparse_bind) {
+ float weight = 0.0f;
+
+ if (data->dvert && data->defgrp_index != -1) {
+ weight = BKE_defvert_find_weight(&data->dvert[index], data->defgrp_index);
+ }
+
+ if (data->invert_vgroup) {
+ weight = 1.0f - weight;
+ }
+
+ if (weight <= 0) {
+ sdvert->binds = NULL;
+ sdvert->numbinds = 0;
+ return;
+ }
+ }
+
copy_v3_v3(point_co, data->vertexCos[index]);
bwdata = computeBindWeights(data, point_co);
@@ -1135,6 +1160,21 @@ static void bindVert(void *__restrict userdata,
freeBindData(bwdata);
}
+/* Remove vertices without bind data from the bind array. */
+static void compactSparseBinds(SurfaceDeformModifierData *smd)
+{
+ smd->num_bind_verts = 0;
+
+ for (uint i = 0; i < smd->num_mesh_verts; i++) {
+ if (smd->verts[i].numbinds > 0) {
+ smd->verts[smd->num_bind_verts++] = smd->verts[i];
+ }
+ }
+
+ smd->verts = MEM_reallocN_id(
+ smd->verts, sizeof(*smd->verts) * smd->num_bind_verts, "SDefBindVerts (sparse)");
+}
+
static bool surfacedeformBind(Object *ob,
SurfaceDeformModifierData *smd_orig,
SurfaceDeformModifierData *smd_eval,
@@ -1142,7 +1182,8 @@ static bool surfacedeformBind(Object *ob,
uint numverts,
uint tnumpoly,
uint tnumverts,
- Mesh *target)
+ Mesh *target,
+ Mesh *mesh)
{
BVHTreeFromMesh treeData = {NULL};
const MVert *mvert = target->mvert;
@@ -1205,9 +1246,15 @@ static bool surfacedeformBind(Object *ob,
return false;
}
- smd_orig->numverts = numverts;
+ smd_orig->num_mesh_verts = numverts;
smd_orig->numpoly = tnumpoly;
+ int defgrp_index;
+ MDeformVert *dvert;
+ MOD_get_vgroup(ob, mesh, smd_orig->defgrp_name, &dvert, &defgrp_index);
+ const bool invert_vgroup = (smd_orig->flags & MOD_SDEF_INVERT_VGROUP) != 0;
+ const bool sparse_bind = (smd_orig->flags & MOD_SDEF_SPARSE_BIND) != 0;
+
SDefBindCalcData data = {
.treeData = &treeData,
.vert_edges = vert_edges,
@@ -1221,6 +1268,10 @@ static bool surfacedeformBind(Object *ob,
.vertexCos = vertexCos,
.falloff = smd_orig->falloff,
.success = MOD_SDEF_BIND_RESULT_SUCCESS,
+ .dvert = dvert,
+ .defgrp_index = defgrp_index,
+ .invert_vgroup = invert_vgroup,
+ .sparse_bind = sparse_bind,
};
if (data.targetCos == NULL) {
@@ -1242,6 +1293,13 @@ static bool surfacedeformBind(Object *ob,
MEM_freeN(data.targetCos);
+ if (sparse_bind) {
+ compactSparseBinds(smd_orig);
+ }
+ else {
+ smd_orig->num_bind_verts = numverts;
+ }
+
if (data.success == MOD_SDEF_BIND_RESULT_MEM_ERR) {
BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "Out of memory");
freeData((ModifierData *)smd_orig);
@@ -1267,6 +1325,11 @@ static bool surfacedeformBind(Object *ob,
BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "Target contains invalid polygons");
freeData((ModifierData *)smd_orig);
}
+ else if (smd_orig->num_bind_verts == 0 || !smd_orig->verts) {
+ data.success = MOD_SDEF_BIND_RESULT_GENERIC_ERR;
+ BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "No vertices were bound");
+ freeData((ModifierData *)smd_orig);
+ }
freeAdjacencyMap(vert_edges, adj_array, edge_polys);
free_bvhtree_from_mesh(&treeData);
@@ -1281,14 +1344,15 @@ static void deformVert(void *__restrict userdata,
const SDefDeformData *const data = (SDefDeformData *)userdata;
const SDefBind *sdbind = data->bind_verts[index].binds;
const int num_binds = data->bind_verts[index].numbinds;
- float *const vertexCos = data->vertexCos[index];
+ const unsigned int vertex_idx = data->bind_verts[index].vertex_idx;
+ float *const vertexCos = data->vertexCos[vertex_idx];
float norm[3], temp[3], offset[3];
/* Retrieve the value of the weight vertex group if specified. */
float weight = 1.0f;
if (data->dvert && data->defgrp_index != -1) {
- weight = BKE_defvert_find_weight(&data->dvert[index], data->defgrp_index);
+ weight = BKE_defvert_find_weight(&data->dvert[vertex_idx], data->defgrp_index);
if (data->invert_vgroup) {
weight = 1.0f - weight;
@@ -1423,7 +1487,8 @@ static void surfacedeformModifier_do(ModifierData *md,
/* Avoid converting edit-mesh data, binding is an exception. */
BKE_mesh_wrapper_ensure_mdata(target);
- if (!surfacedeformBind(ob, smd_orig, smd, vertexCos, numverts, tnumpoly, tnumverts, target)) {
+ if (!surfacedeformBind(
+ ob, smd_orig, smd, vertexCos, numverts, tnumpoly, tnumverts, target, mesh)) {
smd->flags &= ~MOD_SDEF_BIND;
}
/* Early abort, this is binding 'call', no need to perform whole evaluation. */
@@ -1431,8 +1496,9 @@ static void surfacedeformModifier_do(ModifierData *md,
}
/* Poly count checks */
- if (smd->numverts != numverts) {
- BKE_modifier_set_error(ob, md, "Vertices changed from %u to %u", smd->numverts, numverts);
+ if (smd->num_mesh_verts != numverts) {
+ BKE_modifier_set_error(
+ ob, md, "Vertices changed from %u to %u", smd->num_mesh_verts, numverts);
return;
}
if (smd->numpoly != tnumpoly) {
@@ -1468,8 +1534,8 @@ static void surfacedeformModifier_do(ModifierData *md,
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
- settings.use_threading = (numverts > 10000);
- BLI_task_parallel_range(0, numverts, &data, deformVert, &settings);
+ settings.use_threading = (smd->num_bind_verts > 10000);
+ BLI_task_parallel_range(0, smd->num_bind_verts, &data, deformVert, &settings);
MEM_freeN(data.targetCos);
}
@@ -1554,6 +1620,11 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL);
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetEnabled(col, !is_bound);
+ uiLayoutSetActive(col, !is_bound && RNA_string_length(ptr, "vertex_group") != 0);
+ uiItemR(col, ptr, "use_sparse_bind", 0, NULL, ICON_NONE);
+
uiItemS(layout);
col = uiLayoutColumn(layout, false);
@@ -1576,10 +1647,10 @@ static void blendWrite(BlendWriter *writer, const ModifierData *md)
{
const SurfaceDeformModifierData *smd = (const SurfaceDeformModifierData *)md;
- BLO_write_struct_array(writer, SDefVert, smd->numverts, smd->verts);
+ BLO_write_struct_array(writer, SDefVert, smd->num_bind_verts, smd->verts);
if (smd->verts) {
- for (int i = 0; i < smd->numverts; i++) {
+ for (int i = 0; i < smd->num_bind_verts; i++) {
BLO_write_struct_array(writer, SDefBind, smd->verts[i].numbinds, smd->verts[i].binds);
if (smd->verts[i].binds) {
@@ -1607,7 +1678,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
BLO_read_data_address(reader, &smd->verts);
if (smd->verts) {
- for (int i = 0; i < smd->numverts; i++) {
+ for (int i = 0; i < smd->num_bind_verts; i++) {
BLO_read_data_address(reader, &smd->verts[i].binds);
if (smd->verts[i].binds) {
diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c
index 9245afdb096..6239ee45e59 100644
--- a/source/blender/modifiers/intern/MOD_ui_common.c
+++ b/source/blender/modifiers/intern/MOD_ui_common.c
@@ -9,7 +9,7 @@
* 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,
+ * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index bbdd7d0e647..3b147c69716 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -623,7 +623,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
CustomData *ldata = &result->ldata;
clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL);
- /* Keep info whether we had clnors,
+ /* Keep info whether we had clnors,
* it helps when generating clnor spaces and default normals. */
const bool has_clnors = clnors != NULL;
if (!clnors) {
diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c
index 696c4c855c7..e403051d1be 100644
--- a/source/blender/modifiers/intern/MOD_weightvg_util.c
+++ b/source/blender/modifiers/intern/MOD_weightvg_util.c
@@ -167,9 +167,9 @@ void weightvg_do_mask(const ModifierEvalContext *ctx,
const int numVerts = mesh->totvert;
/* Use new generic get_texture_coords, but do not modify our DNA struct for it...
- * XXX Why use a ModifierData stuff here ? Why not a simple, generic struct for parameters ?
- * What e.g. if a modifier wants to use several textures ?
- * Why use only v_co, and not MVert (or both) ?
+ * XXX Why use a ModifierData stuff here ? Why not a simple, generic struct for parameters?
+ * What e.g. if a modifier wants to use several textures?
+ * Why use only v_co, and not MVert (or both)?
*/
t_map.texture = texture;
t_map.map_object = tex_map_object;
diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c
index fe2d699aea8..b1fa2a7d912 100644
--- a/source/blender/modifiers/intern/MOD_weld.c
+++ b/source/blender/modifiers/intern/MOD_weld.c
@@ -1735,6 +1735,9 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd,
uint v1 = me->v1;
uint v2 = me->v2;
+ if (wmd->flag & MOD_WELD_LOOSE_EDGES && (me->flag & ME_LOOSEEDGE) == 0) {
+ continue;
+ }
while (v1 != vert_dest_map[v1]) {
v1 = vert_dest_map[v1];
}
@@ -2019,11 +2022,15 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
+ int weld_mode = RNA_enum_get(ptr, "mode");
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE);
+ if (weld_mode == MOD_WELD_MODE_CONNECTED) {
+ uiItemR(layout, ptr, "loose_edges", 0, NULL, ICON_NONE);
+ }
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL);
modifier_panel_end(layout, ptr);
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index dc19508be04..36e5be6a292 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -175,9 +175,11 @@ set(SRC
geometry/nodes/node_geo_curve_primitive_star.cc
geometry/nodes/node_geo_curve_resample.cc
geometry/nodes/node_geo_curve_reverse.cc
+ geometry/nodes/node_geo_curve_set_handles.cc
geometry/nodes/node_geo_curve_subdivide.cc
geometry/nodes/node_geo_curve_to_mesh.cc
geometry/nodes/node_geo_curve_to_points.cc
+ geometry/nodes/node_geo_curve_trim.cc
geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_input_material.cc
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index ad3a838f4c0..868fcbb33af 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -62,9 +62,11 @@ void register_node_type_geo_curve_primitive_spiral(void);
void register_node_type_geo_curve_primitive_star(void);
void register_node_type_geo_curve_resample(void);
void register_node_type_geo_curve_reverse(void);
+void register_node_type_geo_curve_set_handles(void);
void register_node_type_geo_curve_subdivide(void);
void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_curve_to_points(void);
+void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_input_material(void);
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
index b85862a0176..00d97b24646 100644
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
@@ -31,6 +31,7 @@
*/
#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_function_ref.hh"
#include "BLI_linear_allocator.hh"
#include "BLI_map.hh"
@@ -267,6 +268,7 @@ class TreeLog {
const NodeLog *lookup_node_log(StringRef node_name) const;
const NodeLog *lookup_node_log(const bNode &node) const;
const TreeLog *lookup_child_log(StringRef node_name) const;
+ void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const;
};
/** Contains information about an entire geometry nodes evaluation. */
@@ -296,6 +298,7 @@ class ModifierLog {
const bNodeSocket &socket);
static const NodeLog *find_node_by_spreadsheet_editor_context(
const SpaceSpreadsheet &sspreadsheet);
+ void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const;
private:
using LogByTreeContext = Map<const DTreeContext *, TreeLog *>;
diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh
index f3eb1c24087..9443be820d1 100644
--- a/source/blender/nodes/NOD_math_functions.hh
+++ b/source/blender/nodes/NOD_math_functions.hh
@@ -266,7 +266,7 @@ inline bool try_dispatch_float_math_fl3_fl3_to_fl3(const NodeVectorMathOperation
return dispatch([](float3 a, float3 b) { return float3::cross_high_precision(a, b); });
case NODE_VECTOR_MATH_PROJECT:
return dispatch([](float3 a, float3 b) {
- float length_squared = float3::dot(a, b);
+ float length_squared = b.length_squared();
return (length_squared != 0.0) ? (float3::dot(a, b) / length_squared) * b : float3(0.0f);
});
case NODE_VECTOR_MATH_REFLECT:
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
index e3f31e011e4..7eeeaef0b98 100644
--- a/source/blender/nodes/NOD_node_tree_multi_function.hh
+++ b/source/blender/nodes/NOD_node_tree_multi_function.hh
@@ -233,7 +233,7 @@ class MFNetworkBuilderBase {
/**
* Constructs a new function that will live at least as long as the MFNetwork.
*/
- template<typename T, typename... Args> T &construct_fn(Args &&... args)
+ template<typename T, typename... Args> T &construct_fn(Args &&...args)
{
BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T));
@@ -287,7 +287,7 @@ class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value);
}
- template<typename T, typename... Args> void construct_generator_fn(Args &&... args)
+ template<typename T, typename... Args> void construct_generator_fn(Args &&...args)
{
const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(args)...);
this->set_generator_fn(fn);
@@ -334,7 +334,7 @@ class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
* Tells the builder to build a function that corresponds to the node that is being built. It
* will try to match up sockets.
*/
- template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&... args)
+ template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&...args)
{
T &function = this->construct_fn<T>(std::forward<Args>(args)...);
this->set_matching_fn(function);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 73d4a002991..a091f28f3a0 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -156,7 +156,7 @@ DefNode(CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SA
DefNode(CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" )
DefNode(CompositorNode, CMP_NODE_R_LAYERS, def_cmp_render_layers, "R_LAYERS", RLayers, "Render Layers", "" )
DefNode(CompositorNode, CMP_NODE_COMPOSITE, def_cmp_composite, "COMPOSITE", Composite, "Composite", "" )
-/* NB: OutputFile node has special rna setup function called in rna_nodetree.c */
+/* NOTE: #OutputFile node has special RNA setup function called in rna_nodetree.c */
DefNode(CompositorNode, CMP_NODE_OUTPUT_FILE, 0, "OUTPUT_FILE", OutputFile, "File Output", "" )
DefNode(CompositorNode, CMP_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" )
DefNode(CompositorNode, CMP_NODE_TRANSLATE, def_cmp_translate, "TRANSLATE", Translate, "Translate", "" )
@@ -291,20 +291,22 @@ DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Bo
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "")
DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
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", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "")
DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "")
DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
@@ -320,6 +322,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
@@ -332,7 +335,6 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
-DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index 013d196e1c8..d21e0938356 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -264,14 +264,15 @@ void ntreeCompositExecTree(Scene *scene,
/* *********************************************** */
-/* Update the outputs of the render layer nodes.
+/**
+ * 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.
+ * - #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 ntreeCompositRegisterPass,.
- * which calls node_cmp_rlayers_register_pass for every render layer node.
+ * - #RE_engine_register_pass calls #ntreeCompositRegisterPass,
+ * which calls #node_cmp_rlayers_register_pass for every render layer node.
*/
void ntreeCompositUpdateRLayers(bNodeTree *ntree)
{
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
index 7437496d878..81e2408fcf9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
@@ -25,8 +25,8 @@
* ***** END GPL LICENSE BLOCK *****
*/
-/** \file blender/nodes/composite/nodes/node_composite_antialiasing.c
- * \ingroup cmpnodes
+/** \file
+ * \ingroup cmpnodes
*/
#include "node_composite_util.h"
@@ -42,9 +42,9 @@ static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *nod
{
NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data");
- data->threshold = 1.0f;
- data->contrast_limit = 0.2f;
- data->corner_rounding = 0.25f;
+ data->threshold = CMP_DEFAULT_SMAA_THRESHOLD;
+ data->contrast_limit = CMP_DEFAULT_SMAA_CONTRAST_LIMIT;
+ data->corner_rounding = CMP_DEFAULT_SMAA_CORNER_ROUNDING;
node->storage = data;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_common.c b/source/blender/nodes/composite/nodes/node_composite_common.c
index f5eaaef8a31..61abc80fe93 100644
--- a/source/blender/nodes/composite/nodes/node_composite_common.c
+++ b/source/blender/nodes/composite/nodes/node_composite_common.c
@@ -36,9 +36,8 @@ void register_node_type_cmp_group(void)
{
static bNodeType ntype;
- /* NB: cannot use sh_node_type_base for node group, because it would map the node type
- * to the shared NODE_GROUP integer type id.
- */
+ /* NOTE: Cannot use sh_node_type_base for node group, because it would map the node type
+ * to the shared NODE_GROUP integer type id. */
node_type_base_custom(
&ntype, "CompositorNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT);
ntype.type = NODE_GROUP;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index 9309863f4e9..368c4025eb7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -16,6 +16,8 @@
#include "BLI_task.hh"
+#include "RNA_enum_types.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -132,6 +134,17 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node)
namespace blender::nodes {
+static void geo_node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen)
+{
+ NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage;
+ const char *name;
+ bool enum_label = RNA_enum_name(rna_enum_node_math_items, node_storage.operation, &name);
+ if (!enum_label) {
+ name = "Unknown";
+ }
+ BLI_strncpy(label, IFACE_(name), maxlen);
+}
+
static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage;
@@ -296,6 +309,7 @@ void register_node_type_geo_attribute_math()
node_type_socket_templates(&ntype, geo_node_attribute_math_in, geo_node_attribute_math_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec;
ntype.draw_buttons = geo_node_attribute_math_layout;
+ node_type_label(&ntype, blender::nodes::geo_node_math_label);
node_type_update(&ntype, blender::nodes::geo_node_attribute_math_update);
node_type_init(&ntype, geo_node_attribute_math_init);
node_type_storage(
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
index 756f93f154f..ab2136f4e8d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
@@ -204,7 +204,8 @@ static void get_closest_mesh_polygons(const Mesh &mesh,
Array<int> looptri_indices(positions.size());
get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions);
- Span<MLoopTri> looptris = bke::mesh_surface_sample::get_mesh_looptris(mesh);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
for (const int i : positions.index_range()) {
const MLoopTri &looptri = looptris[looptri_indices[i]];
r_poly_indices[i] = looptri.poly;
@@ -370,10 +371,12 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
break;
}
case ATTR_DOMAIN_CORNER: {
- use_mesh = true;
- mesh_indices.reinitialize(tot_samples);
- mesh_distances_sq.reinitialize(tot_samples);
- get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ if (mesh->totloop > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
break;
}
default: {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
index e017786ae89..be70ebdebfe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -17,6 +17,8 @@
#include "BLI_math_base_safe.h"
#include "BLI_task.hh"
+#include "RNA_enum_types.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -154,6 +156,20 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op
namespace blender::nodes {
+static void geo_node_vector_math_label(bNodeTree *UNUSED(ntree),
+ bNode *node,
+ char *label,
+ int maxlen)
+{
+ NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage;
+ const char *name;
+ bool enum_label = RNA_enum_name(rna_enum_node_vec_math_items, node_storage.operation, &name);
+ if (!enum_label) {
+ name = "Unknown";
+ }
+ BLI_snprintf(label, maxlen, IFACE_("Vector %s"), IFACE_(name));
+}
+
static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node)
{
const NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
@@ -549,6 +565,7 @@ void register_node_type_geo_attribute_vector_math()
&ntype, geo_node_attribute_vector_math_in, geo_node_attribute_vector_math_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec;
ntype.draw_buttons = geo_node_attribute_vector_math_layout;
+ node_type_label(&ntype, blender::nodes::geo_node_vector_math_label);
node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_math_update);
node_type_init(&ntype, geo_node_attribute_vector_math_init);
node_type_storage(
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
index e167219ea6b..78b5b109419 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
@@ -74,7 +74,7 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve(
if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) {
spline->add_point(start,
BezierSpline::HandleType::Align,
- start - (start_handle_right - start) * -1.0f,
+ 2.0f * start - start_handle_right,
BezierSpline::HandleType::Align,
start_handle_right,
1.0f,
@@ -83,7 +83,7 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve(
BezierSpline::HandleType::Align,
end_handle_left,
BezierSpline::HandleType::Align,
- end - (end_handle_left - end) * -1.0f,
+ 2.0f * end - end_handle_left,
1.0f,
0.0f);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
new file mode 100644
index 00000000000..72bd8ab188d
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
@@ -0,0 +1,149 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_set_handles_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_set_handles_out[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+static void geo_node_curve_set_handles_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN(
+ sizeof(NodeGeometryCurveSetHandles), __func__);
+
+ data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
+ node->storage = data;
+}
+
+static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
+{
+ switch (type) {
+ case GEO_NODE_CURVE_HANDLE_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case GEO_NODE_CURVE_HANDLE_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case GEO_NODE_CURVE_HANDLE_FREE:
+ return BezierSpline::HandleType::Free;
+ case GEO_NODE_CURVE_HANDLE_VECTOR:
+ return BezierSpline::HandleType::Vector;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSetHandles *node_storage =
+ (NodeGeometryCurveSetHandles *)params.node().storage;
+ const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)node_storage->handle_type;
+ const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", geometry_set);
+ return;
+ }
+
+ /* Retrieve data for write access so we can avoid new allocations for the handles data. */
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
+ selection_name, ATTR_DOMAIN_POINT, true);
+
+ const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
+ int point_index = 0;
+ bool has_bezier_spline = false;
+ for (SplinePtr &spline : splines) {
+ if (spline->type() != Spline::Type::Bezier) {
+ point_index += spline->positions().size();
+ continue;
+ }
+
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
+ if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
+ /* In this case the automatically calculated handle types need to be "baked", because
+ * they're possibly changing from a type that is calculated automatically to a type that
+ * is positioned manually. */
+ bezier_spline.ensure_auto_handles();
+ }
+ has_bezier_spline = true;
+ for (int i_point : IndexRange(bezier_spline.size())) {
+ if (selection[point_index]) {
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ bezier_spline.handle_types_left()[i_point] = new_handle_type;
+ }
+ if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
+ bezier_spline.handle_types_right()[i_point] = new_handle_type;
+ }
+ }
+ point_index++;
+ }
+ bezier_spline.mark_cache_invalid();
+ }
+
+ if (!has_bezier_spline) {
+ params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
+ }
+
+ params.set_output("Curve", geometry_set);
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_set_handles()
+{
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_curve_set_handles_in, geo_node_curve_set_handles_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSetHandles",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = geo_node_curve_set_handles_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index f1bcb4ed47f..ae5ad4e350b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -47,27 +47,27 @@ static void vert_extrude_to_mesh_data(const Spline &spline,
const float3 profile_vert,
MutableSpan<MVert> r_verts,
MutableSpan<MEdge> r_edges,
- int vert_offset,
- int edge_offset)
+ const int vert_offset,
+ const int edge_offset)
{
Span<float3> positions = spline.evaluated_positions();
for (const int i : IndexRange(positions.size() - 1)) {
- MEdge &edge = r_edges[edge_offset++];
+ MEdge &edge = r_edges[edge_offset + i];
edge.v1 = vert_offset + i;
edge.v2 = vert_offset + i + 1;
edge.flag = ME_LOOSEEDGE;
}
if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
- MEdge &edge = r_edges[edge_offset++];
+ MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1];
edge.v1 = vert_offset;
edge.v2 = vert_offset + positions.size() - 1;
edge.flag = ME_LOOSEEDGE;
}
for (const int i : positions.index_range()) {
- MVert &vert = r_verts[vert_offset++];
+ MVert &vert = r_verts[vert_offset + i];
copy_v3_v3(vert.co, positions[i] + profile_vert);
}
}
@@ -81,14 +81,14 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges)
static void spline_extrude_to_mesh_data(const Spline &spline,
const Spline &profile_spline,
+ const int vert_offset,
+ const int edge_offset,
+ const int loop_offset,
+ const int poly_offset,
MutableSpan<MVert> r_verts,
MutableSpan<MEdge> r_edges,
MutableSpan<MLoop> r_loops,
- MutableSpan<MPoly> r_polys,
- int vert_offset,
- int edge_offset,
- int loop_offset,
- int poly_offset)
+ MutableSpan<MPoly> r_polys)
{
const int spline_vert_len = spline.evaluated_points_size();
const int spline_edge_len = spline.evaluated_edges_size();
@@ -111,13 +111,14 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
/* Add the edges running along the length of the curve, starting at each profile vertex. */
const int spline_edges_start = edge_offset;
for (const int i_profile : IndexRange(profile_vert_len)) {
+ const int profile_edge_offset = spline_edges_start + i_profile * spline_edge_len;
for (const int i_ring : IndexRange(spline_edge_len)) {
const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
- MEdge &edge = r_edges[edge_offset++];
+ MEdge &edge = r_edges[profile_edge_offset + i_ring];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = next_ring_vert_offset + i_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -125,14 +126,15 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
}
/* Add the edges running along each profile ring. */
- const int profile_edges_start = edge_offset;
+ const int profile_edges_start = spline_edges_start + profile_vert_len * spline_edge_len;
for (const int i_ring : IndexRange(spline_vert_len)) {
const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int ring_edge_offset = profile_edges_start + i_ring * profile_edge_len;
for (const int i_profile : IndexRange(profile_edge_len)) {
const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
- MEdge &edge = r_edges[edge_offset++];
+ MEdge &edge = r_edges[ring_edge_offset + i_profile];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = ring_vert_offset + i_next_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -149,29 +151,33 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
+ const int ring_poly_offset = poly_offset + i_ring * profile_edge_len;
+ const int ring_loop_offset = loop_offset + i_ring * profile_edge_len * 4;
+
for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4;
const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
- MPoly &poly = r_polys[poly_offset++];
- poly.loopstart = loop_offset;
+ MPoly &poly = r_polys[ring_poly_offset + i_profile];
+ poly.loopstart = ring_segment_loop_offset;
poly.totloop = 4;
poly.flag = ME_SMOOTH;
- MLoop &loop_a = r_loops[loop_offset++];
+ MLoop &loop_a = r_loops[ring_segment_loop_offset];
loop_a.v = ring_vert_offset + i_profile;
- loop_a.e = ring_edge_start + i_profile;
- MLoop &loop_b = r_loops[loop_offset++];
- loop_b.v = ring_vert_offset + i_next_profile;
- loop_b.e = next_spline_edge_start + i_ring;
- MLoop &loop_c = r_loops[loop_offset++];
+ loop_a.e = spline_edge_start + i_ring;
+ MLoop &loop_b = r_loops[ring_segment_loop_offset + 1];
+ loop_b.v = next_ring_vert_offset + i_profile;
+ loop_b.e = next_ring_edge_offset + i_profile;
+ MLoop &loop_c = r_loops[ring_segment_loop_offset + 2];
loop_c.v = next_ring_vert_offset + i_next_profile;
- loop_c.e = next_ring_edge_offset + i_profile;
- MLoop &loop_d = r_loops[loop_offset++];
- loop_d.v = next_ring_vert_offset + i_profile;
- loop_d.e = spline_edge_start + i_ring;
+ loop_c.e = next_spline_edge_start + i_ring;
+ MLoop &loop_d = r_loops[ring_segment_loop_offset + 3];
+ loop_d.v = ring_vert_offset + i_next_profile;
+ loop_d.e = ring_edge_start + i_profile;
}
}
@@ -185,11 +191,11 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
for (const int i_ring : IndexRange(spline_vert_len)) {
float4x4 point_matrix = float4x4::from_normalized_axis_data(
positions[i_ring], normals[i_ring], tangents[i_ring]);
-
point_matrix.apply_scale(radii[i_ring]);
+ const int ring_vert_start = vert_offset + i_ring * profile_vert_len;
for (const int i_profile : IndexRange(profile_vert_len)) {
- MVert &vert = r_verts[vert_offset++];
+ MVert &vert = r_verts[ring_vert_start + i_profile];
copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
}
}
@@ -275,7 +281,7 @@ static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<Spl
* Although it would be a sensible decision to use the better topology information available while
* generating the mesh to also generate the normals, that work may wasted if the output mesh is
* changed anyway in a way that affects the normals. So currently this code uses the safer /
- * simpler solution of not calculating normals.
+ * simpler solution of deferring normal calculation to the rest of Blender.
*/
static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile)
{
@@ -303,14 +309,14 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr
const int i_mesh = spline_start_index + i_profile;
spline_extrude_to_mesh_data(*curves[i_spline],
*profiles[i_profile],
- {mesh->mvert, mesh->totvert},
- {mesh->medge, mesh->totedge},
- {mesh->mloop, mesh->totloop},
- {mesh->mpoly, mesh->totpoly},
offsets.vert[i_mesh],
offsets.edge[i_mesh],
offsets.loop[i_mesh],
- offsets.poly[i_mesh]);
+ offsets.poly[i_mesh],
+ {mesh->mvert, mesh->totvert},
+ {mesh->medge, mesh->totedge},
+ {mesh->mloop, mesh->totloop},
+ {mesh->mpoly, mesh->totpoly});
}
});
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
new file mode 100644
index 00000000000..f9415c6d27b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -0,0 +1,410 @@
+/*
+ * 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_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::attribute_math::mix2;
+
+static bNodeSocketTemplate geo_node_curve_trim_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
+ {SOCK_FLOAT, N_("End"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
+ {SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 10000.0f, PROP_DISTANCE},
+ {SOCK_FLOAT, N_("End"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_DISTANCE},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_trim_out[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim),
+ __func__);
+
+ data->mode = GEO_NODE_CURVE_INTERPOLATE_FACTOR;
+ node->storage = data;
+}
+
+static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)node->storage;
+ const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode)
+ node_storage.mode;
+
+ bNodeSocket *start_fac = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *end_fac = start_fac->next;
+ bNodeSocket *start_len = end_fac->next;
+ bNodeSocket *end_len = start_len->next;
+
+ nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR);
+ nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR);
+ nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH);
+ nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH);
+}
+
+namespace blender::nodes {
+
+struct TrimLocation {
+ /* Control point index at the start side of the trim location. */
+ int left_index;
+ /* Control point index at the end of the trim location's segment. */
+ int right_index;
+ /* The factor between the left and right indices. */
+ float factor;
+};
+
+template<typename T>
+static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int size)
+{
+ BLI_assert(start_index + size - 1 <= data.size());
+ memmove(data.data(), &data[start_index], sizeof(T) * size);
+}
+
+/* Shift slice to start of span and modifies start and end data. */
+template<typename T>
+static void linear_trim_data(const TrimLocation &start,
+ const TrimLocation &end,
+ MutableSpan<T> data)
+{
+ const int size = end.right_index - start.left_index + 1;
+
+ if (start.left_index > 0) {
+ shift_slice_to_start<T>(data, start.left_index, size);
+ }
+
+ const T start_data = mix2<T>(start.factor, data.first(), data[1]);
+ const T end_data = mix2<T>(end.factor, data[size - 2], data[size - 1]);
+
+ data.first() = start_data;
+ data[size - 1] = end_data;
+}
+
+/**
+ * Identical operation as #linear_trim_data, but copy data to a new #MutableSpan rather than
+ * modifying the original data.
+ */
+template<typename T>
+static void linear_trim_to_output_data(const TrimLocation &start,
+ const TrimLocation &end,
+ Span<T> src,
+ MutableSpan<T> dst)
+{
+ const int size = end.right_index - start.left_index + 1;
+
+ const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]);
+ const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]);
+
+ dst.copy_from(src.slice(start.left_index, size));
+ dst.first() = start_data;
+ dst.last() = end_data;
+}
+
+/* Look up the control points to the left and right of factor, and get the factor between them. */
+static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup,
+ Span<int> control_point_offsets)
+{
+ const int *left_offset = std::lower_bound(
+ control_point_offsets.begin(), control_point_offsets.end(), lookup.evaluated_index);
+ const int index = left_offset - control_point_offsets.begin();
+ const int left = control_point_offsets[index] > lookup.evaluated_index ? index - 1 : index;
+ const int right = left + 1;
+
+ const float factor = std::clamp(
+ (lookup.evaluated_index + lookup.factor - control_point_offsets[left]) /
+ (control_point_offsets[right] - control_point_offsets[left]),
+ 0.0f,
+ 1.0f);
+
+ return {left, right, factor};
+}
+
+static void trim_poly_spline(Spline &spline,
+ const Spline::LookupResult &start_lookup,
+ const Spline::LookupResult &end_lookup)
+{
+ /* Poly splines have a 1 to 1 mapping between control points and evaluated points. */
+ const TrimLocation start = {
+ start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor};
+ const TrimLocation end = {
+ end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor};
+
+ const int size = end.right_index - start.left_index + 1;
+
+ linear_trim_data<float3>(start, end, spline.positions());
+ linear_trim_data<float>(start, end, spline.radii());
+ linear_trim_data<float>(start, end, spline.tilts());
+
+ spline.attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) {
+ std::optional<GMutableSpan> src = spline.attributes.get_for_write(name);
+ BLI_assert(src);
+ attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ linear_trim_data<T>(start, end, src->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ spline.resize(size);
+}
+
+/**
+ * Trim NURB splines by converting to a poly spline.
+ */
+static PolySpline trim_nurbs_spline(const Spline &spline,
+ const Spline::LookupResult &start_lookup,
+ const Spline::LookupResult &end_lookup)
+{
+ /* Since this outputs a poly spline, the evaluated indices are the control point indices. */
+ const TrimLocation start = {
+ start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor};
+ const TrimLocation end = {
+ end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor};
+
+ const int size = end.right_index - start.left_index + 1;
+
+ /* Create poly spline and copy trimmed data to it. */
+ PolySpline new_spline;
+ new_spline.resize(size);
+
+ /* Copy generic attribute data. */
+ spline.attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = spline.attributes.get_for_read(name);
+ BLI_assert(src);
+ if (!new_spline.attributes.create(name, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(name);
+ BLI_assert(dst);
+
+ attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ GVArray_Typed<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>());
+ linear_trim_to_output_data<T>(
+ start, end, eval_data->get_internal_span(), dst->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ linear_trim_to_output_data<float3>(
+ start, end, spline.evaluated_positions(), new_spline.positions());
+
+ GVArray_Typed<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii());
+ linear_trim_to_output_data<float>(
+ start, end, evaluated_radii->get_internal_span(), new_spline.radii());
+
+ GVArray_Typed<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts());
+ linear_trim_to_output_data<float>(
+ start, end, evaluated_tilts->get_internal_span(), new_spline.tilts());
+
+ return new_spline;
+}
+
+/**
+ * Trim Bezier splines by adjusting the first and last handles
+ * and control points to maintain the original shape.
+ */
+static void trim_bezier_spline(Spline &spline,
+ const Spline::LookupResult &start_lookup,
+ const Spline::LookupResult &end_lookup)
+{
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
+ Span<int> control_offsets = bezier_spline.control_point_offsets();
+
+ const TrimLocation start = lookup_control_point_position(start_lookup, control_offsets);
+ TrimLocation end = lookup_control_point_position(end_lookup, control_offsets);
+
+ /* The number of control points in the resulting spline. */
+ const int size = end.right_index - start.left_index + 1;
+
+ /* Trim the spline attributes. Done before end.factor recalculation as it needs
+ * the original end.factor value. */
+ linear_trim_data<float>(start, end, bezier_spline.radii());
+ linear_trim_data<float>(start, end, bezier_spline.tilts());
+ spline.attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) {
+ std::optional<GMutableSpan> src = spline.attributes.get_for_write(name);
+ BLI_assert(src);
+ attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ linear_trim_data<T>(start, end, src->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ /* Recalculate end.factor if the size is two, because the adjustment in the
+ * position of the control point of the spline to the left of the new end point will change the
+ * factor between them. */
+ if (size == 2) {
+ if (start_lookup.factor == 1.0f) {
+ end.factor = 0.0f;
+ }
+ else {
+ end.factor = (end_lookup.evaluated_index + end_lookup.factor -
+ (start_lookup.evaluated_index + start_lookup.factor)) /
+ (control_offsets[end.right_index] -
+ (start_lookup.evaluated_index + start_lookup.factor));
+ end.factor = std::clamp(end.factor, 0.0f, 1.0f);
+ }
+ }
+
+ BezierSpline::InsertResult start_point = bezier_spline.calculate_segment_insertion(
+ start.left_index, start.right_index, start.factor);
+
+ /* Update the start control point parameters so they are used calculating the new end point. */
+ bezier_spline.positions()[start.left_index] = start_point.position;
+ bezier_spline.handle_positions_right()[start.left_index] = start_point.right_handle;
+ bezier_spline.handle_positions_left()[start.right_index] = start_point.handle_next;
+
+ const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion(
+ end.left_index, end.right_index, end.factor);
+
+ /* If size is two, then the start point right handle needs to change to reflect the end point
+ * previous handle update. */
+ if (size == 2) {
+ start_point.right_handle = end_point.handle_prev;
+ }
+
+ /* Shift control point position data to start at beginning of array. */
+ if (start.left_index > 0) {
+ shift_slice_to_start(bezier_spline.positions(), start.left_index, size);
+ shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, size);
+ shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, size);
+ }
+
+ bezier_spline.positions().first() = start_point.position;
+ bezier_spline.positions()[size - 1] = end_point.position;
+
+ bezier_spline.handle_positions_left().first() = start_point.left_handle;
+ bezier_spline.handle_positions_left()[size - 1] = end_point.left_handle;
+
+ bezier_spline.handle_positions_right().first() = start_point.right_handle;
+ bezier_spline.handle_positions_right()[size - 1] = end_point.right_handle;
+
+ /* If there is at least one control point between the endpoints, update the control
+ * point handle to the right of the start point and to the left of the end point. */
+ if (size > 2) {
+ bezier_spline.handle_positions_left()[start.right_index - start.left_index] =
+ start_point.handle_next;
+ bezier_spline.handle_positions_right()[end.left_index - start.left_index] =
+ end_point.handle_prev;
+ }
+
+ bezier_spline.resize(size);
+}
+
+static void geo_node_curve_trim_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage;
+ const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode)
+ node_storage.mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", std::move(geometry_set));
+ return;
+ }
+
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ const float start = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ?
+ params.extract_input<float>("Start") :
+ params.extract_input<float>("Start_001");
+ const float end = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ?
+ params.extract_input<float>("End") :
+ params.extract_input<float>("End_001");
+
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ Spline &spline = *splines[i];
+
+ /* Currently this node doesn't support cyclic splines, it could in the future though. */
+ if (spline.is_cyclic()) {
+ continue;
+ }
+
+ /* Return a spline with one point instead of implicitly
+ * reversing the spline or switching the parameters. */
+ if (end < start) {
+ spline.resize(1);
+ continue;
+ }
+
+ const Spline::LookupResult start_lookup =
+ (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ?
+ spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) :
+ spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f));
+ const Spline::LookupResult end_lookup =
+ (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ?
+ spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) :
+ spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f));
+
+ switch (spline.type()) {
+ case Spline::Type::Bezier:
+ trim_bezier_spline(spline, start_lookup, end_lookup);
+ break;
+ case Spline::Type::Poly:
+ trim_poly_spline(spline, start_lookup, end_lookup);
+ break;
+ case Spline::Type::NURBS:
+ splines[i] = std::make_unique<PolySpline>(
+ trim_nurbs_spline(spline, start_lookup, end_lookup));
+ break;
+ }
+ splines[i]->mark_cache_invalid();
+ }
+ });
+
+ params.set_output("Curve", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_trim()
+{
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_trim_in, geo_node_curve_trim_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec;
+ ntype.draw_buttons = geo_node_curve_trim_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, geo_node_curve_trim_init);
+ node_type_update(&ntype, geo_node_curve_trim_update);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index d456c72744f..99930b5ae46 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -81,13 +81,6 @@ static float3 normal_to_euler_rotation(const float3 normal)
return rotation;
}
-static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
-{
- const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(&mesh);
- const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
- return {looptris, looptris_len};
-}
-
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
@@ -97,7 +90,8 @@ static void sample_mesh_surface(const Mesh &mesh,
Vector<float3> &r_bary_coords,
Vector<int> &r_looptri_indices)
{
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
for (const int looptri_index : looptris.index_range()) {
const MLoopTri &looptri = looptris[looptri_index];
@@ -208,7 +202,8 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
Span<int> looptri_indices,
MutableSpan<bool> elimination_mask)
{
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
for (const int i : bary_coords.index_range()) {
if (elimination_mask[i]) {
continue;
@@ -365,7 +360,8 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
for (const float4x4 &transform : set_group.transforms) {
const int offset = instance_start_offsets[i_instance];
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index 3d5557d4049..f7279cf7524 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -73,7 +73,9 @@ void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
context->~DTreeContext();
}
-/* Returns true if there are any cycles in the node tree. */
+/**
+ * \return True when there is a link cycle. Unavailable sockets are ignored.
+ */
bool DerivedNodeTree::has_link_cycles() const
{
for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
index 3024cc51cad..7487f11d77d 100644
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
@@ -99,6 +99,13 @@ SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_c
return socket_log;
}
+void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
+{
+ if (root_tree_logs_) {
+ root_tree_logs_->foreach_node_log(fn);
+ }
+}
+
const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const
{
const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name);
@@ -122,6 +129,17 @@ const TreeLog *TreeLog::lookup_child_log(StringRef node_name) const
return tree_log->get();
}
+void TreeLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
+{
+ for (auto node_log : node_logs_.items()) {
+ fn(*node_log.value);
+ }
+
+ for (auto child : child_logs_.items()) {
+ child.value->foreach_node_log(fn);
+ }
+}
+
const SocketLog *NodeLog::lookup_socket_log(eNodeSocketInOut in_out, int index) const
{
BLI_assert(index >= 0);
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index 9ce9d6fc273..641d02af902 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -423,7 +423,13 @@ static bool has_link_cycles_recursive(const NodeRef &node,
is_in_stack[node_id] = true;
for (const OutputSocketRef *from_socket : node.outputs()) {
+ if (!from_socket->is_available()) {
+ continue;
+ }
for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
+ if (!to_socket->is_available()) {
+ continue;
+ }
const NodeRef &to_node = to_socket->node();
if (has_link_cycles_recursive(to_node, visited, is_in_stack)) {
return true;
@@ -435,6 +441,9 @@ static bool has_link_cycles_recursive(const NodeRef &node,
return false;
}
+/**
+ * \return True when there is a link cycle. Unavailable sockets are ignored.
+ */
bool NodeTreeRef::has_link_cycles() const
{
const int node_amount = nodes_by_id_.size();
diff --git a/source/blender/nodes/shader/nodes/node_shader_common.c b/source/blender/nodes/shader/nodes/node_shader_common.c
index a864bef60d9..4df5add7151 100644
--- a/source/blender/nodes/shader/nodes/node_shader_common.c
+++ b/source/blender/nodes/shader/nodes/node_shader_common.c
@@ -229,9 +229,9 @@ void register_node_type_sh_group(void)
{
static bNodeType ntype;
- /* NB: cannot use sh_node_type_base for node group, because it would map the node type
- * to the shared NODE_GROUP integer type id.
- */
+ /* NOTE: cannot use #sh_node_type_base for node group, because it would map the node type
+ * to the shared #NODE_GROUP integer type id. */
+
node_type_base_custom(&ntype, "ShaderNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT);
ntype.type = NODE_GROUP;
ntype.poll = sh_node_poll_default;
diff --git a/source/blender/nodes/texture/nodes/node_texture_common.c b/source/blender/nodes/texture/nodes/node_texture_common.c
index b87720be5b0..2de64779ea6 100644
--- a/source/blender/nodes/texture/nodes/node_texture_common.c
+++ b/source/blender/nodes/texture/nodes/node_texture_common.c
@@ -158,9 +158,9 @@ void register_node_type_tex_group(void)
{
static bNodeType ntype;
- /* NB: cannot use sh_node_type_base for node group, because it would map the node type
- * to the shared NODE_GROUP integer type id.
- */
+ /* NOTE: Cannot use #sh_node_type_base for node group, because it would map the node type
+ * to the shared #NODE_GROUP integer type id. */
+
node_type_base_custom(&ntype, "TextureNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT);
ntype.type = NODE_GROUP;
ntype.poll = tex_node_poll_default;
diff --git a/source/blender/nodes/texture/nodes/node_texture_rotate.c b/source/blender/nodes/texture/nodes/node_texture_rotate.c
index 06eb632378c..9985499772e 100644
--- a/source/blender/nodes/texture/nodes/node_texture_rotate.c
+++ b/source/blender/nodes/texture/nodes/node_texture_rotate.c
@@ -47,7 +47,7 @@ static void rotate(float new_co[3], float a, const float ax[3], const float co[3
float cos_a = cosf(a * (float)(2 * M_PI));
float sin_a = sinf(a * (float)(2 * M_PI));
- // x' = xcosa + n(n.x)(1-cosa) + (x*n)sina
+ /* `x' = xcosa + n(n.x)(1-cosa) + (x*n)sina`. */
mul_v3_v3fl(perp, co, cos_a);
mul_v3_v3fl(para, ax, dot_v3v3(co, ax) * (1 - cos_a));
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h
index 90f54c50a6d..43a73363c98 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -20,8 +20,8 @@
#pragma once
-struct AnimationEvalContext;
struct ARegionType;
+struct AnimationEvalContext;
struct ChannelDriver; /* DNA_anim_types.h */
struct ID; /* DNA_ID.h */
struct ListBase; /* DNA_listBase.h */
@@ -30,7 +30,7 @@ struct PathResolvedRNA;
struct Text; /* defined in DNA_text_types.h */
struct bConstraint; /* DNA_constraint_types.h */
struct bConstraintOb; /* DNA_constraint_types.h */
-struct bConstraintTarget; /* DNA_constraint_types.h*/
+struct bConstraintTarget; /* DNA_constraint_types.h */
struct bContext;
struct bContextDataResult;
struct bPythonConstraint; /* DNA_constraint_types.h */
diff --git a/source/blender/python/bmesh/bmesh_py_ops_call.c b/source/blender/python/bmesh/bmesh_py_ops_call.c
index 3d5aabcfda9..24887b24eb6 100644
--- a/source/blender/python/bmesh/bmesh_py_ops_call.c
+++ b/source/blender/python/bmesh/bmesh_py_ops_call.c
@@ -608,7 +608,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot)
/* keep switch in same order as above */
switch (slot->slot_type) {
case BMO_OP_SLOT_BOOL:
- item = PyBool_FromLong((BMO_SLOT_AS_BOOL(slot)));
+ item = PyBool_FromLong(BMO_SLOT_AS_BOOL(slot));
break;
case BMO_OP_SLOT_INT:
item = PyLong_FromLong(BMO_SLOT_AS_INT(slot));
diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c
index c1e28182c53..1c3334f1adc 100644
--- a/source/blender/python/bmesh/bmesh_py_utils.c
+++ b/source/blender/python/bmesh/bmesh_py_utils.c
@@ -189,7 +189,7 @@ static PyObject *bpy_bm_utils_vert_dissolve(PyObject *UNUSED(self), PyObject *ar
bm = py_vert->bm;
- return PyBool_FromLong((BM_vert_dissolve(bm, py_vert->v)));
+ return PyBool_FromLong(BM_vert_dissolve(bm, py_vert->v));
}
PyDoc_STRVAR(bpy_bm_utils_vert_splice_doc,
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index bfdc763e4df..024900db691 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -1836,7 +1836,7 @@ static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject
/* NOTE: we count on int/float being the same size here */
vec = MEM_mallocN(alloc_len, "array assignment");
- if (PyC_AsArray(vec, seq, size, py_type, is_double, "slice assignment: ") == -1) {
+ if (PyC_AsArray(vec, elem_size, seq, size, py_type, "slice assignment: ") == -1) {
MEM_freeN(vec);
return -1;
}
diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c
index 08ddef992a3..87afc40330c 100644
--- a/source/blender/python/generic/imbuf_py_api.c
+++ b/source/blender/python/generic/imbuf_py_api.c
@@ -242,7 +242,7 @@ static int py_imbuf_ppm_set(Py_ImBuf *self, PyObject *value, void *UNUSED(closur
PY_IMBUF_CHECK_INT(self);
double ppm[2];
- if (PyC_AsArray(ppm, value, 2, &PyFloat_Type, true, "ppm") == -1) {
+ if (PyC_AsArray(ppm, sizeof(*ppm), value, 2, &PyFloat_Type, "ppm") == -1) {
return -1;
}
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index a27ef30c849..51e20a31ba8 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -57,10 +57,10 @@
/* array utility function */
int PyC_AsArray_FAST(void *array,
+ const size_t array_item_size,
PyObject *value_fast,
const Py_ssize_t length,
const PyTypeObject *type,
- const bool is_double,
const char *error_prefix)
{
const Py_ssize_t value_len = PySequence_Fast_GET_SIZE(value_fast);
@@ -80,30 +80,97 @@ int PyC_AsArray_FAST(void *array,
/* for each type */
if (type == &PyFloat_Type) {
- if (is_double) {
- double *array_double = array;
- for (i = 0; i < length; i++) {
- array_double[i] = PyFloat_AsDouble(value_fast_items[i]);
+ switch (array_item_size) {
+ case sizeof(double): {
+ double *array_double = array;
+ for (i = 0; i < length; i++) {
+ array_double[i] = PyFloat_AsDouble(value_fast_items[i]);
+ }
+ break;
}
- }
- else {
- float *array_float = array;
- for (i = 0; i < length; i++) {
- array_float[i] = PyFloat_AsDouble(value_fast_items[i]);
+ case sizeof(float): {
+ float *array_float = array;
+ for (i = 0; i < length; i++) {
+ array_float[i] = PyFloat_AsDouble(value_fast_items[i]);
+ }
+ break;
+ }
+ default: {
+ /* Internal error. */
+ BLI_assert_unreachable();
}
}
}
else if (type == &PyLong_Type) {
- /* could use is_double for 'long int' but no use now */
- int *array_int = array;
- for (i = 0; i < length; i++) {
- array_int[i] = PyC_Long_AsI32(value_fast_items[i]);
+ switch (array_item_size) {
+ case sizeof(int64_t): {
+ int64_t *array_int = array;
+ for (i = 0; i < length; i++) {
+ array_int[i] = PyC_Long_AsI64(value_fast_items[i]);
+ }
+ break;
+ }
+ case sizeof(int32_t): {
+ int32_t *array_int = array;
+ for (i = 0; i < length; i++) {
+ array_int[i] = PyC_Long_AsI32(value_fast_items[i]);
+ }
+ break;
+ }
+ case sizeof(int16_t): {
+ int16_t *array_int = array;
+ for (i = 0; i < length; i++) {
+ array_int[i] = PyC_Long_AsI16(value_fast_items[i]);
+ }
+ break;
+ }
+ case sizeof(int8_t): {
+ int8_t *array_int = array;
+ for (i = 0; i < length; i++) {
+ array_int[i] = PyC_Long_AsI8(value_fast_items[i]);
+ }
+ break;
+ }
+ default: {
+ /* Internal error. */
+ BLI_assert_unreachable();
+ }
}
}
else if (type == &PyBool_Type) {
- bool *array_bool = array;
- for (i = 0; i < length; i++) {
- array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0);
+ switch (array_item_size) {
+ case sizeof(int64_t): {
+ int64_t *array_bool = array;
+ for (i = 0; i < length; i++) {
+ array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0);
+ }
+ break;
+ }
+ case sizeof(int32_t): {
+ int32_t *array_bool = array;
+ for (i = 0; i < length; i++) {
+ array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0);
+ }
+ break;
+ }
+ case sizeof(int16_t): {
+ int16_t *array_bool = array;
+ for (i = 0; i < length; i++) {
+ array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0);
+ }
+ break;
+ }
+ case sizeof(int8_t): {
+ int8_t *array_bool = array;
+ for (i = 0; i < length; i++) {
+ array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0);
+ }
+ break;
+ }
+ default: {
+ /* Internal error. */
+ BLI_assert_unreachable();
+ }
}
}
else {
@@ -123,10 +190,10 @@ int PyC_AsArray_FAST(void *array,
}
int PyC_AsArray(void *array,
+ const size_t array_item_size,
PyObject *value,
const Py_ssize_t length,
const PyTypeObject *type,
- const bool is_double,
const char *error_prefix)
{
PyObject *value_fast;
@@ -136,11 +203,111 @@ int PyC_AsArray(void *array,
return -1;
}
- ret = PyC_AsArray_FAST(array, value_fast, length, type, is_double, error_prefix);
+ ret = PyC_AsArray_FAST(array, array_item_size, value_fast, length, type, error_prefix);
Py_DECREF(value_fast);
return ret;
}
+static int PyC_AsArray_Multi_impl(void **array_p,
+ const size_t array_item_size,
+ PyObject *value,
+ const int *dims,
+ const int dims_len,
+ const PyTypeObject *type,
+ const char *error_prefix);
+
+static int PyC_AsArray_Multi_FAST_impl(void **array_p,
+ const size_t array_item_size,
+ PyObject *value_fast,
+ const int *dims,
+ const int dims_len,
+ const PyTypeObject *type,
+ const char *error_prefix)
+{
+ const Py_ssize_t value_len = PySequence_Fast_GET_SIZE(value_fast);
+ const int length = dims[0];
+
+ if (dims_len == 1) {
+ if (PyC_AsArray_FAST(*array_p, array_item_size, value_fast, length, type, error_prefix) ==
+ -1) {
+ return -1;
+ }
+ *array_p = POINTER_OFFSET(*array_p, array_item_size * length);
+ }
+ else {
+ if (value_len != length) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s: invalid sequence length. expected %d, got %d",
+ error_prefix,
+ length,
+ value_len);
+ return -1;
+ }
+
+ PyObject **value_fast_items = PySequence_Fast_ITEMS(value_fast);
+ const int *dims_next = dims + 1;
+ const int dims_next_len = dims_len - 1;
+
+ for (int i = 0; i < length; i++) {
+ if (PyC_AsArray_Multi_impl(array_p,
+ array_item_size,
+ value_fast_items[i],
+ dims_next,
+ dims_next_len,
+ type,
+ error_prefix) == -1) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int PyC_AsArray_Multi_impl(void **array_p,
+ const size_t array_item_size,
+ PyObject *value,
+ const int *dims,
+ const int dims_len,
+ const PyTypeObject *type,
+ const char *error_prefix)
+{
+ PyObject *value_fast;
+ int ret;
+
+ if (!(value_fast = PySequence_Fast(value, error_prefix))) {
+ return -1;
+ }
+
+ ret = PyC_AsArray_Multi_FAST_impl(
+ array_p, array_item_size, value_fast, dims, dims_len, type, error_prefix);
+ Py_DECREF(value_fast);
+ return ret;
+}
+
+int PyC_AsArray_Multi_FAST(void *array,
+ const size_t array_item_size,
+ PyObject *value_fast,
+ const int *dims,
+ const int dims_len,
+ const PyTypeObject *type,
+ const char *error_prefix)
+{
+ return PyC_AsArray_Multi_FAST_impl(
+ &array, array_item_size, value_fast, dims, dims_len, type, error_prefix);
+}
+
+int PyC_AsArray_Multi(void *array,
+ const size_t array_item_size,
+ PyObject *value,
+ const int *dims,
+ const int dims_len,
+ const PyTypeObject *type,
+ const char *error_prefix)
+{
+ return PyC_AsArray_Multi_impl(
+ &array, array_item_size, value, dims, dims_len, type, error_prefix);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -198,6 +365,108 @@ PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Typed Tuple Packing (Multi-Dimensional)
+ * \{ */
+
+static PyObject *PyC_Tuple_PackArray_Multi_F32_impl(const float **array_p,
+ const int dims[],
+ const int dims_len)
+{
+ const int len = dims[0];
+ if (dims_len == 1) {
+ PyObject *tuple = PyC_Tuple_PackArray_F32(*array_p, len);
+ *array_p = (*array_p) + len;
+ return tuple;
+ }
+ PyObject *tuple = PyTuple_New(dims[0]);
+ const int *dims_next = dims + 1;
+ const int dims_next_len = dims_len - 1;
+ for (uint i = 0; i < len; i++) {
+ PyTuple_SET_ITEM(
+ tuple, i, PyC_Tuple_PackArray_Multi_F32_impl(array_p, dims_next, dims_next_len));
+ }
+ return tuple;
+}
+PyObject *PyC_Tuple_PackArray_Multi_F32(const float *array, const int dims[], const int dims_len)
+{
+ return PyC_Tuple_PackArray_Multi_F32_impl(&array, dims, dims_len);
+}
+
+static PyObject *PyC_Tuple_PackArray_Multi_F64_impl(const double **array_p,
+ const int dims[],
+ const int dims_len)
+{
+ const int len = dims[0];
+ if (dims_len == 1) {
+ PyObject *tuple = PyC_Tuple_PackArray_F64(*array_p, len);
+ *array_p = (*array_p) + len;
+ return tuple;
+ }
+ PyObject *tuple = PyTuple_New(dims[0]);
+ const int *dims_next = dims + 1;
+ const int dims_next_len = dims_len - 1;
+ for (uint i = 0; i < len; i++) {
+ PyTuple_SET_ITEM(
+ tuple, i, PyC_Tuple_PackArray_Multi_F64_impl(array_p, dims_next, dims_next_len));
+ }
+ return tuple;
+}
+PyObject *PyC_Tuple_PackArray_Multi_F64(const double *array, const int dims[], const int dims_len)
+{
+ return PyC_Tuple_PackArray_Multi_F64_impl(&array, dims, dims_len);
+}
+
+static PyObject *PyC_Tuple_PackArray_Multi_I32_impl(const int **array_p,
+ const int dims[],
+ const int dims_len)
+{
+ const int len = dims[0];
+ if (dims_len == 1) {
+ PyObject *tuple = PyC_Tuple_PackArray_I32(*array_p, len);
+ *array_p = (*array_p) + len;
+ return tuple;
+ }
+ PyObject *tuple = PyTuple_New(dims[0]);
+ const int *dims_next = dims + 1;
+ const int dims_next_len = dims_len - 1;
+ for (uint i = 0; i < len; i++) {
+ PyTuple_SET_ITEM(
+ tuple, i, PyC_Tuple_PackArray_Multi_I32_impl(array_p, dims_next, dims_next_len));
+ }
+ return tuple;
+}
+PyObject *PyC_Tuple_PackArray_Multi_I32(const int *array, const int dims[], const int dims_len)
+{
+ return PyC_Tuple_PackArray_Multi_I32_impl(&array, dims, dims_len);
+}
+
+static PyObject *PyC_Tuple_PackArray_Multi_Bool_impl(const bool **array_p,
+ const int dims[],
+ const int dims_len)
+{
+ const int len = dims[0];
+ if (dims_len == 1) {
+ PyObject *tuple = PyC_Tuple_PackArray_Bool(*array_p, len);
+ *array_p = (*array_p) + len;
+ return tuple;
+ }
+ PyObject *tuple = PyTuple_New(dims[0]);
+ const int *dims_next = dims + 1;
+ const int dims_next_len = dims_len - 1;
+ for (uint i = 0; i < len; i++) {
+ PyTuple_SET_ITEM(
+ tuple, i, PyC_Tuple_PackArray_Multi_Bool_impl(array_p, dims_next, dims_next_len));
+ }
+ return tuple;
+}
+PyObject *PyC_Tuple_PackArray_Multi_Bool(const bool *array, const int dims[], const int dims_len)
+{
+ return PyC_Tuple_PackArray_Multi_Bool_impl(&array, dims, dims_len);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Tuple/List Filling
* \{ */
@@ -525,7 +794,7 @@ PyObject *PyC_FrozenSetFromStrings(const char **strings)
*
* Implementation - we can't actually prepend the existing exception,
* because it could have _any_ arguments given to it, so instead we get its
- * ``__str__`` output and raise our own exception including it.
+ * `__str__` output and raise our own exception including it.
*/
PyObject *PyC_Err_Format_Prefix(PyObject *exception_type_prefix, const char *format, ...)
{
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index 842e1482c06..1591413530c 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -42,18 +42,34 @@ void PyC_Err_PrintWithFunc(PyObject *py_func);
void PyC_FileAndNum(const char **r_filename, int *r_lineno);
void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno); /* checks python is running */
int PyC_AsArray_FAST(void *array,
+ const size_t array_elem_size,
PyObject *value_fast,
const Py_ssize_t length,
const PyTypeObject *type,
- const bool is_double,
const char *error_prefix);
int PyC_AsArray(void *array,
+ const size_t array_elem_size,
PyObject *value,
const Py_ssize_t length,
const PyTypeObject *type,
- const bool is_double,
const char *error_prefix);
+int PyC_AsArray_Multi_FAST(void *array,
+ const size_t array_item_size,
+ PyObject *value_fast,
+ const int *dims,
+ const int dims_len,
+ const PyTypeObject *type,
+ const char *error_prefix);
+
+int PyC_AsArray_Multi(void *array,
+ const size_t array_item_size,
+ PyObject *value,
+ const int *dims,
+ const int dims_len,
+ const PyTypeObject *type,
+ const char *error_prefix);
+
PyObject *PyC_Tuple_PackArray_F32(const float *array, uint len);
PyObject *PyC_Tuple_PackArray_F64(const double *array, uint len);
PyObject *PyC_Tuple_PackArray_I32(const int *array, uint len);
@@ -71,6 +87,11 @@ PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len);
#define PyC_Tuple_Pack_Bool(...) \
PyC_Tuple_PackArray_Bool(((const bool[]){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__))
+PyObject *PyC_Tuple_PackArray_Multi_F32(const float *array, const int dims[], const int dims_len);
+PyObject *PyC_Tuple_PackArray_Multi_F64(const double *array, const int dims[], const int dims_len);
+PyObject *PyC_Tuple_PackArray_Multi_I32(const int *array, const int dims[], const int dims_len);
+PyObject *PyC_Tuple_PackArray_Multi_Bool(const bool *array, const int dims[], const int dims_len);
+
void PyC_Tuple_Fill(PyObject *tuple, PyObject *value);
void PyC_List_Fill(PyObject *list, PyObject *value);
diff --git a/source/blender/python/gpu/gpu_py.c b/source/blender/python/gpu/gpu_py.c
index 7aea940da94..e6ba46b2b05 100644
--- a/source/blender/python/gpu/gpu_py.c
+++ b/source/blender/python/gpu/gpu_py.c
@@ -17,8 +17,8 @@
/** \file
* \ingroup bpygpu
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c
index 5119b3612f8..db84e03adcb 100644
--- a/source/blender/python/gpu/gpu_py_api.c
+++ b/source/blender/python/gpu/gpu_py_api.c
@@ -20,8 +20,8 @@
* Experimental Python API, not considered public yet (called '_gpu'),
* we may re-expose as public later.
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c
index 5214d156adf..c0c0aa0028d 100644
--- a/source/blender/python/gpu/gpu_py_batch.c
+++ b/source/blender/python/gpu/gpu_py_batch.c
@@ -19,11 +19,11 @@
/** \file
* \ingroup bpygpu
*
- * This file defines the offscreen functionalities of the 'gpu' module
+ * This file defines the off-screen functionalities of the 'gpu' module
* used for off-screen OpenGL rendering.
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c
index 5c004459b44..a1fc89e772e 100644
--- a/source/blender/python/gpu/gpu_py_buffer.c
+++ b/source/blender/python/gpu/gpu_py_buffer.c
@@ -19,8 +19,8 @@
*
* This file defines the gpu.state API.
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c
index cedce485253..f3fb93021b2 100644
--- a/source/blender/python/gpu/gpu_py_capabilities.c
+++ b/source/blender/python/gpu/gpu_py_capabilities.c
@@ -17,8 +17,8 @@
/** \file
* \ingroup bpygpu
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c
index 0cb5e9a2785..2fb722f74db 100644
--- a/source/blender/python/gpu/gpu_py_element.c
+++ b/source/blender/python/gpu/gpu_py_element.c
@@ -17,8 +17,8 @@
/** \file
* \ingroup bpygpu
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
@@ -144,8 +144,12 @@ static PyObject *pygpu_IndexBuf__tp_new(PyTypeObject *UNUSED(type), PyObject *ar
goto finally;
}
- ok = PyC_AsArray_FAST(
- values, seq_fast_item, verts_per_prim, &PyLong_Type, false, error_prefix) == 0;
+ ok = PyC_AsArray_FAST(values,
+ sizeof(*values),
+ seq_fast_item,
+ verts_per_prim,
+ &PyLong_Type,
+ error_prefix) == 0;
if (ok) {
for (uint j = 0; j < verts_per_prim; j++) {
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c
index 2f081fcfd8c..a9347b71723 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.c
+++ b/source/blender/python/gpu/gpu_py_framebuffer.c
@@ -20,8 +20,8 @@
* This file defines the framebuffer functionalities of the 'gpu' module
* used for off-screen OpenGL rendering.
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
@@ -489,7 +489,7 @@ static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
static const char *_keywords[] = {
"x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL};
- static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0};
+ static _PyArg_Parser _parser = {"iiiiiIO&|$O!:read_color", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kwds,
&_parser,
@@ -551,6 +551,57 @@ static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
return (PyObject *)py_buffer;
}
+PyDoc_STRVAR(pygpu_framebuffer_read_depth_doc,
+ ".. function:: read_depth(x, y, xsize, ysize, data=data)\n"
+ "\n"
+ " Read a pixel depth block from the frame buffer.\n"
+ "\n"
+ " :param x, y: Lower left corner of a rectangular block of pixels.\n"
+ " :param xsize, ysize: Dimensions of the pixel rectangle.\n"
+ " :type x, y, xsize, ysize: int\n"
+ " :arg data: Optional Buffer object to fill with the pixels values.\n"
+ " :type data: :class:`gpu.types.Buffer`\n"
+ " :return: The Buffer with the read pixels.\n"
+ " :rtype: :class:`gpu.types.Buffer`\n");
+static PyObject *pygpu_framebuffer_read_depth(BPyGPUFrameBuffer *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
+ int x, y, w, h;
+ BPyGPUBuffer *py_buffer = NULL;
+
+ static const char *_keywords[] = {"x", "y", "xsize", "ysize", "data", NULL};
+ static _PyArg_Parser _parser = {"iiii|$O!:read_depth", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kwds, &_parser, &x, &y, &w, &h, &BPyGPU_BufferType, &py_buffer)) {
+ return NULL;
+ }
+
+ if (py_buffer) {
+ if (py_buffer->format != GPU_DATA_FLOAT) {
+ PyErr_SetString(PyExc_AttributeError, "the format of the buffer must be 'GPU_DATA_FLOAT'");
+ return NULL;
+ }
+
+ size_t size_curr = bpygpu_Buffer_size(py_buffer);
+ size_t size_expected = w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT);
+ if (size_curr < size_expected) {
+ PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
+ return NULL;
+ }
+ }
+ else {
+ py_buffer = BPyGPU_Buffer_CreatePyObject(GPU_DATA_FLOAT, (Py_ssize_t[]){h, w}, 2, NULL);
+ BLI_assert(bpygpu_Buffer_size(py_buffer) ==
+ w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT));
+ }
+
+ GPU_framebuffer_read_depth(self->fb, x, y, w, h, GPU_DATA_FLOAT, py_buffer->buf.as_void);
+
+ return (PyObject *)py_buffer;
+}
+
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
PyDoc_STRVAR(pygpu_framebuffer_free_doc,
".. method:: free()\n"
@@ -598,6 +649,10 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = {
(PyCFunction)pygpu_framebuffer_read_color,
METH_VARARGS | METH_KEYWORDS,
pygpu_framebuffer_read_color_doc},
+ {"read_depth",
+ (PyCFunction)pygpu_framebuffer_read_depth,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_framebuffer_read_depth_doc},
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
{"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
#endif
diff --git a/source/blender/python/gpu/gpu_py_matrix.c b/source/blender/python/gpu/gpu_py_matrix.c
index b379600d33c..cc9b3d702e2 100644
--- a/source/blender/python/gpu/gpu_py_matrix.c
+++ b/source/blender/python/gpu/gpu_py_matrix.c
@@ -22,8 +22,8 @@
* \warning While these functions attempt to ensure correct stack usage.
* Mixing Python and C functions may still crash on invalid use.
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 0a8b294ea41..457f00b1267 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -22,8 +22,8 @@
* This file defines the offscreen functionalities of the 'gpu' module
* used for off-screen OpenGL rendering.
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c
index e49ad18dfd8..132052b6f1d 100644
--- a/source/blender/python/gpu/gpu_py_platform.c
+++ b/source/blender/python/gpu/gpu_py_platform.c
@@ -17,8 +17,8 @@
/** \file
* \ingroup bpygpu
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_select.c b/source/blender/python/gpu/gpu_py_select.c
index b72aca6a792..4db102118f1 100644
--- a/source/blender/python/gpu/gpu_py_select.c
+++ b/source/blender/python/gpu/gpu_py_select.c
@@ -22,8 +22,8 @@
* \note Currently only used for gizmo selection,
* will need to add begin/end and a way to access the hits.
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c
index fc3a7d1360b..b3f1c186716 100644
--- a/source/blender/python/gpu/gpu_py_shader.c
+++ b/source/blender/python/gpu/gpu_py_shader.c
@@ -17,8 +17,8 @@
/** \file
* \ingroup bpygpu
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
@@ -309,7 +309,8 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
ret = -1;
}
else {
- ret = PyC_AsArray_FAST(values, seq_fast, length, &PyLong_Type, false, error_prefix);
+ ret = PyC_AsArray_FAST(
+ values, sizeof(*values), seq_fast, length, &PyLong_Type, error_prefix);
}
Py_DECREF(seq_fast);
}
@@ -448,7 +449,8 @@ static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
ret = -1;
}
else {
- ret = PyC_AsArray_FAST(values, seq_fast, length, &PyLong_Type, false, error_prefix);
+ ret = PyC_AsArray_FAST(
+ values, sizeof(*values), seq_fast, length, &PyLong_Type, error_prefix);
}
Py_DECREF(seq_fast);
}
diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c
index 173c5afba56..7b7a61cc338 100644
--- a/source/blender/python/gpu/gpu_py_state.c
+++ b/source/blender/python/gpu/gpu_py_state.c
@@ -19,8 +19,8 @@
*
* This file defines the gpu.state API.
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c
index 2181c09b537..5d136921300 100644
--- a/source/blender/python/gpu/gpu_py_texture.c
+++ b/source/blender/python/gpu/gpu_py_texture.c
@@ -19,8 +19,8 @@
*
* This file defines the texture functionalities of the 'gpu' module
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
@@ -153,7 +153,13 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg
int len = 1;
if (PySequence_Check(py_size)) {
len = PySequence_Size(py_size);
- if (PyC_AsArray(size, py_size, len, &PyLong_Type, false, "GPUTexture.__new__") == -1) {
+ if ((len < 1) || (len > 3)) {
+ PyErr_Format(PyExc_ValueError,
+ "GPUTexture.__new__: \"size\" must be between 1 and 3 in length (got %d)",
+ len);
+ return NULL;
+ }
+ if (PyC_AsArray(size, sizeof(*size), py_size, len, &PyLong_Type, "GPUTexture.__new__") == -1) {
return NULL;
}
}
@@ -321,10 +327,11 @@ static PyObject *pygpu_texture_clear(BPyGPUTexture *self, PyObject *args, PyObje
memset(&values, 0, sizeof(values));
if (PyC_AsArray(&values,
+ (pygpu_dataformat.value_found == GPU_DATA_FLOAT) ? sizeof(*values.f) :
+ sizeof(*values.i),
py_values,
shape,
- pygpu_dataformat.value_found == GPU_DATA_FLOAT ? &PyFloat_Type : &PyLong_Type,
- false,
+ (pygpu_dataformat.value_found == GPU_DATA_FLOAT) ? &PyFloat_Type : &PyLong_Type,
"clear") == -1) {
return NULL;
}
diff --git a/source/blender/python/gpu/gpu_py_types.c b/source/blender/python/gpu/gpu_py_types.c
index fdd589d788e..a8787cf5c7f 100644
--- a/source/blender/python/gpu/gpu_py_types.c
+++ b/source/blender/python/gpu/gpu_py_types.c
@@ -17,8 +17,8 @@
/** \file
* \ingroup bpygpu
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_uniformbuffer.c b/source/blender/python/gpu/gpu_py_uniformbuffer.c
index cfef20e2e4d..616ef65a5d1 100644
--- a/source/blender/python/gpu/gpu_py_uniformbuffer.c
+++ b/source/blender/python/gpu/gpu_py_uniformbuffer.c
@@ -19,8 +19,8 @@
*
* This file defines the uniform buffer functionalities of the 'gpu' module
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c
index 3c7038186b9..949086378a8 100644
--- a/source/blender/python/gpu/gpu_py_vertex_buffer.c
+++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c
@@ -17,8 +17,8 @@
/** \file
* \ingroup bpygpu
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c
index 39c91966cab..c344429576a 100644
--- a/source/blender/python/gpu/gpu_py_vertex_format.c
+++ b/source/blender/python/gpu/gpu_py_vertex_format.c
@@ -17,8 +17,8 @@
/** \file
* \ingroup bpygpu
*
- * - Use ``bpygpu_`` for local API.
- * - Use ``BPyGPU`` for public API.
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
*/
#include <Python.h>
diff --git a/source/blender/python/intern/bpy_app_ffmpeg.c b/source/blender/python/intern/bpy_app_ffmpeg.c
index d2261ee7311..5f277b0ef07 100644
--- a/source/blender/python/intern/bpy_app_ffmpeg.c
+++ b/source/blender/python/intern/bpy_app_ffmpeg.c
@@ -36,7 +36,7 @@
static PyTypeObject BlenderAppFFmpegType;
#define DEF_FFMPEG_LIB_VERSION(lib) \
- {(#lib "_version"), ("The " #lib " version as a tuple of 3 numbers")}, \
+ {(#lib "_version"), ("The " #lib " version as a tuple of 3 numbers")}, \
{ \
(#lib "_version_string"), ("The " #lib " version formatted as a string") \
}
diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c
index 8dc5a6c629c..d66643c5d61 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -283,7 +283,7 @@ void BPY_app_handlers_reset(const short do_all)
for (i = PyList_GET_SIZE(ls) - 1; i >= 0; i--) {
- if ((PyFunction_Check((item = PyList_GET_ITEM(ls, i)))) &&
+ if (PyFunction_Check((item = PyList_GET_ITEM(ls, i))) &&
(dict_ptr = _PyObject_GetDictPtr(item)) && (*dict_ptr) &&
(PyDict_GetItem(*dict_ptr, perm_id_str) != NULL)) {
/* keep */
diff --git a/source/blender/python/intern/bpy_gizmo_wrap.c b/source/blender/python/intern/bpy_gizmo_wrap.c
index 34d2ba16e69..42e0c7d0003 100644
--- a/source/blender/python/intern/bpy_gizmo_wrap.c
+++ b/source/blender/python/intern/bpy_gizmo_wrap.c
@@ -54,20 +54,23 @@ static bool bpy_gizmotype_target_property_def(wmGizmoType *gzt, PyObject *item)
struct {
char *id;
- char *type_id;
- int type;
+ struct BPy_EnumProperty_Parse type_enum;
int array_length;
} params = {
.id = NULL, /* not optional */
- .type = PROP_FLOAT,
- .type_id = NULL,
+ .type_enum = {.items = rna_enum_property_type_items, .value = PROP_FLOAT},
.array_length = 1,
};
static const char *const _keywords[] = {"id", "type", "array_length", NULL};
- static _PyArg_Parser _parser = {"|$ssi:register_class", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(
- empty_tuple, item, &_parser, &params.id, &params.type_id, &params.array_length)) {
+ static _PyArg_Parser _parser = {"|$sO&i:register_class", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(empty_tuple,
+ item,
+ &_parser,
+ &params.id,
+ pyrna_enum_value_parse_string,
+ &params.type_enum,
+ &params.array_length)) {
goto fail;
}
@@ -76,21 +79,12 @@ static bool bpy_gizmotype_target_property_def(wmGizmoType *gzt, PyObject *item)
goto fail;
}
- if ((params.type_id != NULL) &&
- pyrna_enum_value_from_id(
- rna_enum_property_type_items, params.type_id, &params.type, "'type' enum value") == -1) {
- goto fail;
- }
- else {
- params.type = rna_enum_property_type_items[params.type].value;
- }
-
if ((params.array_length < 1 || params.array_length > RNA_MAX_ARRAY_LENGTH)) {
PyErr_SetString(PyExc_ValueError, "'array_length' out of range");
goto fail;
}
- WM_gizmotype_target_property_def(gzt, params.id, params.type, params.array_length);
+ WM_gizmotype_target_property_def(gzt, params.id, params.type_enum.value, params.array_length);
Py_DECREF(empty_tuple);
return true;
diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c
index 28a97c3fa3b..d9a357c5e2e 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -66,6 +66,8 @@ typedef struct {
char relpath[FILE_MAX];
char abspath[FILE_MAX]; /* absolute path */
BlendHandle *blo_handle;
+ /* Referenced by `blo_handle`, so stored here to keep alive for long enough. */
+ BlendFileReadReport bf_reports;
int flag;
PyObject *dict;
/* Borrowed reference to the `bmain`, taken from the RNA instance of #RNA_BlendDataLibraries.
@@ -259,7 +261,8 @@ static PyObject *bpy_lib_enter(BPy_Library *self)
BKE_reports_init(&reports, RPT_STORE);
BlendFileReadReport bf_reports = {.reports = &reports};
- self->blo_handle = BLO_blendhandle_from_file(self->abspath, &bf_reports);
+ self->bf_reports = bf_reports;
+ self->blo_handle = BLO_blendhandle_from_file(self->abspath, &self->bf_reports);
if (self->blo_handle == NULL) {
if (BPy_reports_to_error(&reports, PyExc_IOError, true) != -1) {
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index 9ee9f2e477f..d5afb2334f0 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -339,7 +339,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
return NULL;
}
- /* When calling bpy.ops.wm.read_factory_settings() bpy.data's main pointer
+ /* When calling `bpy.ops.wm.read_factory_settings()` `bpy.data's` main pointer
* is freed by clear_globals(), further access will crash blender.
* Setting context is not needed in this case, only calling because this
* function corrects bpy.data (internal Main pointer) */
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index 14e25e02d84..a84d920d47d 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -492,6 +492,145 @@ static void bpy_prop_assign_flag_override(PropertyRNA *prop, const int flag_over
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Multi-Dimensional Property Utilities
+ * \{ */
+
+struct BPyPropArrayLength {
+ int len_total;
+ /** Ignore `dims` when `dims_len == 0`. */
+ int dims[RNA_MAX_ARRAY_DIMENSION];
+ int dims_len;
+};
+
+/**
+ * Use with #PyArg_ParseTuple's `O&` formatting.
+ */
+static int bpy_prop_array_length_parse(PyObject *o, void *p)
+{
+ struct BPyPropArrayLength *array_len_info = p;
+
+ if (PyLong_CheckExact(o)) {
+ int size;
+ if (((size = PyLong_AsLong(o)) == -1)) {
+ PyErr_Format(
+ PyExc_ValueError, "expected number or sequence of numbers, got %s", Py_TYPE(o)->tp_name);
+ return 0;
+ }
+ if (size < 1 || size > PYRNA_STACK_ARRAY) {
+ PyErr_Format(
+ PyExc_TypeError, "(size=%d) must be between 1 and " STRINGIFY(PYRNA_STACK_ARRAY), size);
+ return 0;
+ }
+ array_len_info->len_total = size;
+
+ /* Don't use this value. */
+ array_len_info->dims_len = 0;
+ }
+ else {
+ PyObject *seq_fast;
+ if (!(seq_fast = PySequence_Fast(o, "size must be a number of a sequence of numbers"))) {
+ return 0;
+ }
+ const int seq_len = PySequence_Fast_GET_SIZE(seq_fast);
+ if (seq_len < 1 || seq_len > RNA_MAX_ARRAY_DIMENSION) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "(len(size)=%d) length must be between 1 and " STRINGIFY(RNA_MAX_ARRAY_DIMENSION),
+ seq_len);
+ Py_DECREF(seq_fast);
+ return 0;
+ }
+
+ PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
+ for (int i = 0; i < seq_len; i++) {
+ int size;
+ if (((size = PyLong_AsLong(seq_items[i])) == -1)) {
+ Py_DECREF(seq_fast);
+ PyErr_Format(PyExc_ValueError,
+ "expected number in sequence, got %s at index %d",
+ Py_TYPE(o)->tp_name,
+ i);
+ return 0;
+ }
+ if (size < 1 || size > PYRNA_STACK_ARRAY) {
+ Py_DECREF(seq_fast);
+ PyErr_Format(PyExc_TypeError,
+ "(size[%d]=%d) must be between 1 and " STRINGIFY(PYRNA_STACK_ARRAY),
+ i,
+ size);
+ return 0;
+ }
+
+ array_len_info->dims[i] = size;
+ array_len_info->dims_len = seq_len;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Return -1 on error.
+ */
+static int bpy_prop_array_from_py_with_dims(void *values,
+ size_t values_elem_size,
+ PyObject *py_values,
+ const struct BPyPropArrayLength *array_len_info,
+ const PyTypeObject *type,
+ const char *error_str)
+{
+ if (array_len_info->dims_len == 0) {
+ return PyC_AsArray(
+ values, values_elem_size, py_values, array_len_info->len_total, type, error_str);
+ }
+ const int *dims = array_len_info->dims;
+ const int dims_len = array_len_info->dims_len;
+ return PyC_AsArray_Multi(values, values_elem_size, py_values, dims, dims_len, type, error_str);
+}
+
+static bool bpy_prop_array_is_matrix_compatible_ex(int subtype,
+ const struct BPyPropArrayLength *array_len_info)
+{
+ return ((subtype == PROP_MATRIX) && (array_len_info->dims_len == 2) &&
+ ((array_len_info->dims[0] >= 2) && (array_len_info->dims[0] >= 4)) &&
+ ((array_len_info->dims[1] >= 2) && (array_len_info->dims[1] >= 4)));
+}
+
+static bool bpy_prop_array_is_matrix_compatible(PropertyRNA *prop,
+ const struct BPyPropArrayLength *array_len_info)
+{
+ BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
+ return bpy_prop_array_is_matrix_compatible_ex(RNA_property_subtype(prop), array_len_info);
+}
+
+/**
+ * Needed since the internal storage of matrices swaps row/column.
+ */
+static void bpy_prop_array_matrix_swap_row_column_vn_vn(
+ float *values_dst, const float *values_src, const struct BPyPropArrayLength *array_len_info)
+{
+ BLI_assert(values_dst != values_src);
+ const int dim0 = array_len_info->dims[0], dim1 = array_len_info->dims[1];
+ BLI_assert(dim0 <= 4 && dim1 <= 4);
+ for (int i = 0; i < dim0; i++) {
+ for (int j = 0; j < dim1; j++) {
+ values_dst[(j * dim0) + i] = values_src[(i * dim1) + j];
+ }
+ }
+}
+
+static void bpy_prop_array_matrix_swap_row_column_vn(
+ float *values, const struct BPyPropArrayLength *array_len_info)
+{
+ const int dim0 = array_len_info->dims[0], dim1 = array_len_info->dims[1];
+ BLI_assert(dim0 <= 4 && dim1 <= 4);
+ float values_orig[4 * 4];
+ memcpy(values_orig, values, sizeof(float) * (dim0 * dim1));
+ bpy_prop_array_matrix_swap_row_column_vn_vn(values, values_orig, array_len_info);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Shared Property Callbacks
*
* Unique data is accessed via #RNA_property_py_data_get
@@ -687,7 +826,10 @@ static void bpy_prop_boolean_array_get_fn(struct PointerRNA *ptr,
PyGILState_STATE gilstate;
bool use_gil;
const bool is_write_ok = pyrna_write_check();
+ bool is_values_set = false;
int i, len = RNA_property_array_length(ptr, prop);
+ struct BPyPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -711,25 +853,25 @@ static void bpy_prop_boolean_array_get_fn(struct PointerRNA *ptr,
Py_DECREF(args);
- if (ret == NULL) {
- PyC_Err_PrintWithFunc(py_func);
-
- for (i = 0; i < len; i++) {
- values[i] = false;
- }
- }
- else {
- if (PyC_AsArray(values, ret, len, &PyBool_Type, false, "BoolVectorProperty get") == -1) {
+ if (ret != NULL) {
+ if (bpy_prop_array_from_py_with_dims(values,
+ sizeof(*values),
+ ret,
+ &array_len_info,
+ &PyBool_Type,
+ "BoolVectorProperty get callback") == -1) {
PyC_Err_PrintWithFunc(py_func);
-
- for (i = 0; i < len; i++) {
- values[i] = false;
- }
-
- /* PyC_AsArray decrements refcount internally on error */
}
else {
- Py_DECREF(ret);
+ is_values_set = true;
+ }
+ Py_DECREF(ret);
+ }
+
+ if (is_values_set == false) {
+ /* This is the flattened length for multi-dimensional arrays. */
+ for (i = 0; i < len; i++) {
+ values[i] = false;
}
}
@@ -756,6 +898,8 @@ static void bpy_prop_boolean_array_set_fn(struct PointerRNA *ptr,
bool use_gil;
const bool is_write_ok = pyrna_write_check();
const int len = RNA_property_array_length(ptr, prop);
+ struct BPyPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -775,7 +919,13 @@ static void bpy_prop_boolean_array_set_fn(struct PointerRNA *ptr,
self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEM(args, 0, self);
- py_values = PyC_Tuple_PackArray_Bool(values, len);
+ if (array_len_info.dims_len == 0) {
+ py_values = PyC_Tuple_PackArray_Bool(values, len);
+ }
+ else {
+ py_values = PyC_Tuple_PackArray_Multi_Bool(
+ values, array_len_info.dims, array_len_info.dims_len);
+ }
PyTuple_SET_ITEM(args, 1, py_values);
ret = PyObject_CallObject(py_func, args);
@@ -937,7 +1087,10 @@ static void bpy_prop_int_array_get_fn(struct PointerRNA *ptr,
PyGILState_STATE gilstate;
bool use_gil;
const bool is_write_ok = pyrna_write_check();
+ bool is_values_set = false;
int i, len = RNA_property_array_length(ptr, prop);
+ struct BPyPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -961,25 +1114,25 @@ static void bpy_prop_int_array_get_fn(struct PointerRNA *ptr,
Py_DECREF(args);
- if (ret == NULL) {
- PyC_Err_PrintWithFunc(py_func);
-
- for (i = 0; i < len; i++) {
- values[i] = 0;
- }
- }
- else {
- if (PyC_AsArray(values, ret, len, &PyLong_Type, false, "IntVectorProperty get") == -1) {
+ if (ret != NULL) {
+ if (bpy_prop_array_from_py_with_dims(values,
+ sizeof(*values),
+ ret,
+ &array_len_info,
+ &PyLong_Type,
+ "IntVectorProperty get callback") == -1) {
PyC_Err_PrintWithFunc(py_func);
-
- for (i = 0; i < len; i++) {
- values[i] = 0;
- }
-
- /* PyC_AsArray decrements refcount internally on error */
}
else {
- Py_DECREF(ret);
+ is_values_set = true;
+ }
+ Py_DECREF(ret);
+ }
+
+ if (is_values_set == false) {
+ /* This is the flattened length for multi-dimensional arrays. */
+ for (i = 0; i < len; i++) {
+ values[i] = 0;
}
}
@@ -1006,6 +1159,8 @@ static void bpy_prop_int_array_set_fn(struct PointerRNA *ptr,
bool use_gil;
const bool is_write_ok = pyrna_write_check();
const int len = RNA_property_array_length(ptr, prop);
+ struct BPyPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -1025,7 +1180,14 @@ static void bpy_prop_int_array_set_fn(struct PointerRNA *ptr,
self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEM(args, 0, self);
- py_values = PyC_Tuple_PackArray_I32(values, len);
+ if (array_len_info.dims_len == 0) {
+ py_values = PyC_Tuple_PackArray_I32(values, len);
+ }
+ else {
+ py_values = PyC_Tuple_PackArray_Multi_I32(
+ values, array_len_info.dims, array_len_info.dims_len);
+ }
+
PyTuple_SET_ITEM(args, 1, py_values);
ret = PyObject_CallObject(py_func, args);
@@ -1187,7 +1349,10 @@ static void bpy_prop_float_array_get_fn(struct PointerRNA *ptr,
PyGILState_STATE gilstate;
bool use_gil;
const bool is_write_ok = pyrna_write_check();
+ bool is_values_set = false;
int i, len = RNA_property_array_length(ptr, prop);
+ struct BPyPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -1211,25 +1376,29 @@ static void bpy_prop_float_array_get_fn(struct PointerRNA *ptr,
Py_DECREF(args);
- if (ret == NULL) {
- PyC_Err_PrintWithFunc(py_func);
-
- for (i = 0; i < len; i++) {
- values[i] = 0.0f;
- }
- }
- else {
- if (PyC_AsArray(values, ret, len, &PyFloat_Type, false, "FloatVectorProperty get") == -1) {
+ if (ret != NULL) {
+ if (bpy_prop_array_from_py_with_dims(values,
+ sizeof(*values),
+ ret,
+ &array_len_info,
+ &PyFloat_Type,
+ "FloatVectorProperty get callback") == -1) {
PyC_Err_PrintWithFunc(py_func);
-
- for (i = 0; i < len; i++) {
- values[i] = 0.0f;
- }
-
- /* PyC_AsArray decrements refcount internally on error */
}
else {
- Py_DECREF(ret);
+ /* Only for float types. */
+ if (bpy_prop_array_is_matrix_compatible(prop, &array_len_info)) {
+ bpy_prop_array_matrix_swap_row_column_vn(values, &array_len_info);
+ }
+ is_values_set = true;
+ }
+ Py_DECREF(ret);
+ }
+
+ if (is_values_set == false) {
+ /* This is the flattened length for multi-dimensional arrays. */
+ for (i = 0; i < len; i++) {
+ values[i] = 0.0f;
}
}
@@ -1256,6 +1425,8 @@ static void bpy_prop_float_array_set_fn(struct PointerRNA *ptr,
bool use_gil;
const bool is_write_ok = pyrna_write_check();
const int len = RNA_property_array_length(ptr, prop);
+ struct BPyPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -1275,7 +1446,14 @@ static void bpy_prop_float_array_set_fn(struct PointerRNA *ptr,
self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEM(args, 0, self);
- py_values = PyC_Tuple_PackArray_F32(values, len);
+ if (array_len_info.dims_len == 0) {
+ py_values = PyC_Tuple_PackArray_F32(values, len);
+ }
+ else {
+ /* No need for matrix column/row swapping here unless the matrix data is read directly. */
+ py_values = PyC_Tuple_PackArray_Multi_F32(
+ values, array_len_info.dims, array_len_info.dims_len);
+ }
PyTuple_SET_ITEM(args, 1, py_values);
ret = PyObject_CallObject(py_func, args);
@@ -1720,9 +1898,9 @@ static int icon_id_from_name(const char *name)
}
static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
- PyObject *def,
- int *defvalue,
- const bool is_enum_flag)
+ const bool is_enum_flag,
+ PyObject *default_py,
+ int *r_default_value)
{
EnumPropertyItem *items;
PyObject *item;
@@ -1730,9 +1908,9 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
Py_ssize_t totbuf = 0;
int i;
- short def_used = 0;
- const char *def_string_cmp = NULL;
- int def_int_cmp = 0;
+ short default_used = 0;
+ const char *default_str_cmp = NULL;
+ int default_int_cmp = 0;
if (is_enum_flag) {
if (seq_len > RNA_ENUM_BITFLAG_SIZE) {
@@ -1741,23 +1919,23 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
RNA_ENUM_BITFLAG_SIZE) " members for a ENUM_FLAG type property");
return NULL;
}
- if (def && !PySet_Check(def)) {
+ if (default_py && !PySet_Check(default_py)) {
PyErr_Format(PyExc_TypeError,
"EnumProperty(...): default option must be a 'set' "
"type when ENUM_FLAG is enabled, not a '%.200s'",
- Py_TYPE(def)->tp_name);
+ Py_TYPE(default_py)->tp_name);
return NULL;
}
}
else {
- if (def) {
- if (!py_long_as_int(def, &def_int_cmp)) {
- def_string_cmp = PyUnicode_AsUTF8(def);
- if (def_string_cmp == NULL) {
+ if (default_py) {
+ if (!py_long_as_int(default_py, &default_int_cmp)) {
+ default_str_cmp = PyUnicode_AsUTF8(default_py);
+ if (default_str_cmp == NULL) {
PyErr_Format(PyExc_TypeError,
"EnumProperty(...): default option must be a 'str' or 'int' "
"type when ENUM_FLAG is disabled, not a '%.200s'",
- Py_TYPE(def)->tp_name);
+ Py_TYPE(default_py)->tp_name);
return NULL;
}
}
@@ -1765,7 +1943,7 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
}
/* blank value */
- *defvalue = 0;
+ *r_default_value = 0;
items = MEM_callocN(sizeof(EnumPropertyItem) * (seq_len + 1), "enum_items_from_py1");
@@ -1779,7 +1957,7 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
item = seq_fast_items[i];
- if ((PyTuple_CheckExact(item)) && (item_size = PyTuple_GET_SIZE(item)) &&
+ if (PyTuple_CheckExact(item) && (item_size = PyTuple_GET_SIZE(item)) &&
(item_size >= 3 && item_size <= 5) &&
(tmp.identifier = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(item, 0), &id_str_size)) &&
(tmp.name = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(item, 1), &name_str_size)) &&
@@ -1794,9 +1972,9 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
tmp.value = 1 << i;
}
- if (def && PySet_Contains(def, PyTuple_GET_ITEM(item, 0))) {
- *defvalue |= tmp.value;
- def_used++;
+ if (default_py && PySet_Contains(default_py, PyTuple_GET_ITEM(item, 0))) {
+ *r_default_value |= tmp.value;
+ default_used++;
}
}
else {
@@ -1804,11 +1982,11 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
tmp.value = i;
}
- if (def && def_used == 0) {
- if ((def_string_cmp != NULL && STREQ(def_string_cmp, tmp.identifier)) ||
- (def_string_cmp == NULL && def_int_cmp == tmp.value)) {
- *defvalue = tmp.value;
- def_used++; /* only ever 1 */
+ if (default_py && default_used == 0) {
+ if ((default_str_cmp != NULL && STREQ(default_str_cmp, tmp.identifier)) ||
+ (default_str_cmp == NULL && default_int_cmp == tmp.value)) {
+ *r_default_value = tmp.value;
+ default_used++; /* only ever 1 */
}
}
}
@@ -1838,28 +2016,28 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
if (is_enum_flag) {
/* strict check that all set members were used */
- if (def && def_used != PySet_GET_SIZE(def)) {
+ if (default_py && default_used != PySet_GET_SIZE(default_py)) {
MEM_freeN(items);
PyErr_Format(PyExc_TypeError,
"EnumProperty(..., default={...}): set has %d unused member(s)",
- PySet_GET_SIZE(def) - def_used);
+ PySet_GET_SIZE(default_py) - default_used);
return NULL;
}
}
else {
- if (def && def_used == 0) {
+ if (default_py && default_used == 0) {
MEM_freeN(items);
- if (def_string_cmp) {
+ if (default_str_cmp) {
PyErr_Format(PyExc_TypeError,
"EnumProperty(..., default=\'%s\'): not found in enum members",
- def_string_cmp);
+ default_str_cmp);
}
else {
PyErr_Format(PyExc_TypeError,
"EnumProperty(..., default=%d): not found in enum members",
- def_int_cmp);
+ default_int_cmp);
}
return NULL;
}
@@ -1938,7 +2116,7 @@ static const EnumPropertyItem *bpy_prop_enum_itemf_fn(struct bContext *C,
}
else {
PyObject *items_fast;
- int defvalue_dummy = 0;
+ int default_value_dummy = 0;
if (!(items_fast = PySequence_Fast(items,
"EnumProperty(...): "
@@ -1947,7 +2125,7 @@ static const EnumPropertyItem *bpy_prop_enum_itemf_fn(struct bContext *C,
}
else {
eitems = enum_items_from_py(
- items_fast, NULL, &defvalue_dummy, (RNA_property_flag(prop) & PROP_ENUM_FLAG) != 0);
+ items_fast, (RNA_property_flag(prop) & PROP_ENUM_FLAG) != 0, NULL, &default_value_dummy);
Py_DECREF(items_fast);
@@ -2240,84 +2418,141 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
/** \name Shared Method Utilities
* \{ */
-/* this define runs at the start of each function and deals with
- * returning a deferred property (to be registered later) */
-#define BPY_PROPDEF_HEAD(_func) \
- if (PyTuple_GET_SIZE(args) == 1) { \
- PyObject *ret; \
- self = PyTuple_GET_ITEM(args, 0); \
- args = PyTuple_New(0); \
- ret = BPy_##_func(self, args, kw); \
- Py_DECREF(args); \
- return ret; \
- } \
- if (PyTuple_GET_SIZE(args) > 1) { \
- PyErr_SetString(PyExc_ValueError, "all args must be keywords"); \
- return NULL; \
- } \
- srna = srna_from_self(self, #_func "(...):"); \
- if (srna == NULL) { \
- if (PyErr_Occurred()) { \
- return NULL; \
- } \
- return bpy_prop_deferred_data_CreatePyObject(pymeth_##_func, kw); \
- } \
- (void)0
-
-/* terse macros for error checks shared between all funcs can't use function
- * calls because of static strings passed to pyrna_set_to_enum_bitfield */
-#define BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items) \
- if (UNLIKELY(id_len >= MAX_IDPROP_NAME)) { \
- PyErr_Format(PyExc_TypeError, \
- #_func "(): '%.200s' too long, max length is %d", \
- id, \
- MAX_IDPROP_NAME - 1); \
- return NULL; \
- } \
- if (UNLIKELY(RNA_def_property_free_identifier(srna, id) == -1)) { \
- PyErr_Format(PyExc_TypeError, #_func "(): '%s' is defined as a non-dynamic type", id); \
- return NULL; \
- } \
- if (UNLIKELY(pyopts && pyrna_set_to_enum_bitfield( \
- _property_flag_items, pyopts, &opts, #_func "(options={ ...}):"))) { \
- return NULL; \
- } \
- if (UNLIKELY(pyopts_override && pyrna_set_to_enum_bitfield(_property_flag_override_items, \
- pyopts_override, \
- &opts_override, \
- #_func "(override={ ...}):"))) { \
- return NULL; \
- } \
- { \
- const EnumPropertyItem *tag_defines = RNA_struct_property_tag_defines(srna); \
- if (py_tags && !tag_defines) { \
- PyErr_Format(PyExc_TypeError, \
- #_func "(): property-tags not available for '%s'", \
- RNA_struct_identifier(srna)); \
- return NULL; \
- } \
- if (UNLIKELY(py_tags && pyrna_set_to_enum_bitfield( \
- tag_defines, py_tags, &prop_tags, #_func "(tags={ ...}):"))) { \
- return NULL; \
- } \
- } \
- (void)0
-
-#define BPY_PROPDEF_SUBTYPE_CHECK( \
- _func, _property_flag_items, _property_flag_override_items, _subtype) \
- BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items); \
- if (UNLIKELY(pysubtype && RNA_enum_value_from_id(_subtype, pysubtype, &subtype) == 0)) { \
- const char *enum_str = BPy_enum_as_string(_subtype); \
- PyErr_Format(PyExc_TypeError, \
- #_func \
- "(subtype='%s'): " \
- "subtype not found in (%s)", \
- pysubtype, \
- enum_str); \
- MEM_freeN((void *)enum_str); \
- return NULL; \
- } \
- (void)0
+/**
+ * This define runs at the start of each function and deals with
+ * returning a deferred property #BPy_PropDeferred (to be registered later).
+ *
+ * \param self: The self argument from the caller.
+ * \param args: The positional arguments of the caller.
+ * \param kw: The keyword arguments of the caller.
+ * \param method_object: The method of the caller (unfortunately this can't be deduced).
+ * \param r_deferred_result: The deferred result (or NULL in the case of an error).
+ * The caller must return this value unless a valid `srna` is returned.
+ *
+ * \returns When not null, the caller is expected to perform the registration.
+ */
+static StructRNA *bpy_prop_deferred_data_or_srna(PyObject *self,
+ PyObject *args,
+ PyObject *kw,
+ PyObject *method_object,
+ PyObject **r_deferred_result)
+{
+ /* This must be the methods of one of the main property types defined in this file. */
+ BLI_assert(PyCFunction_CheckExact(method_object));
+
+ const int args_len = PyTuple_GET_SIZE(args);
+ PyMethodDef *method_def = ((PyCFunctionObject *)method_object)->m_ml;
+
+ /* Call this function with the first argument set to `self`. */
+ if (args_len == 1) {
+ self = PyTuple_GET_ITEM(args, 0);
+ args = PyTuple_New(0);
+
+ /* This will be #BPy_BoolProperty` or one of the functions that define a type. */
+ PyCFunctionWithKeywords method_fn = (PyCFunctionWithKeywords)method_def->ml_meth;
+ *r_deferred_result = method_fn(self, args, kw);
+ Py_DECREF(args);
+ /* May be an error (depending on `r_deferred_result`). */
+ return NULL;
+ }
+
+ const char *error_prefix = method_def->ml_name;
+ if (args_len > 1) {
+ PyErr_Format(PyExc_ValueError, "%s: all args must be keywords", error_prefix);
+ *r_deferred_result = NULL;
+ /* An error. */
+ return NULL;
+ }
+
+ StructRNA *srna = srna_from_self(self, error_prefix);
+ if (srna == NULL) {
+ *r_deferred_result = PyErr_Occurred() ?
+ NULL :
+ bpy_prop_deferred_data_CreatePyObject(method_object, kw);
+ /* May be an error (depending on `r_deferred_result`). */
+ return NULL;
+ }
+
+ /* Crash if this is ever used by accident! */
+#ifndef NDEBUG
+ *r_deferred_result = (PyObject *)(intptr_t)1;
+#endif
+
+ /* No error or deferred result, perform registration immediately. */
+ return srna;
+}
+
+struct BPy_PropIDParse {
+ const char *value;
+ StructRNA *srna;
+ /**
+ * In the case registering this properly replaces an existing dynamic property.
+ * Store a handle to the property for removal.
+ * This is needed so the property removal is deferred until all other arguments
+ * have been validated, otherwise failure elsewhere could leave the property un-registered.
+ */
+ void *prop_free_handle;
+};
+
+/**
+ * Use with #PyArg_ParseTuple's `O&` formatting.
+ */
+static int bpy_prop_arg_parse_id(PyObject *o, void *p)
+{
+ struct BPy_PropIDParse *parse_data = p;
+ StructRNA *srna = parse_data->srna;
+
+ if (!PyUnicode_Check(o)) {
+ PyErr_Format(PyExc_TypeError, "expected a string (got %.200s)", Py_TYPE(o)->tp_name);
+ return 0;
+ }
+
+ Py_ssize_t id_len;
+ const char *id;
+
+ id = PyUnicode_AsUTF8AndSize(o, &id_len);
+ if (UNLIKELY(id_len >= MAX_IDPROP_NAME)) {
+ PyErr_Format(PyExc_TypeError, "'%.200s' too long, max length is %d", id, MAX_IDPROP_NAME - 1);
+ return 0;
+ }
+
+ parse_data->prop_free_handle = NULL;
+ if (UNLIKELY(RNA_def_property_free_identifier_deferred_prepare(
+ srna, id, &parse_data->prop_free_handle) == -1)) {
+ PyErr_Format(PyExc_TypeError,
+ "'%s' is defined as a non-dynamic type for '%s'",
+ id,
+ RNA_struct_identifier(srna));
+ return 0;
+ }
+ parse_data->value = id;
+ return 1;
+}
+
+/**
+ * Needed so #RNA_struct_property_tag_defines can be called on the `srna`.
+ */
+struct BPy_EnumProperty_Parse_WithSRNA {
+ struct BPy_EnumProperty_Parse base;
+ StructRNA *srna;
+};
+
+/**
+ * Wrapper for #pyrna_enum_bitfield_parse_set
+ * that looks up tags from the `srna`.
+ */
+static int bpy_prop_arg_parse_tag_defines(PyObject *o, void *p)
+{
+ struct BPy_EnumProperty_Parse_WithSRNA *parse_data = p;
+ parse_data->base.items = RNA_struct_property_tag_defines(parse_data->srna);
+ if (parse_data->base.items == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "property-tags not available for '%s'",
+ RNA_struct_identifier(parse_data->srna));
+ return 0;
+ }
+ return pyrna_enum_bitfield_parse_set(o, &parse_data->base);
+}
/** \} */
@@ -2355,8 +2590,9 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
"value in the UI.\n"
#define BPY_PROPDEF_VECSIZE_DOC \
- " :arg size: Vector dimensions in [1, " STRINGIFY(PYRNA_STACK_ARRAY) "].\n" \
-" :type size: int\n"
+ " :arg size: Vector dimensions in [1, " STRINGIFY(PYRNA_STACK_ARRAY) "]. " \
+"An int sequence can be used to define multi-dimension arrays.\n" \
+" :type size: int or int sequence\n"
#define BPY_PROPDEF_INT_STEP_DOC \
" :arg step: Step of increment/decrement in UI, in [1, 100], defaults to 1 (WARNING: unused " \
@@ -2415,11 +2651,15 @@ static int bpy_struct_id_used(StructRNA *srna, char *identifier)
/* -------------------------------------------------------------------- */
/** \name Module Methods
+ *
+ * Functions that register RNA.
+ *
+ * \note The `self` argument is NULL when called from Python,
+ * but being abused from C so we can pass the `srna` along.
+ * This isn't incorrect since its a Python object - but be careful.
+ *
* \{ */
-/* Function that sets RNA, NOTE - self is NULL when called from python,
- * but being abused from C so we can pass the srna along.
- * This isn't incorrect since its a python object - but be careful */
PyDoc_STRVAR(BPy_BoolProperty_doc,
".. function:: BoolProperty(name=\"\", "
"description=\"\", "
@@ -2440,95 +2680,108 @@ PyDoc_STRVAR(BPy_BoolProperty_doc,
static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(BoolProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "";
- Py_ssize_t id_len;
- bool def = false;
- PropertyRNA *prop;
- PyObject *pyopts = NULL;
- PyObject *pyopts_override = NULL;
- int opts = 0;
- int opts_override = 0;
- int prop_tags = 0;
- const char *pysubtype = NULL;
- int subtype = PROP_NONE;
- PyObject *update_fn = NULL;
- PyObject *get_fn = NULL;
- PyObject *set_fn = NULL;
- PyObject *py_tags = NULL;
-
- static const char *_keywords[] = {
- "attr",
- "name",
- "description",
- "default",
- "options",
- "override",
- "tags",
- "subtype",
- "update",
- "get",
- "set",
- NULL,
- };
- static _PyArg_Parser _parser = {"s#|$ssO&O!O!O!sOOO:BoolProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &name,
- &description,
- PyC_ParseBool,
- &def,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &pysubtype,
- &update_fn,
- &get_fn,
- &set_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_BoolProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
+
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ bool default_value = false;
+ PropertyRNA *prop;
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ struct BPy_EnumProperty_Parse subtype_enum = {
+ .items = property_subtype_number_items,
+ .value = PROP_NONE,
+ };
- BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty,
- property_flag_items,
- property_flag_override_items,
- property_subtype_number_items);
+ PyObject *update_fn = NULL;
+ PyObject *get_fn = NULL;
+ PyObject *set_fn = NULL;
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
- return NULL;
- }
+ static const char *_keywords[] = {
+ "attr",
+ "name",
+ "description",
+ "default",
+ "options",
+ "override",
+ "tags",
+ "subtype",
+ "update",
+ "get",
+ "set",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"O&|$ssO&O&O&O&O&OOO:BoolProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &name,
+ &description,
+ PyC_ParseBool,
+ &default_value,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ pyrna_enum_value_parse_string,
+ &subtype_enum,
+ &update_fn,
+ &get_fn,
+ &set_fn)) {
+ return NULL;
+ }
+
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
+ return NULL;
+ }
- prop = RNA_def_property(srna, id, PROP_BOOLEAN, subtype);
- RNA_def_property_boolean_default(prop, def);
- RNA_def_property_ui_text(prop, name ? name : id, description);
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_property(srna, id_data.value, PROP_BOOLEAN, subtype_enum.value);
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
- }
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
- }
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_boolean(prop, get_fn, set_fn);
- RNA_def_property_duplicate_pointers(srna, prop);
+ RNA_def_property_boolean_default(prop, default_value);
+ RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
+ }
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
}
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_boolean(prop, get_fn, set_fn);
+ RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
@@ -2556,120 +2809,136 @@ PyDoc_STRVAR(
static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(BoolVectorProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "";
- Py_ssize_t id_len;
- bool def[PYRNA_STACK_ARRAY] = {0};
- int size = 3;
- PropertyRNA *prop;
- PyObject *pydef = NULL;
- PyObject *pyopts = NULL;
- PyObject *pyopts_override = NULL;
- int opts = 0;
- int opts_override = 0;
- int prop_tags = 0;
- const char *pysubtype = NULL;
- int subtype = PROP_NONE;
- PyObject *update_fn = NULL;
- PyObject *get_fn = NULL;
- PyObject *set_fn = NULL;
- PyObject *py_tags = NULL;
-
- static const char *_keywords[] = {
- "attr",
- "name",
- "description",
- "default",
- "options",
- "override",
- "tags",
- "subtype",
- "size",
- "update",
- "get",
- "set",
- NULL,
- };
- static _PyArg_Parser _parser = {"s#|$ssOO!O!O!siOOO:BoolVectorProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &name,
- &description,
- &pydef,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &pysubtype,
- &size,
- &update_fn,
- &get_fn,
- &set_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(
+ self, args, kw, pymeth_BoolVectorProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
- BPY_PROPDEF_SUBTYPE_CHECK(BoolVectorProperty,
- property_flag_items,
- property_flag_override_items,
- property_subtype_array_items);
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ bool default_value[RNA_MAX_ARRAY_DIMENSION][PYRNA_STACK_ARRAY] = {{false}};
+ struct BPyPropArrayLength array_len_info = {.len_total = 3};
+ PropertyRNA *prop;
+ PyObject *default_py = NULL;
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ struct BPy_EnumProperty_Parse subtype_enum = {
+ .items = property_subtype_array_items,
+ .value = PROP_NONE,
+ };
+ PyObject *update_fn = NULL;
+ PyObject *get_fn = NULL;
+ PyObject *set_fn = NULL;
- if (size < 1 || size > PYRNA_STACK_ARRAY) {
- PyErr_Format(
- PyExc_TypeError,
- "BoolVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY),
- size);
- return NULL;
- }
+ static const char *_keywords[] = {
+ "attr",
+ "name",
+ "description",
+ "default",
+ "options",
+ "override",
+ "tags",
+ "subtype",
+ "size",
+ "update",
+ "get",
+ "set",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"O&|$ssOO&O&O&O&O&OOO:BoolVectorProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &name,
+ &description,
+ &default_py,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ pyrna_enum_value_parse_string,
+ &subtype_enum,
+ bpy_prop_array_length_parse,
+ &array_len_info,
+ &update_fn,
+ &get_fn,
+ &set_fn)) {
+ return NULL;
+ }
- if (pydef &&
- PyC_AsArray(
- def, pydef, size, &PyBool_Type, false, "BoolVectorProperty(default=sequence)") == -1) {
+ if (default_py != NULL) {
+ if (bpy_prop_array_from_py_with_dims(default_value[0],
+ sizeof(*default_value[0]),
+ default_py,
+ &array_len_info,
+ &PyBool_Type,
+ "BoolVectorProperty(default=sequence)") == -1) {
return NULL;
}
+ }
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
- return NULL;
- }
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
+ return NULL;
+ }
-#if 0
- prop = RNA_def_boolean_array(
- srna, id, size, pydef ? def : NULL, name ? name : id, description);
-#endif
- prop = RNA_def_property(srna, id, PROP_BOOLEAN, subtype);
- RNA_def_property_array(prop, size);
- if (pydef) {
- RNA_def_property_boolean_array_default(prop, def);
- }
- RNA_def_property_ui_text(prop, name ? name : id, description);
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_property(srna, id_data.value, PROP_BOOLEAN, subtype_enum.value);
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
+ if (array_len_info.dims_len == 0) {
+ RNA_def_property_array(prop, array_len_info.len_total);
+ if (default_py != NULL) {
+ RNA_def_property_boolean_array_default(prop, default_value[0]);
}
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
+ }
+ else {
+ RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims);
+ if (default_py != NULL) {
+ RNA_def_property_boolean_array_default(prop, &default_value[0][0]);
}
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_boolean_array(prop, get_fn, set_fn);
- RNA_def_property_duplicate_pointers(srna, prop);
}
+ RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
+ }
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_boolean_array(prop, get_fn, set_fn);
+ RNA_def_property_duplicate_pointers(srna, prop);
+
Py_RETURN_NONE;
}
@@ -2699,106 +2968,122 @@ PyDoc_STRVAR(
static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(IntProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "";
- Py_ssize_t id_len;
- int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX, step = 1, def = 0;
- PropertyRNA *prop;
- PyObject *pyopts = NULL;
- int opts = 0;
- PyObject *pyopts_override = NULL;
- int opts_override = 0;
- int prop_tags = 0;
- const char *pysubtype = NULL;
- int subtype = PROP_NONE;
- PyObject *update_fn = NULL;
- PyObject *get_fn = NULL;
- PyObject *set_fn = NULL;
- PyObject *py_tags = NULL;
-
- static const char *_keywords[] = {
- "attr",
- "name",
- "description",
- "default",
- "min",
- "max",
- "soft_min",
- "soft_max",
- "step",
- "options",
- "override",
- "tags",
- "subtype",
- "update",
- "get",
- "set",
- NULL,
- };
- static _PyArg_Parser _parser = {"s#|$ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &name,
- &description,
- &def,
- &min,
- &max,
- &soft_min,
- &soft_max,
- &step,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &pysubtype,
- &update_fn,
- &get_fn,
- &set_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_IntProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
- BPY_PROPDEF_SUBTYPE_CHECK(IntProperty,
- property_flag_items,
- property_flag_override_items,
- property_subtype_number_items);
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX;
+ int step = 1;
+ int default_value = 0;
+ PropertyRNA *prop;
+
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ struct BPy_EnumProperty_Parse subtype_enum = {
+ .items = property_subtype_number_items,
+ .value = PROP_NONE,
+ };
+ PyObject *update_fn = NULL;
+ PyObject *get_fn = NULL;
+ PyObject *set_fn = NULL;
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
- return NULL;
- }
+ static const char *_keywords[] = {
+ "attr",
+ "name",
+ "description",
+ "default",
+ "min",
+ "max",
+ "soft_min",
+ "soft_max",
+ "step",
+ "options",
+ "override",
+ "tags",
+ "subtype",
+ "update",
+ "get",
+ "set",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"O&|$ssiiiiiiO&O&O&O&OOO:IntProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &name,
+ &description,
+ &default_value,
+ &min,
+ &max,
+ &soft_min,
+ &soft_max,
+ &step,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ pyrna_enum_value_parse_string,
+ &subtype_enum,
+ &update_fn,
+ &get_fn,
+ &set_fn)) {
+ return NULL;
+ }
+
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
+ return NULL;
+ }
- prop = RNA_def_property(srna, id, PROP_INT, subtype);
- RNA_def_property_int_default(prop, def);
- RNA_def_property_ui_text(prop, name ? name : id, description);
- RNA_def_property_range(prop, min, max);
- RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, 3);
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_property(srna, id_data.value, PROP_INT, subtype_enum.value);
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
- }
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
- }
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_int(prop, get_fn, set_fn);
- RNA_def_property_duplicate_pointers(srna, prop);
+ RNA_def_property_int_default(prop, default_value);
+ RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
+ RNA_def_property_range(prop, min, max);
+ RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, 3);
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
}
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_int(prop, get_fn, set_fn);
+ RNA_def_property_duplicate_pointers(srna, prop);
+
Py_RETURN_NONE;
}
@@ -2832,128 +3117,151 @@ PyDoc_STRVAR(BPy_IntVectorProperty_doc,
static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(IntVectorProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "";
- Py_ssize_t id_len;
- int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX, step = 1;
- int def[PYRNA_STACK_ARRAY] = {0};
- int size = 3;
- PropertyRNA *prop;
- PyObject *pydef = NULL;
- PyObject *pyopts = NULL;
- int opts = 0;
- PyObject *pyopts_override = NULL;
- int opts_override = 0;
- int prop_tags = 0;
- const char *pysubtype = NULL;
- int subtype = PROP_NONE;
- PyObject *update_fn = NULL;
- PyObject *get_fn = NULL;
- PyObject *set_fn = NULL;
- PyObject *py_tags = NULL;
-
- static const char *_keywords[] = {
- "attr",
- "name",
- "description",
- "default",
- "min",
- "max",
- "soft_min",
- "soft_max",
- "step",
- "options",
- "override",
- "tags",
- "subtype",
- "size",
- "update",
- "get",
- "set",
- NULL,
- };
- static _PyArg_Parser _parser = {"s#|$ssOiiiiiO!O!O!siOOO:IntVectorProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &name,
- &description,
- &pydef,
- &min,
- &max,
- &soft_min,
- &soft_max,
- &step,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &pysubtype,
- &size,
- &update_fn,
- &get_fn,
- &set_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(
+ self, args, kw, pymeth_IntVectorProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
+
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX;
+ int step = 1;
+ int default_value[RNA_MAX_ARRAY_DIMENSION][PYRNA_STACK_ARRAY] = {0};
+ struct BPyPropArrayLength array_len_info = {.len_total = 3};
+ PropertyRNA *prop;
+ PyObject *default_py = NULL;
+
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ struct BPy_EnumProperty_Parse subtype_enum = {
+ .items = property_subtype_array_items,
+ .value = PROP_NONE,
+ };
+ PyObject *update_fn = NULL;
+ PyObject *get_fn = NULL;
+ PyObject *set_fn = NULL;
- BPY_PROPDEF_SUBTYPE_CHECK(IntVectorProperty,
- property_flag_items,
- property_flag_override_items,
- property_subtype_array_items);
+ static const char *_keywords[] = {
+ "attr",
+ "name",
+ "description",
+ "default",
+ "min",
+ "max",
+ "soft_min",
+ "soft_max",
+ "step",
+ "options",
+ "override",
+ "tags",
+ "subtype",
+ "size",
+ "update",
+ "get",
+ "set",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"O&|$ssOiiiiiO&O&O&O&O&OOO:IntVectorProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &name,
+ &description,
+ &default_py,
+ &min,
+ &max,
+ &soft_min,
+ &soft_max,
+ &step,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ pyrna_enum_value_parse_string,
+ &subtype_enum,
+ bpy_prop_array_length_parse,
+ &array_len_info,
+ &update_fn,
+ &get_fn,
+ &set_fn)) {
+ return NULL;
+ }
- if (size < 1 || size > PYRNA_STACK_ARRAY) {
- PyErr_Format(
- PyExc_TypeError,
- "IntVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY),
- size);
+ if (default_py != NULL) {
+ if (bpy_prop_array_from_py_with_dims(default_value[0],
+ sizeof(*default_value[0]),
+ default_py,
+ &array_len_info,
+ &PyLong_Type,
+ "IntVectorProperty(default=sequence)") == -1) {
return NULL;
}
+ }
- if (pydef &&
- PyC_AsArray(
- def, pydef, size, &PyLong_Type, false, "IntVectorProperty(default=sequence)") == -1) {
- return NULL;
- }
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
+ return NULL;
+ }
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
- return NULL;
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_property(srna, id_data.value, PROP_INT, subtype_enum.value);
+
+ if (array_len_info.dims_len == 0) {
+ RNA_def_property_array(prop, array_len_info.len_total);
+ if (default_py != NULL) {
+ RNA_def_property_int_array_default(prop, default_value[0]);
}
- if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
- return NULL;
+ }
+ else {
+ RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims);
+ if (default_py != NULL) {
+ RNA_def_property_int_array_default(prop, &default_value[0][0]);
}
+ }
- prop = RNA_def_property(srna, id, PROP_INT, subtype);
- RNA_def_property_array(prop, size);
- if (pydef) {
- RNA_def_property_int_array_default(prop, def);
- }
- RNA_def_property_range(prop, min, max);
- RNA_def_property_ui_text(prop, name ? name : id, description);
- RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, 3);
+ RNA_def_property_range(prop, min, max);
+ RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
+ RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, 3);
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
- }
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
- }
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_int_array(prop, get_fn, set_fn);
- RNA_def_property_duplicate_pointers(srna, prop);
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
}
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_int_array(prop, get_fn, set_fn);
+ RNA_def_property_duplicate_pointers(srna, prop);
+
Py_RETURN_NONE;
}
@@ -2986,103 +3294,117 @@ PyDoc_STRVAR(BPy_FloatProperty_doc,
static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(FloatProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "";
- Py_ssize_t id_len;
- float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX, step = 3,
- def = 0.0f;
- int precision = 2;
- PropertyRNA *prop;
- PyObject *pyopts = NULL;
- int opts = 0;
- PyObject *pyopts_override = NULL;
- int opts_override = 0;
- int prop_tags = 0;
- const char *pysubtype = NULL;
- int subtype = PROP_NONE;
- const char *pyunit = NULL;
- int unit = PROP_UNIT_NONE;
- PyObject *update_fn = NULL;
- PyObject *get_fn = NULL;
- PyObject *set_fn = NULL;
- PyObject *py_tags = NULL;
-
- static const char *_keywords[] = {
- "attr", "name", "description", "default", "min", "max", "soft_min",
- "soft_max", "step", "precision", "options", "override", "tags", "subtype",
- "unit", "update", "get", "set", NULL,
- };
- static _PyArg_Parser _parser = {"s#|$ssffffffiO!O!O!ssOOO:FloatProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &name,
- &description,
- &def,
- &min,
- &max,
- &soft_min,
- &soft_max,
- &step,
- &precision,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &pysubtype,
- &pyunit,
- &update_fn,
- &get_fn,
- &set_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_FloatProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
- BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty,
- property_flag_items,
- property_flag_override_items,
- property_subtype_number_items);
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX;
+ float step = 3;
+ float default_value = 0.0f;
+ int precision = 2;
+ PropertyRNA *prop;
+
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ struct BPy_EnumProperty_Parse subtype_enum = {
+ .items = property_subtype_number_items,
+ .value = PROP_NONE,
+ };
+ struct BPy_EnumProperty_Parse unit_enum = {
+ .items = rna_enum_property_unit_items,
+ .value = PROP_UNIT_NONE,
+ };
- if (pyunit && RNA_enum_value_from_id(rna_enum_property_unit_items, pyunit, &unit) == 0) {
- PyErr_Format(PyExc_TypeError, "FloatProperty(unit='%s'): invalid unit", pyunit);
- return NULL;
- }
+ PyObject *update_fn = NULL;
+ PyObject *get_fn = NULL;
+ PyObject *set_fn = NULL;
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
- return NULL;
- }
+ static const char *_keywords[] = {
+ "attr", "name", "description", "default", "min", "max", "soft_min",
+ "soft_max", "step", "precision", "options", "override", "tags", "subtype",
+ "unit", "update", "get", "set", NULL,
+ };
+ static _PyArg_Parser _parser = {"O&|$ssffffffiO&O&O&O&O&OOO:FloatProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &name,
+ &description,
+ &default_value,
+ &min,
+ &max,
+ &soft_min,
+ &soft_max,
+ &step,
+ &precision,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ pyrna_enum_value_parse_string,
+ &subtype_enum,
+ pyrna_enum_value_parse_string,
+ &unit_enum,
+ &update_fn,
+ &get_fn,
+ &set_fn)) {
+ return NULL;
+ }
- prop = RNA_def_property(srna, id, PROP_FLOAT, subtype | unit);
- RNA_def_property_float_default(prop, def);
- RNA_def_property_range(prop, min, max);
- RNA_def_property_ui_text(prop, name ? name : id, description);
- RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, precision);
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
+ return NULL;
+ }
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
- }
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
- }
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_float(prop, get_fn, set_fn);
- RNA_def_property_duplicate_pointers(srna, prop);
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_property(srna, id_data.value, PROP_FLOAT, subtype_enum.value | unit_enum.value);
+
+ RNA_def_property_float_default(prop, default_value);
+ RNA_def_property_range(prop, min, max);
+ RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
+ RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, precision);
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
+ }
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
}
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_float(prop, get_fn, set_fn);
+ RNA_def_property_duplicate_pointers(srna, prop);
+
Py_RETURN_NONE;
}
@@ -3118,123 +3440,149 @@ PyDoc_STRVAR(BPy_FloatVectorProperty_doc,
static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(FloatVectorProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "";
- Py_ssize_t id_len;
- float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX, step = 3;
- float def[PYRNA_STACK_ARRAY] = {0.0f};
- int precision = 2, size = 3;
- PropertyRNA *prop;
- PyObject *pydef = NULL;
- PyObject *pyopts = NULL;
- int opts = 0;
- PyObject *pyopts_override = NULL;
- int opts_override = 0;
- int prop_tags = 0;
- const char *pysubtype = NULL;
- int subtype = PROP_NONE;
- const char *pyunit = NULL;
- int unit = PROP_UNIT_NONE;
- PyObject *update_fn = NULL;
- PyObject *get_fn = NULL;
- PyObject *set_fn = NULL;
- PyObject *py_tags = NULL;
-
- static const char *_keywords[] = {
- "attr", "name", "description", "default", "min", "max", "soft_min",
- "soft_max", "step", "precision", "options", "override", "tags", "subtype",
- "unit", "size", "update", "get", "set", NULL,
- };
- static _PyArg_Parser _parser = {"s#|$ssOfffffiO!O!O!ssiOOO:FloatVectorProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &name,
- &description,
- &pydef,
- &min,
- &max,
- &soft_min,
- &soft_max,
- &step,
- &precision,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &pysubtype,
- &pyunit,
- &size,
- &update_fn,
- &get_fn,
- &set_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(
+ self, args, kw, pymeth_FloatVectorProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
- BPY_PROPDEF_SUBTYPE_CHECK(FloatVectorProperty,
- property_flag_items,
- property_flag_override_items,
- property_subtype_array_items);
-
- if (pyunit && RNA_enum_value_from_id(rna_enum_property_unit_items, pyunit, &unit) == 0) {
- PyErr_Format(PyExc_TypeError, "FloatVectorProperty(unit='%s'): invalid unit", pyunit);
- return NULL;
- }
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX;
+ float step = 3;
+ float default_value[RNA_MAX_ARRAY_DIMENSION][PYRNA_STACK_ARRAY] = {{0.0f}};
+ int precision = 2;
+ struct BPyPropArrayLength array_len_info = {.len_total = 3};
+ PropertyRNA *prop;
+ PyObject *default_py = NULL;
+
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ struct BPy_EnumProperty_Parse subtype_enum = {
+ .items = property_subtype_array_items,
+ .value = PROP_NONE,
+ };
+ struct BPy_EnumProperty_Parse unit_enum = {
+ .items = rna_enum_property_unit_items,
+ .value = PROP_UNIT_NONE,
+ };
- if (size < 1 || size > PYRNA_STACK_ARRAY) {
- PyErr_Format(
- PyExc_TypeError,
- "FloatVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY),
- size);
- return NULL;
- }
+ PyObject *update_fn = NULL;
+ PyObject *get_fn = NULL;
+ PyObject *set_fn = NULL;
- if (pydef &&
- PyC_AsArray(
- def, pydef, size, &PyFloat_Type, false, "FloatVectorProperty(default=sequence)") ==
- -1) {
- return NULL;
- }
+ static const char *_keywords[] = {
+ "attr", "name", "description", "default", "min", "max", "soft_min",
+ "soft_max", "step", "precision", "options", "override", "tags", "subtype",
+ "unit", "size", "update", "get", "set", NULL,
+ };
+ static _PyArg_Parser _parser = {
+ "O&|$ssOfffffiO&O&O&O&O&O&OOO:FloatVectorProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &name,
+ &description,
+ &default_py,
+ &min,
+ &max,
+ &soft_min,
+ &soft_max,
+ &step,
+ &precision,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ pyrna_enum_value_parse_string,
+ &subtype_enum,
+ pyrna_enum_value_parse_string,
+ &unit_enum,
+ bpy_prop_array_length_parse,
+ &array_len_info,
+ &update_fn,
+ &get_fn,
+ &set_fn)) {
+ return NULL;
+ }
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ if (default_py != NULL) {
+ if (bpy_prop_array_from_py_with_dims(default_value[0],
+ sizeof(*default_value[0]),
+ default_py,
+ &array_len_info,
+ &PyFloat_Type,
+ "FloatVectorProperty(default=sequence)") == -1) {
return NULL;
}
- if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
- return NULL;
+ if (bpy_prop_array_is_matrix_compatible_ex(subtype_enum.value, &array_len_info)) {
+ bpy_prop_array_matrix_swap_row_column_vn(&default_value[0][0], &array_len_info);
}
+ }
- prop = RNA_def_property(srna, id, PROP_FLOAT, subtype | unit);
- RNA_def_property_array(prop, size);
- if (pydef) {
- RNA_def_property_float_array_default(prop, def);
- }
- RNA_def_property_range(prop, min, max);
- RNA_def_property_ui_text(prop, name ? name : id, description);
- RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, precision);
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
+ return NULL;
+ }
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_property(srna, id_data.value, PROP_FLOAT, subtype_enum.value | unit_enum.value);
+
+ if (array_len_info.dims_len == 0) {
+ RNA_def_property_array(prop, array_len_info.len_total);
+ if (default_py != NULL) {
+ RNA_def_property_float_array_default(prop, default_value[0]);
}
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
+ }
+ else {
+ RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims);
+ if (default_py != NULL) {
+ RNA_def_property_float_array_default(prop, &default_value[0][0]);
}
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_float_array(prop, get_fn, set_fn);
- RNA_def_property_duplicate_pointers(srna, prop);
}
+
+ RNA_def_property_range(prop, min, max);
+ RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
+ RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, precision);
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
+ }
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_float_array(prop, get_fn, set_fn);
+ RNA_def_property_duplicate_pointers(srna, prop);
+
Py_RETURN_NONE;
}
@@ -3262,102 +3610,116 @@ PyDoc_STRVAR(BPy_StringProperty_doc,
static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(StringProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "", *def = "";
- Py_ssize_t id_len;
- int maxlen = 0;
- PropertyRNA *prop;
- PyObject *pyopts = NULL;
- int opts = 0;
- PyObject *pyopts_override = NULL;
- int opts_override = 0;
- int prop_tags = 0;
- const char *pysubtype = NULL;
- int subtype = PROP_NONE;
- PyObject *update_fn = NULL;
- PyObject *get_fn = NULL;
- PyObject *set_fn = NULL;
- PyObject *py_tags = NULL;
-
- static const char *_keywords[] = {
- "attr",
- "name",
- "description",
- "default",
- "maxlen",
- "options",
- "override",
- "tags",
- "subtype",
- "update",
- "get",
- "set",
- NULL,
- };
- static _PyArg_Parser _parser = {"s#|$sssiO!O!O!sOOO:StringProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &name,
- &description,
- &def,
- &maxlen,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &pysubtype,
- &update_fn,
- &get_fn,
- &set_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_StringProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
- BPY_PROPDEF_SUBTYPE_CHECK(StringProperty,
- property_flag_items,
- property_flag_override_items,
- property_subtype_string_items);
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "", *default_value = "";
+ int maxlen = 0;
+ PropertyRNA *prop;
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
- return NULL;
- }
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ struct BPy_EnumProperty_Parse subtype_enum = {
+ .items = property_subtype_string_items,
+ .value = PROP_NONE,
+ };
+ PyObject *update_fn = NULL;
+ PyObject *get_fn = NULL;
+ PyObject *set_fn = NULL;
- prop = RNA_def_property(srna, id, PROP_STRING, subtype);
- if (maxlen != 0) {
- /* +1 since it includes null terminator. */
- RNA_def_property_string_maxlength(prop, maxlen + 1);
- }
- if (def && def[0]) {
- RNA_def_property_string_default(prop, def);
- }
- RNA_def_property_ui_text(prop, name ? name : id, description);
+ static const char *_keywords[] = {
+ "attr",
+ "name",
+ "description",
+ "default",
+ "maxlen",
+ "options",
+ "override",
+ "tags",
+ "subtype",
+ "update",
+ "get",
+ "set",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"O&|$sssiO&O&O&O&OOO:StringProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &name,
+ &description,
+ &default_value,
+ &maxlen,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ pyrna_enum_value_parse_string,
+ &subtype_enum,
+ &update_fn,
+ &get_fn,
+ &set_fn)) {
+ return NULL;
+ }
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
- }
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
- }
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_string(prop, get_fn, set_fn);
- RNA_def_property_duplicate_pointers(srna, prop);
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
}
+ if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
+ return NULL;
+ }
+
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_property(srna, id_data.value, PROP_STRING, subtype_enum.value);
+
+ if (maxlen != 0) {
+ /* +1 since it includes null terminator. */
+ RNA_def_property_string_maxlength(prop, maxlen + 1);
+ }
+ if (default_value && default_value[0]) {
+ RNA_def_property_string_default(prop, default_value);
+ }
+ RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
+ }
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_string(prop, get_fn, set_fn);
+ RNA_def_property_duplicate_pointers(srna, prop);
+
Py_RETURN_NONE;
}
@@ -3419,151 +3781,167 @@ PyDoc_STRVAR(
static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(EnumProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "";
- PyObject *def = NULL;
- Py_ssize_t id_len;
- int defvalue = 0;
- PyObject *items, *items_fast;
- const EnumPropertyItem *eitems;
- PropertyRNA *prop;
- PyObject *pyopts = NULL;
- int opts = 0;
- PyObject *pyopts_override = NULL;
- int opts_override = 0;
- int prop_tags = 0;
- bool is_itemf = false;
- PyObject *update_fn = NULL;
- PyObject *get_fn = NULL;
- PyObject *set_fn = NULL;
- PyObject *py_tags = NULL;
-
- static const char *_keywords[] = {
- "attr",
- "items",
- "name",
- "description",
- "default",
- "options",
- "override",
- "tags",
- "update",
- "get",
- "set",
- NULL,
- };
- static _PyArg_Parser _parser = {"s#O|$ssOO!O!O!OOO:EnumProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &items,
- &name,
- &description,
- &def,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &update_fn,
- &get_fn,
- &set_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_EnumProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
- BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items, property_flag_override_items);
-
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
- return NULL;
- }
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ PyObject *default_py = NULL;
+ int default_value = 0;
+ PyObject *items, *items_fast;
+ const EnumPropertyItem *eitems;
+ PropertyRNA *prop;
+
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_enum_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ bool is_itemf = false;
+ PyObject *update_fn = NULL;
+ PyObject *get_fn = NULL;
+ PyObject *set_fn = NULL;
- if (def == Py_None) {
- /* This allows to get same behavior when explicitly passing None as default value,
- * and not defining a default value at all! */
- def = NULL;
- }
+ static const char *_keywords[] = {
+ "attr",
+ "items",
+ "name",
+ "description",
+ "default",
+ "options",
+ "override",
+ "tags",
+ "update",
+ "get",
+ "set",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"O&O|$ssOO&O&O&OOO:EnumProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &items,
+ &name,
+ &description,
+ &default_py,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ &update_fn,
+ &get_fn,
+ &set_fn)) {
+ return NULL;
+ }
- /* items can be a list or a callable */
- if (PyFunction_Check(
- items)) { /* don't use PyCallable_Check because we need the function code for errors */
- PyCodeObject *f_code = (PyCodeObject *)PyFunction_GET_CODE(items);
- if (f_code->co_argcount != 2) {
- PyErr_Format(PyExc_ValueError,
- "EnumProperty(...): expected 'items' function to take 2 arguments, not %d",
- f_code->co_argcount);
- return NULL;
- }
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
+ return NULL;
+ }
- if (def) {
- /* Only support getting integer default values here. */
- if (!py_long_as_int(def, &defvalue)) {
- /* NOTE: using type error here is odd but python does this for invalid arguments. */
- PyErr_SetString(
- PyExc_TypeError,
- "EnumProperty(...): 'default' can only be an integer when 'items' is a function");
- return NULL;
- }
- }
+ if (default_py == Py_None) {
+ /* This allows to get same behavior when explicitly passing None as default value,
+ * and not defining a default value at all! */
+ default_py = NULL;
+ }
- is_itemf = true;
- eitems = DummyRNA_NULL_items;
+ /* items can be a list or a callable */
+ if (PyFunction_Check(
+ items)) { /* don't use PyCallable_Check because we need the function code for errors */
+ PyCodeObject *f_code = (PyCodeObject *)PyFunction_GET_CODE(items);
+ if (f_code->co_argcount != 2) {
+ PyErr_Format(PyExc_ValueError,
+ "EnumProperty(...): expected 'items' function to take 2 arguments, not %d",
+ f_code->co_argcount);
+ return NULL;
}
- else {
- if (!(items_fast = PySequence_Fast(
- items,
- "EnumProperty(...): "
- "expected a sequence of tuples for the enum items or a function"))) {
- return NULL;
- }
-
- eitems = enum_items_from_py(items_fast, def, &defvalue, (opts & PROP_ENUM_FLAG) != 0);
- if (!eitems) {
- Py_DECREF(items_fast);
+ if (default_py) {
+ /* Only support getting integer default values here. */
+ if (!py_long_as_int(default_py, &default_value)) {
+ /* NOTE: using type error here is odd but python does this for invalid arguments. */
+ PyErr_SetString(
+ PyExc_TypeError,
+ "EnumProperty(...): 'default' can only be an integer when 'items' is a function");
return NULL;
}
}
- if (opts & PROP_ENUM_FLAG) {
- prop = RNA_def_enum_flag(srna, id, eitems, defvalue, name ? name : id, description);
- }
- else {
- prop = RNA_def_enum(srna, id, eitems, defvalue, name ? name : id, description);
+ is_itemf = true;
+ eitems = DummyRNA_NULL_items;
+ }
+ else {
+ if (!(items_fast = PySequence_Fast(
+ items,
+ "EnumProperty(...): "
+ "expected a sequence of tuples for the enum items or a function"))) {
+ return NULL;
}
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
- }
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
- }
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_enum(prop, get_fn, set_fn, (is_itemf ? items : NULL));
- RNA_def_property_duplicate_pointers(srna, prop);
+ eitems = enum_items_from_py(
+ items_fast, (options_enum.value & PROP_ENUM_FLAG) != 0, default_py, &default_value);
- if (is_itemf == false) {
- /* NOTE: this must be postponed until after #RNA_def_property_duplicate_pointers
- * otherwise if this is a generator it may free the strings before we copy them */
+ if (!eitems) {
Py_DECREF(items_fast);
-
- MEM_freeN((void *)eitems);
+ return NULL;
}
}
+
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ if (options_enum.value & PROP_ENUM_FLAG) {
+ prop = RNA_def_enum_flag(
+ srna, id_data.value, eitems, default_value, name ? name : id_data.value, description);
+ }
+ else {
+ prop = RNA_def_enum(
+ srna, id_data.value, eitems, default_value, name ? name : id_data.value, description);
+ }
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
+ }
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_enum(prop, get_fn, set_fn, (is_itemf ? items : NULL));
+ RNA_def_property_duplicate_pointers(srna, prop);
+
+ if (is_itemf == false) {
+ /* NOTE: this must be postponed until after #RNA_def_property_duplicate_pointers
+ * otherwise if this is a generator it may free the strings before we copy them */
+ Py_DECREF(items_fast);
+
+ MEM_freeN((void *)eitems);
+ }
+
Py_RETURN_NONE;
}
@@ -3609,94 +3987,111 @@ PyDoc_STRVAR(BPy_PointerProperty_doc,
PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(PointerProperty);
-
- if (srna) {
- const char *id = NULL, *name = NULL, *description = "";
- Py_ssize_t id_len;
- PropertyRNA *prop;
- StructRNA *ptype;
- PyObject *type = Py_None;
- PyObject *pyopts = NULL;
- PyObject *pyopts_override = NULL;
- PyObject *py_tags = NULL;
- int opts = 0;
- int opts_override = 0;
- int prop_tags = 0;
- PyObject *update_fn = NULL, *poll_fn = NULL;
-
- static const char *_keywords[] = {
- "attr",
- "type",
- "name",
- "description",
- "options",
- "override",
- "tags",
- "poll",
- "update",
- NULL,
- };
- static _PyArg_Parser _parser = {"s#O|$ssO!O!O!OO:PointerProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &type,
- &name,
- &description,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags,
- &poll_fn,
- &update_fn)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(
+ self, args, kw, pymeth_PointerProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
- BPY_PROPDEF_CHECK(PointerProperty, property_flag_items, property_flag_override_items);
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ PropertyRNA *prop;
+ StructRNA *ptype;
+ PyObject *type = Py_None;
+
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
+ PyObject *update_fn = NULL, *poll_fn = NULL;
- ptype = pointer_type_from_py(type, "PointerProperty(...)");
- if (!ptype) {
- return NULL;
- }
- if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) {
- PyErr_Format(PyExc_TypeError,
- "PointerProperty(...) expected an RNA type derived from %.200s or %.200s",
- RNA_struct_ui_name(&RNA_ID),
- RNA_struct_ui_name(&RNA_PropertyGroup));
- return NULL;
- }
- if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
- return NULL;
- }
- if (bpy_prop_callback_check(poll_fn, "poll", 2) == -1) {
- return NULL;
- }
- prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description);
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
- }
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
- }
+ static const char *_keywords[] = {
+ "attr",
+ "type",
+ "name",
+ "description",
+ "options",
+ "override",
+ "tags",
+ "poll",
+ "update",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"O&O|$ssO&O&O&OO:PointerProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &type,
+ &name,
+ &description,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum,
+ &poll_fn,
+ &update_fn)) {
+ return NULL;
+ }
- if (RNA_struct_idprops_contains_datablock(ptype)) {
- if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
- RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
- }
+ ptype = pointer_type_from_py(type, "PointerProperty(...)");
+ if (!ptype) {
+ return NULL;
+ }
+ if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) {
+ PyErr_Format(PyExc_TypeError,
+ "PointerProperty(...) expected an RNA type derived from %.200s or %.200s",
+ RNA_struct_ui_name(&RNA_ID),
+ RNA_struct_ui_name(&RNA_PropertyGroup));
+ return NULL;
+ }
+ if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
+ return NULL;
+ }
+ if (bpy_prop_callback_check(poll_fn, "poll", 2) == -1) {
+ return NULL;
+ }
+
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_pointer_runtime(
+ srna, id_data.value, ptype, name ? name : id_data.value, description);
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
+ }
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+
+ if (RNA_struct_idprops_contains_datablock(ptype)) {
+ if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
+ RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
}
- bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_pointer(prop, poll_fn);
- RNA_def_property_duplicate_pointers(srna, prop);
}
+ bpy_prop_callback_assign_update(prop, update_fn);
+ bpy_prop_callback_assign_pointer(prop, poll_fn);
+ RNA_def_property_duplicate_pointers(srna, prop);
+
Py_RETURN_NONE;
}
@@ -3715,83 +4110,98 @@ PyDoc_STRVAR(BPy_CollectionProperty_doc,
PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
-
- BPY_PROPDEF_HEAD(CollectionProperty);
-
- if (srna) {
- Py_ssize_t id_len;
- const char *id = NULL, *name = NULL, *description = "";
- PropertyRNA *prop;
- StructRNA *ptype;
- PyObject *type = Py_None;
- PyObject *pyopts = NULL;
- PyObject *pyopts_override = NULL;
- PyObject *py_tags = NULL;
- int opts = 0;
- int opts_override = 0;
- int prop_tags = 0;
-
- static const char *_keywords[] = {
- "attr",
- "type",
- "name",
- "description",
- "options",
- "override",
- "tags",
- NULL,
- };
- static _PyArg_Parser _parser = {"s#O|$ssO!O!O!:CollectionProperty", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args,
- kw,
- &_parser,
- &id,
- &id_len,
- &type,
- &name,
- &description,
- &PySet_Type,
- &pyopts,
- &PySet_Type,
- &pyopts_override,
- &PySet_Type,
- &py_tags)) {
- return NULL;
+ { /* Keep this block first. */
+ PyObject *deferred_result;
+ srna = bpy_prop_deferred_data_or_srna(
+ self, args, kw, pymeth_CollectionProperty, &deferred_result);
+ if (srna == NULL) {
+ return deferred_result;
}
+ }
- BPY_PROPDEF_CHECK(
- CollectionProperty, property_flag_items, property_flag_override_collection_items);
+ struct BPy_PropIDParse id_data = {
+ .srna = srna,
+ };
+ const char *name = NULL, *description = "";
+ PropertyRNA *prop;
+ StructRNA *ptype;
+ PyObject *type = Py_None;
+
+ struct BPy_EnumProperty_Parse options_enum = {
+ .items = property_flag_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse override_enum = {
+ .items = property_flag_override_collection_items,
+ .value = 0,
+ };
+ struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+ .srna = srna,
+ };
- ptype = pointer_type_from_py(type, "CollectionProperty(...):");
- if (!ptype) {
- return NULL;
- }
+ static const char *_keywords[] = {
+ "attr",
+ "type",
+ "name",
+ "description",
+ "options",
+ "override",
+ "tags",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"O&O|$ssO&O&O&:CollectionProperty", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ bpy_prop_arg_parse_id,
+ &id_data,
+ &type,
+ &name,
+ &description,
+ pyrna_enum_bitfield_parse_set,
+ &options_enum,
+ pyrna_enum_bitfield_parse_set,
+ &override_enum,
+ bpy_prop_arg_parse_tag_defines,
+ &tags_enum)) {
+ return NULL;
+ }
- if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) {
- PyErr_Format(PyExc_TypeError,
- "CollectionProperty(...) expected an RNA type derived from %.200s",
- RNA_struct_ui_name(&RNA_PropertyGroup));
- return NULL;
- }
+ ptype = pointer_type_from_py(type, "CollectionProperty(...):");
+ if (!ptype) {
+ return NULL;
+ }
- prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description);
- if (py_tags) {
- RNA_def_property_tags(prop, prop_tags);
- }
- if (pyopts) {
- bpy_prop_assign_flag(prop, opts);
- }
- if (pyopts_override) {
- bpy_prop_assign_flag_override(prop, opts_override);
- }
+ if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) {
+ PyErr_Format(PyExc_TypeError,
+ "CollectionProperty(...) expected an RNA type derived from %.200s",
+ RNA_struct_ui_name(&RNA_PropertyGroup));
+ return NULL;
+ }
- if (RNA_struct_idprops_contains_datablock(ptype)) {
- if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
- RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
- }
+ if (id_data.prop_free_handle != NULL) {
+ RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
+ }
+ prop = RNA_def_collection_runtime(
+ srna, id_data.value, ptype, name ? name : id_data.value, description);
+
+ if (tags_enum.base.is_set) {
+ RNA_def_property_tags(prop, tags_enum.base.value);
+ }
+ if (options_enum.is_set) {
+ bpy_prop_assign_flag(prop, options_enum.value);
+ }
+ if (override_enum.is_set) {
+ bpy_prop_assign_flag_override(prop, override_enum.value);
+ }
+
+ if (RNA_struct_idprops_contains_datablock(ptype)) {
+ if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
+ RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
}
- RNA_def_property_duplicate_pointers(srna, prop);
}
+ RNA_def_property_duplicate_pointers(srna, prop);
+
Py_RETURN_NONE;
}
diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h
index bc7f0cfe416..e6bbd6f708d 100644
--- a/source/blender/python/intern/bpy_props.h
+++ b/source/blender/python/intern/bpy_props.h
@@ -33,7 +33,10 @@ StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix);
typedef struct {
PyObject_HEAD
- /* This isn't GC tracked, it's a function from `bpy.props` so it's not going away. */
+ /**
+ * Internally a #PyCFunctionObject type.
+ * \note This isn't GC tracked, it's a function from `bpy.props` so it's not going away.
+ */
void *fn;
PyObject *kw;
} BPy_PropDeferred;
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index e6f3e509469..dff96f74d62 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -817,6 +817,47 @@ int pyrna_enum_value_from_id(const EnumPropertyItem *item,
return 0;
}
+/**
+ * Use with #PyArg_ParseTuple's `O&` formatting.
+ */
+int pyrna_enum_value_parse_string(PyObject *o, void *p)
+{
+ const char *identifier = PyUnicode_AsUTF8(o);
+ if (identifier == NULL) {
+ PyErr_Format(PyExc_TypeError, "expected a string enum, not %.200s", Py_TYPE(o)->tp_name);
+ return 0;
+ }
+ struct BPy_EnumProperty_Parse *parse_data = p;
+ if (pyrna_enum_value_from_id(
+ parse_data->items, identifier, &parse_data->value, "enum identifier") == -1) {
+ return 0;
+ }
+
+ parse_data->value_orig = o;
+ parse_data->is_set = true;
+ return 1;
+}
+
+/**
+ * Use with #PyArg_ParseTuple's `O&` formatting.
+ */
+int pyrna_enum_bitfield_parse_set(PyObject *o, void *p)
+{
+ if (!PySet_Check(o)) {
+ PyErr_Format(PyExc_TypeError, "expected a set, not %.200s", Py_TYPE(o)->tp_name);
+ return 0;
+ }
+
+ struct BPy_EnumProperty_Parse *parse_data = p;
+ if (pyrna_set_to_enum_bitfield(
+ parse_data->items, o, &parse_data->value, "enum identifier set") == -1) {
+ return 0;
+ }
+ parse_data->value_orig = o;
+ parse_data->is_set = true;
+ return 1;
+}
+
/* NOTE(campbell): Regarding comparison `__cmp__`:
* checking the 'ptr->data' matches works in almost all cases,
* however there are a few RNA properties that are fake sub-structs and
@@ -4254,7 +4295,7 @@ static PyObject *pyrna_struct_dir(BPy_StructRNA *self)
}
PyDoc_STRVAR(pyrna_struct_id_properties_ensure_doc,
- ".. method:: id_properties_ensure()\n"
+ ".. method:: id_properties_ensure()\n\n"
" :return: the parent group for an RNA struct's custom IDProperties.\n"
" :rtype: :class:`bpy.types.IDPropertyGroup`\n");
static PyObject *pyrna_struct_id_properties_ensure(BPy_StructRNA *self)
@@ -4282,7 +4323,7 @@ static PyObject *pyrna_struct_id_properties_ensure(BPy_StructRNA *self)
}
PyDoc_STRVAR(pyrna_struct_id_properties_clear_doc,
- ".. method:: id_properties_clear()\n"
+ ".. method:: id_properties_clear()\n\n"
" :return: Remove the parent group for an RNA struct's custom IDProperties.\n");
static PyObject *pyrna_struct_id_properties_clear(BPy_StructRNA *self)
{
@@ -7424,10 +7465,13 @@ static PyObject *pyrna_srna_Subtype(StructRNA *srna)
PyObject *metaclass;
const char *idname = RNA_struct_identifier(srna);
- /* Remove __doc__ for now. */
- // const char *descr = RNA_struct_ui_description(srna);
- // if (!descr) descr = "(no docs)";
- // "__doc__", descr
+ /* Remove `__doc__` for now because we don't need it to generate docs. */
+#if 0
+ const char *descr = RNA_struct_ui_description(srna);
+ if (!descr) {
+ descr = "(no docs)";
+ }
+#endif
if (RNA_struct_idprops_check(srna) &&
!PyObject_IsSubclass(py_base, (PyObject *)&pyrna_struct_meta_idprop_Type)) {
@@ -7996,14 +8040,21 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item
PyObject *py_kw = ((BPy_PropDeferred *)item)->kw;
PyObject *py_srna_cobject, *py_ret;
+ /* Show the function name in errors to help give context. */
+ BLI_assert(PyCFunction_CheckExact(py_func));
+ PyMethodDef *py_func_method_def = ((PyCFunctionObject *)py_func)->m_ml;
+ const char *func_name = py_func_method_def->ml_name;
+
PyObject *args_fake;
+ const char *key_str = PyUnicode_AsUTF8(key);
- if (*PyUnicode_AsUTF8(key) == '_') {
+ if (*key_str == '_') {
PyErr_Format(PyExc_ValueError,
"bpy_struct \"%.200s\" registration error: "
- "%.200s could not register because the property starts with an '_'\n",
+ "'%.200s' %.200s could not register because it starts with an '_'",
RNA_struct_identifier(srna),
- PyUnicode_AsUTF8(key));
+ key_str,
+ func_name);
return -1;
}
py_srna_cobject = PyCapsule_New(srna, NULL, NULL);
@@ -8022,8 +8073,12 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item
*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_CollectionProperty) &&
RNA_struct_idprops_contains_datablock(type_srna)) {
PyErr_Format(PyExc_ValueError,
- "bpy_struct \"%.200s\" doesn't support datablock properties\n",
- RNA_struct_identifier(srna));
+ "bpy_struct \"%.200s\" registration error: "
+ "'%.200s' %.200s could not register because "
+ "this type doesn't support data-block properties",
+ RNA_struct_identifier(srna),
+ key_str,
+ func_name);
return -1;
}
}
@@ -8041,12 +8096,12 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item
Py_DECREF(args_fake); /* Free's py_srna_cobject too. */
- // PyC_LineSpit();
PyErr_Format(PyExc_ValueError,
"bpy_struct \"%.200s\" registration error: "
- "%.200s could not register\n",
+ "'%.200s' %.200s could not register (see previous error)",
RNA_struct_identifier(srna),
- PyUnicode_AsUTF8(key));
+ key_str,
+ func_name);
return -1;
}
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index 75534c05d6c..24dbad53eb3 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -213,6 +213,25 @@ int pyrna_set_to_enum_bitfield(const struct EnumPropertyItem *items,
int *r_value,
const char *error_prefix);
+/**
+ * Data for #pyrna_enum_value_parse_string & #pyrna_enum_bitfield_parse_set parsing utilities.
+ * Use with #PyArg_ParseTuple's `O&` formatting.
+ */
+struct BPy_EnumProperty_Parse {
+ const EnumPropertyItem *items;
+ /**
+ * Set when the value was successfully parsed.
+ * Useful if the input ever needs to be included in an error message.
+ * (if the value is not supported under certain conditions).
+ */
+ PyObject *value_orig;
+
+ int value;
+ bool is_set;
+};
+int pyrna_enum_value_parse_string(PyObject *o, void *p);
+int pyrna_enum_bitfield_parse_set(PyObject *o, void *p);
+
int pyrna_enum_value_from_id(const EnumPropertyItem *item,
const char *identifier,
int *value,
diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c
index 9745f39b6b8..a716f4ab9e5 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -605,7 +605,7 @@ PyObject *pyrna_struct_driver_add(BPy_StructRNA *self, PyObject *args)
DEG_relations_tag_update(CTX_data_main(context));
}
else {
- /* XXX, should be handled by reports, */
+ /* XXX: should be handled by reports. */
PyErr_SetString(PyExc_TypeError,
"bpy_struct.driver_add(): failed because of an internal error");
return NULL;
diff --git a/source/blender/python/intern/bpy_rna_callback.c b/source/blender/python/intern/bpy_rna_callback.c
index 7f8ea54ab98..adc0f425b52 100644
--- a/source/blender/python/intern/bpy_rna_callback.c
+++ b/source/blender/python/intern/bpy_rna_callback.c
@@ -288,76 +288,52 @@ PyObject *pyrna_callback_classmethod_add(PyObject *UNUSED(self), PyObject *args)
/* class specific callbacks */
if (srna == &RNA_WindowManager) {
- const char *error_prefix = "WindowManager.draw_cursor_add";
struct {
- const char *space_type_str;
- const char *region_type_str;
-
- int space_type;
- int region_type;
+ struct BPy_EnumProperty_Parse space_type_enum;
+ struct BPy_EnumProperty_Parse region_type_enum;
} params = {
- .space_type_str = NULL,
- .region_type_str = NULL,
- .space_type = SPACE_TYPE_ANY,
- .region_type = RGN_TYPE_ANY,
+ .space_type_enum = {.items = rna_enum_space_type_items, .value = SPACE_TYPE_ANY},
+ .region_type_enum = {.items = rna_enum_region_type_items, .value = RGN_TYPE_ANY},
};
if (!PyArg_ParseTuple(args,
- "OOO!|ss:WindowManager.draw_cursor_add",
+ "OOO!|O&O&:WindowManager.draw_cursor_add",
&cls,
&cb_func, /* already assigned, no matter */
&PyTuple_Type,
&cb_args,
- &params.space_type_str,
- &params.region_type_str)) {
- return NULL;
- }
-
- if (params.space_type_str && pyrna_enum_value_from_id(rna_enum_space_type_items,
- params.space_type_str,
- &params.space_type,
- error_prefix) == -1) {
- return NULL;
- }
- if (params.region_type_str && pyrna_enum_value_from_id(rna_enum_region_type_items,
- params.region_type_str,
- &params.region_type,
- error_prefix) == -1) {
+ pyrna_enum_value_parse_string,
+ &params.space_type_enum,
+ pyrna_enum_value_parse_string,
+ &params.region_type_enum)) {
return NULL;
}
- handle = WM_paint_cursor_activate(
- params.space_type, params.region_type, NULL, cb_wm_cursor_draw, (void *)args);
+ handle = WM_paint_cursor_activate(params.space_type_enum.value,
+ params.region_type_enum.value,
+ NULL,
+ cb_wm_cursor_draw,
+ (void *)args);
}
else if (RNA_struct_is_a(srna, &RNA_Space)) {
- const char *error_prefix = "Space.draw_handler_add";
struct {
- const char *region_type_str;
- const char *event_str;
-
- int region_type;
- int event;
- } params;
+ struct BPy_EnumProperty_Parse region_type_enum;
+ struct BPy_EnumProperty_Parse event_enum;
+ } params = {
+ .region_type_enum = {.items = rna_enum_region_type_items},
+ .event_enum = {.items = region_draw_mode_items},
+ };
if (!PyArg_ParseTuple(args,
- "OOO!ss:Space.draw_handler_add",
+ "OOO!O&O&:Space.draw_handler_add",
&cls,
&cb_func, /* already assigned, no matter */
&PyTuple_Type,
&cb_args,
- &params.region_type_str,
- &params.event_str)) {
- return NULL;
- }
-
- if (pyrna_enum_value_from_id(
- region_draw_mode_items, params.event_str, &params.event, error_prefix) == -1) {
- return NULL;
- }
- if (pyrna_enum_value_from_id(rna_enum_region_type_items,
- params.region_type_str,
- &params.region_type,
- error_prefix) == -1) {
+ pyrna_enum_value_parse_string,
+ &params.region_type_enum,
+ pyrna_enum_value_parse_string,
+ &params.event_enum)) {
return NULL;
}
@@ -368,12 +344,14 @@ PyObject *pyrna_callback_classmethod_add(PyObject *UNUSED(self), PyObject *args)
}
SpaceType *st = BKE_spacetype_from_id(spaceid);
- ARegionType *art = BKE_regiontype_from_id(st, params.region_type);
+ ARegionType *art = BKE_regiontype_from_id(st, params.region_type_enum.value);
if (art == NULL) {
- PyErr_Format(PyExc_TypeError, "region type '%.200s' not in space", params.region_type_str);
+ PyErr_Format(
+ PyExc_TypeError, "region type %R not in space", params.region_type_enum.value_orig);
return NULL;
}
- handle = ED_region_draw_cb_activate(art, cb_region_draw, (void *)args, params.event);
+ handle = ED_region_draw_cb_activate(
+ art, cb_region_draw, (void *)args, params.event_enum.value);
}
else {
PyErr_SetString(PyExc_TypeError, "callback_add(): type does not support callbacks");
@@ -430,37 +408,37 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
else if (RNA_struct_is_a(srna, &RNA_Space)) {
const char *error_prefix = "Space.draw_handler_remove";
struct {
- const char *region_type_str;
-
- int region_type;
- } params;
+ struct BPy_EnumProperty_Parse region_type_enum;
+ } params = {
+ .region_type_enum = {.items = rna_enum_region_type_items},
+ };
if (!PyArg_ParseTuple(args,
- "OO!s:Space.draw_handler_remove",
+ "OO!O&:Space.draw_handler_remove",
&cls,
&PyCapsule_Type,
&py_handle, /* already assigned, no matter */
- &params.region_type_str)) {
- return NULL;
- }
-
- if (pyrna_enum_value_from_id(rna_enum_region_type_items,
- params.region_type_str,
- &params.region_type,
- error_prefix) == -1) {
+ pyrna_enum_value_parse_string,
+ &params.region_type_enum)) {
return NULL;
}
const eSpace_Type spaceid = rna_Space_refine_reverse(srna);
if (spaceid == SPACE_EMPTY) {
- PyErr_Format(PyExc_TypeError, "unknown space type '%.200s'", RNA_struct_identifier(srna));
+ PyErr_Format(PyExc_TypeError,
+ "%s: unknown space type '%.200s'",
+ error_prefix,
+ RNA_struct_identifier(srna));
return NULL;
}
SpaceType *st = BKE_spacetype_from_id(spaceid);
- ARegionType *art = BKE_regiontype_from_id(st, params.region_type);
+ ARegionType *art = BKE_regiontype_from_id(st, params.region_type_enum.value);
if (art == NULL) {
- PyErr_Format(PyExc_TypeError, "region type '%.200s' not in space", params.region_type_str);
+ PyErr_Format(PyExc_TypeError,
+ "%s: region type %R not in space",
+ error_prefix,
+ params.region_type_enum.value_orig);
return NULL;
}
ED_region_draw_cb_exit(art, handle);
diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c
index 869019692df..f121bfd6e36 100644
--- a/source/blender/python/intern/bpy_rna_gizmo.c
+++ b/source/blender/python/intern/bpy_rna_gizmo.c
@@ -80,10 +80,10 @@ static void py_rna_gizmo_handler_get_cb(const wmGizmo *UNUSED(gz),
}
else {
if (PyC_AsArray(value,
+ sizeof(*value),
ret,
gz_prop->type->array_length,
&PyFloat_Type,
- false,
"Gizmo get callback: ") == -1) {
goto fail;
}
@@ -103,6 +103,8 @@ fail:
PyErr_Print();
PyErr_Clear();
+ Py_DECREF(ret);
+
PyGILState_Release(gilstate);
}
@@ -139,6 +141,7 @@ static void py_rna_gizmo_handler_set_cb(const wmGizmo *UNUSED(gz),
if (ret == NULL) {
goto fail;
}
+ Py_DECREF(args);
Py_DECREF(ret);
PyGILState_Release(gilstate);
@@ -199,11 +202,11 @@ static void py_rna_gizmo_handler_range_get_cb(const wmGizmo *UNUSED(gz),
return;
fail:
- Py_XDECREF(ret);
-
PyErr_Print();
PyErr_Clear();
+ Py_XDECREF(ret);
+
PyGILState_Release(gilstate);
}
@@ -426,11 +429,11 @@ static PyObject *bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *ar
if (array_len != 0) {
float *value = BLI_array_alloca(value, array_len);
if (PyC_AsArray(value,
+ sizeof(*value),
params.value,
gz_prop->type->array_length,
&PyFloat_Type,
- false,
- "Gizmo target property array") == -1) {
+ "Gizmo target property array: ") == -1) {
goto fail;
}
WM_gizmo_target_property_float_set_array(BPY_context_get(), gz, gz_prop, value);
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index 66044311321..1bb68babc3c 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -377,9 +377,16 @@ static PyObject *bpy_orphans_purge(PyObject *UNUSED(self), PyObject *args, PyObj
bool do_recursive_cleanup = false;
static const char *_keywords[] = {"do_local_ids", "do_linked_ids", "do_recursive", NULL};
- static _PyArg_Parser _parser = {"|$ppp:orphans_purge", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(
- args, kwds, &_parser, &do_local_ids, &do_linked_ids, &do_recursive_cleanup)) {
+ static _PyArg_Parser _parser = {"|O&O&O&:orphans_purge", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ PyC_ParseBool,
+ &do_local_ids,
+ PyC_ParseBool,
+ &do_linked_ids,
+ PyC_ParseBool,
+ &do_recursive_cleanup)) {
return NULL;
}
diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c
index e5ac1ba9a95..aa8cf8f2a9f 100644
--- a/source/blender/python/intern/bpy_utils_units.c
+++ b/source/blender/python/intern/bpy_utils_units.c
@@ -67,7 +67,7 @@ static const char *bpyunits_ucategorie_items[] = {
/**
* These fields are just empty placeholders, actual values get set in initializations functions.
* This allows us to avoid many handwriting, and above all,
- * to keep all systems/categories definition stuff in ``BKE_unit.h``.
+ * to keep all systems/categories definition stuff in `BKE_unit.h`.
*/
static PyStructSequence_Field bpyunits_systems_fields[ARRAY_SIZE(bpyunits_usystem_items)];
static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategorie_items)];
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 16bf7120606..5beca7bd71a 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -78,7 +78,7 @@ static int mathutils_array_parse_fast(float *array,
}
/**
- * helper function that returns a Python ``__hash__``.
+ * helper function that returns a Python `__hash__`.
*
* \note consistent with the equivalent tuple of floats (CPython's 'tuplehash')
*/
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index dc98e3313c9..36b8b0b6d35 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -1174,7 +1174,7 @@ static void matrix_invert_with_det_n_internal(float *mat_dst,
}
/**
- * \param r_mat: can be from ``self->matrix`` or not.
+ * \param r_mat: can be from `self->matrix` or not.
*/
static bool matrix_invert_internal(const MatrixObject *self, float *r_mat)
{
@@ -1191,8 +1191,8 @@ static bool matrix_invert_internal(const MatrixObject *self, float *r_mat)
}
/**
- * Similar to ``matrix_invert_internal`` but should never error.
- * \param r_mat: can be from ``self->matrix`` or not.
+ * Similar to `matrix_invert_internal` but should never error.
+ * \param r_mat: can be from `self->matrix` or not.
*/
static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
{
@@ -2998,7 +2998,7 @@ static int Matrix_translation_set(MatrixObject *self, PyObject *value, void *UNU
return -1;
}
- if ((mathutils_array_parse(tvec, 3, 3, value, "Matrix.translation")) == -1) {
+ if (mathutils_array_parse(tvec, 3, 3, value, "Matrix.translation") == -1) {
return -1;
}
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index 88b3bddddf6..c73dea79aac 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -1505,6 +1505,9 @@ static PyObject *list_of_lists_from_arrays(const int *array,
PyObject *ret, *sublist;
int i, j, sublist_len, sublist_start, val;
+ if (array == NULL) {
+ return PyList_New(0);
+ }
ret = PyList_New(toplevel_len);
for (i = 0; i < toplevel_len; i++) {
sublist_len = len_table[i];
@@ -1521,7 +1524,8 @@ static PyObject *list_of_lists_from_arrays(const int *array,
PyDoc_STRVAR(
M_Geometry_delaunay_2d_cdt_doc,
- ".. function:: delaunay_2d_cdt(vert_coords, edges, faces, output_type, epsilon)\n"
+ ".. function:: delaunay_2d_cdt(vert_coords, edges, faces, output_type, epsilon, "
+ "need_ids=True)\n"
"\n"
" Computes the Constrained Delaunay Triangulation of a set of vertices,\n"
" with edges and faces that must appear in the triangulation.\n"
@@ -1533,6 +1537,8 @@ PyDoc_STRVAR(
" input element indices corresponding to the positionally same output element.\n"
" For edges, the orig indices start with the input edges and then continue\n"
" with the edges implied by each of the faces (n of them for an n-gon).\n"
+ " If the need_ids argument is supplied, and False, then the code skips the preparation\n"
+ " of the orig arrays, which may save some time."
"\n"
" :arg vert_coords: Vertex coordinates (2d)\n"
" :type vert_coords: list of :class:`mathutils.Vector`\n"
@@ -1543,10 +1549,14 @@ PyDoc_STRVAR(
" :arg output_type: What output looks like. 0 => triangles with convex hull. "
"1 => triangles inside constraints. "
"2 => the input constraints, intersected. "
- "3 => like 2 but with extra edges to make valid BMesh faces.\n"
+ "3 => like 2 but detect holes and omit them from output. "
+ "4 => like 2 but with extra edges to make valid BMesh faces. "
+ "5 => like 4 but detect holes and omit them from output.\n"
" :type output_type: int\\n"
" :arg epsilon: For nearness tests; should not be zero\n"
" :type epsilon: float\n"
+ " :arg need_ids: are the orig output arrays needed?\n"
+ " :type need_args: bool\n"
" :return: Output tuple, (vert_coords, edges, faces, orig_verts, orig_edges, orig_faces)\n"
" :rtype: (list of `mathutils.Vector`, "
"list of (int, int), "
@@ -1561,6 +1571,7 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar
PyObject *vert_coords, *edges, *faces, *item;
int output_type;
float epsilon;
+ bool need_ids = true;
float(*in_coords)[2] = NULL;
int(*in_edges)[2] = NULL;
int *in_faces = NULL;
@@ -1578,8 +1589,14 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar
PyObject *ret_value = NULL;
int i;
- if (!PyArg_ParseTuple(
- args, "OOOif:delaunay_2d_cdt", &vert_coords, &edges, &faces, &output_type, &epsilon)) {
+ if (!PyArg_ParseTuple(args,
+ "OOOif|p:delaunay_2d_cdt",
+ &vert_coords,
+ &edges,
+ &faces,
+ &output_type,
+ &epsilon,
+ &need_ids)) {
return NULL;
}
@@ -1609,6 +1626,7 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar
in.faces_start_table = in_faces_start_table;
in.faces_len_table = in_faces_len_table;
in.epsilon = epsilon;
+ in.need_ids = need_ids;
res = BLI_delaunay_2d_cdt_calc(&in, output_type);
diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c
index 707fd40e9d0..69d37b345c6 100644
--- a/source/blender/python/mathutils/mathutils_noise.c
+++ b/source/blender/python/mathutils/mathutils_noise.c
@@ -131,8 +131,7 @@ static void next_state(void)
ulong *p = state;
int j;
- /* if init_genrand() has not been called, */
- /* a default initial seed is used */
+ /* If init_genrand() has not been called, a default initial seed is used. */
if (initf == 0) {
init_genrand(5489UL);
}
diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c
index d5653f87c2b..76839651b5d 100644
--- a/source/blender/render/intern/bake.c
+++ b/source/blender/render/intern/bake.c
@@ -26,7 +26,7 @@
* The Bake API is fully implemented with Python rna functions.
* The operator expects/call a function:
*
- * ``def bake(scene, object, pass_type, object_id, pixel_array, num_pixels, depth, result)``
+ * `def bake(scene, object, pass_type, object_id, pixel_array, num_pixels, depth, result)`
* - scene: current scene (Python object)
* - object: object to render (Python object)
* - pass_type: pass to render (string, e.g., "COMBINED", "AO", "NORMAL", ...)
@@ -53,10 +53,10 @@
* \endcode
*
* In python you have access to:
- * - ``primitive_id``, ``object_id``, ``uv``, ``du_dx``, ``du_dy``, ``next``
- * - ``next()`` is a function that returns the next #BakePixel in the array.
+ * - `primitive_id`, `object_id`, `uv`, `du_dx`, `du_dy`, `next`.
+ * - `next()` is a function that returns the next #BakePixel in the array.
*
- * \note Pixels that should not be baked have ``primitive_id == -1``
+ * \note Pixels that should not be baked have `primitive_id == -1`.
*
* For a complete implementation example look at the Cycles Bake commit.
*/
@@ -1056,7 +1056,6 @@ int RE_pass_depth(const eScenePassType pass_type)
case SCE_PASS_NORMAL:
case SCE_PASS_VECTOR:
case SCE_PASS_INDEXOB: /* XXX double check */
- case SCE_PASS_RAYHITS: /* XXX double check */
case SCE_PASS_EMIT:
case SCE_PASS_ENVIRONMENT:
case SCE_PASS_INDEXMA:
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c
index 6329901b4ce..333ee9ecd33 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.c
@@ -2806,7 +2806,6 @@ RenderPass *RE_pass_find_by_type(volatile RenderLayer *rl, int passtype, const c
CHECK_PASS(INDEXOB);
CHECK_PASS(INDEXMA);
CHECK_PASS(MIST);
- CHECK_PASS(RAYHITS);
CHECK_PASS(DIFFUSE_DIRECT);
CHECK_PASS(DIFFUSE_INDIRECT);
CHECK_PASS(DIFFUSE_COLOR);
diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c
index 53252cdb42f..693cddbebbe 100644
--- a/source/blender/render/intern/render_result.c
+++ b/source/blender/render/intern/render_result.c
@@ -332,7 +332,6 @@ RenderResult *render_result_new(
BLI_strncpy(rl->name, view_layer->name, sizeof(rl->name));
rl->layflag = view_layer->layflag;
- /* for debugging: view_layer->passflag | SCE_PASS_RAYHITS; */
rl->passflag = view_layer->passflag;
rl->rectx = rectx;
@@ -399,9 +398,6 @@ RenderResult *render_result_new(
if (view_layer->passflag & SCE_PASS_MIST) {
RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_MIST, view, "Z");
}
- if (rl->passflag & SCE_PASS_RAYHITS) {
- RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 4, RE_PASSNAME_RAYHITS, view, "RGB");
- }
if (view_layer->passflag & SCE_PASS_DIFFUSE_DIRECT) {
RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_DIRECT, view, "RGB");
}
@@ -474,7 +470,7 @@ RenderResult *render_result_new(
}
/* NOTE: this has to be in sync with `scene.c`. */
- rl->layflag = 0x7FFF; /* solid ztra halo strand */
+ rl->layflag = SCE_LAY_FLAG_DEFAULT;
rl->passflag = SCE_PASS_COMBINED;
re->active_view_layer = 0;
@@ -581,7 +577,6 @@ static int passtype_from_name(const char *name)
CHECK_PASS(INDEXOB);
CHECK_PASS(INDEXMA);
CHECK_PASS(MIST);
- CHECK_PASS(RAYHITS);
CHECK_PASS(DIFFUSE_DIRECT);
CHECK_PASS(DIFFUSE_INDIRECT);
CHECK_PASS(DIFFUSE_COLOR);
diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c
index c4ee16a0ecc..62aee564626 100644
--- a/source/blender/render/intern/texture_image.c
+++ b/source/blender/render/intern/texture_image.c
@@ -932,8 +932,8 @@ static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata
#endif
/* `const int out =` */ ibuf_get_color_clip_bilerp(
tc, ibuf, ibuf->x * u, ibuf->y * v, AFD->intpol, AFD->extflag);
- /* TXF alpha: clip |= out;
- * TXF alpha: cw += out ? 0.0f : wt; */
+ /* 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;
diff --git a/source/blender/sequencer/SEQ_effects.h b/source/blender/sequencer/SEQ_effects.h
index 79a36be5a0b..4bd5b54b36b 100644
--- a/source/blender/sequencer/SEQ_effects.h
+++ b/source/blender/sequencer/SEQ_effects.h
@@ -70,9 +70,6 @@ struct SeqEffectHandle {
* 2: out = ibuf2 */
int (*early_out)(struct Sequence *seq, float facf0, float facf1);
- /* stores the y-range of the effect IPO */
- void (*store_icu_yrange)(struct Sequence *seq, short adrcode, float *ymin, float *ymax);
-
/* stores the default facf0 and facf1 if no IPO is present */
void (*get_default_fac)(struct Sequence *seq, float timeline_frame, float *facf0, float *facf1);
diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h
index aa2e182e1c0..cb2091511a9 100644
--- a/source/blender/sequencer/SEQ_iterator.h
+++ b/source/blender/sequencer/SEQ_iterator.h
@@ -30,9 +30,9 @@ extern "C" {
#include "BLI_ghash.h"
struct Editing;
-struct Sequence;
struct GSet;
struct GSetIterator;
+struct Sequence;
#define SEQ_ITERATOR_FOREACH(var, collection) \
for (SeqIterator iter = {{{NULL}}}; \
@@ -71,11 +71,13 @@ bool SEQ_iterator_ensure(SeqCollection *collection,
struct Sequence *SEQ_iterator_yield(SeqIterator *iterator);
SeqCollection *SEQ_collection_create(const char *name);
+SeqCollection *SEQ_collection_duplicate(SeqCollection *collection);
uint SEQ_collection_len(const SeqCollection *collection);
bool SEQ_collection_append_strip(struct Sequence *seq, SeqCollection *data);
bool SEQ_collection_remove_strip(struct Sequence *seq, SeqCollection *data);
void SEQ_collection_free(SeqCollection *collection);
void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src);
+void SEQ_collection_exclude(SeqCollection *collection, SeqCollection *exclude_elements);
void SEQ_collection_expand(struct ListBase *seqbase,
SeqCollection *collection,
void query_func(struct Sequence *seq_reference,
diff --git a/source/blender/sequencer/SEQ_proxy.h b/source/blender/sequencer/SEQ_proxy.h
index b06adef2802..7bfe932ff1c 100644
--- a/source/blender/sequencer/SEQ_proxy.h
+++ b/source/blender/sequencer/SEQ_proxy.h
@@ -34,8 +34,8 @@ struct ListBase;
struct Main;
struct Scene;
struct SeqIndexBuildContext;
-struct Sequence;
struct SeqRenderData;
+struct Sequence;
bool SEQ_proxy_rebuild_context(struct Main *bmain,
struct Depsgraph *depsgraph,
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 706f4064bf3..f4338d13c8f 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -32,8 +32,8 @@ extern "C" {
struct Editing;
struct Scene;
struct Sequence;
-struct SequencerToolSettings;
struct SequenceLookup;
+struct SequencerToolSettings;
/* RNA enums, just to be more readable */
enum {
diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h
index 593a8fe7bf2..732e9bb985a 100644
--- a/source/blender/sequencer/SEQ_time.h
+++ b/source/blender/sequencer/SEQ_time.h
@@ -44,7 +44,6 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene,
const bool do_unselected);
void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq);
void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq);
-int SEQ_time_cmp_time_startdisp(const void *a, const void *b);
bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame);
void SEQ_time_update_meta_strip_range(struct Scene *scene, struct Sequence *seq_meta);
diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h
index 837a2de5742..9ff827333be 100644
--- a/source/blender/sequencer/SEQ_transform.h
+++ b/source/blender/sequencer/SEQ_transform.h
@@ -29,8 +29,8 @@ extern "C" {
struct ListBase;
struct Scene;
-struct Sequence;
struct SeqCollection;
+struct Sequence;
int SEQ_transform_get_left_handle_frame(struct Sequence *seq);
int SEQ_transform_get_right_handle_frame(struct Sequence *seq);
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 0a8be3b33ae..7757271a2e5 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -1428,10 +1428,9 @@ static void do_mul_effect_byte(float facf0,
fac1 = (int)(256.0f * facf0);
fac3 = (int)(256.0f * facf1);
- /* formula:
- * fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + axaux = c * px + py * s; //+centx
- * yaux = -s * px + c * py; //+centy
- */
+ /* Formula:
+ * `fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + axaux = c * px + py * s;` // + centx
+ * `yaux = -s * px + c * py;` // + centy */
while (y--) {
@@ -1483,9 +1482,8 @@ static void do_mul_effect_float(
fac1 = facf0;
fac3 = facf1;
- /* formula:
- * fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + a
- */
+ /* Formula:
+ * `fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + a`. */
while (y--) {
x = xo;
@@ -3086,10 +3084,12 @@ static void init_speed_effect(Sequence *seq)
seq->effectdata = MEM_callocN(sizeof(SpeedControlVars), "speedcontrolvars");
v = (SpeedControlVars *)seq->effectdata;
- v->globalSpeed = 1.0;
v->frameMap = NULL;
- v->flags |= SEQ_SPEED_INTEGRATE; /* should be default behavior */
v->length = 0;
+ v->speed_control_type = SEQ_SPEED_STRETCH;
+ v->speed_fader = 1.0f;
+ v->speed_fader_length = 0.0f;
+ v->speed_fader_frame_number = 0.0f;
}
static void load_speed_effect(Sequence *seq)
@@ -3131,29 +3131,6 @@ static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(facf0), float UNU
return EARLY_DO_EFFECT;
}
-static void store_icu_yrange_speed(Sequence *seq, short UNUSED(adrcode), float *ymin, float *ymax)
-{
- SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
-
- /* if not already done, load / initialize data */
- SEQ_effect_handle_get(seq);
-
- if ((v->flags & SEQ_SPEED_INTEGRATE) != 0) {
- *ymin = -100.0;
- *ymax = 100.0;
- }
- else {
- if (v->flags & SEQ_SPEED_COMPRESS_IPO_Y) {
- *ymin = 0.0;
- *ymax = 1.0;
- }
- else {
- *ymin = 0.0;
- *ymax = seq->len;
- }
- }
-}
-
/**
* Generator strips with zero inputs have their length set to 1 permanently. In some cases it is
* useful to use speed effect on these strips because they can be animated. This can be done by
@@ -3174,7 +3151,6 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force)
float fallback_fac = 1.0f;
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
FCurve *fcu = NULL;
- int flags = v->flags;
/* if not already done, load / initialize data */
SEQ_effect_handle_get(seq);
@@ -3189,7 +3165,20 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force)
/* XXX(campbell): new in 2.5x. should we use the animation system this way?
* The fcurve is needed because many frames need evaluating at once. */
- fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
+ switch (v->speed_control_type) {
+ case SEQ_SPEED_MULTIPLY: {
+ fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
+ break;
+ }
+ case SEQ_SPEED_FRAME_NUMBER: {
+ fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_frame_number", 0, NULL);
+ break;
+ }
+ case SEQ_SPEED_LENGTH: {
+ fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_length", 0, NULL);
+ break;
+ }
+ }
if (!v->frameMap || v->length != seq->len) {
if (v->frameMap) {
MEM_freeN(v->frameMap);
@@ -3204,21 +3193,33 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force)
const int target_strip_length = seq_effect_speed_get_strip_content_length(seq->seq1);
- if (seq->flag & SEQ_USE_EFFECT_DEFAULT_FADE) {
+ if (v->speed_control_type == SEQ_SPEED_STRETCH) {
if ((seq->seq1->enddisp != seq->seq1->start) && (target_strip_length != 0)) {
fallback_fac = (float)target_strip_length / (float)(seq->seq1->enddisp - seq->seq1->start);
- flags = SEQ_SPEED_INTEGRATE;
fcu = NULL;
}
}
else {
/* if there is no fcurve, use value as simple multiplier */
if (!fcu) {
- fallback_fac = seq->speed_fader; /* Same as speed_factor in RNA. */
+ switch (v->speed_control_type) {
+ case SEQ_SPEED_MULTIPLY: {
+ fallback_fac = v->speed_fader;
+ break;
+ }
+ case SEQ_SPEED_FRAME_NUMBER: {
+ fallback_fac = v->speed_fader_frame_number;
+ break;
+ }
+ case SEQ_SPEED_LENGTH: {
+ fallback_fac = v->speed_fader_length;
+ break;
+ }
+ }
}
}
- if (flags & SEQ_SPEED_INTEGRATE) {
+ if (ELEM(v->speed_control_type, SEQ_SPEED_MULTIPLY, SEQ_SPEED_STRETCH)) {
float cursor = 0;
float facf;
@@ -3232,7 +3233,6 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force)
else {
facf = fallback_fac;
}
- facf *= v->globalSpeed;
cursor += facf;
@@ -3258,10 +3258,10 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force)
facf = fallback_fac;
}
- if (flags & SEQ_SPEED_COMPRESS_IPO_Y) {
+ if (v->speed_control_type == SEQ_SPEED_LENGTH) {
facf *= target_strip_length;
+ facf /= 100.0f;
}
- facf *= v->globalSpeed;
if (facf >= target_strip_length) {
facf = target_strip_length - 1;
@@ -4083,14 +4083,6 @@ static int early_out_mul_input2(Sequence *UNUSED(seq), float facf0, float facf1)
return EARLY_DO_EFFECT;
}
-static void store_icu_yrange_noop(Sequence *UNUSED(seq),
- short UNUSED(adrcode),
- float *UNUSED(ymin),
- float *UNUSED(ymax))
-{
- /* defaults are fine */
-}
-
static void get_default_fac_noop(Sequence *UNUSED(seq),
float UNUSED(timeline_frame),
float *facf0,
@@ -4130,7 +4122,6 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type)
rval.free = free_noop;
rval.early_out = early_out_noop;
rval.get_default_fac = get_default_fac_noop;
- rval.store_icu_yrange = store_icu_yrange_noop;
rval.execute = NULL;
rval.init_execution = init_execution;
rval.execute_slice = NULL;
@@ -4244,7 +4235,6 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type)
rval.copy = copy_speed_effect;
rval.execute = do_speed_effect;
rval.early_out = early_out_speed;
- rval.store_icu_yrange = store_icu_yrange_speed;
break;
case SEQ_TYPE_COLOR:
rval.init = init_solid_color;
diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c
index 20a2ee3b183..09f033e70fb 100644
--- a/source/blender/sequencer/intern/iterator.c
+++ b/source/blender/sequencer/intern/iterator.c
@@ -184,6 +184,22 @@ void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collecti
}
/**
+ * Remove strips from collection that are also in `exclude_elements`. Source collection will be
+ * freed.
+ *
+ * \param collection: collection from which strips are removed
+ * \param exclude_elements: collection of strips to be removed
+ */
+void SEQ_collection_exclude(SeqCollection *collection, SeqCollection *exclude_elements)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, exclude_elements) {
+ SEQ_collection_remove_strip(seq, collection);
+ }
+ SEQ_collection_free(exclude_elements);
+}
+
+/**
* Expand collection by running SEQ_query() for each strip, which will be used as reference.
* Results of these queries will be merged into provided collection.
*
@@ -213,6 +229,22 @@ void SEQ_collection_expand(ListBase *seqbase,
}
}
+/**
+ * Duplicate collection
+ *
+ * \param collection: collection to be duplicated
+ * \return duplicate of collection
+ */
+SeqCollection *SEQ_collection_duplicate(SeqCollection *collection)
+{
+ SeqCollection *duplicate = SEQ_collection_create(__func__);
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ SEQ_collection_append_strip(seq, duplicate);
+ }
+ return duplicate;
+}
+
/** \} */
/**
diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c
index b9278b9f971..e92afee08cd 100644
--- a/source/blender/sequencer/intern/strip_edit.c
+++ b/source/blender/sequencer/intern/strip_edit.c
@@ -112,8 +112,8 @@ static void seq_update_muting_recursive(ListBase *seqbasep, Sequence *metaseq, i
Sequence *seq;
int seqmute;
- /* for sound we go over full meta tree to update muted state,
- * since sound is played outside of evaluating the imbufs, */
+ /* For sound we go over full meta tree to update muted state,
+ * since sound is played outside of evaluating the imbufs. */
for (seq = seqbasep->first; seq; seq = seq->next) {
seqmute = (mute || (seq->flag & SEQ_MUTE));
@@ -407,6 +407,8 @@ Sequence *SEQ_edit_strip_split(Main *bmain,
BLI_addtail(&left_strips, seq);
}
+ SEQ_collection_free(collection);
+
/* Sort list, so that no strip can depend on next strip in list.
* This is important for SEQ_time_update_sequence functionality. */
SEQ_sort(&left_strips);
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index ecd6b0c833d..68128690773 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -111,8 +111,8 @@ static void seq_update_sound_bounds_recursive_impl(Scene *scene,
{
Sequence *seq;
- /* for sound we go over full meta tree to update bounds of the sound strips,
- * since sound is played outside of evaluating the imbufs, */
+ /* For sound we go over full meta tree to update bounds of the sound strips,
+ * since sound is played outside of evaluating the imbufs. */
for (seq = metaseq->seqbase.first; seq; seq = seq->next) {
if (seq->type == SEQ_TYPE_META) {
seq_update_sound_bounds_recursive_impl(
@@ -257,15 +257,6 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
}
}
-/** Comparison function suitable to be used with BLI_listbase_sort()... */
-int SEQ_time_cmp_time_startdisp(const void *a, const void *b)
-{
- const Sequence *seq_a = a;
- const Sequence *seq_b = b;
-
- return (seq_a->startdisp > seq_b->startdisp);
-}
-
int SEQ_time_find_next_prev_edit(Scene *scene,
int timeline_frame,
const short side,
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index b7989349ebe..c9af2fced65 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -269,9 +269,9 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep,
}
test->machine += channel_delta;
- SEQ_time_update_sequence(
- evil_scene,
- test); // XXX: I don't think this is needed since were only moving vertically, Campbell.
+
+ /* XXX: I don't think this is needed since were only moving vertically, Campbell. */
+ SEQ_time_update_sequence(evil_scene, test);
}
if ((test->machine < 1) || (test->machine > MAXSEQ)) {
diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c
index 8a259c6aaff..8cb1ea6b66c 100644
--- a/source/blender/shader_fx/intern/FX_ui_common.c
+++ b/source/blender/shader_fx/intern/FX_ui_common.c
@@ -9,7 +9,7 @@
* 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,
+ * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
diff --git a/source/blender/simulation/SIM_mass_spring.h b/source/blender/simulation/SIM_mass_spring.h
index 43de8b155cf..b3299258209 100644
--- a/source/blender/simulation/SIM_mass_spring.h
+++ b/source/blender/simulation/SIM_mass_spring.h
@@ -45,6 +45,8 @@ void SIM_mass_spring_solver_free(struct Implicit_Data *id);
int SIM_mass_spring_solver_numvert(struct Implicit_Data *id);
int SIM_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd);
+void SIM_mass_spring_set_implicit_vertex_mass(struct Implicit_Data *data, int index, float mass);
+
void SIM_cloth_solver_free(struct ClothModifierData *clmd);
int SIM_cloth_solve(struct Depsgraph *depsgraph,
struct Object *ob,
diff --git a/source/blender/simulation/intern/SIM_mass_spring.cpp b/source/blender/simulation/intern/SIM_mass_spring.cpp
index cf654ebff07..f9a22276363 100644
--- a/source/blender/simulation/intern/SIM_mass_spring.cpp
+++ b/source/blender/simulation/intern/SIM_mass_spring.cpp
@@ -203,7 +203,7 @@ int SIM_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
cloth->implicit = id = SIM_mass_spring_solver_create(cloth->mvert_num, nondiag);
for (i = 0; i < cloth->mvert_num; i++) {
- SIM_mass_spring_set_vertex_mass(id, i, verts[i].mass);
+ SIM_mass_spring_set_implicit_vertex_mass(id, i, verts[i].mass);
}
for (i = 0; i < cloth->mvert_num; i++) {
@@ -213,6 +213,11 @@ int SIM_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
return 1;
}
+void SIM_mass_spring_set_implicit_vertex_mass(Implicit_Data *data, int index, float mass)
+{
+ SIM_mass_spring_set_vertex_mass(data, index, mass);
+}
+
void SIM_cloth_solver_free(ClothModifierData *clmd)
{
Cloth *cloth = clmd->clothObject;
@@ -1047,18 +1052,22 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
SIM_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
- // BKE_sim_debug_data_add_circle(
- // clmd->debug_data, x, gdensity, 0.7, 0.3, 1,
- // "grid density", i, j, 3111);
+# if 0
+ BKE_sim_debug_data_add_circle(
+ clmd->debug_data, x, gdensity, 0.7, 0.3, 1,
+ "grid density", i, j, 3111);
+# endif
if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) {
float dvel[3];
sub_v3_v3v3(dvel, gvel_smooth, gvel);
- // BKE_sim_debug_data_add_vector(
- // clmd->debug_data, x, gvel, 0.4, 0, 1,
- // "grid velocity", i, j, 3112);
- // BKE_sim_debug_data_add_vector(
- // clmd->debug_data, x, gvel_smooth, 0.6, 1, 1,
- // "grid velocity", i, j, 3113);
+# if 0
+ BKE_sim_debug_data_add_vector(
+ clmd->debug_data, x, gvel, 0.4, 0, 1,
+ "grid velocity", i, j, 3112);
+ BKE_sim_debug_data_add_vector(
+ clmd->debug_data, x, gvel_smooth, 0.6, 1, 1,
+ "grid velocity", i, j, 3113);
+# endif
BKE_sim_debug_data_add_vector(
clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114);
# if 0
@@ -1069,12 +1078,14 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
interp_v3_v3v3(col, col0, col1,
CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0));
- // BKE_sim_debug_data_add_circle(
- // clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4,
- // "grid velocity", i, j, 3115);
- // BKE_sim_debug_data_add_dot(
- // clmd->debug_data, x, col[0], col[1], col[2],
- // "grid velocity", i, j, 3115);
+# if 0
+ BKE_sim_debug_data_add_circle(
+ clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4,
+ "grid velocity", i, j, 3115);
+ BKE_sim_debug_data_add_dot(
+ clmd->debug_data, x, col[0], col[1], col[2],
+ "grid velocity", i, j, 3115);
+# endif
BKE_sim_debug_data_add_circle(
clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115);
}
diff --git a/source/blender/simulation/intern/hair_volume.cpp b/source/blender/simulation/intern/hair_volume.cpp
index 4966fa2510d..348c8683be9 100644
--- a/source/blender/simulation/intern/hair_volume.cpp
+++ b/source/blender/simulation/intern/hair_volume.cpp
@@ -112,9 +112,11 @@ BLI_INLINE int hair_grid_interp_weights(
uvw[1] = (vec[1] - gmin[1]) * scale - (float)j;
uvw[2] = (vec[2] - gmin[2]) * scale - (float)k;
- // BLI_assert(0.0f <= uvw[0] && uvw[0] <= 1.0001f);
- // BLI_assert(0.0f <= uvw[1] && uvw[1] <= 1.0001f);
- // BLI_assert(0.0f <= uvw[2] && uvw[2] <= 1.0001f);
+#if 0
+ BLI_assert(0.0f <= uvw[0] && uvw[0] <= 1.0001f);
+ BLI_assert(0.0f <= uvw[1] && uvw[1] <= 1.0001f);
+ BLI_assert(0.0f <= uvw[2] && uvw[2] <= 1.0001f);
+#endif
return offset;
}
@@ -350,7 +352,7 @@ BLI_INLINE int hair_grid_weights(
weights[6] = dist_tent_v3f3(uvw, (float)i, (float)(j + 1), (float)(k + 1));
weights[7] = dist_tent_v3f3(uvw, (float)(i + 1), (float)(j + 1), (float)(k + 1));
- // BLI_assert(fabsf(weights_sum(weights) - 1.0f) < 0.0001f);
+ // BLI_assert(fabsf(weights_sum(weights) - 1.0f) < 0.0001f);
return offset;
}
@@ -488,9 +490,10 @@ BLI_INLINE void hair_volume_add_segment_2D(HairGrid *grid,
BKE_sim_debug_data_add_dot(x2w, 0.1, 0.1, 0.7, "grid", 649, debug_i, j, k);
BKE_sim_debug_data_add_line(wloc, x2w, 0.3, 0.8, 0.3, "grid", 253, debug_i, j, k);
BKE_sim_debug_data_add_line(wloc, x3w, 0.8, 0.3, 0.3, "grid", 254, debug_i, j, k);
- // BKE_sim_debug_data_add_circle(
- // x2w, len_v3v3(wloc, x2w), 0.2, 0.7, 0.2,
- // "grid", 255, i, j, k);
+# if 0
+ BKE_sim_debug_data_add_circle(
+ x2w, len_v3v3(wloc, x2w), 0.2, 0.7, 0.2, "grid", 255, i, j, k);
+# endif
}
}
# endif
@@ -1003,9 +1006,10 @@ bool SIM_hair_volume_solve_divergence(HairGrid *grid,
if (!is_margin) {
float dvel[3];
sub_v3_v3v3(dvel, vert->velocity_smooth, vert->velocity);
- // BKE_sim_debug_data_add_vector(
- // grid->debug_data, wloc, dvel, 1, 1, 1,
- // "grid", 5566, i, j, k);
+# if 0
+ BKE_sim_debug_data_add_vector(
+ grid->debug_data, wloc, dvel, 1, 1, 1, "grid", 5566, i, j, k);
+# endif
}
if (!is_margin) {
@@ -1015,11 +1019,12 @@ bool SIM_hair_volume_solve_divergence(HairGrid *grid,
float col[3];
interp_v3_v3v3(col, col0, colp, d);
- // if (d > 0.05f) {
- // BKE_sim_debug_data_add_dot(
- // grid->debug_data, wloc, col[0], col[1], col[2],
- // "grid", 5544, i, j, k);
- // }
+# if 0
+ if (d > 0.05f) {
+ BKE_sim_debug_data_add_dot(
+ grid->debug_data, wloc, col[0], col[1], col[2], "grid", 5544, i, j, k);
+ }
+# endif
}
}
}
diff --git a/source/blender/simulation/intern/implicit_blender.c b/source/blender/simulation/intern/implicit_blender.c
index 8aa3774a3f2..ad903e9da4a 100644
--- a/source/blender/simulation/intern/implicit_blender.c
+++ b/source/blender/simulation/intern/implicit_blender.c
@@ -180,8 +180,8 @@ DO_INLINE void mul_lfvectorS(float (*to)[3],
mul_fvector_S(to[i], fLongVector[i], scalar);
}
}
-/* Multiply long vector with scalar. */
-/* A -= B * float */
+/* Multiply long vector with scalar.
+ * `A -= B * float` */
DO_INLINE void submul_lfvectorS(float (*to)[3],
float (*fLongVector)[3],
float scalar,
@@ -209,7 +209,7 @@ DO_INLINE float dot_lfvector(float (*fLongVectorA)[3],
}
return temp;
}
-/* A = B + C --> for big vector */
+/* `A = B + C` -> for big vector. */
DO_INLINE void add_lfvector_lfvector(float (*to)[3],
float (*fLongVectorA)[3],
float (*fLongVectorB)[3],
@@ -221,7 +221,7 @@ DO_INLINE void add_lfvector_lfvector(float (*to)[3],
add_v3_v3v3(to[i], fLongVectorA[i], fLongVectorB[i]);
}
}
-/* A = B + C * float --> for big vector */
+/* `A = B + C * float` -> for big vector. */
DO_INLINE void add_lfvector_lfvectorS(float (*to)[3],
float (*fLongVectorA)[3],
float (*fLongVectorB)[3],
@@ -234,7 +234,7 @@ DO_INLINE void add_lfvector_lfvectorS(float (*to)[3],
VECADDS(to[i], fLongVectorA[i], fLongVectorB[i], bS);
}
}
-/* A = B * float + C * float --> for big vector */
+/* `A = B * float + C * float` -> for big vector */
DO_INLINE void add_lfvectorS_lfvectorS(float (*to)[3],
float (*fLongVectorA)[3],
float aS,
@@ -248,7 +248,7 @@ DO_INLINE void add_lfvectorS_lfvectorS(float (*to)[3],
VECADDSS(to[i], fLongVectorA[i], aS, fLongVectorB[i], bS);
}
}
-/* A = B - C * float --> for big vector */
+/* `A = B - C * float` -> for big vector. */
DO_INLINE void sub_lfvector_lfvectorS(float (*to)[3],
float (*fLongVectorA)[3],
float (*fLongVectorB)[3],
@@ -260,7 +260,7 @@ DO_INLINE void sub_lfvector_lfvectorS(float (*to)[3],
VECSUBS(to[i], fLongVectorA[i], fLongVectorB[i], bS);
}
}
-/* A = B - C --> for big vector */
+/* `A = B - C` -> for big vector. */
DO_INLINE void sub_lfvector_lfvector(float (*to)[3],
float (*fLongVectorA)[3],
float (*fLongVectorB)[3],
@@ -455,7 +455,7 @@ DO_INLINE void add_fmatrix_fmatrix(float to[3][3],
add_v3_v3v3(to[1], matrixA[1], matrixB[1]);
add_v3_v3v3(to[2], matrixA[2], matrixB[2]);
}
-/* A -= B*x + C*y (3x3 matrix sub-addition with 3x3 matrix) */
+/* `A -= B*x + (C * y)` (3x3 matrix sub-addition with 3x3 matrix). */
DO_INLINE void subadd_fmatrixS_fmatrixS(
float to[3][3], const float matrixA[3][3], float aS, const float matrixB[3][3], float bS)
{
@@ -463,7 +463,7 @@ DO_INLINE void subadd_fmatrixS_fmatrixS(
VECSUBADDSS(to[1], matrixA[1], aS, matrixB[1], bS);
VECSUBADDSS(to[2], matrixA[2], aS, matrixB[2], bS);
}
-/* A = B - C (3x3 matrix subtraction with 3x3 matrix) */
+/* `A = B - C` (3x3 matrix subtraction with 3x3 matrix). */
DO_INLINE void sub_fmatrix_fmatrix(float to[3][3],
const float matrixA[3][3],
const float matrixB[3][3])
@@ -1439,7 +1439,7 @@ void SIM_mass_spring_force_drag(Implicit_Data *data, float drag)
for (i = 0; i < numverts; i++) {
float tmp[3][3];
- /* NB: uses root space velocity, no need to transform */
+ /* NOTE: Uses root space velocity, no need to transform. */
madd_v3_v3fl(data->F[i], data->V[i], -drag);
copy_m3_m3(tmp, I);
@@ -1683,8 +1683,8 @@ BLI_INLINE void dfdx_damp(float to[3][3],
float rest,
float damping)
{
- /* inner spring damping vel is the relative velocity of the endpoints. */
- // return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest)));
+ /* Inner spring damping `vel` is the relative velocity of the endpoints. */
+ // return (I - outerprod(dir, dir)) * (-damping * -(dot(dir, vel) / Max(length, rest)));
mul_fvectorT_fvector(to, dir, dir);
sub_fmatrix_fmatrix(to, I, to);
mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel) / MAX2(length, rest))));
@@ -2204,7 +2204,7 @@ bool SIM_mass_spring_force_spring_bending_hair(Implicit_Data *data,
world_to_root_v3(data, j, goal, target);
spring_hairbend_forces(data, i, j, k, goal, stiffness, damping, k, vecnull, vecnull, fk);
- negate_v3_v3(fj, fk); /* counterforce */
+ negate_v3_v3(fj, fk); /* Counter-force. */
spring_hairbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, i, dfk_dxi);
spring_hairbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, j, dfk_dxj);
diff --git a/source/blender/simulation/intern/implicit_eigen.cpp b/source/blender/simulation/intern/implicit_eigen.cpp
index aa9d5d1d34d..d509b3db873 100644
--- a/source/blender/simulation/intern/implicit_eigen.cpp
+++ b/source/blender/simulation/intern/implicit_eigen.cpp
@@ -797,7 +797,7 @@ void SIM_mass_spring_force_drag(Implicit_Data *data, float drag)
for (int i = 0; i < numverts; i++) {
float tmp[3][3];
- /* NB: uses root space velocity, no need to transform */
+ /* NOTE: Uses root space velocity, no need to transform. */
madd_v3_v3fl(data->F.v3(i), data->V.v3(i), -drag);
copy_m3_m3(tmp, I);
@@ -894,8 +894,8 @@ BLI_INLINE void dfdx_damp(float to[3][3],
float rest,
float damping)
{
- /* inner spring damping vel is the relative velocity of the endpoints. */
- // return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest)));
+ /* Inner spring damping vel is the relative velocity of the endpoints. */
+ // return (I - outerprod(dir, dir)) * (-damping * -(dot(dir, vel) / Max(length, rest)));
mul_fvectorT_fvector(to, dir, dir);
sub_fmatrix_fmatrix(to, I, to);
mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel) / MAX2(length, rest))));
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 1f4598d33fe..c0d408be2e0 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -263,8 +263,9 @@ struct wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *han
wmKeyMap *keymap,
int priority);
-typedef struct wmKeyMap *(wmEventHandler_KeymapDynamicFn)(
- wmWindowManager *wm, struct wmEventHandler_Keymap *handler)ATTR_WARN_UNUSED_RESULT;
+typedef struct wmKeyMap *(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm,
+ struct wmEventHandler_Keymap *handler)
+ ATTR_WARN_UNUSED_RESULT;
struct wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(
struct wmWindowManager *wm, struct wmEventHandler_Keymap *handler);
@@ -618,9 +619,14 @@ void WM_operator_type_modal_from_exec_for_object_edit_coords(struct wmOperatorTy
void WM_uilisttype_init(void);
struct uiListType *WM_uilisttype_find(const char *idname, bool quiet);
bool WM_uilisttype_add(struct uiListType *ult);
-void WM_uilisttype_freelink(struct uiListType *ult);
+void WM_uilisttype_remove_ptr(struct Main *bmain, struct uiListType *ult);
void WM_uilisttype_free(void);
+void WM_uilisttype_to_full_list_id(const struct uiListType *ult,
+ const char *list_id,
+ char r_full_list_id[]);
+const char *WM_uilisttype_list_id_get(const struct uiListType *ult, struct uiList *list);
+
/* wm_menu_type.c */
void WM_menutype_init(void);
struct MenuType *WM_menutype_find(const char *idname, bool quiet);
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 0e3754ae73b..4ead0b2699c 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -439,6 +439,13 @@ typedef struct wmNotifier {
#define ND_SPACE_FILE_PREVIEW (21 << 16)
#define ND_SPACE_SPREADSHEET (22 << 16)
+/* NC_ASSET */
+/* Denotes that the AssetList is done reading some previews. NOT that the preview generation of
+ * assets is done. */
+#define ND_ASSET_LIST (1 << 16)
+#define ND_ASSET_LIST_PREVIEW (2 << 16)
+#define ND_ASSET_LIST_READING (3 << 16)
+
/* subtype, 256 entries too */
#define NOTE_SUBTYPE 0x0000FF00
@@ -917,6 +924,9 @@ typedef struct wmDragID {
} wmDragID;
typedef struct wmDragAsset {
+ /* Note: Can't store the AssetHandle here, since the FileDirEntry it wraps may be freed while
+ * dragging. So store necessary data here directly. */
+
char name[64]; /* MAX_NAME */
/* Always freed. */
const char *path;
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
index 1fbbec7f949..eab62ffce4c 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
@@ -186,7 +186,7 @@ typedef enum eWM_GizmoFlagMapTypeUpdateFlag {
* \brief Gizmo tweak flag.
* Bitflag passed to gizmo while tweaking.
*
- * \note Gizmos are responsible for handling this #wmGizmo.modal callback!.
+ * \note Gizmos are responsible for handling this #wmGizmo.modal callback.
*/
typedef enum {
/* Drag with extra precision (Shift). */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 6a328679c2e..5ec26a7b208 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -91,7 +91,7 @@ enum {
/* -------------------------------------------------------------------- */
/** \name wmGizmoMap Selection Array API
*
- * Just handle ``wm_gizmomap_select_array_*``, not flags or callbacks.
+ * Just handle `wm_gizmomap_select_array_*`, not flags or callbacks.
*
* \{ */
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index da40040ce56..db72dd2a819 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -378,13 +378,14 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode)
static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
{
+ const char *name = asset_drag->name;
+ ID_Type idtype = asset_drag->id_type;
+
switch ((eFileAssetImportType)asset_drag->import_type) {
case FILE_ASSET_IMPORT_LINK:
- return WM_file_link_datablock(
- G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ return WM_file_link_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name);
case FILE_ASSET_IMPORT_APPEND:
- return WM_file_append_datablock(
- G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ return WM_file_append_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name);
}
BLI_assert_unreachable();
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 0922aaaee53..f01e28f8822 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -457,6 +457,7 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_
GPUOffScreen *offscreen = GPU_offscreen_create(
region->winx, region->winy, false, false, NULL);
if (!offscreen) {
+ WM_report(RPT_ERROR, "Region could not be drawn!");
return;
}
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index e7603a02cff..e22285214f0 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -85,10 +85,10 @@ void WM_event_print(const wmEvent *event)
event_ids_from_type_and_value(event->prevtype, event->prevval, &prev_type_id, &prev_val_id);
printf(
- "wmEvent type:%d / %s, val:%d / %s,\n"
- " prev_type:%d / %s, prev_val:%d / %s,\n"
- " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d, is_repeat:%d,\n"
- " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p\n",
+ "wmEvent type:%d / %s, val:%d / %s,\n"
+ " prev_type:%d / %s, prev_val:%d / %s,\n"
+ " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d, is_repeat:%d,\n"
+ " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p\n",
event->type,
type_id,
event->val,
@@ -487,11 +487,13 @@ int WM_event_absolute_delta_y(const struct wmEvent *event)
/**
* Most OS's use `Ctrl+Space` / `OsKey+Space` to switch IME,
* so don't type in the space character.
+ *
+ * \note Shift is excluded from this check since it prevented typing `Shift+Space`, see: T85517.
*/
bool WM_event_is_ime_switch(const struct wmEvent *event)
{
return event->val == KM_PRESS && event->type == EVT_SPACEKEY &&
- (event->ctrl || event->oskey || event->shift || event->alt);
+ (event->ctrl || event->oskey || event->alt);
}
#endif
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 6789a52f890..5e29a22304c 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -61,6 +61,7 @@
#include "BLT_translation.h"
+#include "ED_asset.h"
#include "ED_fileselect.h"
#include "ED_info.h"
#include "ED_screen.h"
@@ -326,6 +327,7 @@ void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
}
}
}
+ ED_assetlist_storage_id_remap(old_id, new_id);
wmWindowManager *wm = bmain->wm.first;
if (wm && wm->message_bus) {
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 3633d3c07d3..06aaf95f232 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -987,7 +987,7 @@ const char *WM_init_state_app_template_get(void)
* or called for 'New File' both startup.blend and userpref.blend are checked.
*
* \param use_factory_settings:
- * Ignore on-disk startup file, use bundled ``datatoc_startup_blend`` instead.
+ * Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead.
* Used for "Restore Factory Settings".
*
* \param use_userdef: Load factory settings as well as startup file.
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 9da901d6c4e..92ca0b87527 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -23,8 +23,8 @@
* Default operator callbacks for use with gestures (border/circle/lasso/straightline).
* Operators themselves are defined elsewhere.
*
- * - Keymaps are in ``wm_operators.c``.
- * - Property definitions are in ``wm_operator_props.c``.
+ * - Keymaps are in `wm_operators.c`.
+ * - Property definitions are in `wm_operator_props.c`.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index e225b9f8aca..d7ea47fc625 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -109,6 +109,7 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
+#include "ED_asset.h"
#include "ED_gpencil.h"
#include "ED_keyframes_edit.h"
#include "ED_keyframing.h"
@@ -552,7 +553,6 @@ void WM_exit_ex(bContext *C, const bool do_python)
wm_surfaces_free();
wm_dropbox_free();
WM_menutype_free();
- WM_uilisttype_free();
/* all non-screen and non-space stuff editors did, like editmode */
if (C) {
@@ -571,6 +571,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
RE_engines_exit();
ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */
+ ED_assetlist_storage_exit();
if (wm) {
/* Before BKE_blender_free! - since the ListBases get freed there. */
@@ -607,6 +608,8 @@ void WM_exit_ex(bContext *C, const bool do_python)
wm_gizmomaptypes_free();
wm_gizmogrouptype_free();
wm_gizmotype_free();
+ /* Same for UI-list types. */
+ WM_uilisttype_free();
BLF_exit();
diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c
index b0b4f0f5904..795f78e215f 100644
--- a/source/blender/windowmanager/intern/wm_keymap_utils.c
+++ b/source/blender/windowmanager/intern/wm_keymap_utils.c
@@ -207,7 +207,7 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C)
/* Needs to be kept up to date with Keymap and Operator naming */
wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
{
- /* Op types purposely skipped for now:
+ /* Op types purposely skipped for now:
* BRUSH_OT
* BOID_OT
* BUTTONS_OT
diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c
index e17d5a9ae70..39435721d1a 100644
--- a/source/blender/windowmanager/intern/wm_operator_type.c
+++ b/source/blender/windowmanager/intern/wm_operator_type.c
@@ -498,12 +498,11 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname,
ot->cancel = wm_macro_cancel;
ot->poll = NULL;
- if (!ot->description) {
- /* XXX All ops should have a description but for now allow them not to. */
- ot->description = UNDOCUMENTED_OPERATOR_TIP;
- }
+ /* XXX All ops should have a description but for now allow them not to. */
+ BLI_assert((ot->description == NULL) || (ot->description[0]));
- RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
+ RNA_def_struct_ui_text(
+ ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
/* Use i18n context from rna_ext.srna if possible (py operators). */
i18n_context = ot->rna_ext.srna ? RNA_struct_translation_context(ot->rna_ext.srna) :
@@ -530,16 +529,16 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *),
ot->cancel = wm_macro_cancel;
ot->poll = NULL;
- if (!ot->description) {
- ot->description = UNDOCUMENTED_OPERATOR_TIP;
- }
+ /* XXX All ops should have a description but for now allow them not to. */
+ BLI_assert((ot->description == NULL) || (ot->description[0]));
/* Set the default i18n context now, so that opfunc can redefine it if needed! */
RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
opfunc(ot, userdata);
- RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
+ RNA_def_struct_ui_text(
+ ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 485e65cf3dc..8a8ea18cc43 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -604,6 +604,12 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
* used for keymaps and macros */
void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
{
+ IDProperty *tmp_properties = NULL;
+ /* Allow passing NULL for properties, just create the properties here then. */
+ if (properties == NULL) {
+ properties = &tmp_properties;
+ }
+
if (*properties == NULL) {
IDPropertyTemplate val = {0};
*properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
@@ -3806,7 +3812,7 @@ static void gesture_circle_modal_keymap(wmKeyConfig *keyconf)
{0, NULL, 0, NULL, NULL},
};
- /* WARNING - name is incorrect, use for non-3d views */
+ /* WARNING: Name is incorrect, use for non-3d views. */
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Gesture Circle");
/* this function is called for each spacetype, only needs to add map once */
diff --git a/source/blender/windowmanager/intern/wm_splash_screen.c b/source/blender/windowmanager/intern/wm_splash_screen.c
index f5881a00998..99c6bc39207 100644
--- a/source/blender/windowmanager/intern/wm_splash_screen.c
+++ b/source/blender/windowmanager/intern/wm_splash_screen.c
@@ -238,8 +238,8 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *region, void *UNUSE
if (!BLI_exists(userpref)) {
mt = WM_menutype_find("WM_MT_splash_quick_setup", true);
- /* The UI_BLOCK_QUICK_SETUP flag prevents the button text from being left-aligned,
- as it is for all menus due to the UI_BLOCK_LOOP flag, see in 'ui_def_but'. */
+ /* The #UI_BLOCK_QUICK_SETUP flag prevents the button text from being left-aligned,
+ * as it is for all menus due to the #UI_BLOCK_LOOP flag, see in #ui_def_but. */
UI_block_flag_enable(block, UI_BLOCK_QUICK_SETUP);
}
else {
diff --git a/source/blender/windowmanager/intern/wm_uilist_type.c b/source/blender/windowmanager/intern/wm_uilist_type.c
index 45c14c0bbe9..82ba4aa6e6f 100644
--- a/source/blender/windowmanager/intern/wm_uilist_type.c
+++ b/source/blender/windowmanager/intern/wm_uilist_type.c
@@ -21,16 +21,24 @@
*/
#include <stdio.h>
+#include <string.h>
+#include "BLI_listbase.h"
#include "BLI_sys_types.h"
+#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
#include "MEM_guardedalloc.h"
+#include "UI_interface.h"
+
#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BKE_main.h"
#include "BKE_screen.h"
#include "WM_api.h"
@@ -60,8 +68,62 @@ bool WM_uilisttype_add(uiListType *ult)
return 1;
}
-void WM_uilisttype_freelink(uiListType *ult)
+static void wm_uilisttype_unlink_from_region(const uiListType *ult, ARegion *region)
{
+ LISTBASE_FOREACH (uiList *, list, &region->ui_lists) {
+ if (list->type == ult) {
+ /* Don't delete the list, it's not just runtime data but stored in files. Freeing would make
+ * that data get lost. */
+ list->type = NULL;
+ }
+ }
+}
+
+static void wm_uilisttype_unlink_from_area(const uiListType *ult, ScrArea *area)
+{
+ LISTBASE_FOREACH (SpaceLink *, space_link, &area->spacedata) {
+ ListBase *regionbase = (space_link == area->spacedata.first) ? &area->regionbase :
+ &space_link->regionbase;
+ LISTBASE_FOREACH (ARegion *, region, regionbase) {
+ wm_uilisttype_unlink_from_region(ult, region);
+ }
+ }
+}
+
+/**
+ * For all lists representing \a ult, clear their `uiListType` pointer. Use when a list-type is
+ * deleted, so that the UI doesn't keep references to it.
+ *
+ * This is a common pattern for unregistering (usually .py defined) types at runtime, e.g. see
+ * #WM_gizmomaptype_group_unlink().
+ * Note that unlike in some other cases using this pattern, we don't actually free the lists with
+ * type \a ult, we just clear the reference to the type. That's because UI-Lists are written to
+ * files and we don't want them to get lost together with their (user visible) settings.
+ */
+static void wm_uilisttype_unlink(Main *bmain, const uiListType *ult)
+{
+ for (wmWindowManager *wm = bmain->wm.first; wm != NULL; wm = wm->id.next) {
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
+ LISTBASE_FOREACH (ScrArea *, global_area, &win->global_areas.areabase) {
+ wm_uilisttype_unlink_from_area(ult, global_area);
+ }
+ }
+ }
+
+ for (bScreen *screen = bmain->screens.first; screen != NULL; screen = screen->id.next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ wm_uilisttype_unlink_from_area(ult, area);
+ }
+
+ LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
+ wm_uilisttype_unlink_from_region(ult, region);
+ }
+ }
+}
+
+void WM_uilisttype_remove_ptr(Main *bmain, uiListType *ult)
+{
+ wm_uilisttype_unlink(bmain, ult);
bool ok = BLI_ghash_remove(uilisttypes_hash, ult->idname, NULL, MEM_freeN);
@@ -88,3 +150,34 @@ void WM_uilisttype_free(void)
BLI_ghash_free(uilisttypes_hash, NULL, MEM_freeN);
uilisttypes_hash = NULL;
}
+
+/**
+ * The "full" list-ID is an internal name used for storing and identifying a list. It is built like
+ * this:
+ * "{uiListType.idname}_{list_id}", whereby "list_id" is an optional parameter passed to
+ * `UILayout.template_list()`. If it is not set, the full list-ID is just "{uiListType.idname}_".
+ *
+ * Note that whenever the Python API refers to the list-ID, it's the short, "non-full" one it
+ * passed to `UILayout.template_list()`. C code can query that through
+ * #WM_uilisttype_list_id_get().
+ */
+void WM_uilisttype_to_full_list_id(const uiListType *ult,
+ const char *list_id,
+ char r_full_list_id[/*UI_MAX_NAME_STR*/])
+{
+ /* We tag the list id with the list type... */
+ BLI_snprintf(r_full_list_id, UI_MAX_NAME_STR, "%s_%s", ult->idname, list_id ? list_id : "");
+}
+
+/**
+ * Get the "non-full" list-ID, see #WM_uilisttype_to_full_list_id() for details.
+ *
+ * \note Assumes `uiList.list_id` was set using #WM_uilisttype_to_full_list_id()!
+ */
+const char *WM_uilisttype_list_id_get(const uiListType *ult, uiList *list)
+{
+ /* Some sanity check for the assumed behavior of #WM_uilisttype_to_full_list_id(). */
+ BLI_assert((list->list_id + strlen(ult->idname))[0] == '_');
+ /* +1 to skip the '_' */
+ return list->list_id + strlen(ult->idname) + 1;
+}
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index c21bebe8062..905c57d901a 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -351,7 +351,7 @@ enum {
/* for event checks */
/* only used for KM_TEXTINPUT, so assume that we want all user-inputtable ascii codes included */
-/* UNUSED - see wm_eventmatch - BUG T30479. */
+/* Unused, see #wm_eventmatch, see: T30479. */
// #define ISTEXTINPUT(event_type) ((event_type) >= ' ' && (event_type) <= 255)
/* NOTE: an alternative could be to check `event->utf8_buf`. */
diff --git a/source/blender/windowmanager/wm_surface.h b/source/blender/windowmanager/wm_surface.h
index 06c29231361..a2483d38154 100644
--- a/source/blender/windowmanager/wm_surface.h
+++ b/source/blender/windowmanager/wm_surface.h
@@ -19,7 +19,7 @@
*
* \name WM-Surface
*
- * Container to manage painting in an offscreen context.
+ * Container to manage painting in an off-screen context.
*/
#pragma once
@@ -42,9 +42,9 @@ typedef struct wmSurface {
/** Free customdata, not the surface itself (done by wm_surface API) */
void (*free_data)(struct wmSurface *);
- /** Called when surface is activated for drawing (made drawable). */
+ /** Called when surface is activated for drawing (made drawable). */
void (*activate)(void);
- /** Called when surface is deactivated for drawing (current drawable cleared). */
+ /** Called when surface is deactivated for drawing (current drawable cleared). */
void (*deactivate)(void);
} wmSurface;
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index 1f722855696..4ac05e339b9 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -24,6 +24,7 @@
#include <string.h>
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "ED_view3d_offscreen.h"
@@ -61,10 +62,12 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat);
copy_v3_v3(eye_pose.position, draw_view->eye_pose.position);
- sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs);
if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
sub_v3_v3(eye_pose.position, draw_view->local_pose.position);
}
+ if ((session_settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) {
+ sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs);
+ }
perspective_m4_fov(r_proj_mat,
draw_view->fov.angle_left,
@@ -89,6 +92,9 @@ static void wm_xr_draw_viewport_buffers_to_active_framebuffer(
const wmXrSurfaceData *surface_data,
const GHOST_XrDrawViewInfo *draw_view)
{
+ const wmXrViewportPair *vp = BLI_findlink(&surface_data->viewports, draw_view->view_idx);
+ BLI_assert(vp && vp->viewport);
+
const bool is_upside_down = GHOST_XrSessionNeedsUpsideDownDrawing(runtime_data->context);
rcti rect = {.xmin = 0, .ymin = 0, .xmax = draw_view->width - 1, .ymax = draw_view->height - 1};
@@ -98,8 +104,7 @@ static void wm_xr_draw_viewport_buffers_to_active_framebuffer(
if (is_upside_down) {
SWAP(int, rect.ymin, rect.ymax);
}
- GPU_viewport_draw_to_screen_ex(
- surface_data->viewport, 0, &rect, draw_view->expects_srgb_buffer, true);
+ GPU_viewport_draw_to_screen_ex(vp->viewport, 0, &rect, draw_view->expects_srgb_buffer, true);
}
/**
@@ -130,6 +135,9 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
return;
}
+ const wmXrViewportPair *vp = BLI_findlink(&surface_data->viewports, draw_view->view_idx);
+ BLI_assert(vp && vp->offscreen && vp->viewport);
+
/* In case a framebuffer is still bound from drawing the last eye. */
GPU_framebuffer_restore();
/* Some systems have drawing glitches without this. */
@@ -151,8 +159,8 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
true,
NULL,
false,
- surface_data->offscreen,
- surface_data->viewport);
+ vp->offscreen,
+ vp->viewport);
/* The draw-manager uses both GPUOffscreen and GPUViewport to manage frame and texture buffers. A
* call to GPU_viewport_draw_to_screen() is still needed to get the final result from the
@@ -162,7 +170,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
* In a next step, Ghost-XR will use the currently bound frame-buffer to retrieve the image
* to be submitted to the OpenXR swap-chain. So do not un-bind the off-screen yet! */
- GPU_offscreen_bind(surface_data->offscreen, false);
+ GPU_offscreen_bind(vp->offscreen, false);
wm_xr_draw_viewport_buffers_to_active_framebuffer(xr_data->runtime, surface_data, draw_view);
}
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index 9bf63be61dd..6415f96e322 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -85,9 +85,15 @@ typedef struct wmXrRuntimeData {
wmXrSessionExitFn exit_fn;
} wmXrRuntimeData;
-typedef struct {
+typedef struct wmXrViewportPair {
+ struct wmXrViewportPair *next, *prev;
struct GPUOffScreen *offscreen;
struct GPUViewport *viewport;
+} wmXrViewportPair;
+
+typedef struct {
+ /** Off-screen buffers/viewports for each view. */
+ ListBase viewports; /* wmXrViewportPair */
} wmXrSurfaceData;
typedef struct wmXrDrawData {
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index 1ddbe228e05..252f358c798 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -225,7 +225,7 @@ typedef enum wmXrSessionStateEvent {
SESSION_STATE_EVENT_NONE = 0,
SESSION_STATE_EVENT_START,
SESSION_STATE_EVENT_RESET_TO_BASE_POSE,
- SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE,
+ SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE,
} wmXrSessionStateEvent;
static bool wm_xr_session_draw_data_needs_reset_to_base_pose(const wmXrSessionState *state,
@@ -253,7 +253,7 @@ static wmXrSessionStateEvent wm_xr_session_state_to_event(const wmXrSessionState
XR_SESSION_USE_POSITION_TRACKING) !=
(settings->flag & XR_SESSION_USE_POSITION_TRACKING));
if (position_tracking_toggled) {
- return SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE;
+ return SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE;
}
return SESSION_STATE_EVENT_NONE;
@@ -288,7 +288,7 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
copy_v3_fl(draw_data->eye_position_ofs, 0.0f);
}
break;
- case SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE:
+ case SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE:
if (use_position_tracking) {
/* Keep the current position, and let the user move from there. */
copy_v3_v3(draw_data->eye_position_ofs, state->prev_eye_position_ofs);
@@ -317,6 +317,7 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
{
GHOST_XrPose viewer_pose;
const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING;
+ const bool use_absolute_tracking = settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING;
mul_qt_qtqt(viewer_pose.orientation_quat,
draw_data->base_pose.orientation_quat,
@@ -324,14 +325,16 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
copy_v3_v3(viewer_pose.position, draw_data->base_pose.position);
/* The local pose and the eye pose (which is copied from an earlier local pose) both are view
* space, so Y-up. In this case we need them in regular Z-up. */
- viewer_pose.position[0] -= draw_data->eye_position_ofs[0];
- viewer_pose.position[1] += draw_data->eye_position_ofs[2];
- viewer_pose.position[2] -= draw_data->eye_position_ofs[1];
if (use_position_tracking) {
viewer_pose.position[0] += draw_view->local_pose.position[0];
viewer_pose.position[1] -= draw_view->local_pose.position[2];
viewer_pose.position[2] += draw_view->local_pose.position[1];
}
+ if (!use_absolute_tracking) {
+ viewer_pose.position[0] -= draw_data->eye_position_ofs[0];
+ viewer_pose.position[1] += draw_data->eye_position_ofs[2];
+ viewer_pose.position[2] -= draw_data->eye_position_ofs[1];
+ }
copy_v3_v3(state->viewer_pose.position, viewer_pose.position);
copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat);
@@ -451,9 +454,14 @@ static void wm_xr_session_controller_mats_update(const XrSessionSettings *settin
float base_inv[4][4];
float tmp[4][4];
- zero_v3(view_ofs);
if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
- add_v3_v3(view_ofs, state->prev_local_pose.position);
+ copy_v3_v3(view_ofs, state->prev_local_pose.position);
+ }
+ else {
+ zero_v3(view_ofs);
+ }
+ if ((settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) {
+ add_v3_v3(view_ofs, state->prev_eye_position_ofs);
}
wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv);
@@ -538,7 +546,6 @@ void wm_xr_session_controller_data_clear(wmXrSessionState *state)
*/
static void wm_xr_session_surface_draw(bContext *C)
{
- wmXrSurfaceData *surface_data = g_xr_surface->customdata;
wmWindowManager *wm = CTX_wm_manager(C);
Main *bmain = CTX_data_main(C);
wmXrDrawData draw_data;
@@ -554,38 +561,50 @@ static void wm_xr_session_surface_draw(bContext *C)
GHOST_XrSessionDrawViews(wm->xr.runtime->context, &draw_data);
- GPU_offscreen_unbind(surface_data->offscreen, false);
+ GPU_framebuffer_restore();
}
bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
const GHOST_XrDrawViewInfo *draw_view)
{
- const bool size_changed = surface_data->offscreen &&
- (GPU_offscreen_width(surface_data->offscreen) != draw_view->width) &&
- (GPU_offscreen_height(surface_data->offscreen) != draw_view->height);
+ wmXrViewportPair *vp = NULL;
+ if (draw_view->view_idx >= BLI_listbase_count(&surface_data->viewports)) {
+ vp = MEM_callocN(sizeof(*vp), __func__);
+ BLI_addtail(&surface_data->viewports, vp);
+ }
+ else {
+ vp = BLI_findlink(&surface_data->viewports, draw_view->view_idx);
+ }
+ BLI_assert(vp);
+
+ GPUOffScreen *offscreen = vp->offscreen;
+ GPUViewport *viewport = vp->viewport;
+ const bool size_changed = offscreen && (GPU_offscreen_width(offscreen) != draw_view->width) &&
+ (GPU_offscreen_height(offscreen) != draw_view->height);
char err_out[256] = "unknown";
bool failure = false;
- if (surface_data->offscreen) {
- BLI_assert(surface_data->viewport);
+ if (offscreen) {
+ BLI_assert(viewport);
if (!size_changed) {
return true;
}
- GPU_viewport_free(surface_data->viewport);
- GPU_offscreen_free(surface_data->offscreen);
- }
-
- if (!(surface_data->offscreen = GPU_offscreen_create(
- draw_view->width, draw_view->height, true, false, err_out))) {
- failure = true;
- }
-
- if (failure) {
- /* Pass. */
+ GPU_viewport_free(viewport);
+ GPU_offscreen_free(offscreen);
+ }
+
+ offscreen = vp->offscreen = GPU_offscreen_create(
+ draw_view->width, draw_view->height, true, false, err_out);
+ if (offscreen) {
+ viewport = vp->viewport = GPU_viewport_create();
+ if (!viewport) {
+ GPU_offscreen_free(offscreen);
+ offscreen = vp->offscreen = NULL;
+ failure = true;
+ }
}
- else if (!(surface_data->viewport = GPU_viewport_create())) {
- GPU_offscreen_free(surface_data->offscreen);
+ else {
failure = true;
}
@@ -600,12 +619,17 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
static void wm_xr_session_surface_free_data(wmSurface *surface)
{
wmXrSurfaceData *data = surface->customdata;
+ ListBase *lb = &data->viewports;
+ wmXrViewportPair *vp;
- if (data->viewport) {
- GPU_viewport_free(data->viewport);
- }
- if (data->offscreen) {
- GPU_offscreen_free(data->offscreen);
+ while ((vp = BLI_pophead(lb))) {
+ if (vp->viewport) {
+ GPU_viewport_free(vp->viewport);
+ }
+ if (vp->offscreen) {
+ GPU_offscreen_free(vp->offscreen);
+ }
+ BLI_freelinkN(lb, vp);
}
MEM_freeN(surface->customdata);
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index a4b32fac9fc..c3aeffe8fda 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -1060,13 +1060,6 @@ elseif(APPLE)
endif()
endif()
- if(WITH_LLVM AND NOT LLVM_STATIC)
- install(
- FILES ${LIBDIR}/llvm/lib/libLLVM-3.4.dylib
- DESTINATION Blender.app/Contents/MacOS
- )
- endif()
-
# python
if(WITH_PYTHON AND NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK)
# Copy the python libs into the install directory
diff --git a/source/creator/blender.map b/source/creator/blender.map
index 9aafddd9666..a817908acfb 100644
--- a/source/creator/blender.map
+++ b/source/creator/blender.map
@@ -74,21 +74,55 @@ local:
*YAML*;
/* LLVM symbols not in the LLVM namespace that can conflict with LLVM usage
- * in OpenGL and OpenCL drivers. */
+ * in OpenGL and OpenCL drivers.
+ *
+ * These are found by doing a Blender build with and without OSL, and
+ * comparing the output of nm -gD ./bin/blender to find symbols. */
+ AlwaysSpillBase;
+ AsmMacroMaxNestingDepth;
+ AttributorRun;
+ CheckBFIUnknownBlockQueries;
+ *cloneBitwiseIVUser*;
+ *computeHostNumHardwareThread*;
+ *computeHostNumPhysicalCores*;
decodeInstruction;
+ DisableBasicAA;
+ DisablePreInliner;
+ DisableWholeProgramVisibility;
+ EnableCHR;
+ EnableConstraintElimination;
+ EnableGVNHoist;
+ EnableGVNSink;
EnableHotColdSplit;
EnableIPRA;
+ EnableIROutliner;
+ EnableKnowledgeRetention;
+ EnableLoopFlatten;
+ EnableMatrix;
EnableOrderFileInstrumentation;
+ EnablePGSO;
+ EnableUnrollAndJam;
EnableVPlanNativePath;
EnableVPlanPredication;
+ ExtraVectorizerPasses;
FlattenedProfileUsed;
+ ForcePGSO;
ForceStackAlign;
ForceSummaryEdgesCold;
FSEC;
+ *getExtendedOperandRecurrence*;
+ *getWideRecurrence*;
+ InlinerFunctionImportStats;
+ *IROutlinerLegacyPass*;
__jit_debug_descriptor;
__jit_debug_register_code;
_Jv_RegisterClasses;
+ *LiveDebugValues*;
+ *LoopInterchangeLegacyPass*;
MachineRegionInfoPassID;
+ MaxDevirtIterations;
+ MaxRegistersForGCPointers;
+ MemOPOptMemcmpBcmp;
MemOPSizeLarge;
MemOPSizeRange;
MISchedPostRA;
@@ -97,18 +131,40 @@ local:
Name;
NumNamedVarArgParams;
PGOViewCounts;
+ PGSOColdCodeOnly;
+ PGSOColdCodeOnlyForInstrPGO;
+ PGSOColdCodeOnlyForPartialSamplePGO;
+ PGSOColdCodeOnlyForSamplePGO;
+ PgsoCutoffInstrProf;
+ PgsoCutoffSampleProf;
+ PGSOLargeWorkingSetSizeOnly;
+ PreInlineThreshold;
PrintBlockFreqFuncName;
PrintBranchProbFuncName;
ProfileLikelyProb;
+ RunNewGVN;
+ RunPartialInlining;
+ RunSLPVectorization;
+ ScalePartialSampleProfileWorkingSetSize;
+ *ScopedAliasMetadataDeepCloner*;
+ ShouldPreserveAllAttributes;
+ SkipFunctionNames;
StartAfterOptName;
StartBeforeOptName;
StaticLikelyProb;
StopAfterOptName;
StopBeforeOptName;
+ UseContextLessSummary;
UseDbgAddr;
+ UseLEB128Directives;
+ UseRegistersForDeoptValues;
+ UseRegistersForGCPointersInLandingPad;
ViewBlockFreqFuncName;
ViewBlockLayoutWithBFI;
ViewHotFreqPercent;
+ WholeProgramVisibility;
+ *widenLoopCompare*;
+ *widenWithVariant*;
WriteRelBFToSummary;
X86CompilationCallback*;
};
diff --git a/source/tools b/source/tools
-Subproject 01f51a0e551ab730f0934dc6488613690ac4bf8
+Subproject c8579c5cf43229843df505da9644b5b0b720197
diff --git a/tests/performance/api/config.py b/tests/performance/api/config.py
index 68f4df8d487..d3a79eede14 100644
--- a/tests/performance/api/config.py
+++ b/tests/performance/api/config.py
@@ -31,6 +31,7 @@ class TestEntry:
device_id: str = 'CPU'
device_name: str = 'Unknown CPU'
status: str = 'queued'
+ error_msg: str = ''
output: Dict = field(default_factory=dict)
benchmark_type: str = 'comparison'
@@ -42,7 +43,8 @@ class TestEntry:
def from_json(self, json_dict):
for field in self.__dataclass_fields__:
- setattr(self, field, json_dict[field])
+ if field in json_dict:
+ setattr(self, field, json_dict[field])
class TestQueue:
@@ -112,7 +114,7 @@ class TestConfig:
self.base_dir = env.base_dir / name
self.logs_dir = self.base_dir / 'logs'
- config = self._read_config_module()
+ config = TestConfig._read_config_module(self.base_dir)
self.tests = TestCollection(env,
getattr(config, 'tests', ['*']),
getattr(config, 'categories', ['*']))
@@ -154,10 +156,17 @@ class TestConfig:
with open(config_file, 'w') as f:
f.write(default_config)
- def _read_config_module(self) -> None:
+ @staticmethod
+ def read_blender_executables(env, name) -> List:
+ config = TestConfig._read_config_module(env.base_dir / name)
+ builds = getattr(config, 'builds', {})
+ return [pathlib.Path(build) for build in builds.values()]
+
+ @staticmethod
+ def _read_config_module(base_dir: pathlib.Path) -> None:
# Import config.py as a module.
import importlib.util
- spec = importlib.util.spec_from_file_location("testconfig", self.base_dir / 'config.py')
+ spec = importlib.util.spec_from_file_location("testconfig", base_dir / 'config.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
@@ -195,14 +204,14 @@ class TestConfig:
# Get entries for revisions based on existing builds.
for revision_name, executable in self.builds.items():
- executable_path = pathlib.Path(executable)
- if not executable_path.exists():
+ executable_path = env._blender_executable_from_path(pathlib.Path(executable))
+ if not executable_path:
sys.stderr.write(f'Error: build {executable} not found\n')
sys.exit(1)
env.set_blender_executable(executable_path)
git_hash, _ = env.run_in_blender(get_build_hash, {})
- env.unset_blender_executable()
+ env.set_default_blender_executable()
mtime = executable_path.stat().st_mtime
entries += self._get_entries(revision_name, git_hash, executable, mtime)
diff --git a/tests/performance/api/environment.py b/tests/performance/api/environment.py
index c9ddd493394..70fc15f251c 100644
--- a/tests/performance/api/environment.py
+++ b/tests/performance/api/environment.py
@@ -27,9 +27,10 @@ class TestEnvironment:
self.git_executable = 'git'
self.cmake_executable = 'cmake'
self.cmake_options = ['-DWITH_INTERNATIONAL=OFF', '-DWITH_BUILDINFO=OFF']
- self.unset_blender_executable()
self.log_file = None
self.machine = None
+ self._init_default_blender_executable()
+ self.set_default_blender_executable()
def get_machine(self, need_gpus: bool=True) -> None:
if not self.machine or (need_gpus and not self.machine.has_gpus):
@@ -46,7 +47,7 @@ class TestEnvironment:
print(f'Init {self.base_dir}')
self.base_dir.mkdir(parents=True, exist_ok=True)
- if len(self.get_configs(names_only=True)) == 0:
+ if len(self.get_configs_names()) == 0:
config_dir = self.base_dir / 'default'
print(f'Creating default configuration in {config_dir}')
TestConfig.write_default_config(self, config_dir)
@@ -77,7 +78,7 @@ class TestEnvironment:
print('Done')
- def checkout(self) -> None:
+ def checkout(self, git_hash) -> None:
# Checkout Blender revision
if not self.blender_dir.exists():
sys.stderr.write('\n\nError: no build set up, run `./benchmark init --build` first\n')
@@ -87,32 +88,68 @@ class TestEnvironment:
self.call([self.git_executable, 'reset', '--hard', 'HEAD'], self.blender_dir)
self.call([self.git_executable, 'checkout', '--detach', git_hash], self.blender_dir)
- self.build()
-
- def build(self) -> None:
+ def build(self) -> bool:
# Build Blender revision
if not self.build_dir.exists():
sys.stderr.write('\n\nError: no build set up, run `./benchmark init --build` first\n')
sys.exit(1)
jobs = str(multiprocessing.cpu_count())
- self.call([self.cmake_executable, '.'] + self.cmake_options, self.build_dir)
- self.call([self.cmake_executable, '--build', '.', '-j', jobs, '--target', 'install'], self.build_dir)
+ try:
+ self.call([self.cmake_executable, '.'] + self.cmake_options, self.build_dir)
+ self.call([self.cmake_executable, '--build', '.', '-j', jobs, '--target', 'install'], self.build_dir)
+ except:
+ return False
+
+ self._init_default_blender_executable()
+ return True
def set_blender_executable(self, executable_path: pathlib.Path) -> None:
# Run all Blender commands with this executable.
self.blender_executable = executable_path
- def unset_blender_executable(self) -> None:
+ def _blender_executable_name(self) -> pathlib.Path:
if platform.system() == "Windows":
- self.blender_executable = self.build_dir / 'bin' / 'blender.exe'
+ return pathlib.Path('blender.exe')
elif platform.system() == "Darwin":
- self.blender_executable = self.build_dir / 'bin' / 'Blender.app' / 'Contents' / 'MacOS' / 'Blender'
+ return pathlib.Path('Blender.app') / 'Contents' / 'MacOS' / 'Blender'
else:
- self.blender_executable = self.build_dir / 'bin' / 'blender'
-
- if not self.blender_executable.exists():
- self.blender_executable = 'blender'
+ return pathlib.Path('blender')
+
+ def _blender_executable_from_path(self, executable: pathlib.Path) -> pathlib.Path:
+ if executable.is_dir():
+ # Directory
+ executable = executable / self._blender_executable_name()
+ elif not executable.is_file() and executable.name == 'blender':
+ # Executable path without proper path on Windows or macOS.
+ executable = executable.parent / self._blender_executable_name()
+
+ if executable.is_file():
+ return executable
+
+ return None
+
+ def _init_default_blender_executable(self) -> None:
+ # Find a default executable to run commands independent of testing a specific build.
+ # Try own built executable.
+ built_executable = self._blender_executable_from_path(self.build_dir / 'bin')
+ if built_executable:
+ self.default_blender_executable = built_executable
+ return
+
+ # Try find an executable in the configs.
+ for config_name in self.get_config_names():
+ for executable in TestConfig.read_blender_executables(self, config_name):
+ executable = self._blender_executable_from_path(executable)
+ if executable:
+ self.default_blender_executable = executable
+ return
+
+ # Fallback to a "blender" command in the hope it's available.
+ self.default_blender_executable = pathlib.Path("blender")
+
+ def set_default_blender_executable(self) -> None:
+ self.blender_executable = self.default_blender_executable
def set_log_file(self, filepath: pathlib.Path, clear=True) -> None:
# Log all commands and output to this file.
@@ -219,26 +256,36 @@ class TestEnvironment:
filepaths.append(pathlib.Path(filename))
return filepaths
+ def get_config_names(self) -> List:
+ names = []
+
+ if self.base_dir.exists():
+ for dirname in os.listdir(self.base_dir):
+ dirpath = self.base_dir / dirname / 'config.py'
+ if dirpath.exists():
+ names.append(dirname)
+
+ return names
+
def get_configs(self, name: str=None, names_only: bool=False) -> List:
# Get list of configurations in the benchmarks directory.
configs = []
- if self.base_dir.exists():
- for dirname in os.listdir(self.base_dir):
- if not name or dirname == name:
- dirpath = self.base_dir / dirname / 'config.py'
- if dirpath.exists():
- if names_only:
- configs.append(dirname)
- else:
- configs.append(TestConfig(self, dirname))
+ for config_name in self.get_config_names():
+ if not name or config_name == name:
+ if names_only:
+ configs.append(config_name)
+ else:
+ configs.append(TestConfig(self, config_name))
return configs
def resolve_git_hash(self, revision):
# Get git hash for a tag or branch.
- return self.call([self.git_executable, 'rev-parse', revision], self.blender_git_dir)[0].strip()
+ lines = self.call([self.git_executable, 'rev-parse', revision], self.blender_git_dir)
+ return lines[0].strip() if len(lines) else revision
def git_hash_date(self, git_hash):
# Get commit data for a git hash.
- return int(self.call([self.git_executable, 'log', '-n1', git_hash, '--format=%at'], self.blender_git_dir)[0].strip())
+ lines = self.call([self.git_executable, 'log', '-n1', git_hash, '--format=%at'], self.blender_git_dir)
+ return int(lines[0].strip()) if len(lines) else 0
diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py
index b3c8329ff27..4ee5ae7cf0e 100644
--- a/tests/performance/api/graph.py
+++ b/tests/performance/api/graph.py
@@ -30,6 +30,7 @@ class TestGraph:
data = []
for device_name, device_entries in devices.items():
+
# Gather used categories.
categories = {}
for entry in device_entries:
@@ -57,6 +58,8 @@ class TestGraph:
self.json = json.dumps(data, indent=2)
def chart(self, device_name: str, chart_name: str, entries: List, chart_type: str, output: str) -> Dict:
+ entries = sorted(entries, key=lambda entry: entry.date)
+
# Gather used tests.
tests = {}
for entry in entries:
diff --git a/tests/performance/benchmark b/tests/performance/benchmark
index 3b43bd0aa96..ad1e07d0ef3 100755
--- a/tests/performance/benchmark
+++ b/tests/performance/benchmark
@@ -66,6 +66,8 @@ def print_row(config: api.TestConfig, entries: List, end='\n') -> None:
if status == 'outdated':
result += " (outdated)"
+ elif status == 'failed':
+ result = "failed: " + entry.error_msg
else:
result = status
@@ -105,20 +107,37 @@ def run_entry(env: api.TestEnvironment, config: api.TestConfig, row: List, entry
logname += '_' + device_id
env.set_log_file(config.logs_dir / (logname + '.log'), clear=True)
+ # Clear output
+ entry.output = None
+ entry.error_msg = ''
+
# Build revision, or just set path to existing executable.
entry.status = 'building'
print_row(config, row, end='\r')
+ executable_ok = True
if len(entry.executable):
env.set_blender_executable(pathlib.Path(entry.executable))
else:
env.checkout(git_hash)
- env.build(git_hash)
+ executable_ok = env.build()
+ if not executable_ok:
+ entry.status = 'failed'
+ entry.error_msg = 'Failed to build'
# Run test and update output and status.
- entry.status = 'running'
- print_row(config, row, end='\r')
- entry.output = test.run(env, device_id)
- entry.status = 'done' if entry.output else 'failed'
+ if executable_ok:
+ entry.status = 'running'
+ print_row(config, row, end='\r')
+
+ try:
+ entry.output = test.run(env, device_id)
+ if not entry.output:
+ raise Exception("Test produced no output")
+ entry.status = 'done'
+ except Exception as e:
+ entry.status = 'failed'
+ entry.error_msg = str(e)
+
print_row(config, row, end='\r')
# Update device name in case the device changed since the entry was created.
@@ -126,7 +145,7 @@ def run_entry(env: api.TestEnvironment, config: api.TestConfig, row: List, entry
# Restore default logging and Blender executable.
env.unset_log_file()
- env.unset_blender_executable()
+ env.set_default_blender_executable()
return True
@@ -155,7 +174,7 @@ def cmd_list(env: api.TestEnvironment, argv: List) -> None:
print('')
print('CONFIGS')
- configs = env.get_configs(names_only=True)
+ configs = env.get_config_names()
for config_name in configs:
print(config_name)
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index 92cebb7d274..0f9665f0a95 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -740,6 +740,29 @@ if(WITH_COMPOSITOR)
endif()
+set(geo_node_tests
+ curves
+ geometry
+ mesh
+ points
+)
+
+foreach(geo_node_test ${geo_node_tests})
+ if(EXISTS "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/")
+ file(GLOB files "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/*.blend")
+ foreach(file ${files})
+ get_filename_component(filename ${file} NAME_WE)
+ add_blender_test(
+ geo_node_${geo_node_test}_test_${filename}
+ ${file}
+ --python ${TEST_PYTHON_DIR}/geo_node_test.py
+ )
+ endforeach()
+ else()
+ MESSAGE(STATUS "No directory named ${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/ found, disabling test.")
+ endif()
+endforeach()
+
if(WITH_OPENGL_DRAW_TESTS)
if(NOT OPENIMAGEIO_IDIFF)
MESSAGE(STATUS "Disabling OpenGL draw tests because OIIO idiff does not exist")
diff --git a/tests/python/bevel_operator.py b/tests/python/bevel_operator.py
index c732d437b57..726bf4b5b03 100644
--- a/tests/python/bevel_operator.py
+++ b/tests/python/bevel_operator.py
@@ -27,272 +27,272 @@ import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest
+from modules.mesh_test import SpecMeshTest, OperatorSpecEditMode, RunTest
def main():
tests = [
# 0
- MeshTest('Cube_test_1', 'Cube_test', 'Cube_result_1',
+ SpecMeshTest('Cube_test_1', 'Cube_test', 'Cube_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.2}, 'EDGE', {10})]),
- MeshTest('Cube_test_2', 'Cube_test', 'Cube_result_2',
+ SpecMeshTest('Cube_test_2', 'Cube_test', 'Cube_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'offset_type': 'WIDTH'}, 'EDGE', {10, 7}, )]),
- MeshTest('Cube_test_3', 'Cube_test', 'Cube_result_3',
+ SpecMeshTest('Cube_test_3', 'Cube_test', 'Cube_result_3',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'offset_type': 'DEPTH'}, 'EDGE', {8, 10, 7}, )]),
- MeshTest('Cube_test_4', 'Cube_test', 'Cube_result_4',
+ SpecMeshTest('Cube_test_4', 'Cube_test', 'Cube_result_4',
[OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 2}, 'EDGE', {10}, )]),
- MeshTest('Cube_test_5', 'Cube_test', 'Cube_result_5',
+ SpecMeshTest('Cube_test_5', 'Cube_test', 'Cube_result_5',
[OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 3}, 'EDGE', {10, 7}, )]),
# 5
- MeshTest('Cube_test_6', 'Cube_test', 'Cube_result_6',
+ SpecMeshTest('Cube_test_6', 'Cube_test', 'Cube_result_6',
[OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 4}, 'EDGE', {8, 10, 7}, )]),
- MeshTest('Cube_test_7', 'Cube_test', 'Cube_result_7',
+ SpecMeshTest('Cube_test_7', 'Cube_test', 'Cube_result_7',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 5, 'profile': 0.2}, 'EDGE', {0, 10, 4, 7}, )]),
- MeshTest('Cube_test_8', 'Cube_test', 'Cube_result_8',
+ SpecMeshTest('Cube_test_8', 'Cube_test', 'Cube_result_8',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 5, 'profile': 0.25}, 'EDGE', {8, 10, 7}, )]),
- MeshTest('Cube_test_9', 'Cube_test', 'Cube_result_9',
+ SpecMeshTest('Cube_test_9', 'Cube_test', 'Cube_result_9',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 6, 'profile': 0.9}, 'EDGE', {8, 10, 7}, )]),
- MeshTest('Cube_test_10', 'Cube_test', 'Cube_result_10',
+ SpecMeshTest('Cube_test_10', 'Cube_test', 'Cube_result_10',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 4, 'profile': 1.0}, 'EDGE', {10, 7}, )]),
# 10
- MeshTest('Cube_test_11', 'Cube_test', 'Cube_result_11',
+ SpecMeshTest('Cube_test_11', 'Cube_test', 'Cube_result_11',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 5, 'profile': 1.0}, 'EDGE', {8, 10, 7}, )]),
- MeshTest("test 12", 'Cube_test', 'Cube_result_12',
+ SpecMeshTest("test 12", 'Cube_test', 'Cube_result_12',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 8}, 'EDGE',
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, )]),
- MeshTest('Pyramid4_test_1', 'Pyr4_test', 'Pyr4_result_1',
+ SpecMeshTest('Pyramid4_test_1', 'Pyr4_test', 'Pyr4_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {5}, )]),
- MeshTest('Pyramid4_test_2', 'Pyr4_test', 'Pyr4_result_2',
+ SpecMeshTest('Pyramid4_test_2', 'Pyr4_test', 'Pyr4_result_2',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 5}, )]),
- MeshTest('Pyramid4_test_3', 'Pyr4_test', 'Pyr4_result_3',
+ SpecMeshTest('Pyramid4_test_3', 'Pyr4_test', 'Pyr4_result_3',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3, 5}, )]),
# 15
- MeshTest('Pyramid4_test_4', 'Pyr4_test', 'Pyr4_result_4',
+ SpecMeshTest('Pyramid4_test_4', 'Pyr4_test', 'Pyr4_result_4',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {1, 2, 3, 5}, )]),
- MeshTest('Pyramid4_test_5', 'Pyr4_test', 'Pyr4_result_5',
+ SpecMeshTest('Pyramid4_test_5', 'Pyr4_test', 'Pyr4_result_5',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 3}, 'EDGE', {1, 2, 3, 5}, )]),
- MeshTest('Pyramid4_test_6', 'Pyr4_test', 'Pyr4_result_6',
+ SpecMeshTest('Pyramid4_test_6', 'Pyr4_test', 'Pyr4_result_6',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {2, 3}, )]),
- MeshTest('Pyramid4_test_7', 'Pyr4_test', 'Pyr4_result_7',
+ SpecMeshTest('Pyramid4_test_7', 'Pyr4_test', 'Pyr4_result_7',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 4, 'profile': 0.15}, 'EDGE', {1, 2, 3, 5}, )]),
- MeshTest('Pyramid4_test_8', 'Pyr4_test', 'Pyr4_result_8',
+ SpecMeshTest('Pyramid4_test_8', 'Pyr4_test', 'Pyr4_result_8',
[OperatorSpecEditMode('bevel',
{'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}, 'VERT', {1}, )]),
# 20
- MeshTest('Pyramid4_test_9', 'Pyr4_test', 'Pyr4_result_9',
+ SpecMeshTest('Pyramid4_test_9', 'Pyr4_test', 'Pyr4_result_9',
[OperatorSpecEditMode('bevel',
{'offset': 0.75, 'segments': 3, 'affect': 'VERTICES', 'profile': 0.25}, 'VERT',
{1}, )]),
- MeshTest('Pyramid6_test_1', 'Pyr6_test', 'Pyr6_result_1',
+ SpecMeshTest('Pyramid6_test_1', 'Pyr6_test', 'Pyr6_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3}, )]),
- MeshTest('Pyramid6_test_2', 'Pyr6_test', 'Pyr6_result_2',
+ SpecMeshTest('Pyramid6_test_2', 'Pyr6_test', 'Pyr6_result_2',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {8, 2, 3}, )]),
- MeshTest('Pyramid6_test_3', 'Pyr6_test', 'Pyr6_result_3',
+ SpecMeshTest('Pyramid6_test_3', 'Pyr6_test', 'Pyr6_result_3',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 4, 'profile': 0.8}, 'EDGE',
{0, 2, 3, 4, 6, 7, 9, 10, 11}, )]),
- MeshTest('Sept_test_1', 'Sept_test', 'Sept_result_1',
+ SpecMeshTest('Sept_test_1', 'Sept_test', 'Sept_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.1}, 'EDGE', {8, 9, 3, 11}, )]),
# 25
- MeshTest('Sept_test_2', 'Sept_test', 'Sept_result_2',
+ SpecMeshTest('Sept_test_2', 'Sept_test', 'Sept_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.1, 'offset_type': 'WIDTH'}, 'EDGE', {8, 9, 11}, )]),
- MeshTest('Saddle_test_1', 'Saddle_test', 'Saddle_result_1',
+ SpecMeshTest('Saddle_test_1', 'Saddle_test', 'Saddle_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.3, 'segments': 5}, 'EDGE', {2, 8, 9, 12, 13, 14}, )]),
- MeshTest('Saddle_test_2', 'Saddle_test', 'Saddle_result_2',
+ SpecMeshTest('Saddle_test_2', 'Saddle_test', 'Saddle_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.6, 'segments': 6, 'affect': 'VERTICES'}, 'VERT', {4}, )]),
- MeshTest('Bent_test', 'Bent_test', 'Bent_result_1',
+ SpecMeshTest('Bent_test', 'Bent_test', 'Bent_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3},
'EDGE',
{2, 5, 8, 11, 14, 18, 21, 24, 27, 30, 34, 37, 40, 43, 46, 50, 53, 56, 59, 62,
112, 113, 114, 115}, )]),
- MeshTest('Bentlines_test_1', 'Bentlines_test', 'Bentlines_result_1',
+ SpecMeshTest('Bentlines_test_1', 'Bentlines_test', 'Bentlines_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 3}, 'EDGE', {1, 8, 9, 10, 11}, )]),
# 30
- MeshTest('Flaretop_test_1', 'Flaretop_test', 'Flaretop_result_1',
+ SpecMeshTest('Flaretop_test_1', 'Flaretop_test', 'Flaretop_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 2}, 'EDGE', {26, 12, 20}, )]),
- MeshTest('Flaretop_test_2', 'Flaretop_test', 'Flaretop_result_2',
+ SpecMeshTest('Flaretop_test_2', 'Flaretop_test', 'Flaretop_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 2, 'profile': 1.0}, 'EDGE', {26, 12, 20}, )]),
- MeshTest('Flaretop_test_3', 'Flaretop_test', 'Flaretop_result_3',
+ SpecMeshTest('Flaretop_test_3', 'Flaretop_test', 'Flaretop_result_3',
[OperatorSpecEditMode('bevel',
{'offset': 0.4, 'segments': 4}, 'FACE', {1, 6, 7, 8, 9, 10, 11, 12}, )]),
- MeshTest('BentL_test', 'BentL_test', 'BentL_result_1',
+ SpecMeshTest('BentL_test', 'BentL_test', 'BentL_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {4, 8, 10, 18, 24}, )]),
- MeshTest('Wires_test_1', 'Wires_test', 'Wires_test_result_1',
+ SpecMeshTest('Wires_test_1', 'Wires_test', 'Wires_test_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.3}, 'EDGE', {0, 1, 2, 10}, )]),
# 35
- MeshTest('Wires_test_2', 'Wires_test', 'Wires_test_result_2',
+ SpecMeshTest('Wires_test_2', 'Wires_test', 'Wires_test_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.3, 'affect': 'VERTICES'}, 'VERT',
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, )]),
- MeshTest('tri_test_1', 'tri', 'tri_result_1',
+ SpecMeshTest('tri_test_1', 'tri', 'tri_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri_test_2', 'tri', 'tri_result_2',
+ SpecMeshTest('tri_test_2', 'tri', 'tri_result_2',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri_test_3', 'tri', 'tri_result_3',
+ SpecMeshTest('tri_test_3', 'tri', 'tri_result_3',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri_test_4', 'tri', 'tri_result_4',
+ SpecMeshTest('tri_test_4', 'tri', 'tri_result_4',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
# 40
- MeshTest('tri_test_5', 'tri', 'tri_result_5',
+ SpecMeshTest('tri_test_5', 'tri', 'tri_result_5',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]),
- MeshTest('tri_test_6', 'tri', 'tri_result_6',
+ SpecMeshTest('tri_test_6', 'tri', 'tri_result_6',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
- MeshTest('tri_test_7', 'tri', 'tri_result_7',
+ SpecMeshTest('tri_test_7', 'tri', 'tri_result_7',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
- MeshTest('tri_test_8', 'tri', 'tri_result_8',
+ SpecMeshTest('tri_test_8', 'tri', 'tri_result_8',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
- MeshTest('tri_test_9', 'tri', 'tri_result_9',
+ SpecMeshTest('tri_test_9', 'tri', 'tri_result_9',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {1}, )]),
# 45
- MeshTest('tri1gap_test_2', 'tri1gap', 'tri1gap_result_2',
+ SpecMeshTest('tri1gap_test_2', 'tri1gap', 'tri1gap_result_2',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri1gap_test_3', 'tri1gap', 'tri1gap_result_3',
+ SpecMeshTest('tri1gap_test_3', 'tri1gap', 'tri1gap_result_3',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri1gap_test_1', 'tri1gap', 'tri1gap_result_1',
+ SpecMeshTest('tri1gap_test_1', 'tri1gap', 'tri1gap_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri1gap_test_4', 'tri1gap', 'tri1gap_result_4',
+ SpecMeshTest('tri1gap_test_4', 'tri1gap', 'tri1gap_result_4',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
- MeshTest('tri1gap_test_5', 'tri1gap', 'tri1gap_result_5',
+ SpecMeshTest('tri1gap_test_5', 'tri1gap', 'tri1gap_result_5',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]),
# 50
- MeshTest('tri1gap_test_6', 'tri1gap', 'tri1gap_result_6',
+ SpecMeshTest('tri1gap_test_6', 'tri1gap', 'tri1gap_result_6',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4}, )]),
- MeshTest('tri1gap_test_7', 'tri1gap', 'tri1gap_result_7',
+ SpecMeshTest('tri1gap_test_7', 'tri1gap', 'tri1gap_result_7',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 5}, )]),
- MeshTest('tri1gap_test_8', 'tri1gap', 'tri1gap_result_8',
+ SpecMeshTest('tri1gap_test_8', 'tri1gap', 'tri1gap_result_8',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 5}, )]),
- MeshTest('tri1gap_test_9', 'tri1gap', 'tri1gap_result_9',
+ SpecMeshTest('tri1gap_test_9', 'tri1gap', 'tri1gap_result_9',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 5}, )]),
- MeshTest('tri1gap_test_10', 'tri1gap', 'tri1gap_result_10',
+ SpecMeshTest('tri1gap_test_10', 'tri1gap', 'tri1gap_result_10',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
# 55
- MeshTest('tri2gaps_test_1', 'tri2gaps', 'tri2gaps_result_1',
+ SpecMeshTest('tri2gaps_test_1', 'tri2gaps', 'tri2gaps_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri2gaps_test_2', 'tri2gaps', 'tri2gaps_result_2',
+ SpecMeshTest('tri2gaps_test_2', 'tri2gaps', 'tri2gaps_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri2gaps_test_3', 'tri2gaps', 'tri2gaps_result_3',
+ SpecMeshTest('tri2gaps_test_3', 'tri2gaps', 'tri2gaps_result_3',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri2gaps_test_4', 'tri2gaps', 'tri2gaps_result_4',
+ SpecMeshTest('tri2gaps_test_4', 'tri2gaps', 'tri2gaps_result_4',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
- MeshTest('tri2gaps_test_5', 'tri2gaps', 'tri2gaps_result_5',
+ SpecMeshTest('tri2gaps_test_5', 'tri2gaps', 'tri2gaps_result_5',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]),
# 60
- MeshTest('tri2gaps_test_6', 'tri2gaps', 'tri2gaps_result_6',
+ SpecMeshTest('tri2gaps_test_6', 'tri2gaps', 'tri2gaps_result_6',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4}, )]),
- MeshTest('tri3gaps_test_1', 'tri3gaps', 'tri3gaps_result_1',
+ SpecMeshTest('tri3gaps_test_1', 'tri3gaps', 'tri3gaps_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri3gaps_test_2', 'tri3gaps', 'tri3gaps_result_2',
+ SpecMeshTest('tri3gaps_test_2', 'tri3gaps', 'tri3gaps_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('tri3gaps_test_3', 'tri3gaps', 'tri3gaps_result_3',
+ SpecMeshTest('tri3gaps_test_3', 'tri3gaps', 'tri3gaps_result_3',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
- MeshTest('cube3_test_1', 'cube3', 'cube3_result_1',
+ SpecMeshTest('cube3_test_1', 'cube3', 'cube3_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.2}, 'EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, )]),
# 65
- MeshTest('cube3_test_2', 'cube3', 'cube3_result_2',
+ SpecMeshTest('cube3_test_2', 'cube3', 'cube3_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2}, 'EDGE',
{32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, )]),
- MeshTest('cube3_test_3', 'cube3', 'cube3_result_3',
+ SpecMeshTest('cube3_test_3', 'cube3', 'cube3_result_3',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {32, 35}, )]),
- MeshTest('cube3_test_4', 'cube3', 'cube3_result_4',
+ SpecMeshTest('cube3_test_4', 'cube3', 'cube3_result_4',
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {24, 35}, )]),
- MeshTest('cube3_test_5', 'cube3', 'cube3_result_5',
+ SpecMeshTest('cube3_test_5', 'cube3', 'cube3_result_5',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {24, 32, 35}, )]),
- MeshTest('cube3_test_6', 'cube3', 'cube3_result_6',
+ SpecMeshTest('cube3_test_6', 'cube3', 'cube3_result_6',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {24, 32, 35}, )]),
# 70
- MeshTest('Tray', 'Tray', 'Tray_result_1',
+ SpecMeshTest('Tray', 'Tray', 'Tray_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.01, 'segments': 2}, 'EDGE', {0, 1, 6, 7, 12, 14, 16, 17}, )]),
- MeshTest("test 73", 'Bumptop', 'Bumptop_result_1',
+ SpecMeshTest("test 73", 'Bumptop', 'Bumptop_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.1, 'segments': 4}, 'EDGE',
{33, 4, 38, 8, 41, 10, 42, 12, 14, 17, 24, 31}, )]),
- MeshTest('Multisegment_test_1', 'Multisegment_test', 'Multisegment_result_1',
+ SpecMeshTest('Multisegment_test_1', 'Multisegment_test', 'Multisegment_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.2}, 'EDGE', {16, 14, 15}, )]),
- MeshTest('Window_test', 'Window_test', 'Window_result_1',
+ SpecMeshTest('Window_test', 'Window_test', 'Window_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.05, 'segments': 2}, 'EDGE', {19, 20, 23, 15}, )]),
# 75
- MeshTest("test 77", 'Cube_hn_test', 'Cube_hn_result_1',
+ SpecMeshTest("test 77", 'Cube_hn_test', 'Cube_hn_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'harden_normals': True}, 'EDGE', {8}, )]),
- MeshTest('Blocksteps_test_1', 'Blocksteps_test', 'Blocksteps_result_1',
+ SpecMeshTest('Blocksteps_test_1', 'Blocksteps_test', 'Blocksteps_result_1',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'miter_outer': 'PATCH'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Blocksteps_test_2', 'Blocksteps_test', 'Blocksteps_result_2',
+ SpecMeshTest('Blocksteps_test_2', 'Blocksteps_test', 'Blocksteps_result_2',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Blocksteps_test_3', 'Blocksteps_test', 'Blocksteps_result_3',
+ SpecMeshTest('Blocksteps_test_3', 'Blocksteps_test', 'Blocksteps_result_3',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 3, 'miter_outer': 'PATCH'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Blocksteps_test_4', 'Blocksteps_test', 'Blocksteps_result_4',
+ SpecMeshTest('Blocksteps_test_4', 'Blocksteps_test', 'Blocksteps_result_4',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]),
# 80
- MeshTest('Blocksteps_test_5', 'Blocksteps_test', 'Blocksteps_result_5',
+ SpecMeshTest('Blocksteps_test_5', 'Blocksteps_test', 'Blocksteps_result_5',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Blocksteps_test_6', 'Blocksteps_test', 'Blocksteps_result_6',
+ SpecMeshTest('Blocksteps_test_6', 'Blocksteps_test', 'Blocksteps_result_6',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Blocksteps_test_7', 'Blocksteps_test', 'Blocksteps_result_7',
+ SpecMeshTest('Blocksteps_test_7', 'Blocksteps_test', 'Blocksteps_result_7',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
- MeshTest("Blocksteps_test_8", 'Blocksteps_test', 'Blocksteps_result_8',
+ SpecMeshTest("Blocksteps_test_8", 'Blocksteps_test', 'Blocksteps_result_8',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'},
'EDGE', {4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Blocksteps2_test', 'Blocksteps2_test', 'Blocksteps2_result_9',
+ SpecMeshTest('Blocksteps2_test', 'Blocksteps2_test', 'Blocksteps2_result_9',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
# 85
- MeshTest('Blocksteps3_test', 'Blocksteps3_test', 'Blocksteps3_result_10',
+ SpecMeshTest('Blocksteps3_test', 'Blocksteps3_test', 'Blocksteps3_result_10',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Blocksteps4_test_1', 'Blocksteps4_test', 'Blocksteps4_result_11',
+ SpecMeshTest('Blocksteps4_test_1', 'Blocksteps4_test', 'Blocksteps4_result_11',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Blocksteps4_test_2', 'Blocksteps4_test', 'Blocksteps4_result_12',
+ SpecMeshTest('Blocksteps4_test_2', 'Blocksteps4_test', 'Blocksteps4_result_12',
[OperatorSpecEditMode('bevel',
{'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}, 'EDGE',
{4, 7, 39, 27, 30, 31}, )]),
- MeshTest('Spike_test', 'Spike_test', 'Spike_result_1',
+ SpecMeshTest('Spike_test', 'Spike_test', 'Spike_result_1',
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 7})])
]
diff --git a/tests/python/bl_pyapi_prop_array.py b/tests/python/bl_pyapi_prop_array.py
index ac1082c009e..2f38b67d7a1 100644
--- a/tests/python/bl_pyapi_prop_array.py
+++ b/tests/python/bl_pyapi_prop_array.py
@@ -2,18 +2,61 @@
# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_prop_array.py -- --verbose
import bpy
+from bpy.props import (
+ BoolVectorProperty,
+ FloatVectorProperty,
+ IntVectorProperty,
+)
import unittest
import numpy as np
+id_inst = bpy.context.scene
+id_type = bpy.types.Scene
+
+
+# -----------------------------------------------------------------------------
+# Utility Functions
+
+def seq_items_xform(data, xform_fn):
+ """
+ Recursively expand items using `xform_fn`.
+ """
+ if hasattr(data, "__len__"):
+ return tuple(seq_items_xform(v, xform_fn) for v in data)
+ return xform_fn(data)
+
+
+def seq_items_as_tuple(data):
+ """
+ Return nested sequences as a nested tuple.
+ Useful when comparing different kinds of nested sequences.
+ """
+ return seq_items_xform(data, lambda v: v)
+
+
+def seq_items_as_dims(data):
+ """
+ Nested length calculation, extracting the length from each sequence.
+ Where a 4x4 matrix returns ``(4, 4)`` for example.
+ """
+ return ((len(data),) + seq_items_as_dims(data[0])) if hasattr(data, "__len__") else ()
+
+
+# -----------------------------------------------------------------------------
+# Tests
class TestPropArray(unittest.TestCase):
def setUp(self):
- bpy.types.Scene.test_array_f = bpy.props.FloatVectorProperty(size=10)
- bpy.types.Scene.test_array_i = bpy.props.IntVectorProperty(size=10)
+ id_type.test_array_f = FloatVectorProperty(size=10)
+ id_type.test_array_i = IntVectorProperty(size=10)
scene = bpy.context.scene
self.array_f = scene.test_array_f
self.array_i = scene.test_array_i
+ def tearDown(self):
+ del id_type.test_array_f
+ del id_type.test_array_i
+
def test_foreach_getset_i(self):
with self.assertRaises(TypeError):
self.array_i.foreach_set(range(5))
@@ -79,6 +122,79 @@ class TestPropArray(unittest.TestCase):
self.assertEqual(v1, v2)
+class TestPropArrayMultiDimensional(unittest.TestCase):
+
+ def setUp(self):
+ self._initial_dir = set(dir(id_type))
+
+ def tearDown(self):
+ for member in (set(dir(id_type)) - self._initial_dir):
+ delattr(id_type, member)
+
+ def test_defaults(self):
+ # The data is in int format, converted into float & bool to avoid duplication.
+ default_data = (
+ # 1D.
+ (1,),
+ (1, 2),
+ (1, 2, 3),
+ (1, 2, 3, 4),
+ # 2D.
+ ((1,),),
+ ((1,), (11,)),
+ ((1, 2), (11, 22)),
+ ((1, 2, 3), (11, 22, 33)),
+ ((1, 2, 3, 4), (11, 22, 33, 44)),
+ # 3D.
+ (((1,),),),
+ ((1,), (11,), (111,)),
+ ((1, 2), (11, 22), (111, 222),),
+ ((1, 2, 3), (11, 22, 33), (111, 222, 333)),
+ ((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444)),
+ )
+ for data in default_data:
+ for (vector_prop_fn, xform_fn) in (
+ (BoolVectorProperty, lambda v: bool(v % 2)),
+ (FloatVectorProperty, lambda v: float(v)),
+ (IntVectorProperty, lambda v: v),
+ ):
+ data_native = seq_items_xform(data, xform_fn)
+ size = seq_items_as_dims(data)
+ id_type.temp = vector_prop_fn(size=size, default=data_native)
+ data_as_tuple = seq_items_as_tuple(id_inst.temp)
+ self.assertEqual(data_as_tuple, data_native)
+ del id_type.temp
+
+ def test_matrix(self):
+ data = ((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444), (1111, 2222, 3333, 4444),)
+ data_native = seq_items_xform(data, lambda v: float(v))
+ id_type.temp = FloatVectorProperty(size=(4, 4), subtype='MATRIX', default=data_native)
+ data_as_tuple = seq_items_as_tuple(id_inst.temp)
+ self.assertEqual(data_as_tuple, data_native)
+ del id_type.temp
+
+ def test_matrix_with_callbacks(self):
+ # """
+ # Internally matrices have rows/columns swapped,
+ # This test ensures this is being done properly.
+ # """
+ data = ((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444), (1111, 2222, 3333, 4444),)
+ data_native = seq_items_xform(data, lambda v: float(v))
+ local_data = {"array": data}
+
+ def get_fn(id_arg):
+ return local_data["array"]
+
+ def set_fn(id_arg, value):
+ local_data["array"] = value
+
+ id_type.temp = FloatVectorProperty(size=(4, 4), subtype='MATRIX', get=get_fn, set=set_fn)
+ id_inst.temp = data_native
+ data_as_tuple = seq_items_as_tuple(id_inst.temp)
+ self.assertEqual(data_as_tuple, data_native)
+ del id_type.temp
+
+
if __name__ == '__main__':
import sys
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
diff --git a/tests/python/boolean_operator.py b/tests/python/boolean_operator.py
index 0db6a074699..7d4de7143dd 100644
--- a/tests/python/boolean_operator.py
+++ b/tests/python/boolean_operator.py
@@ -29,37 +29,37 @@ import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest
+from modules.mesh_test import SpecMeshTest, OperatorSpecEditMode, RunTest
def main():
tests = [
- MeshTest('Cubecube_intersect_union', 'Cubecube', 'Cubecube_result_1',
+ SpecMeshTest('Cubecube_intersect_union', 'Cubecube', 'Cubecube_result_1',
[OperatorSpecEditMode('intersect_boolean',
{'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
- MeshTest('Cubecube_intersect_intersect', 'Cubecube', 'Cubecube_result_2',
+ SpecMeshTest('Cubecube_intersect_intersect', 'Cubecube', 'Cubecube_result_2',
[OperatorSpecEditMode('intersect_boolean', {'operation': 'INTERSECT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
- MeshTest('Cubecube_intersect_difference', 'Cubecube', 'Cubecube_result_3',
+ SpecMeshTest('Cubecube_intersect_difference', 'Cubecube', 'Cubecube_result_3',
[OperatorSpecEditMode('intersect_boolean', {'operation': 'DIFFERENCE', 'solver': 'FAST'}, 'FACE',
{0, 1, 2, 3, 4, 5}, )]),
- MeshTest('Cubecube_intersect_cut', 'Cubecube', 'Cubecube_result_4', [OperatorSpecEditMode('intersect',
+ SpecMeshTest('Cubecube_intersect_cut', 'Cubecube', 'Cubecube_result_4', [OperatorSpecEditMode('intersect',
{'separate_mode': 'CUT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
- MeshTest('Cubecube_intersect_all', 'Cubecube', 'Cubecube_result_5',
+ SpecMeshTest('Cubecube_intersect_all', 'Cubecube', 'Cubecube_result_5',
[OperatorSpecEditMode('intersect',
{'separate_mode': 'ALL', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
- MeshTest('Cubecube_intersect_none', 'Cubecube', 'Cubecube_result_6',
+ SpecMeshTest('Cubecube_intersect_none', 'Cubecube', 'Cubecube_result_6',
[OperatorSpecEditMode('intersect',
{'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
- MeshTest('Cubecube_intersect_select_none', 'Cubecube',
+ SpecMeshTest('Cubecube_intersect_select_none', 'Cubecube',
'Cubecube_result_7',
[OperatorSpecEditMode('intersect',
{'mode': 'SELECT', 'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE',
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, )]),
- MeshTest('Cubecone_intersect_union', 'Cubecone', 'Cubecone_result_1',
+ SpecMeshTest('Cubecone_intersect_union', 'Cubecone', 'Cubecone_result_1',
[OperatorSpecEditMode('intersect_boolean',
{'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {6, 7, 8, 9, 10}, )]),
- MeshTest('Cubecones_intersect_union', 'Cubecones', 'Cubecones_result_1',
+ SpecMeshTest('Cubecones_intersect_union', 'Cubecones', 'Cubecones_result_1',
[OperatorSpecEditMode('intersect_boolean', {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
]
diff --git a/tests/python/curve_to_mesh.py b/tests/python/curve_to_mesh.py
index 998ecdae116..654cfc750d7 100644
--- a/tests/python/curve_to_mesh.py
+++ b/tests/python/curve_to_mesh.py
@@ -24,74 +24,74 @@ import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import MeshTest, OperatorSpecObjectMode, RunTest
+from modules.mesh_test import SpecMeshTest, OperatorSpecObjectMode, RunTest
def main():
tests = [
- MeshTest('2D Non Cyclic', 'test2DNonCyclic', 'expected2DNonCyclic',
+ SpecMeshTest('2D Non Cyclic', 'test2DNonCyclic', 'expected2DNonCyclic',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('2D NURBS With Tail', 'test2DNURBSWithTail', 'expected2DNURBSWithTail',
+ SpecMeshTest('2D NURBS With Tail', 'test2DNURBSWithTail', 'expected2DNURBSWithTail',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('2D Shape With Hole', 'test2DShapeWithHole', 'expected2DShapeWithHole',
+ SpecMeshTest('2D Shape With Hole', 'test2DShapeWithHole', 'expected2DShapeWithHole',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('2D Simple Lower Res', 'test2DSimpleLowerRes', 'expected2DSimpleLowerRes',
+ SpecMeshTest('2D Simple Lower Res', 'test2DSimpleLowerRes', 'expected2DSimpleLowerRes',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('2D Simple Low Res', 'test2DSimpleLowRes', 'expected2DSimpleLowRes',
+ SpecMeshTest('2D Simple Low Res', 'test2DSimpleLowRes', 'expected2DSimpleLowRes',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('2D Square', 'test2DSquare', 'expected2DSquare',
+ SpecMeshTest('2D Square', 'test2DSquare', 'expected2DSquare',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('2D Extrude', 'test2DExtrude', 'expected2DExtrude',
+ SpecMeshTest('2D Extrude', 'test2DExtrude', 'expected2DExtrude',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Back', 'testBevelBack', 'expectedBevelBack',
+ SpecMeshTest('Bevel Back', 'testBevelBack', 'expectedBevelBack',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Back Low Res', 'testBevelBackLowRes', 'expectedBevelBackLowRes',
+ SpecMeshTest('Bevel Back Low Res', 'testBevelBackLowRes', 'expectedBevelBackLowRes',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Extrude Back', 'testBevelExtrudeBack', 'expectedBevelExtrudeBack',
+ SpecMeshTest('Bevel Extrude Back', 'testBevelExtrudeBack', 'expectedBevelExtrudeBack',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Extrude Front', 'testBevelExtrudeFront', 'expectedBevelExtrudeFront',
+ SpecMeshTest('Bevel Extrude Front', 'testBevelExtrudeFront', 'expectedBevelExtrudeFront',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Extrude Full', 'testBevelExtrudeFull', 'expectedBevelExtrudeFull',
+ SpecMeshTest('Bevel Extrude Full', 'testBevelExtrudeFull', 'expectedBevelExtrudeFull',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Extrude Half', 'testBevelExtrudeHalf', 'expectedBevelExtrudeHalf',
+ SpecMeshTest('Bevel Extrude Half', 'testBevelExtrudeHalf', 'expectedBevelExtrudeHalf',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Front', 'testBevelFront', 'expectedBevelFront',
+ SpecMeshTest('Bevel Front', 'testBevelFront', 'expectedBevelFront',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Front Low Res', 'testBevelFrontLowRes', 'expectedBevelFrontLowRes',
+ SpecMeshTest('Bevel Front Low Res', 'testBevelFrontLowRes', 'expectedBevelFrontLowRes',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Full', 'testBevelFull', 'expectedBevelFull',
+ SpecMeshTest('Bevel Full', 'testBevelFull', 'expectedBevelFull',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Full Low Res', 'testBevelFullLowRes', 'expectedBevelFullLowRes',
+ SpecMeshTest('Bevel Full Low Res', 'testBevelFullLowRes', 'expectedBevelFullLowRes',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Half', 'testBevelHalf', 'expectedBevelHalf',
+ SpecMeshTest('Bevel Half', 'testBevelHalf', 'expectedBevelHalf',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Bevel Half Low Res', 'testBevelHalfLowRes', 'expectedBevelHalfLowRes',
+ SpecMeshTest('Bevel Half Low Res', 'testBevelHalfLowRes', 'expectedBevelHalfLowRes',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps None', 'testCapsNone', 'expectedCapsNone',
+ SpecMeshTest('Caps None', 'testCapsNone', 'expectedCapsNone',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps Object Bevel', 'testCapsObjectBevel', 'expectedCapsObjectBevel',
+ SpecMeshTest('Caps Object Bevel', 'testCapsObjectBevel', 'expectedCapsObjectBevel',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps Profile Bevel', 'testCapsProfileBevel', 'expectedCapsProfileBevel',
+ SpecMeshTest('Caps Profile Bevel', 'testCapsProfileBevel', 'expectedCapsProfileBevel',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps Profile Bevel Half', 'testCapsProfileBevelHalf', 'expectedCapsProfileBevelHalf',
+ SpecMeshTest('Caps Profile Bevel Half', 'testCapsProfileBevelHalf', 'expectedCapsProfileBevelHalf',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps Profile Bevel Quarter', 'testCapsProfileBevelQuarter', 'expectedCapsProfileBevelQuarter',
+ SpecMeshTest('Caps Profile Bevel Quarter', 'testCapsProfileBevelQuarter', 'expectedCapsProfileBevelQuarter',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps Round Bevel', 'testCapsRoundBevel', 'expectedCapsRoundBevel',
+ SpecMeshTest('Caps Round Bevel', 'testCapsRoundBevel', 'expectedCapsRoundBevel',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps Round Bevel Extrude', 'testCapsRoundBevelExtrude', 'expectedCapsRoundBevelExtrude',
+ SpecMeshTest('Caps Round Bevel Extrude', 'testCapsRoundBevelExtrude', 'expectedCapsRoundBevelExtrude',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps Round Bevel Half', 'testCapsRoundBevelHalf', 'expectedCapsRoundBevelHalf',
+ SpecMeshTest('Caps Round Bevel Half', 'testCapsRoundBevelHalf', 'expectedCapsRoundBevelHalf',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Caps Round Bevel Quarter', 'testCapsRoundBevelQuarter', 'expectedCapsRoundBevelQuarter',
+ SpecMeshTest('Caps Round Bevel Quarter', 'testCapsRoundBevelQuarter', 'expectedCapsRoundBevelQuarter',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Extrude Back', 'testExtrudeBack', 'expectedExtrudeBack',
+ SpecMeshTest('Extrude Back', 'testExtrudeBack', 'expectedExtrudeBack',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Extrude Front', 'testExtrudeFront', 'expectedExtrudeFront',
+ SpecMeshTest('Extrude Front', 'testExtrudeFront', 'expectedExtrudeFront',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Extrude Full', 'testExtrudeFull', 'expectedExtrudeFull',
+ SpecMeshTest('Extrude Full', 'testExtrudeFull', 'expectedExtrudeFull',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
- MeshTest('Extrude Half', 'testExtrudeHalf', 'expectedExtrudeHalf',
+ SpecMeshTest('Extrude Half', 'testExtrudeHalf', 'expectedExtrudeHalf',
[OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
]
operator_test = RunTest(tests)
diff --git a/tests/python/deform_modifiers.py b/tests/python/deform_modifiers.py
index 7c4ea457e9d..d1241e4d9f0 100644
--- a/tests/python/deform_modifiers.py
+++ b/tests/python/deform_modifiers.py
@@ -28,14 +28,14 @@ import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import MeshTest, ModifierSpec, OperatorSpecObjectMode, DeformModifierSpec, RunTest
+from modules.mesh_test import SpecMeshTest, ModifierSpec, OperatorSpecObjectMode, DeformModifierSpec, RunTest
tests = [
# Surface Deform Test, finally can bind to the Target object.
# Actual deformation occurs by animating imitating user input.
- MeshTest("SurfaceDeform", "testObjMonkeySurfaceDeform", "expObjMonkeySurfaceDeform",
+ SpecMeshTest("SurfaceDeform", "testObjMonkeySurfaceDeform", "expObjMonkeySurfaceDeform",
[DeformModifierSpec(10, [
ModifierSpec('surface_deform', 'SURFACE_DEFORM', {'target': bpy.data.objects["Cube"]})],
OperatorSpecObjectMode('surfacedeform_bind', {'modifier': 'surface_deform'}))]),
@@ -43,7 +43,7 @@ tests = [
# Mesh Deform Test, finally can bind to the Target object.
# Actual deformation occurs by animating imitating user input.
- MeshTest("MeshDeform", "testObjMonkeyMeshDeform", "expObjMonkeyMeshDeform",
+ SpecMeshTest("MeshDeform", "testObjMonkeyMeshDeform", "expObjMonkeyMeshDeform",
[DeformModifierSpec(10, [ModifierSpec('mesh_deform', 'MESH_DEFORM',
{'object': bpy.data.objects["MeshCube"], 'precision': 2})],
OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform'}))]),
@@ -51,21 +51,21 @@ tests = [
# Surface Deform Test, finally can bind to the Target object.
# Actual deformation occurs by animating imitating user input.
- MeshTest("Hook", "testObjHookPlane", "expObjHookPlane",
+ SpecMeshTest("Hook", "testObjHookPlane", "expObjHookPlane",
[DeformModifierSpec(10, [ModifierSpec('hook', 'HOOK',
{'object': bpy.data.objects["Empty"], 'falloff_radius': 1,
'vertex_group': 'Group'})])]),
# Laplacian Deform Test, first a hook is attached.
- MeshTest("Laplace", "testObjCubeLaplacian", "expObjCubeLaplacian",
+ SpecMeshTest("Laplace", "testObjCubeLaplacian", "expObjCubeLaplacian",
[DeformModifierSpec(10,
[ModifierSpec('hook2', 'HOOK', {'object': bpy.data.objects["Empty.001"],
'vertex_group': 'hook_vg'}),
ModifierSpec('laplace', 'LAPLACIANDEFORM', {'vertex_group': 'laplace_vg'})],
OperatorSpecObjectMode('laplaciandeform_bind', {'modifier': 'laplace'}))]),
- MeshTest("WarpPlane", "testObjPlaneWarp", "expObjPlaneWarp",
+ SpecMeshTest("WarpPlane", "testObjPlaneWarp", "expObjPlaneWarp",
[DeformModifierSpec(10, [ModifierSpec('warp', 'WARP',
{'object_from': bpy.data.objects["From"],
'object_to': bpy.data.objects["To"],
@@ -74,29 +74,29 @@ tests = [
#############################################
# Curves Deform Modifiers
#############################################
- MeshTest("CurveArmature", "testObjBezierCurveArmature", "expObjBezierCurveArmature",
+ SpecMeshTest("CurveArmature", "testObjBezierCurveArmature", "expObjBezierCurveArmature",
[DeformModifierSpec(10, [ModifierSpec('curve_armature', 'ARMATURE',
{'object': bpy.data.objects['testArmatureHelper'],
'use_vertex_groups': False, 'use_bone_envelopes': True})])]),
- MeshTest("CurveLattice", "testObjBezierCurveLattice", "expObjBezierCurveLattice",
+ SpecMeshTest("CurveLattice", "testObjBezierCurveLattice", "expObjBezierCurveLattice",
[DeformModifierSpec(10, [ModifierSpec('curve_lattice', 'LATTICE',
{'object': bpy.data.objects['testLatticeCurve']})])]),
# HOOK for Curves can't be tested with current framework, as it requires going to Edit Mode to select vertices,
# here is no equivalent of a vertex group in Curves.
# Dummy test for Hook, can also be called corner case
- MeshTest("CurveHook", "testObjBezierCurveHook", "expObjBezierCurveHook",
+ SpecMeshTest("CurveHook", "testObjBezierCurveHook", "expObjBezierCurveHook",
[DeformModifierSpec(10,
[ModifierSpec('curve_Hook', 'HOOK', {'object': bpy.data.objects['EmptyCurve']})])]),
- MeshTest("MeshDeformCurve", "testObjCurveMeshDeform", "expObjCurveMeshDeform",
+ SpecMeshTest("MeshDeformCurve", "testObjCurveMeshDeform", "expObjCurveMeshDeform",
[DeformModifierSpec(10, [
ModifierSpec('mesh_deform_curve', 'MESH_DEFORM', {'object': bpy.data.objects["Cylinder"],
'precision': 2})],
OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform_curve'}))]),
- MeshTest("WarpCurve", "testObjBezierCurveWarp", "expObjBezierCurveWarp",
+ SpecMeshTest("WarpCurve", "testObjBezierCurveWarp", "expObjBezierCurveWarp",
[DeformModifierSpec(10, [ModifierSpec('warp_curve', 'WARP',
{'object_from': bpy.data.objects["From_curve"],
'object_to': bpy.data.objects["To_curve"]})])]),
diff --git a/tests/python/geo_node_test.py b/tests/python/geo_node_test.py
new file mode 100644
index 00000000000..fa87fb23bee
--- /dev/null
+++ b/tests/python/geo_node_test.py
@@ -0,0 +1,33 @@
+# ##### 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>
+
+
+import os
+import sys
+
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+from modules.mesh_test import BlendFileTest
+
+geo_node_test = BlendFileTest("test_object", "expected_object")
+result = geo_node_test.run_test()
+
+# Telling `ctest` about the failed test by raising Exception.
+if result == False:
+ raise Exception("Failed {}".format(geo_node_test.test_name))
diff --git a/tests/python/modifiers.py b/tests/python/modifiers.py
index 09de29df401..1d9242046b3 100644
--- a/tests/python/modifiers.py
+++ b/tests/python/modifiers.py
@@ -26,7 +26,7 @@ from random import shuffle, seed
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest
seed(0)
@@ -87,22 +87,22 @@ def main():
# List of 'Generate' modifiers on a cube
###############################
# 0
- # MeshTest("testCube", "expectedCube", get_generate_modifiers_list("testCube")),
- MeshTest("CubeRandom", "testCubeRandom", "expectedCubeRandom",
+ # SpecMeshTest("testCube", "expectedCube", get_generate_modifiers_list("testCube")),
+ SpecMeshTest("CubeRandom", "testCubeRandom", "expectedCubeRandom",
get_generate_modifiers_list("testCubeRandom", randomize=True)),
- MeshTest("CubeMaskFirst", "testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list),
+ SpecMeshTest("CubeMaskFirst", "testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list),
- MeshTest("CollapseDecimate", "testCollapseDecimate", "expectedCollapseDecimate",
+ SpecMeshTest("CollapseDecimate", "testCollapseDecimate", "expectedCollapseDecimate",
[ModifierSpec('decimate', 'DECIMATE',
{'decimate_type': 'COLLAPSE', 'ratio': 0.25, 'use_collapse_triangulate': True})]),
- MeshTest("PlanarDecimate", "testPlanarDecimate", "expectedPlanarDecimate",
+ SpecMeshTest("PlanarDecimate", "testPlanarDecimate", "expectedPlanarDecimate",
[ModifierSpec('decimate', 'DECIMATE',
{'decimate_type': 'DISSOLVE', 'angle_limit': math.radians(30)})]),
- MeshTest("UnsubdivideDecimate", "testUnsubdivideDecimate", "expectedUnsubdivideDecimate",
+ SpecMeshTest("UnsubdivideDecimate", "testUnsubdivideDecimate", "expectedUnsubdivideDecimate",
[ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'UNSUBDIV', 'iterations': 2})]),
# 5
- MeshTest("RadialBisectMirror", "testRadialBisectMirror", "expectedRadialBisectMirror",
+ SpecMeshTest("RadialBisectMirror", "testRadialBisectMirror", "expectedRadialBisectMirror",
[ModifierSpec('mirror1', 'MIRROR', {'use_bisect_axis': (True, False, False)}),
ModifierSpec('mirror2', 'MIRROR', {'use_bisect_axis': (True, False, False),
'mirror_object': bpy.data.objects[
@@ -111,17 +111,17 @@ def main():
{'use_axis': (False, True, False), 'use_bisect_axis': (False, True, False),
'use_bisect_flip_axis': (False, True, False),
'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]})]),
- MeshTest("T58411Mirror", "regressT58411Mirror", "expectedT58411Mirror",
+ SpecMeshTest("T58411Mirror", "regressT58411Mirror", "expectedT58411Mirror",
[ModifierSpec('mirror', 'MIRROR', {}),
ModifierSpec('bevel', 'BEVEL', {'segments': 2, 'limit_method': 'WEIGHT'}),
ModifierSpec('subd', 'SUBSURF', {'levels': 1})]),
- MeshTest("BasicScrew", "testBasicScrew", "expectedBasicScrew",
+ SpecMeshTest("BasicScrew", "testBasicScrew", "expectedBasicScrew",
[ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testBasicScrewHelper"]}),
ModifierSpec("screw", 'SCREW',
{'angle': math.radians(400), 'steps': 20, 'iterations': 2, 'screw_offset': 2,
'use_normal_calculate': True})]),
- MeshTest("ObjectScrew", "testObjectScrew", "expectedObjectScrew",
+ SpecMeshTest("ObjectScrew", "testObjectScrew", "expectedObjectScrew",
[ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectScrewHelper2"]}),
ModifierSpec("screw", 'SCREW',
{"angle": math.radians(600), 'steps': 32, 'iterations': 1,
@@ -129,16 +129,16 @@ def main():
'use_normal_calculate': True, 'object': bpy.data.objects["testObjectScrewHelper1"]})]),
# 9
- MeshTest("MergedScrewWeld", "testMergedScrewWeld", "expectedMergedScrewWeld",
+ SpecMeshTest("MergedScrewWeld", "testMergedScrewWeld", "expectedMergedScrewWeld",
[ModifierSpec("screw", 'SCREW',
{'angle': math.radians(360), 'steps': 12, 'iterations': 1, 'screw_offset': 1,
'use_normal_calculate': True, 'use_merge_vertices': True}),
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.001})]),
- MeshTest("T72380Weld", "regressT72380Weld", "expectedT72380Weld",
+ SpecMeshTest("T72380Weld", "regressT72380Weld", "expectedT72380Weld",
[ModifierSpec('vedit', 'VERTEX_WEIGHT_EDIT',
{'vertex_group': 'Group', 'use_remove': True, 'remove_threshold': 1}),
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.2, "vertex_group": "Group"})]),
- MeshTest("T72792Weld", "regressT72792Weld", "expectedT72792Weld",
+ SpecMeshTest("T72792Weld", "regressT72792Weld", "expectedT72792Weld",
[ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 2}),
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.1, "vertex_group": "Group"})]),
@@ -146,89 +146,89 @@ def main():
# One 'Generate' modifier on primitive meshes
#############################################
# 12
- MeshTest("CubeArray", "testCubeArray", "expectedCubeArray",
+ SpecMeshTest("CubeArray", "testCubeArray", "expectedCubeArray",
[ModifierSpec('array', 'ARRAY', {})]),
- MeshTest("CapArray", "testCapArray", "expectedCapArray",
+ SpecMeshTest("CapArray", "testCapArray", "expectedCapArray",
[ModifierSpec('array', 'ARRAY',
{'fit_type': 'FIT_LENGTH', 'fit_length': 2.0,
'start_cap': bpy.data.objects["testCapStart"],
'end_cap': bpy.data.objects["testCapEnd"]})]),
- MeshTest("CurveArray", "testCurveArray", "expectedCurveArray",
+ SpecMeshTest("CurveArray", "testCurveArray", "expectedCurveArray",
[ModifierSpec('array', 'ARRAY',
{'fit_type': 'FIT_CURVE', 'curve': bpy.data.objects["testCurveArrayHelper"],
'use_relative_offset': False, 'use_constant_offset': True,
'constant_offset_displace': (0.5, 0, 0)})]),
- MeshTest("RadialArray", "testRadialArray", "expectedRadialArray",
+ SpecMeshTest("RadialArray", "testRadialArray", "expectedRadialArray",
[ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 3, 'use_merge_vertices': True,
'use_merge_vertices_cap': True, 'use_relative_offset': False,
'use_object_offset': True,
'offset_object': bpy.data.objects["testRadialArrayHelper"]})]),
- MeshTest("CylinderBuild", "testCylinderBuild", "expectedCylinderBuild",
+ SpecMeshTest("CylinderBuild", "testCylinderBuild", "expectedCylinderBuild",
[ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]),
# 17
- MeshTest("ConeDecimate", "testConeDecimate", "expectedConeDecimate",
+ SpecMeshTest("ConeDecimate", "testConeDecimate", "expectedConeDecimate",
[ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]),
- MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
+ SpecMeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
[ModifierSpec('edge split', 'EDGE_SPLIT', {})]),
- MeshTest("SphereMirror", "testSphereMirror", "expectedSphereMirror",
+ SpecMeshTest("SphereMirror", "testSphereMirror", "expectedSphereMirror",
[ModifierSpec('mirror', 'MIRROR', {})]),
- MeshTest("LocalMirror", "testLocalMirror", "expectedLocalMirror",
+ SpecMeshTest("LocalMirror", "testLocalMirror", "expectedLocalMirror",
[ModifierSpec('mirror', 'MIRROR', {'use_clip': True})]),
- MeshTest("ObjectOffsetMirror", "testObjectOffsetMirror", "expectedObjectOffsetMirror",
+ SpecMeshTest("ObjectOffsetMirror", "testObjectOffsetMirror", "expectedObjectOffsetMirror",
[ModifierSpec('mirror', 'MIRROR',
{'mirror_object': bpy.data.objects["testObjectOffsetMirrorHelper"]})]),
- MeshTest("CylinderMask", "testCylinderMask", "expectedCylinderMask",
+ SpecMeshTest("CylinderMask", "testCylinderMask", "expectedCylinderMask",
[ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]),
- MeshTest("ConeMultiRes", "testConeMultiRes", "expectedConeMultiRes",
+ SpecMeshTest("ConeMultiRes", "testConeMultiRes", "expectedConeMultiRes",
[ModifierSpec('multires', 'MULTIRES', {})]),
# 24
- MeshTest("CubeScrew", "testCubeScrew", "expectedCubeScrew",
+ SpecMeshTest("CubeScrew", "testCubeScrew", "expectedCubeScrew",
[ModifierSpec('screw', 'SCREW', {})]),
- MeshTest("CubeSolidify", "testCubeSolidify", "expectedCubeSolidify",
+ SpecMeshTest("CubeSolidify", "testCubeSolidify", "expectedCubeSolidify",
[ModifierSpec('solidify', 'SOLIDIFY', {})]),
- MeshTest("ComplexSolidify", "testComplexSolidify", "expectedComplexSolidify",
+ SpecMeshTest("ComplexSolidify", "testComplexSolidify", "expectedComplexSolidify",
[ModifierSpec('solidify', 'SOLIDIFY', {'solidify_mode': 'NON_MANIFOLD', 'thickness': 0.05, 'offset': 0,
'nonmanifold_thickness_mode': 'CONSTRAINTS'})]),
- MeshTest("T63063Solidify", "regressT63063Solidify", "expectedT63063Solidify",
+ SpecMeshTest("T63063Solidify", "regressT63063Solidify", "expectedT63063Solidify",
[ModifierSpec('solid', 'SOLIDIFY', {'thickness': 0.1, 'offset': 0.7})]),
- MeshTest("T61979Solidify", "regressT61979Solidify", "expectedT61979Solidify",
+ SpecMeshTest("T61979Solidify", "regressT61979Solidify", "expectedT61979Solidify",
[ModifierSpec('solid', 'SOLIDIFY',
{'thickness': -0.25, 'use_even_offset': True, 'use_quality_normals': True})]),
- MeshTest("MonkeySubsurf", "testMonkeySubsurf", "expectedMonkeySubsurf",
+ SpecMeshTest("MonkeySubsurf", "testMonkeySubsurf", "expectedMonkeySubsurf",
[ModifierSpec('subsurf', 'SUBSURF', {})]),
- MeshTest("CatmullClarkSubdivisionSurface", "testCatmullClarkSubdivisionSurface",
+ SpecMeshTest("CatmullClarkSubdivisionSurface", "testCatmullClarkSubdivisionSurface",
"expectedCatmullClarkSubdivisionSurface",
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
- MeshTest("SimpleSubdivisionSurface", "testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface",
+ SpecMeshTest("SimpleSubdivisionSurface", "testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface",
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2, 'subdivision_type': 'SIMPLE'})]),
- MeshTest("Crease2dSubdivisionSurface", "testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface",
+ SpecMeshTest("Crease2dSubdivisionSurface", "testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface",
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
- MeshTest("Crease3dSubdivisionSurface", "testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface",
+ SpecMeshTest("Crease3dSubdivisionSurface", "testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface",
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
# 34
- MeshTest("SphereTriangulate", "testSphereTriangulate", "expectedSphereTriangulate",
+ SpecMeshTest("SphereTriangulate", "testSphereTriangulate", "expectedSphereTriangulate",
[ModifierSpec('triangulate', 'TRIANGULATE', {})]),
- MeshTest("MonkeyWireframe", "testMonkeyWireframe", "expectedMonkeyWireframe",
+ SpecMeshTest("MonkeyWireframe", "testMonkeyWireframe", "expectedMonkeyWireframe",
[ModifierSpec('wireframe', 'WIREFRAME', {})]),
# Duplicate the object, test object and expected object have same world coordinates.
- MeshTest("Skin", "testObjPlaneSkin", "expObjPlaneSkin",
+ SpecMeshTest("Skin", "testObjPlaneSkin", "expObjPlaneSkin",
[ModifierSpec('skin', 'SKIN', {})]),
- MeshTest("MergedWeld", "testMergedWeld", "expectedMergedWeld",
+ SpecMeshTest("MergedWeld", "testMergedWeld", "expectedMergedWeld",
[ModifierSpec("weld", 'WELD', {"merge_threshold": 0.021})]),
- MeshTest("MergedAllWeld", "testMergedAllWeld", "expectedMergedAllWeld",
+ SpecMeshTest("MergedAllWeld", "testMergedAllWeld", "expectedMergedAllWeld",
[ModifierSpec("weld", 'WELD', {"merge_threshold": 1.8})]),
- MeshTest("MergedNoneWeld", "testMergedNoneWeld", "expectedMergedNoneWeld",
+ SpecMeshTest("MergedNoneWeld", "testMergedNoneWeld", "expectedMergedNoneWeld",
[ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]),
@@ -236,109 +236,109 @@ def main():
# One 'Deform' modifier on primitive meshes
#############################################
# 39
- MeshTest("MonkeyArmature", "testMonkeyArmature", "expectedMonkeyArmature",
+ SpecMeshTest("MonkeyArmature", "testMonkeyArmature", "expectedMonkeyArmature",
[ModifierSpec('armature', 'ARMATURE',
{'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]),
- MeshTest("TorusCast", "testTorusCast", "expectedTorusCast",
+ SpecMeshTest("TorusCast", "testTorusCast", "expectedTorusCast",
[ModifierSpec('cast', 'CAST', {'factor': 2.64})]),
- MeshTest("CubeCurve", "testCubeCurve", "expectedCubeCurve",
+ SpecMeshTest("CubeCurve", "testCubeCurve", "expectedCubeCurve",
[ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]),
- MeshTest("MonkeyDisplace", "testMonkeyDisplace", "expectedMonkeyDisplace",
+ SpecMeshTest("MonkeyDisplace", "testMonkeyDisplace", "expectedMonkeyDisplace",
[ModifierSpec('displace', "DISPLACE", {})]),
# Hook modifier requires moving the hook object to get a mesh change
# so can't test it with the current framework
- # MeshTest("MonkeyHook", "testMonkeyHook", "expectedMonkeyHook",
+ # SpecMeshTest("MonkeyHook", "testMonkeyHook", "expectedMonkeyHook",
# [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group':
# "HookVertexGroup"})]),
# 43
# ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh
- MeshTest("CubeLattice", "testCubeLattice", "expectedCubeLattice",
+ SpecMeshTest("CubeLattice", "testCubeLattice", "expectedCubeLattice",
[ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]),
- MeshTest("PlaneShrinkWrap", "testPlaneShrinkWrap", "expectedPlaneShrinkWrap",
+ SpecMeshTest("PlaneShrinkWrap", "testPlaneShrinkWrap", "expectedPlaneShrinkWrap",
[ModifierSpec('shrinkwrap', 'SHRINKWRAP',
{'target': bpy.data.objects["testCubeWrap"], 'offset': 0.5})]),
- MeshTest("CylinderSimpleDeform", "testCylinderSimpleDeform", "expectedCylinderSimpleDeform",
+ SpecMeshTest("CylinderSimpleDeform", "testCylinderSimpleDeform", "expectedCylinderSimpleDeform",
[ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(180), 'deform_axis': 'Z'})]),
- MeshTest("PlaneSmooth", "testPlaneSmooth", "expectedPlaneSmooth",
+ SpecMeshTest("PlaneSmooth", "testPlaneSmooth", "expectedPlaneSmooth",
[ModifierSpec('smooth', 'SMOOTH', {'iterations': 11})]),
# Smooth corrective requires a complex mesh.
- MeshTest("BalloonLaplacianSmooth", "testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth",
+ SpecMeshTest("BalloonLaplacianSmooth", "testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth",
[ModifierSpec('laplaciansmooth', 'LAPLACIANSMOOTH', {'lambda_factor': 12, 'lambda_border': 12})]),
# Gets updated often
- MeshTest("WavePlane", "testObjPlaneWave", "expObjPlaneWave",
+ SpecMeshTest("WavePlane", "testObjPlaneWave", "expObjPlaneWave",
[ModifierSpec('wave', 'WAVE', {})]),
#############################################
# CURVES Generate Modifiers
#############################################
# Caution: Make sure test object has no modifier in "added" state, the test may fail.
- MeshTest("BezCurveArray", "testObjBezierCurveArray", "expObjBezierCurveArray",
+ SpecMeshTest("BezCurveArray", "testObjBezierCurveArray", "expObjBezierCurveArray",
[ModifierSpec('array', 'ARRAY', {})]),
- MeshTest("CurveBevel", "testObjBezierCurveBevel", "expObjBezierCurveBevel",
+ SpecMeshTest("CurveBevel", "testObjBezierCurveBevel", "expObjBezierCurveBevel",
[ModifierSpec('bevel', 'BEVEL', {'limit_method': 'NONE'})]),
- MeshTest("CurveBuild", "testObjBezierCurveBuild", "expObjBezierCurveBuild",
+ SpecMeshTest("CurveBuild", "testObjBezierCurveBuild", "expObjBezierCurveBuild",
[ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]),
- MeshTest("CurveDecimate", "testObjBezierCurveDecimate", "expObjBezierCurveDecimate",
+ SpecMeshTest("CurveDecimate", "testObjBezierCurveDecimate", "expObjBezierCurveDecimate",
[ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]),
- MeshTest("CurveEdgeSplit", "testObjBezierCurveEdgeSplit", "expObjBezierCurveEdgeSplit",
+ SpecMeshTest("CurveEdgeSplit", "testObjBezierCurveEdgeSplit", "expObjBezierCurveEdgeSplit",
[ModifierSpec('edgeSplit', 'EDGE_SPLIT', {})]),
- MeshTest("CurveMirror", "testObjBezierCurveMirror", "expObjBezierCurveMirror",
+ SpecMeshTest("CurveMirror", "testObjBezierCurveMirror", "expObjBezierCurveMirror",
[ModifierSpec('mirror', 'MIRROR', {'use_axis': (True, True, False)})]),
- MeshTest("CurveScrew", "testObjBezierCurveScrew", "expObjBezierCurveScrew",
+ SpecMeshTest("CurveScrew", "testObjBezierCurveScrew", "expObjBezierCurveScrew",
[ModifierSpec('screw', 'SCREW', {})]),
- MeshTest("CurveSolidify", "testObjBezierCurveSolidify", "expObjBezierCurveSolidify",
+ SpecMeshTest("CurveSolidify", "testObjBezierCurveSolidify", "expObjBezierCurveSolidify",
[ModifierSpec('solidify', 'SOLIDIFY', {'thickness': 1})]),
- MeshTest("CurveSubSurf", "testObjBezierCurveSubSurf", "expObjBezierCurveSubSurf",
+ SpecMeshTest("CurveSubSurf", "testObjBezierCurveSubSurf", "expObjBezierCurveSubSurf",
[ModifierSpec('subSurf', 'SUBSURF', {})]),
- MeshTest("CurveTriangulate", "testObjBezierCurveTriangulate", "expObjBezierCurveTriangulate",
+ SpecMeshTest("CurveTriangulate", "testObjBezierCurveTriangulate", "expObjBezierCurveTriangulate",
[ModifierSpec('triangulate', 'TRIANGULATE', {})]),
# Test 60
# Caution Weld: if the distance is increased beyond a limit, the object disappears
- MeshTest("CurveWeld", "testObjBezierCurveWeld", "expObjBezierCurveWeld",
+ SpecMeshTest("CurveWeld", "testObjBezierCurveWeld", "expObjBezierCurveWeld",
[ModifierSpec('weld', 'WELD', {})]),
- MeshTest("CurveWeld2", "testObjBezierCurveWeld2", "expObjBezierCurveWeld2",
+ SpecMeshTest("CurveWeld2", "testObjBezierCurveWeld2", "expObjBezierCurveWeld2",
[ModifierSpec('weld', 'WELD', {})]),
#############################################
# Curves Deform Modifiers
#############################################
# Test 62
- MeshTest("CurveCast", "testObjBezierCurveCast", "expObjBezierCurveCast",
+ SpecMeshTest("CurveCast", "testObjBezierCurveCast", "expObjBezierCurveCast",
[ModifierSpec('Cast', 'CAST', {'cast_type': 'CYLINDER', 'factor': 10})]),
- MeshTest("CurveShrinkWrap", "testObjBezierCurveShrinkWrap", "expObjBezierCurveShrinkWrap",
+ SpecMeshTest("CurveShrinkWrap", "testObjBezierCurveShrinkWrap", "expObjBezierCurveShrinkWrap",
[ModifierSpec('ShrinkWrap', 'SHRINKWRAP',
{'target': bpy.data.objects['testShrinkWrapHelperSuzanne']})]),
- MeshTest("CurveSimpleDeform", "testObjBezierCurveSimpleDeform", "expObjBezierCurveSimpleDeform",
+ SpecMeshTest("CurveSimpleDeform", "testObjBezierCurveSimpleDeform", "expObjBezierCurveSimpleDeform",
[ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(90)})]),
- MeshTest("CurveSmooth", "testObjBezierCurveSmooth", "expObjBezierCurveSmooth",
+ SpecMeshTest("CurveSmooth", "testObjBezierCurveSmooth", "expObjBezierCurveSmooth",
[ModifierSpec('smooth', 'SMOOTH', {'factor': 10})]),
- MeshTest("CurveWave", "testObjBezierCurveWave", "expObjBezierCurveWave",
+ SpecMeshTest("CurveWave", "testObjBezierCurveWave", "expObjBezierCurveWave",
[ModifierSpec('curve_wave', 'WAVE', {'time_offset': -1.5})]),
- MeshTest("CurveCurve", "testObjBezierCurveCurve", "expObjBezierCurveCurve",
+ SpecMeshTest("CurveCurve", "testObjBezierCurveCurve", "expObjBezierCurveCurve",
[ModifierSpec('curve_Curve', 'CURVE', {'object': bpy.data.objects['NurbsCurve']})]),
]
diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py
index 6d921959e6f..4cf545d3292 100644
--- a/tests/python/modules/mesh_test.py
+++ b/tests/python/modules/mesh_test.py
@@ -33,13 +33,14 @@
# delete the duplicate object
#
# The words in angle brackets are parameters of the test, and are specified in
-# the main class MeshTest.
+# the abstract class MeshTest.
#
# If the environment variable BLENDER_TEST_UPDATE is set to 1, the <expected_object>
# is updated with the new test result.
# Tests are verbose when the environment variable BLENDER_VERBOSE is set.
+from abc import ABC, abstractmethod
import bpy
import functools
import inspect
@@ -161,121 +162,321 @@ class DeformModifierSpec:
return "Modifier: " + str(self.modifier_list) + " with object operator " + str(self.object_operator_spec)
-class MeshTest:
+class MeshTest(ABC):
"""
- A mesh testing class targeted at testing modifiers and operators on a single object.
- It holds a stack of mesh operations, i.e. modifiers or operators. The test is executed using
- the public method run_test().
+ A mesh testing Abstract class that hold common functionalities for testting operations.
"""
- def __init__(
- self,
- test_name: str,
- test_object_name: str,
- expected_object_name: str,
- operations_stack=None,
- apply_modifiers=False,
- do_compare=False,
- threshold=None
- ):
- """
- Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name
- or test_object_name don't exist.
- :param test_name: str - unique test name identifier.
+ def __init__(self, test_object_name, exp_object_name, test_name=None, threshold=None, do_compare=True):
+ """
:param test_object_name: str - Name of object of mesh type to run the operations on.
- :param expected_object_name: str - Name of object of mesh type that has the expected
+ :param exp_object_name: str - Name of object of mesh type that has the expected
geometry after running the operations.
- :param operations_stack: list - stack holding operations to perform on the test_object.
- :param apply_modifiers: bool - True if we want to apply the modifiers right after adding them to the object.
- - True if we want to apply the modifier to a list of modifiers, after some operation.
- This affects operations of type ModifierSpec and DeformModifierSpec.
+ :param test_name: str - Name of the test.
+ :param threshold: exponent: To allow variations and accept difference to a certain degree.
:param do_compare: bool - True if we want to compare the test and expected objects, False otherwise.
- :param threshold : exponent: To allow variations and accept difference to a certain degree.
-
"""
- if operations_stack is None:
- operations_stack = []
- for operation in operations_stack:
- if not (isinstance(operation, ModifierSpec) or isinstance(operation, OperatorSpecEditMode)
- or isinstance(operation, OperatorSpecObjectMode) or isinstance(operation, DeformModifierSpec)
- or isinstance(operation, ParticleSystemSpec)):
- raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}".
- format(type(ModifierSpec), type(OperatorSpecEditMode),
- type(DeformModifierSpec), type(ParticleSystemSpec),
- type(operation)))
- self.operations_stack = operations_stack
- self.apply_modifier = apply_modifiers
- self.do_compare = do_compare
+ self.test_object_name = test_object_name
+ self.exp_object_name = exp_object_name
+ if test_name:
+ self.test_name = test_name
+ else:
+ filepath = bpy.data.filepath
+ self.test_name = bpy.path.display_name_from_filepath(filepath)
self.threshold = threshold
- self.test_name = test_name
+ self.do_compare = do_compare
+ self.update = os.getenv("BLENDER_TEST_UPDATE") is not None
+ self.verbose = os.getenv("BLENDER_VERBOSE") is not None
+ self.test_updated_counter = 0
+ objects = bpy.data.objects
+ self.evaluated_object = None
+ self.test_object = objects[self.test_object_name]
- self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
- self.update = os.getenv('BLENDER_TEST_UPDATE') is not None
+ if self.update:
+ if exp_object_name in objects:
+ self.expected_object = objects[self.exp_object_name]
+ else:
+ self.create_expected_object()
+ else:
+ self.expected_object = objects[self.exp_object_name]
- # Initialize test objects.
- objects = bpy.data.objects
- self.test_object = objects[test_object_name]
- self.expected_object = objects[expected_object_name]
+ def create_expected_object(self):
+ """
+ Creates an expected object 10 units away
+ in Y direction from test object.
+ """
if self.verbose:
- print("Found test object {}".format(test_object_name))
- print("Found test object {}".format(expected_object_name))
+ print("Creating expected object...")
+ self.create_evaluated_object()
+ self.expected_object = self.evaluated_object
+ self.expected_object.name = self.exp_object_name
+ x, y, z = self.test_object.location
+ self.expected_object.location = (x, y+10, z)
+ bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath)
- # Private flag to indicate whether the blend file was updated after the test.
- self._test_updated = False
+ def create_evaluated_object(self):
+ """
+ Creates an evaluated object.
+ """
+ bpy.context.view_layer.objects.active = self.test_object
- def set_test_object(self, test_object_name):
+ # Duplicate test object.
+ bpy.ops.object.mode_set(mode="OBJECT")
+ bpy.ops.object.select_all(action="DESELECT")
+ bpy.context.view_layer.objects.active = self.test_object
+
+ self.test_object.select_set(True)
+ bpy.ops.object.duplicate()
+ self.evaluated_object = bpy.context.active_object
+ self.evaluated_object.name = "evaluated_object"
+
+ @staticmethod
+ def _print_result(result):
"""
- Set test object for the test. Raises a KeyError if object with given name does not exist.
- :param test_object_name: name of test object to run operations on.
+ Prints the comparison, selection and validation result.
"""
- objects = bpy.data.objects
- self.test_object = objects[test_object_name]
+ print("Results:")
+ for key in result:
+ print("{} : {}".format(key, result[key][1]))
+ print()
- def set_expected_object(self, expected_object_name):
+ def run_test(self):
"""
- Set expected object for the test. Raises a KeyError if object with given name does not exist
- :param expected_object_name: Name of expected object.
+ Runs a single test, runs it again if test file is updated.
"""
- objects = bpy.data.objects
- self.expected_object = objects[expected_object_name]
- def _on_failed_test(self, compare_result, validation_success, evaluated_test_object):
- if self.update and validation_success:
- if self.verbose:
- print("Test failed expectantly. Updating expected mesh...")
+ self.create_evaluated_object()
+ self.apply_operations(self.evaluated_object.name)
- # Replace expected object with object we ran operations on, i.e. evaluated_test_object.
- evaluated_test_object.location = self.expected_object.location
- expected_object_name = self.expected_object.name
- evaluated_selection = {v.index for v in evaluated_test_object.data.vertices if v.select}
+ if not self.do_compare:
+ print("\nVisualization purpose only: Open Blender in GUI mode")
+ print("Compare evaluated and expected object in Blender.\n")
+ return False
+
+ result = self.compare_meshes(self.evaluated_object, self.expected_object, self.threshold)
- bpy.data.objects.remove(self.expected_object, do_unlink=True)
- evaluated_test_object.name = expected_object_name
- self._do_selection(evaluated_test_object.data, "VERT", evaluated_selection)
+ # Initializing with True to get correct resultant of result_code booleans.
+ success = True
+ inside_loop_flag = False
+ for key in result:
+ inside_loop_flag = True
+ success = success and result[key][0]
- # Save file.
- bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath)
+ # Check "success" is actually evaluated and is not the default True value.
+ if not inside_loop_flag:
+ success = False
- self._test_updated = True
- # Set new expected object.
- self.expected_object = evaluated_test_object
+ if success:
+ self.print_passed_test_result(result)
+ # Clean up.
+ if self.verbose:
+ print("Cleaning up...")
+ # Delete evaluated_test_object.
+ bpy.ops.object.delete()
return True
- else:
- print("Test comparison result: {}".format(compare_result))
- print("Test validation result: {}".format(validation_success))
- print("Resulting object mesh '{}' did not match expected object '{}' from file {}".
- format(evaluated_test_object.name, self.expected_object.name, bpy.data.filepath))
+ elif self.update:
+ self.print_failed_test_result(result)
+ self.update_failed_test()
+ # Check for testing the blend file is updated and re-running.
+ # Also safety check to avoid infinite recursion loop.
+ if self.test_updated_counter == 1:
+ print("Re-running test...")
+ self.run_test()
+ else:
+ print("The test fails consistently. Exiting...")
+ return False
+ else:
+ self.print_failed_test_result(result)
return False
- def is_test_updated(self):
+ def print_failed_test_result(self, result):
+ """
+ Print results for failed test.
+ """
+ print("\nFAILED {} test with the following: ".format(self.test_name))
+ self._print_result(result)
+
+ def print_passed_test_result(self, result):
+ """
+ Print results for passing test.
+ """
+ print("\nPASSED {} test successfully.".format(self.test_name))
+ self._print_result(result)
+
+ def do_selection(self, mesh: bpy.types.Mesh, select_mode: str, selection: set):
+ """
+ Do selection on a mesh.
+ :param mesh: bpy.types.Mesh - input mesh
+ :param: select_mode: str - selection mode. Must be 'VERT', 'EDGE' or 'FACE'
+ :param: selection: set - indices of selection.
+
+ Example: select_mode='VERT' and selection={1,2,3} selects veritces 1, 2 and 3 of input mesh
"""
- Check whether running the test with BLENDER_TEST_UPDATE actually modified the .blend test file.
- :return: Bool - True if blend file has been updated. False otherwise.
+ # Deselect all objects.
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.mesh.select_all(action='DESELECT')
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ bpy.context.tool_settings.mesh_select_mode = (select_mode == 'VERT',
+ select_mode == 'EDGE',
+ select_mode == 'FACE')
+
+ items = (mesh.vertices if select_mode == 'VERT'
+ else mesh.edges if select_mode == 'EDGE'
+ else mesh.polygons if select_mode == 'FACE'
+ else None)
+ if items is None:
+ raise ValueError("Invalid selection mode")
+ for index in selection:
+ items[index].select = True
+
+ def update_failed_test(self):
+ """
+ Updates expected object.
"""
- return self._test_updated
+ self.evaluated_object.location = self.expected_object.location
+ expected_object_name = self.expected_object.name
+ evaluated_selection = {
+ v.index for v in self.evaluated_object.data.vertices if v.select}
+
+ bpy.data.objects.remove(self.expected_object, do_unlink=True)
+ self.evaluated_object.name = expected_object_name
+ self.do_selection(self.evaluated_object.data,
+ "VERT", evaluated_selection)
+
+ # Save file.
+ bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath)
+ self.test_updated_counter += 1
+ self.expected_object = self.evaluated_object
+
+ @staticmethod
+ def compare_meshes(evaluated_object, expected_object, threshold):
+ """
+ Compares evaluated object mesh with expected object mesh.
+ :param evaluated_object: first object for comparison.
+ :param expected_object: second object for comparison.
+ :param threshold: exponent: To allow variations and accept difference to a certain degree.
+ :return: dict: Contains results of different comparisons.
+ """
+ objects = bpy.data.objects
+ evaluated_test_mesh = objects[evaluated_object.name].data
+ expected_mesh = expected_object.data
+ result_codes = {}
+
+ # Mesh Comparison.
+ if threshold:
+ result_mesh = expected_mesh.unit_test_compare(
+ mesh=evaluated_test_mesh, threshold=threshold)
+ else:
+ result_mesh = expected_mesh.unit_test_compare(
+ mesh=evaluated_test_mesh)
+
+ if result_mesh == "Same":
+ result_codes['Mesh Comparison'] = (True, result_mesh)
+ else:
+ result_codes['Mesh Comparison'] = (False, result_mesh)
+
+ # Selection comparison.
+
+ selected_evaluated_verts = [
+ v.index for v in evaluated_test_mesh.vertices if v.select]
+ selected_expected_verts = [
+ v.index for v in expected_mesh.vertices if v.select]
+
+ if selected_evaluated_verts == selected_expected_verts:
+ result_selection = "Same"
+ result_codes['Selection Comparison'] = (True, result_selection)
+ else:
+ result_selection = "Selection doesn't match."
+ result_codes['Selection Comparison'] = (False, result_selection)
+
+ # Validation check.
+ result_validation = evaluated_test_mesh.validate(verbose=True)
+ if result_validation:
+ result_validation = "Invalid Mesh"
+ result_codes['Mesh Validation'] = (False, result_validation)
+ else:
+ result_validation = "Valid"
+ result_codes['Mesh Validation'] = (True, result_validation)
+
+ return result_codes
+
+ @abstractmethod
+ def apply_operations(self, object_name):
+ """
+ Apply operations on this object.
+
+ object_name (str): Name of the test object on which operations will be applied.
+ """
+ pass
+
+
+class SpecMeshTest(MeshTest):
+ """
+ A mesh testing class inherited from MeshTest class targeted at testing modifiers and operators on a single object.
+ It holds a stack of mesh operations, i.e. modifiers or operators. The test is executed using MeshTest's run_test.
+ """
+
+ def __init__(self, test_name,
+ test_object_name,
+ exp_object_name,
+ operations_stack=None,
+ apply_modifier=True,
+ threshold=None):
+ """
+ Constructor for SpecMeshTest.
+
+ :param test_name: str - Name of the test.
+ :param test_object_name: str - Name of object of mesh type to run the operations on.
+ :param exp_object_name: str - Name of object of mesh type that has the expected
+ geometry after running the operations.
+ :param operations_stack: list - stack holding operations to perform on the test_object.
+ :param apply_modifier: bool - True if we want to apply the modifiers right after adding them to the object.
+ - True if we want to apply the modifier to list of modifiers, after some operation.
+ This affects operations of type ModifierSpec and DeformModifierSpec.
+ """
+
+ super().__init__(test_object_name, exp_object_name, test_name, threshold)
+ self.test_name = test_name
+ if operations_stack is None:
+ self.operations_stack = []
+ else:
+ self.operations_stack = operations_stack
+ self.apply_modifier = apply_modifier
+
+ def apply_operations(self, evaluated_test_object_name):
+ # Add modifiers and operators.
+ SpecMeshTest.apply_operations.__doc__ = MeshTest.apply_operations.__doc__
+ evaluated_test_object = bpy.data.objects[evaluated_test_object_name]
+ if self.verbose:
+ print("Applying operations...")
+ for operation in self.operations_stack:
+ if isinstance(operation, ModifierSpec):
+ self._add_modifier(evaluated_test_object, operation)
+ if self.apply_modifier:
+ self._apply_modifier(
+ evaluated_test_object, operation.modifier_name)
+
+ elif isinstance(operation, OperatorSpecEditMode):
+ self._apply_operator_edit_mode(
+ evaluated_test_object, operation)
+
+ elif isinstance(operation, OperatorSpecObjectMode):
+ self._apply_operator_object_mode(operation)
+
+ elif isinstance(operation, DeformModifierSpec):
+ self._apply_deform_modifier(evaluated_test_object, operation)
+
+ elif isinstance(operation, ParticleSystemSpec):
+ self._apply_particle_system(evaluated_test_object, operation)
+
+ else:
+ raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}".
+ format(type(ModifierSpec), type(OperatorSpecEditMode),
+ type(OperatorSpecObjectMode), type(ParticleSystemSpec), type(operation)))
def _set_parameters_impl(self, modifier, modifier_parameters, nested_settings_path, modifier_name):
"""
@@ -312,14 +513,15 @@ class MeshTest:
for key in modifier_parameters:
nested_settings_path.append(key)
- self._set_parameters_impl(modifier, modifier_parameters[key], nested_settings_path, modifier_name)
+ self._set_parameters_impl(
+ modifier, modifier_parameters[key], nested_settings_path, modifier_name)
if nested_settings_path:
nested_settings_path.pop()
def set_parameters(self, modifier, modifier_parameters):
"""
- Wrapper for _set_parameters_util
+ Wrapper for _set_parameters_impl.
"""
settings = []
modifier_name = modifier.name
@@ -430,40 +632,14 @@ class MeshTest:
if self.apply_modifier:
self._apply_modifier(test_object, particle_sys_spec.modifier_name)
- def _do_selection(self, mesh: bpy.types.Mesh, select_mode: str, selection: set):
- """
- Do selection on a mesh
- :param mesh: bpy.types.Mesh - input mesh
- :param: select_mode: str - selection mode. Must be 'VERT', 'EDGE' or 'FACE'
- :param: selection: set - indices of selection.
-
- Example: select_mode='VERT' and selection={1,2,3} selects veritces 1, 2 and 3 of input mesh
- """
- # deselect all
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_all(action='DESELECT')
- bpy.ops.object.mode_set(mode='OBJECT')
-
- bpy.context.tool_settings.mesh_select_mode = (select_mode == 'VERT',
- select_mode == 'EDGE',
- select_mode == 'FACE')
-
- items = (mesh.vertices if select_mode == 'VERT'
- else mesh.edges if select_mode == 'EDGE'
- else mesh.polygons if select_mode == 'FACE'
- else None)
- if items is None:
- raise ValueError("Invalid selection mode")
- for index in selection:
- items[index].select = True
-
def _apply_operator_edit_mode(self, test_object, operator: OperatorSpecEditMode):
"""
Apply operator on test object.
:param test_object: bpy.types.Object - Blender object to apply operator on.
:param operator: OperatorSpecEditMode - OperatorSpecEditMode object with parameters.
"""
- self._do_selection(test_object.data, operator.select_mode, operator.selection)
+ self.do_selection(
+ test_object.data, operator.select_mode, operator.selection)
# Apply operator in edit mode.
bpy.ops.object.mode_set(mode='EDIT')
@@ -528,96 +704,27 @@ class MeshTest:
for mod_name in modifier_names:
self._apply_modifier(test_object, mod_name)
- def run_test(self):
- """
- Apply operations in self.operations_stack on self.test_object and compare the
- resulting mesh with self.expected_object.data
- :return: bool - True if the test passed, False otherwise.
- """
- self._test_updated = False
- bpy.context.view_layer.objects.active = self.test_object
-
- # Duplicate test object.
- bpy.ops.object.mode_set(mode="OBJECT")
- bpy.ops.object.select_all(action="DESELECT")
- bpy.context.view_layer.objects.active = self.test_object
-
- self.test_object.select_set(True)
- bpy.ops.object.duplicate()
- evaluated_test_object = bpy.context.active_object
- evaluated_test_object.name = "evaluated_object"
- if self.verbose:
- print()
- print(evaluated_test_object.name, "is set to active")
-
- # Add modifiers and operators.
- for operation in self.operations_stack:
- if isinstance(operation, ModifierSpec):
- self._add_modifier(evaluated_test_object, operation)
- if self.apply_modifier:
- self._apply_modifier(evaluated_test_object, operation.modifier_name)
-
- elif isinstance(operation, OperatorSpecEditMode):
- self._apply_operator_edit_mode(evaluated_test_object, operation)
-
- elif isinstance(operation, OperatorSpecObjectMode):
- self._apply_operator_object_mode(operation)
-
- elif isinstance(operation, DeformModifierSpec):
- self._apply_deform_modifier(evaluated_test_object, operation)
-
- elif isinstance(operation, ParticleSystemSpec):
- self._apply_particle_system(evaluated_test_object, operation)
-
- else:
- raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}".
- format(type(ModifierSpec), type(OperatorSpecEditMode),
- type(OperatorSpecObjectMode), type(ParticleSystemSpec), type(operation)))
-
- # Compare resulting mesh with expected one.
- # Compare only when self.do_compare is set to True, it is set to False for run-test and returns.
- if not self.do_compare:
- print("Meshes/objects are not compared, compare evaluated and expected object in Blender for "
- "visualization only.")
- return False
-
- if self.verbose:
- print("Comparing expected mesh with resulting mesh...")
- evaluated_test_mesh = evaluated_test_object.data
- expected_mesh = self.expected_object.data
- if self.threshold:
- compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh, threshold=self.threshold)
- else:
- compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh)
- compare_success = (compare_result == 'Same')
-
- selected_evaluatated_verts = [v.index for v in evaluated_test_mesh.vertices if v.select]
- selected_expected_verts = [v.index for v in expected_mesh.vertices if v.select]
- if selected_evaluatated_verts != selected_expected_verts:
- compare_result = "Selection doesn't match"
- compare_success = False
- # Also check if invalid geometry (which is never expected) had to be corrected...
- validation_success = not evaluated_test_mesh.validate(verbose=True)
+class BlendFileTest(MeshTest):
+ """
+ A mesh testing class inherited from MeshTest aimed at testing operations like modifiers loaded directly from
+ blend file i.e. without adding them from scratch or without adding specifications.
+ """
- if compare_success and validation_success:
- if self.verbose:
- print("Success!")
+ def apply_operations(self, evaluated_test_object_name):
- # Clean up.
- if self.verbose:
- print("Cleaning up...")
- # Delete evaluated_test_object.
- bpy.ops.object.delete()
- return True
-
- else:
- return self._on_failed_test(compare_result, validation_success, evaluated_test_object)
+ BlendFileTest.apply_operations.__doc__ = MeshTest.apply_operations.__doc__
+ evaluated_test_object = bpy.data.objects[evaluated_test_object_name]
+ modifiers_list = evaluated_test_object.modifiers
+ if not modifiers_list:
+ raise Exception("No modifiers are added to test object.")
+ for modifier in modifiers_list:
+ bpy.ops.object.modifier_apply(modifier=modifier.name)
class RunTest:
"""
- Helper class that stores and executes modifier tests.
+ Helper class that stores and executes SpecMeshTest tests.
Example usage:
@@ -629,9 +736,9 @@ class RunTest:
>>> OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE),
>>> ]
>>> tests = [
- >>> MeshTest("Test1", "testCube", "expectedCube", modifier_list),
- >>> MeshTest("Test2", "testCube_2", "expectedCube_2", modifier_list),
- >>> MeshTest("MonkeyDeleteEdge", "testMonkey","expectedMonkey", operator_list)
+ >>> SpecMeshTest("Test1", "testCube", "expectedCube", modifier_list),
+ >>> SpecMeshTest("Test2", "testCube_2", "expectedCube_2", modifier_list),
+ >>> SpecMeshTest("MonkeyDeleteEdge", "testMonkey","expectedMonkey", operator_list)
>>> ]
>>> modifiers_test = RunTest(tests)
>>> modifiers_test.run_all_tests()
@@ -639,7 +746,7 @@ class RunTest:
def __init__(self, tests, apply_modifiers=False, do_compare=False):
"""
- Construct a modifier test.
+ Construct a test suite.
:param tests: list - list of modifier or operator test cases. Each element in the list must contain the
following
in the correct order:
@@ -674,7 +781,7 @@ class RunTest:
def run_all_tests(self):
"""
- Run all tests in self.tests list. Raises an exception if one the tests fails.
+ Run all tests in self.tests list. Displays all failed tests at bottom.
"""
for test_number, each_test in enumerate(self.tests):
test_name = each_test.test_name
@@ -717,15 +824,8 @@ class RunTest:
raise Exception('No test called {} found!'.format(test_name))
test = case
- if self.apply_modifiers:
- test.apply_modifier = True
-
- if self.do_compare:
- test.do_compare = True
+ test.apply_modifier = self.apply_modifiers
+ test.do_compare = self.do_compare
success = test.run_test()
- if test.is_test_updated():
- # Run the test again if the blend file has been updated.
- success = test.run_test()
-
return success
diff --git a/tests/python/operators.py b/tests/python/operators.py
index 4501df82175..9e5ac0054e8 100644
--- a/tests/python/operators.py
+++ b/tests/python/operators.py
@@ -26,7 +26,7 @@ from random import shuffle, seed
seed(0)
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest
+from modules.mesh_test import SpecMeshTest, OperatorSpecEditMode, RunTest
# Central vertical loop of Suzanne
MONKEY_LOOP_VERT = {68, 69, 71, 73, 74, 75, 76, 77, 90, 129, 136, 175, 188, 189, 198, 207,
@@ -38,242 +38,242 @@ MONKEY_LOOP_EDGE = {131, 278, 299, 305, 307, 334, 337, 359, 384, 396, 399, 412,
def main():
tests = [
# bisect
- MeshTest("CubeBisect", "testCubeBisect", "expectedCubeBisect",
+ SpecMeshTest("CubeBisect", "testCubeBisect", "expectedCubeBisect",
[OperatorSpecEditMode("bisect",
{"plane_co": (0, 0, 0), "plane_no": (0, 1, 1), "clear_inner": True,
"use_fill": True}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
# blend from shape
- MeshTest("CubeBlendFromShape", "testCubeBlendFromShape", "expectedCubeBlendFromShape",
+ SpecMeshTest("CubeBlendFromShape", "testCubeBlendFromShape", "expectedCubeBlendFromShape",
[OperatorSpecEditMode("blend_from_shape", {"shape": "Key 1"}, 'FACE', {0, 1, 2, 3, 4, 5})]),
# bridge edge loops
- MeshTest("CubeBridgeEdgeLoop", "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop",
+ SpecMeshTest("CubeBridgeEdgeLoop", "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop",
[OperatorSpecEditMode("bridge_edge_loops", {}, "FACE", {0, 1})]),
# decimate
- MeshTest("MonkeyDecimate", "testMonkeyDecimate", "expectedMonkeyDecimate",
+ SpecMeshTest("MonkeyDecimate", "testMonkeyDecimate", "expectedMonkeyDecimate",
[OperatorSpecEditMode("decimate",
{"ratio": 0.1}, "FACE", {i for i in range(500)})]),
# delete
- MeshTest("CubeDeleteVertices", "testCubeDeleteVertices", "expectedCubeDeleteVertices",
+ SpecMeshTest("CubeDeleteVertices", "testCubeDeleteVertices", "expectedCubeDeleteVertices",
[OperatorSpecEditMode("delete", {}, "VERT", {3})]),
- MeshTest("CubeDeleteFaces", "testCubeDeleteFaces", "expectedCubeDeleteFaces",
+ SpecMeshTest("CubeDeleteFaces", "testCubeDeleteFaces", "expectedCubeDeleteFaces",
[OperatorSpecEditMode("delete", {}, "FACE", {0})]),
- MeshTest("CubeDeleteEdges", "testCubeDeleteEdges", "expectedCubeDeleteEdges",
+ SpecMeshTest("CubeDeleteEdges", "testCubeDeleteEdges", "expectedCubeDeleteEdges",
[OperatorSpecEditMode("delete", {}, "EDGE", {0, 1, 2, 3})]),
# delete edge loop
- MeshTest("MonkeyDeleteEdgeLoopVertices", "testMokneyDeleteEdgeLoopVertices",
+ SpecMeshTest("MonkeyDeleteEdgeLoopVertices", "testMokneyDeleteEdgeLoopVertices",
"expectedMonkeyDeleteEdgeLoopVertices",
[OperatorSpecEditMode("delete_edgeloop", {}, "VERT", MONKEY_LOOP_VERT)]),
- MeshTest("MonkeyDeleteEdgeLoopEdges", "testMokneyDeleteEdgeLoopEdges",
+ SpecMeshTest("MonkeyDeleteEdgeLoopEdges", "testMokneyDeleteEdgeLoopEdges",
"expectedMonkeyDeleteEdgeLoopEdges",
[OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE)]),
# delete loose
- MeshTest("CubeDeleteLooseVertices", "testCubeDeleteLooseVertices",
+ SpecMeshTest("CubeDeleteLooseVertices", "testCubeDeleteLooseVertices",
"expectedCubeDeleteLooseVertices",
[OperatorSpecEditMode("delete_loose", {"use_verts": True, "use_edges": False, "use_faces": False},
"VERT",
{i for i in range(12)})]),
- MeshTest("CubeDeleteLooseEdges", "testCubeDeleteLooseEdges",
+ SpecMeshTest("CubeDeleteLooseEdges", "testCubeDeleteLooseEdges",
"expectedCubeDeleteLooseEdges",
[OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False},
"EDGE",
{i for i in range(14)})]),
- MeshTest("CubeDeleteLooseFaces", "testCubeDeleteLooseFaces",
+ SpecMeshTest("CubeDeleteLooseFaces", "testCubeDeleteLooseFaces",
"expectedCubeDeleteLooseFaces",
[OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True},
"FACE",
{i for i in range(7)})]),
# dissolve degenerate
- MeshTest("CubeDissolveDegenerate", "testCubeDissolveDegenerate",
+ SpecMeshTest("CubeDissolveDegenerate", "testCubeDissolveDegenerate",
"expectedCubeDissolveDegenerate",
[OperatorSpecEditMode("dissolve_degenerate", {}, "VERT", {i for i in range(8)})]),
# dissolve edges
- MeshTest("CylinderDissolveEdges", "testCylinderDissolveEdges", "expectedCylinderDissolveEdges",
+ SpecMeshTest("CylinderDissolveEdges", "testCylinderDissolveEdges", "expectedCylinderDissolveEdges",
[OperatorSpecEditMode("dissolve_edges", {}, "EDGE", {0, 5, 6, 9})]),
# dissolve faces
- MeshTest("CubeDissolveFaces", "testCubeDissolveFaces", "expectedCubeDissolveFaces",
+ SpecMeshTest("CubeDissolveFaces", "testCubeDissolveFaces", "expectedCubeDissolveFaces",
[OperatorSpecEditMode("dissolve_faces", {}, "VERT", {5, 34, 47, 49, 83, 91, 95})]),
# dissolve verts
- MeshTest("CubeDissolveVerts", "testCubeDissolveVerts", "expectedCubeDissolveVerts",
+ SpecMeshTest("CubeDissolveVerts", "testCubeDissolveVerts", "expectedCubeDissolveVerts",
[OperatorSpecEditMode("dissolve_verts", {}, "VERT", {16, 20, 22, 23, 25})]),
# duplicate
- MeshTest("ConeDuplicateVertices", "testConeDuplicateVertices",
+ SpecMeshTest("ConeDuplicateVertices", "testConeDuplicateVertices",
"expectedConeDuplicateVertices",
[OperatorSpecEditMode("duplicate", {}, "VERT", {i for i in range(33)} - {23})]),
- MeshTest("ConeDuplicateOneVertex", "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex",
+ SpecMeshTest("ConeDuplicateOneVertex", "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex",
[OperatorSpecEditMode("duplicate", {}, "VERT", {23})]),
- MeshTest("ConeDuplicateFaces", "testConeDuplicateFaces", "expectedConeDuplicateFaces",
+ SpecMeshTest("ConeDuplicateFaces", "testConeDuplicateFaces", "expectedConeDuplicateFaces",
[OperatorSpecEditMode("duplicate", {}, "FACE", {6, 9})]),
- MeshTest("ConeDuplicateEdges", "testConeDuplicateEdges", "expectedConeDuplicateEdges",
+ SpecMeshTest("ConeDuplicateEdges", "testConeDuplicateEdges", "expectedConeDuplicateEdges",
[OperatorSpecEditMode("duplicate", {}, "EDGE", {i for i in range(64)})]),
# edge collapse
- MeshTest("CylinderEdgeCollapse", "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse",
+ SpecMeshTest("CylinderEdgeCollapse", "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse",
[OperatorSpecEditMode("edge_collapse", {}, "EDGE", {1, 9, 4})]),
# edge face add
- MeshTest("CubeEdgeFaceAddFace", "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace",
+ SpecMeshTest("CubeEdgeFaceAddFace", "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace",
[OperatorSpecEditMode("edge_face_add", {}, "VERT", {1, 3, 4, 5, 7})]),
- MeshTest("CubeEdgeFaceAddEdge", "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge",
+ SpecMeshTest("CubeEdgeFaceAddEdge", "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge",
[OperatorSpecEditMode("edge_face_add", {}, "VERT", {4, 5})]),
# edge rotate
- MeshTest("CubeEdgeRotate", "testCubeEdgeRotate", "expectedCubeEdgeRotate",
+ SpecMeshTest("CubeEdgeRotate", "testCubeEdgeRotate", "expectedCubeEdgeRotate",
[OperatorSpecEditMode("edge_rotate", {}, "EDGE", {1})]),
# edge split
- MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
+ SpecMeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
[OperatorSpecEditMode("edge_split", {}, "EDGE", {2, 5, 8, 11, 14, 17, 20, 23})]),
# edge ring select - Cannot be tested. Need user input.
- # MeshTest("CubeEdgeRingSelect", "testCubeEdgeRingSelect", "expectedCubeEdgeRingSelect",
+ # SpecMeshTest("CubeEdgeRingSelect", "testCubeEdgeRingSelect", "expectedCubeEdgeRingSelect",
# [OperatorSpecEditMode("edgering_select", {}, "EDGE", {5, 20, 25, 26})]),
- # MeshTest("EmptyMeshEdgeRingSelect", "testGridEdgeRingSelect", "expectedGridEdgeRingSelect",
+ # SpecMeshTest("EmptyMeshEdgeRingSelect", "testGridEdgeRingSelect", "expectedGridEdgeRingSelect",
# [OperatorSpecEditMode("edgering_select", {}, "VERT", {65, 66, 67})]),
- # MeshTest("EmptyMeshEdgeRingSelect", "testEmptyMeshdgeRingSelect", "expectedEmptyMeshEdgeRingSelect",
+ # SpecMeshTest("EmptyMeshEdgeRingSelect", "testEmptyMeshdgeRingSelect", "expectedEmptyMeshEdgeRingSelect",
# [OperatorSpecEditMode("edgering_select", {}, "VERT", {})]),
# edges select sharp
- MeshTest("CubeEdgesSelectSharp", "testCubeEdgeSelectSharp", "expectedCubeEdgeSelectSharp",
+ SpecMeshTest("CubeEdgesSelectSharp", "testCubeEdgeSelectSharp", "expectedCubeEdgeSelectSharp",
[OperatorSpecEditMode("edges_select_sharp", {}, "EDGE", {20})]),
- MeshTest("SphereEdgesSelectSharp", "testSphereEdgesSelectSharp", "expectedSphereEdgeSelectSharp",
+ SpecMeshTest("SphereEdgesSelectSharp", "testSphereEdgesSelectSharp", "expectedSphereEdgeSelectSharp",
[OperatorSpecEditMode("edges_select_sharp", {"sharpness": 0.25}, "EDGE", {288})]),
- MeshTest("HoledSphereEdgesSelectSharp", "testHoledSphereEdgesSelectSharp", "expectedHoledSphereEdgeSelectSharp",
+ SpecMeshTest("HoledSphereEdgesSelectSharp", "testHoledSphereEdgesSelectSharp", "expectedHoledSphereEdgeSelectSharp",
[OperatorSpecEditMode("edges_select_sharp", {"sharpness": 0.18}, "VERT", {})]),
- MeshTest("EmptyMeshEdgesSelectSharp", "testEmptyMeshEdgeSelectSharp", "expectedEmptyMeshEdgeSelectSharp",
+ SpecMeshTest("EmptyMeshEdgesSelectSharp", "testEmptyMeshEdgeSelectSharp", "expectedEmptyMeshEdgeSelectSharp",
[OperatorSpecEditMode("edges_select_sharp", {}, "VERT", {})]),
# face make planar
- MeshTest("MonkeyFaceMakePlanar", "testMonkeyFaceMakePlanar",
+ SpecMeshTest("MonkeyFaceMakePlanar", "testMonkeyFaceMakePlanar",
"expectedMonkeyFaceMakePlanar",
[OperatorSpecEditMode("face_make_planar", {}, "FACE", {i for i in range(500)})]),
# face split by edges
- MeshTest("PlaneFaceSplitByEdges", "testPlaneFaceSplitByEdges",
+ SpecMeshTest("PlaneFaceSplitByEdges", "testPlaneFaceSplitByEdges",
"expectedPlaneFaceSplitByEdges",
[OperatorSpecEditMode("face_split_by_edges", {}, "VERT", {i for i in range(6)})]),
# faces select linked flat
- MeshTest("CubeFacesSelectLinkedFlat", "testCubeFaceSelectLinkedFlat", "expectedCubeFaceSelectLinkedFlat",
+ SpecMeshTest("CubeFacesSelectLinkedFlat", "testCubeFaceSelectLinkedFlat", "expectedCubeFaceSelectLinkedFlat",
[OperatorSpecEditMode("faces_select_linked_flat", {}, "FACE", {7})]),
- MeshTest("PlaneFacesSelectLinkedFlat", "testPlaneFaceSelectLinkedFlat", "expectedPlaneFaceSelectLinkedFlat",
+ SpecMeshTest("PlaneFacesSelectLinkedFlat", "testPlaneFaceSelectLinkedFlat", "expectedPlaneFaceSelectLinkedFlat",
[OperatorSpecEditMode("faces_select_linked_flat", {}, "VERT", {1})]),
- MeshTest("EmptyMeshFacesSelectLinkedFlat", "testEmptyMeshFaceSelectLinkedFlat",
+ SpecMeshTest("EmptyMeshFacesSelectLinkedFlat", "testEmptyMeshFaceSelectLinkedFlat",
"expectedEmptyMeshFaceSelectLinkedFlat",
[OperatorSpecEditMode("faces_select_linked_flat", {}, "VERT", {})]),
# fill
- MeshTest("IcosphereFill", "testIcosphereFill", "expectedIcosphereFill",
+ SpecMeshTest("IcosphereFill", "testIcosphereFill", "expectedIcosphereFill",
[OperatorSpecEditMode("fill", {}, "EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]),
- MeshTest("IcosphereFillUseBeautyFalse",
+ SpecMeshTest("IcosphereFillUseBeautyFalse",
"testIcosphereFillUseBeautyFalse", "expectedIcosphereFillUseBeautyFalse",
[OperatorSpecEditMode("fill", {"use_beauty": False}, "EDGE",
{20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]),
# fill grid
- MeshTest("PlaneFillGrid", "testPlaneFillGrid",
+ SpecMeshTest("PlaneFillGrid", "testPlaneFillGrid",
"expectedPlaneFillGrid",
[OperatorSpecEditMode("fill_grid", {}, "EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]),
- MeshTest("PlaneFillGridSimpleBlending",
+ SpecMeshTest("PlaneFillGridSimpleBlending",
"testPlaneFillGridSimpleBlending",
"expectedPlaneFillGridSimpleBlending",
[OperatorSpecEditMode("fill_grid", {"use_interp_simple": True}, "EDGE",
{1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]),
# fill holes
- MeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles",
+ SpecMeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles",
[OperatorSpecEditMode("fill_holes", {"sides": 9}, "VERT", {i for i in range(481)})]),
# face shade smooth (not a real test)
- MeshTest("CubeShadeSmooth", "testCubeShadeSmooth", "expectedCubeShadeSmooth",
+ SpecMeshTest("CubeShadeSmooth", "testCubeShadeSmooth", "expectedCubeShadeSmooth",
[OperatorSpecEditMode("faces_shade_smooth", {}, "VERT", {i for i in range(8)})]),
# faces shade flat (not a real test)
- MeshTest("CubeShadeFlat", "testCubeShadeFlat", "expectedCubeShadeFlat",
+ SpecMeshTest("CubeShadeFlat", "testCubeShadeFlat", "expectedCubeShadeFlat",
[OperatorSpecEditMode("faces_shade_flat", {}, "FACE", {i for i in range(6)})]),
# inset faces
- MeshTest("CubeInset",
+ SpecMeshTest("CubeInset",
"testCubeInset", "expectedCubeInset", [OperatorSpecEditMode("inset", {"thickness": 0.2}, "VERT",
{5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50,
52,
59, 61, 62, 65, 83, 91, 95})]),
- MeshTest("CubeInsetEvenOffsetFalse",
+ SpecMeshTest("CubeInsetEvenOffsetFalse",
"testCubeInsetEvenOffsetFalse", "expectedCubeInsetEvenOffsetFalse",
[OperatorSpecEditMode("inset", {"thickness": 0.2, "use_even_offset": False}, "VERT",
{5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95})]),
- MeshTest("CubeInsetDepth",
+ SpecMeshTest("CubeInsetDepth",
"testCubeInsetDepth",
"expectedCubeInsetDepth", [OperatorSpecEditMode("inset", {"thickness": 0.2, "depth": 0.2}, "VERT",
{5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61,
62,
65, 83, 91, 95})]),
- MeshTest("GridInsetRelativeOffset", "testGridInsetRelativeOffset",
+ SpecMeshTest("GridInsetRelativeOffset", "testGridInsetRelativeOffset",
"expectedGridInsetRelativeOffset",
[OperatorSpecEditMode("inset", {"thickness": 0.4,
"use_relative_offset": True}, "FACE",
{35, 36, 37, 45, 46, 47, 55, 56, 57})]),
# loop multi select
- MeshTest("MokeyLoopMultiSelect", "testMonkeyLoopMultiSelect", "expectedMonkeyLoopMultiSelect",
+ SpecMeshTest("MokeyLoopMultiSelect", "testMonkeyLoopMultiSelect", "expectedMonkeyLoopMultiSelect",
[OperatorSpecEditMode("loop_multi_select", {}, "VERT", {355, 359, 73, 301, 302})]),
- MeshTest("HoledGridLoopMultiSelect", "testGridLoopMultiSelect", "expectedGridLoopMultiSelect",
+ SpecMeshTest("HoledGridLoopMultiSelect", "testGridLoopMultiSelect", "expectedGridLoopMultiSelect",
[OperatorSpecEditMode("loop_multi_select", {}, "VERT", {257, 169, 202, 207, 274, 278, 63})]),
- MeshTest("EmptyMeshLoopMultiSelect", "testEmptyMeshLoopMultiSelect", "expectedEmptyMeshLoopMultiSelect",
+ SpecMeshTest("EmptyMeshLoopMultiSelect", "testEmptyMeshLoopMultiSelect", "expectedEmptyMeshLoopMultiSelect",
[OperatorSpecEditMode("loop_multi_select", {}, "VERT", {})]),
# mark seam
- MeshTest("CubeMarkSeam", "testCubeMarkSeam", "expectedCubeMarkSeam",
+ SpecMeshTest("CubeMarkSeam", "testCubeMarkSeam", "expectedCubeMarkSeam",
[OperatorSpecEditMode("mark_seam", {}, "EDGE", {1})]),
# select all
- MeshTest("CircleSelectAll", "testCircleSelectAll", "expectedCircleSelectAll",
+ SpecMeshTest("CircleSelectAll", "testCircleSelectAll", "expectedCircleSelectAll",
[OperatorSpecEditMode("select_all", {}, "VERT", {1})]),
- MeshTest("IsolatedVertsSelectAll", "testIsolatedVertsSelectAll", "expectedIsolatedVertsSelectAll",
+ SpecMeshTest("IsolatedVertsSelectAll", "testIsolatedVertsSelectAll", "expectedIsolatedVertsSelectAll",
[OperatorSpecEditMode("select_all", {}, "VERT", {})]),
- MeshTest("EmptyMeshSelectAll", "testEmptyMeshSelectAll", "expectedEmptyMeshSelectAll",
+ SpecMeshTest("EmptyMeshSelectAll", "testEmptyMeshSelectAll", "expectedEmptyMeshSelectAll",
[OperatorSpecEditMode("select_all", {}, "VERT", {})]),
# select axis - Cannot be tested. Needs active vert selection
- # MeshTest("MonkeySelectAxisX", "testMonkeySelectAxisX", "expectedMonkeySelectAxisX",
+ # SpecMeshTest("MonkeySelectAxisX", "testMonkeySelectAxisX", "expectedMonkeySelectAxisX",
# [OperatorSpecEditMode("select_axis", {"axis": "X"}, "VERT", {13})]),
- # MeshTest("MonkeySelectAxisY", "testMonkeySelectAxisY", "expectedMonkeySelectAxisY",
+ # SpecMeshTest("MonkeySelectAxisY", "testMonkeySelectAxisY", "expectedMonkeySelectAxisY",
# [OperatorSpecEditMode("select_axis", {"axis": "Y", "sign": "NEG"}, "FACE", {317})]),
- # MeshTest("MonkeySelectAxisXYZ", "testMonkeySelectAxisXYZ", "expectedMonkeySelectAxisXYZ",
+ # SpecMeshTest("MonkeySelectAxisXYZ", "testMonkeySelectAxisXYZ", "expectedMonkeySelectAxisXYZ",
# [OperatorSpecEditMode("select_axis", {"axis": "X", "sign": "NEG"}, "FACE", {317}),
# OperatorSpecEditMode("select_axis", {"axis": "Y", "sign": "POS"}, "FACE", {}),
# OperatorSpecEditMode("select_axis", {"axis": "Z", "sign": "NEG"}, "FACE", {})]),
# select faces by sides
- MeshTest("CubeSelectFacesBySide", "testCubeSelectFacesBySide", "expectedCubeSelectFacesBySide",
+ SpecMeshTest("CubeSelectFacesBySide", "testCubeSelectFacesBySide", "expectedCubeSelectFacesBySide",
[OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]),
- MeshTest("CubeSelectFacesBySideGreater", "testCubeSelectFacesBySideGreater", "expectedCubeSelectFacesBySideGreater",
+ SpecMeshTest("CubeSelectFacesBySideGreater", "testCubeSelectFacesBySideGreater", "expectedCubeSelectFacesBySideGreater",
[OperatorSpecEditMode("select_face_by_sides", {"number": 4, "type": "GREATER", "extend": True}, "FACE", {})]),
- MeshTest("CubeSelectFacesBySideLess", "testCubeSelectFacesBySideLess", "expectedCubeSelectFacesBySideLess",
+ SpecMeshTest("CubeSelectFacesBySideLess", "testCubeSelectFacesBySideLess", "expectedCubeSelectFacesBySideLess",
[OperatorSpecEditMode("select_face_by_sides", {"number": 4, "type": "GREATER", "extend": True}, "FACE", {})]),
# select interior faces
- MeshTest("CubeSelectInteriorFaces", "testCubeSelectInteriorFaces", "expectedCubeSelectInteriorFaces",
+ SpecMeshTest("CubeSelectInteriorFaces", "testCubeSelectInteriorFaces", "expectedCubeSelectInteriorFaces",
[OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]),
- MeshTest("HoledCubeSelectInteriorFaces", "testHoledCubeSelectInteriorFaces", "expectedHoledCubeSelectInteriorFaces",
+ SpecMeshTest("HoledCubeSelectInteriorFaces", "testHoledCubeSelectInteriorFaces", "expectedHoledCubeSelectInteriorFaces",
[OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]),
- MeshTest("EmptyMeshSelectInteriorFaces", "testEmptyMeshSelectInteriorFaces", "expectedEmptyMeshSelectInteriorFaces",
+ SpecMeshTest("EmptyMeshSelectInteriorFaces", "testEmptyMeshSelectInteriorFaces", "expectedEmptyMeshSelectInteriorFaces",
[OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]),
# select less
- MeshTest("MonkeySelectLess", "testMonkeySelectLess", "expectedMonkeySelectLess",
+ SpecMeshTest("MonkeySelectLess", "testMonkeySelectLess", "expectedMonkeySelectLess",
[OperatorSpecEditMode("select_less", {}, "VERT", {2, 8, 24, 34, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 68,
69, 70, 71, 74, 75, 78, 80, 81, 82, 83, 90, 91, 93, 95, 97, 99,
101, 109, 111, 115, 117, 119, 121, 123, 125, 127, 129, 130, 131,
@@ -295,37 +295,37 @@ def main():
462, 463, 464, 471, 473, 474, 475, 476, 477, 478, 479, 480, 481,
482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495,
496, 497, 498, 499, 502, 505})]),
- MeshTest("HoledCubeSelectLess", "testHoledCubeSelectLess", "expectedHoledCubeSelectLess",
+ SpecMeshTest("HoledCubeSelectLess", "testHoledCubeSelectLess", "expectedHoledCubeSelectLess",
[OperatorSpecEditMode("select_face_by_sides", {}, "FACE", {})]),
- MeshTest("EmptyMeshSelectLess", "testEmptyMeshSelectLess", "expectedEmptyMeshSelectLess",
+ SpecMeshTest("EmptyMeshSelectLess", "testEmptyMeshSelectLess", "expectedEmptyMeshSelectLess",
[OperatorSpecEditMode("select_face_by_sides", {}, "VERT", {})]),
# select linked
- MeshTest("PlanesSelectLinked", "testPlanesSelectLinked", "expectedPlanesSelectedLinked",
+ SpecMeshTest("PlanesSelectLinked", "testPlanesSelectLinked", "expectedPlanesSelectedLinked",
[OperatorSpecEditMode("select_linked", {}, "VERT", {7})]),
- MeshTest("CubesSelectLinked", "testCubesSelectLinked", "expectedCubesSelectLinked",
+ SpecMeshTest("CubesSelectLinked", "testCubesSelectLinked", "expectedCubesSelectLinked",
[OperatorSpecEditMode("select_linked", {}, "VERT", {11})]),
- MeshTest("EmptyMeshSelectLinked", "testEmptyMeshSelectLinked", "expectedEmptyMeshSelectLinked",
+ SpecMeshTest("EmptyMeshSelectLinked", "testEmptyMeshSelectLinked", "expectedEmptyMeshSelectLinked",
[OperatorSpecEditMode("select_linked", {}, "VERT", {})]),
# select nth (checkered deselect)
- MeshTest("CircleSelect2nd", "testCircleSelect2nd", "expectedCircleSelect2nd",
+ SpecMeshTest("CircleSelect2nd", "testCircleSelect2nd", "expectedCircleSelect2nd",
[OperatorSpecEditMode("select_nth", {}, "VERT", {i for i in range(32)})]),
# unsubdivide
# normal case
- MeshTest("CubeFaceUnsubdivide", "testCubeUnsubdivide", "expectedCubeUnsubdivide",
+ SpecMeshTest("CubeFaceUnsubdivide", "testCubeUnsubdivide", "expectedCubeUnsubdivide",
[OperatorSpecEditMode("unsubdivide", {}, "FACE", {i for i in range(6)})]),
# T87259 - test cases
- MeshTest("CubeEdgeUnsubdivide", "testCubeEdgeUnsubdivide", "expectedCubeEdgeUnsubdivide",
+ SpecMeshTest("CubeEdgeUnsubdivide", "testCubeEdgeUnsubdivide", "expectedCubeEdgeUnsubdivide",
[OperatorSpecEditMode("unsubdivide", {}, "EDGE", {i for i in range(6)})]),
- MeshTest("UVSphereUnsubdivide", "testUVSphereUnsubdivide", "expectedUVSphereUnsubdivide",
- [OperatorSpecEditMode("unsubdivide", {'iterations': 9}, "FACE", {i for i in range(512)})]),
+ SpecMeshTest("UVSphereUnsubdivide", "testUVSphereUnsubdivide", "expectedUVSphereUnsubdivide",
+ [OperatorSpecEditMode("unsubdivide", {'iterations': 9}, "FACE", {i for i in range(512)})]),
# vert connect path
# Tip: It works only if there is an already existing face or more than 2 vertices.
- MeshTest("CubeVertConnectPath", "testCubeVertConnectPath", "expectedCubeVertConnectPath",
+ SpecMeshTest("CubeVertConnectPath", "testCubeVertConnectPath", "expectedCubeVertConnectPath",
[OperatorSpecEditMode("vert_connect_path", {}, "VERT", {0, 5})]),
]
diff --git a/tests/python/physics_cloth.py b/tests/python/physics_cloth.py
index b88b4d63f9d..83f1366037c 100644
--- a/tests/python/physics_cloth.py
+++ b/tests/python/physics_cloth.py
@@ -24,25 +24,25 @@ import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest
def main():
test = [
- MeshTest("ClothSimple", "testClothPlane", "expectedClothPlane",
+ SpecMeshTest("ClothSimple", "testClothPlane", "expectedClothPlane",
[ModifierSpec('Cloth', 'CLOTH', {'settings': {'quality': 5}}, 15)], threshold=1e-3),
# Not reproducible
- # MeshTest("ClothPressure", "testObjClothPressure", "expObjClothPressure",
+ # SpecMeshTest("ClothPressure", "testObjClothPressure", "expObjClothPressure",
# [ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_pressure': True,
# 'uniform_pressure_force': 1}}, 16)]),
# Not reproducible
- # MeshTest("ClothSelfCollision", "testClothCollision", "expClothCollision",
+ # SpecMeshTest("ClothSelfCollision", "testClothCollision", "expClothCollision",
# [ModifierSpec('Cloth', 'CLOTH', {'collision_settings': {'use_self_collision': True}}, 67)]),
- MeshTest("ClothSpring", "testTorusClothSpring", "expTorusClothSpring",
+ SpecMeshTest("ClothSpring", "testTorusClothSpring", "expTorusClothSpring",
[ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_internal_springs': True}}, 10)], threshold=1e-3),
]
diff --git a/tests/python/physics_dynamic_paint.py b/tests/python/physics_dynamic_paint.py
index b5d09c8cb3a..d1f31b2718b 100644
--- a/tests/python/physics_dynamic_paint.py
+++ b/tests/python/physics_dynamic_paint.py
@@ -24,13 +24,13 @@ import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest
def main():
test = [
- MeshTest("DynamicPaintSimple", "testObjDynamicPaintPlane", "expObjDynamicPaintPlane",
+ SpecMeshTest("DynamicPaintSimple", "testObjDynamicPaintPlane", "expObjDynamicPaintPlane",
[ModifierSpec('dynamic_paint', 'DYNAMIC_PAINT',
{'ui_type': 'CANVAS',
'canvas_settings': {'canvas_surfaces': {'surface_type': 'WAVE', 'frame_end': 15}}},
diff --git a/tests/python/physics_ocean.py b/tests/python/physics_ocean.py
index 40227d3d8d7..59eb71aa9f1 100644
--- a/tests/python/physics_ocean.py
+++ b/tests/python/physics_ocean.py
@@ -24,13 +24,13 @@ import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest
def main():
test = [
# World coordinates of test and expected object should be same.
- MeshTest("PlaneOcean", "testObjPlaneOcean", "expObjPlaneOcean",
+ SpecMeshTest("PlaneOcean", "testObjPlaneOcean", "expObjPlaneOcean",
[ModifierSpec('Ocean', 'OCEAN', {})]),
]
ocean_test = RunTest(test)
diff --git a/tests/python/physics_particle_instance.py b/tests/python/physics_particle_instance.py
index e12e357e4ce..ce0bdffcd8b 100644
--- a/tests/python/physics_particle_instance.py
+++ b/tests/python/physics_particle_instance.py
@@ -24,13 +24,13 @@ import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest
def main():
test = [
- MeshTest("ParticleInstanceSimple", "testParticleInstance", "expectedParticleInstance",
+ SpecMeshTest("ParticleInstanceSimple", "testParticleInstance", "expectedParticleInstance",
[ModifierSpec('ParticleInstance', 'PARTICLE_INSTANCE', {'object': bpy.data.objects['Cube']})],
threshold=1e-3),
diff --git a/tests/python/physics_particle_system.py b/tests/python/physics_particle_system.py
index 0adc5ab1c54..4f54b87e8b0 100644
--- a/tests/python/physics_particle_system.py
+++ b/tests/python/physics_particle_system.py
@@ -20,16 +20,15 @@
import os
import sys
-
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import RunTest, ParticleSystemSpec, MeshTest
+from modules.mesh_test import RunTest, ParticleSystemSpec, SpecMeshTest
def main():
test = [
- MeshTest("ParticleSystemTest", "testParticleSystem", "expParticleSystem",
+ SpecMeshTest("ParticleSystemTest", "testParticleSystem", "expParticleSystem",
[ParticleSystemSpec('Particles', 'PARTICLE_SYSTEM', {'render_type': "OBJECT",
'instance_object': bpy.data.objects['Cube']}, 20)], threshold=1e-3),
diff --git a/tests/python/physics_softbody.py b/tests/python/physics_softbody.py
index 985b3a29bb4..6d4d4cc08d5 100644
--- a/tests/python/physics_softbody.py
+++ b/tests/python/physics_softbody.py
@@ -24,13 +24,13 @@ import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest
def main():
test = [
- MeshTest("SoftBodySimple", "testSoftBody", "expectedSoftBody",
+ SpecMeshTest("SoftBodySimple", "testSoftBody", "expectedSoftBody",
[ModifierSpec('Softbody', 'SOFT_BODY',
{'settings': {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}},
45)]),